Remove the Quallenalarm (jellyfish) typing minigame

Letters-round lessons only had bubbles and jellyfish as eligible arcade
modes; with jellyfish gone, bubbles is now always used instead of the
two alternating - no lessons need to be dropped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 00:38:19 +02:00
parent a69db98241
commit 75b0eed080
10 changed files with 20 additions and 197 deletions

View File

@@ -38,7 +38,7 @@ __all__ = [
]
type LessonKind = Literal["letters", "fragments", "words", "sentences"]
type ModeId = Literal["dive", "bubbles", "jellyfish", "feed", "race"]
type ModeId = Literal["dive", "bubbles", "feed", "race"]
type CreatureId = Literal["clownfish", "octopus", "seahorse", "turtle", "pearlmussel"]
#: In world order - see ``tippen/src/lib/aquarium.ts``, the one place this list is
@@ -52,9 +52,9 @@ CREATURE_IDS: Final[tuple[CreatureId, ...]] = (
)
#: Which modes make sense for a kind - letters rounds are single keys, so only the
#: arcade modes fit; only words and sentences are long enough for a race.
#: arcade mode fits; only words and sentences are long enough for a race.
ELIGIBLE_MODES: Final[dict[LessonKind, tuple[ModeId, ...]]] = {
"letters": ("bubbles", "jellyfish"),
"letters": ("bubbles",),
"fragments": ("dive", "feed"),
"words": ("dive", "feed", "race"),
"sentences": ("dive", "race"),
@@ -230,9 +230,6 @@ def _build_lessons(worlds: tuple[_YamlWorld, ...]) -> tuple[Lesson, ...]:
lessons: list[Lesson] = []
active: set[str] = set()
seen_before: set[str] = set()
# Letters rounds alternate bubbles/jellyfish across the whole course, so the arcade
# game never repeats twice in a row even across a fragments/words lesson in between.
last_arcade: ModeId = "jellyfish" # so lesson 1 opens on bubbles
for world in worlds:
for entry in world.lessons:
@@ -260,16 +257,10 @@ def _build_lessons(worlds: tuple[_YamlWorld, ...]) -> tuple[Lesson, ...]:
else:
emphasis = "mixed"
primary_mode: ModeId
if kind == "letters":
default_arcade: ModeId = "jellyfish" if last_arcade == "bubbles" else "bubbles"
primary_mode = entry.mode or default_arcade
last_arcade = primary_mode
else:
primary_mode = entry.mode or "dive"
primary_mode: ModeId = entry.mode or ("bubbles" if kind == "letters" else "dive")
# Bonus replays are only ever feed/race - dive is the plain default, and
# bubbles/jellyfish already alternate on their own.
# bubbles is the only letters-round arcade mode.
bonus_modes = tuple(
mode
for mode in ELIGIBLE_MODES[kind]

View File

@@ -244,11 +244,10 @@ def test_plays_a_mode_that_fits_its_kind(curriculum) -> None:
assert mode in ELIGIBLE_MODES[lesson.kind], where
def test_never_plays_the_same_arcade_game_twice_in_a_row(curriculum) -> None:
arcade = [lesson for lesson in curriculum.lessons if lesson.kind == "letters"]
for i in range(1, len(arcade)):
where = f"{arcade[i].number}: {arcade[i].title}"
assert arcade[i].primary_mode != arcade[i - 1].primary_mode, where
def test_letters_rounds_always_play_bubbles(curriculum) -> None:
for lesson in curriculum.lessons:
if lesson.kind == "letters":
assert lesson.primary_mode == "bubbles", f"{lesson.number}: {lesson.title}"
def test_offers_every_eligible_mode_not_gated_on_as_a_bonus(curriculum) -> None: