diff -r 9f25df78925e -r 1b12cf799fee test/v2-regression/bigtest.html --- a/test/v2-regression/bigtest.html Thu Nov 10 18:44:48 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,855 +0,0 @@ - - -
-- 1 /* - 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - 3 * - 4 * Copyright 2014 Mike Becker. All rights reserved. - 5 * - 6 * Redistribution and use in source and binary forms, with or without - 7 * modification, are permitted provided that the following conditions are met: - 8 * - 9 * 1. Redistributions of source code must retain the above copyright - 10 * notice, this list of conditions and the following disclaimer. - 11 * - 12 * 2. Redistributions in binary form must reproduce the above copyright - 13 * notice, this list of conditions and the following disclaimer in the - 14 * documentation and/or other materials provided with the distribution. - 15 * - 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - 26 * POSSIBILITY OF SUCH DAMAGE. - 27 * - 28 */ - 29 - 30 #include "rules.h" - 31 #include "chess.h" - 32 #include <string.h> - 33 #include <stdlib.h> - 34 #include <sys/time.h> - 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 - 47 void gamestate_init(GameState *gamestate) { - 48 memset(gamestate, 0, sizeof(GameState)); - 49 - 50 Board initboard = { - 51 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, - 52 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, - 53 {0, 0, 0, 0, 0, 0, 0, 0}, - 54 {0, 0, 0, 0, 0, 0, 0, 0}, - 55 {0, 0, 0, 0, 0, 0, 0, 0}, - 56 {0, 0, 0, 0, 0, 0, 0, 0}, - 57 {BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN}, - 58 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} - 59 }; - 60 memcpy(gamestate->board, initboard, sizeof(Board)); - 61 } - 62 - 63 void gamestate_cleanup(GameState *gamestate) { - 64 MoveList *elem; - 65 elem = gamestate->movelist; - 66 while (elem) { - 67 MoveList *cur = elem; - 68 elem = elem->next; - 69 free(cur); - 70 }; - 71 } - 72 - 73 /* MUST be called IMMEDIATLY after applying a move to work correctly */ - 74 static void format_move(GameState *gamestate, Move *move) { - 75 char *string = move->string; - 76 - 77 /* at least 8 characters should be available, wipe them out */ - 78 memset(string, 0, 8); - 79 - 80 /* special formats for castling */ - 81 if ((move->piece&PIECE_MASK) == KING && - 82 abs(move->tofile-move->fromfile) == 2) { - 83 if (move->tofile==fileidx('c')) { - 84 memcpy(string, "O-O-O", 5); - 85 } else { - 86 memcpy(string, "O-O", 3); - 87 } - 88 } - 89 - 90 /* start by notating the piece character */ - 91 string[0] = getpiecechr(move->piece); - 92 int idx = string[0] ? 1 : 0; - 93 - 94 /* find out how many source information we do need */ - 95 uint8_t piece = move->piece & PIECE_MASK; - 96 if (piece == PAWN) { - 97 if (move->capture) { - 98 string[idx++] = filechr(move->fromfile); - 99 } -100 } else if (piece != KING) { -101 Move threats[16]; -102 uint8_t threatcount; -103 get_real_threats(gamestate, move->torow, move->tofile, -104 move->piece&COLOR_MASK, threats, &threatcount); -105 if (threatcount > 1) { -106 int ambrows = 0, ambfiles = 0; -107 for (uint8_t i = 0 ; i < threatcount ; i++) { -108 if (threats[i].fromrow == move->fromrow) { -109 ambrows++; -110 } -111 if (threats[i].fromfile == move->fromfile) { -112 ambfiles++; -113 } -114 } -115 /* ambiguous row, name file */ -116 if (ambrows > 1) { -117 string[idx++] = filechr(move->fromfile); -118 } -119 /* ambiguous file, name row */ -120 if (ambfiles > 1) { -121 string[idx++] = filechr(move->fromrow); -122 } -123 } -124 } -125 -126 /* capturing? */ -127 if (move->capture) { -128 string[idx++] = 'x'; -129 } -130 -131 /* destination */ -132 string[idx++] = filechr(move->tofile); -133 string[idx++] = rowchr(move->torow); -134 -135 /* promotion? */ -136 if (move->promotion) { -137 string[idx++] = '='; -138 string[idx++] = getpiecechr(move->promotion); -139 } -140 -141 /* check? */ -142 if (move->check) { -143 /* works only, if this function is called when applying the move */ -144 string[idx++] = gamestate->checkmate?'#':'+'; -145 } -146 } -147 -148 static void addmove(GameState* gamestate, Move *move) { -149 MoveList *elem = malloc(sizeof(MoveList)); -150 elem->next = NULL; -151 elem->move = *move; -152 -153 struct timeval curtimestamp; -154 gettimeofday(&curtimestamp, NULL); -155 elem->move.timestamp.tv_sec = curtimestamp.tv_sec; -156 elem->move.timestamp.tv_usec = curtimestamp.tv_usec; -157 -158 if (gamestate->lastmove) { -159 struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp); -160 uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec; -161 suseconds_t micros; -162 if (curtimestamp.tv_usec < lasttstamp->tv_usec) { -163 micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec); -164 sec--; -165 } else { -166 micros = curtimestamp.tv_usec - lasttstamp->tv_usec; -167 } -168 -169 elem->move.movetime.tv_sec = sec; -170 elem->move.movetime.tv_usec = micros; -171 -172 gamestate->lastmove->next = elem; -173 gamestate->lastmove = elem; -174 } else { -175 elem->move.movetime.tv_usec = 0; -176 elem->move.movetime.tv_sec = 0; -177 gamestate->movelist = gamestate->lastmove = elem; -178 } -179 } -180 -181 char getpiecechr(uint8_t piece) { -182 switch (piece & PIECE_MASK) { -183 case ROOK: return 'R'; -184 case KNIGHT: return 'N'; -185 case BISHOP: return 'B'; -186 case QUEEN: return 'Q'; -187 case KING: return 'K'; -188 default: return '\0'; -189 } -190 } -191 -192 uint8_t getpiece(char c) { -193 switch (c) { -194 case 'R': return ROOK; -195 case 'N': return KNIGHT; -196 case 'B': return BISHOP; -197 case 'Q': return QUEEN; -198 case 'K': return KING; -199 default: return 0; -200 } -201 } -202 -203 static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { -204 uint8_t piece = move->piece & PIECE_MASK; -205 uint8_t color = move->piece & COLOR_MASK; -206 -207 /* en passant capture */ -208 if (move->capture && piece == PAWN && -209 mdst(gamestate->board, move) == 0) { -210 gamestate->board[move->fromrow][move->tofile] = 0; -211 } -212 -213 /* remove old en passant threats */ -214 for (uint8_t file = 0 ; file < 8 ; file++) { -215 gamestate->board[3][file] &= ~ENPASSANT_THREAT; -216 gamestate->board[4][file] &= ~ENPASSANT_THREAT; -217 } -218 -219 /* add new en passant threat */ -220 if (piece == PAWN && ( -221 (move->fromrow == 1 && move->torow == 3) || -222 (move->fromrow == 6 && move->torow == 4))) { -223 move->piece |= ENPASSANT_THREAT; -224 } -225 -226 /* move (and maybe capture or promote) */ -227 msrc(gamestate->board, move) = 0; -228 if (move->promotion) { -229 mdst(gamestate->board, move) = move->promotion; -230 } else { -231 mdst(gamestate->board, move) = move->piece; -232 } -233 -234 /* castling */ -235 if (piece == KING && move->fromfile == fileidx('e')) { -236 -237 if (move->tofile == fileidx('g')) { -238 gamestate->board[move->torow][fileidx('h')] = 0; -239 gamestate->board[move->torow][fileidx('f')] = color|ROOK; -240 } else if (move->tofile == fileidx('c')) { -241 gamestate->board[move->torow][fileidx('a')] = 0; -242 gamestate->board[move->torow][fileidx('d')] = color|ROOK; -243 } -244 } -245 -246 if (!simulate) { -247 if (!move->string[0]) { -248 format_move(gamestate, move); -249 } -250 } -251 /* add move, even in simulation (checkmate test needs it) */ -252 addmove(gamestate, move); -253 } -254 -255 void apply_move(GameState *gamestate, Move *move) { -256 apply_move_impl(gamestate, move, 0); -257 } -258 -259 static int validate_move_rules(GameState *gamestate, Move *move) { -260 /* validate indices (don't trust opponent) */ -261 if (!chkidx(move)) { -262 return INVALID_POSITION; -263 } -264 -265 /* must move */ -266 if (move->fromfile == move->tofile && move->fromrow == move->torow) { -267 return INVALID_MOVE_SYNTAX; -268 } -269 -270 /* does piece exist */ -271 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) -272 != (move->piece&(PIECE_MASK|COLOR_MASK))) { -273 return INVALID_POSITION; -274 } -275 -276 /* can't capture own pieces */ -277 if ((mdst(gamestate->board, move) & COLOR_MASK) -278 == (move->piece & COLOR_MASK)) { -279 return RULES_VIOLATED; -280 } -281 -282 /* must capture, if and only if destination is occupied */ -283 if ((mdst(gamestate->board, move) == 0 && move->capture) || -284 (mdst(gamestate->board, move) != 0 && !move->capture)) { -285 return INVALID_MOVE_SYNTAX; -286 } -287 -288 /* validate individual rules */ -289 _Bool chkrules; -290 switch (move->piece & PIECE_MASK) { -291 case PAWN: -292 chkrules = pawn_chkrules(gamestate, move) && -293 !pawn_isblocked(gamestate, move); -294 break; -295 case ROOK: -296 chkrules = rook_chkrules(move) && -297 !rook_isblocked(gamestate, move); -298 break; -299 case KNIGHT: -300 chkrules = knight_chkrules(move); /* knight is never blocked */ -301 break; -302 case BISHOP: -303 chkrules = bishop_chkrules(move) && -304 !bishop_isblocked(gamestate, move); -305 break; -306 case QUEEN: -307 chkrules = queen_chkrules(move) && -308 !queen_isblocked(gamestate, move); -309 break; -310 case KING: -311 chkrules = king_chkrules(gamestate, move) && -312 !king_isblocked(gamestate, move); -313 break; -314 default: -315 return INVALID_MOVE_SYNTAX; -316 } -317 -318 return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; -319 } -320 -321 int validate_move(GameState *gamestate, Move *move) { -322 -323 int result = validate_move_rules(gamestate, move); -324 -325 /* cancel processing to save resources */ -326 if (result != VALID_MOVE_SEMANTICS) { -327 return result; -328 } -329 -330 /* find kings for check validation */ -331 uint8_t piececolor = (move->piece & COLOR_MASK); -332 -333 uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0; -334 for (uint8_t row = 0 ; row < 8 ; row++) { -335 for (uint8_t file = 0 ; file < 8 ; file++) { -336 if (gamestate->board[row][file] == -337 (piececolor == WHITE?WKING:BKING)) { -338 mykingfile = file; -339 mykingrow = row; -340 } else if (gamestate->board[row][file] == -341 (piececolor == WHITE?BKING:WKING)) { -342 opkingfile = file; -343 opkingrow = row; -344 } -345 } -346 } -347 -348 /* simulate move for check validation */ -349 GameState simulation = gamestate_copy_sim(gamestate); -350 Move simmove = *move; -351 apply_move_impl(&simulation, &simmove, 1); -352 -353 /* don't move into or stay in check position */ -354 if (is_covered(&simulation, mykingrow, mykingfile, -355 opponent_color(piececolor))) { -356 -357 gamestate_cleanup(&simulation); -358 if ((move->piece & PIECE_MASK) == KING) { -359 return KING_MOVES_INTO_CHECK; -360 } else { -361 /* last move is always not null in this case */ -362 return gamestate->lastmove->move.check ? -363 KING_IN_CHECK : PIECE_PINNED; -364 } -365 } -366 -367 /* correct check and checkmate flags (move is still valid) */ -368 Move threats[16]; -369 uint8_t threatcount; -370 move->check = get_threats(&simulation, opkingrow, opkingfile, -371 piececolor, threats, &threatcount); -372 -373 if (move->check) { -374 /* determine possible escape fields */ -375 _Bool canescape = 0; -376 for (int dr = -1 ; dr <= 1 && !canescape ; dr++) { -377 for (int df = -1 ; df <= 1 && !canescape ; df++) { -378 if (!(dr == 0 && df == 0) && -379 isidx(opkingrow + dr) && isidx(opkingfile + df)) { -380 -381 /* escape field neither blocked nor covered */ -382 if ((simulation.board[opkingrow + dr][opkingfile + df] -383 & COLOR_MASK) != opponent_color(piececolor)) { -384 canescape |= !is_covered(&simulation, -385 opkingrow + dr, opkingfile + df, piececolor); -386 } -387 } -388 } -389 } -390 /* can't escape, can he capture? */ -391 if (!canescape && threatcount == 1) { -392 canescape = is_attacked(&simulation, threats[0].fromrow, -393 threats[0].fromfile, opponent_color(piececolor)); -394 } -395 -396 /* can't capture, can he block? */ -397 if (!canescape && threatcount == 1) { -398 Move *threat = &(threats[0]); -399 uint8_t threatpiece = threat->piece & PIECE_MASK; -400 -401 /* knight, pawns and the king cannot be blocked */ -402 if (threatpiece == BISHOP || threatpiece == ROOK -403 || threatpiece == QUEEN) { -404 if (threat->fromrow == threat->torow) { -405 /* rook aspect (on row) */ -406 int d = threat->tofile > threat->fromfile ? 1 : -1; -407 uint8_t file = threat->fromfile; -408 while (!canescape && file != threat->tofile - d) { -409 file += d; -410 canescape |= is_protected(&simulation, -411 threat->torow, file, opponent_color(piececolor)); -412 } -413 } else if (threat->fromfile == threat->tofile) { -414 /* rook aspect (on file) */ -415 int d = threat->torow > threat->fromrow ? 1 : -1; -416 uint8_t row = threat->fromrow; -417 while (!canescape && row != threat->torow - d) { -418 row += d; -419 canescape |= is_protected(&simulation, -420 row, threat->tofile, opponent_color(piececolor)); -421 } -422 } else { -423 /* bishop aspect */ -424 int dr = threat->torow > threat->fromrow ? 1 : -1; -425 int df = threat->tofile > threat->fromfile ? 1 : -1; -426 -427 uint8_t row = threat->fromrow; -428 uint8_t file = threat->fromfile; -429 while (!canescape && file != threat->tofile - df -430 && row != threat->torow - dr) { -431 row += dr; -432 file += df; -433 canescape |= is_protected(&simulation, row, file, -434 opponent_color(piececolor)); -435 } -436 } -437 } -438 } -439 -440 if (!canescape) { -441 gamestate->checkmate = 1; -442 } -443 } -444 -445 gamestate_cleanup(&simulation); -446 -447 return VALID_MOVE_SEMANTICS; -448 } -449 -450 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file, -451 uint8_t color, Move *threats, uint8_t *threatcount) { -452 Move candidates[32]; -453 int candidatecount = 0; -454 for (uint8_t r = 0 ; r < 8 ; r++) { -455 for (uint8_t f = 0 ; f < 8 ; f++) { -456 if ((gamestate->board[r][f] & COLOR_MASK) == color) { -457 // non-capturing move -458 memset(&(candidates[candidatecount]), 0, sizeof(Move)); -459 candidates[candidatecount].piece = gamestate->board[r][f]; -460 candidates[candidatecount].fromrow = r; -461 candidates[candidatecount].fromfile = f; -462 candidates[candidatecount].torow = row; -463 candidates[candidatecount].tofile = file; -464 candidatecount++; -465 -466 // capturing move -467 memcpy(&(candidates[candidatecount]), -468 &(candidates[candidatecount-1]), sizeof(Move)); -469 candidates[candidatecount].capture = 1; -470 candidatecount++; -471 } -472 } -473 } -474 -475 if (threatcount) { -476 *threatcount = 0; -477 } -478 -479 -480 _Bool result = 0; -481 -482 for (int i = 0 ; i < candidatecount ; i++) { -483 if (validate_move_rules(gamestate, &(candidates[i])) -484 == VALID_MOVE_SEMANTICS) { -485 result = 1; -486 if (threats && threatcount) { -487 threats[(*threatcount)++] = candidates[i]; -488 } -489 } -490 } -491 -492 return result; -493 } -494 -495 _Bool is_pinned(GameState *gamestate, Move *move) { -496 uint8_t color = move->piece & COLOR_MASK; -497 -498 uint8_t kingfile = 0, kingrow = 0; -499 for (uint8_t row = 0 ; row < 8 ; row++) { -500 for (uint8_t file = 0 ; file < 8 ; file++) { -501 if (gamestate->board[row][file] == (color|KING)) { -502 kingfile = file; -503 kingrow = row; -504 } -505 } -506 } -507 -508 GameState simulation = gamestate_copy_sim(gamestate); -509 Move simmove = *move; -510 apply_move(&simulation, &simmove); -511 _Bool covered = is_covered(&simulation, -512 kingrow, kingfile, opponent_color(color)); -513 gamestate_cleanup(&simulation); -514 -515 return covered; -516 } -517 -518 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, -519 uint8_t color, Move *threats, uint8_t *threatcount) { -520 -521 if (threatcount) { -522 *threatcount = 0; -523 } -524 -525 Move candidates[16]; -526 uint8_t candidatecount; -527 if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) { -528 -529 _Bool result = 0; -530 uint8_t kingfile = 0, kingrow = 0; -531 for (uint8_t row = 0 ; row < 8 ; row++) { -532 for (uint8_t file = 0 ; file < 8 ; file++) { -533 if (gamestate->board[row][file] == (color|KING)) { -534 kingfile = file; -535 kingrow = row; -536 } -537 } -538 } -539 -540 for (uint8_t i = 0 ; i < candidatecount ; i++) { -541 GameState simulation = gamestate_copy_sim(gamestate); -542 Move simmove = candidates[i]; -543 apply_move(&simulation, &simmove); -544 if (!is_covered(&simulation, kingrow, kingfile, -545 opponent_color(color))) { -546 result = 1; -547 if (threats && threatcount) { -548 threats[(*threatcount)++] = candidates[i]; -549 } -550 } -551 } -552 -553 return result; -554 } else { -555 return 0; -556 } -557 } -558 -559 static int getlocation(GameState *gamestate, Move *move) { -560 -561 uint8_t color = move->piece & COLOR_MASK; -562 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; -563 -564 Move threats[16], *threat = NULL; -565 uint8_t threatcount; -566 -567 if (get_threats(gamestate, move->torow, move->tofile, color, -568 threats, &threatcount)) { -569 -570 int reason = INVALID_POSITION; -571 -572 // find threats for the specified position -573 for (uint8_t i = 0 ; i < threatcount ; i++) { -574 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) -575 == move->piece && -576 (move->fromrow == POS_UNSPECIFIED || -577 move->fromrow == threats[i].fromrow) && -578 (move->fromfile == POS_UNSPECIFIED || -579 move->fromfile == threats[i].fromfile)) { -580 -581 if (threat) { -582 return AMBIGUOUS_MOVE; -583 } else { -584 // found threat is no real threat -585 if (is_pinned(gamestate, &(threats[i]))) { -586 reason = incheck?KING_IN_CHECK:PIECE_PINNED; -587 } else { -588 threat = &(threats[i]); -589 } -590 } -591 } -592 } -593 -594 // can't threaten specified position -595 if (!threat) { -596 return reason; -597 } -598 -599 memcpy(move, threat, sizeof(Move)); -600 return VALID_MOVE_SYNTAX; -601 } else { -602 return INVALID_POSITION; -603 } -604 } -605 -606 int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) { -607 memset(move, 0, sizeof(Move)); -608 move->fromfile = POS_UNSPECIFIED; -609 move->fromrow = POS_UNSPECIFIED; -610 -611 size_t len = strlen(mstr); -612 if (len < 1 || len > 6) { -613 return INVALID_MOVE_SYNTAX; -614 } -615 -616 /* evaluate check/checkmate flags */ -617 if (mstr[len-1] == '+') { -618 len--; mstr[len] = '\0'; -619 move->check = 1; -620 } else if (mstr[len-1] == '#') { -621 len--; mstr[len] = '\0'; -622 /* ignore - validation should set game state */ -623 } -624 -625 /* evaluate promotion */ -626 if (len > 3 && mstr[len-2] == '=') { -627 move->promotion = getpiece(mstr[len-1]); -628 if (!move->promotion) { -629 return INVALID_MOVE_SYNTAX; -630 } else { -631 move->promotion |= color; -632 len -= 2; -633 mstr[len] = 0; -634 } -635 } -636 -637 if (len == 2) { -638 /* pawn move (e.g. "e4") */ -639 move->piece = PAWN; -640 move->tofile = fileidx(mstr[0]); -641 move->torow = rowidx(mstr[1]); -642 } else if (len == 3) { -643 if (strcmp(mstr, "O-O") == 0) { -644 /* king side castling */ -645 move->piece = KING; -646 move->fromfile = fileidx('e'); -647 move->tofile = fileidx('g'); -648 move->fromrow = move->torow = color == WHITE ? 0 : 7; -649 } else { -650 /* move (e.g. "Nf3") */ -651 move->piece = getpiece(mstr[0]); -652 move->tofile = fileidx(mstr[1]); -653 move->torow = rowidx(mstr[2]); -654 } -655 } else if (len == 4) { -656 move->piece = getpiece(mstr[0]); -657 if (!move->piece) { -658 move->piece = PAWN; -659 move->fromfile = fileidx(mstr[0]); -660 } -661 if (mstr[1] == 'x') { -662 /* capture (e.g. "Nxf3", "dxe5") */ -663 move->capture = 1; -664 } else { -665 /* move (e.g. "Ndf3", "N2c3", "e2e4") */ -666 if (isfile(mstr[1])) { -667 move->fromfile = fileidx(mstr[1]); -668 if (move->piece == PAWN) { -669 move->piece = 0; -670 } -671 } else { -672 move->fromrow = rowidx(mstr[1]); -673 } -674 } -675 move->tofile = fileidx(mstr[2]); -676 move->torow = rowidx(mstr[3]); -677 } else if (len == 5) { -678 if (strcmp(mstr, "O-O-O") == 0) { -679 /* queen side castling "O-O-O" */ -680 move->piece = KING; -681 move->fromfile = fileidx('e'); -682 move->tofile = fileidx('c'); -683 move->fromrow = move->torow = color == WHITE ? 0 : 7; -684 } else { -685 move->piece = getpiece(mstr[0]); -686 if (mstr[2] == 'x') { -687 move->capture = 1; -688 if (move->piece) { -689 /* capture (e.g. "Ndxf3") */ -690 move->fromfile = fileidx(mstr[1]); -691 } else { -692 /* long notation capture (e.g. "e5xf6") */ -693 move->piece = PAWN; -694 move->fromfile = fileidx(mstr[0]); -695 move->fromrow = rowidx(mstr[1]); -696 } -697 } else { -698 /* long notation move (e.g. "Nc5a4") */ -699 move->fromfile = fileidx(mstr[1]); -700 move->fromrow = rowidx(mstr[2]); -701 } -702 move->tofile = fileidx(mstr[3]); -703 move->torow = rowidx(mstr[4]); -704 } -705 } else if (len == 6) { -706 /* long notation capture (e.g. "Nc5xf3") */ -707 if (mstr[3] == 'x') { -708 move->capture = 1; -709 move->piece = getpiece(mstr[0]); -710 move->fromfile = fileidx(mstr[1]); -711 move->fromrow = rowidx(mstr[2]); -712 move->tofile = fileidx(mstr[4]); -713 move->torow = rowidx(mstr[5]); -714 } -715 } -716 -717 -718 if (move->piece) { -719 if (move->piece == PAWN -720 && move->torow == (color==WHITE?7:0) -721 && !move->promotion) { -722 return NEED_PROMOTION; -723 } -724 -725 move->piece |= color; -726 if (move->fromfile == POS_UNSPECIFIED -727 || move->fromrow == POS_UNSPECIFIED) { -728 return getlocation(gamestate, move); -729 } else { -730 return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; -731 } -732 } else { -733 return INVALID_MOVE_SYNTAX; -734 } -735 } -736 -737 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file, -738 uint8_t color) { -739 -740 Move threats[16]; -741 uint8_t threatcount; -742 if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) { -743 for (int i = 0 ; i < threatcount ; i++) { -744 if (threats[i].piece != (color|KING)) { -745 return 1; -746 } -747 } -748 return 0; -749 } else { -750 return 0; -751 } -752 } -753 -754 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate, -755 uint8_t color) { -756 if (!gameinfo->timecontrol) { -757 return 0; -758 } -759 -760 if (gamestate->movelist) { -761 uint16_t time = gameinfo->time; -762 suseconds_t micros = 0; -763 -764 MoveList *movelist = color == WHITE ? -765 gamestate->movelist : gamestate->movelist->next; -766 -767 while (movelist) { -768 time += gameinfo->addtime; -769 -770 struct movetimeval *movetime = &(movelist->move.movetime); -771 if (movetime->tv_sec >= time) { -772 return 0; -773 } -774 -775 time -= movetime->tv_sec; -776 micros += movetime->tv_usec; -777 -778 movelist = movelist->next ? movelist->next->next : NULL; -779 } -780 -781 time_t sec; -782 movelist = gamestate->lastmove; -783 if ((movelist->move.piece & COLOR_MASK) != color) { -784 struct movetimeval *lastmovetstamp = &(movelist->move.timestamp); -785 struct timeval currenttstamp; -786 gettimeofday(¤ttstamp, NULL); -787 micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec; -788 sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec; -789 if (sec >= time) { -790 return 0; -791 } -792 -793 time -= sec; -794 } -795 -796 sec = micros / 1e6L; -797 -798 if (sec >= time) { -799 return 0; -800 } -801 -802 time -= sec; -803 -804 return time; -805 } else { -806 return gameinfo->time; -807 } -808 } -- - -