package sim import ( "path/filepath" "testing" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng" ) // loadChain laddar alla banor och ger resan som börjar med id. func loadChain(t *testing.T, id string) []*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) } c := level.ChainOf(all, id) if len(c) < 2 { t.Fatalf("%s är ingen resa med byten: %v", id, c) } return c } // TestJourneyWithTransfer: bana 007 fortsätter på Ueno efter första tåget – // klockan, plånboken och sällskapet följer med, par-tiden adderas, och // resan vinns först när andra tåget nått Asakusa. func TestJourneyWithTransfer(t *testing.T) { c := loadChain(t, "007-akihabara") head, leg := c[0], c[1] r := NewRun(head, RunOptions{Seed: rng.FromInt(7), Chain: c[1:]}) if r.Legs() != 2 || r.LegNo() != 1 || r.IsLastLeg() || r.FinalDestination().Get("ja") != "浅草" || r.TimeLimit() != head.TimeLimitSec { t.Fatalf("resans start: legs=%d no=%d final=%s limit=%d", r.Legs(), r.LegNo(), r.FinalDestination().Get("ja"), r.TimeLimit()) } par1 := r.ParSec() for _, m := range r.Party.Members { m.Card = 100000 // egna kort med gott om saldo: testet handlar om bytet } r.Wallet.Card = 100000 tr := boardAt(t, r, level.Platform) ride(t, r) if r.Outcome != Ongoing || r.Level != leg || r.LegNo() != 2 || !r.IsLastLeg() { t.Fatalf("efter första tåget: outcome=%v level=%s legNo=%d", r.Outcome, r.Level.ID, r.LegNo()) } if r.TransferAt < 0 || r.FlashKey != FlashTransfer || r.Player.Tile != leg.SpawnPoint() || len(r.Rides) != 1 || r.Rides[0] != tr { t.Fatalf("bytet: transferAt=%.1f flash=%q tile=%v rides=%d", r.TransferAt, r.FlashKey, r.Player.Tile, len(r.Rides)) } if r.Time < 10 || r.ParSec() <= par1 || r.Fog.Explored(leg) >= 0.5 { t.Fatalf("klockan ska fortsätta (%.1f), par adderas (%.0f > %.0f) och dimman är ny (%.2f)", r.Time, r.ParSec(), par1, r.Fog.Explored(leg)) } if from, to := r.Party.FollowerTiles(1); from != leg.SpawnPoint() || to != leg.SpawnPoint() { t.Fatalf("sällskapet ska stå på nya starten: %v %v", from, to) } boardAt(t, r, level.Platform) ride(t, r) if r.Outcome != Won || len(r.Rides) != 2 || r.Score.Total <= 0 { t.Fatalf("resan ska vinnas efter andra tåget: outcome=%v (%s) rides=%d", r.Outcome, r.LostReason, len(r.Rides)) } if r.Score.Explore <= 0 || r.Score.Explore > ScoreExploreMax { t.Fatalf("utforskat är snittet över delbanorna: %d", r.Score.Explore) } } // TestJourneyWrongTrainOnSecondLeg: fel tåg på bytesstationen förlorar resan. func TestJourneyWrongTrainOnSecondLeg(t *testing.T) { c := loadChain(t, "007-akihabara") r := NewRun(c[0], RunOptions{Seed: rng.FromInt(8), Chain: c[1:]}) for _, m := range r.Party.Members { m.Card = 100000 } r.Wallet.Card = 100000 boardAt(t, r, level.Platform2) // Yamanote mot Ueno – också rätt ride(t, r) if r.Outcome != Ongoing || r.LegNo() != 2 { t.Fatalf("första tåget: %v", r.Outcome) } boardAt(t, r, level.WrongPlatform) // Ginza mot Shibuya: fel håll ride(t, r) if r.Outcome != Lost || r.LostReason != LostWrongDirection { t.Fatalf("outcome=%v reason=%q", r.Outcome, r.LostReason) } } // TestChainLevelsLoad: alla delbanor går att läsa en och en också. func TestChainLevelsLoad(t *testing.T) { dir, err := res.Path("data", "levels") if err != nil { t.Fatal(err) } for _, id := range []string{"007b-ueno", "008b-nippori"} { l, err := level.LoadFile(filepath.Join(dir, id+".json")) if err != nil || !l.IsLeg() { t.Fatalf("%s: %v", id, err) } } }