src/chess/rules.c

changeset 48
0cedda2544da
parent 47
d726e4b46c33
child 49
02c509a44e98
equal deleted inserted replaced
47:d726e4b46c33 48:0cedda2544da
31 #include "chess.h" 31 #include "chess.h"
32 #include <string.h> 32 #include <string.h>
33 #include <stdlib.h> 33 #include <stdlib.h>
34 #include <sys/time.h> 34 #include <sys/time.h>
35 35
36 static GameState gamestate_copy_sim(GameState *gamestate) {
37 GameState simulation = *gamestate;
38 if (simulation.lastmove) {
39 MoveList *lastmovecopy = malloc(sizeof(MoveList));
40 *lastmovecopy = *(simulation.lastmove);
41 simulation.movelist = simulation.lastmove = lastmovecopy;
42 }
43
44 return simulation;
45 }
46
36 void gamestate_cleanup(GameState *gamestate) { 47 void gamestate_cleanup(GameState *gamestate) {
37 MoveList *elem; 48 MoveList *elem;
38 elem = gamestate->movelist; 49 elem = gamestate->movelist;
39 while (elem) { 50 while (elem) {
40 MoveList *cur = elem; 51 MoveList *cur = elem;
143 } 154 }
144 155
145 addmove(gamestate, move); 156 addmove(gamestate, move);
146 } 157 }
147 158
148 static _Bool validate_move_rules(GameState *gamestate, Move *move) { 159 static int validate_move_rules(GameState *gamestate, Move *move) {
149 /* validate indices (don't trust opponent) */ 160 /* validate indices (don't trust opponent) */
150 if (!chkidx(move)) { 161 if (!chkidx(move)) {
151 return 0; 162 return INVALID_POSITION;
152 } 163 }
153 164
154 /* must move */ 165 /* must move */
155 if (move->fromfile == move->tofile && move->fromrow == move->torow) { 166 if (move->fromfile == move->tofile && move->fromrow == move->torow) {
156 return 0; 167 return INVALID_MOVE_SYNTAX;
157 } 168 }
158 169
159 /* does piece exist */ 170 /* does piece exist */
160 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) 171 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
161 != (move->piece&(PIECE_MASK|COLOR_MASK))) { 172 != (move->piece&(PIECE_MASK|COLOR_MASK))) {
162 return 0; 173 return INVALID_POSITION;
163 } 174 }
164 175
165 /* can't capture own pieces */ 176 /* can't capture own pieces */
166 if ((mdst(gamestate->board, move) & COLOR_MASK) 177 if ((mdst(gamestate->board, move) & COLOR_MASK)
167 == (move->piece & COLOR_MASK)) { 178 == (move->piece & COLOR_MASK)) {
168 return 0; 179 return RULES_VIOLATED;
169 } 180 }
170 181
171 /* must capture, if and only if destination is occupied */ 182 /* must capture, if and only if destination is occupied */
172 if ((mdst(gamestate->board, move) == 0 && move->capture) || 183 if ((mdst(gamestate->board, move) == 0 && move->capture) ||
173 (mdst(gamestate->board, move) != 0 && !move->capture)) { 184 (mdst(gamestate->board, move) != 0 && !move->capture)) {
174 return 0; 185 return INVALID_MOVE_SYNTAX;
175 } 186 }
176 187
177 /* validate individual rules */ 188 /* validate individual rules */
189 _Bool chkrules;
178 switch (move->piece & PIECE_MASK) { 190 switch (move->piece & PIECE_MASK) {
179 case PAWN: 191 case PAWN:
180 return pawn_chkrules(gamestate, move) && 192 chkrules = pawn_chkrules(gamestate, move) &&
181 !pawn_isblocked(gamestate, move); 193 !pawn_isblocked(gamestate, move);
194 break;
182 case ROOK: 195 case ROOK:
183 return rook_chkrules(move) && 196 chkrules = rook_chkrules(move) &&
184 !rook_isblocked(gamestate, move); 197 !rook_isblocked(gamestate, move);
198 break;
185 case KNIGHT: 199 case KNIGHT:
186 return knight_chkrules(move); /* knight is never blocked */ 200 chkrules = knight_chkrules(move); /* knight is never blocked */
201 break;
187 case BISHOP: 202 case BISHOP:
188 return bishop_chkrules(move) && 203 chkrules = bishop_chkrules(move) &&
189 !bishop_isblocked(gamestate, move); 204 !bishop_isblocked(gamestate, move);
205 break;
190 case QUEEN: 206 case QUEEN:
191 return queen_chkrules(move) && 207 chkrules = queen_chkrules(move) &&
192 !queen_isblocked(gamestate, move); 208 !queen_isblocked(gamestate, move);
209 break;
193 case KING: 210 case KING:
194 return king_chkrules(gamestate, move) && 211 chkrules = king_chkrules(gamestate, move) &&
195 !king_isblocked(gamestate, move); 212 !king_isblocked(gamestate, move);
213 break;
196 default: 214 default:
197 return 0; 215 return INVALID_MOVE_SYNTAX;
198 } 216 }
199 } 217
200 218 return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
201 _Bool validate_move(GameState *gamestate, Move *move) { 219 }
202 // TODO: provide more details via a return code 220
203 221 int validate_move(GameState *gamestate, Move *move) {
204 _Bool result = validate_move_rules(gamestate, move); 222
223 int result = validate_move_rules(gamestate, move);
205 224
206 /* cancel processing to save resources */ 225 /* cancel processing to save resources */
207 if (!result) { 226 if (result != VALID_MOVE_SEMANTICS) {
208 return 0; 227 return result;
209 } 228 }
210 229
211 /* find kings for check validation */ 230 /* find kings for check validation */
212 uint8_t piececolor = (move->piece & COLOR_MASK); 231 uint8_t piececolor = (move->piece & COLOR_MASK);
213 232
224 opkingrow = row; 243 opkingrow = row;
225 } 244 }
226 } 245 }
227 } 246 }
228 247
229 /* simulation move for check validation */ 248 /* simulate move for check validation */
230 GameState simulation = *gamestate; 249 GameState simulation = gamestate_copy_sim(gamestate);
231 Move simmove = *move; 250 Move simmove = *move;
232 apply_move(&simulation, &simmove); 251 apply_move(&simulation, &simmove);
233 252
234 /* don't move into or stay in check position */ 253 /* don't move into or stay in check position */
235 if (is_covered(&simulation, mykingrow, mykingfile, 254 if (is_covered(&simulation, mykingrow, mykingfile,
236 opponent_color(piececolor))) { 255 opponent_color(piececolor))) {
237 return 0; 256
257 gamestate_cleanup(&simulation);
258 if ((move->piece & PIECE_MASK) == KING) {
259 return KING_MOVES_INTO_CHECK;
260 } else {
261 /* last move is always not null in this case */
262 return gamestate->lastmove->move.check ?
263 KING_IN_CHECK : PIECE_PINNED;
264 }
238 } 265 }
239 266
240 /* correct check and checkmate flags (move is still valid) */ 267 /* correct check and checkmate flags (move is still valid) */
241 Move threats[16]; 268 Move threats[16];
242 uint8_t threatcount; 269 uint8_t threatcount;
313 if (!canescape) { 340 if (!canescape) {
314 gamestate->checkmate = 1; 341 gamestate->checkmate = 1;
315 } 342 }
316 } 343 }
317 344
318 return 1; 345 gamestate_cleanup(&simulation);
346
347 return VALID_MOVE_SEMANTICS;
319 } 348 }
320 349
321 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, 350 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
322 uint8_t color, Move *threats, uint8_t *threatcount) { 351 uint8_t color, Move *threats, uint8_t *threatcount) {
323 Move candidates[32]; 352 Move candidates[32];
349 378
350 379
351 _Bool result = 0; 380 _Bool result = 0;
352 381
353 for (int i = 0 ; i < candidatecount ; i++) { 382 for (int i = 0 ; i < candidatecount ; i++) {
354 if (validate_move_rules(gamestate, &(candidates[i]))) { 383 if (validate_move_rules(gamestate, &(candidates[i]))
384 == VALID_MOVE_SEMANTICS) {
355 result = 1; 385 result = 1;
356 if (threats && threatcount) { 386 if (threats && threatcount) {
357 threats[(*threatcount)++] = candidates[i]; 387 threats[(*threatcount)++] = candidates[i];
358 } 388 }
359 } 389 }
373 kingrow = row; 403 kingrow = row;
374 } 404 }
375 } 405 }
376 } 406 }
377 407
378 GameState simulation = *gamestate; 408 GameState simulation = gamestate_copy_sim(gamestate);
379 Move simmove = *move; 409 Move simmove = *move;
380 apply_move(&simulation, &simmove); 410 apply_move(&simulation, &simmove);
381 return is_covered(&simulation, kingrow, kingfile, opponent_color(color)); 411 _Bool covered = is_covered(&simulation,
412 kingrow, kingfile, opponent_color(color));
413 gamestate_cleanup(&simulation);
414
415 return covered;
382 } 416 }
383 417
384 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, 418 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
385 uint8_t color, Move *threats, uint8_t *threatcount) { 419 uint8_t color, Move *threats, uint8_t *threatcount) {
386 420
402 } 436 }
403 } 437 }
404 } 438 }
405 439
406 for (uint8_t i = 0 ; i < candidatecount ; i++) { 440 for (uint8_t i = 0 ; i < candidatecount ; i++) {
407 GameState simulation = *gamestate; 441 GameState simulation = gamestate_copy_sim(gamestate);
408 Move simmove = candidates[i]; 442 Move simmove = candidates[i];
409 apply_move(&simulation, &simmove); 443 apply_move(&simulation, &simmove);
410 if (!is_covered(&simulation, kingrow, kingfile, 444 if (!is_covered(&simulation, kingrow, kingfile,
411 opponent_color(color))) { 445 opponent_color(color))) {
412 result = 1; 446 result = 1;
413 if (threats && threatcount) { 447 if (threats && threatcount) {
414 threats[(*threatcount)++] = candidates[i]; 448 threats[(*threatcount)++] = candidates[i];
415 } 449 }
416 } 450 }
451 gamestate_cleanup(&simulation);
417 } 452 }
418 453
419 return result; 454 return result;
420 } else { 455 } else {
421 return 0; 456 return 0;
422 } 457 }
423 } 458 }
424 #include <ncurses.h> 459
425 static int getlocation(GameState *gamestate, Move *move) { 460 static int getlocation(GameState *gamestate, Move *move) {
426 461
427 uint8_t color = move->piece & COLOR_MASK; 462 uint8_t color = move->piece & COLOR_MASK;
428 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; 463 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
429 464
471 memset(move, 0, sizeof(Move)); 506 memset(move, 0, sizeof(Move));
472 move->fromfile = POS_UNSPECIFIED; 507 move->fromfile = POS_UNSPECIFIED;
473 move->fromrow = POS_UNSPECIFIED; 508 move->fromrow = POS_UNSPECIFIED;
474 509
475 size_t len = strlen(mstr); 510 size_t len = strlen(mstr);
511 if (len < 1 || len > 6) {
512 return INVALID_MOVE_SYNTAX;
513 }
476 514
477 /* evaluate check/checkmate flags */ 515 /* evaluate check/checkmate flags */
478 if (mstr[len-1] == '+') { 516 if (mstr[len-1] == '+') {
479 len--; mstr[len] = '\0'; 517 len--; mstr[len] = '\0';
480 move->check = 1; 518 move->check = 1;
511 /* move (e.g. "Nf3") */ 549 /* move (e.g. "Nf3") */
512 move->piece = getpiece(mstr[0]); 550 move->piece = getpiece(mstr[0]);
513 move->tofile = fileidx(mstr[1]); 551 move->tofile = fileidx(mstr[1]);
514 move->torow = rowidx(mstr[2]); 552 move->torow = rowidx(mstr[2]);
515 } 553 }
516
517 } else if (len == 4) { 554 } else if (len == 4) {
518 move->piece = getpiece(mstr[0]); 555 move->piece = getpiece(mstr[0]);
519 if (!move->piece) { 556 if (!move->piece) {
520 move->piece = PAWN; 557 move->piece = PAWN;
521 move->fromfile = fileidx(mstr[0]); 558 move->fromfile = fileidx(mstr[0]);

mercurial