M11: oändligt läge och New Game+
All checks were successful
release / build-release (push) Successful in 1m34s
All checks were successful
release / build-release (push) Successful in 1m34s
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>
This commit is contained in:
115
internal/endless/endless_test.go
Normal file
115
internal/endless/endless_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user