fixed checkmate and completed implementation (more testing is still advised)

Mon, 07 Apr 2014 14:08:57 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 07 Apr 2014 14:08:57 +0200
changeset 29
c6a1ad6cf749
parent 28
0c1371488d87
child 30
a285ee393860

fixed checkmate and completed implementation (more testing is still advised)

src/chess/rules.c file | annotate | diff | comparison | revisions
src/chess/rules.h file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
     1.1 --- a/src/chess/rules.c	Fri Apr 04 17:36:42 2014 +0200
     1.2 +++ b/src/chess/rules.c	Mon Apr 07 14:08:57 2014 +0200
     1.3 @@ -90,36 +90,6 @@
     1.4      }
     1.5  }
     1.6  
     1.7 -_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file,
     1.8 -        uint8_t color, Move *threat) {
     1.9 -    Move threats[16];
    1.10 -    int threatcount = 0;
    1.11 -    for (uint8_t r = 0 ; r < 8 ; r++) {
    1.12 -        for (uint8_t f = 0 ; f < 8 ; f++) {
    1.13 -            if ((gamestate->board[r][f] & COLOR_MASK) == color) {
    1.14 -                memset(&(threats[threatcount]), 0, sizeof(Move));
    1.15 -                threats[threatcount].piece = gamestate->board[r][f];
    1.16 -                threats[threatcount].fromrow = r;
    1.17 -                threats[threatcount].fromfile = f;
    1.18 -                threats[threatcount].torow = row;
    1.19 -                threats[threatcount].tofile = file;
    1.20 -                threatcount++;
    1.21 -            }
    1.22 -        }
    1.23 -    }
    1.24 -    
    1.25 -    for (int i = 0 ; i < threatcount ; i++) {
    1.26 -        if (validate_move(gamestate, &(threats[i]))) {
    1.27 -            if (threat) {
    1.28 -                *threat = threats[i];
    1.29 -            }
    1.30 -            return 1;
    1.31 -        }
    1.32 -    }
    1.33 -    
    1.34 -    return 0;
    1.35 -}
    1.36 -
    1.37  void apply_move(GameState *gamestate, Move *move) {
    1.38      uint8_t piece = move->piece & PIECE_MASK;
    1.39      uint8_t color = move->piece & COLOR_MASK;
    1.40 @@ -167,9 +137,7 @@
    1.41      addmove(gamestate, move);
    1.42  }
    1.43  
    1.44 -_Bool validate_move(GameState *gamestate, Move *move) {
    1.45 -    _Bool result;
    1.46 -    
    1.47 +static _Bool validate_move_rules(GameState *gamestate, Move *move) {
    1.48      /* validate indices (don't trust opponent) */
    1.49      if (!chkidx(move)) {
    1.50          return 0;
    1.51 @@ -181,50 +149,52 @@
    1.52      }
    1.53      
    1.54      /* does piece exist */
    1.55 -    result = msrc(gamestate->board, move) == move->piece;
    1.56 +    if (msrc(gamestate->board, move) != move->piece) {
    1.57 +        return 0;
    1.58 +    }
    1.59      
    1.60      /* can't capture own pieces */
    1.61 -    uint8_t piececolor = (move->piece & COLOR_MASK);
    1.62 -    if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) {
    1.63 +    if ((mdst(gamestate->board, move) & COLOR_MASK)
    1.64 +        == (move->piece & COLOR_MASK)) {
    1.65          return 0;
    1.66      }
    1.67      
    1.68      /* validate individual rules */
    1.69      switch (move->piece & PIECE_MASK) {
    1.70      case PAWN:
    1.71 -        result = result && pawn_chkrules(gamestate, move);
    1.72 -        result = result && !pawn_isblocked(gamestate, move);
    1.73 -        break;
    1.74 +        return pawn_chkrules(gamestate, move) &&
    1.75 +            !pawn_isblocked(gamestate, move);
    1.76      case ROOK:
    1.77 -        result = result && rook_chkrules(move);
    1.78 -        result = result && !rook_isblocked(gamestate, move);
    1.79 -        break;
    1.80 +        return rook_chkrules(move) &&
    1.81 +            !rook_isblocked(gamestate, move);
    1.82      case KNIGHT:
    1.83 -        result = result && knight_chkrules(move);
    1.84 -        result = result && !knight_isblocked(gamestate, move);
    1.85 -        break;
    1.86 +        return knight_chkrules(move); /* knight is never blocked */
    1.87      case BISHOP:
    1.88 -        result = result && bishop_chkrules(move);
    1.89 -        result = result && !bishop_isblocked(gamestate, move);
    1.90 -        break;
    1.91 +        return bishop_chkrules(move) &&
    1.92 +            !bishop_isblocked(gamestate, move);
    1.93      case QUEEN:
    1.94 -        result = result && queen_chkrules(move);
    1.95 -        result = result && !queen_isblocked(gamestate, move);
    1.96 -        break;
    1.97 +        return queen_chkrules(move) &&
    1.98 +            !queen_isblocked(gamestate, move);
    1.99      case KING:
   1.100 -        result = result && king_chkrules(gamestate, move);
   1.101 -        result = result && !king_isblocked(gamestate, move);
   1.102 -        break;
   1.103 +        return king_chkrules(gamestate, move) &&
   1.104 +            !king_isblocked(gamestate, move);
   1.105      default:
   1.106 -        result = 0;
   1.107 +        return 0;
   1.108      }
   1.109 +}
   1.110 +
   1.111 +_Bool validate_move(GameState *gamestate, Move *move) {
   1.112      
   1.113 -    /* cancel processing to avoid recursion overflow with is_covered() */
   1.114 +    _Bool result = validate_move_rules(gamestate, move);
   1.115 +    
   1.116 +    /* cancel processing to save resources */
   1.117      if (!result) {
   1.118          return 0;
   1.119      }
   1.120      
   1.121      /* find kings for check validation */
   1.122 +    uint8_t piececolor = (move->piece & COLOR_MASK);
   1.123 +    
   1.124      uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
   1.125      for (uint8_t row = 0 ; row < 8 ; row++) {
   1.126          for (uint8_t file = 0 ; file < 8 ; file++) {
   1.127 @@ -252,9 +222,10 @@
   1.128      }
   1.129      
   1.130      /* correct check and checkmate flags (move is still valid) */
   1.131 -    Move threat;
   1.132 -    move->check = get_any_threat_for(&simulation, opkingrow, opkingfile,
   1.133 -        piececolor, &threat);
   1.134 +    Move threats[16];
   1.135 +    uint8_t threatcount;
   1.136 +    move->check = get_threats(&simulation, opkingrow, opkingfile,
   1.137 +        piececolor, threats, &threatcount);
   1.138      
   1.139      if (move->check) {
   1.140          /* determine possible escape fields */
   1.141 @@ -273,17 +244,58 @@
   1.142                  }
   1.143              }
   1.144          }
   1.145 -        /* can't escape, can we capture? */
   1.146 +        /* can't escape, can he capture? */
   1.147 +        if (!canescape && threatcount == 1) {
   1.148 +            canescape = is_attacked(&simulation, threats[0].fromrow,
   1.149 +                threats[0].fromfile, opponent_color(piececolor));
   1.150 +        }
   1.151 +        
   1.152 +        /* can't capture, can he block? */
   1.153 +        if (!canescape && threatcount == 1) {
   1.154 +            Move *threat = &(threats[0]);
   1.155 +            uint8_t threatpiece = threat->piece & PIECE_MASK;
   1.156 +            
   1.157 +            /* knight, pawns and the king cannot be blocked */
   1.158 +            if (threatpiece == BISHOP || threatpiece == ROOK
   1.159 +                || threatpiece == QUEEN) {
   1.160 +                if (threat->fromrow == threat->torow) {
   1.161 +                    /* rook aspect (on row) */
   1.162 +                    int d = threat->tofile > threat->fromfile ? 1 : -1;
   1.163 +                    uint8_t file = threat->fromfile;
   1.164 +                    while (!canescape && file != threat->tofile - d) {
   1.165 +                        file += d;
   1.166 +                        canescape |= is_protected(&simulation,
   1.167 +                            threat->torow, file, opponent_color(piececolor));
   1.168 +                    }
   1.169 +                } else if (threat->fromfile == threat->tofile) {
   1.170 +                    /* rook aspect (on file) */
   1.171 +                    int d = threat->torow > threat->fromrow ? 1 : -1;
   1.172 +                    uint8_t row = threat->fromrow;
   1.173 +                    while (!canescape && row != threat->torow - d) {
   1.174 +                        row += d;
   1.175 +                        canescape |= is_protected(&simulation,
   1.176 +                            row, threat->tofile, opponent_color(piececolor));
   1.177 +                    }
   1.178 +                } else {
   1.179 +                    /* bishop aspect */
   1.180 +                    int dr = move->torow > move->fromrow ? 1 : -1;
   1.181 +                    int df = move->tofile > move->fromfile ? 1 : -1;
   1.182 +
   1.183 +                    uint8_t row = move->fromrow;
   1.184 +                    uint8_t file = move->fromfile;
   1.185 +                    while (!canescape && file != move->tofile - df
   1.186 +                        && row != move->torow - dr) {
   1.187 +                        row += dr;
   1.188 +                        file += df;
   1.189 +                        canescape |= is_protected(&simulation, row, file,
   1.190 +                            opponent_color(piececolor));
   1.191 +                    }
   1.192 +                }
   1.193 +            }
   1.194 +        }
   1.195 +            
   1.196          if (!canescape) {
   1.197 -            canescape = is_covered(&simulation, threat.fromrow,
   1.198 -                threat.fromfile, opponent_color(piececolor));
   1.199 -
   1.200 -            /* can't capture, can we block? */
   1.201 -            // TODO: make it so
   1.202 -            
   1.203 -            if (!canescape) {
   1.204 -                gamestate->checkmate = 1;
   1.205 -            }
   1.206 +            gamestate->checkmate = 1;
   1.207          }
   1.208      }
   1.209      
   1.210 @@ -418,3 +430,97 @@
   1.211          return INVALID_MOVE_SYNTAX;
   1.212      }
   1.213  }
   1.214 +
   1.215 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
   1.216 +        uint8_t color, Move *threats, uint8_t *threatcount) {
   1.217 +    Move candidates[16];
   1.218 +    int candidatecount = 0;
   1.219 +    for (uint8_t r = 0 ; r < 8 ; r++) {
   1.220 +        for (uint8_t f = 0 ; f < 8 ; f++) {
   1.221 +            if ((gamestate->board[r][f] & COLOR_MASK) == color) {
   1.222 +                memset(&(candidates[candidatecount]), 0, sizeof(Move));
   1.223 +                candidates[candidatecount].piece = gamestate->board[r][f];
   1.224 +                candidates[candidatecount].fromrow = r;
   1.225 +                candidates[candidatecount].fromfile = f;
   1.226 +                candidates[candidatecount].torow = row;
   1.227 +                candidates[candidatecount].tofile = file;
   1.228 +                candidatecount++;
   1.229 +            }
   1.230 +        }
   1.231 +    }
   1.232 +
   1.233 +    if (threatcount) {
   1.234 +        *threatcount = 0;
   1.235 +    }
   1.236 +    
   1.237 +    
   1.238 +    _Bool result = 0;
   1.239 +    
   1.240 +    for (int i = 0 ; i < candidatecount ; i++) {
   1.241 +        if (validate_move_rules(gamestate, &(candidates[i]))) {
   1.242 +            result = 1;
   1.243 +            if (threats && threatcount) {
   1.244 +                threats[(*threatcount)++] = candidates[i];
   1.245 +            }
   1.246 +        }
   1.247 +    }
   1.248 +    
   1.249 +    return result;
   1.250 +}
   1.251 +
   1.252 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
   1.253 +        uint8_t color, Move *threats, uint8_t *threatcount) {
   1.254 +
   1.255 +    Move candidates[16];
   1.256 +    uint8_t candidatecount;
   1.257 +    if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
   1.258 +        
   1.259 +        if (threatcount) {
   1.260 +            *threatcount = 0;
   1.261 +        }
   1.262 +        _Bool result = 0;
   1.263 +        uint8_t kingfile = 0, kingrow = 0;
   1.264 +        for (uint8_t row = 0 ; row < 8 ; row++) {
   1.265 +            for (uint8_t file = 0 ; file < 8 ; file++) {
   1.266 +                if ((gamestate->board[row][file] & COLOR_MASK) == color) {
   1.267 +                    kingfile = file;
   1.268 +                    kingrow = row;
   1.269 +                }
   1.270 +            }
   1.271 +        }
   1.272 +        
   1.273 +        for (uint8_t i = 0 ; i < candidatecount ; i++) {
   1.274 +            GameState simulation;
   1.275 +            memcpy(&simulation, gamestate, sizeof(GameState));
   1.276 +            apply_move(&simulation, &(candidates[i]));
   1.277 +            if (!is_covered(&simulation, kingrow, kingfile,
   1.278 +                    opponent_color(color))) {
   1.279 +                result = 1;
   1.280 +                if (threats && threatcount) {
   1.281 +                    threats[(*threatcount)++] = candidates[i];
   1.282 +                }
   1.283 +            }
   1.284 +        }
   1.285 +        
   1.286 +        return result;
   1.287 +    } else {
   1.288 +        return 0;
   1.289 +    }
   1.290 +}
   1.291 +
   1.292 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
   1.293 +        uint8_t color) {
   1.294 +    
   1.295 +    Move threats[16];
   1.296 +    uint8_t threatcount;
   1.297 +    if (get_real_threats(gamestate, row, file, color, threats, &threatcount)) {
   1.298 +        for (int i = 0 ; i < threatcount ; i++) {
   1.299 +            if (threats[i].piece != (color|KING)) {
   1.300 +                return 1;
   1.301 +            }
   1.302 +        }
   1.303 +        return 0;
   1.304 +    } else {
   1.305 +        return 0;
   1.306 +    }
   1.307 +}
     2.1 --- a/src/chess/rules.h	Fri Apr 04 17:36:42 2014 +0200
     2.2 +++ b/src/chess/rules.h	Mon Apr 07 14:08:57 2014 +0200
     2.3 @@ -145,20 +145,41 @@
     2.4  /**
     2.5   * Checks, if a specified field is covered by a piece of a certain color.
     2.6   * 
     2.7 - * Note: when the field is covered by multiple pieces, this function returns
     2.8 - * the first piece it can find.
     2.9 + * The out-parameters may both be NULL, but if any of them is set, the other
    2.10 + * must be set, too.
    2.11   * 
    2.12   * @param gamestate the current game state
    2.13   * @param row row of the field to check
    2.14   * @param file file of the field to check
    2.15   * @param color the color of the piece that should threaten the field
    2.16 - * @param threat if not NULL: a pointer to the move structure where
    2.17 - * the move that could be performed to capture the field should be stored
    2.18 + * @param threats the array where to store the threats (should be able to the
    2.19 + * rare maximum of 16 elements)
    2.20 + * @param threatcount a pointer to an uint8_t where to store the amount of threats
    2.21   * @return TRUE, if any piece of the specified color threatens the specified
    2.22   * field (i.e. could capture an opponent piece)
    2.23   */
    2.24 -_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file,
    2.25 -        uint8_t color, Move *threat);
    2.26 +_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
    2.27 +        uint8_t color, Move* threats, uint8_t* threatcount);
    2.28 +
    2.29 +/**
    2.30 + * Checks, if a specified field is covered by a piece of a certain color AND
    2.31 + * if this piece is not pinned and therefore able to perform the move.
    2.32 + * 
    2.33 + * The out-parameters may both be NULL, but if any of them is set, the other
    2.34 + * must be set, too.
    2.35 + * 
    2.36 + * @param gamestate the current game state
    2.37 + * @param row row of the field to check
    2.38 + * @param file file of the field to check
    2.39 + * @param color the color of the piece that should threaten the field
    2.40 + * @param threats the array where to store the threats (should be able to the
    2.41 + * rare maximum of 16 elements)
    2.42 + * @param threatcount a pointer to an uint8_t where to store the amount of threats
    2.43 + * @return TRUE, if any piece of the specified color threatens the specified
    2.44 + * field (i.e. could capture an opponent piece)
    2.45 + */
    2.46 +_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
    2.47 +        uint8_t color, Move* threats, uint8_t* threatcount);
    2.48  
    2.49  /**
    2.50   * Checks, if a specified field is covered by a piece of a certain color.
    2.51 @@ -168,10 +189,42 @@
    2.52   * @param file file of the field to check
    2.53   * @param color the color of the piece that should cover the field
    2.54   * @return TRUE, if any piece of the specified color threatens the specified
    2.55 - * field (i.e. could capture an opponent piece)
    2.56 + * field
    2.57   */
    2.58  #define is_covered(gamestate, row, file, color) \
    2.59 -    get_any_threat_for(gamestate, row, file, color, NULL)
    2.60 +    get_threats(gamestate, row, file, color, NULL, NULL)
    2.61 +
    2.62 +/**
    2.63 + * Checks, if a specified field is attacked by a piece of a certain color.
    2.64 + * 
    2.65 + * I.e. the field is covered by a piece AND this piece is not pinned and
    2.66 + * therefore able to perform the move.
    2.67 + * 
    2.68 + * @param gamestate the current game state
    2.69 + * @param row row of the field to check
    2.70 + * @param file file of the field to check
    2.71 + * @param color the color of the piece that should cover the field
    2.72 + * @return TRUE, if any piece of the specified color threatens the specified
    2.73 + * field and could capture an opponent piece
    2.74 + */
    2.75 +#define is_attacked(gamestate, row, file, color) \
    2.76 +    get_threats(gamestate, row, file, color, NULL, NULL)
    2.77 +
    2.78 +/**
    2.79 + * Checks, if a specified field is protected by a piece of a certain color.
    2.80 + * 
    2.81 + * I.e. the field is covered by a piece that is NOT the king AND this piece is
    2.82 + * not pinned and therefore able to perform the move.
    2.83 + * 
    2.84 + * @param gamestate the current game state
    2.85 + * @param row row of the field to check
    2.86 + * @param file file of the field to check
    2.87 + * @param color the color of the piece that should cover the field
    2.88 + * @return TRUE, if any piece (excluding the king) of the specified color
    2.89 + * threatens the specified field and could capture an opponent piece
    2.90 + */
    2.91 +_Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
    2.92 +        uint8_t color);
    2.93  
    2.94  /**
    2.95   * Evaluates a move syntactically and stores the move data in the specified
     3.1 --- a/src/main.c	Fri Apr 04 17:36:42 2014 +0200
     3.2 +++ b/src/main.c	Mon Apr 07 14:08:57 2014 +0200
     3.3 @@ -164,6 +164,7 @@
     3.4      }    
     3.5      initscr();
     3.6      cbreak();
     3.7 +    keypad(stdscr, TRUE);
     3.8      if (has_colors()) {
     3.9          start_color();
    3.10          init_colorpairs();

mercurial