Skip to content

Learning algorithm

Source: data/app_data.py (AppData)

Learn sessions keep one set in memory as an AppData instance. Statistics columns on each row drive which items are drawn next. After every answer the table is written back with save_set, so progress survives leaving the screen.

flowchart LR
    CSV[Set CSV on disk] --> Load[AppData.__init__ / refresh]
    Load --> Group[draw_index_group]
    Group --> Row[current row]
    Row --> Answer[good / bad answer]
    Answer --> CSV

Catalog CRUD and path resolution stay outside this class. See Data and storage. UI learn controls such as BaseWordField in ui/components/base_word_field.py construct AppData when a learn session starts; they do not reimplement queue rules.

Statistics columns

Each set row stores four columns from StatsColumns:

Column Type Meaning in the algorithm
correct_answers int Lifetime count of correct answers for the row
good_answer bool Whether the latest answer was correct
good_answers_in_a_row bool Whether consecutive correct answers have been confirmed
word_to_learn bool Whether the row is prioritized for practice

Developers often write the three booleans as good_answer / good_answers_in_a_row / word_to_learn, abbreviated G/S/W or as a 0/0/0 triple in user-facing copy.

Named state G/S/W Typical meaning
Unknown 0/0/1 Failed recently; marked to learn
Unverified 0/0/0 Not yet confirmed through practice
Unconfirmed known 1/0/0, 1/0/1, 1/1/1 Correct recently but not fully learned
Known 1/1/0 Correct streak confirmed; not marked to learn

number_of_known_words() counts the Known triple. Progress reset uses module-level set_default_progress(file_name), which sets all four columns to their defaults (0 / False / False / False) and saves the file.

Drawing a practice group

draw_index_group() builds the row indexes for the current round. Group size is that set's effective queue_size. An empty catalog cell uses the Settings slider for the set's kind (LearnPreferences); a stored cell is clamped to the same range:

Kind Settings label Range Default
words Word formations 3–10 5
definitions Definitions 4–15 10

AppData.__init__ sets kind before the first draw, so a new session uses the set's limit. A change on Settings or on /set/settings applies the next time draw_index_group runs (session start and a new AppData). The group already drawn stays as it is.

  1. Take rows in the Unknown state (0/0/1).
  2. If the group is still shorter than the limit, add Unverified rows (0/0/0), preferring lower correct_answers.
  3. If still short, add Unconfirmed-known combinations, again preferring lower correct_answers.
  4. Sample at most that many indexes into current_group_of_indexes.
count = self.words.draw_index_group(save_indexes_in_class_art=True)
if count == 0:
    # nothing left to practice in this session setup
    ...

When save_indexes_in_class_art=True, the drawn indexes are also stored on AppData.last_group_of_indexes for UI helpers such as was_this_index_drawn() (the learn menu Previous session chip). That list is kept when leaving a session — including forced back — so the chip still reflects the queue the user had started. Leaving the learn menu via its Back button clears the list through delete_last_group_of_indexes().

draw_new_row() advances through the group. it_is_not_last_index_of_group() reports whether more indexes remain before the group is cleared.

Answer transitions

Both answer helpers mutate the current row and call save_set immediately.

Correct: good_answer_at_current_row()

  • Increments correct_answers.
  • Sets good_answer to True.
  • If the previous answer was already correct, sets good_answers_in_a_row to True.
  • Special cases clear word_to_learn when the row has completed the confirmation path used by the queue (see implementation for the 1/1/1 and 1/0/1 branches).

Incorrect: bad_answer_at_current_row()

  • Sets good_answer and good_answers_in_a_row to False.
  • Sets word_to_learn to True so the next draw prioritizes the row.
if answer_ok:
    self.words.good_answer_at_current_row()
else:
    self.words.bad_answer_at_current_row()

Those helpers run on the first Check of a card. When Settings Retype until correct is off, Check then becomes Next and the queue advances as usual.

Retry until correct

Retry is a UI flag, not an AppData rule. Check reads the set's effective retry_until_correct: a stored catalog cell, or LearnPreferences when that cell is empty. When it is on:

  1. The first wrong Check still calls bad_answer_at_current_row() once.
  2. Check becomes Try again; the same row stays current.
  3. Further Checks only compare the typed answer. They do not increment correct_answers or flip G/S/W again.
  4. A correct retype unlocks Next. The queue then advances without a second stats write for that card.

On word-formation sets, WordFields.prepare_retry keeps already-green fields filled so only missed forms need typing again. Definitions reset the word field and lock the speaker until the next reveal.

The app default is loaded at startup and edited from Settings. One set can store its own value from /set/settings — see Learning and Per-set settings.

Progress helpers

Method Use
number_of_known_words() Count Known rows (1/1/0)
number_of_learning_words() Count rows still in the learning mix
number_of_all_words() Total rows in the loaded set
are_all_words_learned() True when known count equals total
get_set_learn_progress(file_name) Module helper: (units, max_units) in quarter-word weights for Home tiles and the learn-menu bar
refresh() Reload the CSV into words after an external reset
set_default_progress(file_name) Module helper: wipe stats on disk

get_set_learn_progress does not start a learn session. Known rows count as 1, rows still in the mix as 0.25, and unverified 0/0/0 as 0, stored as known * 4 + learning over total * 4. Home ContentTile bars use this helper; returning to Home refreshes the tile list so the strip matches disk.

Learn UI uses are_all_words_learned() before starting a session and may offer set_default_progress when every row is Known.

Ownership boundaries

Concern Owner
Path and catalog FilePathManager, module-level app_data helpers
Queue and answer rules AppData
Retry-until-correct flag Effective set value; empty cell uses LearnPreferences
Practice-queue size Effective set queue_size; empty cell uses LearnPreferences; draw_index_group reads it
Presenting fields / buttons ui/components/ learn controls and WordListMenu
Transient UI chrome lock AppSession — never learning statistics

Do not keep a second copy of row statistics in screen fields or session bags. Reload with refresh() or construct a new AppData when the file may have changed outside the session.

Continue reading