src/chess/rules.c

Mon, 16 Jun 2014 13:45:31 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 16 Jun 2014 13:45:31 +0200
changeset 50
41017d0a72c5
parent 49
02c509a44e98
child 51
84f2e380a434
permissions
-rw-r--r--

added pgn parser and writer (without comment support yet) + minor refactorings

     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_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     /* find kings for check validation */
   331     uint8_t piececolor = (move->piece & COLOR_MASK);
   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     }
   348     /* simulate move for check validation */
   349     GameState simulation = gamestate_copy_sim(gamestate);
   350     Move simmove = *move;
   351     apply_move_impl(&simulation, &simmove, 1);
   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 = move->torow > move->fromrow ? 1 : -1;
   425                     int df = move->tofile > move->fromfile ? 1 : -1;
   427                     uint8_t row = move->fromrow;
   428                     uint8_t file = move->fromfile;
   429                     while (!canescape && file != move->tofile - df
   430                         && row != move->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     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     }
   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);
   515     return covered;
   516 }
   518 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
   519         uint8_t color, Move *threats, uint8_t *threatcount) {
   521     if (threatcount) {
   522         *threatcount = 0;
   523     }
   525     Move candidates[16];
   526     uint8_t candidatecount;
   527     if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
   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         }
   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         }
   553         return result;
   554     } else {
   555         return 0;
   556     }
   557 }
   559 static int getlocation(GameState *gamestate, Move *move) {   
   561     uint8_t color = move->piece & COLOR_MASK;
   562     _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
   564     Move threats[16], *threat = NULL;
   565     uint8_t threatcount;
   567     if (get_threats(gamestate, move->torow, move->tofile, color,
   568             threats, &threatcount)) {
   570         int reason = INVALID_POSITION;
   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)) {
   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         }
   594         // can't threaten specified position
   595         if (!threat) {
   596             return reason;
   597         }
   599         memcpy(move, threat, sizeof(Move));
   600         return VALID_MOVE_SYNTAX;
   601     } else {
   602         return INVALID_POSITION;
   603     }
   604 }
   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;
   611     size_t len = strlen(mstr);
   612     if (len < 1 || len > 6) {
   613         return INVALID_MOVE_SYNTAX;
   614     }
   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     }
   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     }
   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     }
   718     if (move->piece) {
   719         if (move->piece == PAWN
   720             && move->torow == (color==WHITE?7:0)
   721             && !move->promotion) {
   722             return NEED_PROMOTION;
   723         }
   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 }
   737 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
   738         uint8_t color) {
   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 }
   754 uint16_t remaining_movetime(GameInfo *gameinfo, GameState *gamestate,
   755         uint8_t color) {
   756     if (!gameinfo->timecontrol) {
   757         return 0;
   758     }
   760     if (gamestate->movelist) {
   761         uint16_t time = gameinfo->time;
   762         suseconds_t micros = 0;
   764         MoveList *movelist = color == WHITE ?
   765             gamestate->movelist : gamestate->movelist->next;
   767         while (movelist) {
   768             time += gameinfo->addtime;
   770             struct movetimeval *movetime = &(movelist->move.movetime);
   771             if (movetime->tv_sec >= time) {
   772                 return 0;
   773             }
   775             time -= movetime->tv_sec;
   776             micros += movetime->tv_usec;
   778             movelist = movelist->next ? movelist->next->next : NULL;
   779         }
   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(&currenttstamp, 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             }
   793             time -= sec;
   794         }
   796         sec = micros / 1e6L;
   798         if (sec >= time) {
   799             return 0;
   800         }
   802         time -= sec;
   804         return time;
   805     } else {
   806         return gameinfo->time;
   807     }
   808 }

mercurial