clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

documentation and action work

Dami committed 783da5b
commit
783da5b07442cbdb3272983d566bd95a1a62f6b9
parent
2564c95e4f50a98d993745fb2487a63be7a1ce09
author
Dami <dami.etoile@gmail.com>
3 files changed, +142 -29
M.github/workflows/vhs.yml13+---
MCLAUDE.md103+++++++++++++++++++++++++++---
MREADME.md55++++++++++++++--
M .github/workflows/vhs.yml +3 -10
M CLAUDE.md +92 -11
@@ -41,6 +41,12 @@ just fmt # gofumpt -w .
4141 just tidy # go mod tidy4242 ```4343 44+Run a single package's tests:45+```bash46+go test ./nonogram/47+go test ./sudoku/ -run TestGenerateGrid48+```49+4450 ### VHS GIF Previews4551 ```bash4652 just vhs # generate all game preview GIFs (requires vhs + ffmpeg + ttyd)
@@ -85,17 +91,30 @@ Save/load uses `game.Registry` — each package registers its import function vi
8591 8692 ### Adding a New Puzzle Type8793 94+#### Game package files (e.g. `mypuzzle/`)95+8896 1. Create a new package under the project root (e.g., `mypuzzle/`)8997 2. Define a mode struct embedding `game.BaseMode` that implements `game.Spawner`9098 3. Add compile-time checks: `var _ game.Mode = MyMode{}` and `var _ game.Spawner = MyMode{}`9199 4. Export a `Modes` variable (`[]list.Item`) listing available difficulties92100 5. Implement `game.Gamer` on a `Model` struct93101 6. Register the import function in an `init()`: `game.Register("My Puzzle", func(data []byte) (game.Gamer, error) { ... })`94-7. Add the category to `GameCategories` in `model.go` (maintain alphabetical order)95-8. Add aliases to `categoryAliases` in `resolve.go`96-9. Create a `README.md` in the package directory (see existing games for format)97-10. Create a VHS tape file in `vhs/<game>.tape` (see VHS Tape Files section)98-11. Add a one-line entry to "Current Implementations" below102+7. Write tests in `mypuzzle_test.go` (see Testing Conventions below)103+8. Create a `README.md` in the package directory (see Game README Format below)104+105+#### Main folder edits required106+107+9. **`model.go`**: Import the new package and add a `game.Category` entry to `GameCategories` (maintain alphabetical order):108+ - Add import: `"github.com/FelineStateMachine/puzzletea/mypuzzle"`109+ - Add entry: `game.Category{Name: "My Puzzle", Desc: "Short description.", Modes: mypuzzle.Modes}`110+ - The import triggers the package's `init()`, which registers the save/load function — no blank import needed.111+10. **`resolve.go`**: Add the canonical name and any CLI aliases to the `categoryAliases` map (lowercase, spaces not hyphens).112+11. **`CLAUDE.md`**: Add a one-line entry to "Current Implementations" below.113+114+#### VHS and CI115+116+12. Create a VHS tape file in `vhs/<game>.tape` (see VHS Tape Files section)117+13. **`justfile`**: Add the new tape to the `vhs` target (e.g. `vhs vhs/mypuzzle.tape`)99118 100119 Each package follows a consistent file structure:101120 - `Gamemode.go`: Mode struct, `NewMode()`, `Spawn()`, `Modes` var, `init()` with `game.Register()`
@@ -154,6 +173,40 @@ Version is injected at build time via `-ldflags "-X main.version=..."`. The `ver
154173 155174 All Go code must pass `gofumpt` (strict superset of `gofmt`) and `golangci-lint` with the project's `.golangci.yml` config. Enabled linters: gofumpt, govet, errcheck, staticcheck, unused, gosimple, ineffassign, misspell. Run `just fmt` and `just lint` before committing.156175 176+### Testing Conventions177+178+Each game package has a `<game>_test.go` file. Tests follow these conventions:179+180+- **Table-driven tests** with subtests (`t.Run`) for all pure functions.181+- **Priority annotations** in section comments: `(P0)` = critical core logic, `(P1)` = important functionality (save/load, interface checks), `(P2)` = generators and less critical paths.182+- **Section separators**: `// --- FunctionName (P0) ---` above each test group.183+- **Save/load round-trip tests**: Create a model, call `GetSave()`, pass bytes to `ImportModel()`, and verify state is preserved.184+- **Compile-time interface checks** in tests: `var _ game.Gamer = Model{}` (or `&Model{}`).185+- **Edge cases**: Empty grids, 1x1 grids, maximum sizes, boundary conditions.186+- **Generator validation**: Verify generated puzzles satisfy game rules (e.g. solvability, uniqueness).187+188+Run `just test` before committing. All tests must pass in CI.189+190+### File Naming Convention191+192+Game packages use capital first letters for the three core files and lowercase for helpers:193+- **Capital**: `Gamemode.go`, `Model.go`, `Export.go`194+- **Lowercase**: `grid.go`, `keys.go`, `style.go`, `generator.go`, `<game>_test.go`195+196+### Game README Format197+198+Each game package contains a `README.md` with this structure:199+200+1. **Title** (`# Game Name`)201+2. **One-line description**202+3. **GIF preview** (`![Gameplay](../vhs/gamename.gif)`)203+4. **How to Play** — rules and mechanics204+5. **Controls** — table with Key and Action columns; include game-specific keys plus shared keys (`Ctrl+H`, `Ctrl+E`, `Ctrl+N`)205+6. **Modes** — table listing each mode with parameters and description206+7. **Quick Start** — `puzzletea new <game> <mode>` examples207+208+See `nonogram/README.md` for a complete example.209+157210 ### Color Convention158211 159212 All colors must use `lipgloss.AdaptiveColor{Light: "X", Dark: "Y"}` with ANSI 256 palette numbers. Avoid hex colors (`#rrggbb`) — they require true-color support and don't adapt to terminal background.
@@ -163,11 +216,15 @@ All colors must use `lipgloss.AdaptiveColor{Light: "X", Dark: "Y"}` with ANSI 25
163216 Each game has a `.tape` file in `vhs/` that generates a GIF preview using [charmbracelet/vhs](https://github.com/charmbracelet/vhs). All tapes share a common preamble:164217 165218 ```tape166-Set FontSize 13167-Set Width 854168-Set Height 480169-Set Padding 40170-Set Theme "Catppuccin Mocha"219+Output vhs/<game>.gif220+221+Set Theme "catppuccin-mocha"222+Set FontSize 14223+Set Width 1200224+Set Height 800225+Set Margin 60226+Set MarginFill "#A85C40"227+Set WindowBar Colorful171228 172229 Env COLORTERM "truecolor"173230 Env CLICOLOR_FORCE "1"
@@ -176,7 +233,31 @@ Env CI ""
176233 177234 The three `Env` lines are required for CI color support. `termenv` (used by lipgloss/bubbletea) checks `CI` env var and returns no-color when set — GitHub Actions sets `CI=true`. `COLORTERM=truecolor` sets the color profile, and `CLICOLOR_FORCE=1` is a fallback.178235 179-Each tape uses a `Hide` block to build the binary and clear the screen before `Show`ing the gameplay commands. Use `Ctrl+L` (not `clear`) to clear the screen in the hidden block.236+After the preamble, each tape follows this structure:237+238+```tape239+Hide240+Type "go build -o puzzletea"241+Enter242+Sleep 2s243+Ctrl+L244+Show245+246+Type "./puzzletea new <game> <mode>"247+Sleep 500ms248+Enter249+Sleep 2s250+251+# Demo gameplay actions...252+253+Sleep 3s254+```255+256+- Use a `Hide`/`Show` block to build the binary and clear the screen before showing gameplay.257+- Use `Ctrl+L` (not `clear`) to clear the screen in the hidden block.258+- Demonstrate core gameplay mechanics (filling cells, toggling, navigating, etc.).259+- End with `Sleep 3s` to let viewers see the final state.260+- Add the new tape to the `vhs` target in the `justfile`.180261 181262 GIFs are not tracked in git — they are generated by CI and committed via PR.182263
M README.md +47 -8
@@ -153,31 +153,70 @@ Arrow keys, WASD, and Vim bindings (hjkl) are supported for grid movement across
153153 154154 Games are automatically saved to `~/.puzzletea/history.db` (SQLite). Navigating away saves progress; quitting with `Ctrl+C` marks the game as abandoned. Completed games are preserved and can be revisited.155155 156-## Building156+## Building and Testing157157 158158 [just](https://github.com/casey/just) is used as the command runner:159159 160160 ```bash161161 just # build162162 just run # build and run163-just test # run tests163+just test # run tests (go test ./...)164164 just lint # run golangci-lint165165 just fmt # format with gofumpt166+just tidy # go mod tidy166167 just install # install to $GOPATH/bin167168 just clean # remove build artifacts169+just vhs # generate all VHS GIF previews168170 ```169171 172+Run a single package's tests:173+174+```bash175+go test ./nonogram/176+go test ./sudoku/ -run TestGenerateGrid177+```178+179+All code must pass `gofumpt` and `golangci-lint` before committing. CI runs both on every PR.180+170181 ## Adding a New Puzzle171182 172183 PuzzleTea uses a plugin architecture. To add a new puzzle type:173184 174-1. Create a package (e.g., `mypuzzle/`)175-2. Implement the `game.Gamer` interface on a Model struct176-3. Define modes embedding `game.BaseMode` that implement `game.Spawner`177-4. Register an import function in `init()` for save/load support178-5. Add the category to `GameCategories` in `model.go`185+### 1. Create the game package186+187+Create a directory (e.g., `mypuzzle/`) with these files:188+189+| File | Purpose |190+|------|---------|191+| `Gamemode.go` | Mode struct embedding `game.BaseMode`, `Spawn()`, `Modes` var, `init()` with `game.Register()` |192+| `Model.go` | `Model` struct implementing `game.Gamer` |193+| `Export.go` | `Save` struct, `GetSave()`, `ImportModel()` for persistence |194+| `keys.go` | Game-specific `KeyMap` struct |195+| `style.go` | lipgloss styling and rendering helpers |196+| `generator.go` | Puzzle generation logic (if applicable) |197+| `grid.go` | Grid type and serialization (for grid-based games) |198+| `mypuzzle_test.go` | Tests (table-driven, save/load round-trip, generator validation) |199+| `README.md` | Game docs: rules, controls table, modes table, quick start examples |200+201+### 2. Wire it into the main application202+203+These files in the project root must be edited:204+205+- **`model.go`**: Import the package and add a `game.Category` entry to `GameCategories` (maintain alphabetical order). The import triggers `init()`, which registers save/load — no blank import needed.206+- **`resolve.go`**: Add the canonical name and any CLI aliases to `categoryAliases`.207+208+### 3. Add a VHS preview209+210+- Create `vhs/<game>.tape` following the format in existing tapes.211+- Add the tape to the `vhs` target in the `justfile`.212+213+### 4. Verify214+215+```bash216+just fmt && just lint && just test217+```179218 180-See any existing game package (e.g., `nonogram/`) for the full pattern.219+See any existing game package (e.g., `nonogram/`) for the full pattern, and `CLAUDE.md` for detailed conventions.181220 182221 ## License183222