src/chess/rules.c

Wed, 11 Jun 2014 16:54:20 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 11 Jun 2014 16:54:20 +0200
changeset 49
02c509a44e98
parent 48
0cedda2544da
child 50
41017d0a72c5
permissions
-rw-r--r--

logging string representation of moves in short algebraic notation

     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  */
    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_cleanup(GameState *gamestate) {
    48     MoveList *elem;
    49     elem = gamestate->movelist;
    50     while (elem) {
    51         MoveList *cur = elem;
    52         elem = elem->next;
    53         free(cur);
    54     };
    55 }
    57 /* MUST be called IMMEDIATLY after applying a move to work correctly */
    58 static void format_move(GameState *gamestate, Move *move) {
    59     char *string = move->string;
    61     /* at least 8 characters should be available, wipe them out */
    62     memset(string, 0, 8);
    64     /* special formats for castling */
    65     if ((move->piece&PIECE_MASK) == KING &&
    66             abs(move->tofile-move->fromfile) == 2) {
    67         if (move->tofile==fileidx('c')) {
    68             memcpy(string, "O-O-O", 5);
    69         } else {
    70             memcpy(string, "O-O", 3);
    71         }
    72     }
    74     /* start by notating the piece character */
    75     string[0] = getpiecechr(move->piece);
    76     int idx = string[0] ? 1 : 0;
    78     /* find out how many source information we do need */
    79     uint8_t piece = move->piece & PIECE_MASK;
    80     if (piece == PAWN) {
    81         if (move->capture) {
    82             string[idx++] = filechr(move->fromfile);
    83         }
    84     } else if (piece != KING) {
    85         Move threats[16];
    86         uint8_t threatcount;
    87         get_real_threats(gamestate, move->torow, move->tofile,
    88             move->piece&COLOR_MASK, threats, &threatcount);
    89         if (threatcount > 1) {
    90             int ambrows = 0, ambfiles = 0;
    91             for (uint8_t i = 0 ; i < threatcount ; i++) {
    92                 if (threats[i].fromrow == move->fromrow) {
    93                     ambrows++;
    94                 }
    95                 if (threats[i].fromfile == move->fromfile) {
    96                     ambfiles++;
    97                 }
    98             }
    99             /* ambiguous row, name file */
   100             if (ambrows > 1) {
   101                 string[idx++] = filechr(move->fromfile);
   102             }
   103             /* ambiguous file, name row */
   104             if (ambfiles > 1) {
   105                 string[idx++] = filechr(move->fromrow);
   106             }
   107         }
   108     }
   110     /* capturing? */
   111     if (move->capture) {
   112         string[idx++] = 'x';
   113     }
   115     /* destination */
   116     string[idx++] = filechr(move->tofile);
   117     string[idx++] = rowchr(move->torow);
   119     /* promotion? */
   120     if (move->promotion) {
   121         string[idx++] = '=';
   122         string[idx++] = getpiecechr(move->promotion);
   123     }
   125     /* check? */
   126     if (move->check) {
   127         /* works only, if this function is called when applying the move */
   128         string[idx++] = gamestate->checkmate?'#':'+';
   129     }
   130 }
   132 static void addmove(GameState* gamestate, Move *move) {
   133     MoveList *elem = malloc(sizeof(MoveList));
   134     elem->next = NULL;
   135     elem->move = *move;
   137     struct timeval curtimestamp;
   138     gettimeofday(&curtimestamp, NULL);
   139     elem->move.timestamp.tv_sec = curtimestamp.tv_sec;
   140     elem->move.timestamp.tv_usec = curtimestamp.tv_usec;
   142     if (gamestate->lastmove) {
   143         struct movetimeval *lasttstamp = &(gamestate->lastmove->move.timestamp);
   144         uint64_t sec = curtimestamp.tv_sec - lasttstamp->tv_sec;
   145         suseconds_t micros;
   146         if (curtimestamp.tv_usec < lasttstamp->tv_usec) {
   147             micros = 1e6L-(lasttstamp->tv_usec - curtimestamp.tv_usec);
   148             sec--;
   149         } else {
   150             micros = curtimestamp.tv_usec - lasttstamp->tv_usec;
   151         }
   153         elem->move.movetime.tv_sec = sec;
   154         elem->move.movetime.tv_usec = micros;
   156         gamestate->lastmove->next = elem;
   157         gamestate->lastmove = elem;
   158     } else {
   159         elem->move.movetime.tv_usec = 0;
   160         elem->move.movetime.tv_sec = 0;
   161         gamestate->movelist = gamestate->lastmove = elem;
   162     }
   163 }
   165 char getpiecechr(uint8_t piece) {
   166     switch (piece & PIECE_MASK) {
   167     case ROOK: return 'R';
   168     case KNIGHT: return 'N';
   169     case BISHOP: return 'B';
   170     case QUEEN: return 'Q';
   171     case KING: return 'K';
   172     default: return '\0';
   173     }
   174 }
   176 uint8_t getpiece(char c) {
   177     switch (c) {
   178         case 'R': return ROOK;
   179         case 'N': return KNIGHT;
   180         case 'B': return BISHOP;
   181         case 'Q': return QUEEN;
   182         case 'K': return KING;
   183         default: return 0;
   184     }
   185 }
   187 static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) {
   188     uint8_t piece = move->piece & PIECE_MASK;
   189     uint8_t color = move->piece & COLOR_MASK;
   191     /* en passant capture */
   192     if (move->capture && piece == PAWN &&
   193         mdst(gamestate->board, move) == 0) {
   194         gamestate->board[move->fromrow][move->tofile] = 0;
   195     }
   197     /* remove old en passant threats */
   198     for (uint8_t file = 0 ; file < 8 ; file++) {
   199         gamestate->board[3][file] &= ~ENPASSANT_THREAT;
   200         gamestate->board[4][file] &= ~ENPASSANT_THREAT;
   201     }
   203     /* add new en passant threat */
   204     if (piece == PAWN && (
   205         (move->fromrow == 1 && move->torow == 3) ||
   206         (move->fromrow == 6 && move->torow == 4))) {
   207         move->piece |= ENPASSANT_THREAT;
   208     }
   210     /* move (and maybe capture or promote) */
   211     msrc(gamestate->board, move) = 0;
   212     if (move->promotion) {
   213         mdst(gamestate->board, move) = move->promotion;
   214     } else {
   215         mdst(gamestate->board, move) = move->piece;
   216     }
   218     /* castling */
   219     if (piece == KING && move->fromfile == fileidx('e')) {
   221         if (move->tofile == fileidx('g')) {
   222             gamestate->board[move->torow][fileidx('h')] = 0;
   223             gamestate->board[move->torow][fileidx('f')] = color|ROOK;
   224         } else if (move->tofile == fileidx('c')) {
   225             gamestate->board[move->torow][fileidx('a')] = 0;
   226             gamestate->board[move->torow][fileidx('d')] = color|ROOK;
   227         }
   228     }
   230     if (!simulate) {
   231         if (!move->string[0]) {
   232             format_move(gamestate, move);
   233         }
   234     }
   235     /* add move, even in simulation (checkmate test needs it) */
   236     addmove(gamestate, move);
   237 }
   239 void apply_move(GameState *gamestate, Move *move) {
   240     apply_move_impl(gamestate, move, 0);
   241 }
   243 static int validate_move_rules(GameState *gamestate, Move *move) {
   244     /* validate indices (don't trust opponent) */
   245     if (!chkidx(move)) {
   246         return INVALID_POSITION;
   247     }
   249     /* must move */
   250     if (move->fromfile == move->tofile && move->fromrow == move->torow) {
   251         return INVALID_MOVE_SYNTAX;
   252     }
   254     /* does piece exist */
   255     if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
   256            != (move->piece&(PIECE_MASK|COLOR_MASK))) {
   257         return INVALID_POSITION;
   258     }
   260     /* can't capture own pieces */
   261     if ((mdst(gamestate->board, move) & COLOR_MASK)
   262             == (move->piece & COLOR_MASK)) {
   263         return RULES_VIOLATED;
   264     }
   266     /* must capture, if and only if destination is occupied */
   267     if ((mdst(gamestate->board, move) == 0 && move->capture) ||
   268             (mdst(gamestate->board, move) != 0 && !move->capture)) {
   269         return INVALID_MOVE_SYNTAX;
   270     }
   272     /* validate individual rules */
   273     _Bool chkrules;
   274     switch (move->piece & PIECE_MASK) {
   275     case PAWN:
   276         chkrules = pawn_chkrules(gamestate, move) &&
   277             !pawn_isblocked(gamestate, move);
   278         break;
   279     case ROOK:
   280         chkrules = rook_chkrules(move) &&
   281             !rook_isblocked(gamestate, move);
   282         break;
   283     case KNIGHT:
   284         chkrules = knight_chkrules(move); /* knight is never blocked */
   285         break;
   286     case BISHOP:
   287         chkrules = bishop_chkrules(move) &&
   288             !bishop_isblocked(gamestate, move);
   289         break;
   290     case QUEEN:
   291         chkrules = queen_chkrules(move) &&
   292             !queen_isblocked(gamestate, move);
   293         break;
   294     case KING:
   295         chkrules = king_chkrules(gamestate, move) &&
   296             !king_isblocked(gamestate, move);
   297         break;
   298     default:
   299         return INVALID_MOVE_SYNTAX;
   300     }
   302     return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
   303 }
   305 int validate_move(GameState *gamestate, Move *move) {
   307     int result = validate_move_rules(gamestate, move);
   309     /* cancel processing to save resources */
   310     if (result != VALID_MOVE_SEMANTICS) {
   311         return result;
   312     }
   314     /* find kings for check validation */
   315     uint8_t piececolor = (move->piece & COLOR_MASK);
   317     uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
   318     for (uint8_t row = 0 ; row < 8 ; row++) {
   319         for (uint8_t file = 0 ; file < 8 ; file++) {
   320             if (gamestate->board[row][file] ==
   321                     (piececolor == WHITE?WKING:BKING)) {
   322                 mykingfile = file;
   323                 mykingrow = row;
   324             } else if (gamestate->board[row][file] ==
   325                     (piececolor == WHITE?BKING:WKING)) {
   326                 opkingfile = file;
   327                 opkingrow = row;
   328             }
   329         }
   330     }
   332     /* simulate move for check validation */
   333     GameState simulation = gamestate_copy_sim(gamestate);
   334     Move simmove = *move;
   335     apply_move_impl(&simulation, &simmove, 1);
   337     /* don't move into or stay in check position */
   338     if (is_covered(&simulation, mykingrow, mykingfile,
   339         opponent_color(piececolor))) {
   341         gamestate_cleanup(&simulation);
   342         if ((move->piece & PIECE_MASK) == KING) {
   343             return KING_MOVES_INTO_CHECK;
   344         } else {
   345             /* last move is always not null in this case */
   346             return gamestate->lastmove->move.check ?
   347                 KING_IN_CHECK : PIECE_PINNED;
   348         }
   349     }
   351     /* correct check and checkmate flags (move is still valid) */
   352     Move threats[16];
   353     uint8_t threatcount;
   354     move->check = get_threats(&simulation, opkingrow, opkingfile,
   355         piececolor, threats, &threatcount);
   357     if (move->check) {
   358         /* determine possible escape fields */
   359         _Bool canescape = 0;
   360         for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
   361             for (int df = -1 ; df <= 1 && !canescape ; df++) {
   362                 if (!(dr == 0 && df == 0)  &&
   363                         isidx(opkingrow + dr) && isidx(opkingfile + df)) {
   365                     /* escape field neither blocked nor covered */
   366                     if ((simulation.board[opkingrow + dr][opkingfile + df]
   367                             & COLOR_MASK) != opponent_color(piececolor)) {
   368                         canescape |= !is_covered(&simulation,
   369                             opkingrow + dr, opkingfile + df, piececolor);
   370                     }
   371                 }
   372             }
   373         }
   374         /* can't escape, can he capture? */
   375         if (!canescape && threatcount == 1) {
   376             canescape = is_attacked(&simulation, threats[0].fromrow,
   377                 threats[0].fromfile, opponent_color(piececolor));
   378         }
   380         /* can't capture, can he block? */
   381         if (!canescape && threatcount == 1) {
   382             Move *threat = &(threats[0]);
   383             uint8_t threatpiece = threat->piece & PIECE_MASK;
   385             /* knight, pawns and the king cannot be blocked */
   386             if (threatpiece == BISHOP || threatpiece == ROOK
   387                 || threatpiece == QUEEN) {
   388                 if (threat->fromrow == threat->torow) {
   389                     /* rook aspect (on row) */
   390                     int d = threat->tofile > threat->fromfile ? 1 : -1;
   391                     uint8_t file = threat->fromfile;
   392                     while (!canescape && file != threat->tofile - d) {
   393                         file += d;
   394                         canescape |= is_protected(&simulation,
   395                             threat->torow, file, opponent_color(piececolor));
   396                     }
   397                 } else if (threat->fromfile == threat->tofile) {
   398                     /* rook aspect (on file) */
   399                     int d = threat->torow > threat->fromrow ? 1 : -1;
   400                     uint8_t row = threat->fromrow;
   401                     while (!canescape && row != threat->torow - d) {
   402                         row += d;
   403                         canescape |= is_protected(&simulation,
   404                             row, threat->tofile, opponent_color(piececolor));
   405                     }
   406                 } else {
   407                     /* bishop aspect */
   408                     int dr = move->torow > move->fromrow ? 1 : -1;
   409                     int df = move->tofile > move->fromfile ? 1 : -1;
   411                     uint8_t row = move->fromrow;
   412                     uint8_t file = move->fromfile;
   413                     while (!canescape && file != move->tofile - df
   414                         && row != move->torow - dr) {
   415                         row += dr;
   416                         file += df;
   417                         canescape |= is_protected(&simulation, row, file,
   418                             opponent_color(piececolor));
   419                     }
   420                 }
   421             }
   422         }
   424         if (!canescape) {
   425             gamestate->checkmate = 1;
   426         }
   427     }
   429     gamestate_cleanup(&simulation);
   431     return VALID_MOVE_SEMANTICS;
   432 }
   434 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
   435         uint8_t color, Move *threats, uint8_t *threatcount) {
   436     Move candidates[32];
   437     int candidatecount = 0;
   438     for (uint8_t r = 0 ; r < 8 ; r++) {
   439         for (uint8_t f = 0 ; f < 8 ; f++) {
   440             if ((gamestate->board[r][f] & COLOR_MASK) == color) {
   441                 // non-capturing move
   442                 memset(&(candidates[candidatecount]), 0, sizeof(Move));
   443                 candidates[candidatecount].piece = gamestate->board[r][f];
   444                 candidates[candidatecount].fromrow = r;
   445                 candidates[candidatecount].fromfile = f;
   446                 candidates[candidatecount].torow = row;
   447                 candidates[candidatecount].tofile = file;
   448                 candidatecount++;
   450                 // capturing move
   451                 memcpy(&(candidates[candidatecount]),
   452                     &(candidates[candidatecount-1]), sizeof(Move));
   453                 candidates[candidatecount].capture = 1;
   454                 candidatecount++;
   455             }
   456         }
   457     }
   459     if (threatcount) {
   460         *threatcount = 0;
   461     }
   464     _Bool result = 0;
   466     for (int i = 0 ; i < candidatecount ; i++) {
   467         if (validate_move_rules(gamestate, &(candidates[i]))
   468                 == VALID_MOVE_SEMANTICS) {
   469             result = 1;
   470             if (threats && threatcount) {
   471                 threats[(*threatcount)++] = candidates[i];
   472             }
   473         }
   474     }
   476     return result;
   477 }
   479 _Bool is_pinned(GameState *gamestate, Move *move) {
   480     uint8_t color = move->piece & COLOR_MASK;
   482     uint8_t kingfile = 0, kingrow = 0;
   483     for (uint8_t row = 0 ; row < 8 ; row++) {
   484         for (uint8_t file = 0 ; file < 8 ; file++) {
   485             if (gamestate->board[row][file] == (color|KING)) {
   486                 kingfile = file;
   487                 kingrow = row;
   488             }
   489         }
   490     }
   492     GameState simulation = gamestate_copy_sim(gamestate);
   493     Move simmove = *move;
   494     apply_move(&simulation, &simmove);
   495     _Bool covered = is_covered(&simulation,
   496         kingrow, kingfile, opponent_color(color));
   497     gamestate_cleanup(&simulation);
   499     return covered;
   500 }
   502 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
   503         uint8_t color, Move *threats, uint8_t *threatcount) {
   505     if (threatcount) {
   506         *threatcount = 0;
   507     }
   509     Move candidates[16];
   510     uint8_t candidatecount;
   511     if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
   513         _Bool result = 0;
   514         uint8_t kingfile = 0, kingrow = 0;
   515         for (uint8_t row = 0 ; row < 8 ; row++) {
   516             for (uint8_t file = 0 ; file < 8 ; file++) {
   517                 if (gamestate->board[row][file] == (color|KING)) {
   518                     kingfile = file;
   519                     kingrow = row;
   520                 }
   521             }
   522         }
   524         for (uint8_t i = 0 ; i < candidatecount ; i++) {
   525             GameState simulation = gamestate_copy_sim(gamestate);
   526             Move simmove = candidates[i];
   527             apply_move(&simulation, &simmove);
   528             if (!is_covered(&simulation, kingrow, kingfile,
   529                     opponent_color(color))) {
   530                 result = 1;
   531                 if (threats && threatcount) {
   532                     threats[(*threatcount)++] = candidates[i];
   533                 }
   534             }
   535         }
   537         return result;
   538     } else {
   539         return 0;
   540     }
   541 }
   543 static int getlocation(GameState *gamestate, Move *move) {   
   545     uint8_t color = move->piece & COLOR_MASK;
   546     _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
   548     Move threats[16], *threat = NULL;
   549     uint8_t threatcount;
   551     if (get_threats(gamestate, move->torow, move->tofile, color,
   552             threats, &threatcount)) {
   554         int reason = INVALID_POSITION;
   556         // find threats for the specified position
   557         for (uint8_t i = 0 ; i < threatcount ; i++) {
   558             if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
   559                     == move->piece &&
   560                     (move->fromrow == POS_UNSPECIFIED ||
   561                     move->fromrow == threats[i].fromrow) &&
   562                     (move->fromfile == POS_UNSPECIFIED ||
   563                     move->fromfile == threats[i].fromfile)) {
   565                 if (threat) {
   566                     return AMBIGUOUS_MOVE;
   567                 } else {
   568                     // found threat is no real threat
   569                     if (is_pinned(gamestate, &(threats[i]))) {
   570                         reason = incheck?KING_IN_CHECK:PIECE_PINNED;
   571                     } else {
   572                         threat = &(threats[i]);
   573                     }
   574                 }
   575             }
   576         }
   578         // can't threaten specified position
   579         if (!threat) {
   580             return reason;
   581         }
   583         memcpy(move, threat, sizeof(Move));
   584         return VALID_MOVE_SYNTAX;
   585     } else {
   586         return INVALID_POSITION;
   587     }
   588 }
   590 int eval_move(GameState *gamestate, char *mstr, Move *move) {
   591     memset(move, 0, sizeof(Move));
   592     move->fromfile = POS_UNSPECIFIED;
   593     move->fromrow = POS_UNSPECIFIED;
   595     size_t len = strlen(mstr);
   596     if (len < 1 || len > 6) {
   597         return INVALID_MOVE_SYNTAX;
   598     }
   600     /* evaluate check/checkmate flags */
   601     if (mstr[len-1] == '+') {
   602         len--; mstr[len] = '\0';
   603         move->check = 1;
   604     } else if (mstr[len-1] == '#') {
   605         len--; mstr[len] = '\0';
   606         /* ignore - validation should set game state */
   607     }
   609     /* evaluate promotion */
   610     if (len > 3 && mstr[len-2] == '=') {
   611         move->promotion = getpiece(mstr[len-1]);
   612         if (!move->promotion) {
   613             return INVALID_MOVE_SYNTAX;
   614         } else {
   615             move->promotion |= gamestate->mycolor;
   616             len -= 2;
   617             mstr[len] = 0;
   618         }
   619     }
   621     if (len == 2) {
   622         /* pawn move (e.g. "e4") */
   623         move->piece = PAWN;
   624         move->tofile = fileidx(mstr[0]);
   625         move->torow = rowidx(mstr[1]);
   626     } else if (len == 3) {
   627         if (strcmp(mstr, "O-O") == 0) {
   628             /* king side castling */
   629             move->piece = KING;
   630             move->fromfile = fileidx('e');
   631             move->tofile = fileidx('g');
   632             move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7;
   633         } else {
   634             /* move (e.g. "Nf3") */
   635             move->piece = getpiece(mstr[0]);
   636             move->tofile = fileidx(mstr[1]);
   637             move->torow = rowidx(mstr[2]);
   638         }
   639     } else if (len == 4) {
   640         move->piece = getpiece(mstr[0]);
   641         if (!move->piece) {
   642             move->piece = PAWN;
   643             move->fromfile = fileidx(mstr[0]);
   644         }
   645         if (mstr[1] == 'x') {
   646             /* capture (e.g. "Nxf3", "dxe5") */
   647             move->capture = 1;
   648         } else {
   649             /* move (e.g. "Ndf3", "N2c3", "e2e4") */
   650             if (isfile(mstr[1])) {
   651                 move->fromfile = fileidx(mstr[1]);
   652                 if (move->piece == PAWN) {
   653                     move->piece = 0;
   654                 }
   655             } else {
   656                 move->fromrow = rowidx(mstr[1]);
   657             }
   658         }
   659         move->tofile = fileidx(mstr[2]);
   660         move->torow = rowidx(mstr[3]);
   661     } else if (len == 5) {
   662         if (strcmp(mstr, "O-O-O") == 0) {
   663             /* queen side castling "O-O-O" */
   664             move->piece = KING;
   665             move->fromfile = fileidx('e');
   666             move->tofile = fileidx('c');
   667             move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7;
   668         } else {
   669             move->piece = getpiece(mstr[0]);
   670             if (mstr[2] == 'x') {
   671                 move->capture = 1;
   672                 if (move->piece) {
   673                     /* capture (e.g. "Ndxf3") */
   674                     move->fromfile = fileidx(mstr[1]);
   675                 } else {
   676                     /* long notation capture (e.g. "e5xf6") */
   677                     move->piece = PAWN;
   678                     move->fromfile = fileidx(mstr[0]);
   679                     move->fromrow = rowidx(mstr[1]);
   680                 }
   681             } else {
   682                 /* long notation move (e.g. "Nc5a4") */
   683                 move->fromfile = fileidx(mstr[1]);
   684                 move->fromrow = rowidx(mstr[2]);
   685             }
   686             move->tofile = fileidx(mstr[3]);
   687             move->torow = rowidx(mstr[4]);
   688         }
   689     } else if (len == 6) {
   690         /* long notation capture (e.g. "Nc5xf3") */
   691         if (mstr[3] == 'x') {
   692             move->capture = 1;
   693             move->piece = getpiece(mstr[0]);
   694             move->fromfile = fileidx(mstr[1]);
   695             move->fromrow = rowidx(mstr[2]);
   696             move->tofile = fileidx(mstr[4]);
   697             move->torow = rowidx(mstr[5]);
   698         }
   699     }
   702     if (move->piece) {
   703         if (move->piece == PAWN
   704             && move->torow == (gamestate->mycolor==WHITE?7:0)
   705             && !move->promotion) {
   706             return NEED_PROMOTION;
   707         }
   709         move->piece |= gamestate->mycolor;
   710         if (move->fromfile == POS_UNSPECIFIED
   711             || move->fromrow == POS_UNSPECIFIED) {
   712             return getlocation(gamestate, move);
   713         } else {
   714             return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
   715         }
   716     } else {
   717         return INVALID_MOVE_SYNTAX;
   718     }
   719 }
   721 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
   722         uint8_t color) {
   724     Move threats[16];
   725     uint8_t threatcount;
   726     if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
   727         for (int i = 0 ; i < threatcount ; i++) {
   728             if (threats[i].piece != (color|KING)) {
   729                 return 1;
   730             }
   731         }
   732         return 0;
   733     } else {
   734         return 0;
   735     }
   736 }
   738 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
   739         uint8_t color) {
   740     if (!gameinfo->timecontrol) {
   741         return 0;
   742     }
   744     if (gamestate->movelist) {
   745         uint16_t time = gameinfo->time;
   746         suseconds_t micros = 0;
   748         MoveList *movelist = color == WHITE ?
   749             gamestate->movelist : gamestate->movelist->next;
   751         while (movelist) {
   752             time += gameinfo->addtime;
   754             struct movetimeval *movetime = &(movelist->move.movetime);
   755             if (movetime->tv_sec >= time) {
   756                 return 0;
   757             }
   759             time -= movetime->tv_sec;
   760             micros += movetime->tv_usec;
   762             movelist = movelist->next ? movelist->next->next : NULL;
   763         }
   765         time_t sec;
   766         movelist = gamestate->lastmove;
   767         if ((movelist->move.piece & COLOR_MASK) != color) {
   768             struct movetimeval *lastmovetstamp = &(movelist->move.timestamp);
   769             struct timeval currenttstamp;
   770             gettimeofday(&currenttstamp, NULL);
   771             micros += currenttstamp.tv_usec - lastmovetstamp->tv_usec;
   772             sec = currenttstamp.tv_sec - lastmovetstamp->tv_sec;
   773             if (sec >= time) {
   774                 return 0;
   775             }
   777             time -= sec;
   778         }
   780         sec = micros / 1e6L;
   782         if (sec >= time) {
   783             return 0;
   784         }
   786         time -= sec;
   788         return time;
   789     } else {
   790         return gameinfo->time;
   791     }
   792 }

mercurial