Files
HanjaKanaMun/AGENTS.md
T
2026-10-01 21:27:28 +02:00

4.4 KiB

HanjaKanaMun - Project Plan

Overview

Odin + raylib multilingual memory card game for learning Japanese (Hiragana/Katakana), Korean (Hangul), and Thai scripts.

Commands (see justfile): just run, just check, just test, just package <version>

Current Status

Completed

  • Code split into modules under src/ (themes, assets, audio, ui, card, memory, screens)
  • .odin files tracked in git (.gitignore used to ignore *.odin)
  • Real font: M PLUS Rounded 1c (Medium + ExtraBold, OFL) loaded via LoadFontEx with kana codepoints
  • Per-language themes (themes.Theme) with flag-based card backs: Japan (Hinomaru), Korea (Taegukgi), Thailand (Trairong)
  • Card flip animation: card art is rendered to textures and squashed horizontally, with lift, shadow and lighting; matched cards pulse
  • Sound effects synthesized in code (flip, match, mismatch, win, button click), no audio files needed
  • Japanese memory game: kana ↔ romaji pairs, 12 pairs picked at random from the 46 basic hiragana
  • ESC goes back one screen (quits from the main menu); no longer closes the app everywhere
  • Memory layout fixed (header and Back button no longer overlap cards), real shuffle, moves counted per pair
  • Win panel with Play Again / Menu

Pending

  • Keyboard support for card selection (1-9, A-L, etc.)
  • Korean memory game (needs a Hangul font, e.g. Noto Sans KR; theme and card back already exist)
  • Thai memory game (needs e.g. Noto Sans Thai; theme and card back already exist)
  • Katakana mode
  • Screen transitions
  • Score tracking and high scores
  • Difficulty levels (number of pairs)

Known Issues / Gotchas

  1. Font glyphs must be listed: raylib only bakes the codepoints passed to LoadFontEx. Any new non-ASCII text needs its characters added in assets.build_codepoints, otherwise it renders as ?.
  2. Old font failure root cause: DroidSansJapanese.ttf was a GitHub HTML page saved from a blob URL, not a font. Always download fonts from raw URLs and check them with file.
  3. Thai shaping: raylib does no text shaping, so Thai combining vowels/tone marks may be misplaced. Prefer standalone consonants on Thai cards.
  4. Tests share the memory game's global state, so they must run single-threaded (just test sets ODIN_TEST_THREADS=1).
  5. The game changes directory to the executable's folder at startup so assets/ is found from anywhere.
  6. Windows builds are cross-compiled: Odin can't link Windows binaries on Linux, so just windows builds objects with -build-mode:obj and links with lld-link against an xwin SDK. The VC runtime is linked statically; only Windows system DLLs are needed. Not yet tested on a real Windows machine.

File Structure

HanjaKanaMun/
├── AGENTS.md              # Project plan (this file)
├── README.md
├── justfile               # build/run/check/test/package recipes
├── ols.json               # ols language server settings (neovim)
├── odinfmt.json           # odinfmt formatter settings (4 spaces, LF)
├── .gitea/
│   ├── actions/setup/      # shared CI toolchain setup (apt, Odin, just)
│   └── workflows/
│       ├── gitea-build.yaml    # check + test + build on main/develop
│       └── gitea-release.yaml  # on tags v*: Linux + Windows packages, package registry upload, Gitea release
├── main.odin              # Window/audio setup, screen state machine
├── src/
│   ├── themes/themes.odin  # Theme struct, flag colors, Screen/Flag enums, layout constants
│   ├── assets/assets.odin  # Font loading (codepoints), get_font
│   ├── audio/audio.odin    # Synthesized sound effects, play()
│   ├── ui/ui.odin          # Text helpers, buttons, hover cursor
│   ├── card/card.odin      # Card struct, flag card-back art, face textures, flip animation
│   ├── memory/memory.odin  # Memory game state, logic and drawing
│   └── screens/screens.odin # Main menu and language menus
├── tests/memory_test.odin  # Game logic and card animation tests
└── assets/
    ├── MPLUSRounded1c-Medium.ttf
    ├── MPLUSRounded1c-ExtraBold.ttf
    └── MPLUSRounded1c-OFL.txt

Notes

  • NO rm -rf or starting over
  • Make small, incremental changes
  • Test after each change (just check, just test, then just run)
  • Keep existing functionality working
  • Commit after each working step