mirror of
https://github.com/deunix-educ/aventuriers-des-mots.git
synced 2026-10-13 02:11:58 +02:00
Accept elided corrections (j'achèterais) and fix the expected answer (0.8.1)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,7 @@
|
|||||||
{
|
{
|
||||||
"text": "j'achèterai",
|
"text": "j'achèterai",
|
||||||
"correct": false,
|
"correct": false,
|
||||||
"expected": "achèterais",
|
"expected": "j'achèterais",
|
||||||
"rule_id": "regle_futur_conditionnel"
|
"rule_id": "regle_futur_conditionnel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -403,3 +403,5 @@ Volume : 6 tableaux, 18 règles, 144 QCM, 72 corrections, 54 dictées (voix Pipe
|
|||||||
**Contrôles** : les gardes-fous de doublons et de quasi-doublons ont attrapé 14 collisions pendant la rédaction (dont avec CE2), toutes corrigées. Aucune dictée ne contient de point d'interrogation ou d'exclamation isolé (l'espace insécable avant « ? » rend la saisie ambiguë) ; ces phrases sont réservées aux QCM et corrections.
|
**Contrôles** : les gardes-fous de doublons et de quasi-doublons ont attrapé 14 collisions pendant la rédaction (dont avec CE2), toutes corrigées. Aucune dictée ne contient de point d'interrogation ou d'exclamation isolé (l'espace insécable avant « ? » rend la saisie ambiguë) ; ces phrases sont réservées aux QCM et corrections.
|
||||||
|
|
||||||
**Reste / limites** : CM1 et CM2 n'ont pas été relus pédagogiquement (indices, niveau de difficulté, règles du passé simple et de l'accord du participe avec COD, à faire valider) ; voix des 54 dictées non écoutées ; la création de profil propose désormais 4 niveaux (CE1, CE2, CM1, CM2).
|
**Reste / limites** : CM1 et CM2 n'ont pas été relus pédagogiquement (indices, niveau de difficulté, règles du passé simple et de l'accord du participe avec COD, à faire valider) ; voix des 54 dictées non écoutées ; la création de profil propose désormais 4 niveaux (CE1, CE2, CM1, CM2).
|
||||||
|
|
||||||
|
**Correctif (0.8.1) — mots touchés avec élision** : dans « La coulée de lave », le mot touché était « j'achèterai » mais l'attendu « achèterais » (sans « j' ») : la réponse naturelle « j'achèterais » était refusée. Données corrigées (attendu « j'achèterais ») et validateur rendu tolérant : pour un mot touché avec élision, on accepte le mot entier ou seulement le verbe, avec apostrophe droite ou typographique. Tests : validateur (formes acceptées et refusées) et garde-fou de données (un attendu ne perd pas le préfixe d'élision du mot touché).
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ config_version=5
|
|||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Les Aventuriers des mots"
|
config/name="Les Aventuriers des mots"
|
||||||
config/version="0.8.0"
|
config/version="0.8.1"
|
||||||
run/main_scene="res://scenes/boot/boot.tscn"
|
run/main_scene="res://scenes/boot/boot.tscn"
|
||||||
config/features=PackedStringArray("4.6", "Forward Plus")
|
config/features=PackedStringArray("4.6", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ static func validate_sentence_correction(
|
|||||||
errors.append(_missed_error(i, token, exercise))
|
errors.append(_missed_error(i, token, exercise))
|
||||||
elif is_faulty and was_selected:
|
elif is_faulty and was_selected:
|
||||||
var submitted: String = corrections.get(i, "")
|
var submitted: String = corrections.get(i, "")
|
||||||
if not _matches_expected(submitted, token["expected"]):
|
if not _matches_expected(submitted, token["expected"], token["text"]):
|
||||||
errors.append(_wrong_correction_error(i, submitted, token, exercise))
|
errors.append(_wrong_correction_error(i, submitted, token, exercise))
|
||||||
elif not is_faulty and was_selected:
|
elif not is_faulty and was_selected:
|
||||||
errors.append(_false_positive_error(i, token, exercise))
|
errors.append(_false_positive_error(i, token, exercise))
|
||||||
@@ -68,13 +68,31 @@ static func validate_sentence_correction(
|
|||||||
## Compare la correction saisie a celle attendue en ignorant les differences
|
## Compare la correction saisie a celle attendue en ignorant les differences
|
||||||
## incidentes (majuscule/minuscule, apostrophe typographique, espaces,
|
## incidentes (majuscule/minuscule, apostrophe typographique, espaces,
|
||||||
## accents decomposes) : ce n'est pas la casse qui est evaluee ici, c'est
|
## accents decomposes) : ce n'est pas la casse qui est evaluee ici, c'est
|
||||||
## l'accord. Une reponse ambigue n'est pas declaree fausse automatiquement
|
## l'accord. Quand le mot touche porte une elision (« j'achèterai »), l'enfant
|
||||||
## (CLAUDE.md, invariants pedagogiques).
|
## peut taper le mot entier (« j'achèterais ») ou seulement le verbe
|
||||||
static func _matches_expected(submitted: String, expected: String) -> bool:
|
## (« achèterais ») : une reponse ambigue n'est pas declaree fausse
|
||||||
return (
|
## automatiquement (CLAUDE.md, invariants pedagogiques).
|
||||||
TextNormalizer.normalize(submitted).to_lower()
|
static func _matches_expected(submitted: String, expected: String, token_text: String = "") -> bool:
|
||||||
== TextNormalizer.normalize(expected).to_lower()
|
var typed := TextNormalizer.normalize(submitted).to_lower()
|
||||||
)
|
for form in _accepted_forms(expected, token_text):
|
||||||
|
if typed == TextNormalizer.normalize(form).to_lower():
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
## Formes acceptees : l'attendu, sans son prefixe d'elision (« j' », « l' »...),
|
||||||
|
## ou avec le prefixe du mot touche quand l'attendu n'en porte pas.
|
||||||
|
static func _accepted_forms(expected: String, token_text: String) -> Array[String]:
|
||||||
|
var forms: Array[String] = [expected]
|
||||||
|
var normalized_expected := TextNormalizer.normalize(expected)
|
||||||
|
var normalized_token := TextNormalizer.normalize(token_text)
|
||||||
|
var expected_cut := normalized_expected.find("'")
|
||||||
|
if expected_cut != -1:
|
||||||
|
forms.append(normalized_expected.substr(expected_cut + 1))
|
||||||
|
var token_cut := normalized_token.find("'")
|
||||||
|
if token_cut != -1 and expected_cut == -1:
|
||||||
|
forms.append(normalized_token.substr(0, token_cut + 1) + normalized_expected)
|
||||||
|
return forms
|
||||||
|
|
||||||
|
|
||||||
static func _missed_error(index: int, token: Dictionary, exercise: Dictionary) -> Dictionary:
|
static func _missed_error(index: int, token: Dictionary, exercise: Dictionary) -> Dictionary:
|
||||||
|
|||||||
@@ -113,3 +113,39 @@ func test_sentence_correction_accepts_decomposed_accented_correction() -> void:
|
|||||||
exercise_with_accent, [0], {0: "école"}
|
exercise_with_accent, [0], {0: "école"}
|
||||||
)
|
)
|
||||||
assert_true(result.is_correct)
|
assert_true(result.is_correct)
|
||||||
|
|
||||||
|
|
||||||
|
func _elision_exercise() -> Dictionary:
|
||||||
|
return {
|
||||||
|
"rule_id": "regle_x",
|
||||||
|
"hint": "indice",
|
||||||
|
"explanation": "explication",
|
||||||
|
"tokens":
|
||||||
|
[
|
||||||
|
{"text": "Si", "correct": true},
|
||||||
|
{"text": "j'achèterai", "correct": false, "expected": "j'achèterais"},
|
||||||
|
{"text": "une", "correct": true},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func test_elided_token_accepts_the_whole_word_or_only_the_verb() -> void:
|
||||||
|
var exercise := _elision_exercise()
|
||||||
|
for typed in ["j'achèterais", "J'achèterais", "j’achèterais", "achèterais", " achèterais "]:
|
||||||
|
var result := AnswerValidator.validate_sentence_correction(exercise, [1], {1: typed})
|
||||||
|
assert_true(result.is_correct, "« %s » doit etre accepte" % typed)
|
||||||
|
|
||||||
|
|
||||||
|
func test_elided_token_still_rejects_a_wrong_correction() -> void:
|
||||||
|
var exercise := _elision_exercise()
|
||||||
|
for typed in ["j'achèterai", "achèterai", "achetterais", ""]:
|
||||||
|
var result := AnswerValidator.validate_sentence_correction(exercise, [1], {1: typed})
|
||||||
|
assert_false(result.is_correct, "« %s » ne doit pas etre accepte" % typed)
|
||||||
|
|
||||||
|
|
||||||
|
func test_expected_without_elision_accepts_the_prefix_of_the_touched_word() -> void:
|
||||||
|
var exercise := _elision_exercise()
|
||||||
|
exercise["tokens"][1]["expected"] = "achèterais"
|
||||||
|
for typed in ["achèterais", "j'achèterais"]:
|
||||||
|
var result := AnswerValidator.validate_sentence_correction(exercise, [1], {1: typed})
|
||||||
|
assert_true(result.is_correct, "« %s » doit etre accepte" % typed)
|
||||||
|
|||||||
@@ -79,3 +79,30 @@ func test_correct_answer_position_is_not_always_the_same() -> void:
|
|||||||
1,
|
1,
|
||||||
"bonne reponse toujours au meme rang : %s" % rule_id
|
"bonne reponse toujours au meme rang : %s" % rule_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func test_elided_wrong_tokens_keep_the_same_prefix_unless_the_word_changes() -> void:
|
||||||
|
# Regression : « j'achèterai » avait pour attendu « achèterais » (sans « j' »),
|
||||||
|
# donc la reponse naturelle « j'achèterais » etait refusee. Un mot touche avec
|
||||||
|
# elision doit avoir un attendu qui garde le prefixe d'elision, sauf si c'est
|
||||||
|
# l'elision elle-meme qui est l'objet de l'exercice (la/l'a, qu'elle/quelle...).
|
||||||
|
for rule_id: String in ExerciseEngine._rules:
|
||||||
|
for exercise: Dictionary in ExerciseEngine._candidates_for(rule_id, "sentence_correction"):
|
||||||
|
for token: Dictionary in exercise["tokens"]:
|
||||||
|
if token.get("correct", true):
|
||||||
|
continue
|
||||||
|
var text: String = token["text"]
|
||||||
|
var expected: String = token["expected"]
|
||||||
|
if not text.contains("'") or expected.contains("'"):
|
||||||
|
continue
|
||||||
|
# Le mot change de nature (ex. « Qu'elle » -> « Quelle ») : seules
|
||||||
|
# les corrections ou l'elision est conservee sont concernees.
|
||||||
|
var prefix := text.split("'")[0]
|
||||||
|
var stem := text.substr(prefix.length() + 1)
|
||||||
|
assert_false(
|
||||||
|
expected.to_lower().begins_with(stem.substr(0, 3).to_lower()),
|
||||||
|
(
|
||||||
|
"%s : attendu « %s » sans le prefixe de « %s »"
|
||||||
|
% [exercise["id"], expected, text]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user