diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/casino/command_hints_test.go | 10 | ||||
| -rw-r--r-- | internal/casino/roulette.go | 21 | ||||
| -rw-r--r-- | internal/game/look_casino.go | 9 | ||||
| -rw-r--r-- | internal/game/look_casino_test.go | 13 | ||||
| -rw-r--r-- | internal/game/roulette_animation_test.go | 10 |
5 files changed, 42 insertions, 21 deletions
diff --git a/internal/casino/command_hints_test.go b/internal/casino/command_hints_test.go index bd15883..b391526 100644 --- a/internal/casino/command_hints_test.go +++ b/internal/casino/command_hints_test.go @@ -119,23 +119,23 @@ func TestOtherCasinoHintsRespectCommitment(t *testing.T) { r := newRouletteTable(1, TableConfig{ID: "r", Game: GameRoulette, BettingTicks: 10}, newTestRand()) r.join("a") r.join("b") - if hintHas(r.menuEvent("a").Menu.Commands, "bet / rebet") { + if hintHas(r.menuEvent("a", true).Menu.Commands, "bet / rebet") { t.Fatal("roulette no wager to repeat") } r.bet("a", "red", 10, &testWallet{chips: 1000}) - if hintBet(r.menuEvent("a").Menu.Commands) { + if hintBet(r.menuEvent("a", true).Menu.Commands) { t.Fatal("roulette duplicate bet advertised") } - if !hintBet(r.menuEvent("b").Menu.Commands) { + if !hintBet(r.menuEvent("b", true).Menu.Commands) { t.Fatal("roulette missing eligible bet") } r.phase = phasePlaying - if hintBet(r.menuEvent("b").Menu.Commands) { + if hintBet(r.menuEvent("b", true).Menu.Commands) { t.Fatal("roulette spinning bets advertised") } r.phase = phaseWaiting r.players.get("a").HasBet = false - if !hintHas(r.menuEvent("a").Menu.Commands, "bet / rebet") { + if !hintHas(r.menuEvent("a", true).Menu.Commands, "bet / rebet") { t.Fatal("roulette repeat missing") } } diff --git a/internal/casino/roulette.go b/internal/casino/roulette.go index 783d22f..8f45d49 100644 --- a/internal/casino/roulette.go +++ b/internal/casino/roulette.go @@ -510,7 +510,11 @@ func (t *rouletteTable) board(_ string) Snapshot { defer t.mu.Unlock() return t.view() } -func (t *rouletteTable) menuEvent(name string) Event { + +// menuEvent builds the next-spin menu. board controls whether it also carries +// the table graphic: sitting down and the live status keep it, settlement does +// not, so a resolved spin does not redraw the wheel, racetrack, and history. +func (t *rouletteTable) menuEvent(name string, board bool) Event { commands := []MenuCommand{} p := t.players.get(name) if t.phase != phasePlaying && p != nil && !p.HasBet { @@ -527,20 +531,22 @@ func (t *rouletteTable) menuEvent(name string) Event { commands = withTableHints(commands, t.wagerRows()) } e := t.betMenuEvent(name, commands) - e.Snapshot = t.view() + if board { + e.Snapshot = t.view() + } return e } func (t *rouletteTable) join(name string) []Event { t.mu.Lock() defer t.mu.Unlock() if t.players.has(name) { - return []Event{t.menuEvent(name)} + return []Event{t.menuEvent(name, true)} } if t.players.count() >= t.Config.MaxPlayers { return []Event{t.private(name, "That roulette table is full.")} } e := t.addSeatLocked(name, &roulettePlayer{Participant: Participant{Name: name, Connected: true}}) - return []Event{e, t.menuEvent(name)} + return []Event{e, t.menuEvent(name, true)} } func (t *rouletteTable) leave(name string) []Event { t.mu.Lock() @@ -715,7 +721,8 @@ func (t *rouletteTable) settle() []Event { wheel[(index+1)%n], wheel[(index+2)%n], } t.lastResult = s - e := t.event("The ball lands in {casino_emphasis}"+pocket+"{/}!", true) + // A leading blank line separates the reveal from the spinning strips above. + e := t.event("\nThe ball lands in {casino_emphasis}"+pocket+"{/}!", true) e.Snapshot = s events := []Event{e} for _, p := range t.players.ordered() { @@ -762,7 +769,7 @@ func (t *rouletteTable) settle() []Event { events = append(events, t.finishControlsLocked()...) for _, p := range t.players.ordered() { if p.Connected && !p.SittingOut { - events = append(events, t.menuEvent(p.Name)) + events = append(events, t.menuEvent(p.Name, false)) } } return events @@ -799,7 +806,7 @@ func (t *rouletteTable) rebet(name string) (string, int) { func (t *rouletteTable) status(name string) []Event { t.mu.Lock() defer t.mu.Unlock() - e := t.menuEvent(name) + e := t.menuEvent(name, true) events := []Event{e} if t.phase == phaseWaiting && t.lastResult != nil { last := t.private(name, "Last spin:") diff --git a/internal/game/look_casino.go b/internal/game/look_casino.go index 1e91ced..8d2b5c3 100644 --- a/internal/game/look_casino.go +++ b/internal/game/look_casino.go @@ -66,7 +66,8 @@ func (g *Game) resolveRoomTable(sess *net.Session, target string, game casino.Ga } // renderCasinoTableDetails formats the details block and, for the felt games, -// the per-table rule card. It never prints the craps or roulette board. +// the per-table rule card. Roulette adds its live wheel; craps never prints a +// board here. func (g *Game) renderCasinoTableDetails(sess *net.Session, cfg casino.TableConfig, table *casino.Table) string { seats := table.Seats() seated := "no one" @@ -82,6 +83,12 @@ func (g *Game) renderCasinoTableDetails(sess *net.Session, cfg casino.TableConfi fmt.Sprintf(" Seated %s", seated), } switch cfg.Game { + case casino.GameRoulette: + // Look, like sitting down and the table command, shows the live + // wheel: tracker, racetrack, and betting layout. + if rendered := strings.TrimRight(g.renderCasinoSnapshot(sess, table.Board(sess.Player.Name)), "\n"); rendered != "" { + lines = append(lines, "", rendered) + } case casino.GameBlackjack: lines = append(lines, "", g.renderCasinoSnapshot(sess, &casino.BlackjackRulesView{Config: cfg})) case casino.GameBaccarat: diff --git a/internal/game/look_casino_test.go b/internal/game/look_casino_test.go index 37205cd..651c74f 100644 --- a/internal/game/look_casino_test.go +++ b/internal/game/look_casino_test.go @@ -28,7 +28,7 @@ casino_tables: envy: true ` -func TestLookCasinoTableShowsDetailsWithoutInvariantBoards(t *testing.T) { +func TestLookCasinoTableDetailsAndBoards(t *testing.T) { g, sess, conn := casinoCrapsGame(t, lookCasinoRoom) // Craps: details only. The graphical felt and the full rule dump are gone. @@ -48,7 +48,7 @@ func TestLookCasinoTableShowsDetailsWithoutInvariantBoards(t *testing.T) { } } - // Roulette: details only, no betting board or payout reference. + // Roulette: details plus the live wheel (tracker, layout, and history). conn.data = nil if !g.lookRouletteTable(sess, "roulette") { t.Fatal("look roulette did not match") @@ -57,10 +57,11 @@ func TestLookCasinoTableShowsDetailsWithoutInvariantBoards(t *testing.T) { if !strings.Contains(text, "Roulette / Single zero") || !strings.Contains(text, "single-zero wheel") { t.Fatalf("look roulette missing variant summary: %s", text) } - for _, gone := range []string{"Roulette payouts", "History: last"} { - if strings.Contains(text, gone) { - t.Fatalf("look roulette showed invariant board %q: %s", gone, text) - } + if !strings.Contains(text, "History: last") { + t.Fatalf("look roulette missing the live board: %s", text) + } + if strings.Contains(text, "Roulette payouts") { + t.Fatalf("look roulette still dumps the payout reference: %s", text) } } diff --git a/internal/game/roulette_animation_test.go b/internal/game/roulette_animation_test.go index 96f14bb..d7f5626 100644 --- a/internal/game/roulette_animation_test.go +++ b/internal/game/roulette_animation_test.go @@ -46,8 +46,14 @@ func TestRouletteSpinOutputHasNoPromptOrBlankLines(t *testing.T) { } } g.CasinoTick() - if !strings.Contains(string(conn.data), "The ball lands in") || g.rouletteSpinning(sess) { - t.Fatal("result did not end animation") + result := strings.ReplaceAll(string(conn.data), "\r\n", "\n") + if !strings.HasPrefix(result, "\nThe ball lands in") || g.rouletteSpinning(sess) { + t.Fatalf("result did not start on a blank line or end animation: %q", result) + } + // A resolved spin shows the result and the next-spin menu, but not the + // tracker, racetrack, and history again. + if strings.Contains(result, "History: last") { + t.Fatalf("settlement redrew the table board: %q", result) } sess.ClearPrompt() conn.data = nil |
