Files
metro-puzzle-jp/internal/sim/nav_test.go
claude f762f9554e
All checks were successful
release / build-release (push) Successful in 1m34s
M9: navigeringslägen, linjenät och terminal
internal/nav med resources/data/network.json (76 stationer, 11 linjer,
Yamanote som ringlinje). Tåg bedöms från lineId + terminus mot banans
station/to: rätt, fel håll, för långt bort (fler än 12 stationer) eller
ingen trafik; correct räknas ut för nätkopplade banor, 1-4 rätta tåg.
Tågresan visar linjens riktiga stationer. Nya förlustorsaker "Fel håll"
och "För långt bort". Listläge (nästa tåg nere till vänster) och
skyltläge (+50 % poäng) växlas med N på bankartan och sparas; -mode.
Terminalen: biljettautomaten visar linjetavlan på nätbanor. Perronger
P p Q q R r. Bana 006 Ikebukuro med fyra perronger. Banorna 001-005
kopplade till nätet. Skript: waitover och pick calm; poängraden
radbryts; skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 13:07:36 +02:00

129 lines
4.5 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 sim
import (
"strings"
"testing"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/minigame"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/nav"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng"
)
// boardAt går till perrongen med tecknet tile, väntar in tåget, kliver på
// och väljer vagn; resan pågår när funktionen återvänder.
func boardAt(t *testing.T, r *Run, tile level.Tile) *level.Train {
t.Helper()
r.NPCs = nil // folket ska inte stå i vägen för testboten
r.Party.nextEventAt = 1e9
l := r.Level
path := l.Path(r.Player.Tile, l.Find(tile)[0], r.PathCanEnter)
if path == nil {
t.Fatalf("ingen väg till perrong %c", tile)
}
walk(t, r, path)
tr := waitForTrain(t, r)
r.Step(frame, None, true)
if r.MiniKind == MiniCarPick {
pickCar(t, r)
}
if r.MiniKind != MiniRide {
t.Fatalf("ingen tågresa (kind=%v)", r.MiniKind)
}
return tr
}
// TestNetworkRideUsesRealStops: på nätkopplade banor är tågresans stationer
// linjens riktiga stationer i färdriktningen, med resmålet på rätt plats.
func TestNetworkRideUsesRealStops(t *testing.T) {
l := loadLevel(t, "005-ueno")
if l.Net == nil {
t.Fatal("005 ska vara kopplad till nätet")
}
r := NewRun(l, RunOptions{Seed: rng.FromInt(5)})
tr := boardAt(t, r, level.Platform)
g := r.Mini.(*minigame.Ride)
if tr.Verdict != nav.Direct || g.Dest != 1 || g.Stops[0] != "御徒町" || g.Stops[1] != "秋葉原" || len(g.Stops) != l.RideStops {
t.Fatalf("Yamanote från Ueno: dest=%d stops=%v", g.Dest, g.Stops)
}
ride(t, r)
if r.Outcome != Won {
t.Fatalf("outcome=%v (%s)", r.Outcome, r.LostReason)
}
}
// TestWrongDirectionLoses: Keihin-Tohoku mot Omiya från Ueno går åt fel håll
// – resan visar stationerna norrut, resmålet kommer aldrig och banan förloras
// med orsaken "fel håll".
func TestWrongDirectionLoses(t *testing.T) {
l := loadLevel(t, "005-ueno")
r := NewRun(l, RunOptions{Seed: rng.FromInt(5)})
tr := boardAt(t, r, level.WrongPlatform)
g := r.Mini.(*minigame.Ride)
if tr.Verdict != nav.WrongDirection || g.Dest != -1 || g.Stops[0] != "鶯谷" {
t.Fatalf("Keihin-Tohoku mot Omiya: verdict=%v dest=%d stops=%v", tr.Verdict, g.Dest, g.Stops)
}
ride(t, r)
if r.Outcome != Lost || r.LostReason != LostWrongDirection {
t.Fatalf("outcome=%v reason=%q", r.Outcome, r.LostReason)
}
}
// TestTooFarLoses: Marunouchi från Ikebukuro når Shinjuku först via city –
// för långt bort.
func TestTooFarLoses(t *testing.T) {
l := loadLevel(t, "006-ikebukuro")
r := NewRun(l, RunOptions{Seed: rng.FromInt(6)})
if n := r.NextTrains(); len(n) != 2 {
t.Fatalf("006 ska ha två rätta tåg, har %d", len(n))
}
tr := boardAt(t, r, level.Platform3)
if tr.Verdict != nav.TooFar || tr.LineID != "marunouchi" {
t.Fatalf("q-perrongen: %v %s", tr.Verdict, tr.LineID)
}
ride(t, r)
if r.Outcome != Lost || r.LostReason != LostTooFar {
t.Fatalf("outcome=%v reason=%q", r.Outcome, r.LostReason)
}
}
// TestSignsModeBonus: skyltläget ger 50 % bonus på grundpoängen, listläget inte.
func TestSignsModeBonus(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
list := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
signs := NewRun(l, RunOptions{Seed: rng.FromInt(1), Mode: nav.Signs})
if list.Mode != nav.List || signs.Mode != nav.Signs {
t.Fatal("läget ska följa RunOptions")
}
for _, r := range []*Run{list, signs} {
r.Time = 30
r.computeScore()
}
base := list.Score.Time + list.Score.Money + list.Score.Meters + list.Score.Explore + list.Score.Car
if list.Score.Bonus != 0 || signs.Score.Bonus != base*ScoreSignsBonusPct/100 || signs.Score.Total != list.Score.Total+signs.Score.Bonus {
t.Fatalf("list=%+v signs=%+v", list.Score, signs.Score)
}
if n := list.NextTrains(); len(n) != 1 || !n[0].Correct {
t.Fatalf("NextTrains=%v", n)
}
}
// TestTerminalLines: terminalen listar linjerna vid stationen med grannstationer.
func TestTerminalLines(t *testing.T) {
r := NewRun(loadLevel(t, "005-ueno"), RunOptions{Seed: rng.FromInt(5)})
lines := r.TerminalLines()
if len(lines) < 3 {
t.Fatalf("Ueno har flera linjer, fick %v", lines)
}
joined := strings.Join(lines, "\n")
for _, want := range []string{"山手線: ", "御徒町", "鶯谷", "← 上野 →", "京浜東北線"} {
if !strings.Contains(joined, want) {
t.Errorf("terminalen saknar %q:\n%s", want, joined)
}
}
plain := NewRun(loadLevel(t, "002b-kabukicho"), RunOptions{Seed: rng.FromInt(1)})
if plain.TerminalLines() != nil || plain.Level.Net != nil {
t.Fatal("okopplad bana har ingen terminal")
}
}