001 package rules; 002 003 import java.math.BigInteger; 004 import java.util.ArrayList; 005 import java.util.Collection; 006 import java.util.Collections; 007 import java.util.HashMap; 008 import java.util.Map; 009 import java.util.Random; 010 011 import core.Color; 012 import core.Move; 013 import core.Piece; 014 import core.Position; 015 016 017 /** 018 * An AntichessGameState is a GameState specific to Antichess. It represents all 019 * the information needed to play the game, but not auxiliary information like 020 * history and times. 021 * 022 * An AntichessGameState is mutable. 023 * 024 * @specfield board : map<position, piece> //Location of pieces on the board 025 * @specfield tomove : color //The color of the player whose move it is 026 * @specfield moveNum : int //The number of moves that have passed 027 * 028 * Abstract invariant: board is a valid antichess board. 029 */ 030 public class GameState 031 { 032 /* The state of the board */ 033 final private Board board; 034 035 /* Whose turn it is */ 036 private Color toMove; 037 038 /* The number of ply the game has proceeded */ 039 private int moveNum; 040 041 private Collection<Move> legalMovesCache; 042 043 private static int[][][] primeTable = computePrimeTable(16, 8, 8); 044 045 private static int[][][] computePrimeTable(int a, int b, int c){ 046 int[][][] ans = new int[a][b][c]; 047 Random r = new Random(); 048 049 for(int x = 0; x < a; x++) { 050 for(int y = 0; y < b; y++) { 051 for(int z = 0; z < c; z++) { 052 int prime = BigInteger.probablePrime(30, r).intValue(); 053 ans[x][y][z] = prime; 054 } 055 } 056 } 057 return ans; 058 } 059 060 private final boolean doCheckRep = false; 061 062 // AF: 063 // board = board 064 // tomove= toMove 065 // moveNum = moveNum 066 // 067 // RI: 068 // board, toMove != null 069 // board contains mappings from exactly the valid board positions 070 // All values in board are not null 071 072 public void checkRep() 073 { 074 if (doCheckRep) 075 { 076 assert (toMove != null); 077 assert (board != null); 078 assert (moveNum >= 0); 079 for (int x = 0; x < 8; x++) 080 { 081 for (int y = 0; y < 8; y++) 082 { 083 //assert (board.containsKey(Position.get(x, y))); 084 assert (board.get(x, y) != null); 085 } 086 } 087 } 088 } 089 090 /** 091 * Construct an Antichess-specific GameState. 092 * 093 * @requires p, n, b != null 094 */ 095 public GameState(Color p, int n, Board b) 096 { 097 toMove = p; 098 moveNum = n; 099 board = b; 100 checkRep(); 101 } 102 103 /** 104 * Construct an Antichess-specific GameState 105 * 106 * @requires p, n, b != null 107 * @requires s represents a valid input to board.fromString 108 */ 109 public GameState(Color p, int n, String s) 110 { 111 toMove = p; 112 moveNum = n; 113 board = Board.fromString(s); 114 checkRep(); 115 } 116 117 public GameState copy(){ 118 return new GameState(toMove, moveNum, board.copy()); 119 } 120 121 /** 122 * Construct the initial GameState, representing the starting board 123 * position. 124 */ 125 public GameState() 126 { 127 this(Color.WHITE, 0, Board.startingBoard); 128 checkRep(); 129 } 130 131 /** 132 * Construct the initial GameState, representing the starting board 133 * position. 134 */ 135 public GameState(Map<Position, Piece> powerupDiffs) 136 { 137 this(Color.WHITE, 0, Board.startingBoard); 138 for(Position p: powerupDiffs.keySet()) { 139 board.put(p, powerupDiffs.get(p)); 140 } 141 checkRep(); 142 } 143 144 public Board getBoard(){ 145 return board; 146 } 147 148 /** 149 * @returns A map from positions to pieces, representing the state of the 150 * board. 151 */ 152 public Map<Position, Piece> getPieceMap() 153 { 154 Map<Position, Piece> ans = new HashMap<Position, Piece>(); 155 for(Position pos: Position.ALL){ 156 ans.put(pos, board.get(pos)); 157 } 158 return Collections.unmodifiableMap(ans); 159 } 160 161 /** 162 * @returns The color of the player whose turn it is. 163 */ 164 public Color getCurrentColor() 165 { 166 return toMove; 167 } 168 169 /** 170 * @returns the Color who has lost all his pieces (or NONE) 171 */ 172 public Color piecesWinner() 173 { 174 boolean whiteLost = true, blackLost = true; 175 for(int x = 0; x < 8; x++){ 176 for(int y = 0; y < 8; y++){ 177 Piece p = board.get(x, y); 178 if (!(p instanceof King) && p.getColor() != Color.NONE){ 179 if (p.getColor() == Color.WHITE) { 180 whiteLost = false; 181 if (!blackLost) 182 break; 183 } else if (p.getColor() == Color.BLACK) { 184 blackLost = false; 185 if (!whiteLost) 186 break; 187 } 188 } 189 } 190 } 191 if (whiteLost) { 192 return Color.WHITE; 193 } else if (blackLost) { 194 return Color.BLACK; 195 } else { 196 return Color.NONE; 197 } 198 } 199 200 /** 201 * @returns The Color of the player who has checkmated his opponent (or NONE) 202 */ 203 public Color checkmater() 204 { 205 Position kingPos = kingPosition(toMove); 206 if (kingPos == null) { 207 System.out.println(board); 208 throw new RuntimeException("No " + toMove + " king?"); 209 } 210 211 if (isThreatened(kingPos, toMove.otherColor()) && !canMove()){ 212 return toMove.otherColor(); 213 } else { 214 return Color.NONE; 215 } 216 } 217 218 /** 219 * @returns The Color of the player who has won (or NONE if no player has 220 * won yet). 221 */ 222 public Color getWinner() 223 { 224 Color mater = checkmater(); 225 if (mater != Color.NONE) 226 { 227 return mater; 228 } 229 return piecesWinner(); 230 } 231 232 /** 233 * @returns The string representation of the board (mainly for debugging). 234 */ 235 public String toString() 236 { 237 return boardString(); 238 } 239 240 /** 241 * @returns the number of moves (by either player) since the beginning of 242 * the game. 243 */ 244 public int getMoveNum() 245 { 246 return moveNum; 247 } 248 249 /** 250 * @returns whether the current player has any move other than a pass. 251 */ 252 public boolean canMove() 253 { 254 Collection<Move> moves = legalMoves(); 255 if (moves.size() == 1) 256 { 257 for (Move m : moves) 258 { 259 if (m.isPass()) 260 { 261 return false; 262 } 263 } 264 } 265 return true; 266 } 267 268 private Collection<Move> getMoveCandidates(){ 269 Collection<Move> moveCandidates = new ArrayList<Move>(); 270 271 // Get all possibilities 272 for (Position pos: Position.ALL) { 273 Piece piece = board.get(pos); 274 if (piece.getColor() == toMove) { 275 moveCandidates.addAll(piece.moveCandidates(this, pos)); 276 } 277 } 278 return moveCandidates; 279 } 280 281 /** 282 * @returns The set of legal moves from the current position. Always has at 283 * least one move in this set. 284 */ 285 public Collection<Move> legalMoves() 286 { 287 if (legalMovesCache != null) 288 return legalMovesCache; 289 Collection<Move> result = new ArrayList<Move>(); 290 291 // Remove candidates that put us in check, 292 // filter based on ability to capture. 293 boolean canCapture = false; 294 for (Move m : getMoveCandidates()) 295 { 296 Map<Position, Piece> diff = m.toMap(this); 297 boolean thisIsCapture = reversibleMakeMove(diff, true); 298 if (!canCapture || thisIsCapture) 299 { 300 boolean invalid = invalidState(); 301 reversibleMakeMove(diff, false); 302 if (!invalid) 303 { 304 if (thisIsCapture == canCapture) 305 { 306 result.add(m); 307 } else if (!canCapture) 308 { // && thisIsCapture 309 canCapture = true; 310 result.clear(); 311 result.add(m); 312 } else 313 { // canCapture && !thisIsCapture 314 } 315 } 316 } else 317 { 318 reversibleMakeMove(diff, false); 319 } 320 } 321 if (result.size() == 0) 322 { 323 result.add(new Move()); // pass 324 } 325 legalMovesCache = result; 326 checkRep(); 327 return result; 328 } 329 330 /** 331 * Does a color threaten a position? (occupying does not count). 332 * 333 * @returns whether a given position is threatened by a color 334 * @requires pos != null && color != null 335 */ 336 public boolean isThreatened(Position pos, Color color) 337 { 338 return (StraightPiece.threatens(this, color, pos) || 339 Knight.threatens(this, color, pos) || 340 Pawn.threatens(this, color, pos) || 341 King.threatens(this, color, pos)); 342 } 343 344 /** 345 * @returns the Position of the king of a certain color (or null if that 346 * king died) 347 */ 348 private Position kingPosition(Color c) 349 { 350 for(int x = 0; x < 8; x++){ 351 for(int y = 0; y < 8; y++){ 352 Piece p = board.get(x, y); 353 if (p instanceof King && p.getColor() == c) 354 return Position.get(x, y); 355 } 356 } 357 return null; 358 } 359 360 /** 361 * Is the current state invalid, by the opposite color being in check? 362 * 363 * @returns whether the current state is invalid. 364 */ 365 public boolean invalidState() 366 { 367 Position kingPos = kingPosition(toMove.otherColor()); 368 if (kingPos == null) //destroy or upgrade can do this 369 return true; 370 return isThreatened(kingPos, toMove); 371 } 372 373 /** 374 * @returns Whether a move is legal. 375 * @requires m != null 376 */ 377 public boolean isLegal(Move m) 378 { 379 return legalMoves().contains(m); 380 } 381 382 public Collection<Piece> getPieces(){ 383 return board.getPieces(); 384 } 385 386 /** 387 * Make a move 388 * 389 * @modifies this 390 * @effects continues game by one move 391 * @requires m != null 392 */ 393 public void makeMove(Move m) 394 { 395 reversibleMakeMove(m.toMap(this), true); 396 } 397 398 /** 399 * Modify the AntichessGameState to apply a move, and turn the map into its 400 * inverse. 401 * 402 * @requires m != null 403 * @modifies this 404 * @modifies m 405 * @effects this goes moves forward or backward as defined by isForward by 406 * updating by the map m. 407 * @effects m changes to its inverse on the current board, so a subsequent 408 * call to reversibleMakeMove(m, !isForward) will revert the 409 * change. 410 * @returns whether this move involves a capture. 411 * @requires m != null 412 */ 413 public boolean reversibleMakeMove(Map<Position, Piece> m, boolean isForward) 414 { 415 Piece tmp; 416 boolean canCapture = false; 417 toMove = toMove.otherColor(); 418 legalMovesCache = null; 419 if (isForward) 420 { 421 moveNum++; 422 } else 423 { 424 moveNum--; 425 } 426 for (Map.Entry<Position, Piece> e : m.entrySet()) 427 { 428 tmp = board.get(e.getKey()); 429 board.put(e.getKey(), e.getValue()); 430 e.setValue(tmp); 431 if (tmp.getColor() == toMove) 432 { 433 canCapture = true; 434 } 435 } 436 checkRep(); 437 return canCapture; 438 } 439 440 /** 441 * Return the String representation of the board 442 */ 443 public String boardString() 444 { 445 return board.toString(); 446 } 447 448 public int hashCode(){ 449 int ans = 0; 450 for(int x = 0; x < 8; x++){ 451 for(int y = 0; y < 8; y++){ 452 Piece p = board.get(x, y); 453 ans ^= primeTable[p.hashCode()][x][y]; 454 } 455 } 456 ans = ans - ans % 100 + moveNum; 457 return ans; 458 } 459 }