Files
metro-puzzle-jp/internal/sim/nav_test.go
claude f04d6a88f9
All checks were successful
release / build-release (push) Successful in 2m7s
M10c: vem betalar i kiosken och spärrar utan återvändo
Björns tillägg 2026-10-24. Med egna kort och fler i sällskapet frågar
kiosken vem som betalar (minigame.Payer: kontant, spelarens kort eller en
följares kort; den som inte har råd nekas, Esc avbryter). Avgiften dras
nu när man passerar spärren in i det betalda området (level.InsideGates),
inte på spärrrutan. Banflaggan oneWayGates: incheckad släpps inte ut igen
förrän man åkt; Enter vid spärren kallar på personal (10 s) som låser upp
korten; ny inpassering kostar igen. På i 005, 007/007b och 008/008b.
Skriptkommandon urge och push. Docs, wiki och skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 22:12:13 +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 := pathTo(r, l.Find(tile)[0])
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")
}
}