diff -r 3fff4c364ffc -r 12d8f0f6ef06 test/golden-master/bigtest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/golden-master/bigtest.html Mon Oct 03 12:14:53 2022 +0200 @@ -0,0 +1,861 @@ + + +
++ 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 } ++ + +