git.haldean.org grandmaster / 1f184dc
fuck source control: reformat everything Haldean Brown 8 months ago
23 changed file(s) with 2979 addition(s) and 2718 deletion(s). Raw diff Collapse all Expand all
2121 #include "test_core.h"
2222 #include "test_tree.h"
2323
24 int
25 main()
26 {
27 int n_failures;
28 SRunner *sr;
2429
25 int main()
26 {
27 int n_failures;
28 SRunner *sr;
30 sr = srunner_create(make_core_suite());
31 srunner_add_suite(sr, make_tree_suite());
32 srunner_run_all(sr, CK_NORMAL);
33 n_failures = srunner_ntests_failed(sr);
34 srunner_free(sr);
2935
30 sr = srunner_create(make_core_suite());
31 srunner_add_suite(sr, make_tree_suite());
32 srunner_run_all(sr, CK_NORMAL);
33 n_failures = srunner_ntests_failed(sr);
34 srunner_free(sr);
35
36 return n_failures;
36 return n_failures;
3737 }
3333 int
3434 check_status(char *fen)
3535 {
36 struct move *last;
37
38 last = parse_fen(fen, strlen(fen));
39 if (last == NULL) {
40 fprintf(stderr, "failed to parse FEN string %s\n", fen);
41 return FAILURE;
42 }
43
44 if (in_stalemate(last, opposite(last->player)))
45 return STALEMATE;
46 if (in_checkmate(last, opposite(last->player)))
47 return CHECKMATE;
48 if (in_check(last, opposite(last->player)))
49 return CHECK;
50 return MOVE_AVAILABLE;
36 struct move *last;
37
38 last = parse_fen(fen, strlen(fen));
39 if (last == NULL)
40 {
41 fprintf(stderr, "failed to parse FEN string %s\n", fen);
42 return FAILURE;
43 }
44
45 if (in_stalemate(last, opposite(last->player)))
46 return STALEMATE;
47 if (in_checkmate(last, opposite(last->player)))
48 return CHECKMATE;
49 if (in_check(last, opposite(last->player)))
50 return CHECK;
51 return MOVE_AVAILABLE;
5152 }
5253
5354 struct move *
5455 apply_moves_to_fen(char *fen, size_t n_moves, char *moves[])
5556 {
56 struct move *last;
57 struct move *next;
58 size_t i;
59
60 last = parse_fen(fen, strlen(fen));
61 for (i = 0; i < n_moves; i++) {
62 parse_algebraic(moves[i], last, &next);
63 if (next == NULL)
64 return NULL;
65 last = next;
66 }
67
68 return last;
57 struct move *last;
58 struct move *next;
59 size_t i;
60
61 last = parse_fen(fen, strlen(fen));
62 for (i = 0; i < n_moves; i++)
63 {
64 parse_algebraic(moves[i], last, &next);
65 if (next == NULL)
66 return NULL;
67 last = next;
68 }
69
70 return last;
6971 }
7072
7173 START_TEST(test_pawn)
7274 {
73 struct move *res;
74 char *move1[1];
75 char *move2[2];
76 char *move3[3];
77
78 move1[0] = "a4";
79 res = apply_moves_to_fen(START_FEN, 1, move1);
80 ck_assert_ptr_ne(res, NULL);
81 move1[0] = "a3";
82 res = apply_moves_to_fen(START_FEN, 1, move1);
83 ck_assert_ptr_ne(res, NULL);
84 move1[0] = "a5";
85 res = apply_moves_to_fen(START_FEN, 1, move1);
86 ck_assert_ptr_eq(res, NULL);
87
88 move2[0] = "a4"; move2[1] = "b5";
89 res = apply_moves_to_fen(START_FEN, 2, move2);
90 ck_assert_ptr_ne(res, NULL);
91
92 move3[0] = "a4"; move3[1] = "b5"; move3[2] = "axb5";
93 res = apply_moves_to_fen(START_FEN, 3, move3);
94 ck_assert_ptr_ne(res, NULL);
95 move3[0] = "a4"; move3[1] = "c5"; move3[2] = "axb5";
96 res = apply_moves_to_fen(START_FEN, 3, move3);
97 ck_assert_ptr_eq(res, NULL);
75 struct move *res;
76 char *move1[1];
77 char *move2[2];
78 char *move3[3];
79
80 move1[0] = "a4";
81 res = apply_moves_to_fen(START_FEN, 1, move1);
82 ck_assert_ptr_ne(res, NULL);
83 move1[0] = "a3";
84 res = apply_moves_to_fen(START_FEN, 1, move1);
85 ck_assert_ptr_ne(res, NULL);
86 move1[0] = "a5";
87 res = apply_moves_to_fen(START_FEN, 1, move1);
88 ck_assert_ptr_eq(res, NULL);
89
90 move2[0] = "a4";
91 move2[1] = "b5";
92 res = apply_moves_to_fen(START_FEN, 2, move2);
93 ck_assert_ptr_ne(res, NULL);
94
95 move3[0] = "a4";
96 move3[1] = "b5";
97 move3[2] = "axb5";
98 res = apply_moves_to_fen(START_FEN, 3, move3);
99 ck_assert_ptr_ne(res, NULL);
100 move3[0] = "a4";
101 move3[1] = "c5";
102 move3[2] = "axb5";
103 res = apply_moves_to_fen(START_FEN, 3, move3);
104 ck_assert_ptr_eq(res, NULL);
98105 }
99106 END_TEST
100107
101108 START_TEST(test_bishop)
102109 {
103 struct move *res;
104 char *move4[] = {"a4", "b5", "a5", "Ba6"};
105 char *move6[] = {"a4", "b5", "a5", "b6", "a6", "Ba6"};
106
107 res = apply_moves_to_fen(START_FEN, 4, move4);
108 ck_assert_ptr_ne(res, NULL);
109 res = apply_moves_to_fen(START_FEN, 6, move6);
110 ck_assert_ptr_eq(res, NULL);
110 struct move *res;
111 char *move4[] = { "a4", "b5", "a5", "Ba6" };
112 char *move6[] = { "a4", "b5", "a5", "b6", "a6", "Ba6" };
113
114 res = apply_moves_to_fen(START_FEN, 4, move4);
115 ck_assert_ptr_ne(res, NULL);
116 res = apply_moves_to_fen(START_FEN, 6, move6);
117 ck_assert_ptr_eq(res, NULL);
111118 }
112119 END_TEST
113120
114121 START_TEST(test_castle)
115122 {
116 struct move *res;
117 char *ks_castle[1] = {"O-O"};
118 char *qs_castle[1] = {"O-O-O"};
119 char *qs_rook_ks_castle[3] = {"Rb8", "c3", "O-O"};
120 char *qs_rook_qs_castle[3] = {"Rb8", "c3", "O-O-O"};
121 char *ks_rook_ks_castle[3] = {"Rg8", "c3", "O-O"};
122 char *ks_rook_qs_castle[3] = {"Rg8", "c3", "O-O-O"};
123
124 res = apply_moves_to_fen(
125 "r3kbnr/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b q - - -",
126 1, qs_castle);
127 ck_assert_ptr_ne(res, NULL);
128 ck_assert_str_eq(
129 res->post_board->fen,
130 "2kr1bnr/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
131
132 /* castle not available */
133 res = apply_moves_to_fen(
134 "r3kbnr/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b - - - -",
135 1, qs_castle);
136 ck_assert_ptr_eq(res, NULL);
137
138 res = apply_moves_to_fen(
139 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
140 1, ks_castle);
141 ck_assert_ptr_ne(res, NULL);
142 ck_assert_str_eq(
143 res->post_board->fen,
144 "r4rk1/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
145
146 res = apply_moves_to_fen(
147 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
148 1, qs_castle);
149 ck_assert_ptr_ne(res, NULL);
150 ck_assert_str_eq(
151 res->post_board->fen,
152 "2kr3r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
153
154 res = apply_moves_to_fen(
155 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
156 1, ks_castle);
157 ck_assert_ptr_ne(res, NULL);
158 ck_assert_str_eq(
159 res->post_board->fen,
160 "r4rk1/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
161
162 res = apply_moves_to_fen(
163 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
164 3, qs_rook_ks_castle);
165 ck_assert_ptr_ne(res, NULL);
166 ck_assert_str_eq(
167 res->post_board->fen,
168 "1r3rk1/p1pp1ppp/8/8/1p6/P1P1p3/1P1PPPPP/RNBQKBNR w - - - 2");
169
170 res = apply_moves_to_fen(
171 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
172 3, qs_rook_qs_castle);
173 ck_assert_ptr_eq(res, NULL);
174
175 res = apply_moves_to_fen(
176 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
177 3, ks_rook_qs_castle);
178 ck_assert_ptr_ne(res, NULL);
179 ck_assert_str_eq(
180 res->post_board->fen,
181 "2kr2r1/p1pp1ppp/8/8/1p6/P1P1p3/1P1PPPPP/RNBQKBNR w - - - 2");
182
183 res = apply_moves_to_fen(
184 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -",
185 3, ks_rook_ks_castle);
186 ck_assert_ptr_eq(res, NULL);
123 struct move *res;
124 char *ks_castle[1] = { "O-O" };
125 char *qs_castle[1] = { "O-O-O" };
126 char *qs_rook_ks_castle[3] = { "Rb8", "c3", "O-O" };
127 char *qs_rook_qs_castle[3] = { "Rb8", "c3", "O-O-O" };
128 char *ks_rook_ks_castle[3] = { "Rg8", "c3", "O-O" };
129 char *ks_rook_qs_castle[3] = { "Rg8", "c3", "O-O-O" };
130
131 res = apply_moves_to_fen(
132 "r3kbnr/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b q - - -", 1,
133 qs_castle);
134 ck_assert_ptr_ne(res, NULL);
135 ck_assert_str_eq(
136 res->post_board->fen,
137 "2kr1bnr/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
138
139 /* castle not available */
140 res = apply_moves_to_fen(
141 "r3kbnr/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b - - - -", 1,
142 qs_castle);
143 ck_assert_ptr_eq(res, NULL);
144
145 res = apply_moves_to_fen(
146 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 1,
147 ks_castle);
148 ck_assert_ptr_ne(res, NULL);
149 ck_assert_str_eq(
150 res->post_board->fen,
151 "r4rk1/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
152
153 res = apply_moves_to_fen(
154 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 1,
155 qs_castle);
156 ck_assert_ptr_ne(res, NULL);
157 ck_assert_str_eq(
158 res->post_board->fen,
159 "2kr3r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
160
161 res = apply_moves_to_fen(
162 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 1,
163 ks_castle);
164 ck_assert_ptr_ne(res, NULL);
165 ck_assert_str_eq(
166 res->post_board->fen,
167 "r4rk1/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR w - - - 1");
168
169 res = apply_moves_to_fen(
170 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 3,
171 qs_rook_ks_castle);
172 ck_assert_ptr_ne(res, NULL);
173 ck_assert_str_eq(
174 res->post_board->fen,
175 "1r3rk1/p1pp1ppp/8/8/1p6/P1P1p3/1P1PPPPP/RNBQKBNR w - - - 2");
176
177 res = apply_moves_to_fen(
178 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 3,
179 qs_rook_qs_castle);
180 ck_assert_ptr_eq(res, NULL);
181
182 res = apply_moves_to_fen(
183 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 3,
184 ks_rook_qs_castle);
185 ck_assert_ptr_ne(res, NULL);
186 ck_assert_str_eq(
187 res->post_board->fen,
188 "2kr2r1/p1pp1ppp/8/8/1p6/P1P1p3/1P1PPPPP/RNBQKBNR w - - - 2");
189
190 res = apply_moves_to_fen(
191 "r3k2r/p1pp1ppp/8/8/1p6/P3p3/1PPPPPPP/RNBQKBNR b kq - - -", 3,
192 ks_rook_ks_castle);
193 ck_assert_ptr_eq(res, NULL);
187194 }
188195 END_TEST
189196
190197 START_TEST(test_en_passant)
191198 {
192 struct move *res;
193 char *valid_moves[2] = {"c5", "dxc6"};
194 char *invalid_moves[4] = {"c5", "a3", "a5", "dxc6"};
195
196 res = apply_moves_to_fen(
197 "3k4/p1p5/8/3P4/8/8/P7/3K4 b - - - -", 2, valid_moves);
198 ck_assert_ptr_ne(res, NULL);
199 ck_assert_str_eq(
200 res->post_board->fen,
201 "3k4/p7/2P5/8/8/8/P7/3K4 b - - - 1");
202
203 res = apply_moves_to_fen(
204 "3k4/p1p5/8/3P4/8/8/P7/3K4 b - - - -", 4, invalid_moves);
205 ck_assert_ptr_eq(res, NULL);
199 struct move *res;
200 char *valid_moves[2] = { "c5", "dxc6" };
201 char *invalid_moves[4] = { "c5", "a3", "a5", "dxc6" };
202
203 res = apply_moves_to_fen(
204 "3k4/p1p5/8/3P4/8/8/P7/3K4 b - - - -", 2, valid_moves);
205 ck_assert_ptr_ne(res, NULL);
206 ck_assert_str_eq(
207 res->post_board->fen, "3k4/p7/2P5/8/8/8/P7/3K4 b - - - 1");
208
209 res = apply_moves_to_fen(
210 "3k4/p1p5/8/3P4/8/8/P7/3K4 b - - - -", 4, invalid_moves);
211 ck_assert_ptr_eq(res, NULL);
206212 }
207213 END_TEST
208214
209215 START_TEST(test_not_in_check)
210216 {
211 ck_assert_int_eq(
212 MOVE_AVAILABLE,
213 check_status("3k4/2p5/8/B2P4/8/8/8/3K4 b - - - -"));
217 ck_assert_int_eq(
218 MOVE_AVAILABLE,
219 check_status("3k4/2p5/8/B2P4/8/8/8/3K4 b - - - -"));
214220 }
215221 END_TEST
216222
217223 START_TEST(test_in_check)
218224 {
219 ck_assert_int_eq(
220 CHECK,
221 check_status("3k4/8/8/B2P4/8/8/8/3K4 b - - - -"));
222 ck_assert_int_eq(
223 CHECK,
224 check_status("3k4/8/8/B2P4/8/8/8/3K1r2 b - - - -"));
225 ck_assert_int_eq(
226 CHECK,
227 check_status("3k4/8/8/B2P4/8/8/8/3K1r2 w - - - -"));
228 ck_assert_int_eq(
229 CHECK,
230 check_status("3k4/3Q4/8/B2P4/8/8/8/3K4 b - - - -"));
231 ck_assert_int_eq(
232 CHECK,
233 check_status("3k4/r2Q4/8/3P4/B7/8/8/3K4 b - - - -"));
234 ck_assert_int_eq(
235 CHECK,
236 check_status("2Bkr3/r7/3Q4/8/B7/8/8/3K4 b - - - -"));
237 ck_assert_int_eq(
238 CHECK,
239 check_status("k7/8/3r4/4q3/5r2/8/2R5/4K3 w - - - -"));
225 ck_assert_int_eq(
226 CHECK, check_status("3k4/8/8/B2P4/8/8/8/3K4 b - - - -"));
227 ck_assert_int_eq(
228 CHECK, check_status("3k4/8/8/B2P4/8/8/8/3K1r2 b - - - -"));
229 ck_assert_int_eq(
230 CHECK, check_status("3k4/8/8/B2P4/8/8/8/3K1r2 w - - - -"));
231 ck_assert_int_eq(
232 CHECK, check_status("3k4/3Q4/8/B2P4/8/8/8/3K4 b - - - -"));
233 ck_assert_int_eq(
234 CHECK, check_status("3k4/r2Q4/8/3P4/B7/8/8/3K4 b - - - -"));
235 ck_assert_int_eq(
236 CHECK, check_status("2Bkr3/r7/3Q4/8/B7/8/8/3K4 b - - - -"));
237 ck_assert_int_eq(
238 CHECK, check_status("k7/8/3r4/4q3/5r2/8/2R5/4K3 w - - - -"));
240239 }
241240 END_TEST
242241
243242 START_TEST(test_check_movement)
244243 {
245 struct move *res;
246 char *qs_castle[1] = {"O-O-O"};
247 char *move_into_check[1] = {"f6"};
248 res = apply_moves_to_fen(
249 "r3kbnr/p1p2ppp/8/8/3R4/P7/1PPPPPPP/RNBQKBNR b kq - - -",
250 1, qs_castle);
251 ck_assert_ptr_eq(res, NULL);
252
253 res = apply_moves_to_fen(
254 "5bnr/4p1pq/4Qpkr/7p/7P/4P3/PPPP1PP1/RNB1KBNR b - - - -",
255 1, move_into_check);
256 ck_assert_ptr_eq(res, NULL);
244 struct move *res;
245 char *qs_castle[1] = { "O-O-O" };
246 char *move_into_check[1] = { "f6" };
247 res = apply_moves_to_fen(
248 "r3kbnr/p1p2ppp/8/8/3R4/P7/1PPPPPPP/RNBQKBNR b kq - - -", 1,
249 qs_castle);
250 ck_assert_ptr_eq(res, NULL);
251
252 res = apply_moves_to_fen(
253 "5bnr/4p1pq/4Qpkr/7p/7P/4P3/PPPP1PP1/RNB1KBNR b - - - -", 1,
254 move_into_check);
255 ck_assert_ptr_eq(res, NULL);
257256 }
258257 END_TEST
259258
260259 START_TEST(test_checkmate)
261260 {
262 ck_assert_int_eq(
263 CHECKMATE,
264 check_status("3k4/3Q4/8/3P4/B7/8/8/3K4 b - - - -"));
261 ck_assert_int_eq(
262 CHECKMATE, check_status("3k4/3Q4/8/3P4/B7/8/8/3K4 b - - - -"));
265263 }
266264 END_TEST
267265
268266 START_TEST(test_stalemate)
269267 {
270 ck_assert_int_eq(
271 STALEMATE,
272 check_status("3k4/8/8/8/8/1r6/2r5/K7 w - - - -"));
273 ck_assert_int_eq(
274 STALEMATE,
275 check_status("5bnr/4p1pq/4Qpkr/7p/7P/4P3/PPPP1PP1/RNB1KBNR b - - - -"));
268 ck_assert_int_eq(
269 STALEMATE, check_status("3k4/8/8/8/8/1r6/2r5/K7 w - - - -"));
270 ck_assert_int_eq(
271 STALEMATE,
272 check_status(
273 "5bnr/4p1pq/4Qpkr/7p/7P/4P3/PPPP1PP1/RNB1KBNR b - - - -"));
276274 }
277275 END_TEST
278276
279277 Suite *
280278 make_core_suite()
281279 {
282 Suite *s;
283 TCase *tc;
284
285 s = suite_create("core");
286 tc = tcase_create("movement");
287 tcase_add_test(tc, test_pawn);
288 tcase_add_test(tc, test_bishop);
289 tcase_add_test(tc, test_castle);
290 tcase_add_test(tc, test_en_passant);
291 suite_add_tcase(s, tc);
292
293
294 tc = tcase_create("in_check");
295 tcase_add_test(tc, test_in_check);
296 tcase_add_test(tc, test_not_in_check);
297 tcase_add_test(tc, test_checkmate);
298 tcase_add_test(tc, test_stalemate);
299 tcase_add_test(tc, test_check_movement);
300 suite_add_tcase(s, tc);
301
302 return s;
303 }
280 Suite *s;
281 TCase *tc;
282
283 s = suite_create("core");
284 tc = tcase_create("movement");
285 tcase_add_test(tc, test_pawn);
286 tcase_add_test(tc, test_bishop);
287 tcase_add_test(tc, test_castle);
288 tcase_add_test(tc, test_en_passant);
289 suite_add_tcase(s, tc);
290
291 tc = tcase_create("in_check");
292 tcase_add_test(tc, test_in_check);
293 tcase_add_test(tc, test_not_in_check);
294 tcase_add_test(tc, test_checkmate);
295 tcase_add_test(tc, test_stalemate);
296 tcase_add_test(tc, test_check_movement);
297 suite_add_tcase(s, tc);
298
299 return s;
300 }
2727
2828 START_TEST(test_make_move_wrong_player)
2929 {
30 struct game_tree *gt;
31 game_id_t g1;
30 struct game_tree *gt;
31 game_id_t g1;
3232
33 gt = calloc(1, sizeof(struct game_tree));
34 init_gametree(gt);
33 gt = calloc(1, sizeof(struct game_tree));
34 init_gametree(gt);
3535
36 g1 = new_game(gt, 1, 2);
36 g1 = new_game(gt, 1, 2);
3737
38 ck_assert(!make_move(gt, g1, 2, "e4"));
39 ck_assert(make_move(gt, g1, 1, "e4"));
38 ck_assert(!make_move(gt, g1, 2, "e4"));
39 ck_assert(make_move(gt, g1, 1, "e4"));
4040
41 free_game_tree(gt);
41 free_game_tree(gt);
4242 }
4343 END_TEST
4444
4545 START_TEST(test_tree_notation_dedup)
4646 {
47 struct game_tree *gt;
48 game_id_t g1, g2;
47 struct game_tree *gt;
48 game_id_t g1, g2;
4949
50 gt = calloc(1, sizeof(struct game_tree));
51 init_gametree(gt);
50 gt = calloc(1, sizeof(struct game_tree));
51 init_gametree(gt);
5252
53 g1 = new_game(gt, 12, 56);
54 g2 = new_game(gt, 34, 56);
53 g1 = new_game(gt, 12, 56);
54 g2 = new_game(gt, 34, 56);
5555
56 ck_assert(make_move(gt, g1, 12, "e4"));
57 ck_assert(make_move(gt, g2, 34, "e4?"));
56 ck_assert(make_move(gt, g1, 12, "e4"));
57 ck_assert(make_move(gt, g2, 34, "e4?"));
5858
59 ck_assert_int_eq(2, gt->n_states);
60 ck_assert_ptr_eq(gt->games[g1]->current, gt->games[g2]->current);
59 ck_assert_int_eq(2, gt->n_states);
60 ck_assert_ptr_eq(gt->games[g1]->current, gt->games[g2]->current);
6161
62 free_game_tree(gt);
62 free_game_tree(gt);
6363 }
6464 END_TEST
6565
6666 START_TEST(test_tree)
6767 {
68 struct game_tree *gt;
69 game_id_t g1, g2, g3;
68 struct game_tree *gt;
69 game_id_t g1, g2, g3;
7070
71 gt = calloc(1, sizeof(struct game_tree));
72 init_gametree(gt);
71 gt = calloc(1, sizeof(struct game_tree));
72 init_gametree(gt);
7373
74 g1 = new_game(gt, 12, 56);
75 g2 = new_game(gt, 34, 56);
76 g3 = new_game(gt, 12, 34);
74 g1 = new_game(gt, 12, 56);
75 g2 = new_game(gt, 34, 56);
76 g3 = new_game(gt, 12, 34);
7777
78 ck_assert(make_move(gt, g1, 12, "e4"));
79 ck_assert(make_move(gt, g1, 56, "d5"));
80 ck_assert(make_move(gt, g2, 34, "e4"));
81 ck_assert(make_move(gt, g3, 12, "Nc3"));
78 ck_assert(make_move(gt, g1, 12, "e4"));
79 ck_assert(make_move(gt, g1, 56, "d5"));
80 ck_assert(make_move(gt, g2, 34, "e4"));
81 ck_assert(make_move(gt, g3, 12, "Nc3"));
8282
83 ck_assert_int_eq(4, gt->n_states);
84 ck_assert_ptr_eq(gt->games[g1]->current->parent, gt->games[g2]->current);
83 ck_assert_int_eq(4, gt->n_states);
84 ck_assert_ptr_eq(
85 gt->games[g1]->current->parent, gt->games[g2]->current);
8586
86 free_game_tree(gt);
87 free_game_tree(gt);
8788 }
8889 END_TEST
8990
9091 Suite *
9192 make_tree_suite()
9293 {
93 Suite *s;
94 TCase *tc;
94 Suite *s;
95 TCase *tc;
9596
96 s = suite_create("tree");
97 tc = tcase_create("tree");
98 tcase_add_test(tc, test_tree);
99 tcase_add_test(tc, test_tree_notation_dedup);
100 tcase_add_test(tc, test_make_move_wrong_player);
101 suite_add_tcase(s, tc);
97 s = suite_create("tree");
98 tc = tcase_create("tree");
99 tcase_add_test(tc, test_tree);
100 tcase_add_test(tc, test_tree_notation_dedup);
101 tcase_add_test(tc, test_make_move_wrong_player);
102 suite_add_tcase(s, tc);
102103
103 return s;
104 return s;
104105 }
2525 int
2626 main(int argc, char *argv[])
2727 {
28 char *op_mode;
29 if (argc < 2) {
30 fprintf(stderr, "usage: gm [client|server]\n");
28 char *op_mode;
29 if (argc < 2)
30 {
31 fprintf(stderr, "usage: gm [client|server]\n");
32 return 1;
33 }
34 op_mode = argv[1];
35 argc--;
36 argv++;
37 if (strcmp(op_mode, "client") == 0)
38 return client_main();
39 if (strcmp(op_mode, "server") == 0)
40 return server_main(argc, argv);
41 fprintf(stderr, "unrecognized operating mode %s\n", op_mode);
3142 return 1;
32 }
33 op_mode = argv[1];
34 argc--; argv++;
35 if (strcmp(op_mode, "client") == 0)
36 return client_main();
37 if (strcmp(op_mode, "server") == 0)
38 return server_main(argc, argv);
39 fprintf(stderr, "unrecognized operating mode %s\n", op_mode);
40 return 1;
4143 }
2222 #include <stdio.h>
2323 #include <stdlib.h>
2424 #include <string.h>
25 #include <sys/socket.h>
2526 #include <sys/types.h>
26 #include <sys/socket.h>
2727 #include <unistd.h>
2828
2929 #define GM_PORT ("61234")
3131 int
3232 client_main()
3333 {
34 int err;
35 int msglen;
36 int sockfd;
37 char *buf;
38 struct addrinfo *self;
39 struct addrinfo hints;
40 char msg[MAX_MSG_LEN];
34 int err;
35 int msglen;
36 int sockfd;
37 char *buf;
38 struct addrinfo *self;
39 struct addrinfo hints;
40 char msg[MAX_MSG_LEN];
4141
42 msglen = read(STDIN_FILENO, msg, MAX_MSG_LEN - 1);
43 msg[msglen++] = 0;
42 msglen = read(STDIN_FILENO, msg, MAX_MSG_LEN - 1);
43 msg[msglen++] = 0;
4444
45 memset(&hints, 0x00, sizeof(struct addrinfo));
46 hints.ai_family = AF_INET;
47 hints.ai_socktype = SOCK_STREAM;
48 hints.ai_flags = 0;
45 memset(&hints, 0x00, sizeof(struct addrinfo));
46 hints.ai_family = AF_INET;
47 hints.ai_socktype = SOCK_STREAM;
48 hints.ai_flags = 0;
4949
50 err = getaddrinfo(NULL, GM_PORT, &hints, &self);
51 if (err) {
52 fprintf(stderr, "E: getaddrinfo error: %s\n", gai_strerror(err));
53 return -1;
54 }
50 err = getaddrinfo(NULL, GM_PORT, &hints, &self);
51 if (err)
52 {
53 fprintf(
54 stderr, "E: getaddrinfo error: %s\n", gai_strerror(err));
55 return -1;
56 }
5557
56 sockfd = socket(self->ai_family, self->ai_socktype, self->ai_protocol);
57 if (sockfd == -1) {
58 perror("E: socket error");
59 return -1;
60 }
58 sockfd = socket(self->ai_family, self->ai_socktype, self->ai_protocol);
59 if (sockfd == -1)
60 {
61 perror("E: socket error");
62 return -1;
63 }
6164
62 err = connect(sockfd, self->ai_addr, self->ai_addrlen);
63 if (err) {
64 perror("E: connect error");
65 return -1;
66 }
65 err = connect(sockfd, self->ai_addr, self->ai_addrlen);
66 if (err)
67 {
68 perror("E: connect error");
69 return -1;
70 }
6771
68 send_str(sockfd, msg);
69 buf = read_str(sockfd, MAX_MSG_LEN);
72 send_str(sockfd, msg);
73 buf = read_str(sockfd, MAX_MSG_LEN);
7074
71 if (!buf) {
72 fprintf(stderr, "E: got no response\n");
73 return -1;
74 }
75 fprintf(stderr, "OK\n");
76 printf("%s\n", buf);
77 return 0;
75 if (!buf)
76 {
77 fprintf(stderr, "E: got no response\n");
78 return -1;
79 }
80 fprintf(stderr, "OK\n");
81 printf("%s\n", buf);
82 return 0;
7883 }
1717 */
1818
1919 #include <grandmaster/core.h>
20 #include <grandmaster/gmutil.h>
2021 #include <grandmaster/tree.h>
21 #include <grandmaster/gmutil.h>
2222
2323 #include <jansson.h>
2424 #include <netdb.h>
2525 #include <signal.h>
2626 #include <stdio.h>
2727 #include <string.h>
28 #include <sys/socket.h>
2829 #include <sys/types.h>
29 #include <sys/socket.h>
3030 #include <unistd.h>
3131
3232 #define GM_PORT ("61234")
3434 static int sockfd = -1;
3535
3636 json_t *
37 handle_json(
38 struct game_tree *gt,
39 json_t *req)
40 {
41 const char *req_kind;
42 size_t req_kind_len;
43 json_t *resp;
44 json_t *t;
45
46 t = json_object_get(req, "kind");
47 if (t == NULL) {
48 resp = json_pack("{ss}", "error", "no kind in request");
49 } else {
50 req_kind = json_string_value(t);
51 req_kind_len = strnlen(req_kind, MAX_MSG_LEN);
52
53 if (strncmp(req_kind, "new_game", req_kind_len) == 0) {
54 resp = handle_new_game(gt, req);
55 } else if (strncmp(req_kind, "get_game", req_kind_len) == 0) {
56 resp = handle_get_game(gt, req);
57 } else if (strncmp(req_kind, "move", req_kind_len) == 0) {
58 resp = handle_move(gt, req);
59 } else if (strncmp(req_kind, "game_from_pgn", req_kind_len) == 0) {
60 resp = handle_game_from_pgn(gt, req);
61 } else if (strncmp(req_kind, "end_game", req_kind_len) == 0) {
62 resp = handle_end_game(gt, req);
63 } else {
64 resp = json_pack("{ss}", "error", "unknown kind");
65 }
66 }
67
68 return resp;
37 handle_json(struct game_tree *gt, json_t *req)
38 {
39 const char *req_kind;
40 size_t req_kind_len;
41 json_t *resp;
42 json_t *t;
43
44 t = json_object_get(req, "kind");
45 if (t == NULL)
46 {
47 resp = json_pack("{ss}", "error", "no kind in request");
48 }
49 else
50 {
51 req_kind = json_string_value(t);
52 req_kind_len = strnlen(req_kind, MAX_MSG_LEN);
53
54 if (strncmp(req_kind, "new_game", req_kind_len) == 0)
55 {
56 resp = handle_new_game(gt, req);
57 }
58 else if (strncmp(req_kind, "get_game", req_kind_len) == 0)
59 {
60 resp = handle_get_game(gt, req);
61 }
62 else if (strncmp(req_kind, "move", req_kind_len) == 0)
63 {
64 resp = handle_move(gt, req);
65 }
66 else if (strncmp(req_kind, "game_from_pgn", req_kind_len) == 0)
67 {
68 resp = handle_game_from_pgn(gt, req);
69 }
70 else if (strncmp(req_kind, "end_game", req_kind_len) == 0)
71 {
72 resp = handle_end_game(gt, req);
73 }
74 else
75 {
76 resp = json_pack("{ss}", "error", "unknown kind");
77 }
78 }
79
80 return resp;
6981 }
7082
7183 int
7284 load_aol(struct game_tree *gt, FILE *aol)
7385 {
74 int msg_n;
75 long region_start;
76 long region_end;
77 char buf[MAX_MSG_LEN];
78 size_t read_len;
79 size_t i;
80 int res;
81 json_t *req;
82 json_error_t json_err;
83
84 rewind(aol);
85 msg_n = 0;
86
87 while (!feof(aol)) {
88 region_start = ftell(aol);
89 if (region_start < 0) {
90 perror("E: aol ftell failed");
91 return 1;
92 }
93
94 read_len = fread(buf, sizeof(char), MAX_MSG_LEN, aol);
95 if (read_len == 0)
96 break;
97 for (i = 0; i < read_len; i++)
98 if (buf[i] == 0)
99 break;
100 if (buf[i] == 0) {
101 region_end = region_start + i;
102 } else {
103 printf("E: no trailing NUL on aol record %d\n", msg_n);
104 return 1;
105 }
106
107 req = json_loads(buf, 0, &json_err);
108 if (!req) {
109 printf("E: unable to parse record %d\n", msg_n);
110 return 1;
111 }
112 if (handle_json(gt, req) == NULL) {
113 printf("E: failed to parse record %d\n", msg_n);
114 return 1;
115 }
116
117 res = fseek(aol, region_end + 1, SEEK_SET);
118 if (res != 0) {
119 perror("E: aol seek failed");
120 return 1;
121 }
122 msg_n++;
123 }
124
125 printf("I: read %d records from aol\n", msg_n);
126 return 0;
86 int msg_n;
87 long region_start;
88 long region_end;
89 char buf[MAX_MSG_LEN];
90 size_t read_len;
91 size_t i;
92 int res;
93 json_t *req;
94 json_error_t json_err;
95
96 rewind(aol);
97 msg_n = 0;
98
99 while (!feof(aol))
100 {
101 region_start = ftell(aol);
102 if (region_start < 0)
103 {
104 perror("E: aol ftell failed");
105 return 1;
106 }
107
108 read_len = fread(buf, sizeof(char), MAX_MSG_LEN, aol);
109 if (read_len == 0)
110 break;
111 for (i = 0; i < read_len; i++)
112 if (buf[i] == 0)
113 break;
114 if (buf[i] == 0)
115 {
116 region_end = region_start + i;
117 }
118 else
119 {
120 printf("E: no trailing NUL on aol record %d\n", msg_n);
121 return 1;
122 }
123
124 req = json_loads(buf, 0, &json_err);
125 if (!req)
126 {
127 printf("E: unable to parse record %d\n", msg_n);
128 return 1;
129 }
130 if (handle_json(gt, req) == NULL)
131 {
132 printf("E: failed to parse record %d\n", msg_n);
133 return 1;
134 }
135
136 res = fseek(aol, region_end + 1, SEEK_SET);
137 if (res != 0)
138 {
139 perror("E: aol seek failed");
140 return 1;
141 }
142 msg_n++;
143 }
144
145 printf("I: read %d records from aol\n", msg_n);
146 return 0;
127147 }
128148
129149 bool
130 handle_client(
131 struct game_tree *gt,
132 int insock,
133 FILE *aol)
134 {
135 char *req_msg;
136 char *resp_msg;
137 json_t *req;
138 size_t req_len;
139 json_t *resp;
140 json_t *t;
141 json_error_t json_err;
142 bool ok;
143
144 ok = false;
145
146 req_msg = read_str(insock, MAX_MSG_LEN);
147 if (req_msg == NULL) {
148 fprintf(stderr, "I: unable to load message string\n");
149 resp = json_pack("{ss}", "error", "couldn't load message string");
150 goto close;
151 }
152 req_len = strlen(req_msg);
153
154 req = json_loads(req_msg, 0, &json_err);
155 if (!req) {
156 fprintf(stderr, "I: unable to parse json\n");
157 resp = json_pack("{ss}", "error", "couldn't parse json");
158 goto close;
159 }
160
161 resp = handle_json(gt, req);
162 if (resp == NULL)
163 goto close;
164
165 t = json_object_get(resp, "error");
166 if (json_string_value(t) == NULL) {
167 /* +1 to account for null byte, which acts as a delimiter */
168 fwrite(req_msg, req_len + 1, 1, aol);
169 fflush(aol);
170 ok = true;
171 }
150 handle_client(struct game_tree *gt, int insock, FILE *aol)
151 {
152 char *req_msg;
153 char *resp_msg;
154 json_t *req;
155 size_t req_len;
156 json_t *resp;
157 json_t *t;
158 json_error_t json_err;
159 bool ok;
160
161 ok = false;
162
163 req_msg = read_str(insock, MAX_MSG_LEN);
164 if (req_msg == NULL)
165 {
166 fprintf(stderr, "I: unable to load message string\n");
167 resp =
168 json_pack("{ss}", "error", "couldn't load message string");
169 goto close;
170 }
171 req_len = strlen(req_msg);
172
173 req = json_loads(req_msg, 0, &json_err);
174 if (!req)
175 {
176 fprintf(stderr, "I: unable to parse json\n");
177 resp = json_pack("{ss}", "error", "couldn't parse json");
178 goto close;
179 }
180
181 resp = handle_json(gt, req);
182 if (resp == NULL)
183 goto close;
184
185 t = json_object_get(resp, "error");
186 if (json_string_value(t) == NULL)
187 {
188 /* +1 to account for null byte, which acts as a delimiter */
189 fwrite(req_msg, req_len + 1, 1, aol);
190 fflush(aol);
191 ok = true;
192 }
172193
173194 close:
174 free(req_msg);
175 if (req != NULL)
176 json_decref(req);
177 if (resp != NULL) {
178 resp_msg = json_dumps(resp, JSON_PRESERVE_ORDER | JSON_INDENT(4));
179 send_str(insock, resp_msg);
180 free(resp_msg);
181 json_decref(resp);
182 }
183 close(insock);
184 return ok;
195 free(req_msg);
196 if (req != NULL)
197 json_decref(req);
198 if (resp != NULL)
199 {
200 resp_msg =
201 json_dumps(resp, JSON_PRESERVE_ORDER | JSON_INDENT(4));
202 send_str(insock, resp_msg);
203 free(resp_msg);
204 json_decref(resp);
205 }
206 close(insock);
207 return ok;
185208 }
186209
187210 void
188211 run_gm(struct game_tree *gt, FILE *aol)
189212 {
190 int err;
191 int insock;
192 struct addrinfo *self;
193 struct addrinfo hints;
194 struct sockaddr inaddr;
195 socklen_t inaddr_len;
196
197 sockfd = -1;
198
199 memset(&hints, 0x00, sizeof(struct addrinfo));
200 hints.ai_family = AF_INET;
201 hints.ai_flags = AI_PASSIVE;
202 hints.ai_protocol = 0;
203 hints.ai_socktype = SOCK_STREAM;
204
205 err = getaddrinfo(NULL, GM_PORT, &hints, &self);
206 if (err) {
207 fprintf(stderr, "E: getaddrinfo error: %s\n", gai_strerror(err));
208 return;
209 }
210
211 sockfd = socket(self->ai_family, self->ai_socktype, self->ai_protocol);
212 if (sockfd == -1) {
213 perror("E: socket error");
214 return;
215 }
216
217 err = bind(sockfd, self->ai_addr, self->ai_addrlen);
218 if (err) {
219 perror("E: bind error");
220 return;
221 }
222
223 err = listen(sockfd, 10);
224 if (err) {
225 perror("E: listen error");
226 return;
227 }
228
229 for (;;) {
230 inaddr_len = sizeof(struct sockaddr);
231 insock = accept(sockfd, &inaddr, &inaddr_len);
232 if (insock == -1) {
233 perror("E: accept error");
234 goto close;
235 }
236 handle_client(gt, insock, aol);
237 }
213 int err;
214 int insock;
215 struct addrinfo *self;
216 struct addrinfo hints;
217 struct sockaddr inaddr;
218 socklen_t inaddr_len;
219
220 sockfd = -1;
221
222 memset(&hints, 0x00, sizeof(struct addrinfo));
223 hints.ai_family = AF_INET;
224 hints.ai_flags = AI_PASSIVE;
225 hints.ai_protocol = 0;
226 hints.ai_socktype = SOCK_STREAM;
227
228 err = getaddrinfo(NULL, GM_PORT, &hints, &self);
229 if (err)
230 {
231 fprintf(
232 stderr, "E: getaddrinfo error: %s\n", gai_strerror(err));
233 return;
234 }
235
236 sockfd = socket(self->ai_family, self->ai_socktype, self->ai_protocol);
237 if (sockfd == -1)
238 {
239 perror("E: socket error");
240 return;
241 }
242
243 err = bind(sockfd, self->ai_addr, self->ai_addrlen);
244 if (err)
245 {
246 perror("E: bind error");
247 return;
248 }
249
250 err = listen(sockfd, 10);
251 if (err)
252 {
253 perror("E: listen error");
254 return;
255 }
256
257 for (;;)
258 {
259 inaddr_len = sizeof(struct sockaddr);
260 insock = accept(sockfd, &inaddr, &inaddr_len);
261 if (insock == -1)
262 {
263 perror("E: accept error");
264 goto close;
265 }
266 handle_client(gt, insock, aol);
267 }
238268
239269 close:
240 freeaddrinfo(self);
241 if (sockfd != -1)
242 close(sockfd);
270 freeaddrinfo(self);
271 if (sockfd != -1)
272 close(sockfd);
243273 }
244274
245275 void
246276 handle_signal(int sig)
247277 {
248 int old_sockfd;
249 switch (sig) {
250 case SIGTERM:
251 case SIGINT:
252 if (sockfd != -1) {
253 /* set sockfd to -1 before closing it, so that the event loop
254 * doesn't try to close the socket a second time immediately after
255 * the call to accept fails. */
256 old_sockfd = sockfd;
257 sockfd = -1;
258 close(old_sockfd);
259 }
260 }
278 int old_sockfd;
279 switch (sig)
280 {
281 case SIGTERM:
282 case SIGINT:
283 if (sockfd != -1)
284 {
285 /* set sockfd to -1 before closing it, so that
286 * the event loop doesn't try to close the
287 * socket a second time immediately after the
288 * call to accept fails. */
289 old_sockfd = sockfd;
290 sockfd = -1;
291 close(old_sockfd);
292 }
293 }
261294 }
262295
263296 int
264297 server_main(int argc, char *argv[])
265298 {
266 struct game_tree gt;
267 FILE *aol;
268 char *aol_path;
269 int res;
270
271 if (argc < 2) {
272 printf("usage: gm server path/to/append-only.log\n");
273 return 1;
274 }
275 aol_path = argv[1];
276
277 signal(SIGTERM, handle_signal);
278 signal(SIGINT, handle_signal);
279
280 aol = fopen(aol_path, "a+");
281 if (aol == NULL) {
282 perror("E: append-only log couldn't be opened");
283 return 1;
284 }
285
286 init_gametree(&gt);
287 res = load_aol(&gt, aol);
288 if (res != 0)
289 return res;
290 run_gm(&gt, aol);
291 return 0;
292 }
299 struct game_tree gt;
300 FILE *aol;
301 char *aol_path;
302 int res;
303
304 if (argc < 2)
305 {
306 printf("usage: gm server path/to/append-only.log\n");
307 return 1;
308 }
309 aol_path = argv[1];
310
311 signal(SIGTERM, handle_signal);
312 signal(SIGINT, handle_signal);
313
314 aol = fopen(aol_path, "a+");
315 if (aol == NULL)
316 {
317 perror("E: append-only log couldn't be opened");
318 return 1;
319 }
320
321 init_gametree(&gt);
322 res = load_aol(&gt, aol);
323 if (res != 0)
324 return res;
325 run_gm(&gt, aol);
326 return 0;
327 }
2727 #include <string.h>
2828 #include <sys/socket.h>
2929
30
3130 char *
3231 read_str(int sock, ssize_t max_len)
3332 {
34 ssize_t read_len;
35 int32_t msg_len;
36 char *str;
33 ssize_t read_len;
34 int32_t msg_len;
35 char *str;
3736
38 read_len = recv(sock, &msg_len, sizeof(msg_len), 0);
39 if (read_len < (ssize_t) sizeof(msg_len)) {
40 return NULL;
41 }
42 msg_len = ntohl(msg_len);
43 if (msg_len > max_len) {
44 fprintf(stderr, "W: msg len %d too big: max len %ld. discarding.\n",
45 msg_len, max_len);
46 return NULL;
47 }
48 str = malloc(msg_len + 1);
49 read_len = recv(sock, str, msg_len, 0);
50 if (read_len < msg_len) {
51 free(str);
52 return NULL;
53 }
54 str[msg_len] = 0;
55 return str;
37 read_len = recv(sock, &msg_len, sizeof(msg_len), 0);
38 if (read_len < (ssize_t)sizeof(msg_len))
39 {
40 return NULL;
41 }
42 msg_len = ntohl(msg_len);
43 if (msg_len > max_len)
44 {
45 fprintf(
46 stderr,
47 "W: msg len %d too big: max len %ld. discarding.\n",
48 msg_len, max_len);
49 return NULL;
50 }
51 str = malloc(msg_len + 1);
52 read_len = recv(sock, str, msg_len, 0);
53 if (read_len < msg_len)
54 {
55 free(str);
56 return NULL;
57 }
58 str[msg_len] = 0;
59 return str;
5660 }
5761
5862 int
5963 send_str(int sock, char *str)
6064 {
61 ssize_t msg_len;
62 ssize_t sent;
63 ssize_t total_sent;
64 uint32_t msg_len_n;
65 ssize_t msg_len;
66 ssize_t sent;
67 ssize_t total_sent;
68 uint32_t msg_len_n;
6569
66 msg_len = strlen(str);
67 msg_len_n = htonl(msg_len);
68 sent = send(sock, &msg_len_n, sizeof(msg_len_n), 0);
69 if (sent == -1) {
70 perror("E: failed to send message");
71 }
72 if (sent != sizeof(msg_len_n)) {
73 fprintf(stderr, "E: failed to send message header");
74 return -1;
75 }
70 msg_len = strlen(str);
71 msg_len_n = htonl(msg_len);
72 sent = send(sock, &msg_len_n, sizeof(msg_len_n), 0);
73 if (sent == -1)
74 {
75 perror("E: failed to send message");
76 }
77 if (sent != sizeof(msg_len_n))
78 {
79 fprintf(stderr, "E: failed to send message header");
80 return -1;
81 }
7682
77 total_sent = 0;
78 while (total_sent < msg_len) {
79 sent = send(sock, str + total_sent, msg_len - total_sent, 0);
80 if (sent == -1) {
81 perror("E: failed to send message");
83 total_sent = 0;
84 while (total_sent < msg_len)
85 {
86 sent = send(sock, str + total_sent, msg_len - total_sent, 0);
87 if (sent == -1)
88 {
89 perror("E: failed to send message");
90 }
91 total_sent += sent;
8292 }
83 total_sent += sent;
84 }
85 return 0;
93 return 0;
8694 }
1616 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1717 */
1818
19
2019 #include <grandmaster/core.h>
20 #include <grandmaster/gmutil.h>
2121 #include <grandmaster/tree.h>
22 #include <grandmaster/gmutil.h>
2322
2423 #include <jansson.h>
2524
26 #define get(t, req, field) \
27 t = json_object_get(req, field); \
28 if (!t) return json_pack("{ss}", "error", "missing field " field);
25 #define get(t, req, field) \
26 t = json_object_get(req, field); \
27 if (!t) \
28 return json_pack("{ss}", "error", "missing field " field);
2929
3030 json_t *
3131 game_state(struct game_tree *gt, game_id_t game_id)
3232 {
33 json_t *res;
34 struct move *move;
35 struct game *game;
36
37 game = get_game(gt, game_id);
38 if (game == NULL)
39 return NULL;
40 move = game->current->move;
41 res = board_to_json(move->post_board);
42 json_object_set_new(
43 res, "termination",
44 json_string(termination_str(game->termination)));
45 return res;
33 json_t *res;
34 struct move *move;
35 struct game *game;
36
37 game = get_game(gt, game_id);
38 if (game == NULL)
39 return NULL;
40 move = game->current->move;
41 res = board_to_json(move->post_board);
42 json_object_set_new(
43 res, "termination",
44 json_string(termination_str(game->termination)));
45 return res;
4646 }
4747
4848 json_t *
4949 handle_new_game(struct game_tree *gt, json_t *req)
5050 {
51 game_id_t game;
52 player_id_t white;
53 player_id_t black;
54 json_t *t;
55
56 get(t, req, "player_white");
57 white = json_integer_value(t);
58
59 get(t, req, "player_black");
60 black = json_integer_value(t);
61
62 game = new_game(gt, white, black);
63 return json_pack("{sIsosn}",
64 "game_id", game,
65 "state", game_state(gt, game),
51 game_id_t game;
52 player_id_t white;
53 player_id_t black;
54 json_t *t;
55
56 get(t, req, "player_white");
57 white = json_integer_value(t);
58
59 get(t, req, "player_black");
60 black = json_integer_value(t);
61
62 game = new_game(gt, white, black);
63 return json_pack(
64 "{sIsosn}", "game_id", game, "state", game_state(gt, game),
6665 "error" /* undefined */);
6766 }
6867
6968 json_t *
7069 handle_new_game(struct game_tree *gt, json_t *req)
7170 {
72 game_id_t game;
73 json_t *t;
74
75 get(t, req, "game_id");
76 game = json_integer_value(t);
77
78 return json_pack("{sIsosn}",
79 "game_id", game,
80 "state", game_state(gt, game),
71 game_id_t game;
72 json_t *t;
73
74 get(t, req, "game_id");
75 game = json_integer_value(t);
76
77 return json_pack(
78 "{sIsosn}", "game_id", game, "state", game_state(gt, game),
8179 "error" /* undefined */);
8280 }
8381
8482 json_t *
8583 handle_game_from_pgn(struct game_tree *gt, json_t *req)
8684 {
87 game_id_t game;
88 player_id_t white;
89 player_id_t black;
90 const char *pgn;
91 json_t *t;
92
93 get(t, req, "player_white");
94 white = json_integer_value(t);
95
96 get(t, req, "player_black");
97 black = json_integer_value(t);
98
99 get(t, req, "pgn");
100 pgn = json_string_value(t);
101
102 game = new_game_from_pgn(gt, white, black, pgn);
103 if (game == NO_GAME)
104 return json_pack("{ss}", "error", "could not parse PGN");
105 return json_pack("{sIsosn}",
106 "game_id", game,
107 "state", game_state(gt, game),
85 game_id_t game;
86 player_id_t white;
87 player_id_t black;
88 const char *pgn;
89 json_t *t;
90
91 get(t, req, "player_white");
92 white = json_integer_value(t);
93
94 get(t, req, "player_black");
95 black = json_integer_value(t);
96
97 get(t, req, "pgn");
98 pgn = json_string_value(t);
99
100 game = new_game_from_pgn(gt, white, black, pgn);
101 if (game == NO_GAME)
102 return json_pack("{ss}", "error", "could not parse PGN");
103 return json_pack(
104 "{sIsosn}", "game_id", game, "state", game_state(gt, game),
108105 "error" /* undefined */);
109106 }
110107
111108 json_t *
112109 handle_move(struct game_tree *gt, json_t *req)
113110 {
114 game_id_t game_id;
115 player_id_t player;
116 const char *notation;
117 json_t *t;
118 bool success;
119
120 get(t, req, "player");
121 player = json_integer_value(t);
122
123 get(t, req, "game_id");
124 game_id = json_integer_value(t);
125
126 get(t, req, "move");
127 notation = json_string_value(t);
128
129 if (get_game(gt, game_id) == NULL) {
130 return json_pack("{snss}", "state", "error", "game does not exist");
131 }
132
133 success = make_move(gt, game_id, player, notation);
134 if (success) {
135 return json_pack("{sosn}", "state", game_state(gt, game_id), "error");
136 }
137 return json_pack("{snss}", "state", "error", "could not perform move");
111 game_id_t game_id;
112 player_id_t player;
113 const char *notation;
114 json_t *t;
115 bool success;
116
117 get(t, req, "player");
118 player = json_integer_value(t);
119
120 get(t, req, "game_id");
121 game_id = json_integer_value(t);
122
123 get(t, req, "move");
124 notation = json_string_value(t);
125
126 if (get_game(gt, game_id) == NULL)
127 {
128 return json_pack(
129 "{snss}", "state", "error", "game does not exist");
130 }
131
132 success = make_move(gt, game_id, player, notation);
133 if (success)
134 {
135 return json_pack(
136 "{sosn}", "state", game_state(gt, game_id), "error");
137 }
138 return json_pack("{snss}", "state", "error", "could not perform move");
138139 }
139140
140141 json_t *
141142 handle_end_game(struct game_tree *gt, json_t *req)
142143 {
143 game_id_t game_id;
144 player_id_t player;
145 termination_t termination;
146 json_t *t;
147 bool success;
148 struct game *game;
149 color_t player_color;
150
151 get(t, req, "player");
152 player = json_integer_value(t);
153
154 get(t, req, "game_id");
155 game_id = json_integer_value(t);
156
157 get(t, req, "termination");
158 termination = termination_from_str(json_string_value(t));
159
160 if (get_game(gt, game_id) == NULL) {
161 return json_pack("{snss}", "state", "error", "game does not exist");
162 }
163 if (termination == INVALID) {
164 return json_pack("{snss}", "state", "error", "invalid termination");
165 }
166
167 game = get_game(gt, game_id);
168 if (player == game->player_white)
169 player_color = WHITE;
170 else if (player == game->player_black)
171 player_color = BLACK;
172 else
173 return json_pack("{snss}", "state", "error", "invalid player");
174
175 switch (termination)
176 {
177 case TAKEN_DRAW_WHITE:
178 case TAKEN_DRAW_BLACK:
179 if (game->current->move->player == player_color)
180 return json_pack(
181 "{snss}", "state", "error",
182 "player can only take draw on their move");
183 if (!game->current->move->post_board->draws)
184 return json_pack(
185 "{snss}", "state", "error", "no draws available");
186 __attribute__((fallthrough));
187 case RESIGNATION_WHITE:
188 case RESIGNATION_BLACK:
189 if (player_color == WHITE) {
190 if (termination == TAKEN_DRAW_BLACK ||
191 termination == RESIGNATION_BLACK)
192 return json_pack(
193 "{snss}", "state", "error",
194 "cannot end game for other player");
195 } else {
196 if (termination == TAKEN_DRAW_WHITE ||
197 termination == RESIGNATION_WHITE)
198 return json_pack(
199 "{snss}", "state", "error",
200 "cannot end game for other player");
201 }
202 success = end_game(gt, game_id, termination);
203 if (success) {
204 return json_pack(
205 "{sosn}", "state", game_state(gt, game_id), "error");
206 }
207 return json_pack("{snss}", "state", "error", "unknown error");
208
209 default:
210 return json_pack(
211 "{snss}", "state",
212 "error", "provided termination cannot be voluntary");
213 }
214 }
144 game_id_t game_id;
145 player_id_t player;
146 termination_t termination;
147 json_t *t;
148 bool success;
149 struct game *game;
150 color_t player_color;
151
152 get(t, req, "player");
153 player = json_integer_value(t);
154
155 get(t, req, "game_id");
156 game_id = json_integer_value(t);
157
158 get(t, req, "termination");
159 termination = termination_from_str(json_string_value(t));
160
161 if (get_game(gt, game_id) == NULL)
162 {
163 return json_pack(
164 "{snss}", "state", "error", "game does not exist");
165 }
166 if (termination == INVALID)
167 {
168 return json_pack(
169 "{snss}", "state", "error", "invalid termination");
170 }
171
172 game = get_game(gt, game_id);
173 if (player == game->player_white)
174 player_color = WHITE;
175 else if (player == game->player_black)
176 player_color = BLACK;
177 else
178 return json_pack("{snss}", "state", "error", "invalid player");
179
180 switch (termination)
181 {
182 case TAKEN_DRAW_WHITE:
183 case TAKEN_DRAW_BLACK:
184 if (game->current->move->player == player_color)
185 return json_pack(
186 "{snss}", "state", "error",
187 "player can only take draw on their move");
188 if (!game->current->move->post_board->draws)
189 return json_pack(
190 "{snss}", "state", "error",
191 "no draws available");
192 __attribute__((fallthrough));
193 case RESIGNATION_WHITE:
194 case RESIGNATION_BLACK:
195 if (player_color == WHITE)
196 {
197 if (termination == TAKEN_DRAW_BLACK
198 || termination == RESIGNATION_BLACK)
199 return json_pack(
200 "{snss}", "state", "error",
201 "cannot end game for other "
202 "player");
203 }
204 else
205 {
206 if (termination == TAKEN_DRAW_WHITE
207 || termination == RESIGNATION_WHITE)
208 return json_pack(
209 "{snss}", "state", "error",
210 "cannot end game for other "
211 "player");
212 }
213 success = end_game(gt, game_id, termination);
214 if (success)
215 {
216 return json_pack(
217 "{sosn}", "state", game_state(gt, game_id),
218 "error");
219 }
220 return json_pack(
221 "{snss}", "state", "error", "unknown error");
222
223 default:
224 return json_pack(
225 "{snss}", "state", "error",
226 "provided termination cannot be voluntary");
227 }
228 }
2626 #define NO_PASSANT (-1)
2727
2828 typedef enum {
29 PAWN = 'p',
30 ROOK = 'R',
31 KNIGHT = 'N',
32 BISHOP = 'B',
33 QUEEN = 'Q',
34 KING = 'K'
29 PAWN = 'p',
30 ROOK = 'R',
31 KNIGHT = 'N',
32 BISHOP = 'B',
33 QUEEN = 'Q',
34 KING = 'K'
3535 } piece_type_t;
3636
37 #define ALL_PIECES ((piece_type_t[]) {\
38 PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING })
39
40 typedef enum {
41 WHITE = 'w',
42 BLACK = 'b'
43 } color_t;
44
45 typedef enum {
46 WHITE_KINGSIDE = 0x01,
47 WHITE_QUEENSIDE = 0x02,
48 BLACK_KINGSIDE = 0x04,
49 BLACK_QUEENSIDE = 0x08
37 #define ALL_PIECES \
38 ((piece_type_t[]){ PAWN, ROOK, KNIGHT, BISHOP, QUEEN, KING })
39
40 typedef enum { WHITE = 'w', BLACK = 'b' } color_t;
41
42 typedef enum {
43 WHITE_KINGSIDE = 0x01,
44 WHITE_QUEENSIDE = 0x02,
45 BLACK_KINGSIDE = 0x04,
46 BLACK_QUEENSIDE = 0x08
5047 } castles_t;
5148
5249 typedef enum {
53 /* game is in progress, no termination other than resignation possible */
54 AVAILABLE_MOVE = 0x00,
55 /* white wins, black is in checkmate */
56 VICTORY_WHITE = 0x81,
57 /* black wins, white is in checkmate */
58 VICTORY_BLACK = 0x82,
59 /* a stalemate has been reached */
60 STALEMATE = 0x83,
61 /* white has chosen a draw by the 50 move rule or threefold repetition */
62 TAKEN_DRAW_WHITE = 0x84,
63 /* black has chosen a draw by the 50 move rule or threefold repetition */
64 TAKEN_DRAW_BLACK = 0x85,
65 /* white has resigned, black wins by default */
66 RESIGNATION_WHITE = 0x86,
67 /* black has resigned, white wins by default */
68 RESIGNATION_BLACK = 0x87,
69 /* sentinel invalid termination value */
70 INVALID = 0xFF,
50 /* game is in progress, no termination other than resignation possible
51 */
52 AVAILABLE_MOVE = 0x00,
53 /* white wins, black is in checkmate */
54 VICTORY_WHITE = 0x81,
55 /* black wins, white is in checkmate */
56 VICTORY_BLACK = 0x82,
57 /* a stalemate has been reached */
58 STALEMATE = 0x83,
59 /* white has chosen a draw by the 50 move rule or threefold repetition
60 */
61 TAKEN_DRAW_WHITE = 0x84,
62 /* black has chosen a draw by the 50 move rule or threefold repetition
63 */
64 TAKEN_DRAW_BLACK = 0x85,
65 /* white has resigned, black wins by default */
66 RESIGNATION_WHITE = 0x86,
67 /* black has resigned, white wins by default */
68 RESIGNATION_BLACK = 0x87,
69 /* sentinel invalid termination value */
70 INVALID = 0xFF,
7171 } termination_t;
7272
7373 #define TERM_GAME_OVER_MASK 0x80
7474
7575 typedef enum {
76 /* no draws available */
77 DRAW_NONE = 0x00,
78 /* a player could choose to take a draw due to the 50 move rule */
79 DRAW_50 = 0x01,
80 /* a player could choose to take a draw due to threefold repetition */
81 DRAW_THREEFOLD = 0x02,
76 /* no draws available */
77 DRAW_NONE = 0x00,
78 /* a player could choose to take a draw due to the 50 move rule */
79 DRAW_50 = 0x01,
80 /* a player could choose to take a draw due to threefold repetition */
81 DRAW_THREEFOLD = 0x02,
8282 } draws_t;
8383
84 struct piece {
85 piece_type_t piece_type;
86 color_t color;
87 };
88
89 struct position {
90 int8_t rank;
91 int8_t file;
92 };
93
94 struct move {
95 struct position start;
96 struct position end;
97 color_t player;
98 char *algebraic;
99 struct move *parent;
100
101 struct board *post_board;
102 };
103
104 struct access_map {
105 struct {
106 int n_accessors;
107 struct position *accessors;
108 } board[8][8];
109 };
110
111 struct board {
112 struct piece board[8][8];
113 struct access_map *access_map;
114 uint8_t available_castles;
115 int8_t passant_file;
116 /* ply index is the number of plys that have been played, inclusive. */
117 uint16_t ply_index;
118 /* PGN is the PGN for the game played up until this point. */
119 char *pgn;
120 /* FEN is the board state encoded in Forsyth-Edwards notation. */
121 char *fen;
122 /* the available termination state for this move. Note that there are
123 * multiple "the game is not over" "termination" states: both
124 * AVAILABLE_MOVE and AVAILABLE_STALEMATE are valid values here.
125 * AVAILABLE_STALEMATE means that the player to play after this move could
126 * choose to take a stalemate.
127 *
128 * Some termination values are not valid here; notably, chosen draws or
129 * resignations will never be stored here. This is because they are
130 * optional on the part of the player; this struct is for the properties
131 * of all games that reach this point. Optional terminations are stored in
132 * the game struct. */
133 termination_t termination;
134 /* the draw methods available after this turn. */
135 draws_t draws;
136 /* whether the next player to play is in check. */
137 bool in_check;
138 /* the number of plys since the last pawn move or capture. */
139 uint16_t fifty_move_counter;
84 struct piece
85 {
86 piece_type_t piece_type;
87 color_t color;
88 };
89
90 struct position
91 {
92 int8_t rank;
93 int8_t file;
94 };
95
96 struct move
97 {
98 struct position start;
99 struct position end;
100 color_t player;
101 char *algebraic;
102 struct move *parent;
103
104 struct board *post_board;
105 };
106
107 struct access_map
108 {
109 struct
110 {
111 int n_accessors;
112 struct position *accessors;
113 } board[8][8];
114 };
115
116 struct board
117 {
118 struct piece board[8][8];
119 struct access_map *access_map;
120 uint8_t available_castles;
121 int8_t passant_file;
122 /* ply index is the number of plys that have been played, inclusive. */
123 uint16_t ply_index;
124 /* PGN is the PGN for the game played up until this point. */
125 char *pgn;
126 /* FEN is the board state encoded in Forsyth-Edwards notation. */
127 char *fen;
128 /* the available termination state for this move. Note that there are
129 * multiple "the game is not over" "termination" states: both
130 * AVAILABLE_MOVE and AVAILABLE_STALEMATE are valid values here.
131 * AVAILABLE_STALEMATE means that the player to play after this move
132 * could choose to take a stalemate.
133 *
134 * Some termination values are not valid here; notably, chosen draws or
135 * resignations will never be stored here. This is because they are
136 * optional on the part of the player; this struct is for the
137 * properties of all games that reach this point. Optional terminations
138 * are stored in the game struct. */
139 termination_t termination;
140 /* the draw methods available after this turn. */
141 draws_t draws;
142 /* whether the next player to play is in check. */
143 bool in_check;
144 /* the number of plys since the last pawn move or capture. */
145 uint16_t fifty_move_counter;
140146 };
141147
142148 /* Returns the opposite color of the given color. */
143 color_t
144 opposite(color_t color);
149 color_t opposite(color_t color);
145150
146151 /* Parse algebraic notation and return the result. **out is set to null if the
147152 * input was not a valid move. */
148 void
149 parse_algebraic(
150 const char *notation,
151 struct move *last_move,
152 struct move **out);
153 void parse_algebraic(
154 const char *notation, struct move *last_move, struct move **out);
153155
154156 /* Returns true if the movement in the move struct represents a valid movement
155157 * for the piece that moved. Moves passed into this function must have their
156158 * post_board correctly filled out. */
157 bool
158 is_movement_valid(struct move *move);
159 bool is_movement_valid(struct move *move);
159160
160161 /* Returns true if the given player is in checkmate. */
161 bool
162 in_checkmate(struct move *move, color_t player);
162 bool in_checkmate(struct move *move, color_t player);
163163
164164 /* Returns true if the given player is in check or checkmate. */
165 bool
166 in_check(struct move *move, color_t player);
165 bool in_check(struct move *move, color_t player);
167166
168167 /* Returns true if the given player is in forced stalemate. */
169 bool
170 in_stalemate(struct move *move, color_t player);
168 bool in_stalemate(struct move *move, color_t player);
171169
172170 /* Finds all pieces of the given color and type that has access to move->end,
173 * respecting any preexisting values in move->start. Returns a list of positions
174 * where accessible pieces are located. */
175 void
176 find_all_with_access(
177 struct piece piece,
178 struct move *move,
179 int *n_results,
171 * respecting any preexisting values in move->start. Returns a list of
172 * positions where accessible pieces are located. */
173 void find_all_with_access(
174 struct piece piece, struct move *move, int *n_results,
180175 struct position **results);
181176
182177 /* Finds a piece of the given color and type that has access to move->end,
183178 * respecting any preexisting values in move->start. Populates the value at
184179 * move->start with results if any are found. If none are found, move is
185180 * entirely unchanged. */
186 void
187 find_piece_with_access(struct piece piece, struct move *move);
181 void find_piece_with_access(struct piece piece, struct move *move);
188182
189183 /* Returns true if the given player can attack the given square. */
190 bool
191 can_attack(struct move *move, struct position position, color_t to_move);
184 bool can_attack(struct move *move, struct position position, color_t to_move);
192185
193186 /* Returns true if the given color can block the piece at the "mover" position
194187 * from reaching the "target" position. Assumes that, without blockage, the
195188 * mover could move to the target using a valid movement. */
196 bool
197 can_block(
198 struct move *move,
199 struct position mover,
200 struct position target,
189 bool can_block(
190 struct move *move, struct position mover, struct position target,
201191 color_t to_move);
202192
203193 /* Returns true if the two boards are equivalent. */
204 bool
205 boards_equal(struct board *, struct board *);
194 bool boards_equal(struct board *, struct board *);
206195
207196 /* Convert a move to JSON. */
208 json_t *
209 move_to_json(const struct move *);
197 json_t *move_to_json(const struct move *);
210198
211199 /* Convert a board to JSON. */
212 json_t *
213 board_to_json(const struct board *board);
200 json_t *board_to_json(const struct board *board);
214201
215202 /* Convert a move to FEN. */
216 char *
217 move_to_fen(const struct move *);
203 char *move_to_fen(const struct move *);
218204
219205 /* String representation for a termination state. */
220 char *
221 termination_str(termination_t term);
206 char *termination_str(termination_t term);
222207
223208 /* Termination state from a NUL-terminated string representation. */
224 termination_t
225 termination_from_str(const char *);
209 termination_t termination_from_str(const char *);
226210
227211 #endif
2525
2626 #define MAX_MSG_LEN 16384
2727
28 struct aol_tx {
29 FILE *f;
30 char *path;
28 struct aol_tx
29 {
30 FILE *f;
31 char *path;
3132 };
3233
3334 /* Receive a length-encoded string on the given socket. */
34 char *
35 read_str(int sock, ssize_t max_len);
35 char *read_str(int sock, ssize_t max_len);
3636
3737 /* Send a length-encoded string on the given socket. Returns 0 on success or -1
3838 * on error.*/
39 int
40 send_str(int sock, char *str);
39 int send_str(int sock, char *str);
4140
42 json_t *
43 handle_new_game(struct game_tree *gt, json_t *req);
41 json_t *handle_new_game(struct game_tree *gt, json_t *req);
4442
45 json_t *
46 handle_move(struct game_tree *gt, json_t *req);
43 json_t *handle_move(struct game_tree *gt, json_t *req);
4744
48 json_t *
49 handle_game_from_pgn(struct game_tree *gt, json_t *req);
45 json_t *handle_game_from_pgn(struct game_tree *gt, json_t *req);
5046
51 json_t *
52 handle_end_game(struct game_tree *gt, json_t *req);
47 json_t *handle_end_game(struct game_tree *gt, json_t *req);
2323 #include <stdio.h>
2424
2525 #ifdef DEBUG
26 # define debugf(...) do { printf(__VA_ARGS__); putchar('\n'); } while (0);
26 #define debugf(...) \
27 do \
28 { \
29 printf(__VA_ARGS__); \
30 putchar('\n'); \
31 } while (0);
2732 #else
28 # define debugf(...) do {} while (0);
33 #define debugf(...) \
34 do \
35 { \
36 } while (0);
2937 #endif
3038
31 void
32 read_location(const char *str, struct position *result);
39 void read_location(const char *str, struct position *result);
3340
3441 /* Parse FEN data into a move. Private API because it makes a "truncated" move
3542 * with no hierarchy associated with it. */
36 struct move *
37 parse_fen(const char *fen, int n);
43 struct move *parse_fen(const char *fen, int n);
3844
3945 /* Print a move to stdout. */
40 void
41 print_move(const struct move *);
46 void print_move(const struct move *);
4247
4348 /* Convert a board to JSON. */
44 json_t *
45 board_to_json(const struct board *);
49 json_t *board_to_json(const struct board *);
4650
4751 /* Blindly apply movement represented by start and end points, disregarding the
4852 * validity of the move itself. */
49 void
50 apply_movement(struct move *m);
53 void apply_movement(struct move *m);
5154
5255 /* Asserts that the given color is either white or black. */
53 void
54 assert_valid_color(color_t color);
56 void assert_valid_color(color_t color);
5557
5658 /* Builds an access map from a move. */
57 void
58 build_access_map(struct move *move, struct access_map *out);
59 void build_access_map(struct move *move, struct access_map *out);
5960
6061 /* Frees an access map. */
61 void
62 free_access_map(struct access_map *map);
62 void free_access_map(struct access_map *map);
6363
6464 /* Loads the opening position into a board object. */
65 void
66 load_default_board(struct board *b);
65 void load_default_board(struct board *b);
6766
6867 /* Returns a move that contains the root of the full game tree. */
69 void
70 get_root(struct move *out);
68 void get_root(struct move *out);
7169
7270 /* Free a move struct, leaving its parent move untouched. */
73 void
74 free_move(struct move *move);
71 void free_move(struct move *move);
7572
7673 /* Free a move struct and all of its parents. */
77 void
78 free_move_tree(struct move *move);
74 void free_move_tree(struct move *move);
7975
8076 /* Creates PGN for a given move. In most cases, callers should use
81 * move->post_board->pgn instead; this is what is used to populate the pgn field
82 * on the board struct. */
83 char *
84 create_pgn(struct move *move);
77 * move->post_board->pgn instead; this is what is used to populate the pgn
78 * field on the board struct. */
79 char *create_pgn(struct move *move);
8580
8681 /* Convert a board struct to Forsythe-Edwards Notation. */
87 char *
88 board_to_fen(struct board *board, color_t player);
82 char *board_to_fen(struct board *board, color_t player);
8983
9084 #endif
2727 typedef uint64_t game_id_t;
2828 typedef uint64_t player_id_t;
2929
30 #define NO_GAME ((game_id_t) -1)
30 #define NO_GAME ((game_id_t)-1)
3131
32 struct state_node {
33 struct move *move;
34 size_t n_children;
35 struct state_node **children;
36 struct state_node *parent;
32 struct state_node
33 {
34 struct move *move;
35 size_t n_children;
36 struct state_node **children;
37 struct state_node *parent;
3738 };
3839
39 struct game {
40 game_id_t id;
41 player_id_t player_white;
42 player_id_t player_black;
43 struct state_node *current;
44 termination_t termination;
40 struct game
41 {
42 game_id_t id;
43 player_id_t player_white;
44 player_id_t player_black;
45 struct state_node *current;
46 termination_t termination;
4547 };
4648
47 struct game_tree {
48 size_t n_states;
49 struct state_node **states;
50 size_t n_games;
51 struct game **games;
49 struct game_tree
50 {
51 size_t n_states;
52 struct state_node **states;
53 size_t n_games;
54 struct game **games;
5255 };
5356
54 void
55 init_gametree(struct game_tree *gt);
57 void init_gametree(struct game_tree *gt);
5658
57 game_id_t
58 new_game(struct game_tree *gt, player_id_t white, player_id_t black);
59 game_id_t new_game(struct game_tree *gt, player_id_t white, player_id_t black);
5960
60 struct game *
61 get_game(struct game_tree *gt, game_id_t game);
61 struct game *get_game(struct game_tree *gt, game_id_t game);
6262
63 bool
64 make_move(
65 struct game_tree *gt,
66 game_id_t game,
67 player_id_t player,
63 bool make_move(
64 struct game_tree *gt, game_id_t game, player_id_t player,
6865 const char *notation);
6966
70 bool
71 end_game(struct game_tree *gt, game_id_t game, termination_t termination);
67 bool end_game(struct game_tree *gt, game_id_t game, termination_t termination);
7268
73 game_id_t
74 new_game_from_pgn(
75 struct game_tree *gt,
76 player_id_t white,
77 player_id_t black,
69 game_id_t new_game_from_pgn(
70 struct game_tree *gt, player_id_t white, player_id_t black,
7871 const char *pgn);
7972
80 void
81 free_game_tree(struct game_tree *gt);
73 void free_game_tree(struct game_tree *gt);
8274
83 json_t *
84 game_tree_to_json(struct game_tree *gt);
75 json_t *game_tree_to_json(struct game_tree *gt);
8576
86 void
87 game_tree_from_json(json_t *doc, struct game_tree *gt);
77 void game_tree_from_json(json_t *doc, struct game_tree *gt);
8878
8979 #endif
1919 #include "grandmaster/core.h"
2020 #include "grandmaster/internal.h"
2121
22 #include <stdio.h>
23 #include <stdlib.h>
2224 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
2525
2626 #define sign(x) (((x) > 0) - ((x) < 0))
2727
2828 void
2929 find_all_with_access(
30 struct piece piece,
31 struct move *move,
32 int *n_results,
30 struct piece piece, struct move *move, int *n_results,
3331 struct position **results)
3432 {
35 int8_t rank;
36 int8_t file;
37 struct move test_move;
38 struct piece *board_piece;
39 struct position *res;
40 const int max_res = 16;
41 const int max_res_arraylen = max_res * sizeof(struct position);
42
43 *n_results = 0;
44 /* instead of repeatedly growing the result array, alloc an array large
45 * enough to store the maximum number of responses and then realloc at the
46 * end of the function down to the appropriate size. Even at max length,
47 * this is only 32 bytes of data. */
48 res = malloc(max_res_arraylen);
49 if (res == NULL) {
50 return;
51 }
52 memset(res, 0xFF, max_res_arraylen);
53
54 memset(&test_move, 0x00, sizeof(struct move));
55 test_move.parent = move->parent;
56 test_move.end = move->end;
57
58 for (rank = 0; rank < 8; rank++) {
59 /* we could do this more efficiently by skipping the loop altogether in
60 * this case, but I like the succinctness of doing this all in one loop
61 * with no special cases. */
62 if (move->start.rank != -1 && move->start.rank != rank)
63 continue;
64 for (file = 0; file < 8; file++) {
65 if (move->start.file != -1 && move->start.file != file)
66 continue;
67 board_piece = &move->parent->post_board->board[rank][file];
68 if (board_piece->piece_type == 0)
69 continue;
70 if (piece.piece_type != 0 &&
71 board_piece->piece_type != piece.piece_type)
72 continue;
73 if (piece.color != 0 && board_piece->color != piece.color)
74 continue;
75 test_move.start.rank = rank;
76 test_move.start.file = file;
77 test_move.player = board_piece->color;
78 apply_movement(&test_move);
79 if (is_movement_valid(&test_move) && *n_results < max_res) {
80 res[*n_results] = test_move.start;
81 (*n_results)++;
82 }
83 free(test_move.post_board);
84 test_move.post_board = NULL;
85 }
86 }
87
88 *results = realloc(res, *n_results * sizeof(struct position));
89 if (*results == NULL && *n_results) {
33 int8_t rank;
34 int8_t file;
35 struct move test_move;
36 struct piece *board_piece;
37 struct position *res;
38 const int max_res = 16;
39 const int max_res_arraylen = max_res * sizeof(struct position);
40
9041 *n_results = 0;
91 free(res);
92 return;
93 }
42 /* instead of repeatedly growing the result array, alloc an array large
43 * enough to store the maximum number of responses and then realloc at
44 * the end of the function down to the appropriate size. Even at max
45 * length, this is only 32 bytes of data. */
46 res = malloc(max_res_arraylen);
47 if (res == NULL)
48 {
49 return;
50 }
51 memset(res, 0xFF, max_res_arraylen);
52
53 memset(&test_move, 0x00, sizeof(struct move));
54 test_move.parent = move->parent;
55 test_move.end = move->end;
56
57 for (rank = 0; rank < 8; rank++)
58 {
59 /* we could do this more efficiently by skipping the loop
60 * altogether in this case, but I like the succinctness of
61 * doing this all in one loop with no special cases. */
62 if (move->start.rank != -1 && move->start.rank != rank)
63 continue;
64 for (file = 0; file < 8; file++)
65 {
66 if (move->start.file != -1 && move->start.file != file)
67 continue;
68 board_piece =
69 &move->parent->post_board->board[rank][file];
70 if (board_piece->piece_type == 0)
71 continue;
72 if (piece.piece_type != 0
73 && board_piece->piece_type != piece.piece_type)
74 continue;
75 if (piece.color != 0
76 && board_piece->color != piece.color)
77 continue;
78 test_move.start.rank = rank;
79 test_move.start.file = file;
80 test_move.player = board_piece->color;
81 apply_movement(&test_move);
82 if (is_movement_valid(&test_move)
83 && *n_results < max_res)
84 {
85 res[*n_results] = test_move.start;
86 (*n_results)++;
87 }
88 free(test_move.post_board);
89 test_move.post_board = NULL;
90 }
91 }
92
93 *results = realloc(res, *n_results * sizeof(struct position));
94 if (*results == NULL && *n_results)
95 {
96 *n_results = 0;
97 free(res);
98 return;
99 }
94100 }
95101
96102 void
97103 find_piece_with_access(struct piece piece, struct move *move)
98104 {
99 int8_t rank;
100 int8_t file;
101 struct move test_move;
102 struct piece *board_piece;
103
104 memset(&test_move, 0x00, sizeof(struct move));
105 test_move.parent = move->parent;
106 test_move.end = move->end;
107
108 for (rank = 0; rank < 8; rank++) {
109 /* we could do this more efficiently by skipping the loop altogether in
110 * this case, but I like the succinctness of doing this all in one loop
111 * with no special cases. */
112 if (move->start.rank != -1 && move->start.rank != rank)
113 continue;
114 for (file = 0; file < 8; file++) {
115 if (move->start.file != -1 && move->start.file != file)
116 continue;
117 board_piece = &move->parent->post_board->board[rank][file];
118 if (board_piece->piece_type == 0)
119 continue;
120 if (piece.piece_type != 0 &&
121 board_piece->piece_type != piece.piece_type)
122 continue;
123 if (piece.color != 0 && board_piece->color != piece.color)
124 continue;
125 test_move.start.rank = rank;
126 test_move.start.file = file;
127 test_move.player = board_piece->color;
128 apply_movement(&test_move);
129 if (is_movement_valid(&test_move)) {
130 move->start = test_move.start;
131 free(test_move.post_board);
132 return;
133 }
134 free(test_move.post_board);
135 test_move.post_board = NULL;
136 }
137 }
105 int8_t rank;
106 int8_t file;
107 struct move test_move;
108 struct piece *board_piece;
109
110 memset(&test_move, 0x00, sizeof(struct move));
111 test_move.parent = move->parent;
112 test_move.end = move->end;
113
114 for (rank = 0; rank < 8; rank++)
115 {
116 /* we could do this more efficiently by skipping the loop
117 * altogether in this case, but I like the succinctness of
118 * doing this all in one loop with no special cases. */
119 if (move->start.rank != -1 && move->start.rank != rank)
120 continue;
121 for (file = 0; file < 8; file++)
122 {
123 if (move->start.file != -1 && move->start.file != file)
124 continue;
125 board_piece =
126 &move->parent->post_board->board[rank][file];
127 if (board_piece->piece_type == 0)
128 continue;
129 if (piece.piece_type != 0
130 && board_piece->piece_type != piece.piece_type)
131 continue;
132 if (piece.color != 0
133 && board_piece->color != piece.color)
134 continue;
135 test_move.start.rank = rank;
136 test_move.start.file = file;
137 test_move.player = board_piece->color;
138 apply_movement(&test_move);
139 if (is_movement_valid(&test_move))
140 {
141 move->start = test_move.start;
142 free(test_move.post_board);
143 return;
144 }
145 free(test_move.post_board);
146 test_move.post_board = NULL;
147 }
148 }
138149 }
139150
140151 bool
141 can_attack(
142 struct move *move,
143 struct position position,
144 color_t to_move
145 )
146 {
147 struct move test_move;
148 struct piece constraint;
149
150 test_move.parent = move;
151 test_move.start.rank = -1;
152 test_move.start.file = -1;
153 test_move.end.rank = position.rank;
154 test_move.end.file = position.file;
155
156 constraint.color = to_move;
157 constraint.piece_type = 0;
158
159 find_piece_with_access(constraint, &test_move);
160
161 return test_move.start.rank != -1;
152 can_attack(struct move *move, struct position position, color_t to_move)
153 {
154 struct move test_move;
155 struct piece constraint;
156
157 test_move.parent = move;
158 test_move.start.rank = -1;
159 test_move.start.file = -1;
160 test_move.end.rank = position.rank;
161 test_move.end.file = position.file;
162
163 constraint.color = to_move;
164 constraint.piece_type = 0;
165
166 find_piece_with_access(constraint, &test_move);
167
168 return test_move.start.rank != -1;
162169 }
163170
164171 bool
165172 can_block(
166 struct move *move,
167 struct position mover,
168 struct position target,
169 color_t to_move
170 )
171 {
172 struct piece *moving_piece;
173 struct move test_move;
174 struct piece constraint;
175 int rank_step;
176 int file_step;
177 int d_rank;
178 int d_file;
179
180 moving_piece = &move->post_board->board[mover.rank][mover.file];
181 if (moving_piece->piece_type == KNIGHT) {
182 /* No blocking a knight. You're done for. */
173 struct move *move, struct position mover, struct position target,
174 color_t to_move)
175 {
176 struct piece *moving_piece;
177 struct move test_move;
178 struct piece constraint;
179 int rank_step;
180 int file_step;
181 int d_rank;
182 int d_file;
183
184 moving_piece = &move->post_board->board[mover.rank][mover.file];
185 if (moving_piece->piece_type == KNIGHT)
186 {
187 /* No blocking a knight. You're done for. */
188 return false;
189 }
190
191 rank_step = sign(target.rank - mover.rank);
192 file_step = sign(target.file - mover.file);
193 d_rank = rank_step;
194 d_file = file_step;
195
196 test_move.parent = move;
197 test_move.start.rank = -1;
198 test_move.start.file = -1;
199 constraint.color = to_move;
200 constraint.piece_type = 0;
201
202 while (mover.rank + d_rank != target.rank
203 || mover.file + d_file != target.file)
204 {
205 test_move.end.rank = mover.rank + d_rank;
206 test_move.end.file = mover.file + d_file;
207
208 find_piece_with_access(constraint, &test_move);
209 if (test_move.start.rank != -1)
210 {
211 return true;
212 }
213
214 d_rank += rank_step;
215 d_file += file_step;
216 }
183217 return false;
184 }
185
186 rank_step = sign(target.rank - mover.rank);
187 file_step = sign(target.file - mover.file);
188 d_rank = rank_step;
189 d_file = file_step;
190
191 test_move.parent = move;
192 test_move.start.rank = -1;
193 test_move.start.file = -1;
194 constraint.color = to_move;
195 constraint.piece_type = 0;
196
197 while (mover.rank + d_rank != target.rank
198 || mover.file + d_file != target.file) {
199 test_move.end.rank = mover.rank + d_rank;
200 test_move.end.file = mover.file + d_file;
201
202 find_piece_with_access(constraint, &test_move);
203 if (test_move.start.rank != -1) {
204 return true;
205 }
206
207 d_rank += rank_step;
208 d_file += file_step;
209 }
210 return false;
211 }
218 }
2525 #include <string.h>
2626
2727 #ifdef DEBUG
28 # define alg_fail(...) do {\
29 printf("alg_fail: "); \
30 printf(__VA_ARGS__); \
31 printf("\n"); \
32 goto error; } while (0);
28 #define alg_fail(...) \
29 do \
30 { \
31 printf("alg_fail: "); \
32 printf(__VA_ARGS__); \
33 printf("\n"); \
34 goto error; \
35 } while (0);
3336 #else
34 # define alg_fail(...) do { goto error; } while (0);
37 #define alg_fail(...) \
38 do \
39 { \
40 goto error; \
41 } while (0);
3542 #endif
3643
3744 bool
3845 is_valid_position(const struct position pos)
3946 {
40 return 0 <= pos.rank && pos.rank < 8 && 0 <= pos.file && pos.file < 8;
47 return 0 <= pos.rank && pos.rank < 8 && 0 <= pos.file && pos.file < 8;
4148 }
4249
4350 bool
44 is_threefold_available(const struct move *m) {
45 struct board *b0;
46 uint16_t equivs;
47
48 b0 = m->post_board;
49 equivs = 0;
50 while (m->parent != NULL) {
51 m = m->parent;
52 if (boards_equal(b0, m->post_board))
53 equivs++;
54 if (equivs >= 3)
55 return true;
56 }
57 return false;
51 is_threefold_available(const struct move *m)
52 {
53 struct board *b0;
54 uint16_t equivs;
55
56 b0 = m->post_board;
57 equivs = 0;
58 while (m->parent != NULL)
59 {
60 m = m->parent;
61 if (boards_equal(b0, m->post_board))
62 equivs++;
63 if (equivs >= 3)
64 return true;
65 }
66 return false;
5867 }
5968
6069 int
6170 parse_castle(
62 const char *notation,
63 const struct move *last_move,
64 struct move *out)
65 {
66 bool is_kingside;
67 castles_t castle_type;
68 int rook_start_file;
69 int rook_end_file;
70
71 castle_type = 0;
72 if (!strncmp(notation, "0-0-0", 5) || !strncmp(notation, "O-O-O", 5)) {
73 castle_type = out->player == WHITE ? WHITE_QUEENSIDE : BLACK_QUEENSIDE;
74 }
75 else if (!strncmp(notation, "0-0", 3) || !strncmp(notation, "O-O", 3)) {
76 castle_type = out->player == WHITE ? WHITE_KINGSIDE : BLACK_KINGSIDE;
77 }
78
79 if (!castle_type)
80 return 0;
81 if (!(last_move->post_board->available_castles & castle_type))
82 return 0;
83
84 if (out->player == BLACK) {
85 out->start.rank = 7;
86 out->end.rank = 7;
87 } else {
88 out->start.rank = 0;
89 out->end.rank = 0;
90 }
91
92 is_kingside = castle_type & (WHITE_KINGSIDE | BLACK_KINGSIDE);
93 out->start.file = 4;
94 out->end.file = is_kingside ? 6 : 2;
95 rook_start_file = is_kingside ? 7 : 0;
96 rook_end_file = is_kingside ? 5 : 3;
97
98 out->post_board = calloc(1, sizeof(struct board));
99 memcpy(out->post_board, last_move->post_board, sizeof(struct board));
100
101 out->post_board->access_map = NULL;
102
103 /* update available castles */
104 out->post_board->available_castles =
105 last_move->post_board->available_castles;
106 if (out->player == BLACK) {
107 out->post_board->available_castles &=
108 ~(BLACK_KINGSIDE | BLACK_QUEENSIDE);
109 } else {
110 out->post_board->available_castles &=
111 ~(WHITE_KINGSIDE | WHITE_QUEENSIDE);
112 }
113
114
115 /* move the king */
116 out->post_board->board[out->end.rank][out->end.file] =
117 out->post_board->board[out->start.rank][out->start.file];
118 out->post_board->board[out->start.rank][out->start.file] =
119 (struct piece) { .color = 0, .piece_type = 0 };
120
121 /* move the rook */
122 out->post_board->board[out->end.rank][rook_end_file] =
123 out->post_board->board[out->start.rank][rook_start_file];
124 out->post_board->board[out->start.rank][rook_start_file] =
125 (struct piece) { .color = 0, .piece_type = 0 };
126
127 return 1;
128 }
129
130 int
131 parse_pawn(
132 const char *notation,
133 const struct move *last_move,
134 const bool is_capture,
135 struct move *out)
136 {
137 struct board *b;
138 int capture_rank;
139 color_t player;
140 bool is_passant;
141
142 player = opposite(last_move->player);
143 b = last_move->post_board;
144
145 out->start.file = notation[0] - 'a';
146 out->start.rank = 0;
147 out->end.file = notation[strlen(notation) - 2] - 'a';
148 out->end.rank = notation[strlen(notation) - 1] - '1';
149 if (!is_valid_position(out->start))
150 return 0;
151 if (!is_valid_position(out->end))
152 return 0;
153
154 is_passant = false;
155
156 if (is_capture) {
157 if (player == WHITE) {
158 out->start.rank = out->end.rank - 1;
159 } else {
160 out->start.rank = out->end.rank + 1;
161 }
162 is_passant =
163 !b->board[out->end.rank][out->end.file].piece_type;
164 } else {
165 if (player == WHITE) {
166 if (out->end.rank == 3) {
167 if (b->board[2][out->start.file].color == WHITE) {
168 out->start.rank = 2;
169 } else {
170 out->start.rank = 1;
171 }
172 } else {
173 out->start.rank = out->end.rank - 1;
174 }
175 } else {
176 if (out->end.rank == 4) {
177 if (b->board[5][out->start.file].color == BLACK) {
178 out->start.rank = 5;
179 } else {
180 out->start.rank = 6;
181 }
182 } else {
183 out->start.rank = out->end.rank + 1;
184 }
185 }
186 }
187
188 if (is_passant) {
71 const char *notation, const struct move *last_move, struct move *out)
72 {
73 bool is_kingside;
74 castles_t castle_type;
75 int rook_start_file;
76 int rook_end_file;
77
78 castle_type = 0;
79 if (!strncmp(notation, "0-0-0", 5) || !strncmp(notation, "O-O-O", 5))
80 {
81 castle_type =
82 out->player == WHITE ? WHITE_QUEENSIDE : BLACK_QUEENSIDE;
83 }
84 else if (!strncmp(notation, "0-0", 3) || !strncmp(notation, "O-O", 3))
85 {
86 castle_type =
87 out->player == WHITE ? WHITE_KINGSIDE : BLACK_KINGSIDE;
88 }
89
90 if (!castle_type)
91 return 0;
92 if (!(last_move->post_board->available_castles & castle_type))
93 return 0;
94
95 if (out->player == BLACK)
96 {
97 out->start.rank = 7;
98 out->end.rank = 7;
99 }
100 else
101 {
102 out->start.rank = 0;
103 out->end.rank = 0;
104 }
105
106 is_kingside = castle_type & (WHITE_KINGSIDE | BLACK_KINGSIDE);
107 out->start.file = 4;
108 out->end.file = is_kingside ? 6 : 2;
109 rook_start_file = is_kingside ? 7 : 0;
110 rook_end_file = is_kingside ? 5 : 3;
111
189112 out->post_board = calloc(1, sizeof(struct board));
190113 memcpy(out->post_board, last_move->post_board, sizeof(struct board));
191114
192 /* move the pawn */
115 out->post_board->access_map = NULL;
116
117 /* update available castles */
118 out->post_board->available_castles =
119 last_move->post_board->available_castles;
120 if (out->player == BLACK)
121 {
122 out->post_board->available_castles &=
123 ~(BLACK_KINGSIDE | BLACK_QUEENSIDE);
124 }
125 else
126 {
127 out->post_board->available_castles &=
128 ~(WHITE_KINGSIDE | WHITE_QUEENSIDE);
129 }
130
131 /* move the king */
193132 out->post_board->board[out->end.rank][out->end.file] =
194133 out->post_board->board[out->start.rank][out->start.file];
195134 out->post_board->board[out->start.rank][out->start.file] =
196 (struct piece) { .color = 0, .piece_type = 0 };
197
198 /* remove the captured piece */
199 capture_rank = out->end.rank;
200 if (player == WHITE)
201 capture_rank--;
202 else
203 capture_rank++;
204 out->post_board->board[capture_rank][out->end.file] =
205 (struct piece) { .color = 0, .piece_type = 0 };
206 }
207 return 1;
135 (struct piece){ .color = 0, .piece_type = 0 };
136
137 /* move the rook */
138 out->post_board->board[out->end.rank][rook_end_file] =
139 out->post_board->board[out->start.rank][rook_start_file];
140 out->post_board->board[out->start.rank][rook_start_file] =
141 (struct piece){ .color = 0, .piece_type = 0 };
142
143 return 1;
144 }
145
146 int
147 parse_pawn(
148 const char *notation, const struct move *last_move, const bool is_capture,
149 struct move *out)
150 {
151 struct board *b;
152 int capture_rank;
153 color_t player;
154 bool is_passant;
155
156 player = opposite(last_move->player);
157 b = last_move->post_board;
158
159 out->start.file = notation[0] - 'a';
160 out->start.rank = 0;
161 out->end.file = notation[strlen(notation) - 2] - 'a';
162 out->end.rank = notation[strlen(notation) - 1] - '1';
163 if (!is_valid_position(out->start))
164 return 0;
165 if (!is_valid_position(out->end))
166 return 0;
167
168 is_passant = false;
169
170 if (is_capture)
171 {
172 if (player == WHITE)
173 {
174 out->start.rank = out->end.rank - 1;
175 }
176 else
177 {
178 out->start.rank = out->end.rank + 1;
179 }
180 is_passant =
181 !b->board[out->end.rank][out->end.file].piece_type;
182 }
183 else
184 {
185 if (player == WHITE)
186 {
187 if (out->end.rank == 3)
188 {
189 if (b->board[2][out->start.file].color
190 == WHITE)
191 {
192 out->start.rank = 2;
193 }
194 else
195 {
196 out->start.rank = 1;
197 }
198 }
199 else
200 {
201 out->start.rank = out->end.rank - 1;
202 }
203 }
204 else
205 {
206 if (out->end.rank == 4)
207 {
208 if (b->board[5][out->start.file].color
209 == BLACK)
210 {
211 out->start.rank = 5;
212 }
213 else
214 {
215 out->start.rank = 6;
216 }
217 }
218 else
219 {
220 out->start.rank = out->end.rank + 1;
221 }
222 }
223 }
224
225 if (is_passant)
226 {
227 out->post_board = calloc(1, sizeof(struct board));
228 memcpy(
229 out->post_board, last_move->post_board,
230 sizeof(struct board));
231
232 /* move the pawn */
233 out->post_board->board[out->end.rank][out->end.file] =
234 out->post_board->board[out->start.rank][out->start.file];
235 out->post_board->board[out->start.rank][out->start.file] =
236 (struct piece){ .color = 0, .piece_type = 0 };
237
238 /* remove the captured piece */
239 capture_rank = out->end.rank;
240 if (player == WHITE)
241 capture_rank--;
242 else
243 capture_rank++;
244 out->post_board->board[capture_rank][out->end.file] =
245 (struct piece){ .color = 0, .piece_type = 0 };
246 }
247 return 1;
208248 }
209249
210250 size_t
211251 trim_commentary(const char *in)
212252 {
213 size_t end;
214 size_t in_len;
215 in_len = strlen(in);
216 for (end = 0; end < in_len; end++) {
217 if (in[end] == '!')
218 break;
219 if (in[end] == '?')
220 break;
221 }
222 return end;
253 size_t end;
254 size_t in_len;
255 in_len = strlen(in);
256 for (end = 0; end < in_len; end++)
257 {
258 if (in[end] == '!')
259 break;
260 if (in[end] == '?')
261 break;
262 }
263 return end;
223264 }
224265
225266 void
226 parse_algebraic(
227 const char *input,
228 struct move *last_move,
229 struct move **out)
230 {
231 char *notation;
232 char *notation_head;
233 bool is_capture;
234 size_t capture_index; /* only set if is_capture */
235 size_t input_len;
236 size_t disambig_len;
237 struct move *result;
238 struct piece piece;
239 int i;
240
241 /* we modify the notation later to ease parsing, so we copy it here to avoid
242 * modifying our input. */
243 input_len = trim_commentary(input);
244 notation = calloc(input_len + 1, sizeof(char));
245 notation_head = notation;
246 strncpy(notation, input, input_len);
247 notation[input_len] = '\0';
248
249 /* create result and fill in known fields */
250 result = calloc(1, sizeof(struct move));
251 result->player = opposite(last_move->player);
252 result->algebraic = calloc(input_len + 1, sizeof(char));
253 strncpy(result->algebraic, notation, input_len);
254 result->parent = last_move;
255
256 if (input_len < 2) {
257 alg_fail("input too short");
258 }
259 if (last_move->post_board->termination & TERM_GAME_OVER_MASK) {
260 alg_fail("game is over, termination %d",
261 last_move->post_board->termination);
262 }
263
264 is_capture = false;
265
266 if (parse_castle(notation, last_move, result)) {
267 piece.piece_type = KING;
267 parse_algebraic(const char *input, struct move *last_move, struct move **out)
268 {
269 char *notation;
270 char *notation_head;
271 bool is_capture;
272 size_t capture_index; /* only set if is_capture */
273 size_t input_len;
274 size_t disambig_len;
275 struct move *result;
276 struct piece piece;
277 int i;
278
279 /* we modify the notation later to ease parsing, so we copy it here to
280 * avoid modifying our input. */
281 input_len = trim_commentary(input);
282 notation = calloc(input_len + 1, sizeof(char));
283 notation_head = notation;
284 strncpy(notation, input, input_len);
285 notation[input_len] = '\0';
286
287 /* create result and fill in known fields */
288 result = calloc(1, sizeof(struct move));
289 result->player = opposite(last_move->player);
290 result->algebraic = calloc(input_len + 1, sizeof(char));
291 strncpy(result->algebraic, notation, input_len);
292 result->parent = last_move;
293
294 if (input_len < 2)
295 {
296 alg_fail("input too short");
297 }
298 if (last_move->post_board->termination & TERM_GAME_OVER_MASK)
299 {
300 alg_fail(
301 "game is over, termination %d",
302 last_move->post_board->termination);
303 }
304
305 is_capture = false;
306
307 if (parse_castle(notation, last_move, result))
308 {
309 piece.piece_type = KING;
310 piece.color = result->player;
311 goto done;
312 }
313
268314 piece.color = result->player;
269 goto done;
270 }
271
272 piece.color = result->player;
273 piece.piece_type = PAWN;
274 for (i = 0; i < (int) (sizeof(ALL_PIECES) / sizeof(piece_type_t)); i++) {
275 if ((int) ALL_PIECES[i] == notation[0]) {
276 piece.piece_type = notation[0];
277 /* strip the piece off the front of the notation */
278 notation++;
279 break;
280 }
281 }
282
283 capture_index = 0;
284 for (i = 0; notation[i] != '\0'; i++) {
285 if (notation[i] == 'x') {
286 is_capture = true;
287 capture_index = i;
288 break;
289 }
290 }
291
292 /* strip the check/checkmate annotation, we don't need that to determine the
293 * nature of the move. */
294 i = strlen(notation) - 1;
295 if (notation[i] == '#' || notation[i] == '+') {
296 notation[i] = '\0';
297 }
298 result->end.rank = -1;
299 result->end.file = -1;
300 read_location(&notation[strlen(notation) - 2], &result->end);
301 if (result->end.rank == -1 || result->end.file == -1) {
302 alg_fail("end location didn't parse");
303 }
304
305 if (piece.piece_type == PAWN) {
306 if (parse_pawn(notation, last_move, is_capture, result)) {
307 goto done;
308 }
309 alg_fail("failed to parse pawn movement");
310 }
311
312 if (is_capture) {
313 disambig_len = capture_index;
314 } else {
315 disambig_len = strlen(notation) - 2;
316 }
317
318 /* put in sentinel values so we can tell what was initialized (if anything)
319 * during disambiguation loading. */
320 result->start.rank = -1;
321 result->start.file = -1;
322 if (disambig_len == 2) {
323 /* easiest case: we have two disambig characters that give us the full
324 * location of the start piece. */
325 read_location(notation, &result->start);
326 goto done;
327 } else if (disambig_len == 1) {
328 if ('a' <= notation[0] && notation[0] <= 'h') {
329 result->start.file = notation[0] - 'a';
330 } else if ('1' <= notation[0] && notation[0] <= '8') {
331 result->start.rank = notation[0] - '1';
332 } else {
333 alg_fail("invalid disambig char %c", notation[0]);
334 }
335 }
336
337 find_piece_with_access(piece, result);
338 if (result->start.rank == -1 || result->end.rank == -1)
339 alg_fail("no pieces with access to end location");
315 piece.piece_type = PAWN;
316 for (i = 0; i < (int)(sizeof(ALL_PIECES) / sizeof(piece_type_t)); i++)
317 {
318 if ((int)ALL_PIECES[i] == notation[0])
319 {
320 piece.piece_type = notation[0];
321 /* strip the piece off the front of the notation */
322 notation++;
323 break;
324 }
325 }
326
327 capture_index = 0;
328 for (i = 0; notation[i] != '\0'; i++)
329 {
330 if (notation[i] == 'x')
331 {
332 is_capture = true;
333 capture_index = i;
334 break;
335 }
336 }
337
338 /* strip the check/checkmate annotation, we don't need that to
339 * determine the nature of the move. */
340 i = strlen(notation) - 1;
341 if (notation[i] == '#' || notation[i] == '+')
342 {
343 notation[i] = '\0';
344 }
345 result->end.rank = -1;
346 result->end.file = -1;
347 read_location(&notation[strlen(notation) - 2], &result->end);
348 if (result->end.rank == -1 || result->end.file == -1)
349 {
350 alg_fail("end location didn't parse");
351 }
352
353 if (piece.piece_type == PAWN)
354 {
355 if (parse_pawn(notation, last_move, is_capture, result))
356 {
357 goto done;
358 }
359 alg_fail("failed to parse pawn movement");
360 }
361
362 if (is_capture)
363 {
364 disambig_len = capture_index;
365 }
366 else
367 {
368 disambig_len = strlen(notation) - 2;
369 }
370
371 /* put in sentinel values so we can tell what was initialized (if
372 * anything) during disambiguation loading. */
373 result->start.rank = -1;
374 result->start.file = -1;
375 if (disambig_len == 2)
376 {
377 /* easiest case: we have two disambig characters that give us
378 * the full location of the start piece. */
379 read_location(notation, &result->start);
380 goto done;
381 }
382 else if (disambig_len == 1)
383 {
384 if ('a' <= notation[0] && notation[0] <= 'h')
385 {
386 result->start.file = notation[0] - 'a';
387 }
388 else if ('1' <= notation[0] && notation[0] <= '8')
389 {
390 result->start.rank = notation[0] - '1';
391 }
392 else
393 {
394 alg_fail("invalid disambig char %c", notation[0]);
395 }
396 }
397
398 find_piece_with_access(piece, result);
399 if (result->start.rank == -1 || result->end.rank == -1)
400 alg_fail("no pieces with access to end location");
340401
341402 done:
342 apply_movement(result);
343
344 if (!is_movement_valid(result)) {
345 alg_fail("movement wasn't valid");
346 }
347
348 if (piece.piece_type == KING) {
349 if (result->player == WHITE) {
350 result->post_board->available_castles &=
351 ~(WHITE_KINGSIDE | WHITE_QUEENSIDE);
352 } else {
353 result->post_board->available_castles &=
354 ~(BLACK_KINGSIDE | BLACK_QUEENSIDE);
355 }
356 }
357 else if (piece.piece_type == ROOK) {
358 if (result->start.file == 0) {
359 result->post_board->available_castles &=
360 ~(WHITE_QUEENSIDE | BLACK_QUEENSIDE);
361 }
362 else if (result->start.file == 7) {
363 result->post_board->available_castles &=
364 ~(WHITE_KINGSIDE | BLACK_KINGSIDE);
365 }
366 }
367
368 if (piece.piece_type == PAWN) {
369 if (abs(result->start.rank - result->end.rank) == 2 &&
370 result->start.file == result->end.file)
371 result->post_board->passant_file = result->end.file;
372 else
373 result->post_board->