Feature: Leaderboard difficulty isolation, proposal category select, numeric answer matcher, 25 new questions (65 total)
This commit is contained in:
+56
-11
@@ -33,11 +33,47 @@ def levenshtein_distance(s1: str, s2: str) -> int:
|
||||
previous_row = current_row
|
||||
return previous_row[-1]
|
||||
|
||||
WORD_TO_NUM = {
|
||||
"null": "0", "zero": "0",
|
||||
"eins": "1", "ein": "1", "one": "1",
|
||||
"zwei": "2", "two": "2",
|
||||
"drei": "3", "three": "3",
|
||||
"vier": "4", "four": "4",
|
||||
"fuenf": "5", "five": "5",
|
||||
"sechs": "6", "six": "6",
|
||||
"sieben": "7", "seven": "7",
|
||||
"acht": "8", "eight": "8",
|
||||
"neun": "9", "nine": "9",
|
||||
"zehn": "10", "ten": "10",
|
||||
"elf": "11", "eleven": "11",
|
||||
"zwoelf": "12", "twelve": "12",
|
||||
"dreizehn": "13", "thirteen": "13",
|
||||
"vierzehn": "14", "fourteen": "14",
|
||||
"fuenfzehn": "15", "fifteen": "15",
|
||||
"sechzehn": "16", "sixteen": "16",
|
||||
"siebzehn": "17", "seventeen": "17",
|
||||
"achtzehn": "18", "eighteen": "18",
|
||||
"neunzehn": "19", "nineteen": "19",
|
||||
"zwanzig": "20", "twenty": "20",
|
||||
"fuenfzig": "50", "fifty": "50",
|
||||
"hundert": "100", "einhundert": "100", "one hundred": "100",
|
||||
"einhundertfuenfzig": "150", "one hundred fifty": "150", "one hundred and fifty": "150"
|
||||
}
|
||||
|
||||
NUM_TO_WORDS = {}
|
||||
for w, n in WORD_TO_NUM.items():
|
||||
NUM_TO_WORDS.setdefault(n, []).append(w)
|
||||
|
||||
def is_answer_correct(user_input: str, canonical: str, accepted_raw: str | None = None, canonical_en: str | None = None) -> bool:
|
||||
clean_user = normalize_text(user_input)
|
||||
if not clean_user:
|
||||
return False
|
||||
|
||||
# Also consider digit conversion of user input if a word was typed
|
||||
user_variants = [clean_user]
|
||||
if clean_user in WORD_TO_NUM:
|
||||
user_variants.append(WORD_TO_NUM[clean_user])
|
||||
|
||||
targets = [normalize_text(canonical)]
|
||||
if canonical_en:
|
||||
clean_en = normalize_text(canonical_en)
|
||||
@@ -56,18 +92,27 @@ def is_answer_correct(user_input: str, canonical: str, accepted_raw: str | None
|
||||
if part_clean:
|
||||
targets.append(part_clean)
|
||||
|
||||
for target in targets:
|
||||
# Expand any numeric target with corresponding number words and vice-versa
|
||||
expanded_targets = list(targets)
|
||||
for t in targets:
|
||||
if t in NUM_TO_WORDS:
|
||||
expanded_targets.extend(NUM_TO_WORDS[t])
|
||||
if t in WORD_TO_NUM:
|
||||
expanded_targets.append(WORD_TO_NUM[t])
|
||||
|
||||
for target in expanded_targets:
|
||||
if not target:
|
||||
continue
|
||||
if clean_user == target:
|
||||
return True
|
||||
# For short answers (1-3 chars, e.g. numbers or abbreviations), require exact match
|
||||
if len(target) <= 3:
|
||||
continue
|
||||
# For medium words (4-6 chars), allow max 1 typo
|
||||
max_allowed = 1 if len(target) <= 6 else 2
|
||||
dist = levenshtein_distance(clean_user, target)
|
||||
if dist <= max_allowed:
|
||||
return True
|
||||
for u in user_variants:
|
||||
if u == target:
|
||||
return True
|
||||
# For short answers (1-3 chars, e.g. numbers or abbreviations), require exact match
|
||||
if len(target) <= 3 or len(u) <= 3:
|
||||
continue
|
||||
# For medium words (4-6 chars), allow max 1 typo
|
||||
max_allowed = 1 if len(target) <= 6 else 2
|
||||
dist = levenshtein_distance(u, target)
|
||||
if dist <= max_allowed:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user