Change tiebreaking prompt according to nature of tie

This commit is contained in:
RunasSudo 2021-07-31 17:51:09 +10:00
parent 32e89312fa
commit 116ff39fa5
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 19 additions and 19 deletions

View File

@ -98,7 +98,7 @@ where
} }
}; };
let elected_candidate = if max_cands.len() > 1 { let elected_candidate = if max_cands.len() > 1 {
super::choose_highest(state, opts, max_cands)? super::choose_highest(state, opts, max_cands, "Which candidate's surplus to distribute?")?
} else { } else {
max_cands[0] max_cands[0]
}; };

View File

@ -835,7 +835,7 @@ fn elect_sure_winners<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOp
while state.num_elected < state.election.seats { while state.num_elected < state.election.seats {
let max_cands = ties::multiple_max_by(&hopefuls, |c| &state.candidates[c].votes); let max_cands = ties::multiple_max_by(&hopefuls, |c| &state.candidates[c].votes);
let candidate = if max_cands.len() > 1 { let candidate = if max_cands.len() > 1 {
choose_highest(state, opts, max_cands)? choose_highest(state, opts, max_cands, "Which candidate to elect?")?
} else { } else {
max_cands[0] max_cands[0]
}; };
@ -879,7 +879,7 @@ fn elect_hopefuls<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOption
// Declare elected in descending order of votes // Declare elected in descending order of votes
let max_cands = ties::multiple_max_by(&cands_meeting_quota, |c| &state.candidates[c].votes); let max_cands = ties::multiple_max_by(&cands_meeting_quota, |c| &state.candidates[c].votes);
let candidate = if max_cands.len() > 1 { let candidate = if max_cands.len() > 1 {
choose_highest(state, opts, max_cands)? choose_highest(state, opts, max_cands, "Which candidate to elect?")?
} else { } else {
max_cands[0] max_cands[0]
}; };
@ -1016,7 +1016,7 @@ fn do_bulk_elect<N: Number>(state: &mut CountState<N>, opts: &STVOptions, templa
while !hopefuls.is_empty() { while !hopefuls.is_empty() {
let max_cands = ties::multiple_max_by(&hopefuls, |c| &state.candidates[c].votes); let max_cands = ties::multiple_max_by(&hopefuls, |c| &state.candidates[c].votes);
let candidate = if max_cands.len() > 1 { let candidate = if max_cands.len() > 1 {
choose_highest(state, opts, max_cands)? choose_highest(state, opts, max_cands, "Which candidate to elect?")?
} else { } else {
max_cands[0] max_cands[0]
}; };
@ -1077,7 +1077,7 @@ where
// Exclude only the lowest-ranked doomed candidate // Exclude only the lowest-ranked doomed candidate
let min_cands = ties::multiple_min_by(&doomed, |c| &state.candidates[c].votes); let min_cands = ties::multiple_min_by(&doomed, |c| &state.candidates[c].votes);
excluded_candidates = if min_cands.len() > 1 { excluded_candidates = if min_cands.len() > 1 {
vec![choose_lowest(state, opts, min_cands)?] vec![choose_lowest(state, opts, min_cands, "Which candidate to exclude?")?]
} else { } else {
vec![min_cands[0]] vec![min_cands[0]]
}; };
@ -1179,7 +1179,7 @@ where
let min_cands = ties::multiple_min_by(&hopefuls, |c| &state.candidates[c].votes); let min_cands = ties::multiple_min_by(&hopefuls, |c| &state.candidates[c].votes);
excluded_candidates = if min_cands.len() > 1 { excluded_candidates = if min_cands.len() > 1 {
vec![choose_lowest(state, opts, min_cands)?] vec![choose_lowest(state, opts, min_cands, "Which candidate to exclude?")?]
} else { } else {
vec![min_cands[0]] vec![min_cands[0]]
}; };
@ -1291,9 +1291,9 @@ fn finished_before_stage<N: Number>(state: &CountState<N>) -> bool {
/// Break a tie between the given candidates according to [STVOptions::ties], selecting the highest candidate /// Break a tie between the given candidates according to [STVOptions::ties], selecting the highest candidate
/// ///
/// The given candidates are assumed to be tied in this round /// The given candidates are assumed to be tied in this round
fn choose_highest<'c, N: Number>(state: &mut CountState<N>, opts: &STVOptions, candidates: Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { fn choose_highest<'c, N: Number>(state: &mut CountState<N>, opts: &STVOptions, candidates: Vec<&'c Candidate>, prompt_text: &str) -> Result<&'c Candidate, STVError> {
for strategy in opts.ties.iter() { for strategy in opts.ties.iter() {
match strategy.choose_highest(state, opts, &candidates) { match strategy.choose_highest(state, opts, &candidates, prompt_text) {
Ok(c) => { Ok(c) => {
return Ok(c); return Ok(c);
} }
@ -1312,9 +1312,9 @@ fn choose_highest<'c, N: Number>(state: &mut CountState<N>, opts: &STVOptions, c
/// Break a tie between the given candidates according to [STVOptions::ties], selecting the lowest candidate /// Break a tie between the given candidates according to [STVOptions::ties], selecting the lowest candidate
/// ///
/// The given candidates are assumed to be tied in this round /// The given candidates are assumed to be tied in this round
fn choose_lowest<'c, N: Number>(state: &mut CountState<N>, opts: &STVOptions, candidates: Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { fn choose_lowest<'c, N: Number>(state: &mut CountState<N>, opts: &STVOptions, candidates: Vec<&'c Candidate>, prompt_text: &str) -> Result<&'c Candidate, STVError> {
for strategy in opts.ties.iter() { for strategy in opts.ties.iter() {
match strategy.choose_lowest(state, opts, &candidates) { match strategy.choose_lowest(state, opts, &candidates, prompt_text) {
Ok(c) => { Ok(c) => {
return Ok(c); return Ok(c);
} }

View File

@ -53,7 +53,7 @@ impl TieStrategy {
/// Break a tie between the given candidates, selecting the highest candidate /// Break a tie between the given candidates, selecting the highest candidate
/// ///
/// The given candidates are assumed to be tied in this round /// The given candidates are assumed to be tied in this round
pub fn choose_highest<'c, N: Number>(&self, state: &mut CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { pub fn choose_highest<'c, N: Number>(&self, state: &mut CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>, prompt_text: &str) -> Result<&'c Candidate, STVError> {
match self { match self {
Self::Forwards => { Self::Forwards => {
let mut candidates = candidates.clone(); let mut candidates = candidates.clone();
@ -87,7 +87,7 @@ impl TieStrategy {
return Ok(candidates[state.random.as_mut().unwrap().next(candidates.len())]); return Ok(candidates[state.random.as_mut().unwrap().next(candidates.len())]);
} }
Self::Prompt => { Self::Prompt => {
match prompt(state, opts, candidates) { match prompt(state, opts, candidates, prompt_text) {
Ok(c) => { Ok(c) => {
state.logger.log_literal(format!("Tie between {} broken by manual intervention.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect()))); state.logger.log_literal(format!("Tie between {} broken by manual intervention.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect())));
return Ok(c); return Ok(c);
@ -101,7 +101,7 @@ impl TieStrategy {
/// Break a tie between the given candidates, selecting the lowest candidate /// Break a tie between the given candidates, selecting the lowest candidate
/// ///
/// The given candidates are assumed to be tied in this round /// The given candidates are assumed to be tied in this round
pub fn choose_lowest<'c, N: Number>(&self, state: &mut CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { pub fn choose_lowest<'c, N: Number>(&self, state: &mut CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>, prompt_text: &str) -> Result<&'c Candidate, STVError> {
match self { match self {
Self::Forwards => { Self::Forwards => {
let mut candidates = candidates.clone(); let mut candidates = candidates.clone();
@ -130,10 +130,10 @@ impl TieStrategy {
} }
} }
Self::Random(_seed) => { Self::Random(_seed) => {
return self.choose_highest(state, opts, candidates); return self.choose_highest(state, opts, candidates, prompt_text);
} }
Self::Prompt => { Self::Prompt => {
return self.choose_highest(state, opts, candidates); return self.choose_highest(state, opts, candidates, prompt_text);
} }
} }
} }
@ -199,7 +199,7 @@ where
/// Prompt the candidate for input, depending on CLI or WebAssembly target /// Prompt the candidate for input, depending on CLI or WebAssembly target
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn prompt<'c, N: Number>(state: &CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { fn prompt<'c, N: Number>(state: &CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>, prompt_text: &str) -> Result<&'c Candidate, STVError> {
// Show intrastage progress if required // Show intrastage progress if required
if !state.logger.entries.is_empty() { if !state.logger.entries.is_empty() {
// Print stage details // Print stage details
@ -224,7 +224,7 @@ fn prompt<'c, N: Number>(state: &CountState<N>, opts: &STVOptions, candidates: &
} }
let mut buffer = String::new(); let mut buffer = String::new();
loop { loop {
print!("Which candidate to select? [1-{}] ", candidates.len()); print!("{} [1-{}] ", prompt_text, candidates.len());
stdout().flush().expect("IO Error"); stdout().flush().expect("IO Error");
stdin().read_line(&mut buffer).expect("IO Error"); stdin().read_line(&mut buffer).expect("IO Error");
match buffer.trim().parse::<usize>() { match buffer.trim().parse::<usize>() {
@ -252,7 +252,7 @@ extern "C" {
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
fn prompt<'c, N: Number>(state: &CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { fn prompt<'c, N: Number>(state: &CountState<N>, opts: &STVOptions, candidates: &Vec<&'c Candidate>, prompt_text: &str) -> Result<&'c Candidate, STVError> {
let mut message = String::new(); let mut message = String::new();
// Show intrastage progress if required // Show intrastage progress if required
@ -278,7 +278,7 @@ fn prompt<'c, N: Number>(state: &CountState<N>, opts: &STVOptions, candidates: &
for (i, candidate) in candidates.iter().enumerate() { for (i, candidate) in candidates.iter().enumerate() {
message.push_str(&format!("{}. {}\n", i + 1, candidate.name)); message.push_str(&format!("{}. {}\n", i + 1, candidate.name));
} }
message.push_str(&format!("Which candidate to select? [1-{}] ", candidates.len())); message.push_str(&format!("{} [1-{}] ", prompt_text, candidates.len()));
loop { loop {
let response = get_user_input(&message); let response = get_user_input(&message);