[gnome-games/glchess-vala] Handle castling rules better



commit f9e6e8af79b9f65345aa988ebeb52bc2293cc442
Author: Robert Ancell <robert ancell canonical com>
Date:   Tue Dec 28 14:56:41 2010 +1100

    Handle castling rules better

 glchess/src/chess-game.vala      |  116 ++++++++++++++++++++++----------------
 glchess/src/test-chess-game.vala |   23 +++++--
 2 files changed, 83 insertions(+), 56 deletions(-)
---
diff --git a/glchess/src/chess-game.vala b/glchess/src/chess-game.vala
index 6a531f9..5bcbc8d 100644
--- a/glchess/src/chess-game.vala
+++ b/glchess/src/chess-game.vala
@@ -113,10 +113,14 @@ public class ChessState
     public int number = 0;
     public ChessPlayer players[2];
     public ChessPlayer current_player;
-    public ChessPlayer opponent;
+    public ChessPlayer opponent
+    {
+        get { return current_player.color == Color.WHITE ? players[Color.BLACK] : players[Color.WHITE]; }
+    }
     public bool can_castle_kingside[2];
     public bool can_castle_queenside[2];
     public int en_passant_index = -1;
+    public bool in_check;
 
     public ChessPiece[] board;
     public ChessMove? last_move = null;
@@ -171,15 +175,9 @@ public class ChessState
 
         /* Field 2: Active color */
         if (fields[1] == "w")
-        {
             current_player = players[Color.WHITE];
-            opponent = players[Color.BLACK];
-        }
         else if (fields[1] == "b")
-        {
             current_player = players[Color.BLACK];
-            opponent = players[Color.WHITE];
-        }
         //else
         //    throw new Error ("Unknown active color: %s", fields[1]);
 
@@ -191,7 +189,7 @@ public class ChessState
                 var c = fields[2][i];
                 if (c == 'K')
                     can_castle_kingside[Color.WHITE] = true;
-                else if (c == 'q')
+                else if (c == 'Q')
                     can_castle_queenside[Color.WHITE] = true;
                 else if (c == 'k')
                     can_castle_kingside[Color.BLACK] = true;
@@ -217,6 +215,8 @@ public class ChessState
         number = (fields[5].to_int () - 1) * 2;
         if (current_player.color == Color.BLACK)
             number++;
+
+        in_check = is_in_check (current_player);
     }
 
     public ChessState copy ()
@@ -227,12 +227,12 @@ public class ChessState
         state.players[Color.WHITE] = players[Color.WHITE];
         state.players[Color.BLACK] = players[Color.BLACK];
         state.current_player = current_player;
-        state.opponent = opponent;
         state.can_castle_kingside[Color.WHITE] = can_castle_kingside[Color.WHITE];
         state.can_castle_queenside[Color.WHITE] = can_castle_queenside[Color.WHITE];        
         state.can_castle_kingside[Color.BLACK] = can_castle_kingside[Color.BLACK];
         state.can_castle_queenside[Color.BLACK] = can_castle_queenside[Color.BLACK];
         state.en_passant_index = en_passant_index;
+        state.in_check = in_check;
         if (last_move != null)
             state.last_move = last_move.copy();
         for (int i = 0; i < 64; i++)
@@ -262,23 +262,28 @@ public class ChessState
     {
         int r0, f0, r1, f1;
         PieceType promotion_type;
-        if (!decode_move (move, out r0, out f0, out r1, out f1, out promotion_type))
+
+        if (!decode_move (current_player, move, out r0, out f0, out r1, out f1, out promotion_type))
             return false;
-        return move_with_coords (r0, f0, r1, f1, promotion_type, apply);
+
+        if (!move_with_coords (current_player, r0, f0, r1, f1, promotion_type, apply))
+            return false;
+
+        return true;
     }
 
-    public bool move_with_coords (int r0, int f0, int r1, int f1, PieceType promotion_type = PieceType.QUEEN, bool apply = true, bool test_check = true)
+    public bool move_with_coords (ChessPlayer player, int r0, int f0, int r1, int f1, PieceType promotion_type = PieceType.QUEEN, bool apply = true, bool test_check = true)
     {
         // FIXME: Make this use indexes to be faster
         int start = get_index (r0, f0);
         int end = get_index (r1, f1);
 
-        var color = current_player.color;
-        var opponent_color = opponent.color;
+        var color = player.color;
+        var opponent_color = color == Color.WHITE ? Color.BLACK : Color.WHITE;
 
         /* Must be moving own piece */
         ChessPiece? piece = board[start];
-        if (piece == null || piece.player != current_player)
+        if (piece == null || piece.player != player)
             return false;
 
         /* Check valid move */
@@ -297,7 +302,7 @@ public class ChessState
         int victim_index = end;
 
         /* Can't take own pieces */
-        if (victim != null && victim.player == current_player)
+        if (victim != null && victim.player == player)
             return false;
 
         /* Check if taking an marched pawn */
@@ -354,7 +359,16 @@ public class ChessState
                 if ((rook_over_mask & (piece_masks[Color.WHITE] | piece_masks[Color.BLACK])) != 0)
                     return false;
 
-                // FIXME: Can't be in check before, during or after move (check if rook can be taken?)
+                /* Can't castle when in check */
+                if (in_check)
+                    return false;
+
+                /* Square moved across can't be under attack */
+                for (int i = 0; i < 64; i++)
+                {
+                    if (move_with_coords (opponent, get_rank (i), get_file (i), get_rank (rook_end), get_file (rook_end), PieceType.QUEEN, false, false))
+                        return false;
+                }
             }
             break;
         default:
@@ -366,20 +380,19 @@ public class ChessState
 
         var old_white_mask = piece_masks[Color.WHITE];
         var old_black_mask = piece_masks[Color.BLACK];
-        var old_current_player = current_player;
-        var old_opponent = opponent;
         var old_white_can_castle_kingside = can_castle_kingside[Color.WHITE];
         var old_white_can_castle_queenside = can_castle_queenside[Color.WHITE];
         var old_black_can_castle_kingside = can_castle_kingside[Color.BLACK];
         var old_black_can_castle_queenside = can_castle_queenside[Color.BLACK];
         var old_en_passant_index = en_passant_index;
+        var old_in_check = in_check;
 
         /* Update board */
         board[start] = null;
         if (victim != null)
             board[victim_index] = null;
         if (piece.type == PieceType.PAWN && (r1 == 0 || r1 == 7))
-            board[end] = new ChessPiece (old_current_player, promotion_type);
+            board[end] = new ChessPiece (player, promotion_type);
         else
             board[end] = piece;
         piece_masks[Color.WHITE] &= BitBoard.clear_location_masks[start];
@@ -394,8 +407,6 @@ public class ChessState
             piece_masks[color] &= BitBoard.clear_location_masks[rook_start];
             piece_masks[color] |= BitBoard.set_location_masks[rook_end];
         }
-        current_player = old_opponent;
-        opponent = old_current_player;
 
         /* Can't castle once king has moved */
         if (piece.type == PieceType.KING)
@@ -426,30 +437,14 @@ public class ChessState
         bool result = true;
         if (test_check)
         {
-            for (int king_index = 0; king_index < 64; king_index++)
-            {
-                var p = board[king_index];
-                if (p != null && p.player == opponent && p.type == PieceType.KING)
-                {
-                    /* See if any enemy pieces can take the king */
-                    for (int i = 0; i < 64; i++)
-                    {
-                        // FIXME: Should check all promotion types...
-                        if (move_with_coords (get_rank (i), get_file (i), get_rank (king_index), get_file (king_index), PieceType.QUEEN, false, false))
-                        {
-                            result = false;
-                            break;
-                        }
-                    }
-                }
-            }
+            in_check = is_in_check (player);
+            if (in_check)
+                result = false;
         }
 
         /* Undo move */
-        if (!apply || !result)
+        if (!apply || in_check)
         {
-            current_player = old_current_player;
-            opponent = old_opponent;
             board[start] = piece;
             board[end] = null;
             if (victim != null)
@@ -467,11 +462,14 @@ public class ChessState
             can_castle_kingside[Color.BLACK] = old_black_can_castle_kingside;
             can_castle_queenside[Color.BLACK] = old_black_can_castle_queenside;
             en_passant_index = old_en_passant_index;
+            in_check = old_in_check;
         }
 
         if (!apply)
             return result;
 
+        current_player = color == Color.WHITE ? players[Color.BLACK] : players[Color.WHITE];
+
         last_move = new ChessMove ();
         last_move.number = number;
         last_move.piece = piece;
@@ -490,6 +488,27 @@ public class ChessState
         return true;
     }
 
+    private bool is_in_check (ChessPlayer player)
+    {
+        var opponent = player.color == Color.WHITE ? players[Color.BLACK] : players[Color.WHITE];
+
+        for (int king_index = 0; king_index < 64; king_index++)
+        {
+            var p = board[king_index];
+            if (p != null && p.player == player && p.type == PieceType.KING)
+            {
+                /* See if any enemy pieces can take the king */
+                for (int i = 0; i < 64; i++)
+                {
+                    if (move_with_coords (opponent, get_rank (i), get_file (i), get_rank (king_index), get_file (king_index), PieceType.QUEEN, false, false))
+                        return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
     private bool decode_piece_type (unichar c, out PieceType type)
     {
         switch (c)
@@ -517,14 +536,14 @@ public class ChessState
         }
     }
 
-    private bool decode_move (string move, out int r0, out int f0, out int r1, out int f1, out PieceType promotion_type)
+    private bool decode_move (ChessPlayer player, string move, out int r0, out int f0, out int r1, out int f1, out PieceType promotion_type)
     {
         int i = 0;
 
         promotion_type = PieceType.QUEEN;
         if (move.has_prefix ("O-O-O"))
         {
-            if (current_player.color == Color.WHITE)
+            if (player.color == Color.WHITE)
                 r0 = r1 = 0;
             else
                 r0 = r1 = 7;
@@ -534,7 +553,7 @@ public class ChessState
         }
         else if (move.has_prefix ("O-O"))
         {
-            if (current_player.color == Color.WHITE)
+            if (player.color == Color.WHITE)
                 r0 = r1 = 0;
             else
                 r0 = r1 = 7;
@@ -602,12 +621,11 @@ public class ChessState
 
                         /* Only check this players pieces of the correct type */
                         var piece = board[get_index (rank, file)];
-                        if (piece == null || piece.type != type || piece.player != current_player)
+                        if (piece == null || piece.type != type || piece.player != player)
                             continue;
 
                         /* See if can move here */
-                        // FIXME: Should check all promotion types...
-                        if (!this.move_with_coords (rank, file, r1, f1, PieceType.QUEEN, false))
+                        if (!this.move_with_coords (player, rank, file, r1, f1, PieceType.QUEEN, false))
                             continue;
 
                         /* Duplicate match */
@@ -736,7 +754,7 @@ public class ChessGame
         }
         else
         {
-            if (!state.move_with_coords (r0, f0, r1, f1, PieceType.QUEEN, apply))
+            if (!state.move_with_coords (player, r0, f0, r1, f1, PieceType.QUEEN, apply))
                 return false;
         }
 
diff --git a/glchess/src/test-chess-game.vala b/glchess/src/test-chess-game.vala
index 6f683d9..bc038da 100644
--- a/glchess/src/test-chess-game.vala
+++ b/glchess/src/test-chess-game.vala
@@ -28,25 +28,34 @@ class GlChess
     public static int main (string[] args)
     {
         /* Pawn move */
-        test_good_move ("8/8/8/8/8/8/P7/8 w KQkr - 0 1", "a2a3");
+        test_good_move ("8/8/8/8/8/8/P7/8 w - - 0 1", "a2a3");
 
         /* Pawn march */
-        test_good_move ("8/8/8/8/8/8/P7/8 w KQkr - 0 1", "a2a4");
+        test_good_move ("8/8/8/8/8/8/P7/8 w - - 0 1", "a2a4");
 
         /* Pawn march only allowed from baseline */
-        test_bad_move ("8/8/8/8/8/P7/8/8 w KQkr - 0 1", "a2a5");
+        test_bad_move ("8/8/8/8/8/P7/8/8 w - - 0 1", "a2a5");
         
         /* En passant */
-        test_good_move ("8/8/8/pP6/8/8/8/8 w KQkr a6 0 1", "b5a6");
+        test_good_move ("8/8/8/pP6/8/8/8/8 w - a6 0 1", "b5a6");
 
         /* Castle kingside */
-        test_good_move ("8/8/8/8/8/8/8/4K2R w KQkr - 0 1", "Kg1");
+        test_good_move ("8/8/8/8/8/8/8/4K2R w K - 0 1", "O-O");
+
+        /* Castle queenside */
+        test_good_move ("8/8/8/8/8/8/8/R3K3 w Q - 0 1", "O-O-O");
+
+        /* Can't castle if pieces moved */
+        test_bad_move ("8/8/8/8/8/8/8/4K2R w - - 0 1", "O-O");
+
+        /* Can't castle if rook not there (shouldn't occur as then the castle flag would not be there) */
+        test_bad_move ("8/8/8/8/8/8/8/4K3 w K - 0 1", "O-O");
 
         /* Can't castle when in check */
-        test_bad_move ("4r3/8/8/8/8/8/8/4K2R w KQkr - 0 1", "Kg1");
+        test_bad_move ("4r3/8/8/8/8/8/8/4K2R w K - 0 1", "O-O");
 
         /* Can't move across square that would put into check */
-        test_bad_move ("5r2/8/8/8/8/8/8/4K2R w KQkr - 0 1", "Kg1");
+        test_bad_move ("5r2/8/8/8/8/8/8/4K2R w K - 0 1", "O-O");
 
         /* Can't move into check */
         test_bad_move ("4r3/8/8/8/8/8/4R3/4K3 w KQkr - 0 1", "e2f2");



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]