src/chess/rules.c

changeset 49
02c509a44e98
parent 48
0cedda2544da
child 50
41017d0a72c5
--- a/src/chess/rules.c	Wed Jun 11 15:38:01 2014 +0200
+++ b/src/chess/rules.c	Wed Jun 11 16:54:20 2014 +0200
@@ -54,6 +54,81 @@
     };
 }
 
+/* MUST be called IMMEDIATLY after applying a move to work correctly */
+static void format_move(GameState *gamestate, Move *move) {
+    char *string = move->string;
+    
+    /* at least 8 characters should be available, wipe them out */
+    memset(string, 0, 8);
+    
+    /* special formats for castling */
+    if ((move->piece&PIECE_MASK) == KING &&
+            abs(move->tofile-move->fromfile) == 2) {
+        if (move->tofile==fileidx('c')) {
+            memcpy(string, "O-O-O", 5);
+        } else {
+            memcpy(string, "O-O", 3);
+        }
+    }
+
+    /* start by notating the piece character */
+    string[0] = getpiecechr(move->piece);
+    int idx = string[0] ? 1 : 0;
+    
+    /* find out how many source information we do need */
+    uint8_t piece = move->piece & PIECE_MASK;
+    if (piece == PAWN) {
+        if (move->capture) {
+            string[idx++] = filechr(move->fromfile);
+        }
+    } else if (piece != KING) {
+        Move threats[16];
+        uint8_t threatcount;
+        get_real_threats(gamestate, move->torow, move->tofile,
+            move->piece&COLOR_MASK, threats, &threatcount);
+        if (threatcount > 1) {
+            int ambrows = 0, ambfiles = 0;
+            for (uint8_t i = 0 ; i < threatcount ; i++) {
+                if (threats[i].fromrow == move->fromrow) {
+                    ambrows++;
+                }
+                if (threats[i].fromfile == move->fromfile) {
+                    ambfiles++;
+                }
+            }
+            /* ambiguous row, name file */
+            if (ambrows > 1) {
+                string[idx++] = filechr(move->fromfile);
+            }
+            /* ambiguous file, name row */
+            if (ambfiles > 1) {
+                string[idx++] = filechr(move->fromrow);
+            }
+        }
+    }
+    
+    /* capturing? */
+    if (move->capture) {
+        string[idx++] = 'x';
+    }
+    
+    /* destination */
+    string[idx++] = filechr(move->tofile);
+    string[idx++] = rowchr(move->torow);
+    
+    /* promotion? */
+    if (move->promotion) {
+        string[idx++] = '=';
+        string[idx++] = getpiecechr(move->promotion);
+    }
+    
+    /* check? */
+    if (move->check) {
+        /* works only, if this function is called when applying the move */
+        string[idx++] = gamestate->checkmate?'#':'+';
+    }
+}
+
 static void addmove(GameState* gamestate, Move *move) {
     MoveList *elem = malloc(sizeof(MoveList));
     elem->next = NULL;
@@ -109,7 +184,7 @@
     }
 }
 
-void apply_move(GameState *gamestate, Move *move) {
+static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) {
     uint8_t piece = move->piece & PIECE_MASK;
     uint8_t color = move->piece & COLOR_MASK;
     
@@ -141,8 +216,7 @@
     }
     
     /* castling */
-    if (piece == KING &&
-        move->fromfile == fileidx('e')) {
+    if (piece == KING && move->fromfile == fileidx('e')) {
         
         if (move->tofile == fileidx('g')) {
             gamestate->board[move->torow][fileidx('h')] = 0;
@@ -153,9 +227,19 @@
         }
     }
 
+    if (!simulate) {
+        if (!move->string[0]) {
+            format_move(gamestate, move);
+        }
+    }
+    /* add move, even in simulation (checkmate test needs it) */
     addmove(gamestate, move);
 }
 
+void apply_move(GameState *gamestate, Move *move) {
+    apply_move_impl(gamestate, move, 0);
+}
+
 static int validate_move_rules(GameState *gamestate, Move *move) {
     /* validate indices (don't trust opponent) */
     if (!chkidx(move)) {
@@ -248,7 +332,7 @@
     /* simulate move for check validation */
     GameState simulation = gamestate_copy_sim(gamestate);
     Move simmove = *move;
-    apply_move(&simulation, &simmove);
+    apply_move_impl(&simulation, &simmove, 1);
     
     /* don't move into or stay in check position */
     if (is_covered(&simulation, mykingrow, mykingfile,
@@ -448,7 +532,6 @@
                     threats[(*threatcount)++] = candidates[i];
                 }
             }
-            gamestate_cleanup(&simulation);
         }
         
         return result;
@@ -468,6 +551,8 @@
     if (get_threats(gamestate, move->torow, move->tofile, color,
             threats, &threatcount)) {
         
+        int reason = INVALID_POSITION;
+        
         // find threats for the specified position
         for (uint8_t i = 0 ; i < threatcount ; i++) {
             if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
@@ -480,23 +565,23 @@
                 if (threat) {
                     return AMBIGUOUS_MOVE;
                 } else {
-                    threat = &(threats[i]);
+                    // found threat is no real threat
+                    if (is_pinned(gamestate, &(threats[i]))) {
+                        reason = incheck?KING_IN_CHECK:PIECE_PINNED;
+                    } else {
+                        threat = &(threats[i]);
+                    }
                 }
             }
         }
         
         // can't threaten specified position
         if (!threat) {
-            return INVALID_POSITION;
+            return reason;
         }
 
-        // found threat is no real threat
-        if (is_pinned(gamestate, threat)) {
-            return incheck?KING_IN_CHECK:PIECE_PINNED;
-        } else {
-            memcpy(move, threat, sizeof(Move));
-            return VALID_MOVE_SYNTAX;
-        }
+        memcpy(move, threat, sizeof(Move));
+        return VALID_MOVE_SYNTAX;
     } else {
         return INVALID_POSITION;
     }

mercurial