All checks were successful
release / build-release (push) Successful in 1m36s
Ett tåg kan vara en buss (vehicle: bus): samma tidtabell, faser, bedömning i nätet och ombordstigning, men ett fordon på en väg i stället för vagnar på räls, hållplatsskylt, "1番" på tavlan, tuta vid inrullning och dörrsignal före avgång. Nätet har tre busslinjer från Narita. Nya sidobanor: 001b Flygbussterminalen (biljettbana med tre bussar) och 003b Harajuku (Yamanote åt rätt eller fel håll runt ringen). Overload-flaggan ger en rullande reklamticker och maximalt med affischer (Ueno, Tokyo). Romaji utan makroner (fonten saknar dem). Skärmdumparna som börjar på bankartan går via titelskärmen. Tester, docs och wiki uppdaterade. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
162 lines
5.6 KiB
Go
162 lines
5.6 KiB
Go
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")
|
||
}
|
||
}
|
||
|
||
// TestBusTerminalFlow: flygbussterminalen är en biljettbana med bussar –
|
||
// köp biljett till Shinjuku, gå till hållplats 1 och åk.
|
||
func TestBusTerminalFlow(t *testing.T) {
|
||
l := loadLevel(t, "001b-flygbussen")
|
||
if !l.TicketRequired || !l.Trains[0].IsBus() || l.Trains[0].Cars != 1 {
|
||
t.Fatal("001b ska vara en biljettbana med bussar")
|
||
}
|
||
r := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(3)})
|
||
r.Party.nextEventAt = 1e9
|
||
r.NPCs = nil
|
||
walk(t, r, pathTo(r, l.Find(level.TicketMachine)[0]))
|
||
r.Step(frame, None, true)
|
||
tk, ok := r.Mini.(*minigame.Ticket)
|
||
if !ok {
|
||
t.Fatalf("ingen biljettautomat (kind=%v)", r.MiniKind)
|
||
}
|
||
for tk.Options[tk.Cursor].Dest != "新宿" {
|
||
r.StepMini(frame, minigame.Input{Move: 1})
|
||
}
|
||
r.StepMini(frame, minigame.Input{Confirm: true})
|
||
if r.Wallet.Ticket != "新宿" {
|
||
t.Fatalf("biljett=%q", r.Wallet.Ticket)
|
||
}
|
||
tr := boardAt(t, r, level.Platform)
|
||
if !tr.IsBus() || tr.Verdict != nav.Direct {
|
||
t.Fatalf("hållplats 1: %v %v", tr.IsBus(), tr.Verdict)
|
||
}
|
||
ride(t, r)
|
||
if r.Outcome != Won {
|
||
t.Fatalf("outcome=%v (%s)", r.Outcome, r.LostReason)
|
||
}
|
||
}
|