aboutsummaryrefslogtreecommitdiff
path: root/internal/game/render_big_six.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/render_big_six.go')
-rw-r--r--internal/game/render_big_six.go209
1 files changed, 209 insertions, 0 deletions
diff --git a/internal/game/render_big_six.go b/internal/game/render_big_six.go
new file mode 100644
index 0000000..19579f0
--- /dev/null
+++ b/internal/game/render_big_six.go
@@ -0,0 +1,209 @@
+package game
+
+import (
+ "fmt"
+ "strings"
+
+ "thehouseoficarus/internal/casino"
+ "thehouseoficarus/internal/color"
+)
+
+func bigSixInk(mode, label, text string, dim bool) string {
+ fg := 2
+ switch label {
+ case "2":
+ fg = 6
+ case "5":
+ fg = 4
+ case "10":
+ fg = 5
+ case "20":
+ fg = 3
+ case "SWORD":
+ fg = 15
+ case "SHIELD":
+ fg = 6
+ case "DOUBLE":
+ fg = 11
+ }
+ return color.Render(mode, color.ColorSpec{Fg: fg, Bg: -1, Bold: !dim, Dim: dim}, text)
+}
+func bigSixLabel(s string) string {
+ if s == "SWORD" || s == "SHIELD" || s == "DOUBLE" {
+ return s
+ }
+ return "$" + s
+}
+
+func bigSixPanel(spot casino.BigSixSpot, width int, mode string, unicode bool) []string {
+ b := rouletteBorders(unicode)
+ label := bigSixLabel(spot.Label)
+ art := []string{"$ $", ".--------.", "|" + rouletteCenter(label, 8) + "|", "'--------'", "$ $"}
+ if unicode {
+ art = []string{"$ $", "╭────────╮", "│" + rouletteCenter(label, 8) + "│", "╰────────╯", "$ $"}
+ }
+ switch spot.Label {
+ case "SWORD":
+ // Two full blade rows below the tip; the guard sits just above the grip.
+ art = []string{"/\\", "||", "||", "===++===", "()"}
+ if unicode {
+ art = []string{"▲", "║", "║", "╘══╬══╛", "╩"}
+ }
+ case "SHIELD":
+ art = []string{".--------.", "| /\\ |", "| \\/ |", "\\ /", "'------'"}
+ if unicode {
+ // Keep a fixed-width canvas: centering each tapered row separately
+ // would shift the corners apart and break the shield's outline.
+ art = []string{"╭───────╮", "│ ◇ │", "╰╮ ╭╯", " ╰╮ ╭╯ ", " ╰───╯ "}
+ }
+ }
+ lines := []string{b.tl + strings.Repeat(b.h, width) + b.tr, b.v + rouletteCenter(label, width) + b.v}
+ for _, a := range art {
+ lines = append(lines, b.v+rouletteCenter(a, width)+b.v)
+ }
+ lines = append(lines, b.bl+strings.Repeat(b.h, width)+b.br)
+ // The payout text is dim, but its box walls keep the same bold weight as
+ // the rest of the panel; otherwise the row's verticals look thin.
+ wall := bigSixInk(mode, spot.Label, b.v, false)
+ oddsLine := wall + bigSixInk(mode, spot.Label, rouletteCenter(fmt.Sprintf("pays %d:1", spot.Pays), width), true) + wall
+ lines = append(lines[:len(lines)-1], oddsLine, lines[len(lines)-1])
+ for i := range lines {
+ if i == len(lines)-2 {
+ continue // oddsLine is already colored: bold walls, dim payout
+ }
+ lines[i] = bigSixInk(mode, spot.Label, lines[i], false)
+ }
+ return lines
+}
+
+// Self-contained scrollback art, always within 80 columns. The last two
+// betting panels expand into the space formerly occupied by DOUBLE; DOUBLE
+// is an automatic feature, advertised in a shallow full-width banner instead.
+func renderBigSixSnapshot(s *casino.BigSixSnapshot, mode string, unicode bool) string {
+ if len(s.Pockets) > 0 {
+ return renderBigSixStrip(s, mode, unicode)
+ }
+ if s.Phase == "playing" {
+ return "Big 6 wheel spinning | live stakes " + s.Multiplier + "x"
+ }
+ var out strings.Builder
+ spots := casino.BigSixSpots()
+ for _, row := range [][]casino.BigSixSpot{spots[:4], spots[4:7]} {
+ var panels [][]string
+ for _, spot := range row {
+ width := 15
+ if spot.Label == "SWORD" || spot.Label == "SHIELD" {
+ width = 24
+ }
+ panels = append(panels, bigSixPanel(spot, width, mode, unicode))
+ }
+ for line := range panels[0] {
+ out.WriteString(" ")
+ for col, panel := range panels {
+ if col > 0 {
+ out.WriteByte(' ')
+ }
+ out.WriteString(panel[line])
+ }
+ out.WriteByte('\n')
+ }
+ }
+ b := rouletteBorders(unicode)
+ const bannerWidth = 69 // 71 columns including borders, like both betting rows.
+ banner := []string{
+ b.tl + strings.Repeat(b.h, bannerWidth) + b.tr,
+ b.v + rouletteCenter("DOUBLE", bannerWidth) + b.v,
+ b.v + rouletteCenter("Every live bet doubles and the wheel spins again", bannerWidth) + b.v,
+ b.v + rouletteCenter("Double again. And again. No cap.", bannerWidth) + b.v,
+ b.bl + strings.Repeat(b.h, bannerWidth) + b.br,
+ }
+ for _, line := range banner {
+ out.WriteString(" " + bigSixInk(mode, "DOUBLE", line, false) + "\n")
+ }
+ fmt.Fprintf(&out, " Bets: %s", betLimitsText(s.MinBet, s.MaxBet))
+ return out.String()
+}
+
+func renderBigSixStrip(s *casino.BigSixSnapshot, mode string, unicode bool) string {
+ result := s.Phase == "result"
+ width := 6
+ if result {
+ width = 7
+ }
+ n := len(s.Pockets)
+ center := n / 2
+ pointer := 1 + center*(width+1) + width/2
+ b := rouletteBorders(unicode)
+ wall := "|"
+ if unicode {
+ wall = "│"
+ }
+ // Every rail and separator on the wheel uses the same dim-yellow border
+ // ink; the per-pocket colors paint only the interiors.
+ borderInk := func(text string) string { return bigSixInk(mode, "20", text, true) }
+ var out strings.Builder
+ tip := "\\/"
+ if unicode {
+ tip = "▼"
+ }
+ // No leading/trailing newline: WriteLine supplies the sole frame separator.
+ out.WriteString(strings.Repeat(" ", pointer) + bigSixInk(mode, "DOUBLE", tip, false) + "\n")
+ top := b.tl + strings.Repeat(strings.Repeat(b.h, width)+b.teeTop, n-1) + strings.Repeat(b.h, width) + b.tr
+ bottom := b.bl + strings.Repeat(strings.Repeat(b.h, width)+b.teeBottom, n-1) + strings.Repeat(b.h, width) + b.br
+ out.WriteString(borderInk(top) + "\n")
+ rows := 3
+ if result {
+ rows = 4
+ }
+ for row := 0; row < rows; row++ {
+ out.WriteString(borderInk(b.v))
+ for i, pocket := range s.Pockets {
+ label := bigSixLabel(pocket)
+ var art []string
+ if result {
+ // The bill's side walls continue through the number row so the
+ // outline stays complete once the wheel stops.
+ art = []string{".-----.", "| $ |", wall + rouletteCenter(label, 5) + wall, "'-----'"}
+ if unicode {
+ art = []string{"╭─────╮", "│ $ │", wall + rouletteCenter(label, 5) + wall, "╰─────╯"}
+ }
+ switch pocket {
+ case "SWORD":
+ art = []string{"/\\", "||", "--+--", label}
+ if unicode {
+ art = []string{"▲", "╘═╬═╛", "╩", label}
+ }
+ case "SHIELD":
+ art = []string{"/---\\", "| + |", "\\___/", label}
+ if unicode {
+ art = []string{"╭─────╮", "╰╮ ◇ ╭╯", " ╰───╯ ", label}
+ }
+ case "DOUBLE":
+ art = []string{"", "x 2", label, "RESPIN"}
+ if unicode {
+ art[1] = "× 2"
+ }
+ }
+ } else {
+ // Only the dollar symbols are drawn as banknotes; the logo and
+ // DOUBLE spaces show their name without the bill walls.
+ art = []string{".----.", wall + rouletteCenter(label, 4) + wall, "'----'"}
+ if unicode {
+ art = []string{"╭────╮", wall + rouletteCenter(label, 4) + wall, "╰────╯"}
+ }
+ switch pocket {
+ case "SWORD", "SHIELD", "DOUBLE":
+ art = []string{"", label, ""}
+ }
+ }
+ out.WriteString(bigSixInk(mode, pocket, rouletteCenter(art[row], width), result && i != center))
+ out.WriteString(borderInk(b.v))
+ }
+ out.WriteByte('\n')
+ }
+ out.WriteString(borderInk(bottom))
+ if result {
+ out.WriteString("\n" + strings.Repeat(" ", pointer-3) + bigSixInk(mode, s.Pockets[center], "WINNER", false))
+ }
+ return out.String()
+}