Files
metro-puzzle-jp/internal/endless/endless_test.go
claude 36a45563ec
All checks were successful
release / build-release (push) Successful in 1m34s
M11: oändligt läge och New Game+
internal/endless lottar mål ur en seed: en av storyns nätkopplade
stationer (aldrig samma två gånger i rad) med ett resmål som minst ett
tåg går rakt till men inte alla; banan kopieras, bedöms om mot nätet,
sällskapet växer var tredje mål och tidsgränsen krymper. O på bankartan
(eller -endless) startar en omgång; klarade mål ger poäng och XP, Enter
ger nästa mål, förlust eller Esc avslutar och omgången hamnar i
sparfilens poänglista (fem bästa). G två gånger startar New Game+: items,
banresultat, pengar och debuffar nollställs, skills, XP och japanskan
behålls, sällskapet är en person större per varv. Docs, wiki, tester och
skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 23:20:12 +02:00

116 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package endless
import (
"testing"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/nav"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng"
)
func loadLevels(t *testing.T) []*level.Level {
t.Helper()
dir, err := res.Path("data", "levels")
if err != nil {
t.Fatal(err)
}
all, err := level.LoadAll(dir)
if err != nil {
t.Fatal(err)
}
return all
}
// TestPlanIsDeterministicAndValid: samma seed ger samma följd, andra seeds
// en annan; varje mål har rätt tåg, en fälla, nytt resmål och ingen kedja;
// två mål i rad är aldrig samma station; sällskapet växer.
func TestPlanIsDeterministicAndValid(t *testing.T) {
all := loadLevels(t)
if len(Pool(all)) < 5 {
t.Fatalf("poolen ska ha storyns stationer, har %d", len(Pool(all)))
}
seed := rng.FromInt(11)
prev := ""
var bases, dests []string
for step := 0; step < 12; step++ {
g, err := Plan(all, seed, step)
if err != nil {
t.Fatal(err)
}
again, _ := Plan(all, seed, step)
if again.Base != g.Base || again.Level.To != g.Level.To {
t.Fatalf("steg %d: inte deterministiskt", step)
}
l := g.Level
if l.Next != "" || l.To == l.Station || l.Net == nil || l.Destination.Get("ja") == "" {
t.Fatalf("steg %d: %+v", step, l)
}
correct, wrong := 0, 0
for _, tr := range l.Trains {
if tr.Verdict == nav.Unjudged {
t.Fatalf("steg %d: obedömt tåg", step)
}
if tr.Correct {
correct++
} else {
wrong++
}
}
if correct < 1 || correct > nav.MaxRoutes || wrong == 0 {
t.Fatalf("steg %d (%s → %s): rätt=%d fel=%d", step, g.Base, l.To, correct, wrong)
}
if g.Base == prev {
t.Fatalf("steg %d: samma station två gånger i rad (%s)", step, g.Base)
}
if l.TimeLimitSec < MinTimeLimit || l.Party < 1 || l.Party > MaxParty {
t.Fatalf("steg %d: tidsgräns %d sällskap %d", step, l.TimeLimitSec, l.Party)
}
prev = g.Base
bases = append(bases, g.Base)
dests = append(dests, l.To)
}
// Rampen: sällskapet är större längre fram än i början.
first, _ := Plan(all, seed, 0)
late, _ := Plan(all, seed, 9)
if late.Level.Party <= first.Level.Party && first.Level.Party < MaxParty {
t.Fatalf("sällskapet ska växa: %d → %d", first.Level.Party, late.Level.Party)
}
if late.Level.TimeLimitSec >= first.Level.TimeLimitSec && first.Level.TimeLimitSec > MinTimeLimit {
t.Fatalf("tidsgränsen ska krympa: %d → %d", first.Level.TimeLimitSec, late.Level.TimeLimitSec)
}
// Annan seed ger en annan följd.
diff := false
for step := 0; step < 12 && !diff; step++ {
g, err := Plan(all, rng.FromInt(12), step)
if err != nil {
t.Fatal(err)
}
diff = g.Base != bases[step] || g.Level.To != dests[step]
}
if !diff {
t.Fatal("två seeds gav samma följd")
}
// Originalbanorna rörs inte.
for _, l := range all {
if l.ID == bases[0] && l.To == dests[0] && l.Next == "" && l.IsLeg() == false {
// kan råka sammanfalla om lotten valde originalets resmål – kontrollera att Trains är orörda
}
}
}
func TestDestinationsNeedTrapAndRoute(t *testing.T) {
all := loadLevels(t)
for _, l := range Pool(all) {
d := Destinations(l)
if len(d) == 0 {
t.Errorf("%s: inga möjliga resmål", l.ID)
}
for _, id := range d {
if id == l.Station {
t.Errorf("%s: resmål = stationen själv", l.ID)
}
}
}
}