Skip to content

lang_tools.exercises.diacritic_typing

Diacritic typing exercise (from go-accenter).

The user types a hidden accented word character by character. Each submit evaluates one keystroke; correct characters are revealed, wrong keys are added to a disabled set. A round is "won" when every position is filled.

Classes:

DiacriticTypingExercise dataclass

DiacriticTypingExercise(**kwargs: object)

Bases: _BaseExercise

Diacritic typing round factory.

Initialize the exercise.

Parameters:

  • **kwargs (object, default: {} ) –

    Forwarded to _BaseExercise (e.g. progress_callback).

Methods:

  • start

    Build a round for a single word.

  • submit

    Score a single keystroke.

Source code in src/lang_tools/exercises/diacritic_typing.py
def __init__(self, **kwargs: object) -> None:
    """Initialize the exercise.

    Args:
        **kwargs: Forwarded to `_BaseExercise` (e.g. `progress_callback`).
    """
    super().__init__(exercise_type="diacritic_typing", **kwargs)  # type: ignore[arg-type]

start

start(
    word: Word, *, hint_level: HintLevel = "off"
) -> ExerciseRound

Build a round for a single word.

Parameters:

  • word (Word) –

    Target word; must contain accented characters for the exercise to be meaningful.

  • hint_level (HintLevel, default: 'off' ) –

    Initial hint level (see HintLevel).

Returns:

  • ExerciseRound

    ExerciseRound whose expected is the internal _DiacriticState.

Source code in src/lang_tools/exercises/diacritic_typing.py
def start(
    self,
    word: Word,
    *,
    hint_level: HintLevel = "off",
) -> ExerciseRound:
    """Build a round for a single word.

    Args:
        word: Target word; must contain accented characters for the
            exercise to be meaningful.
        hint_level: Initial hint level (see `HintLevel`).

    Returns:
        `ExerciseRound` whose `expected` is the internal `_DiacriticState`.
    """
    self._ensure_started()
    state = _DiacriticState(
        word=word,
        hint_level=hint_level,
        display=_initial_display(word.text, hint_level),
    )
    # Advance cursor past pre-revealed characters.
    while state.cursor < len(word.text) and state.display[state.cursor] != "_":
        state.cursor += 1

    return ExerciseRound(
        prompt={
            "display": state.display.copy(),
            "hint_level": hint_level,
            "disabled_keys": set(),
            "glosses": [g.text for g in word.glosses],
        },
        expected=state,
    )

submit

submit(
    round_: ExerciseRound, character: str
) -> RoundResult

Score a single keystroke.

Parameters:

  • round_ (ExerciseRound) –

    The round returned by start.

  • character (str) –

    The character the user pressed.

Returns:

  • RoundResult

    RoundResult whose correct is True for that keystroke. Once the

  • RoundResult

    word is complete, a WordResult with overall correctness (no

  • RoundResult

    errors) is added to word_results.

Source code in src/lang_tools/exercises/diacritic_typing.py
def submit(self, round_: ExerciseRound, character: str) -> RoundResult:
    """Score a single keystroke.

    Args:
        round_: The round returned by `start`.
        character: The character the user pressed.

    Returns:
        `RoundResult` whose `correct` is True for that keystroke. Once the
        word is complete, a `WordResult` with overall correctness (no
        errors) is added to `word_results`.
    """
    state: _DiacriticState = round_.expected

    if state.cursor >= len(state.word.text):
        return RoundResult(correct=True, feedback="Word already complete.")

    expected_char = state.word.text[state.cursor]
    correct = character == expected_char

    if correct:
        state.display[state.cursor] = expected_char
        state.cursor += 1
        # Skip already-revealed positions.
        while (
            state.cursor < len(state.word.text)
            and state.display[state.cursor] != "_"
        ):
            state.cursor += 1
    else:
        state.disabled_keys.add(character)
        state.error_count += 1

    # Update prompt mirror so callers reading `round_.prompt` see live state.
    round_.prompt["display"] = state.display.copy()
    round_.prompt["disabled_keys"] = set(state.disabled_keys)

    word_results: list[WordResult] = []
    completed = state.cursor >= len(state.word.text)
    if completed:
        word_results.append(
            WordResult(word_id=state.word.id, correct=state.error_count == 0),
        )

    result = RoundResult(
        correct=correct,
        feedback=(
            None
            if correct
            else f"{character!r} is not at position {state.cursor}."
        ),
        word_results=word_results,
    )
    self._bookkeep(result)
    return result