diff -r 903b46fc4214 -r c06ab07fd29d test/v2-regression/bigtest.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/v2-regression/bigtest.html Tue Aug 23 14:13:46 2016 +0200 @@ -0,0 +1,855 @@ + + + + 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  }
+
+ + +