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