Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

minichess_test.gno

8.70 Kb · 269 lines
  1package minichess
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	mc "gno.land/p/g1um2pwwnx3gn8p3ryfdert8mhpu4u4up49sc9ru/minichess"
  8	"gno.land/p/nt/avl/v0"
  9	"gno.land/p/nt/testutils/v0"
 10	"gno.land/p/nt/uassert/v0"
 11)
 12
 13var (
 14	alice = testutils.TestAddress("alice")
 15	bob   = testutils.TestAddress("bob")
 16	carol = testutils.TestAddress("carol")
 17)
 18
 19func reset() {
 20	games = avl.NewTree()
 21	openGames = avl.NewTree()
 22	playerGames = avl.NewTree()
 23	nextID = 1
 24}
 25
 26func TestSoloAsWhite(cur realm, t *testing.T) {
 27	reset()
 28	testing.SetOriginCaller(alice)
 29	testing.SetRealm(testing.NewUserRealm(alice))
 30	id := NewSoloGame(cross(cur), "white", 1)
 31	g := mustGetGame(id)
 32	uassert.Equal(t, alice.String(), g.White.String())
 33	uassert.Equal(t, "", g.Black.String())
 34	uassert.Equal(t, 0, len(g.Moves))
 35
 36	out := Move(cross(cur), id, "b1c3")
 37	uassert.True(t, strings.Contains(out, "bot played"), out)
 38	uassert.Equal(t, 2, len(g.Moves))
 39	uassert.Equal(t, int(mc.White), int(g.Pos.Turn))
 40}
 41
 42func TestSoloAsBlackBotMovesFirst(cur realm, t *testing.T) {
 43	reset()
 44	testing.SetOriginCaller(alice)
 45	testing.SetRealm(testing.NewUserRealm(alice))
 46	id := NewSoloGame(cross(cur), "black", 2)
 47	g := mustGetGame(id)
 48	uassert.Equal(t, 1, len(g.Moves))
 49	uassert.Equal(t, int(mc.Black), int(g.Pos.Turn))
 50}
 51
 52func TestMoveValidation(cur realm, t *testing.T) {
 53	reset()
 54	testing.SetOriginCaller(alice)
 55	testing.SetRealm(testing.NewUserRealm(alice))
 56	id := NewSoloGame(cross(cur), "white", 1)
 57
 58	uassert.AbortsWithMessage(t, cur, "illegal move: e2e5", func() {
 59		Move(cross(cur), id, "e2e5")
 60	})
 61	uassert.AbortsWithMessage(t, cur, "chess: move must look like e2e4 or e7e8q", func() {
 62		Move(cross(cur), id, "b2")
 63	})
 64	testing.SetOriginCaller(bob)
 65	testing.SetRealm(testing.NewUserRealm(bob))
 66	uassert.AbortsWithMessage(t, cur, "you are not a player in this game", func() {
 67		Move(cross(cur), id, "b2b3")
 68	})
 69	uassert.AbortsWithMessage(t, cur, "game 99 not found", func() {
 70		Move(cross(cur), 99, "b2b3")
 71	})
 72	uassert.AbortsWithMessage(t, cur, "level must be between 1 and 3", func() {
 73		NewSoloGame(cross(cur), "white", 4)
 74	})
 75}
 76
 77func TestTwoPlayerCheckmate(cur realm, t *testing.T) {
 78	reset()
 79	testing.SetOriginCaller(alice)
 80	testing.SetRealm(testing.NewUserRealm(alice))
 81	id := Challenge(cross(cur), "", "white")
 82	g := mustGetGame(id)
 83	uassert.Equal(t, int(StateWaiting), int(g.State))
 84	uassert.True(t, strings.Contains(Render(""), "func=AcceptChallenge&gameID=1"))
 85
 86	uassert.AbortsWithMessage(t, cur, "you cannot accept your own challenge", func() {
 87		AcceptChallenge(cross(cur), id)
 88	})
 89	testing.SetOriginCaller(bob)
 90	testing.SetRealm(testing.NewUserRealm(bob))
 91	AcceptChallenge(cross(cur), id)
 92	uassert.Equal(t, int(StateActive), int(g.State))
 93	uassert.Equal(t, bob.String(), g.Black.String())
 94	uassert.True(t, strings.Contains(Render(""), "_None right now._"))
 95
 96	uassert.AbortsWithMessage(t, cur, "it is not your turn", func() {
 97		Move(cross(cur), id, "e7e5")
 98	})
 99
100	// Scholar's mate, with castling-free development: 4. Qxf7#.
101	seq := []string{"e2e4", "e7e5", "f1c4", "b8c6", "d1h5", "g8f6", "h5f7"}
102	for i, m := range seq {
103		if i%2 == 0 {
104			testing.SetOriginCaller(alice)
105			testing.SetRealm(testing.NewUserRealm(alice))
106		} else {
107			testing.SetOriginCaller(bob)
108			testing.SetRealm(testing.NewUserRealm(bob))
109		}
110		Move(cross(cur), id, m)
111	}
112	uassert.Equal(t, int(StateWhiteWon), int(g.State))
113	uassert.Equal(t, "checkmate", g.Reason)
114
115	testing.SetOriginCaller(bob)
116	testing.SetRealm(testing.NewUserRealm(bob))
117	uassert.AbortsWithMessage(t, cur, "game is not in progress (white won)", func() {
118		Move(cross(cur), id, "e8f7")
119	})
120}
121
122func TestInviteOnlyAndCancel(cur realm, t *testing.T) {
123	reset()
124	testing.SetOriginCaller(alice)
125	testing.SetRealm(testing.NewUserRealm(alice))
126	uassert.AbortsWithMessage(t, cur, "you cannot challenge yourself", func() {
127		Challenge(cross(cur), alice.String(), "white")
128	})
129	uassert.AbortsWithMessage(t, cur, "invalid opponent address", func() {
130		Challenge(cross(cur), "not-an-address", "white")
131	})
132	id := Challenge(cross(cur), bob.String(), "black")
133
134	testing.SetOriginCaller(carol)
135	testing.SetRealm(testing.NewUserRealm(carol))
136	uassert.AbortsWithMessage(t, cur, "this challenge is for another player", func() {
137		AcceptChallenge(cross(cur), id)
138	})
139	uassert.AbortsWithMessage(t, cur, "only the creator can cancel this challenge", func() {
140		CancelChallenge(cross(cur), id)
141	})
142
143	testing.SetOriginCaller(alice)
144	testing.SetRealm(testing.NewUserRealm(alice))
145	CancelChallenge(cross(cur), id)
146	uassert.Equal(t, int(StateCancelled), int(mustGetGame(id).State))
147
148	testing.SetOriginCaller(bob)
149	testing.SetRealm(testing.NewUserRealm(bob))
150	uassert.AbortsWithMessage(t, cur, "game is not waiting for an opponent", func() {
151		AcceptChallenge(cross(cur), id)
152	})
153}
154
155func TestDrawAndResign(cur realm, t *testing.T) {
156	reset()
157	testing.SetOriginCaller(alice)
158	testing.SetRealm(testing.NewUserRealm(alice))
159	id := Challenge(cross(cur), bob.String(), "white")
160	testing.SetOriginCaller(bob)
161	testing.SetRealm(testing.NewUserRealm(bob))
162	AcceptChallenge(cross(cur), id)
163	g := mustGetGame(id)
164
165	testing.SetOriginCaller(alice)
166	testing.SetRealm(testing.NewUserRealm(alice))
167	OfferDraw(cross(cur), id)
168	uassert.AbortsWithMessage(t, cur, "there is no draw offer from your opponent", func() {
169		AcceptDraw(cross(cur), id)
170	})
171	testing.SetOriginCaller(bob)
172	testing.SetRealm(testing.NewUserRealm(bob))
173	AcceptDraw(cross(cur), id)
174	uassert.Equal(t, int(StateDraw), int(g.State))
175
176	// New game: offer is declined by moving, then black resigns.
177	testing.SetOriginCaller(alice)
178	testing.SetRealm(testing.NewUserRealm(alice))
179	id2 := Challenge(cross(cur), "", "white")
180	testing.SetOriginCaller(bob)
181	testing.SetRealm(testing.NewUserRealm(bob))
182	AcceptChallenge(cross(cur), id2)
183	g2 := mustGetGame(id2)
184	testing.SetOriginCaller(alice)
185	testing.SetRealm(testing.NewUserRealm(alice))
186	Move(cross(cur), id2, "b1c3")
187	testing.SetOriginCaller(alice)
188	testing.SetRealm(testing.NewUserRealm(alice))
189	OfferDraw(cross(cur), id2)
190	testing.SetOriginCaller(bob)
191	testing.SetRealm(testing.NewUserRealm(bob))
192	Move(cross(cur), id2, "g8f6")
193	uassert.Equal(t, "", g2.DrawOffer.String())
194	Resign(cross(cur), id2)
195	uassert.Equal(t, int(StateWhiteWon), int(g2.State))
196}
197
198func TestThreefoldRepetition(cur realm, t *testing.T) {
199	reset()
200	testing.SetOriginCaller(alice)
201	testing.SetRealm(testing.NewUserRealm(alice))
202	id := Challenge(cross(cur), "", "white")
203	testing.SetOriginCaller(bob)
204	testing.SetRealm(testing.NewUserRealm(bob))
205	AcceptChallenge(cross(cur), id)
206	// Knights hop out and back twice: the start position occurs 3 times.
207	moves := []string{"g1f3", "g8f6", "f3g1", "f6g8", "g1f3", "g8f6", "f3g1", "f6g8"}
208	for i, m := range moves {
209		if i%2 == 0 {
210			testing.SetOriginCaller(alice)
211			testing.SetRealm(testing.NewUserRealm(alice))
212		} else {
213			testing.SetOriginCaller(bob)
214			testing.SetRealm(testing.NewUserRealm(bob))
215		}
216		Move(cross(cur), id, m)
217	}
218	g := mustGetGame(id)
219	uassert.Equal(t, int(StateDraw), int(g.State))
220	uassert.Equal(t, "threefold repetition", g.Reason)
221}
222
223func TestSpecialMovesInRealm(cur realm, t *testing.T) {
224	reset()
225	testing.SetOriginCaller(alice)
226	testing.SetRealm(testing.NewUserRealm(alice))
227	id := Challenge(cross(cur), bob.String(), "white")
228	testing.SetOriginCaller(bob)
229	testing.SetRealm(testing.NewUserRealm(bob))
230	AcceptChallenge(cross(cur), id)
231	g := mustGetGame(id)
232	// Castling and en passant through the public API.
233	moves := []string{"e2e4", "a7a6", "e4e5", "d7d5", "e5d6", "c7d6", "g1f3", "b8c6", "f1e2", "c8g4", "e1g1"}
234	for i, m := range moves {
235		if i%2 == 0 {
236			testing.SetOriginCaller(alice)
237			testing.SetRealm(testing.NewUserRealm(alice))
238		} else {
239			testing.SetOriginCaller(bob)
240			testing.SetRealm(testing.NewUserRealm(bob))
241		}
242		Move(cross(cur), id, m)
243	}
244	uassert.Equal(t, int(mc.King), int(g.Pos.PieceAt("g1")))
245	uassert.Equal(t, int(mc.Rook), int(g.Pos.PieceAt("f1")))
246	uassert.Equal(t, int(mc.Empty), int(g.Pos.PieceAt("d5")))
247	uassert.Equal(t, int(StateActive), int(g.State))
248}
249
250func TestRender(cur realm, t *testing.T) {
251	reset()
252	testing.SetOriginCaller(alice)
253	testing.SetRealm(testing.NewUserRealm(alice))
254	id := NewSoloGame(cross(cur), "white", 1)
255	home := Render("")
256	uassert.True(t, strings.Contains(home, "# ♟ Mini Chess"))
257	uassert.True(t, strings.Contains(home, "$help&func=NewSoloGame&color=white&level=1"))
258
259	page := Render("game/1")
260	uassert.True(t, strings.Contains(page, "data:image/svg+xml;base64,"))
261	uassert.True(t, strings.Contains(page, "$help&func=Move&gameID=1&move=b1c3"), page)
262	uassert.True(t, strings.Contains(page, "White to move"))
263
264	uassert.True(t, strings.Contains(Render("player/"+alice.String()), "#1"))
265	uassert.True(t, strings.Contains(Render("rules"), "en passant"))
266	uassert.True(t, strings.Contains(Render("game/42"), "does not exist"))
267	uassert.Equal(t, mc.StartFEN, GameFEN(id))
268	uassert.Equal(t, "in progress, white to move", GameStatus(id))
269}