Inlärning (framsteg, stjärnor, ordlista) och M4: lämmeltåget
All checks were successful
release / build-release (push) Successful in 1m49s
All checks were successful
release / build-release (push) Successful in 1m49s
Inlärning enligt Björns regler: - lang.Knowledge: framsteg per ord (3 läsningar = känt), språkpoäng, köp av valfria ord (kostnad = tier), Award per stjärna - sim.Run: RunOptions med seed; stjärnor 1–3 mot par (parSec eller kortaste vägen × 0,5 s + 20 s); ≥2 stjärnor lär resmålets ord; att stanna på skylt/perrong ger seedad 35 % chans till framsteg per okänt ord - game: ordlista (L) med kända ord, framsteg och kostnad; stjärnor, poäng och inlärt resmål på resultatskärmen; framstegsnotis; flaggor -points/-seed M4 lämmeltåget: - sim.Party: medlemmar med mat/dryck/toa-mätare, behov under tröskel, seedade händelser var 45 s / antal (toa, törst, hunger, distraktion), fartstraff, toalett (W, 3 s/person) och kiosk (K, 150 ¥/person), följare på ledarens spår - level: rutor W och K, party och parSec i bandatan; banor med sällskap måste ha toalett och kiosk (testas); 001 har 2, 002 har 3 i sällskapet - game: följare med färgton, behovsmarkering över huvudet, mätarpanel, facilitetsrutor med förlopp; genart ritar toalett och kiosk - skriptläget: move stannar exakt på målrutan även när farten inte är bildruteexakt (sällskapets fartstraff), stall-skydd mot väggar - docs/README/wiki/spelbeskrivning uppdaterade Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
143
internal/sim/party_test.go
Normal file
143
internal/sim/party_test.go
Normal file
@@ -0,0 +1,143 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user