added return code to move validation (for more informative messages) + fixed a bug where simulations added movelist items to the original gamestate

Wed, 11 Jun 2014 15:38:01 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 11 Jun 2014 15:38:01 +0200
changeset 48
0cedda2544da
parent 47
d726e4b46c33
child 49
02c509a44e98

added return code to move validation (for more informative messages) + fixed a bug where simulations added movelist items to the original gamestate

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
src/network.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
--- 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) {
--- a/src/chess/rules.h	Wed May 28 15:47:57 2014 +0200
+++ b/src/chess/rules.h	Wed Jun 11 15:38:01 2014 +0200
@@ -33,13 +33,16 @@
 #include <stdint.h>
 #include <sys/time.h>
 
-#define VALID_MOVE_SYNTAX   0
-#define INVALID_MOVE_SYNTAX 1
-#define INVALID_POSITION    2
-#define AMBIGUOUS_MOVE      3
-#define NEED_PROMOTION      4
-#define PIECE_PINNED        5
-#define KING_IN_CHECK       6
+#define VALID_MOVE_SYNTAX      0
+#define VALID_MOVE_SEMANTICS   0 /* use same code for a success */
+#define INVALID_MOVE_SYNTAX    1
+#define INVALID_POSITION       2
+#define AMBIGUOUS_MOVE         3
+#define NEED_PROMOTION         4
+#define PIECE_PINNED           5
+#define KING_IN_CHECK          6
+#define KING_MOVES_INTO_CHECK  7
+#define RULES_VIOLATED        10
 
 
 #define PIECE_MASK       0x0F
@@ -268,7 +271,7 @@
  * @param gamestate the current game state
  * @param mstr the input string to parse
  * @param move a pointer to object where the move data shall be stored
- * @return status code (see rules/rules.h for the list of codes)
+ * @return status code (see macros in this file for the list of codes)
  */
 int eval_move(GameState *gamestate, char *mstr, Move *move);
 
@@ -276,9 +279,9 @@
  * Validates move by applying chess rules.
  * @param gamestate the current game state
  * @param move the move to validate
- * @return TRUE, if the move complies to chess rules, FALSE otherwise
+ * @return status code (see macros in this file for the list of codes)
  */
-_Bool validate_move(GameState *gamestate, Move *move);
+int validate_move(GameState *gamestate, Move *move);
 
 /**
  * Applies a move and deletes captured pieces.
--- a/src/game.c	Wed May 28 15:47:57 2014 +0200
+++ b/src/game.c	Wed Jun 11 15:38:01 2014 +0200
@@ -162,7 +162,7 @@
         printw("Ambiguous move - please specify the piece to move.");
         break;
     case INVALID_POSITION:
-        printw("Cannot find the piece that shall be moved.");
+        printw("No piece can be moved this way.");
         break;
     case NEED_PROMOTION:
         printw("You need to promote the pawn (append \"=Q\" e.g.)!");
@@ -176,6 +176,12 @@
     case INVALID_MOVE_SYNTAX:
         printw("Can't interpret move - please use algebraic notation.");
         break;
+    case RULES_VIOLATED:
+        printw("Move does not comply chess rules.");
+        break;
+    case KING_MOVES_INTO_CHECK:
+        printw("Can't move the king into a check position.");
+        break;
     default:
         printw("Unknown move parser error.");
     }
@@ -218,7 +224,8 @@
                 int eval_result = eval_move(gamestate, movestr, &move);
                 switch (eval_result) {
                 case VALID_MOVE_SYNTAX:
-                    if (validate_move(gamestate, &move)) {
+                    eval_result = validate_move(gamestate, &move);
+                    if (eval_result == VALID_MOVE_SEMANTICS) {
                         apply_move(gamestate, &move);
                         if (gamestate->checkmate) {
                             printw("Checkmate!");
@@ -232,7 +239,7 @@
                             return 0;
                         }
                     } else {
-                        printw("Invalid move.");
+                        eval_move_failed_msg(eval_result);
                     }
                     break;
                 default:
@@ -306,11 +313,15 @@
                 case VALID_MOVE_SYNTAX:
                     net_send_data(opponent, NETCODE_MOVE, &move, sizeof(Move));
                     code = net_recieve_code(opponent);
-                    move.check = code == NETCODE_CHECK;
+                    move.check = code == NETCODE_CHECK ||
+                        code == NETCODE_CHECKMATE;
                     gamestate->checkmate = code == NETCODE_CHECKMATE;
                     gamestate->stalemate = code == NETCODE_STALEMATE;
                     if (code == NETCODE_DECLINE) {
-                        printw("Invalid move.");
+                        uint32_t reason;
+                        net_recieve_data(opponent, &reason, sizeof(uint32_t));
+                        reason = ntohl(reason);
+                        eval_move_failed_msg(reason);
                     } else if (code == NETCODE_ACCEPT
                             || code == NETCODE_CHECK
                             || code == NETCODE_CHECKMATE
@@ -397,7 +408,8 @@
                 break;
             case NETCODE_MOVE:
                 net_recieve_data(opponent, &move, sizeof(Move));
-                if (validate_move(gamestate, &move)) {
+                code = validate_move(gamestate, &move);
+                if (code == VALID_MOVE_SEMANTICS) {
                     apply_move(gamestate, &move);
                     if (move.check) {
                         net_send_code(opponent, NETCODE_CHECK);
@@ -416,7 +428,9 @@
                     }
                     return 0;
                 } else {
-                    net_send_code(opponent, NETCODE_DECLINE);
+                    uint32_t reason = htonl(code);
+                    net_send_data(opponent, NETCODE_DECLINE,
+                        &reason, sizeof(uint32_t));
                 }
                 break;
             default:
--- a/src/network.c	Wed May 28 15:47:57 2014 +0200
+++ b/src/network.c	Wed Jun 11 15:38:01 2014 +0200
@@ -56,7 +56,7 @@
     }
 }
 
-int getaddrinfo_intrnl(char *host, char *port, struct addrinfo **info) {
+static int getaddrinfo_intrnl(char *host, char *port, struct addrinfo **info) {
     struct addrinfo hints;
     memset(&hints, 0, sizeof(hints));
     hints.ai_socktype = SOCK_STREAM;
--- a/src/network.h	Wed May 28 15:47:57 2014 +0200
+++ b/src/network.h	Wed Jun 11 15:38:01 2014 +0200
@@ -49,7 +49,7 @@
 #define NETCODE_TIMEOVER 0x44
 #define NETCODE_CONNLOST 0x80
 
-#define NETCODE_VERSION 13
+#define NETCODE_VERSION 14
 
 typedef struct {
     int fd; /* -1, if we are the client */

mercurial