aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cash_hunt_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-04 22:54:38 -0400
committerhistoria <[not public]>2026-10-04 22:54:38 -0400
commit8b78aa4cd930fde145959704ba7bdc0e57465b9b (patch)
treee9f3b5fe691084514359fb420cb57f5e41791a3d /internal/game/cash_hunt_test.go
parentc5ee77c345b4cafe10cf29b6c17a69e78cc5c630 (diff)
downloadthehouseoficarus-8b78aa4cd930fde145959704ba7bdc0e57465b9b.tar.gz
feat: better ux in cash hunt
Diffstat (limited to 'internal/game/cash_hunt_test.go')
-rw-r--r--internal/game/cash_hunt_test.go114
1 files changed, 96 insertions, 18 deletions
diff --git a/internal/game/cash_hunt_test.go b/internal/game/cash_hunt_test.go
index 65ef16f..78d3e38 100644
--- a/internal/game/cash_hunt_test.go
+++ b/internal/game/cash_hunt_test.go
@@ -25,7 +25,7 @@ func TestCashHuntCommandsAndReceipt(t *testing.T) {
g.executeBet(s, []string{"1"}, "bet 1")
text := string(conn.data)
preview, hidden := strings.Index(text, "PRIZE PREVIEW"), strings.Index(text, "hidden and mixed")
- if preview < 0 || hidden <= preview || !strings.Contains(text, " 100 ") || len(s.Player.PendingWagers) != 1 {
+ if preview < 0 || hidden <= preview || !strings.Contains(text, cashHuntCenter("100", cashHuntTileWidth)) || len(s.Player.PendingWagers) != 1 {
t.Fatalf("preview/shuffle sequence missing: %s", text)
}
if table.Busy(s.Player.Name) {
@@ -114,32 +114,47 @@ func TestCashHuntBoardRendering(t *testing.T) {
if phase == "result" {
s.Selected = 100
}
- rendered := renderCashHunt(s, mode, unicode)
+ rendered := renderCashHunt(s, mode, unicode, "98%")
if mode == "none" && strings.Contains(rendered, "\x1b") {
t.Fatal("plain board has ANSI escapes")
}
lines := strings.Split(stripANSI(rendered), "\n")
- if len(lines) != 22 {
- t.Fatalf("want two headers and 20 tile lines, got %d", len(lines))
+ // One subtitle, a header top border + header row, a grid top
+ // separator, ten rows of (number, prize, separator), and a bottom
+ // border.
+ if len(lines) != 34 {
+ t.Fatalf("want a headed bordered 10x10 grid, got %d lines", len(lines))
}
- for _, line := range lines[2:] {
- if utf8.RuneCountInString(line) != 70 {
- t.Fatalf("tile row is not 70 columns: %q", line)
+ for _, line := range lines[1:] {
+ if utf8.RuneCountInString(line) != cashHuntBoardWidth {
+ t.Fatalf("grid line is not %d columns: %q", cashHuntBoardWidth, line)
}
}
- board := strings.Join(lines[2:], "\n")
+ board := strings.Join(lines[1:], "\n")
if strings.ContainsAny(board, "[]") {
t.Fatalf("square numbers must not use brackets: %q", board)
}
+ header := lines[2]
+ if !strings.Contains(header, "HARD 98%") || !strings.Contains(header, "CASH HUNT") || !strings.Contains(header, "TOP PRIZE: 50000x") {
+ t.Fatalf("header row wrong: %q", header)
+ }
+ if unicode {
+ // The header is one merged cell, so its bottom border meets the
+ // grid columns with down-T junctions, not crosses.
+ if !strings.Contains(lines[3], "┬") || strings.Contains(lines[3], "┼") {
+ t.Fatalf("grid top border should use down-T junctions: %q", lines[3])
+ }
+ }
for row := 0; row < 10; row++ {
- numberLine := lines[2+2*row]
+ numberRunes := []rune(lines[4+3*row])
for col := 0; col < 10; col++ {
square := row*10 + col + 1
label := fmt.Sprintf("%d", square)
if square == s.Selected {
label = fmt.Sprintf(">%d<", square)
}
- got := numberLine[col*cashHuntTileWidth : (col+1)*cashHuntTileWidth]
+ start := 1 + col*(cashHuntTileWidth+1)
+ got := string(numberRunes[start : start+cashHuntTileWidth])
if want := cashHuntCenter(label, cashHuntTileWidth); got != want {
t.Fatalf("square %d label: got %q want %q", square, got, want)
}
@@ -156,23 +171,86 @@ func TestCashHuntBoardRendering(t *testing.T) {
}
}
if mode == "xterm256" {
- // Dark rainbow columns replace the old black/blue checker.
- if strings.Contains(rendered, "\x1b[48;5;16m") || strings.Contains(rendered, "\x1b[48;5;17m") {
- t.Fatal("black/blue tile background survived")
+ // The whole board is black; the box-drawing grid supplies the
+ // separation.
+ if !strings.Contains(rendered, color.BgCode("xterm256", cashHuntTileBlack)) {
+ t.Fatal("missing black tile background")
}
- for _, shade := range cashHuntTileColors {
- if !strings.Contains(rendered, color.BgCode("xterm256", shade)) {
- t.Fatalf("missing dark rainbow shade %d", shade)
+ if strings.Contains(rendered, color.BgCode("xterm256", 234)) {
+ t.Fatal("checkerboard gray should be gone")
+ }
+ for _, shade := range []int{88, 94, 100, 28, 29, 30, 24, 18, 90, 89} {
+ if strings.Contains(rendered, color.BgCode("xterm256", shade)) {
+ t.Fatalf("rainbow tile shade %d should be gone", shade)
}
}
+ if !strings.Contains(rendered, color.FgCode("xterm256", 244)) {
+ t.Fatal("square numbers should be dimmer than the header")
+ }
+ if !strings.Contains(rendered, color.FgCode("xterm256", cashHuntBoxFg)) {
+ t.Fatal("grid characters should be darkened")
+ }
+ // 0x is not a prize: it stays dim and is never bold.
+ if !strings.Contains(board, cashHuntCenter("0x", cashHuntTileWidth)) {
+ t.Fatal("0x prize missing")
+ }
+ if strings.Contains(rendered, "\x1b[1m"+color.FgCode("xterm256", 237)) {
+ t.Fatal("0x should not render bold")
+ }
+ if !strings.Contains(rendered, "\x1b[1m"+color.FgCode("xterm256", 225)) {
+ t.Fatal("real multipliers should render bold")
+ }
}
}
- hidden := renderCashHunt(&casino.CashHuntSnapshot{Phase: "hidden"}, mode, unicode)
+ hidden := renderCashHunt(&casino.CashHuntSnapshot{Phase: "hidden"}, mode, unicode, "")
for _, line := range strings.Split(stripANSI(hidden), "\n") {
- if utf8.RuneCountInString(line) != 70 {
+ if utf8.RuneCountInString(line) != cashHuntBoardWidth {
t.Fatalf("shuffle banner width changed: %q", line)
}
}
+ if mode == "xterm256" {
+ if !strings.Contains(hidden, color.FgCode("xterm256", 196)) {
+ t.Fatal("hidden gradient should start on red")
+ }
+ if !strings.Contains(hidden, color.FgCode("xterm256", 129)) {
+ t.Fatal("hidden gradient should end on purple")
+ }
+ }
}
}
}
+
+// TestCashHuntHeader pins the board header contents: difficulty left, title
+// centered, top multiplier right, and the RTP readout only when a house-edge
+// item was carried (the renderer receives an empty rtp otherwise).
+func TestCashHuntHeader(t *testing.T) {
+ s := &casino.CashHuntSnapshot{Phase: "preview", Risk: "medium"}
+ s.Squares[0] = 50000
+
+ withRTP := stripANSI(renderCashHunt(s, "none", true, "98.00%"))
+ header := strings.Split(withRTP, "\n")[2]
+ if !strings.Contains(header, "MEDIUM 98.00%") || !strings.Contains(header, "CASH HUNT") || !strings.Contains(header, "TOP PRIZE: 50000x") {
+ t.Fatalf("header row wrong: %q", header)
+ }
+
+ withoutRTP := stripANSI(renderCashHunt(s, "none", true, ""))
+ header = strings.Split(withoutRTP, "\n")[2]
+ if strings.Contains(header, "98") {
+ t.Fatalf("header showed RTP without a house-edge item: %q", header)
+ }
+}
+
+// TestCashHuntHeaderRTPWithHouseEdgeItem verifies the full path: carrying a
+// house-edge item makes the board header show the RTP beside the difficulty,
+// at the item's precision.
+func TestCashHuntHeaderRTPWithHouseEdgeItem(t *testing.T) {
+ g, s, conn := houseEdgeGame(t, "name: Vault\ncasino_tables:\n - id: cash_hunt\n game: cash_hunt\n")
+ s.Player.Options = map[string]any{"color": "none", "casino_hints": "off"}
+ writeHouseEdgeManual(t, g, s, 2)
+ g.executePlay(s, []string{"cash", "hunt"}, "play cash hunt")
+ g.executeBet(s, []string{"1"}, "bet 1")
+ text := stripANSI(string(conn.data))
+ if !strings.Contains(text, "EASY 98.00%") {
+ t.Fatalf("header missing item RTP readout:\n%s", text)
+ }
+}