NEED TESTING: implemented check and checkmate - TODO: avoid checkmate by moving another piece in between

Fri, 04 Apr 2014 17:36:42 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 04 Apr 2014 17:36:42 +0200
changeset 28
0c1371488d87
parent 27
efeb98bc69c9
child 29
c6a1ad6cf749

NEED TESTING: implemented check and checkmate - TODO: avoid checkmate by moving another piece in between

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
--- a/src/chess/rules.c	Thu Apr 03 16:07:04 2014 +0200
+++ b/src/chess/rules.c	Fri Apr 04 17:36:42 2014 +0200
@@ -90,12 +90,14 @@
     }
 }
 
-_Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color) {
+_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file,
+        uint8_t color, Move *threat) {
     Move threats[16];
     int threatcount = 0;
     for (uint8_t r = 0 ; r < 8 ; r++) {
         for (uint8_t f = 0 ; f < 8 ; f++) {
             if ((gamestate->board[r][f] & COLOR_MASK) == color) {
+                memset(&(threats[threatcount]), 0, sizeof(Move));
                 threats[threatcount].piece = gamestate->board[r][f];
                 threats[threatcount].fromrow = r;
                 threats[threatcount].fromfile = f;
@@ -108,6 +110,9 @@
     
     for (int i = 0 ; i < threatcount ; i++) {
         if (validate_move(gamestate, &(threats[i]))) {
+            if (threat) {
+                *threat = threats[i];
+            }
             return 1;
         }
     }
@@ -158,7 +163,7 @@
             gamestate->board[move->torow][fileidx('d')] = color|ROOK;
         }
     }
-    
+
     addmove(gamestate, move);
 }
 
@@ -179,8 +184,8 @@
     result = msrc(gamestate->board, move) == move->piece;
     
     /* can't capture own pieces */
-    if ((mdst(gamestate->board, move) & COLOR_MASK)
-        == (move->piece & COLOR_MASK)) {
+    uint8_t piececolor = (move->piece & COLOR_MASK);
+    if ((mdst(gamestate->board, move) & COLOR_MASK) == piececolor) {
         return 0;
     }
     
@@ -219,13 +224,70 @@
         return 0;
     }
     
-    /* is piece pinned */
-    // TODO: make it so
+    /* find kings for check validation */
+    uint8_t mykingfile = 0, mykingrow = 0, opkingfile = 0, opkingrow = 0;
+    for (uint8_t row = 0 ; row < 8 ; row++) {
+        for (uint8_t file = 0 ; file < 8 ; file++) {
+            if (gamestate->board[row][file] ==
+                    (piececolor == WHITE?WKING:BKING)) {
+                mykingfile = file;
+                mykingrow = row;
+            } else if (gamestate->board[row][file] ==
+                    (piececolor == WHITE?BKING:WKING)) {
+                opkingfile = file;
+                opkingrow = row;
+            }
+        }
+    }
+    
+    /* simulation move for check validation */
+    GameState simulation;
+    memcpy(&simulation, gamestate, sizeof(GameState));
+    apply_move(&simulation, move);
+    
+    /* don't move into or stay in check position */
+    if (is_covered(&simulation, mykingrow, mykingfile,
+        opponent_color(piececolor))) {
+        return 0;
+    }
     
     /* correct check and checkmate flags (move is still valid) */
-    // TODO: make it so
+    Move threat;
+    move->check = get_any_threat_for(&simulation, opkingrow, opkingfile,
+        piececolor, &threat);
     
-    return result;
+    if (move->check) {
+        /* determine possible escape fields */
+        _Bool canescape = 0;
+        for (int dr = -1 ; dr <= 1 && !canescape ; dr++) {
+            for (int df = -1 ; df <= 1 && !canescape ; df++) {
+                if (!(dr == 0 && df == 0)  &&
+                        isidx(opkingrow + dr) && isidx(opkingfile + df)) {
+                    
+                    /* escape field neither blocked nor covered */
+                    if ((simulation.board[opkingrow + dr][opkingfile + df]
+                            & COLOR_MASK) != opponent_color(piececolor)) {
+                        canescape |= !is_covered(&simulation,
+                            opkingrow + dr, opkingfile + df, piececolor);
+                    }
+                }
+            }
+        }
+        /* can't escape, can we capture? */
+        if (!canescape) {
+            canescape = is_covered(&simulation, threat.fromrow,
+                threat.fromfile, opponent_color(piececolor));
+
+            /* can't capture, can we block? */
+            // TODO: make it so
+            
+            if (!canescape) {
+                gamestate->checkmate = 1;
+            }
+        }
+    }
+    
+    return 1;
 }
 
 int eval_move(GameState *gamestate, char *mstr, Move *move) {
--- a/src/chess/rules.h	Thu Apr 03 16:07:04 2014 +0200
+++ b/src/chess/rules.h	Fri Apr 04 17:36:42 2014 +0200
@@ -102,7 +102,7 @@
 #define mdst(b,m) b[(m)->torow][(m)->tofile]
 #define msrc(b,m) b[(m)->fromrow][(m)->fromfile]
 
-#define isidx(idx) ((uint8_t)idx < 8)
+#define isidx(idx) ((uint8_t)(idx) < 8)
 
 #define isfile(file) (file >= 'a' && file <= 'h')
 #define isrow(row) (row >= '1' && row <= '8')
@@ -145,6 +145,24 @@
 /**
  * Checks, if a specified field is covered by a piece of a certain color.
  * 
+ * Note: when the field is covered by multiple pieces, this function returns
+ * the first piece it can find.
+ * 
+ * @param gamestate the current game state
+ * @param row row of the field to check
+ * @param file file of the field to check
+ * @param color the color of the piece that should threaten the field
+ * @param threat if not NULL: a pointer to the move structure where
+ * the move that could be performed to capture the field should be stored
+ * @return TRUE, if any piece of the specified color threatens the specified
+ * field (i.e. could capture an opponent piece)
+ */
+_Bool get_any_threat_for(GameState *gamestate, uint8_t row, uint8_t file,
+        uint8_t color, Move *threat);
+
+/**
+ * Checks, if a specified field is covered by a piece of a certain color.
+ * 
  * @param gamestate the current game state
  * @param row row of the field to check
  * @param file file of the field to check
@@ -152,7 +170,8 @@
  * @return TRUE, if any piece of the specified color threatens the specified
  * field (i.e. could capture an opponent piece)
  */
-_Bool is_covered(GameState *gamestate,uint8_t row,uint8_t file,uint8_t color);
+#define is_covered(gamestate, row, file, color) \
+    get_any_threat_for(gamestate, row, file, color, NULL)
 
 /**
  * Evaluates a move syntactically and stores the move data in the specified
--- a/src/game.c	Thu Apr 03 16:07:04 2014 +0200
+++ b/src/game.c	Fri Apr 04 17:36:42 2014 +0200
@@ -106,7 +106,7 @@
             }
             if (!logelem->next) {
                 if (gamestate->checkmate) {
-                    addch('#');
+                    addstr("\b#");
                 } else if (gamestate->stalemate) {
                     addstr(" stalemate");
                 }
@@ -343,9 +343,9 @@
 
 void game_start_singlemachine(Settings *settings) {
     GameState gamestate;
+    memset(&gamestate, 0, sizeof(GameState));
     init_board(&gamestate);
     gamestate.mycolor = WHITE;
-    gamestate.movelist = gamestate.lastmove = NULL;
     // TODO: time limit
     _Bool running;
     do {
@@ -354,6 +354,8 @@
         running = !domove_singlemachine(&gamestate);
         gamestate.mycolor = opponent_color(gamestate.mycolor);
     }  while (running);
+    move(0,0);
+    draw_board(&gamestate);
     
     gamestate_cleanup(&gamestate);
     
@@ -369,9 +371,9 @@
     
     // TODO: time limit
     GameState gamestate;
+    memset(&gamestate, 0, sizeof(GameState));
     init_board(&gamestate);
     gamestate.mycolor = myturn ? WHITE:BLACK;
-    gamestate.movelist = gamestate.lastmove = NULL;
     
     _Bool running;
     do {
@@ -386,6 +388,9 @@
         myturn ^= TRUE;
     }  while (running);
     
+    move(0,0);
+    draw_board(&gamestate);
+    
     gamestate_cleanup(&gamestate);
     
     mvaddstr(getmaxy(stdscr)-1, 0,

mercurial