aboutsummaryrefslogtreecommitdiff
path: root/internal/game/render_craps.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/render_craps.go')
-rw-r--r--internal/game/render_craps.go113
1 files changed, 76 insertions, 37 deletions
diff --git a/internal/game/render_craps.go b/internal/game/render_craps.go
index ce9825f..e4e6618 100644
--- a/internal/game/render_craps.go
+++ b/internal/game/render_craps.go
@@ -21,17 +21,23 @@ const (
crapsPuckOn
crapsPuckOff
crapsBonus
+ crapsGoldInk
+ crapsBonusInk
+ crapsBonusDim
)
var crapsPaint = []color.ColorSpec{
{Fg: 15, Bg: crapsFelt},
{Fg: 229, Bg: crapsFelt, Bold: true},
- {Fg: 217, Bg: crapsFelt, Bold: true},
+ {Fg: 160, Bg: crapsFelt, Bold: true}, // deep red for come/pass/props
{Fg: 108, Bg: crapsFelt},
{Fg: 0, Bg: 220, Bold: true},
{Fg: 0, Bg: 15, Bold: true},
{Fg: 15, Bg: 0, Bold: true},
{Fg: 229, Bg: 24, Bold: true}, // Fire/ATS tracker: yellow on blue
+ {Fg: 229, Bg: crapsFelt}, // regular-weight gold, used for field notes
+ {Fg: 229, Bg: 24}, // regular-weight tracker text and brackets
+ {Fg: 108, Bg: 24}, // dim tracker odds, matching the place/lay payouts
}
type crapsCell struct {
@@ -93,6 +99,26 @@ func (c *crapsCanvas) center(x1, x2, y int, s string, ink int) {
c.text(x1+(x2-x1+1-color.VisibleLen(s))/2, y, s, ink)
}
+// crapsSpan is one color run inside a centered line. centerSpans lays the runs
+// end to end and centers their combined width, so a label can stay regular
+// weight while its payout is dim, or a heading can pair two inks on one row.
+type crapsSpan struct {
+ text string
+ ink int
+}
+
+func (c *crapsCanvas) centerSpans(x1, x2, y int, spans ...crapsSpan) {
+ total := 0
+ for _, s := range spans {
+ total += color.VisibleLen(s.text)
+ }
+ x := x1 + (x2-x1+1-total)/2
+ for _, s := range spans {
+ c.text(x, y, s.text, s.ink)
+ x += color.VisibleLen(s.text)
+ }
+}
+
// crapsJoints maps a union of box edges (N=1, E=2, S=4, W=8) to its glyph.
var crapsJoints = map[uint8]rune{3: '└', 6: '┌', 9: '┘', 12: '┐', 7: '├', 11: '┴', 13: '┤', 14: '┬', 15: '┼'}
@@ -191,17 +217,15 @@ func renderCrapsTrackers(s *casino.CrapsSnapshot, mode string, unicode bool) str
c.box(38, 4, 76, 6)
c.text(3, 1, "FIRE", crapsBonus)
for i, n := range []int{4, 5, 6, 8, 9, 10} {
- ink := crapsBonus
- if crapsHas(s.Made, n) {
- ink = crapsMarked
- }
- c.text(15+i*9, 1, fmt.Sprintf("[%2d]", n), ink)
+ crapsTrackerPoint(c, 15+i*9, 1, n, crapsHas(s.Made, n))
}
bounds := []int{0, 25, 50, 76}
for i, payout := range []string{"24:1", "249:1", "999:1"} {
lo, hi := bounds[i], bounds[i+1]
c.box(lo, 2, hi, 4)
- c.center(lo+1, hi-1, 3, fmt.Sprintf("%d POINTS %s", i+4, payout), crapsBonus)
+ c.centerSpans(lo+1, hi-1, 3,
+ crapsSpan{fmt.Sprintf("%d POINTS", i+4), crapsBonusInk},
+ crapsSpan{" " + payout, crapsBonusDim})
}
c.text(2, 5, "SMALL", crapsBonus)
c.text(40, 5, "TALL", crapsBonus)
@@ -210,16 +234,27 @@ func renderCrapsTrackers(s *casino.CrapsSnapshot, mode string, unicode bool) str
if i >= 5 {
x = 48 + (i-5)*5
}
- ink := crapsBonus
- if crapsHas(s.Seen, n) {
- ink = crapsMarked
- }
- c.text(x, 5, fmt.Sprintf("[%2d]", n), ink)
+ crapsTrackerPoint(c, x, 5, n, crapsHas(s.Seen, n))
}
return c.render(mode, unicode)
}
+
+// crapsTrackerPoint draws one bracketed total for the Fire/ATS tracker. The
+// brackets stay regular weight while the number itself is bold; a made or
+// seen point keeps its solid highlight across the whole token.
+func crapsTrackerPoint(c *crapsCanvas, x, y, n int, marked bool) {
+ if marked {
+ c.text(x, y, fmt.Sprintf("[%2d]", n), crapsMarked)
+ return
+ }
+ c.text(x, y, "[", crapsBonusInk)
+ c.text(x+1, y, fmt.Sprintf("%2d", n), crapsBonus)
+ c.text(x+3, y, "]", crapsBonusInk)
+}
func renderCrapsFelt(s *casino.CrapsSnapshot, mode string, unicode bool) string {
- c := newCrapsCanvas(26)
+ // Compact half-table: every rail keeps its label and value on adjacent
+ // rows, so the felt is five rows shorter than the spaced-out layout.
+ c := newCrapsCanvas(21)
c.box(0, 0, 14, 6)
c.center(1, 13, 4, "DON'T COME", crapsInk)
c.center(1, 13, 5, "BAR 12", crapsMuted)
@@ -241,9 +276,9 @@ func renderCrapsFelt(s *casino.CrapsSnapshot, mode string, unicode bool) string
c.box(0, 6, 50, 10)
c.center(1, 49, 7, "PLACE / BUY / LAY ON THE NUMBERS ABOVE", crapsMuted)
c.center(1, 49, 9, "C O M E", crapsRed)
- c.box(0, 10, 50, 16)
- c.center(1, 49, 11, "FIELD", crapsGold)
- c.center(1, 49, 13, "( 2 ) 3 4 9 10 11 ( 12 )", crapsInk)
+ c.box(0, 10, 50, 14)
+ c.center(1, 49, 11, "F I E L D", crapsGold)
+ c.center(1, 49, 12, "( 2 ) 3 4 9 10 11 ( 12 )", crapsInk)
twelve := s.Field12
if twelve == 0 {
twelve = 2
@@ -252,13 +287,17 @@ func renderCrapsFelt(s *casino.CrapsSnapshot, mode string, unicode bool) string
if twelve == 3 {
twelvePay = "TRIPLE"
}
- c.center(1, 49, 15, fmt.Sprintf("2 PAYS DOUBLE 12 PAYS %s", twelvePay), crapsGold)
- c.box(0, 16, 50, 19)
- c.center(1, 49, 18, "DON'T PASS / BAR 12", crapsInk)
- c.box(0, 19, 50, 22)
- c.center(1, 49, 21, "PASS LINE", crapsGold)
- c.box(0, 22, 50, 25)
- c.center(1, 49, 24, "FREE ODDS / BEHIND THE LINE", crapsInk)
+ c.center(1, 49, 13, fmt.Sprintf("2 PAYS DOUBLE 12 PAYS %s", twelvePay), crapsGoldInk)
+ c.box(0, 14, 50, 16)
+ c.centerSpans(1, 49, 15, crapsSpan{"DON'T PASS", crapsInk}, crapsSpan{" BAR 12", crapsMuted})
+ c.box(0, 16, 50, 18)
+ c.center(1, 49, 17, "PASS LINE", crapsRed)
+ c.box(0, 18, 50, 20)
+ odds := s.Odds
+ if odds == "" {
+ odds = "3x-4x-5x"
+ }
+ c.centerSpans(1, 49, 19, crapsSpan{"FREE ODDS", crapsInk}, crapsSpan{" (" + odds + ")", crapsMuted})
// Centre action: four bonus spots, four hardways, six propositions, horn/hop.
// The Fire and ATS spots share one blue field.
bonus := []struct{ label, pays string }{{"FIRE", "24x"}, {"SMALL", "34:1"}, {"ALL", "175:1"}, {"TALL", "34:1"}}
@@ -269,11 +308,11 @@ func renderCrapsFelt(s *casino.CrapsSnapshot, mode string, unicode bool) string
c.fill(50, 0, 76, 5, crapsBonus)
for i, spot := range bonus {
c.center(xs[i]+1, xs[i+1]-1, 2, spot.label, crapsBonus)
- c.center(xs[i]+1, xs[i+1]-1, 4, spot.pays, crapsBonus)
+ c.center(xs[i]+1, xs[i+1]-1, 4, spot.pays, crapsBonusDim)
}
for i, n := range []int{4, 6, 8, 10} {
- x, y := 50+(i%2)*13, 5+(i/2)*4
- c.box(x, y, x+13, y+4)
+ x, y := 50+(i%2)*13, 5+(i/2)*3
+ c.box(x, y, x+13, y+3)
c.center(x+1, x+12, y+1, fmt.Sprintf("HARD %d", n), crapsInk)
pay := 7
if n == 6 || n == 8 {
@@ -283,29 +322,29 @@ func renderCrapsFelt(s *casino.CrapsSnapshot, mode string, unicode bool) string
if unicode {
pair = crapsDie(n/2) + "+" + crapsDie(n/2)
}
- c.center(x+1, x+12, y+3, fmt.Sprintf("%s %d:1", pair, pay), crapsGold)
+ c.centerSpans(x+1, x+12, y+2, crapsSpan{pair + " ", crapsGold}, crapsSpan{fmt.Sprintf("%d:1", pay), crapsMuted})
}
props := []string{"SEVEN", "CRAPS", "ELEVEN", "ACES", "ACE 3", "BOXCARS"}
- odds := []string{"4:1", "7:1", "15:1", "30:1", "15:1", "30:1"}
+ oddsRows := []string{"4:1", "7:1", "15:1", "30:1", "15:1", "30:1"}
px := []int{50, 59, 67, 76}
for i, label := range props {
- col, y := i%3, 13+(i/3)*4
+ col, y := i%3, 11+(i/3)*3
x, x2 := px[col], px[col+1]
- c.box(x, y, x2, y+4)
+ c.box(x, y, x2, y+3)
c.center(x+1, x2-1, y+1, label, crapsRed)
- c.center(x+1, x2-1, y+3, odds[i], crapsInk)
+ c.center(x+1, x2-1, y+2, oddsRows[i], crapsMuted)
}
- c.box(50, 21, 63, 25)
- c.center(51, 62, 22, "HORN", crapsGold)
+ c.box(50, 17, 63, 20)
+ c.center(51, 62, 18, "HORN", crapsGold)
horn := "2 3 11 12"
if unicode {
// Horn combinations as painted dice pairs: 2, 3, 11, 12.
horn = strings.Join([]string{crapsDie(1) + crapsDie(1), crapsDie(1) + crapsDie(2), crapsDie(5) + crapsDie(6), crapsDie(6) + crapsDie(6)}, " ")
}
- c.center(51, 62, 24, horn, crapsMuted)
- c.box(63, 21, 76, 25)
- c.center(64, 75, 22, "HOP", crapsGold)
- c.center(64, 75, 24, "15:1 / 30:1", crapsMuted)
+ c.center(51, 62, 19, horn, crapsMuted)
+ c.box(63, 17, 76, 20)
+ c.center(64, 75, 18, "HOP", crapsGold)
+ c.center(64, 75, 19, "15:1 / 30:1", crapsMuted)
return c.render(mode, unicode)
}