src/chess/rules.c

changeset 48
0cedda2544da
parent 47
d726e4b46c33
child 49
02c509a44e98
--- a/src/chess/rules.c	Wed May 28 15:47:57 2014 +0200
+++ b/src/chess/rules.c	Wed Jun 11 15:38:01 2014 +0200
@@ -33,6 +33,17 @@
 #include <stdlib.h>
 #include <sys/time.h>
 
+static GameState gamestate_copy_sim(GameState *gamestate) {
+    GameState simulation = *gamestate;
+    if (simulation.lastmove) {
+        MoveList *lastmovecopy = malloc(sizeof(MoveList));
+        *lastmovecopy = *(simulation.lastmove);
+        simulation.movelist = simulation.lastmove = lastmovecopy;
+    }
+        
+    return simulation;
+}
+
 void gamestate_cleanup(GameState *gamestate) {
     MoveList *elem;
     elem = gamestate->movelist;
@@ -145,67 +156,75 @@
     addmove(gamestate, move);
 }
 
-static _Bool validate_move_rules(GameState *gamestate, Move *move) {
+static int validate_move_rules(GameState *gamestate, Move *move) {
     /* validate indices (don't trust opponent) */
     if (!chkidx(move)) {
-        return 0;
+        return INVALID_POSITION;
     }
     
     /* must move */
     if (move->fromfile == move->tofile && move->fromrow == move->torow) {
-        return 0;
+        return INVALID_MOVE_SYNTAX;
     }
     
     /* does piece exist */
     if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
            != (move->piece&(PIECE_MASK|COLOR_MASK))) {
-        return 0;
+        return INVALID_POSITION;
     }
     
     /* can't capture own pieces */
     if ((mdst(gamestate->board, move) & COLOR_MASK)
             == (move->piece & COLOR_MASK)) {
-        return 0;
+        return RULES_VIOLATED;
     }
     
     /* must capture, if and only if destination is occupied */
     if ((mdst(gamestate->board, move) == 0 && move->capture) ||
             (mdst(gamestate->board, move) != 0 && !move->capture)) {
-        return 0;
+        return INVALID_MOVE_SYNTAX;
     }
     
     /* validate individual rules */
+    _Bool chkrules;
     switch (move->piece & PIECE_MASK) {
     case PAWN:
-        return pawn_chkrules(gamestate, move) &&
+        chkrules = pawn_chkrules(gamestate, move) &&
             !pawn_isblocked(gamestate, move);
+        break;
     case ROOK:
-        return rook_chkrules(move) &&
+        chkrules = rook_chkrules(move) &&
             !rook_isblocked(gamestate, move);
+        break;
     case KNIGHT:
-        return knight_chkrules(move); /* knight is never blocked */
+        chkrules = knight_chkrules(move); /* knight is never blocked */
+        break;
     case BISHOP:
-        return bishop_chkrules(move) &&
+        chkrules = bishop_chkrules(move) &&
             !bishop_isblocked(gamestate, move);
+        break;
     case QUEEN:
-        return queen_chkrules(move) &&
+        chkrules = queen_chkrules(move) &&
             !queen_isblocked(gamestate, move);
+        break;
     case KING:
-        return king_chkrules(gamestate, move) &&
+        chkrules = king_chkrules(gamestate, move) &&
             !king_isblocked(gamestate, move);
+        break;
     default:
-        return 0;
+        return INVALID_MOVE_SYNTAX;
     }
+    
+    return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED;
 }
 
-_Bool validate_move(GameState *gamestate, Move *move) {
-    // TODO: provide more details via a return code
+int validate_move(GameState *gamestate, Move *move) {
     
-    _Bool result = validate_move_rules(gamestate, move);
+    int result = validate_move_rules(gamestate, move);
     
     /* cancel processing to save resources */
-    if (!result) {
-        return 0;
+    if (result != VALID_MOVE_SEMANTICS) {
+        return result;
     }
     
     /* find kings for check validation */
@@ -226,15 +245,23 @@
         }
     }
     
-    /* simulation move for check validation */
-    GameState simulation = *gamestate;
+    /* simulate move for check validation */
+    GameState simulation = gamestate_copy_sim(gamestate);
     Move simmove = *move;
     apply_move(&simulation, &simmove);
     
     /* don't move into or stay in check position */
     if (is_covered(&simulation, mykingrow, mykingfile,
         opponent_color(piececolor))) {
-        return 0;
+        
+        gamestate_cleanup(&simulation);
+        if ((move->piece & PIECE_MASK) == KING) {
+            return KING_MOVES_INTO_CHECK;
+        } else {
+            /* last move is always not null in this case */
+            return gamestate->lastmove->move.check ?
+                KING_IN_CHECK : PIECE_PINNED;
+        }
     }
     
     /* correct check and checkmate flags (move is still valid) */
@@ -315,7 +342,9 @@
         }
     }
     
-    return 1;
+    gamestate_cleanup(&simulation);
+    
+    return VALID_MOVE_SEMANTICS;
 }
 
 _Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
@@ -351,7 +380,8 @@
     _Bool result = 0;
     
     for (int i = 0 ; i < candidatecount ; i++) {
-        if (validate_move_rules(gamestate, &(candidates[i]))) {
+        if (validate_move_rules(gamestate, &(candidates[i]))
+                == VALID_MOVE_SEMANTICS) {
             result = 1;
             if (threats && threatcount) {
                 threats[(*threatcount)++] = candidates[i];
@@ -375,10 +405,14 @@
         }
     }
 
-    GameState simulation = *gamestate;
+    GameState simulation = gamestate_copy_sim(gamestate);
     Move simmove = *move;
     apply_move(&simulation, &simmove);
-    return is_covered(&simulation, kingrow, kingfile, opponent_color(color));
+    _Bool covered = is_covered(&simulation,
+        kingrow, kingfile, opponent_color(color));
+    gamestate_cleanup(&simulation);
+    
+    return covered;
 }
 
 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
@@ -404,7 +438,7 @@
         }
 
         for (uint8_t i = 0 ; i < candidatecount ; i++) {
-            GameState simulation = *gamestate;
+            GameState simulation = gamestate_copy_sim(gamestate);
             Move simmove = candidates[i];
             apply_move(&simulation, &simmove);
             if (!is_covered(&simulation, kingrow, kingfile,
@@ -414,6 +448,7 @@
                     threats[(*threatcount)++] = candidates[i];
                 }
             }
+            gamestate_cleanup(&simulation);
         }
         
         return result;
@@ -421,7 +456,7 @@
         return 0;
     }
 }
-#include <ncurses.h>
+
 static int getlocation(GameState *gamestate, Move *move) {   
 
     uint8_t color = move->piece & COLOR_MASK;
@@ -473,6 +508,9 @@
     move->fromrow = POS_UNSPECIFIED;
 
     size_t len = strlen(mstr);
+    if (len < 1 || len > 6) {
+        return INVALID_MOVE_SYNTAX;
+    }
     
     /* evaluate check/checkmate flags */
     if (mstr[len-1] == '+') {
@@ -513,7 +551,6 @@
             move->tofile = fileidx(mstr[1]);
             move->torow = rowidx(mstr[2]);
         }
-        
     } else if (len == 4) {
         move->piece = getpiece(mstr[0]);
         if (!move->piece) {

mercurial