M0: skelett, byggkedja och dokumentation för JP Subway Pussel

- Go 1.27 + Ebitengine v2.10, intern upplösning 320x180 med heltalsskalning
- internal/res (resursuppslag), internal/rng (seedad slump), internal/level
  (banformat, validering, BFS-lösbarhet) med tester; internal/archtest
  kontrollerar att bara presentationslagret beror på Ebitengine
- tools/build: debug -> build/debug/, release -> build/release/<os>-<arch>/
  med resources/ bredvid binären; Windows korsbyggs utan C-verktyg
- VS Code tasks (build/test/release) och launch (dlv)
- Första banan 001-flygplatsen, sv/en-texter, skyltordbok, placeholder-sprite
- doc/: spelbeskrivning (renskriven), mål och tester, roadmap med teknikval
  (M0–M12), plan.md; README med bygg- och beroendeinformation

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 18:52:10 +02:00
commit c1f9546af6
32 changed files with 2165 additions and 0 deletions

130
doc/mal-och-tester.md Normal file
View File

@@ -0,0 +1,130 @@
# Mål och tester
## Projektmål
### Vision
Ett pixel art-pusselspel där känslan av att vara vilse på en japansk
station är själva spelet – och där man faktiskt blir bättre på att läsa
skyltarna ju mer man spelar. Spelet ska vara en enda binär som är lika lätt
att bygga på Linux som på Windows.
### Konkreta mål
1. **Spelbar story** från flygplatsen till flygplatsen, med stigande
svårighet, flerbytesbanor, sidobanor, slutanimation och eftertexter.
2. **En binär + en `resources/`-mapp** per plattform. Inga installers, inga
runtime-beroenden utöver vad ett vanligt skrivbord redan har.
3. **Få byggverktyg.** Go är det enda kravet (Linux behöver dessutom en
C-kompilator och X11/GL-headers för fönsterhanteringen). Allt byggs med
`go run ./tools/build`.
4. **Debug och release i VS Code** med F5 respektive Ctrl+Shift+B, utan
extra skript.
5. **All spellogik testbar utan fönster.** Bara presentationslagret känner
till Ebitengine.
6. **Deterministiskt.** Samma seed ger samma minispel och samma mål i
oändligt läge.
7. **Windows utan omskrivning.** `-os windows` ska vara allt som krävs för
att få en fungerande Windows-binär från Linux.
### Mätbara mål
| Mål | Mått | Status 2026-10-23 |
|-----|------|-------------------|
| Inkrementellt debug-bygge | < 10 s | 4,7 s |
| Release-binär | < 20 MB | 10,2 MB (Linux), 10,8 MB (Windows) |
| Testsviten | `go test ./...` grön på < 30 s | ~0,1 s |
| Banor | 100 % passerar det automatiska lösbarhetstestet | 1 av 1 |
| Windows-korsbygge från Linux | fungerar utan extra verktyg | ✓ (CGO_ENABLED=0) |
| Linux-binärens länkade bibliotek | bara libc | ✓ (GL/X11 laddas dynamiskt vid körning) |
| Bildfrekvens | 60 fps vid 4× skala på integrerad grafik | ej mätt |
| Uppstart | < 2 s till första bildruta | ej mätt |
## Teststrategi
### Principer
- **Ren logik → enhetstester.** Paket under `internal/` som inte är
presentation får inte importera Ebitengine. Det kontrolleras av
`internal/archtest`, som läser beroendegrafen med `go list` och faller om
något annat paket än `internal/game`, `internal/ui`, `internal/audio`
eller `cmd/…` drar in Ebitengine.
- **Data testas som data.** Varje bana, språkfil och ordbok som ligger i
`resources/` valideras av tester, så att trasigt innehåll stoppar bygget
i stället för att krascha spelet.
- **Slump går alltid via `internal/rng`** med en känd seed. Då går
minispel och event-timing att testa deterministiskt.
- **Inga tester kräver display.** Smoke-testet av själva fönstret körs
manuellt (eller på en maskin med skärm) med `-smoke`.
### Testnivåer
| Nivå | Vad | Hur | Körs |
|------|-----|-----|------|
| 1. Enhet | Funktioner i ett paket | `*_test.go` bredvid koden | `go test ./...` |
| 2. Data | Allt under `resources/` | Datadrivna tester som läser filerna | `go test ./...` |
| 3. Arkitektur | Beroenderegler | `internal/archtest` | `go test ./...` |
| 4. Simulering | En bot spelar en bana headless | Deterministisk körning av spelloopen utan rendering (kommande) | `go test ./...` |
| 5. Smoke | Binären öppnar fönster och avslutar | `jp-subway-pussel -smoke 90` | manuellt / med skärm |
| 6. Speltest | Känsla, svårighet, buggar | Checklistan nedan | före varje release |
### Tester som finns i dag
| Paket | Test | Verifierar |
|-------|------|------------|
| `internal/res` | `TestLocate_*` | Uppslagsordningen env → bredvid binären → uppåt från cwd |
| `internal/res` | `TestDir_RealRepo` | Att repots `resources/` hittas och att sv.json + placeholder.png går att läsa |
| `internal/rng` | `TestDeterministic`, `TestDeriveIsStableAndContextual` | Samma seed → samma sekvens; del-seeds per kontext |
| `internal/level` | `TestParseAndSolve`, `TestUnsolvableWhenWalledOff` | BFS hittar/hittar inte väg till rätt perrong |
| `internal/level` | `TestValidationErrors` | Ojämna rader, okända tecken, fel antal starter, tåg vid saknad perrong … |
| `internal/level` | `TestAllShippedLevels` | Varje bana i `resources/data/levels` parsar, har id = filnamn, är lösbar och har inga onåbara interaktiva rutor |
| `internal/archtest` | `TestOnlyPresentationPackagesImportEbitengine` | Beroenderegeln ovan |
### Planerade tester per milstolpe
| Milstolpe (se roadmap) | Tester som ska finnas när den är klar |
|------------------------|----------------------------------------|
| M1 Rörelse & kamera | Kollision mot vägg, rörelse i 4 riktningar, kamera klampas till kartan |
| M2 Stationen | Tidtabell: nästa avgång beräknas rätt; spärr släpper igenom med giltigt kort/biljett och stoppar annars; vinst när rätt tåg tas, förlust vid fel tåg; simuleringstest: bot går kortaste vägen och klarar bana 001 |
| M3 Japanska & skyltar | Delvis översättning ger exakt de ord spelaren kan; andel engelska följer svårighetstabellen; ordboken: alla nycklar har kana/romaji/en/sv; alla skyltord i banorna finns i ordboken; hjälptid = f(japanska-nivå) |
| M4 Lämmeltåget | Mätare sjunker med rätt takt; event-frekvens ökar med gruppstorlek; event väljs deterministiskt per seed |
| M5 Minikarta | Upptäckta rutor markeras, återställs per bana; avgångar syns från start |
| M6 Kort/biljetter/minispel | Saldo dras i spärr; tomt kort → automatkrav; biljettminispel: rätt/fel biljett; telefon-skanning misslyckas med given procent per seed; batteri tar bara slut över tröskeltid |
| M7 Svårighet/poäng/debuffs | Poängformeln (tid, pengar, mätare, läge); tidsgräns och maxantal missade tåg; varje debuff ändrar rätt stat; temporära debuffar räknar ned 1–4 banor |
| M8 Progression & sparfil | Sparfil round-trip (skriv → läs ger samma tillstånd inkl. seed); skill tree-krav; upplåsningar efter rätt bana; "< 33 %"-regeln kontrolleras mot itemlistan |
| M9 Navigeringslägen | Listläge genererar rätt ordning av tåg; skyltläge: 1–4 giltiga vägar; fel-riktnings-förlust triggar vid rätt slutstation |
| M10 Story-innehåll | Alla banor lösbara (redan automatiskt); flerbytesbanor har sammanhängande byten; sista banan slutar på flygplatsen |
| M11 Oändligt läge & NG+ | Målsekvens deterministisk per seed; NG+ behåller skills men nollställer items |
| M12 Ljud & polish | Alla ljudfiler som refereras finns och går att avkoda; Windows-binär startar (manuellt) |
### Acceptanskriterier för "klar"
En milstolpe är klar när:
1. Alla tester i raden ovan finns och är gröna.
2. `go vet ./...` och `go test -race ./...` är gröna.
3. Debug- och release-bygget går igenom för Linux och Windows.
4. Roadmapen är uppdaterad med datum och eventuella avsteg.
### Manuell testchecklista (speltest)
- [ ] Spelet startar till meny på under 2 s; ESC/avsluta fungerar.
- [ ] Fönstret kan skalas; pixlarna är skarpa (heltalsskalning).
- [ ] Bana 001 går att klara i både listläge och skyltläge.
- [ ] Fel tåg ger tydlig förlust med förklaring.
- [ ] Mätarna syns, sjunker och går att fylla på.
- [ ] Tomt kort tvingar till automaten; minispelet är begripligt första gången.
- [ ] Minikartan fylls i när man går runt och nollställs vid ny bana.
- [ ] Spara mitt i en bana, ladda, och få samma minispelsutfall.
- [ ] Ljud spelas vid ankomst och avgång; inget knaster vid loop.
- [ ] Samma sparfil fungerar på Linux och Windows.
### Kommandon
```bash
go test ./... # hela sviten
go test -race -v ./... # utförligt med race-detektor
go test ./internal/level/ -run TestAllShippedLevels -v # bara banorna
go vet ./...
go run ./tools/build -mode debug -run -- -smoke 90 # bygg + smoke-test med fönster
```