|
|
|
@@ -14,19 +14,24 @@ pub fn extract_final_integer(text: &str) -> Option<String> { |
|
|
|
} |
|
|
|
|
|
|
|
pub fn extract_choice(text: &str) -> Option<char> { |
|
|
|
let answer = Regex::new(r"(?i)\b(?:answer|choose|choice)\s*(?:is|:|-)?\s*\(?([A-D])\)?\b") |
|
|
|
.expect("valid answer choice regex"); |
|
|
|
if let Some(captures) = answer.captures(text) { |
|
|
|
return captures |
|
|
|
.get(1) |
|
|
|
.and_then(|value| value.as_str().chars().next()) |
|
|
|
.map(|choice| choice.to_ascii_uppercase()); |
|
|
|
let explicit = Regex::new( |
|
|
|
r"(?i)\b(?:final\s+answer|answer|choose|choice)\s*(?:is|:|-)?\s*\(?([A-D])\)?\b", |
|
|
|
) |
|
|
|
.expect("valid explicit choice regex"); |
|
|
|
if let Some(captures) = explicit.captures_iter(text).last() { |
|
|
|
return choice_from_capture(&captures, 1); |
|
|
|
} |
|
|
|
|
|
|
|
let standalone = Regex::new(r"(?i)\b([A-D])\b").expect("valid standalone choice regex"); |
|
|
|
standalone |
|
|
|
.captures(text) |
|
|
|
.and_then(|captures| captures.get(1)) |
|
|
|
let whole_output = |
|
|
|
Regex::new(r"(?i)^(?:([A-D])|\(([A-D])\))$").expect("valid whole output choice regex"); |
|
|
|
whole_output.captures(text.trim()).and_then(|captures| { |
|
|
|
choice_from_capture(&captures, 1).or_else(|| choice_from_capture(&captures, 2)) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
fn choice_from_capture(captures: ®ex::Captures<'_>, index: usize) -> Option<char> { |
|
|
|
captures |
|
|
|
.get(index) |
|
|
|
.and_then(|value| value.as_str().chars().next()) |
|
|
|
.map(|choice| choice.to_ascii_uppercase()) |
|
|
|
} |
|
|
|
@@ -69,6 +74,40 @@ mod tests { |
|
|
|
assert_eq!(extract_choice("I choose (b)."), Some('B')); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn extracts_choice_from_final_answer_instead_of_earlier_prose_option() { |
|
|
|
assert_eq!( |
|
|
|
extract_choice( |
|
|
|
"Option A is tempting, but after checking the calculation the answer is D" |
|
|
|
), |
|
|
|
Some('D') |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn extracts_choice_from_final_answer_after_considered_options() { |
|
|
|
assert_eq!( |
|
|
|
extract_choice("I considered A and B before deciding. Final answer: C"), |
|
|
|
Some('C') |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn extracts_last_explicit_choice() { |
|
|
|
assert_eq!( |
|
|
|
extract_choice("Answer: A was my first thought. Final answer: D"), |
|
|
|
Some('D') |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn ignores_prose_without_final_choice() { |
|
|
|
assert_eq!( |
|
|
|
extract_choice("This explanation has no final option."), |
|
|
|
None |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
#[test] |
|
|
|
fn judges_integer_by_extracted_value() { |
|
|
|
assert!(judge_integer("Final: 42.", "42")); |
|
|
|
|