package sim import ( "strings" "testing" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level" "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng" ) func TestMetersDecayAtRates(t *testing.T) { p := NewParty(2, pt(1, 1), rng.FromInt(1)) for i := 0; i < 60*60; i++ { // 60 s p.Advance(frame, float64(i)*frame) } m := p.Members[1].Meters for _, c := range []struct { name string got float64 rate float64 }{{"mat", m.Food, FoodPerSec}, {"dryck", m.Drink, DrinkPerSec}, {"toa", m.Toilet, ToiletPerSec}} { if want := 100 - c.rate*60; c.got > want+0.5 || c.got < want-0.5 { t.Errorf("%s = %.1f, vill ha ~%.1f", c.name, c.got, want) } } } // TestMoreMembersMoreEvents: fler i lämmeltåget ger fler händelser. func TestMoreMembersMoreEvents(t *testing.T) { count := func(n int) int { p := NewParty(n, pt(1, 1), rng.FromInt(3)) for i := 0; i < 60*600; i++ { // 10 min p.Advance(frame, float64(i)*frame) } return p.Events } one, four := count(1), count(4) if one < 8 || one > 20 { t.Fatalf("ensam: %d händelser på 10 min (väntat ~13)", one) } if four < 3*one { t.Fatalf("fyra i sällskapet (%d) ska ge klart fler händelser än en (%d)", four, one) } } func TestEventsAreSeeded(t *testing.T) { run := func(seed uint64) []string { p := NewParty(3, pt(1, 1), rng.FromInt(seed)) for i := 0; i < 60*300; i++ { p.Advance(frame, float64(i)*frame) } return p.Log } a, b := run(9), run(9) if strings.Join(a, "|") != strings.Join(b, "|") { t.Fatal("samma seed ska ge samma händelser") } if strings.Join(a, "|") == strings.Join(run(10), "|") { t.Fatal("olika seeds ska ge olika händelser") } if len(a) == 0 { t.Fatal("inga händelser på fem minuter") } } func TestUrgesSlowAndFacilitiesRestore(t *testing.T) { p := NewParty(3, pt(1, 1), rng.FromInt(1)) if p.SpeedFactor() != 1 { t.Fatal("utan behov full fart") } anna, ville := p.Members[1], p.Members[2] anna.setUrge(NeedToilet, 0) anna.Meters.Toilet = 10 if p.SpeedFactor() != UrgeSpeedFactor || len(p.NeedsToilet()) != 1 { t.Fatalf("toabehov: fart=%.2f behöver=%d", p.SpeedFactor(), len(p.NeedsToilet())) } if n := p.UseToilet(); n != 1 || anna.Urge != NoUrge || anna.Meters.Toilet != 100 || p.SpeedFactor() != 1 { t.Fatalf("toaletten: n=%d urge=%v toa=%.0f", n, anna.Urge, anna.Meters.Toilet) } ville.setUrge(Thirsty, 0) ville.Meters.Drink = 5 anna.Meters.Food = 40 // under RestoreBelow men utan behov – kiosken hjälper ändå paid := 0 pay := func(yen int) bool { paid += yen; return true } if n := p.BuyAtKiosk(pay); n != 2 || paid != 2*KioskPriceYen || ville.Urge != NoUrge || ville.Meters.Drink != 100 || anna.Meters.Food != 100 { t.Fatalf("kiosken: n=%d betalt=%d", n, paid) } // Slut på pengar: ingen försörjs. ville.Meters.Drink = 5 if n := p.BuyAtKiosk(func(int) bool { return false }); n != 0 || ville.Meters.Drink != 5 { t.Fatal("utan pengar ska ingen få något") } ville.Meters.Drink = 100 // annars uppstår törsten igen när distraktionen går över p.nextEventAt = 1e9 // inga slumphändelser i resten av testet ville.setUrge(Distracted, 100) if p.SpeedFactor() != DistractedSpeedFactor { t.Fatal("distraktion halverar farten") } p.Advance(frame, 100+DistractionSec) if ville.Urge != NoUrge { t.Fatal("distraktionen ska gå över av sig själv") } ville.Meters.Food = 0 if p.SpeedFactor() != EmptySpeedFactor { t.Fatal("tom mätare ger stor fartstraff") } } func TestUrgeArisesBelowThreshold(t *testing.T) { p := NewParty(1, pt(1, 1), rng.FromInt(1)) p.Members[0].Meters.Drink = UrgeThreshold - 1 p.Advance(frame, 0) if p.Members[0].Urge != Thirsty { t.Fatalf("urge=%v", p.Members[0].Urge) } } func TestFollowersTrailLeader(t *testing.T) { p := NewParty(3, pt(1, 1), rng.FromInt(1)) for _, tile := range []level.Point{pt(2, 1), pt(3, 1), pt(3, 2)} { p.OnLeaderArrived(tile) } p.OnLeaderArrived(pt(3, 2)) // samma ruta igen: ingen dubblett if from, to := p.FollowerTiles(1); from != pt(3, 1) || to != pt(3, 2) { t.Fatalf("följare 1: %v -> %v", from, to) } if from, to := p.FollowerTiles(2); from != pt(2, 1) || to != pt(3, 1) { t.Fatalf("följare 2: %v -> %v", from, to) } // Vid start står alla på startrutan. q := NewParty(3, pt(5, 5), rng.FromInt(1)) if from, to := q.FollowerTiles(2); from != pt(5, 5) || to != pt(5, 5) { t.Fatalf("start: %v -> %v", from, to) } } func TestPartySizeIsClamped(t *testing.T) { if NewParty(0, pt(0, 0), rng.FromInt(1)).Size() != 1 || NewParty(99, pt(0, 0), rng.FromInt(1)).Size() != len(PartyNames) { t.Fatal("storleken ska klampas") } }