src/chess/rules.c

Tue, 28 Aug 2018 15:56:33 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 28 Aug 2018 15:56:33 +0200
changeset 62
564af8a16828
parent 55
54ea19938d57
child 63
611332453da0
permissions
-rw-r--r--

fixes move validation working on old king's position, when the king moves

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2016 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  */
    30 #include "rules.h"
    31 #include "chess.h"
    32 #include <string.h>
    33 #include <stdlib.h>
    34 #include <sys/time.h>
    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     }
    44     return simulation;
    45 }
    47 void gamestate_init(GameState *gamestate) {
    48     memset(gamestate, 0, sizeof(GameState));
    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 }
    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 }
    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;
    77     /* at least 8 characters should be available, wipe them out */
    78     memset(string, 0, 8);
    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     }
    90     /* start by notating the piece character */
    91     string[0] = getpiecechr(move->piece);
    92     int idx = string[0] ? 1 : 0;
    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     }
   126     /* capturing? */
   127     if (move->capture) {
   128         string[idx++] = 'x';
   129     }
   131     /* destination */
   132     string[idx++] = filechr(move->tofile);
   133     string[idx++] = rowchr(move->torow);
   135     /* promotion? */
   136     if (move->promotion) {
   137         string[idx++] = '=';
   138         string[idx++] = getpiecechr(move->promotion);
   139     }
   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 }
   148 static void addmove(GameState* gamestate, Move *move) {
   149     MoveList *elem = malloc(sizeof(MoveList));
   150     elem->next = NULL;
   151     elem->move = *move;
   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;
   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         }
   169         elem->move.movetime.tv_sec = sec;
   170         elem->move.movetime.tv_usec = micros;
   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 }
   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 }
   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 }
   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;
   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     }
   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     }
   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     }
   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     }
   234     /* castling */
   235     if (piece == KING && move->fromfile == fileidx('e')) {
   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     }
   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 }
   255 void apply_move(GameState *gamestate, Move *move) {
   256     apply_move_impl(gamestate, move, 0);
   257 }
   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     }
   265     /* must move */
   266     if (move->fromfile == move->tofile && move->fromrow == move->torow) {
   267         return INVALID_MOVE_SYNTAX;
   268     }
   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     }
   276     /* can't capture own pieces */
   277     if ((mdst(gamestate->board, move) & COLOR_MASK)
   278             == (move->piece & COLOR_MASK)) {
   279         return RULES_VIOLATED;
   280     }
   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     }
   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     }
   318     return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
   319 }
   321 int validate_move(GameState *gamestate, Move *move) {
   323     int result = validate_move_rules(gamestate, move);
   325     /* cancel processing to save resources */
   326     if (result != VALID_MOVE_SEMANTICS) {
   327         return result;
   328     }
   330     /* simulate move for check validation */
   331     GameState simulation = gamestate_copy_sim(gamestate);
   332     Move simmove = *move;
   333     apply_move_impl(&simulation, &simmove, 1);
   335     /* find kings for check validation */
   336     uint8_t piececolor = (move->piece & COLOR_MASK);
   338     uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
   339     for (uint8_t row = 0 ; row < 8 ; row++) {
   340         for (uint8_t file = 0 ; file < 8 ; file++) {
   341             if (simulation.board[row][file] ==
   342                     (piececolor == WHITE?WKING:BKING)) {
   343                 mykingfile = file;
   344                 mykingrow = row;
   345             } else if (simulation.board[row][file] ==
   346                     (piececolor == WHITE?BKING:WKING)) {
   347                 opkingfile = file;
   348                 opkingrow = row;
   349             }
   350         }
   351     }
   353     /* don't move into or stay in check position */
   354     if (is_covered(&simulation, mykingrow, mykingfile,
   355         opponent_color(piececolor))) {
   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     }
   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);
   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)) {
   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         }
   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;
   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;
   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         }
   440         if (!canescape) {
   441             gamestate->checkmate = 1;
   442         }
   443     }
   445     gamestate_cleanup(&simulation);
   447     return VALID_MOVE_SEMANTICS;
   448 }
   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++;
   466                 // capturing move
   467                 memcpy(&(candidates[candidatecount]),
   468                     &(candidates[candidatecount-1]), sizeof(Move));
   469                 candidates[candidatecount].capture = 1;
   470                 candidatecount++;
   471             }
   472         }
   473     }
   475     if (threatcount) {
   476         *threatcount = 0;
   477     }
   480     _Bool result = 0;
   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     }
   492     return result;
   493 }
   495 _Bool is_pinned(GameState *gamestate, Move *move) {
   496     uint8_t color = move->piece & COLOR_MASK;
   498     GameState simulation = gamestate_copy_sim(gamestate);
   499     Move simmove = *move;
   500     apply_move(&simulation, &simmove);
   502     uint8_t kingfile = 0, kingrow = 0;
   503     for (uint8_t row = 0 ; row < 8 ; row++) {
   504         for (uint8_t file = 0 ; file < 8 ; file++) {
   505             if (simulation.board[row][file] == (color|KING)) {
   506                 kingfile = file;
   507                 kingrow = row;
   508             }
   509         }
   510     }
   512     _Bool covered = is_covered(&simulation,
   513         kingrow, kingfile, opponent_color(color));
   514     gamestate_cleanup(&simulation);
   516     return covered;
   517 }
   519 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
   520         uint8_t color, Move *threats, uint8_t *threatcount) {
   522     if (threatcount) {
   523         *threatcount = 0;
   524     }
   526     Move candidates[16];
   527     uint8_t candidatecount;
   528     if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
   530         _Bool result = 0;
   531         uint8_t kingfile = 0, kingrow = 0;
   532         for (uint8_t row = 0 ; row < 8 ; row++) {
   533             for (uint8_t file = 0 ; file < 8 ; file++) {
   534                 if (gamestate->board[row][file] == (color|KING)) {
   535                     kingfile = file;
   536                     kingrow = row;
   537                 }
   538             }
   539         }
   541         for (uint8_t i = 0 ; i < candidatecount ; i++) {
   542             GameState simulation = gamestate_copy_sim(gamestate);
   543             Move simmove = candidates[i];
   544             apply_move(&simulation, &simmove);
   545             if (!is_covered(&simulation, kingrow, kingfile,
   546                     opponent_color(color))) {
   547                 result = 1;
   548                 if (threats && threatcount) {
   549                     threats[(*threatcount)++] = candidates[i];
   550                 }
   551             }
   552         }
   554         return result;
   555     } else {
   556         return 0;
   557     }
   558 }
   560 static int getlocation(GameState *gamestate, Move *move) {   
   562     uint8_t color = move->piece & COLOR_MASK;
   563     _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
   565     Move threats[16], *threat = NULL;
   566     uint8_t threatcount;
   568     if (get_threats(gamestate, move->torow, move->tofile, color,
   569             threats, &threatcount)) {
   571         int reason = INVALID_POSITION;
   573         // find threats for the specified position
   574         for (uint8_t i = 0 ; i < threatcount ; i++) {            
   575             if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
   576                     == move->piece &&
   577                     (move->fromrow == POS_UNSPECIFIED ||
   578                     move->fromrow == threats[i].fromrow) &&
   579                     (move->fromfile == POS_UNSPECIFIED ||
   580                     move->fromfile == threats[i].fromfile)) {
   582                 if (threat) {
   583                     return AMBIGUOUS_MOVE;
   584                 } else {
   585                     // found threat is no real threat
   586                     if (is_pinned(gamestate, &(threats[i]))) {
   587                         reason = incheck?KING_IN_CHECK:PIECE_PINNED;
   588                     } else {
   589                         threat = &(threats[i]);
   590                     }
   591                 }
   592             }
   593         }
   595         // can't threaten specified position
   596         if (!threat) {
   597             return reason;
   598         }
   600         memcpy(move, threat, sizeof(Move));
   601         return VALID_MOVE_SYNTAX;
   602     } else {
   603         return INVALID_POSITION;
   604     }
   605 }
   607 int eval_move(GameState *gamestate, char *mstr, Move *move, uint8_t color) {
   608     memset(move, 0, sizeof(Move));
   609     move->fromfile = POS_UNSPECIFIED;
   610     move->fromrow = POS_UNSPECIFIED;
   612     size_t len = strlen(mstr);
   613     if (len < 1 || len > 6) {
   614         return INVALID_MOVE_SYNTAX;
   615     }
   617     /* evaluate check/checkmate flags */
   618     if (mstr[len-1] == '+') {
   619         len--; mstr[len] = '\0';
   620         move->check = 1;
   621     } else if (mstr[len-1] == '#') {
   622         len--; mstr[len] = '\0';
   623         /* ignore - validation should set game state */
   624     }
   626     /* evaluate promotion */
   627     if (len > 3 && mstr[len-2] == '=') {
   628         move->promotion = getpiece(mstr[len-1]);
   629         if (!move->promotion) {
   630             return INVALID_MOVE_SYNTAX;
   631         } else {
   632             move->promotion |= color;
   633             len -= 2;
   634             mstr[len] = 0;
   635         }
   636     }
   638     if (len == 2) {
   639         /* pawn move (e.g. "e4") */
   640         move->piece = PAWN;
   641         move->tofile = fileidx(mstr[0]);
   642         move->torow = rowidx(mstr[1]);
   643     } else if (len == 3) {
   644         if (strcmp(mstr, "O-O") == 0) {
   645             /* king side castling */
   646             move->piece = KING;
   647             move->fromfile = fileidx('e');
   648             move->tofile = fileidx('g');
   649             move->fromrow = move->torow = color == WHITE ? 0 : 7;
   650         } else {
   651             /* move (e.g. "Nf3") */
   652             move->piece = getpiece(mstr[0]);
   653             move->tofile = fileidx(mstr[1]);
   654             move->torow = rowidx(mstr[2]);
   655         }
   656     } else if (len == 4) {
   657         move->piece = getpiece(mstr[0]);
   658         if (!move->piece) {
   659             move->piece = PAWN;
   660             move->fromfile = fileidx(mstr[0]);
   661         }
   662         if (mstr[1] == 'x') {
   663             /* capture (e.g. "Nxf3", "dxe5") */
   664             move->capture = 1;
   665         } else {
   666             /* move (e.g. "Ndf3", "N2c3", "e2e4") */
   667             if (isfile(mstr[1])) {
   668                 move->fromfile = fileidx(mstr[1]);
   669                 if (move->piece == PAWN) {
   670                     move->piece = 0;
   671                 }
   672             } else {
   673                 move->fromrow = rowidx(mstr[1]);
   674             }
   675         }
   676         move->tofile = fileidx(mstr[2]);
   677         move->torow = rowidx(mstr[3]);
   678     } else if (len == 5) {
   679         if (strcmp(mstr, "O-O-O") == 0) {
   680             /* queen side castling "O-O-O" */
   681             move->piece = KING;
   682             move->fromfile = fileidx('e');
   683             move->tofile = fileidx('c');
   684             move->fromrow = move->torow = color == WHITE ? 0 : 7;
   685         } else {
   686             move->piece = getpiece(mstr[0]);
   687             if (mstr[2] == 'x') {
   688                 move->capture = 1;
   689                 if (move->piece) {
   690                     /* capture (e.g. "Ndxf3") */
   691                     move->fromfile = fileidx(mstr[1]);
   692                 } else {
   693                     /* long notation capture (e.g. "e5xf6") */
   694                     move->piece = PAWN;
   695                     move->fromfile = fileidx(mstr[0]);
   696                     move->fromrow = rowidx(mstr[1]);
   697                 }
   698             } else {
   699                 /* long notation move (e.g. "Nc5a4") */
   700                 move->fromfile = fileidx(mstr[1]);
   701                 move->fromrow = rowidx(mstr[2]);
   702             }
   703             move->tofile = fileidx(mstr[3]);
   704             move->torow = rowidx(mstr[4]);
   705         }
   706     } else if (len == 6) {
   707         /* long notation capture (e.g. "Nc5xf3") */
   708         if (mstr[3] == 'x') {
   709             move->capture = 1;
   710             move->piece = getpiece(mstr[0]);
   711             move->fromfile = fileidx(mstr[1]);
   712             move->fromrow = rowidx(mstr[2]);
   713             move->tofile = fileidx(mstr[4]);
   714             move->torow = rowidx(mstr[5]);
   715         }
   716     }
   719     if (move->piece) {
   720         if (move->piece == PAWN
   721             && move->torow == (color==WHITE?7:0)
   722             && !move->promotion) {
   723             return NEED_PROMOTION;
   724         }
   726         move->piece |= color;
   727         if (move->fromfile == POS_UNSPECIFIED
   728             || move->fromrow == POS_UNSPECIFIED) {
   729             return getlocation(gamestate, move);
   730         } else {
   731             return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
   732         }
   733     } else {
   734         return INVALID_MOVE_SYNTAX;
   735     }
   736 }
   738 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
   739         uint8_t color) {
   741     Move threats[16];
   742     uint8_t threatcount;
   743     if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
   744         for (int i = 0 ; i < threatcount ; i++) {
   745             if (threats[i].piece != (color|KING)) {
   746                 return 1;
   747             }
   748         }
   749         return 0;
   750     } else {
   751         return 0;
   752     }
   753 }
   755 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
   756         uint8_t color) {
   757     if (!gameinfo->timecontrol) {
   758         return 0;
   759     }
   761     if (gamestate->movelist) {
   762         uint16_t time = gameinfo->time;
   763         suseconds_t micros = 0;
   765         MoveList *movelist = color == WHITE ?
   766             gamestate->movelist : gamestate->movelist->next;
   768         while (movelist) {
   769             time += gameinfo->addtime;
   771             struct movetimeval *movetime = &(movelist->move.movetime);
   772             if (movetime->tv_sec >= time) {
   773                 return 0;
   774             }
   776             time -= movetime->tv_sec;
   777             micros += movetime->tv_usec;
   779             movelist = movelist->next ? movelist->next->next : NULL;
   780         }
   782         time_t sec;
   783         movelist = gamestate->lastmove;
   784         if ((movelist->move.piece & COLOR_MASK) != color) {
   785             struct movetimeval *lastmovetstamp = &(movelist->move.timestamp);
   786             struct timeval currenttstamp;
   787             gettimeofday(&currenttstamp, NULL);
   788             micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec;
   789             sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
   790             if (sec >= time) {
   791                 return 0;
   792             }
   794             time -= sec;
   795         }
   797         sec = micros / 1e6L;
   799         if (sec >= time) {
   800             return 0;
   801         }
   803         time -= sec;
   805         return time;
   806     } else {
   807         return gameinfo->time;
   808     }
   809 }

mercurial