1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
package game
import (
"strings"
"testing"
"thehouseoficarus/internal/casino"
"thehouseoficarus/internal/color"
"thehouseoficarus/internal/combat"
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/player"
"thehouseoficarus/internal/world"
)
func TestCrapsHalfTableGeometryAndPuck(t *testing.T) {
for _, unicode := range []bool{false, true} {
for _, point := range []int{0, 4, 5, 6, 8, 9, 10} {
s := &casino.CrapsSnapshot{Point: point, Field12: 3}
art := renderCrapsFelt(s, "none", unicode)
rows := strings.Split(strings.TrimSuffix(art, "\n"), "\n")
if len(rows) != 26 {
t.Fatal(len(rows))
}
for _, row := range rows {
if color.VisibleLen(row) != 77 {
t.Fatalf("misaligned (%d): %s", color.VisibleLen(row), row)
}
}
// Check connected box edges at the main rail and centre-action partition.
if unicode {
for _, r := range []rune{'┌', '┬', '┼', '├', '┤', '┴', '┘'} {
if !strings.ContainsRune(art, r) {
t.Fatalf("missing joint %c", r)
}
}
}
v := "|"
if unicode {
v = "│"
}
want := v + "OFF" + v
if point != 0 {
want = v + "ON " + v
}
if strings.Count(art, want) != 1 {
t.Fatalf("missing/duplicate puck %d: %s", point, art)
}
if !strings.Contains(art, "12 PAYS 3:1") {
t.Fatal("field variant invisible")
}
for _, bet := range []string{"DON'T COME", "C O M E", "FIELD", "DON'T PASS", "PASS LINE", "HARD 4", "HARD 6", "HARD 8", "HARD 10", "SEVEN", "CRAPS", "ELEVEN", "ACES", "ACE 3", "BOXCARS", "HORN", "HOP"} {
if strings.Count(art, bet) != 1 {
t.Fatalf("missing or duplicate %q", bet)
}
}
}
}
t.Log("\n" + renderCrapsSnapshot(&casino.CrapsSnapshot{Point: 6, Field12: 3, Dice: [2]int{3, 3}, Queue: []string{"Alice", "Bob"}, Made: []int{4, 5}, Seen: []int{2, 4, 5, 6, 9}, Phase: "betting", ShootIn: 10, DealerIn: 70}, "none", true))
}
func TestCrapsTrackersAndDiceStyles(t *testing.T) {
s := &casino.CrapsSnapshot{Made: []int{4, 6}, Seen: []int{2, 12}}
art := renderCrapsTrackers(s, "xterm256", true)
plain := plainRoulette(art)
for _, token := range []string{"[ 4*]", "[ 5 ]", "[ 6*]", " 2*", "12*"} {
if !strings.Contains(plain, token) {
t.Fatal("tracker missing", token)
}
}
if strings.Count(art, color.BgCode("xterm256", 220)) != 4 {
t.Fatal("wrong number of filled tracker tokens")
}
if !strings.Contains(renderCrapsFelt(s, "xterm256", true), color.BgCode("xterm256", 22)) {
t.Fatal("felt not dark green")
}
g := &Game{}
p := player.New("Alice")
p.Options = map[string]any{"card_style": "light", "unicode": true}
sess := &net.Session{Player: p}
s.Dice = [2]int{2, 5}
light := g.renderCasinoSnapshot(sess, s)
p.Options["card_style"] = "dark"
dark := g.renderCasinoSnapshot(sess, s)
if light == dark {
t.Fatal("card_style not wired to craps")
}
for a := 1; a <= 6; a++ {
for b := 1; b <= 6; b++ {
for _, unicode := range []bool{false, true} {
lit := renderCrapsDice([2]int{a, b}, "xterm256", unicode, "light")
dim := renderCrapsDice([2]int{a, b}, "xterm256", unicode, "dark")
if !strings.Contains(lit, color.BgCode("xterm256", 15)) || !strings.Contains(dim, color.BgCode("xterm256", 0)) {
t.Fatal("incorrect dice background")
}
pip := "o"
if unicode {
pip = "●"
}
if strings.Count(strings.ReplaceAll(plainRoulette(lit), "Total:", ""), pip) != a+b {
t.Fatal("wrong pip count", a, b)
}
if plainRoulette(lit) != plainRoulette(dim) {
t.Fatal("style changed dice outcome")
}
}
}
}
}
func TestCrapsRemindersFollowArt(t *testing.T) {
g := &Game{Casino: casino.NewManager(1), Combat: combat.NewTracker(), Deps: Deps{World: world.New(t.TempDir())}}
g.Casino.Join(1, casino.TableConfig{ID: "c", Game: casino.GameCraps}, "alice")
conn := &casinoTestConn{}
p := player.New("alice")
sess := &net.Session{Player: p, Conn: conn}
g.loggedInChars = map[string]*net.Session{"alice": sess}
for _, display := range []string{"full", "compact"} {
p.Options = map[string]any{"casino_display": display}
conn.data = nil
g.emitCasinoEvents(g.Casino.Table(1, "c").Status("alice"))
text := plainRoulette(string(conn.data))
felt, commands := strings.Index(text, "PASS LINE"), strings.Index(text, "bet <amount> on <target>")
if felt < 0 || commands < felt {
t.Fatal("reminder above graphic", text)
}
for _, old := range []string{"Next in order:", "Fire points made:", "ATS numbers:", "Phase:", "Timer: 0"} {
if strings.Contains(text, old) {
t.Fatal("raw status leaked", old)
}
}
}
}
|