M9b: folk, filer, reklam, sorl och vagnval
All checks were successful
release / build-release (push) Successful in 1m33s

Folk på stationen (NPC:er som vandrar, kliver av tåg och går mot
utgången, blockerar spelaren men inte varandra), vänster-/högerfiler
som bromsar på fel sida, bredare gångar, reklamaffischer (clutter),
japanskt sorl i loop (noise), vagnval med kvinnovagn (stort avdrag med
man i sällskapet, tidsstyrd), kömarkeringar på perrongen, trängsel per
vagn (bonus för tom vagn, svettig-debuff och svårare resa i full vagn,
släpp förbi passagerare) och att folk kliver av innan man får gå på.
Ny bana 005 Ueno. Skärmdumpsverktyget kan filtrera på bana/skript.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 12:33:44 +02:00
parent 8fca14e42d
commit de61568e44
49 changed files with 1568 additions and 136 deletions

View File

@@ -25,16 +25,58 @@ func fillSeats(r *Run, tk *minigame.Ticket) {
}
}
// pickCar väljer vagn: låter folk kliva av, väljer sedan den lugnaste vagnen
// som inte är kvinnovagn. Hanterar också ett ev. trängselminispel.
func pickCar(t *testing.T, r *Run) {
t.Helper()
g, ok := r.Mini.(*minigame.CarPick)
if !ok {
t.Fatalf("ingen vagnval pågår (kind=%v)", r.MiniKind)
}
for g.Alighting > 0 {
r.StepMini(frame, minigame.Input{})
}
best := -1
for i, c := range g.Cars {
if c.WomenOnly {
continue
}
if best < 0 || c.Crowd < g.Cars[best].Crowd {
best = i
}
}
if best < 0 {
best = 0
}
for g.Cursor != best {
r.StepMini(frame, minigame.Input{MoveX: 1})
}
r.StepMini(frame, minigame.Input{Confirm: true})
if cr, ok := r.Mini.(*minigame.Crowd); ok {
for i := 0; i < 600 && !cr.InZone(); i++ {
r.StepMini(frame, minigame.Input{})
}
r.StepMini(frame, minigame.Input{Confirm: true})
}
}
// ride spelar tågresan: kliver av när tåget står vid resmålet. På fel tåg
// finns inget resmål, så resan går till slutstationen.
// finns inget resmål, så resan går till slutstationen. Släpper förbi folk.
func ride(t *testing.T, r *Run) {
t.Helper()
if r.MiniKind == MiniCarPick {
pickCar(t, r)
}
if r.MiniKind != MiniRide {
t.Fatalf("ingen tågresa pågår (kind=%v)", r.MiniKind)
}
for i := 0; i < 60*120 && r.Mini != nil; i++ {
g := r.Mini.(*minigame.Ride)
r.StepMini(frame, minigame.Input{Confirm: g.Index == g.Dest})
in := minigame.Input{Confirm: g.Index == g.Dest}
if g.NeedsLetPass() {
in.MoveX = -1
}
r.StepMini(frame, in)
}
if r.Mini != nil {
t.Fatal("resan tog aldrig slut")
@@ -137,7 +179,7 @@ func TestBotWinsLevel001(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
r := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
goal := l.Find(level.Platform)[0]
path := l.Path(r.Player.Tile, goal, r.CanEnter)
path := l.Path(r.Player.Tile, goal, r.PathCanEnter)
if path == nil {
t.Fatal("ingen väg till perrongen")
}
@@ -152,8 +194,8 @@ func TestBotWinsLevel001(t *testing.T) {
t.Fatal("rätt perrong ska ha rätt tåg")
}
r.Step(frame, None, true)
if r.Outcome != Ongoing || r.MiniKind != MiniRide {
t.Fatalf("att kliva på ska starta tågresan: outcome=%v kind=%v", r.Outcome, r.MiniKind)
if r.Outcome != Ongoing || r.MiniKind != MiniCarPick {
t.Fatalf("att kliva på ska börja med vagnvalet: outcome=%v kind=%v", r.Outcome, r.MiniKind)
}
ride(t, r)
if r.Outcome != Won || r.Boarded != tr {
@@ -180,7 +222,7 @@ func TestBotLosesOnWrongTrain(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
r := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
goal := l.Find(level.WrongPlatform)[0]
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, goal, r.PathCanEnter))
tr := waitForTrain(t, r)
r.Step(frame, None, true)
ride(t, r)
@@ -200,7 +242,7 @@ func TestGateNeedsCardBalance(t *testing.T) {
if r.DeniedAt < 0 {
t.Fatal("nekandet ska registreras för HUD:en")
}
if l.Path(r.Player.Tile, l.Find(level.Platform)[0], r.CanEnter) != nil {
if l.Path(r.Player.Tile, l.Find(level.Platform)[0], r.PathCanEnter) != nil {
t.Fatal("utan saldo ska perrongen vara onåbar")
}
r.Wallet.Card = l.FareYen
@@ -220,7 +262,7 @@ func TestBoardOnlyWhenTrainPresent(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
r := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
goal := l.Find(level.Platform)[0]
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, goal, r.PathCanEnter))
// Spola fram till en tidpunkt då tåget är borta.
for {
_, at, _ := r.PlatformHere()
@@ -239,7 +281,7 @@ func TestSignHereOnlyWhenStill(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
r := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
sign := l.Find(level.Sign)[0]
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, sign, r.PathCanEnter))
if sg := r.SignHere(); sg == nil || sg.Text.Get("sv") == "" {
t.Fatal("stilla på skyltrutan ska ge skylten")
}
@@ -256,7 +298,7 @@ func TestAskHelpRevealsSignAndTeachesWord(t *testing.T) {
know := lang.NewKnowledge()
r := NewRun(l, RunOptions{Dict: loadDict(t), Know: know, Seed: rng.FromInt(1)})
sign := l.Find(level.Sign)[1]
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, sign, r.PathCanEnter))
if !r.AskHelp() {
t.Fatal("ska kunna fråga om hjälp på en skylt")
@@ -296,7 +338,7 @@ func TestHelpIsCancelledByWalkingAway(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
r := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(1)})
sign := l.Find(level.Sign)[1]
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, sign, r.PathCanEnter))
r.AskHelp()
r.Step(frame, Left, false)
if r.Help.Active {
@@ -344,7 +386,7 @@ func TestGoodResultTeachesDestination(t *testing.T) {
}{{"snabb", false, 3, true}, {"långsam", true, 1, false}} {
know := lang.NewKnowledge()
r := NewRun(l, RunOptions{Dict: loadDict(t), Know: know, Seed: rng.FromInt(2)})
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.PathCanEnter))
waitForTrain(t, r)
if tc.slow {
r.Time = 10 * r.ParSec()
@@ -374,7 +416,7 @@ func TestReadingGivesSeededProgress(t *testing.T) {
play := func(seed uint64) *lang.Knowledge {
know := lang.NewKnowledge()
r := NewRun(l, RunOptions{Dict: loadDict(t), Know: know, Seed: rng.FromInt(seed)})
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, sign, r.PathCanEnter))
for i := 0; i < 15; i++ { // gå av och på skylten = nya besök
walk(t, r, []level.Point{sign, left})
walk(t, r, []level.Point{left, sign})
@@ -414,7 +456,7 @@ func TestToiletAndKioskInRun(t *testing.T) {
// Toalett: tar ToiletSecPerMember per person, avbryts om man går.
toilet := l.Find(level.Toilet)[0]
walk(t, r, l.Path(r.Player.Tile, toilet, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, toilet, r.PathCanEnter))
anna.Meters.Toilet = 10
anna.Urge = NeedToilet
r.Step(frame, None, true)
@@ -428,7 +470,7 @@ func TestToiletAndKioskInRun(t *testing.T) {
for r.Player.Moving() { // gå klart rutan innan vi vänder tillbaka
r.Step(frame, None, false)
}
walk(t, r, l.Path(r.Player.Tile, toilet, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, toilet, r.PathCanEnter))
r.Step(frame, None, true)
if !r.Facility.Active {
t.Fatal("besöket ska starta igen")
@@ -442,7 +484,7 @@ func TestToiletAndKioskInRun(t *testing.T) {
// Kiosk: direkt, kostar KioskPriceYen per person från kortet.
kiosk := l.Find(level.Kiosk)[0]
walk(t, r, l.Path(r.Player.Tile, kiosk, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, kiosk, r.PathCanEnter))
anna.Meters.Drink = 5
anna.Urge = Thirsty
before := r.Wallet.Card
@@ -471,9 +513,10 @@ func TestRideWrongOrMissedStopLoses(t *testing.T) {
reason string
}{{"fel station", true, LostWrongStop}, {"åker förbi", false, LostMissedStop}} {
r := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(1)})
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.PathCanEnter))
waitForTrain(t, r)
r.Step(frame, None, true)
pickCar(t, r)
g := r.Mini.(*minigame.Ride)
if g.Dest < 1 {
t.Fatalf("resmålet ska ligga på linjen men inte först: %d", g.Dest)
@@ -500,7 +543,7 @@ func TestTopUpFlowAtMachine(t *testing.T) {
t.Fatal("tomt kort stoppas")
}
machine := l.Find(level.TicketMachine)[0]
walk(t, r, l.Path(r.Player.Tile, machine, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, machine, r.PathCanEnter))
r.Step(frame, None, true)
g, ok := r.Mini.(*minigame.TopUp)
if !ok || r.MiniKind != MiniTopUp {
@@ -548,7 +591,7 @@ func TestTicketLevelFlow(t *testing.T) {
t.Fatal("utan biljett stoppar spärren även med kort")
}
machine := l.Find(level.TicketMachine)[0]
walk(t, r, l.Path(r.Player.Tile, machine, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, machine, r.PathCanEnter))
// Fel biljett först: tåget vägrar.
r.Step(frame, None, true)
@@ -563,7 +606,7 @@ func TestTicketLevelFlow(t *testing.T) {
t.Fatalf("biljett=%q cash=%d", r.Wallet.Ticket, r.Wallet.Cash)
}
goal := l.CorrectPlatform()
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, goal, r.PathCanEnter))
waitForTrain(t, r)
r.Step(frame, None, true)
if r.Mini != nil || r.FlashKey != FlashWrongTicket || r.Outcome != Ongoing {
@@ -571,7 +614,7 @@ func TestTicketLevelFlow(t *testing.T) {
}
// Rätt biljett.
walk(t, r, l.Path(r.Player.Tile, machine, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, machine, r.PathCanEnter))
r.Step(frame, None, true)
tk = r.Mini.(*minigame.Ticket)
for tk.Options[tk.Cursor].Dest != "渋谷" {
@@ -582,9 +625,17 @@ func TestTicketLevelFlow(t *testing.T) {
if r.Wallet.Ticket != "渋谷" {
t.Fatalf("biljett=%q", r.Wallet.Ticket)
}
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, goal, r.PathCanEnter))
waitForTrain(t, r)
r.Step(frame, None, true)
cp, ok := r.Mini.(*minigame.CarPick)
if !ok {
t.Fatalf("ombordstigning börjar med vagnval, fick %T", r.Mini)
}
for cp.Alighting > 0 {
r.StepMini(frame, minigame.Input{})
}
r.StepMini(frame, minigame.Input{Confirm: true})
cr, ok := r.Mini.(*minigame.Crowd)
if !ok {
t.Fatalf("fullt tåg ska ge trängselminispel, fick %T", r.Mini)
@@ -608,9 +659,14 @@ func TestCrowdFailureMissesTrain(t *testing.T) {
r.Party.nextEventAt = 1e9
r.Wallet.Ticket = "渋谷"
goal := l.CorrectPlatform()
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, goal, r.PathCanEnter))
waitForTrain(t, r)
r.Step(frame, None, true)
cp := r.Mini.(*minigame.CarPick)
for cp.Alighting > 0 {
r.StepMini(frame, minigame.Input{})
}
r.StepMini(frame, minigame.Input{Confirm: true})
cr := r.Mini.(*minigame.Crowd)
for n := 0; n < CrowdTries; n++ {
for cr.InZone() {
@@ -641,7 +697,7 @@ func TestPhoneSpeedRescanAndBattery(t *testing.T) {
r.Step(frame, None, false)
}
gate := l.Find(level.Gate)[0]
path := l.Path(r.Player.Tile, gate, r.CanEnter)
path := l.Path(r.Player.Tile, gate, r.PathCanEnter)
for i, p := range path { // stanna på den första spärrutan – där sker skanningen
if l.At(p) == level.Gate {
path = path[:i+1]
@@ -669,7 +725,7 @@ func TestPhoneSpeedRescanAndBattery(t *testing.T) {
r := NewRun(l, RunOptions{Seed: rng.FromInt(5)})
r.Party.nextEventAt = 1e9
r.Wallet.Phone = true
walk(t, r, l.Path(r.Player.Tile, l.Find(level.Gate)[0], r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, l.Find(level.Gate)[0], r.PathCanEnter))
return r.Rescans
}
if run() != run() {
@@ -720,12 +776,12 @@ func TestScoreBreakdown(t *testing.T) {
l := loadLevel(t, "001-flygplatsen")
r := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(2)})
r.Party.nextEventAt = 1e9
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.PathCanEnter))
waitForTrain(t, r)
r.Step(frame, None, true)
ride(t, r)
sc := r.Score
if r.Outcome != Won || sc.Total <= 0 || sc.Total != sc.Time+sc.Money+sc.Meters+sc.Explore-sc.Penalty {
if r.Outcome != Won || sc.Total <= 0 || sc.Total != sc.Time+sc.Money+sc.Meters+sc.Explore+sc.Car-sc.Penalty {
t.Fatalf("score=%+v", sc)
}
if sc.Time != ScoreTimeMax || sc.Money != ScoreMoneyMax-l.FareYen/10 || sc.Penalty != 0 {
@@ -756,7 +812,7 @@ func TestDebuffsAffectRunAndAreEarned(t *testing.T) {
for r.Player.Moving() {
r.Step(frame, None, false)
}
walk(t, r, l.Path(r.Player.Tile, l.Find(level.Sign)[1], r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, l.Find(level.Sign)[1], r.PathCanEnter))
r.AskHelp()
if want := lang.HelpSeconds(0) / 0.7; math.Abs(r.Help.Total-want) > 1e-9 {
t.Fatalf("hjälptid med dåligt fokus %.2f, vill ha %.2f", r.Help.Total, want)
@@ -794,7 +850,7 @@ func TestIndividualCardsAtGateAndMachine(t *testing.T) {
t.Fatal("utan saldo på Annas kort stoppar spärren alla")
}
// Fyll på Annas kort i automaten (personval först).
walk(t, r, l1.Path(r.Player.Tile, l1.Find(level.TicketMachine)[0], r.CanEnter))
walk(t, r, l1.Path(r.Player.Tile, l1.Find(level.TicketMachine)[0], r.PathCanEnter))
r.Step(frame, None, true)
g := r.Mini.(*minigame.TopUp)
if g.Stage != minigame.TopUpPickMember || len(g.Members) != 2 {
@@ -816,7 +872,7 @@ func TestIndividualCardsAtGateAndMachine(t *testing.T) {
}
// Spärren drar från allas kort.
before := r.Wallet.Spent
path := l1.Path(r.Player.Tile, gate, r.CanEnter)
path := l1.Path(r.Player.Tile, gate, r.PathCanEnter)
for i, p := range path {
if l1.At(p) == level.Gate {
path = path[:i+1]
@@ -836,7 +892,7 @@ func TestLevel003Flow(t *testing.T) {
r := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(21)})
r.Party.nextEventAt = 1e9
machine := l.Find(level.TicketMachine)[0]
walk(t, r, l.Path(r.Player.Tile, machine, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, machine, r.PathCanEnter))
r.Step(frame, None, true)
tk := r.Mini.(*minigame.Ticket)
if len(tk.Members) != 4 || tk.Cash != StartCashYen/4 {
@@ -896,15 +952,10 @@ func TestLevel003Flow(t *testing.T) {
t.Fatal("missnöje ska flashas")
}
goal := l.CorrectPlatform()
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, goal, r.PathCanEnter))
waitForTrain(t, r)
r.Step(frame, None, true)
cr := r.Mini.(*minigame.Crowd)
for i := 0; i < 600 && !cr.InZone(); i++ {
r.StepMini(frame, minigame.Input{})
}
r.StepMini(frame, minigame.Input{Confirm: true})
ride(t, r)
ride(t, r) // vagnval, ev. trängsel, resa
if r.Outcome != Won || r.Wallet.TicketCount != 0 {
t.Fatalf("outcome=%v reason=%q tickets=%d", r.Outcome, r.LostReason, r.Wallet.TicketCount)
}
@@ -917,7 +968,7 @@ func TestNearTrackAndPhases(t *testing.T) {
if _, _, _, _, _, ok := r.NearTrack(2); ok {
t.Fatal("från starten är ingen perrong nära")
}
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.CanEnter))
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.PathCanEnter))
tr, p, phase, prog, dist, ok := r.NearTrack(2)
if !ok || dist != 0 || p != l.CorrectPlatform() || !tr.Correct || prog < 0 || prog > 1 {
t.Fatalf("ok=%v dist=%d p=%v", ok, dist, p)
@@ -987,7 +1038,7 @@ func TestRunOptionsFromProgression(t *testing.T) {
for r.Player.Moving() {
r.Step(frame, None, false)
}
path := l.Path(r.Player.Tile, gate, r.CanEnter)
path := l.Path(r.Player.Tile, gate, r.PathCanEnter)
for i, p := range path {
if l.At(p) == level.Gate {
path = path[:i+1]
@@ -1055,7 +1106,7 @@ func TestStairsMoveBetweenFloors(t *testing.T) {
// Hela vägen till perrongen med BFS över våningar. Man står i trappans
// nedre ände, så kliv av först – trappan tar en när man kliver PÅ den.
walk(t, r, []level.Point{r.Player.Tile, {X: 3, Y: 1, Z: 0}})
path := l.Path(r.Player.Tile, l.CorrectPlatform(), r.CanEnter)
path := l.Path(r.Player.Tile, l.CorrectPlatform(), r.PathCanEnter)
if path == nil {
t.Fatal("ingen väg")
}
@@ -1064,3 +1115,71 @@ func TestStairsMoveBetweenFloors(t *testing.T) {
t.Fatalf("boten nådde inte perrongen: %v", r.Player.Tile)
}
}
func TestCarsWomenOnlyEarlyAndCrowd(t *testing.T) {
l := loadLevel(t, "005-ueno")
tr := &l.Trains[0]
if tr.Cars != 4 || tr.WomenOnlyCar != 1 || !tr.WomenOnlyActive(10) || tr.WomenOnlyActive(200) {
t.Fatal("005: fyra vagnar, vagn 1 kvinnovagn de första 150 s")
}
r := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(8)})
r.Party.nextEventAt = 1e9
r.NPCs = nil // folket ska inte stå i vägen för testboten
if !r.Party.HasMen() {
t.Fatal("tre i sällskapet: Du, Anna, Ville – Ville är man")
}
walk(t, r, l.Path(r.Player.Tile, l.CorrectPlatform(), r.PathCanEnter))
waitForTrain(t, r)
if r.Time >= 150 {
t.Skip("tåget kom efter rusningen i det här seedet")
}
r.Step(frame, None, true)
cp := r.Mini.(*minigame.CarPick)
if !cp.Cars[0].WomenOnly {
t.Fatal("vagn 1 ska vara kvinnovagn nu")
}
// För tidigt: folk kliver av först.
r.StepMini(frame, minigame.Input{Confirm: true})
for cp.Alighting > 0 {
r.StepMini(frame, minigame.Input{})
}
// Välj kvinnovagnen med en man i sällskapet: grova minus.
for cp.Cursor != 0 {
r.StepMini(frame, minigame.Input{MoveX: -1})
}
r.StepMini(frame, minigame.Input{Confirm: true})
if !r.WomenOnlyViolation || r.EarlyBoard != 1 || r.FlashKey != FlashWomenOnly || r.CarChosen == nil || r.CarChosen.Index != 1 {
t.Fatalf("violation=%v early=%d flash=%q car=%+v", r.WomenOnlyViolation, r.EarlyBoard, r.FlashKey, r.CarChosen)
}
if cr, ok := r.Mini.(*minigame.Crowd); ok { // full vagn -> trängsel
for i := 0; i < 600 && !cr.InZone(); i++ {
r.StepMini(frame, minigame.Input{})
}
r.StepMini(frame, minigame.Input{Confirm: true})
}
rd := r.Mini.(*minigame.Ride)
if rd.Crowd != r.CarChosen.Crowd || rd.StopSec != minigame.RideStopSec(rd.Crowd) {
t.Fatal("resan ska ärva vagnens trängsel")
}
ride(t, r)
if r.Outcome != Won {
t.Fatalf("outcome=%v reason=%q", r.Outcome, r.LostReason)
}
if r.Score.Penalty < PenaltyWomenOnly+PenaltyEarlyBoard {
t.Fatalf("avdrag=%d, vill ha minst %d", r.Score.Penalty, PenaltyWomenOnly+PenaltyEarlyBoard)
}
// Bonus för lugn vagn.
r2 := NewRun(l, RunOptions{Dict: loadDict(t), Seed: rng.FromInt(8)})
r2.Party.nextEventAt = 1e9
r2.NPCs = nil
walk(t, r2, l.Path(r2.Player.Tile, l.CorrectPlatform(), r2.PathCanEnter))
waitForTrain(t, r2)
r2.Step(frame, None, true)
ride(t, r2) // pickCar väljer lugnaste icke-kvinnovagnen
if r2.Outcome == Won && r2.CarChosen.Crowd == 0 && r2.Score.Car != ScoreCarBonus {
t.Fatalf("lugn vagn ska ge bonus: %+v", r2.Score)
}
if r2.WomenOnlyViolation || r2.Score.Penalty >= PenaltyWomenOnly {
t.Fatal("rätt vagn: inga grova minus")
}
}