added return code to move validation (for more informative messages) + fixed a bug where simulations added movelist items to the original gamestate

Wed, 11 Jun 2014 15:38:01 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 11 Jun 2014 15:38:01 +0200
changeset 48
0cedda2544da
parent 47
d726e4b46c33
child 49
02c509a44e98

added return code to move validation (for more informative messages) + fixed a bug where simulations added movelist items to the original gamestate

src/chess/rules.c file | annotate | diff | comparison | revisions
src/chess/rules.h file | annotate | diff | comparison | revisions
src/game.c file | annotate | diff | comparison | revisions
src/network.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
     1.1 --- a/src/chess/rules.c	Wed May 28 15:47:57 2014 +0200
     1.2 +++ b/src/chess/rules.c	Wed Jun 11 15:38:01 2014 +0200
     1.3 @@ -33,6 +33,17 @@
     1.4  #include <stdlib.h>
     1.5  #include <sys/time.h>
     1.6  
     1.7 +static GameState gamestate_copy_sim(GameState *gamestate) {
     1.8 +    GameState simulation = *gamestate;
     1.9 +    if (simulation.lastmove) {
    1.10 +        MoveList *lastmovecopy = malloc(sizeof(MoveList));
    1.11 +        *lastmovecopy = *(simulation.lastmove);
    1.12 +        simulation.movelist = simulation.lastmove = lastmovecopy;
    1.13 +    }
    1.14 +        
    1.15 +    return simulation;
    1.16 +}
    1.17 +
    1.18  void gamestate_cleanup(GameState *gamestate) {
    1.19      MoveList *elem;
    1.20      elem = gamestate->movelist;
    1.21 @@ -145,67 +156,75 @@
    1.22      addmove(gamestate, move);
    1.23  }
    1.24  
    1.25 -static _Bool validate_move_rules(GameState *gamestate, Move *move) {
    1.26 +static int validate_move_rules(GameState *gamestate, Move *move) {
    1.27      /* validate indices (don't trust opponent) */
    1.28      if (!chkidx(move)) {
    1.29 -        return 0;
    1.30 +        return INVALID_POSITION;
    1.31      }
    1.32      
    1.33      /* must move */
    1.34      if (move->fromfile == move->tofile && move->fromrow == move->torow) {
    1.35 -        return 0;
    1.36 +        return INVALID_MOVE_SYNTAX;
    1.37      }
    1.38      
    1.39      /* does piece exist */
    1.40      if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
    1.41             != (move->piece&(PIECE_MASK|COLOR_MASK))) {
    1.42 -        return 0;
    1.43 +        return INVALID_POSITION;
    1.44      }
    1.45      
    1.46      /* can't capture own pieces */
    1.47      if ((mdst(gamestate->board, move) & COLOR_MASK)
    1.48              == (move->piece & COLOR_MASK)) {
    1.49 -        return 0;
    1.50 +        return RULES_VIOLATED;
    1.51      }
    1.52      
    1.53      /* must capture, if and only if destination is occupied */
    1.54      if ((mdst(gamestate->board, move) == 0 && move->capture) ||
    1.55              (mdst(gamestate->board, move) != 0 && !move->capture)) {
    1.56 -        return 0;
    1.57 +        return INVALID_MOVE_SYNTAX;
    1.58      }
    1.59      
    1.60      /* validate individual rules */
    1.61 +    _Bool chkrules;
    1.62      switch (move->piece & PIECE_MASK) {
    1.63      case PAWN:
    1.64 -        return pawn_chkrules(gamestate, move) &&
    1.65 +        chkrules = pawn_chkrules(gamestate, move) &&
    1.66              !pawn_isblocked(gamestate, move);
    1.67 +        break;
    1.68      case ROOK:
    1.69 -        return rook_chkrules(move) &&
    1.70 +        chkrules = rook_chkrules(move) &&
    1.71              !rook_isblocked(gamestate, move);
    1.72 +        break;
    1.73      case KNIGHT:
    1.74 -        return knight_chkrules(move); /* knight is never blocked */
    1.75 +        chkrules = knight_chkrules(move); /* knight is never blocked */
    1.76 +        break;
    1.77      case BISHOP:
    1.78 -        return bishop_chkrules(move) &&
    1.79 +        chkrules = bishop_chkrules(move) &&
    1.80              !bishop_isblocked(gamestate, move);
    1.81 +        break;
    1.82      case QUEEN:
    1.83 -        return queen_chkrules(move) &&
    1.84 +        chkrules = queen_chkrules(move) &&
    1.85              !queen_isblocked(gamestate, move);
    1.86 +        break;
    1.87      case KING:
    1.88 -        return king_chkrules(gamestate, move) &&
    1.89 +        chkrules = king_chkrules(gamestate, move) &&
    1.90              !king_isblocked(gamestate, move);
    1.91 +        break;
    1.92      default:
    1.93 -        return 0;
    1.94 +        return INVALID_MOVE_SYNTAX;
    1.95      }
    1.96 +    
    1.97 +    return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
    1.98  }
    1.99  
   1.100 -_Bool validate_move(GameState *gamestate, Move *move) {
   1.101 -    // TODO: provide more details via a return code
   1.102 +int validate_move(GameState *gamestate, Move *move) {
   1.103      
   1.104 -    _Bool result = validate_move_rules(gamestate, move);
   1.105 +    int result = validate_move_rules(gamestate, move);
   1.106      
   1.107      /* cancel processing to save resources */
   1.108 -    if (!result) {
   1.109 -        return 0;
   1.110 +    if (result != VALID_MOVE_SEMANTICS) {
   1.111 +        return result;
   1.112      }
   1.113      
   1.114      /* find kings for check validation */
   1.115 @@ -226,15 +245,23 @@
   1.116          }
   1.117      }
   1.118      
   1.119 -    /* simulation move for check validation */
   1.120 -    GameState simulation = *gamestate;
   1.121 +    /* simulate move for check validation */
   1.122 +    GameState simulation = gamestate_copy_sim(gamestate);
   1.123      Move simmove = *move;
   1.124      apply_move(&simulation, &simmove);
   1.125      
   1.126      /* don't move into or stay in check position */
   1.127      if (is_covered(&simulation, mykingrow, mykingfile,
   1.128          opponent_color(piececolor))) {
   1.129 -        return 0;
   1.130 +        
   1.131 +        gamestate_cleanup(&simulation);
   1.132 +        if ((move->piece & PIECE_MASK) == KING) {
   1.133 +            return KING_MOVES_INTO_CHECK;
   1.134 +        } else {
   1.135 +            /* last move is always not null in this case */
   1.136 +            return gamestate->lastmove->move.check ?
   1.137 +                KING_IN_CHECK : PIECE_PINNED;
   1.138 +        }
   1.139      }
   1.140      
   1.141      /* correct check and checkmate flags (move is still valid) */
   1.142 @@ -315,7 +342,9 @@
   1.143          }
   1.144      }
   1.145      
   1.146 -    return 1;
   1.147 +    gamestate_cleanup(&simulation);
   1.148 +    
   1.149 +    return VALID_MOVE_SEMANTICS;
   1.150  }
   1.151  
   1.152  _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
   1.153 @@ -351,7 +380,8 @@
   1.154      _Bool result = 0;
   1.155      
   1.156      for (int i = 0 ; i < candidatecount ; i++) {
   1.157 -        if (validate_move_rules(gamestate, &(candidates[i]))) {
   1.158 +        if (validate_move_rules(gamestate, &(candidates[i]))
   1.159 +                == VALID_MOVE_SEMANTICS) {
   1.160              result = 1;
   1.161              if (threats && threatcount) {
   1.162                  threats[(*threatcount)++] = candidates[i];
   1.163 @@ -375,10 +405,14 @@
   1.164          }
   1.165      }
   1.166  
   1.167 -    GameState simulation = *gamestate;
   1.168 +    GameState simulation = gamestate_copy_sim(gamestate);
   1.169      Move simmove = *move;
   1.170      apply_move(&simulation, &simmove);
   1.171 -    return is_covered(&simulation, kingrow, kingfile, opponent_color(color));
   1.172 +    _Bool covered = is_covered(&simulation,
   1.173 +        kingrow, kingfile, opponent_color(color));
   1.174 +    gamestate_cleanup(&simulation);
   1.175 +    
   1.176 +    return covered;
   1.177  }
   1.178  
   1.179  _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
   1.180 @@ -404,7 +438,7 @@
   1.181          }
   1.182  
   1.183          for (uint8_t i = 0 ; i < candidatecount ; i++) {
   1.184 -            GameState simulation = *gamestate;
   1.185 +            GameState simulation = gamestate_copy_sim(gamestate);
   1.186              Move simmove = candidates[i];
   1.187              apply_move(&simulation, &simmove);
   1.188              if (!is_covered(&simulation, kingrow, kingfile,
   1.189 @@ -414,6 +448,7 @@
   1.190                      threats[(*threatcount)++] = candidates[i];
   1.191                  }
   1.192              }
   1.193 +            gamestate_cleanup(&simulation);
   1.194          }
   1.195          
   1.196          return result;
   1.197 @@ -421,7 +456,7 @@
   1.198          return 0;
   1.199      }
   1.200  }
   1.201 -#include <ncurses.h>
   1.202 +
   1.203  static int getlocation(GameState *gamestate, Move *move) {   
   1.204  
   1.205      uint8_t color = move->piece & COLOR_MASK;
   1.206 @@ -473,6 +508,9 @@
   1.207      move->fromrow = POS_UNSPECIFIED;
   1.208  
   1.209      size_t len = strlen(mstr);
   1.210 +    if (len < 1 || len > 6) {
   1.211 +        return INVALID_MOVE_SYNTAX;
   1.212 +    }
   1.213      
   1.214      /* evaluate check/checkmate flags */
   1.215      if (mstr[len-1] == '+') {
   1.216 @@ -513,7 +551,6 @@
   1.217              move->tofile = fileidx(mstr[1]);
   1.218              move->torow = rowidx(mstr[2]);
   1.219          }
   1.220 -        
   1.221      } else if (len == 4) {
   1.222          move->piece = getpiece(mstr[0]);
   1.223          if (!move->piece) {
     2.1 --- a/src/chess/rules.h	Wed May 28 15:47:57 2014 +0200
     2.2 +++ b/src/chess/rules.h	Wed Jun 11 15:38:01 2014 +0200
     2.3 @@ -33,13 +33,16 @@
     2.4  #include <stdint.h>
     2.5  #include <sys/time.h>
     2.6  
     2.7 -#define VALID_MOVE_SYNTAX   0
     2.8 -#define INVALID_MOVE_SYNTAX 1
     2.9 -#define INVALID_POSITION    2
    2.10 -#define AMBIGUOUS_MOVE      3
    2.11 -#define NEED_PROMOTION      4
    2.12 -#define PIECE_PINNED        5
    2.13 -#define KING_IN_CHECK       6
    2.14 +#define VALID_MOVE_SYNTAX      0
    2.15 +#define VALID_MOVE_SEMANTICS   0 /* use same code for a success */
    2.16 +#define INVALID_MOVE_SYNTAX    1
    2.17 +#define INVALID_POSITION       2
    2.18 +#define AMBIGUOUS_MOVE         3
    2.19 +#define NEED_PROMOTION         4
    2.20 +#define PIECE_PINNED           5
    2.21 +#define KING_IN_CHECK          6
    2.22 +#define KING_MOVES_INTO_CHECK  7
    2.23 +#define RULES_VIOLATED        10
    2.24  
    2.25  
    2.26  #define PIECE_MASK       0x0F
    2.27 @@ -268,7 +271,7 @@
    2.28   * @param gamestate the current game state
    2.29   * @param mstr the input string to parse
    2.30   * @param move a pointer to object where the move data shall be stored
    2.31 - * @return status code (see rules/rules.h for the list of codes)
    2.32 + * @return status code (see macros in this file for the list of codes)
    2.33   */
    2.34  int eval_move(GameState *gamestate, char *mstr, Move *move);
    2.35  
    2.36 @@ -276,9 +279,9 @@
    2.37   * Validates move by applying chess rules.
    2.38   * @param gamestate the current game state
    2.39   * @param move the move to validate
    2.40 - * @return TRUE, if the move complies to chess rules, FALSE otherwise
    2.41 + * @return status code (see macros in this file for the list of codes)
    2.42   */
    2.43 -_Bool validate_move(GameState *gamestate, Move *move);
    2.44 +int validate_move(GameState *gamestate, Move *move);
    2.45  
    2.46  /**
    2.47   * Applies a move and deletes captured pieces.
     3.1 --- a/src/game.c	Wed May 28 15:47:57 2014 +0200
     3.2 +++ b/src/game.c	Wed Jun 11 15:38:01 2014 +0200
     3.3 @@ -162,7 +162,7 @@
     3.4          printw("Ambiguous move - please specify the piece to move.");
     3.5          break;
     3.6      case INVALID_POSITION:
     3.7 -        printw("Cannot find the piece that shall be moved.");
     3.8 +        printw("No piece can be moved this way.");
     3.9          break;
    3.10      case NEED_PROMOTION:
    3.11          printw("You need to promote the pawn (append \"=Q\" e.g.)!");
    3.12 @@ -176,6 +176,12 @@
    3.13      case INVALID_MOVE_SYNTAX:
    3.14          printw("Can't interpret move - please use algebraic notation.");
    3.15          break;
    3.16 +    case RULES_VIOLATED:
    3.17 +        printw("Move does not comply chess rules.");
    3.18 +        break;
    3.19 +    case KING_MOVES_INTO_CHECK:
    3.20 +        printw("Can't move the king into a check position.");
    3.21 +        break;
    3.22      default:
    3.23          printw("Unknown move parser error.");
    3.24      }
    3.25 @@ -218,7 +224,8 @@
    3.26                  int eval_result = eval_move(gamestate, movestr, &move);
    3.27                  switch (eval_result) {
    3.28                  case VALID_MOVE_SYNTAX:
    3.29 -                    if (validate_move(gamestate, &move)) {
    3.30 +                    eval_result = validate_move(gamestate, &move);
    3.31 +                    if (eval_result == VALID_MOVE_SEMANTICS) {
    3.32                          apply_move(gamestate, &move);
    3.33                          if (gamestate->checkmate) {
    3.34                              printw("Checkmate!");
    3.35 @@ -232,7 +239,7 @@
    3.36                              return 0;
    3.37                          }
    3.38                      } else {
    3.39 -                        printw("Invalid move.");
    3.40 +                        eval_move_failed_msg(eval_result);
    3.41                      }
    3.42                      break;
    3.43                  default:
    3.44 @@ -306,11 +313,15 @@
    3.45                  case VALID_MOVE_SYNTAX:
    3.46                      net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
    3.47                      code = net_recieve_code(opponent);
    3.48 -                    move.check = code == NETCODE_CHECK;
    3.49 +                    move.check = code == NETCODE_CHECK ||
    3.50 +                        code == NETCODE_CHECKMATE;
    3.51                      gamestate->checkmate = code == NETCODE_CHECKMATE;
    3.52                      gamestate->stalemate = code == NETCODE_STALEMATE;
    3.53                      if (code == NETCODE_DECLINE) {
    3.54 -                        printw("Invalid move.");
    3.55 +                        uint32_t reason;
    3.56 +                        net_recieve_data(opponent, &reason, sizeof(uint32_t));
    3.57 +                        reason = ntohl(reason);
    3.58 +                        eval_move_failed_msg(reason);
    3.59                      } else if (code == NETCODE_ACCEPT
    3.60                              || code == NETCODE_CHECK
    3.61                              || code == NETCODE_CHECKMATE
    3.62 @@ -397,7 +408,8 @@
    3.63                  break;
    3.64              case NETCODE_MOVE:
    3.65                  net_recieve_data(opponent, &move, sizeof(Move));
    3.66 -                if (validate_move(gamestate, &move)) {
    3.67 +                code = validate_move(gamestate, &move);
    3.68 +                if (code == VALID_MOVE_SEMANTICS) {
    3.69                      apply_move(gamestate, &move);
    3.70                      if (move.check) {
    3.71                          net_send_code(opponent, NETCODE_CHECK);
    3.72 @@ -416,7 +428,9 @@
    3.73                      }
    3.74                      return 0;
    3.75                  } else {
    3.76 -                    net_send_code(opponent, NETCODE_DECLINE);
    3.77 +                    uint32_t reason = htonl(code);
    3.78 +                    net_send_data(opponent, NETCODE_DECLINE,
    3.79 +                        &reason, sizeof(uint32_t));
    3.80                  }
    3.81                  break;
    3.82              default:
     4.1 --- a/src/network.c	Wed May 28 15:47:57 2014 +0200
     4.2 +++ b/src/network.c	Wed Jun 11 15:38:01 2014 +0200
     4.3 @@ -56,7 +56,7 @@
     4.4      }
     4.5  }
     4.6  
     4.7 -int getaddrinfo_intrnl(char *host, char *port, struct addrinfo **info) {
     4.8 +static int getaddrinfo_intrnl(char *host, char *port, struct addrinfo **info) {
     4.9      struct addrinfo hints;
    4.10      memset(&hints, 0, sizeof(hints));
    4.11      hints.ai_socktype = SOCK_STREAM;
     5.1 --- a/src/network.h	Wed May 28 15:47:57 2014 +0200
     5.2 +++ b/src/network.h	Wed Jun 11 15:38:01 2014 +0200
     5.3 @@ -49,7 +49,7 @@
     5.4  #define NETCODE_TIMEOVER 0x44
     5.5  #define NETCODE_CONNLOST 0x80
     5.6  
     5.7 -#define NETCODE_VERSION 13
     5.8 +#define NETCODE_VERSION 14
     5.9  
    5.10  typedef struct {
    5.11      int fd; /* -1, if we are the client */

mercurial