M7: svårighet, tid, poäng, debuffar, egna kort och sittplatsval
All checks were successful
release / build-release (push) Successful in 1m37s
All checks were successful
release / build-release (push) Successful in 1m37s
- sim/stats.go: Stats (fart, törst, fokus, toalett, tur), debuffar som multiplikatorer, Profile som följer med mellan banor och räknar ned - sim.Run: tidsgräns och maxantal missade tåg förlorar banan; poängformel (tid mot par, pengar, sällskapets mätare, utforskat, avdrag); debuffar tjänas av många steg, lång tid, tom matmätare och seedad otur vid kiosken; stats påverkar fart, mätartakter, läsning och hjälptid - egna kort (individualCards): Member.Card, spärren kräver allas saldo och drar från alla, automaten frågar vems kort, biljetter köps åt alla - sittplatsval (seatSelection): Ticket får ett sittplatssteg med seedade önskemål (fönster/gång/sida/bredvid); missnöjda ger mätar- och poängavdrag - level: maxMissedTrains, individualCards, seatSelection; bana 003 Shibuya (svårighet 3, fyra i sällskapet) och 002 med tidsgräns och sittplatser; ordboken utökad; generatorn är spec-driven - game: tid kvar/missade i HUD, debuffar i meny/HUD/resultat, poäng och förlustorsaker på resultatet, vyer för personval och vagn, skript-pick, flaggan -debuffs; HUD och resultat komprimerade för fyra i sällskapet - plan (Björn 2026-10-24): flera våningar med trappor som M9a, egna kort och sittplatsval som svårighetsinställningar i M7 – i spelbeskrivning, roadmap, testplan - docs/README/wiki med sittplatsbilden Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -11,13 +11,13 @@ import (
|
||||
const frame = 1.0 / 60
|
||||
|
||||
func TestTopUpFlowAndDeterminism(t *testing.T) {
|
||||
g := NewTopUp(rng.FromInt(1), 5000)
|
||||
h := NewTopUp(rng.FromInt(1), 5000)
|
||||
g := NewTopUp(rng.FromInt(1), 5000, nil)
|
||||
h := NewTopUp(rng.FromInt(1), 5000, nil)
|
||||
if strings.Join(g.Buttons, ",") != strings.Join(h.Buttons, ",") {
|
||||
t.Fatal("samma seed ska ge samma knappordning")
|
||||
}
|
||||
if strings.Join(g.Buttons, ",") == strings.Join(NewTopUp(rng.FromInt(2), 5000).Buttons, ",") &&
|
||||
strings.Join(g.Buttons, ",") == strings.Join(NewTopUp(rng.FromInt(3), 5000).Buttons, ",") {
|
||||
if strings.Join(g.Buttons, ",") == strings.Join(NewTopUp(rng.FromInt(2), 5000, nil).Buttons, ",") &&
|
||||
strings.Join(g.Buttons, ",") == strings.Join(NewTopUp(rng.FromInt(3), 5000, nil).Buttons, ",") {
|
||||
t.Fatal("olika seeds ska (nästan alltid) ge olika ordning")
|
||||
}
|
||||
// Fel knapp: straff och frysning.
|
||||
@@ -37,7 +37,7 @@ func TestTopUpFlowAndDeterminism(t *testing.T) {
|
||||
}
|
||||
// Rätt knapp -> belopp -> mata sedlar.
|
||||
for g.Buttons[g.Cursor] != "チャージ" {
|
||||
g.Update(Input{Move: 1}, frame)
|
||||
g.Update(Input{MoveX: 1}, frame)
|
||||
}
|
||||
g.Update(Input{Confirm: true}, frame)
|
||||
if g.Stage != TopUpPickAmount {
|
||||
@@ -65,14 +65,14 @@ func TestTopUpFlowAndDeterminism(t *testing.T) {
|
||||
t.Fatalf("done=%v success=%v amount=%d", g.Done(), g.Success(), g.Amount)
|
||||
}
|
||||
// Kontanter begränsar beloppen; utan pengar är spelet över direkt.
|
||||
if a := NewTopUp(rng.FromInt(1), 2500).Amounts; len(a) != 2 || a[1] != 2000 {
|
||||
if a := NewTopUp(rng.FromInt(1), 2500, nil).Amounts; len(a) != 2 || a[1] != 2000 {
|
||||
t.Fatalf("amounts med 2500 kr: %v", a)
|
||||
}
|
||||
if !NewTopUp(rng.FromInt(1), 500).Done() {
|
||||
if !NewTopUp(rng.FromInt(1), 500, nil).Done() {
|
||||
t.Fatal("utan råd ska automaten vara klar direkt")
|
||||
}
|
||||
// Esc avbryter utan köp.
|
||||
c := NewTopUp(rng.FromInt(1), 5000)
|
||||
c := NewTopUp(rng.FromInt(1), 5000, nil)
|
||||
c.Update(Input{Cancel: true}, frame)
|
||||
if !c.Done() || c.Success() {
|
||||
t.Fatal("avbrutet = klart utan framgång")
|
||||
@@ -80,8 +80,8 @@ func TestTopUpFlowAndDeterminism(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTicketBoardSeededAndAffordable(t *testing.T) {
|
||||
g := NewTicket(rng.FromInt(4), "渋谷", []string{"東京", "上野", "新宿"}, 5000)
|
||||
h := NewTicket(rng.FromInt(4), "渋谷", []string{"東京", "上野", "新宿"}, 5000)
|
||||
g := NewTicket(rng.FromInt(4), "渋谷", []string{"東京", "上野", "新宿"}, 5000, nil)
|
||||
h := NewTicket(rng.FromInt(4), "渋谷", []string{"東京", "上野", "新宿"}, 5000, nil)
|
||||
if len(g.Options) != 4 {
|
||||
t.Fatalf("%d alternativ", len(g.Options))
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func TestTicketBoardSeededAndAffordable(t *testing.T) {
|
||||
t.Fatalf("köp: %+v", g.Chosen)
|
||||
}
|
||||
// Utan råd går det inte att köpa.
|
||||
p := NewTicket(rng.FromInt(4), "渋谷", []string{"東京"}, 100)
|
||||
p := NewTicket(rng.FromInt(4), "渋谷", []string{"東京"}, 100, nil)
|
||||
p.Update(Input{Confirm: true}, frame)
|
||||
if p.Done() {
|
||||
t.Fatal("utan råd ska köpet inte gå igenom")
|
||||
@@ -204,3 +204,92 @@ func TestRideWithoutFillersStillHasDestination(t *testing.T) {
|
||||
t.Fatalf("stops=%v dest=%d", g.Stops, g.Dest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopUpPicksMemberWithIndividualCards(t *testing.T) {
|
||||
g := NewTopUp(rng.FromInt(1), 5000, []string{"Du", "Anna", "Ville"})
|
||||
if g.Stage != TopUpPickMember {
|
||||
t.Fatal("med egna kort väljs person först")
|
||||
}
|
||||
g.Update(Input{Move: 1}, frame)
|
||||
g.Update(Input{Confirm: true}, frame)
|
||||
if g.Member != 1 || g.Stage != TopUpPickButton || g.Cursor != 0 {
|
||||
t.Fatalf("member=%d stage=%v", g.Member, g.Stage)
|
||||
}
|
||||
// 2×2-rutnätet: höger = +1, ner = +2.
|
||||
g.Update(Input{MoveX: 1}, frame)
|
||||
g.Update(Input{Move: 1}, frame)
|
||||
if g.Cursor != 3 {
|
||||
t.Fatalf("cursor=%d", g.Cursor)
|
||||
}
|
||||
if NewTopUp(rng.FromInt(1), 5000, []string{"Du"}).Stage != TopUpPickButton {
|
||||
t.Fatal("ensam = inget personval")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeatSelectionPreferences(t *testing.T) {
|
||||
members := []string{"Du", "Anna", "Ville", "Sara"}
|
||||
g := NewTicket(rng.FromInt(9), "渋谷", []string{"東京", "上野"}, 5000, members)
|
||||
h := NewTicket(rng.FromInt(9), "渋谷", []string{"東京", "上野"}, 5000, members)
|
||||
if len(g.Prefs) != 3 || len(g.Assigned) != 4 {
|
||||
t.Fatalf("prefs=%d assigned=%d", len(g.Prefs), len(g.Assigned))
|
||||
}
|
||||
for i := range g.Prefs {
|
||||
if g.Prefs[i] != h.Prefs[i] {
|
||||
t.Fatal("önskemålen ska vara seedade")
|
||||
}
|
||||
if g.Prefs[i].Kind == PrefWith && g.Prefs[i].With == g.Prefs[i].Member {
|
||||
t.Fatal("ingen vill sitta bredvid sig själv")
|
||||
}
|
||||
}
|
||||
// Köp resmålet -> sittplatssteget.
|
||||
for g.Options[g.Cursor].Dest != "渋谷" {
|
||||
g.Update(Input{Move: 1}, frame)
|
||||
}
|
||||
g.Update(Input{Confirm: true}, frame)
|
||||
if g.Done() || g.Stage != TicketPickSeat || g.Chosen == nil {
|
||||
t.Fatal("efter resmålet kommer sittplatserna")
|
||||
}
|
||||
// Placera alla på rad 0: plats 0,1,2,3 i ordning; upptagen plats går inte.
|
||||
g.Update(Input{Confirm: true}, frame) // Du på (0,0)
|
||||
g.Update(Input{Confirm: true}, frame) // upptagen: ignoreras
|
||||
if g.Current != 1 {
|
||||
t.Fatal("upptagen plats ska inte gå att välja")
|
||||
}
|
||||
for i := 1; i < 4; i++ {
|
||||
g.Update(Input{MoveX: 1}, frame)
|
||||
g.Update(Input{Confirm: true}, frame)
|
||||
}
|
||||
if !g.Done() || !g.Success() {
|
||||
t.Fatal("alla placerade = köpet klart")
|
||||
}
|
||||
// Facit: räkna ut vilka som borde vara missnöjda med placeringen rad 0 / plats i.
|
||||
var want []int
|
||||
for _, p := range g.Prefs {
|
||||
if !p.Satisfied(g.Assigned) {
|
||||
want = append(want, p.Member)
|
||||
}
|
||||
}
|
||||
if len(g.Unhappy) != len(want) {
|
||||
t.Fatalf("unhappy=%v want=%v (prefs %+v)", g.Unhappy, want, g.Prefs)
|
||||
}
|
||||
// Egenskaperna.
|
||||
if !(Seat{0, 0}).Window() || !(Seat{0, 3}).Window() || (Seat{0, 1}).Window() || !(Seat{0, 1}).Aisle() {
|
||||
t.Fatal("fönster/gång")
|
||||
}
|
||||
if !(Seat{0, 0}).Left() || !(Seat{0, 2}).Right() || !(Seat{1, 0}).Beside(Seat{1, 1}) || (Seat{1, 1}).Beside(Seat{1, 2}) || (Seat{0, 0}).Beside(Seat{1, 0}) {
|
||||
t.Fatal("sida/bredvid")
|
||||
}
|
||||
if (SeatPref{Member: 1, Kind: PrefWindow}).Satisfied([]Seat{{0, 0}, {-1, 0}}) {
|
||||
t.Fatal("oplacerad = inte nöjd")
|
||||
}
|
||||
// Esc under sittplatsvalet avbryter köpet.
|
||||
c := NewTicket(rng.FromInt(9), "渋谷", []string{"東京"}, 5000, members)
|
||||
for c.Options[c.Cursor].Dest != "渋谷" {
|
||||
c.Update(Input{Move: 1}, frame)
|
||||
}
|
||||
c.Update(Input{Confirm: true}, frame)
|
||||
c.Update(Input{Cancel: true}, frame)
|
||||
if !c.Done() || c.Success() {
|
||||
t.Fatal("avbrutet sittplatsval = inget köp")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user