aboutsummaryrefslogtreecommitdiff
path: root/internal/game/casino_look_test.go
blob: cdb0b59454dc8e5cdab995f2a156b395abd55d63 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package game

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"thehouseoficarus/internal/casino"
)

// lookOutput polls the queued session writer until the look output has
// settled (no growth across two samples), then returns it, ANSI-stripped.
func lookOutput(t *testing.T, conn *chanConn) string {
	t.Helper()
	last := 0
	stable := 0
	for stable < 2 {
		n := len(conn.Bytes())
		if n == last {
			stable++
		} else {
			stable = 0
			last = n
		}
		time.Sleep(10 * time.Millisecond)
	}
	return stripANSI(string(conn.Bytes()))
}

// TestLookAtTableShowsGameStateAndHints covers feature 1 with the full
// production harness: a seated player's look shows the room first, then the
// game table, then the current hint commands — rules always, list when the
// game has multiple wagers.
func TestLookAtTableShowsGameStateAndHints(t *testing.T) {
	h := newHarness(t)
	h.seedAccount("alice")
	h.seedCharacter("alice", "alice")
	sess, conn := h.connect("alice")

	// Add a casino room and move there.
	room := "id: 4\nname: Simplified Salon\ncasino_tables:\n  - id: simple\n    game: craps\n    craps:\n      variant: simplified\n"
	path := filepath.Join(h.dir, "rooms", "4.yaml")
	if err := os.WriteFile(path, []byte(room), 0644); err != nil {
		t.Fatal(err)
	}
	h.g.World.AddRoomPath(4, path)
	sess.Player.RoomID = 4
	h.hub.EnterRoom(sess, 4)

	cfg := casino.TableConfig{ID: "simple", Game: casino.GameCraps, Craps: casino.CrapsConfig{Variant: "simplified"}}
	h.g.Casino.Join(4, cfg, "alice")
	h.g.executeLook(sess, nil, "look")
	text := lookOutput(t, conn)
	if !strings.Contains(text, "Simplified Salon") {
		t.Fatalf("look lost the room: %q", text)
	}
	if !strings.Contains(strings.ToUpper(text), "SIMPLIFIED CRAPS") || !strings.Contains(text, "seated") {
		t.Fatalf("look missing the table state: %q", text)
	}
	if !strings.Contains(text, "rules") {
		t.Fatalf("look missing the rules hint: %q", text)
	}
}

// TestLookStandingShowsFixtureOnly: an unseated player's look keeps the
// plain room listing (no game state block).
func TestLookStandingShowsFixtureOnly(t *testing.T) {
	h := newHarness(t)
	h.seedAccount("alice")
	h.seedCharacter("alice", "alice")
	sess, conn := h.connect("alice")
	room := "id: 4\nname: Simplified Salon\ncasino_tables:\n  - id: simple\n    game: craps\n    craps:\n      variant: simplified\n"
	path := filepath.Join(h.dir, "rooms", "4.yaml")
	if err := os.WriteFile(path, []byte(room), 0644); err != nil {
		t.Fatal(err)
	}
	h.g.World.AddRoomPath(4, path)
	sess.Player.RoomID = 4
	h.hub.EnterRoom(sess, 4)
	h.g.executeLook(sess, nil, "look")
	text := lookOutput(t, conn)
	if strings.Contains(text, "bet <amount>") {
		t.Fatalf("standing look leaked a betting menu: %q", text)
	}
	if !strings.Contains(text, "play craps") {
		t.Fatalf("standing look missing the play hint: %q", text)
	}
}

// TestLookCasinoRoomSingleBlankBeforeExits pins the room-look spacing: the
// casino fixture listing and the exit section are separated by one blank
// line, whether or not the mini-map supplies trailing rows of its own.
func TestLookCasinoRoomSingleBlankBeforeExits(t *testing.T) {
	for _, mapOpt := range []string{"off", "right"} {
		t.Run(mapOpt, func(t *testing.T) {
			h := newHarness(t)
			h.seedAccount("alice")
			h.seedCharacter("alice", "alice")
			sess, conn := h.connect("alice")
			sess.Player.Options = map[string]any{"tiny_map": mapOpt}

			room := "id: 4\nname: Bank Craps Pit\ndescription: A sweeping green felt table curves beneath warm brass lamps. Ivory dice rest beside a polished stick.\nexits:\n  north:\n    room: 1\ncasino_tables:\n  - id: bank_craps\n    game: craps\n    craps:\n      variant: bank\n"
			path := filepath.Join(h.dir, "rooms", "4.yaml")
			if err := os.WriteFile(path, []byte(room), 0644); err != nil {
				t.Fatal(err)
			}
			h.g.World.AddRoomPath(4, path)
			sess.Player.RoomID = 4
			h.hub.EnterRoom(sess, 4)

			h.g.executeLook(sess, nil, "look")
			lines := strings.Split(lookOutput(t, conn), "\n")
			play, exits := -1, -1
			for i, line := range lines {
				if strings.Contains(line, "play craps") {
					play = i
				}
				if strings.TrimSpace(line) == "Exits:" {
					exits = i
				}
			}
			if play < 0 || exits < 0 || exits <= play {
				t.Fatalf("could not locate table/exits: %q", lines)
			}
			for i := play; i < exits; i++ {
				if strings.TrimSpace(lines[i]) != "" {
					continue
				}
				if i+1 < exits && strings.TrimSpace(lines[i+1]) == "" {
					t.Fatalf("consecutive blank lines before Exits: %q", lines)
				}
			}
		})
	}
}