aboutsummaryrefslogtreecommitdiff
path: root/internal/game/pai_gow_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-01 14:38:08 -0400
committerhistoria <[not public]>2026-10-01 14:38:08 -0400
commit1082f3336085c15992ca83d5553c0ba52208d70a (patch)
tree4fcedaa90bc01c41456258a9d57f492f31754948 /internal/game/pai_gow_test.go
parentd4eae333a8d8a96452bbf3617aa85b50466c11a7 (diff)
downloadthehouseoficarus-1082f3336085c15992ca83d5553c0ba52208d70a.tar.gz
feat: pai gow poker added to the casino
Diffstat (limited to 'internal/game/pai_gow_test.go')
-rw-r--r--internal/game/pai_gow_test.go141
1 files changed, 141 insertions, 0 deletions
diff --git a/internal/game/pai_gow_test.go b/internal/game/pai_gow_test.go
new file mode 100644
index 0000000..614d0dd
--- /dev/null
+++ b/internal/game/pai_gow_test.go
@@ -0,0 +1,141 @@
+package game
+
+import (
+ "strings"
+ "testing"
+ "unicode/utf8"
+
+ "thehouseoficarus/internal/cardart"
+ "thehouseoficarus/internal/casino"
+ "thehouseoficarus/internal/world"
+)
+
+func TestPaiGowCommandsLookAndSettlement(t *testing.T) {
+ g, sess, conn := casinoCrapsGame(t, "name: Pai Gow\ncasino_tables:\n - id: fortune\n game: pai_gow\n pai_gow:\n envy: true\n")
+ g.executePlay(sess, []string{"pai"}, "play pai")
+ table, playing := g.Casino.Participation(sess.Player.Name)
+ if !playing || table.Config.Game != casino.GamePaiGow {
+ t.Fatal("alias failed")
+ }
+ conn.data = nil
+ g.executeTableStatus(sess, []string{"rules"}, "table rules")
+ text := stripANSI(string(conn.data))
+ for _, want := range []string{"FORTUNE", "ENVY (FIXED)", "750:1", "Three pair"} {
+ if !strings.Contains(text, want) {
+ t.Fatalf("missing %s: %s", want, text)
+ }
+ }
+ g.executeBet(sess, []string{"20"}, "bet 20")
+ g.executeBet(sess, []string{"5", "fortune"}, "bet 5 fortune")
+ g.executeTableAction(sess, "deal")
+ snap := table.Status(sess.Player.Name)[0].Snapshot.(*casino.PaiGowSnapshot)
+ if len(snap.Cards) != 7 || len(snap.DealerHigh) != 0 || len(sess.Player.PendingWagers) != 2 {
+ t.Fatal("deal/side bet")
+ }
+ conn.data = nil
+ g.executeCommand(sess, "22", nil, "22")
+ if text = string(conn.data); !strings.Contains(text, "different numbers") {
+ t.Fatal(text)
+ }
+ if classifyCommand("67") != ClassInstant || classifyCommand("13456") != ClassInstant {
+ t.Fatal("numeric classification")
+ }
+ // A numeric selection either locks a valid split or gives a clear foul;
+ // either way it must reach Pai Gow rather than the generic unknown verb.
+ conn.data = nil
+ g.executeCommand(sess, "67", nil, "67")
+ text = string(conn.data)
+ if table.Status(sess.Player.Name)[0].Snapshot.(*casino.PaiGowSnapshot).Phase == "setting" && !strings.Contains(text, "FOUL HAND") {
+ t.Fatal(text)
+ }
+ g.executeCommand(sess, "house", nil, "house")
+ if table.Status(sess.Player.Name)[0].Snapshot.(*casino.PaiGowSnapshot).Phase != "waiting" || len(sess.Player.PendingWagers) != 0 {
+ t.Fatal("settlement did not finish")
+ }
+}
+func TestPaiGowFeltAndJokerAllModes(t *testing.T) {
+ store := cardart.Load("../../data/cards.yaml")
+ for _, size := range []int{1, 5, 7, 9} {
+ if len(store.Art("JK", size)) != size {
+ t.Fatalf("missing JK art at %d", size)
+ }
+ for _, unicode := range []bool{false, true} {
+ for _, style := range []string{"dark", "light"} {
+ for _, art := range []*cardart.Store{nil, store} {
+ lines := jokerCardArt(size, unicode, style, art)
+ if len(lines) != size {
+ t.Fatal(size, lines)
+ }
+ for _, line := range lines {
+ if size > 1 && utf8.RuneCountInString(line) != size {
+ t.Fatalf("%d width: %q", size, line)
+ }
+ if !unicode {
+ for _, r := range line {
+ if r > 127 {
+ t.Fatalf("ASCII Joker has Unicode: %q", line)
+ }
+ }
+ }
+ }
+ for _, mode := range []string{"none", "xterm256", "truecolor"} {
+ out := cardsLine(mode, []casino.CardView{{Rank: "JK", Suit: "joker"}}, cardRenderSize(unicode, size), unicode, style, art)
+ if !strings.Contains(strings.Join(out, "\n"), "J") {
+ t.Fatal(out)
+ }
+ }
+ }
+ }
+ }
+ }
+ for _, envy := range []bool{false, true} {
+ for _, unicode := range []bool{false, true} {
+ cfg := casino.TableConfig{Game: casino.GamePaiGow, PaiGow: casino.PaiGowConfig{Envy: envy}}
+ felt := renderPaiGowFelt(cfg, "none", unicode)
+ lines := strings.Split(strings.TrimRight(felt, "\n"), "\n")
+ width := utf8.RuneCountInString(lines[0])
+ for _, line := range lines {
+ if utf8.RuneCountInString(line) != width {
+ t.Fatal("jagged felt", line)
+ }
+ }
+ if !strings.Contains(felt, "Three pair") || !strings.Contains(felt, "1000:1") {
+ t.Fatal(felt)
+ }
+ if !envy && (!strings.Contains(felt, "ENVY OFF") || !strings.Contains(felt, "30:1")) {
+ t.Fatal(felt)
+ }
+ if cfg.PaiGow.Envy && (strings.Contains(felt, "ENVY OFF") || !strings.Contains(felt, "ENVY (FIXED)")) {
+ t.Fatal(felt)
+ }
+ }
+ }
+ cfg := casino.TableConfig{MinBet: 5, MaxBet: 1000, Game: casino.GamePaiGow, PaiGow: casino.PaiGowConfig{Variant: casino.PaiGowFaceUp, PushAceHigh: "5_15_40"}}
+ felt := renderPaiGowFelt(cfg, "none", true)
+ if !strings.Contains(felt, "DEALER FACE UP") || !strings.Contains(felt, "PUSH ACE HIGH 5/15/40:1") {
+ t.Fatal(felt)
+ }
+}
+func TestVestaPaiGowRooms(t *testing.T) {
+ _, rooms, _ := loadVesta(t)
+ for id, variant := range map[int]string{6195: casino.PaiGowClassic, 6196: casino.PaiGowEZ, 6197: casino.PaiGowNoPush, 6198: casino.PaiGowFaceUp} {
+ r, ok := rooms[id]
+ if !ok || len(r.CasinoTables) != 1 {
+ t.Fatalf("missing Pai Gow room %d", id)
+ }
+ cfg := r.CasinoTables[0]
+ if cfg.Validate() != nil || cfg.Game != casino.GamePaiGow || cfg.PaiGow.Variant != variant || cfg.PaiGow.Envy != (id != 6197) {
+ t.Fatalf("bad Pai Gow configuration: %+v", cfg)
+ }
+ }
+ if rooms[6192].Exits[world.East].Room != 6195 || rooms[6195].Exits[world.West].Room != 6192 || rooms[6195].Exits[world.North].Room != 6196 || rooms[6196].Exits[world.South].Room != 6195 || rooms[6195].Exits[world.South].Room != 6197 || rooms[6197].Exits[world.North].Room != 6195 || rooms[6196].Exits[world.North].Room != 6198 || rooms[6198].Exits[world.South].Room != 6196 {
+ t.Fatal("broken Pai Gow navigation")
+ }
+}
+func TestJokerEmptySuitDoesNotPanic(t *testing.T) {
+ for _, size := range []int{5, 7, 9} {
+ if len(cardArtLines(casino.CardView{Rank: "JK"}, size, true, "dark", nil)) != size {
+ t.Fatal(size)
+ }
+ }
+}