Files
Denis Defolie 2fe455f5d1 Add pure correction and pedagogy logic with full test coverage
scripts/correction/: AnswerValidator (multiple choice, sentence
correction via tap-the-faulty-word + retype) and DictationAnalyzer
build the CorrectionResult structure from cahier des charges §6.
Dictation analysis aligns words with WordAligner (Wagner-Fischer edit
distance) before any character comparison, so an omitted or added
word doesn't cascade into false errors on every following word.
TextNormalizer handles non-breaking spaces, typographic apostrophes,
and decomposed-vs-composed French accented characters via an
explicit table (GDScript has no built-in NFC/NFD normalization).
Error classification follows the data annotations only (rule_id
present -> grammatical, absent -> unclassified), never a runtime
linguistic guess.

scripts/pedagogy/: DifficultyManager (per-skill difficulty from
recent results), AttemptFlow (3-attempt graduated aid: hint, then
explanation, then reveal, matching exercise-lifecycle.md's second
gap), and ReviewScheduler (the five mastery states from §9, with
session-counted spaced review intervals [1, 3, 6, 12] rather than
wall-clock dates, since a family device's clock isn't trustworthy).

All of it is pure: no scene tree, no clock or RNG read directly,
every effectful input (session number, attempt outcome) is a
parameter. 65 headless tests across both layers, including the
word-omission-doesn't-cascade regression and the full
decouverte -> acquise -> a_reviser -> acquise path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-11 17:03:14 +02:00

40 lines
1.3 KiB
GDScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends GutTest
## Tests de TextNormalizer : espaces, apostrophes, caracteres francais decomposes.
func test_normalizes_non_breaking_spaces() -> void:
var text := "Les petits chats"
assert_eq(TextNormalizer.normalize(text), "Les petits chats")
func test_normalizes_typographic_apostrophe() -> void:
assert_eq(TextNormalizer.normalize("l’école"), "l'école")
func test_collapses_multiple_spaces() -> void:
assert_eq(TextNormalizer.normalize("Les petits chats"), "Les petits chats")
func test_recomposes_decomposed_accented_characters() -> void:
# "e" + accent aigu combinant (U+0301) doit devenir "é" precompose.
var decomposed := "école"
assert_eq(TextNormalizer.normalize(decomposed), "école")
func test_recomposes_cedille() -> void:
var decomposed := "français"
assert_eq(TextNormalizer.normalize(decomposed), "français")
func test_strips_leading_and_trailing_whitespace() -> void:
assert_eq(TextNormalizer.normalize(" bonjour "), "bonjour")
func test_split_words_splits_on_single_spaces() -> void:
var words := TextNormalizer.split_words("Les petits chats dorment.")
assert_eq(words, ["Les", "petits", "chats", "dorment."])
func test_split_words_on_empty_text_returns_empty_array() -> void:
assert_eq(TextNormalizer.split_words(" "), [])