aboutsummaryrefslogtreecommitdiff
path: root/internal/game/casino_test.go
blob: b74baab1da2e53a72f2c835c4ef678ab317a04cd (plain)
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
package game

import (
	"strings"
	"testing"

	"thehouseoficarus/internal/casino"
	"thehouseoficarus/internal/color"
	"thehouseoficarus/internal/net"
	"thehouseoficarus/internal/player"
)

type casinoTestConn struct {
	data []byte
}

func (c *casinoTestConn) ReadMessage() (string, error) { return "", nil }
func (c *casinoTestConn) Write(b []byte) (int, error) {
	c.data = append(c.data, b...)
	return len(b), nil
}
func (c *casinoTestConn) Close() error       { return nil }
func (c *casinoTestConn) SetEcho(bool) error { return nil }

func TestExecuteBetRejectsInvalidMachineArgs(t *testing.T) {
	g := &Game{Casino: casino.NewManager(1)}
	cfg := casino.MachineConfig{ID: "slots", Game: casino.GameSlots}
	g.Casino.JoinMachine(1, cfg, "alice")
	conn := &casinoTestConn{}
	sess := &net.Session{Player: player.New("alice"), Conn: conn}
	g.loggedInChars = map[string]*net.Session{"alice": sess}
	for _, args := range [][]string{{"banana"}, {"-5"}, {"10", "extra"}} {
		conn.data = nil
		g.executeBet(sess, args, "bet "+strings.Join(args, " "))
		if !strings.Contains(string(conn.data), "Use bet, bet <amount>, or bet max.") {
			t.Fatalf("bet %v: expected usage error, got %q", args, string(conn.data))
		}
	}
	// A bare bet at a machine reaches the spin path instead of erroring.
	g.Casino.SetMachineBet(1, "slots", "alice", 10)
	conn.data = nil
	g.executeBet(sess, nil, "bet")
	if !strings.Contains(string(conn.data), "You don't have enough chips and credits") {
		t.Fatalf("bare bet should attempt a spin, got %q", string(conn.data))
	}
}

func TestRenderSlotSnapshotColorsWordSymbols(t *testing.T) {
	snapshot := &casino.SlotSnapshot{StoppedReels: 1}
	snapshot.Grid[0][0] = "scatter"
	if !strings.Contains(renderSlotSnapshot(snapshot, "none", false), "scatter") {
		t.Fatal("expected word-based slot symbols")
	}
	output := renderSlotSnapshot(snapshot, "xterm256", true)
	if !strings.Contains(output, "\033[38;5;") {
		t.Fatalf("expected colored slot symbols, got %q", output)
	}
	for _, line := range strings.Split(output, "\n") {
		if color.VisibleLen(line) != 51 {
			t.Fatalf("line visible width = %d, want 51: %q", color.VisibleLen(line), line)
		}
	}
	if !strings.Contains(output, "┌") || !strings.Contains(output, "└") {
		t.Fatalf("slot output lacks the Unicode frame: %q", output)
	}
}

func TestRenderBlackjackSnapshotUsesCompactASCIIArt(t *testing.T) {
	snapshot := &casino.BlackjackSnapshot{
		Dealer:  []casino.BlackjackCardView{{Rank: "?", Hidden: true}, {Rank: "K", Suit: "hearts"}},
		Players: []casino.BlackjackPlayerView{{Name: "alice", Score: "20", Cards: []casino.BlackjackCardView{{Rank: "5", Suit: "clubs"}, {Rank: "Q", Suit: "spades"}}}},
	}
	plain := renderBlackjackSnapshot(snapshot, "none", false, 5, "dark", nil)
	if !strings.Contains(plain, "+---+") || !strings.Contains(plain, "Dealer:") || !strings.Contains(plain, "alice (20):") {
		t.Fatalf("unexpected blackjack art: %q", plain)
	}
	output := renderBlackjackSnapshot(snapshot, "xterm256", false, 5, "dark", nil)
	if !strings.Contains(output, "\033[38;5;") {
		t.Fatalf("expected colored blackjack art: %q", output)
	}
	if strings.ContainsAny(output, "│─┌┐└┘") {
		t.Fatalf("blackjack art contains Unicode borders: %q", output)
	}
	for _, line := range strings.Split(output, "\n") {
		if color.VisibleLen(line) > 80 {
			t.Fatalf("blackjack line exceeds console width: %d", color.VisibleLen(line))
		}
	}
	unicodeOutput := renderBlackjackSnapshot(snapshot, "none", true, 7, "dark", nil)
	if !strings.Contains(unicodeOutput, "│5    │") || !strings.Contains(unicodeOutput, "│    5│") || !strings.Contains(unicodeOutput, "│  ♣  │") {
		t.Fatalf("unexpected Unicode card alignment: %q", unicodeOutput)
	}
	coloredUnicode := renderBlackjackSnapshot(snapshot, "xterm256", true, 7, "dark", nil)
	if !strings.Contains(coloredUnicode, "\033[38;5;15m") || !strings.Contains(coloredUnicode, "\033[38;5;27m") {
		t.Fatalf("expected white borders and blue card art: %q", coloredUnicode)
	}
	light := renderBlackjackSnapshot(snapshot, "xterm256", true, 7, "light", nil)
	if !strings.Contains(light, "\033[48;5;15m") {
		t.Fatalf("expected white card background in light style: %q", light)
	}
	lightLines := strings.Split(light, "\n")
	if strings.Contains(lightLines[1], "\033[48;5;15m") || strings.Contains(lightLines[7], "\033[48;5;15m") {
		t.Fatalf("card edges should not have a background: %q", light)
	}
	lightPlain := renderBlackjackSnapshot(snapshot, "none", true, 7, "light", nil)
	if !strings.Contains(lightPlain, "▀▀▀▀▀▀▀") || !strings.Contains(lightPlain, "▄▄▄▄▄▄▄") {
		t.Fatalf("expected half-height light card edges: %q", lightPlain)
	}
}