src/chess/rules.c

Thu, 03 Apr 2014 16:07:04 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 03 Apr 2014 16:07:04 +0200
changeset 27
efeb98bc69c9
parent 25
3ab0c2e1a4e2
child 28
0c1371488d87
permissions
-rw-r--r--

moved checkmate and stalemate flags to gamestate

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2014 Mike Becker. All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  *
    28  */
    30 #include "rules.h"
    31 #include "chess.h"
    32 #include <string.h>
    33 #include <stdlib.h>
    35 void gamestate_cleanup(GameState *gamestate) {
    36     MoveList *elem;
    37     elem = gamestate->movelist;
    38     while (elem) {
    39         MoveList *cur = elem;
    40         elem = elem->next;
    41         free(cur);
    42     };
    43 }
    45 static void addmove(GameState* gamestate, Move *move) {
    46     MoveList *elem = malloc(sizeof(MoveList));
    47     elem->next = NULL;
    48     elem->move = *move;
    50     if (gamestate->lastmove) {
    51         gamestate->lastmove->next = elem;
    52         gamestate->lastmove = elem;
    53     } else {
    54         gamestate->movelist = gamestate->lastmove = elem;
    55     }
    56 }
    58 char getpiecechr(uint8_t piece) {
    59     switch (piece & PIECE_MASK) {
    60     case ROOK: return 'R';
    61     case KNIGHT: return 'N';
    62     case BISHOP: return 'B';
    63     case QUEEN: return 'Q';
    64     case KING: return 'K';
    65     default: return '\0';
    66     }
    67 }
    69 uint8_t getpiece(char c) {
    70     switch (c) {
    71         case 'R': return ROOK;
    72         case 'N': return KNIGHT;
    73         case 'B': return BISHOP;
    74         case 'Q': return QUEEN;
    75         case 'K': return KING;
    76         default: return 0;
    77     }
    78 }
    80 static int getlocation(GameState *gamestate, Move *move) {   
    81     uint8_t piece = move->piece & PIECE_MASK;
    82     switch (piece) {
    83         case PAWN: return pawn_getlocation(gamestate, move);
    84         case ROOK: return rook_getlocation(gamestate, move);
    85         case KNIGHT: return knight_getlocation(gamestate, move);
    86         case BISHOP: return bishop_getlocation(gamestate, move);
    87         case QUEEN: return queen_getlocation(gamestate, move);
    88         case KING: return king_getlocation(gamestate, move);
    89         default: return INVALID_MOVE_SYNTAX;
    90     }
    91 }
    93 _Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color) {
    94     Move threats[16];
    95     int threatcount = 0;
    96     for (uint8_t r = 0 ; r < 8 ; r++) {
    97         for (uint8_t f = 0 ; f < 8 ; f++) {
    98             if ((gamestate->board[r][f] & COLOR_MASK) == color) {
    99                 threats[threatcount].piece = gamestate->board[r][f];
   100                 threats[threatcount].fromrow = r;
   101                 threats[threatcount].fromfile = f;
   102                 threats[threatcount].torow = row;
   103                 threats[threatcount].tofile = file;
   104                 threatcount++;
   105             }
   106         }
   107     }
   109     for (int i = 0 ; i < threatcount ; i++) {
   110         if (validate_move(gamestate, &(threats[i]))) {
   111             return 1;
   112         }
   113     }
   115     return 0;
   116 }
   118 void apply_move(GameState *gamestate, Move *move) {
   119     uint8_t piece = move->piece & PIECE_MASK;
   120     uint8_t color = move->piece & COLOR_MASK;
   122     /* en passant capture */
   123     if (move->capture && piece == PAWN &&
   124         mdst(gamestate->board, move) == 0) {
   125         gamestate->board[move->fromrow][move->tofile] = 0;
   126     }
   128     /* remove old en passant threats */
   129     for (uint8_t file = 0 ; file < 8 ; file++) {
   130         gamestate->board[3][file] &= ~ENPASSANT_THREAT;
   131         gamestate->board[4][file] &= ~ENPASSANT_THREAT;
   132     }
   134     /* add new en passant threat */
   135     if (piece == PAWN && (
   136         (move->fromrow == 1 && move->torow == 3) ||
   137         (move->fromrow == 6 && move->torow == 4))) {
   138         move->piece |= ENPASSANT_THREAT;
   139     }
   141     /* move (and maybe capture or promote) */
   142     msrc(gamestate->board, move) = 0;
   143     if (move->promotion) {
   144         mdst(gamestate->board, move) = move->promotion;
   145     } else {
   146         mdst(gamestate->board, move) = move->piece;
   147     }
   149     /* castling */
   150     if (piece == KING &&
   151         move->fromfile == fileidx('e')) {
   153         if (move->tofile == fileidx('g')) {
   154             gamestate->board[move->torow][fileidx('h')] = 0;
   155             gamestate->board[move->torow][fileidx('f')] = color|ROOK;
   156         } else if (move->tofile == fileidx('c')) {
   157             gamestate->board[move->torow][fileidx('a')] = 0;
   158             gamestate->board[move->torow][fileidx('d')] = color|ROOK;
   159         }
   160     }
   162     addmove(gamestate, move);
   163 }
   165 _Bool validate_move(GameState *gamestate, Move *move) {
   166     _Bool result;
   168     /* validate indices (don't trust opponent) */
   169     if (!chkidx(move)) {
   170         return 0;
   171     }
   173     /* must move */
   174     if (move->fromfile == move->tofile && move->fromrow == move->torow) {
   175         return 0;
   176     }
   178     /* does piece exist */
   179     result = msrc(gamestate->board, move) == move->piece;
   181     /* can't capture own pieces */
   182     if ((mdst(gamestate->board, move) & COLOR_MASK)
   183         == (move->piece & COLOR_MASK)) {
   184         return 0;
   185     }
   187     /* validate individual rules */
   188     switch (move->piece & PIECE_MASK) {
   189     case PAWN:
   190         result = result && pawn_chkrules(gamestate, move);
   191         result = result && !pawn_isblocked(gamestate, move);
   192         break;
   193     case ROOK:
   194         result = result && rook_chkrules(move);
   195         result = result && !rook_isblocked(gamestate, move);
   196         break;
   197     case KNIGHT:
   198         result = result && knight_chkrules(move);
   199         result = result && !knight_isblocked(gamestate, move);
   200         break;
   201     case BISHOP:
   202         result = result && bishop_chkrules(move);
   203         result = result && !bishop_isblocked(gamestate, move);
   204         break;
   205     case QUEEN:
   206         result = result && queen_chkrules(move);
   207         result = result && !queen_isblocked(gamestate, move);
   208         break;
   209     case KING:
   210         result = result && king_chkrules(gamestate, move);
   211         result = result && !king_isblocked(gamestate, move);
   212         break;
   213     default:
   214         result = 0;
   215     }
   217     /* cancel processing to avoid recursion overflow with is_covered() */
   218     if (!result) {
   219         return 0;
   220     }
   222     /* is piece pinned */
   223     // TODO: make it so
   225     /* correct check and checkmate flags (move is still valid) */
   226     // TODO: make it so
   228     return result;
   229 }
   231 int eval_move(GameState *gamestate, char *mstr, Move *move) {
   232     memset(move, 0, sizeof(Move));
   233     move->fromfile = POS_UNSPECIFIED;
   234     move->fromrow = POS_UNSPECIFIED;
   236     size_t len = strlen(mstr);
   238     /* evaluate check/checkmate flags */
   239     if (mstr[len-1] == '+') {
   240         len--; mstr[len] = '\0';
   241         move->check = 1;
   242     } else if (mstr[len-1] == '#') {
   243         len--; mstr[len] = '\0';
   244         /* ignore - validation should set game state */
   245     }
   247     /* evaluate promotion */
   248     if (len > 3 && mstr[len-2] == '=') {
   249         move->promotion = getpiece(mstr[len-1]);
   250         if (!move->promotion) {
   251             return INVALID_MOVE_SYNTAX;
   252         } else {
   253             move->promotion |= gamestate->mycolor;
   254             len -= 2;
   255             mstr[len] = 0;
   256         }
   257     }
   259     if (len == 2) {
   260         /* pawn move (e.g. "e4") */
   261         move->piece = PAWN;
   262         move->tofile = fileidx(mstr[0]);
   263         move->torow = rowidx(mstr[1]);
   264     } else if (len == 3) {
   265         if (strcmp(mstr, "O-O") == 0) {
   266             /* king side castling */
   267             move->piece = KING;
   268             move->fromfile = fileidx('e');
   269             move->tofile = fileidx('g');
   270             move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7;
   271         } else {
   272             /* move (e.g. "Nf3") */
   273             move->piece = getpiece(mstr[0]);
   274             move->tofile = fileidx(mstr[1]);
   275             move->torow = rowidx(mstr[2]);
   276         }
   278     } else if (len == 4) {
   279         move->piece = getpiece(mstr[0]);
   280         if (!move->piece) {
   281             move->piece = PAWN;
   282             move->fromfile = fileidx(mstr[0]);
   283         }
   284         if (mstr[1] == 'x') {
   285             /* capture (e.g. "Nxf3", "dxe5") */
   286             move->capture = 1;
   287         } else {
   288             /* move (e.g. "Ndf3", "N2c3", "e2e4") */
   289             if (isfile(mstr[1])) {
   290                 move->fromfile = fileidx(mstr[1]);
   291                 if (move->piece == PAWN) {
   292                     move->piece = 0;
   293                 }
   294             } else {
   295                 move->fromrow = rowidx(mstr[1]);
   296             }
   297         }
   298         move->tofile = fileidx(mstr[2]);
   299         move->torow = rowidx(mstr[3]);
   300     } else if (len == 5) {
   301         if (strcmp(mstr, "O-O-O") == 0) {
   302             /* queen side castling "O-O-O" */
   303             move->piece = KING;
   304             move->fromfile = fileidx('e');
   305             move->tofile = fileidx('c');
   306             move->fromrow = move->torow = gamestate->mycolor == WHITE ? 0 : 7;
   307         } else {
   308             move->piece = getpiece(mstr[0]);
   309             if (mstr[2] == 'x') {
   310                 move->capture = 1;
   311                 if (move->piece) {
   312                     /* capture (e.g. "Ndxf3") */
   313                     move->fromfile = fileidx(mstr[1]);
   314                 } else {
   315                     /* long notation capture (e.g. "e5xf6") */
   316                     move->piece = PAWN;
   317                     move->fromfile = fileidx(mstr[0]);
   318                     move->fromrow = rowidx(mstr[1]);
   319                 }
   320             } else {
   321                 /* long notation move (e.g. "Nc5a4") */
   322                 move->fromfile = fileidx(mstr[1]);
   323                 move->fromrow = rowidx(mstr[2]);
   324             }
   325             move->tofile = fileidx(mstr[3]);
   326             move->torow = rowidx(mstr[4]);
   327         }
   328     } else if (len == 6) {
   329         /* long notation capture (e.g. "Nc5xf3") */
   330         if (mstr[3] == 'x') {
   331             move->capture = 1;
   332             move->piece = getpiece(mstr[0]);
   333             move->fromfile = fileidx(mstr[1]);
   334             move->fromrow = rowidx(mstr[2]);
   335             move->tofile = fileidx(mstr[4]);
   336             move->torow = rowidx(mstr[5]);
   337         }
   338     }
   341     if (move->piece) {
   342         if (move->piece == PAWN
   343             && move->torow == (gamestate->mycolor==WHITE?7:0)
   344             && !move->promotion) {
   345             return NEED_PROMOTION;
   346         }
   348         move->piece |= gamestate->mycolor;
   349         if (move->fromfile == POS_UNSPECIFIED
   350             || move->fromrow == POS_UNSPECIFIED) {
   351             return getlocation(gamestate, move);
   352         } else {
   353             return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION;
   354         }
   355     } else {
   356         return INVALID_MOVE_SYNTAX;
   357     }
   358 }

mercurial