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) } } } }