1 /** 2 * The chess framework library. 3 * More information is available at http://www.jinchess.com/. 4 * Copyright (C) 2002 Alexander Maryanovsky. 5 * All rights reserved. 6 * 7 * The chess framework library is free software; you can redistribute 8 * it and/or modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation; either version 2 of the 10 * License, or (at your option) any later version. 11 * 12 * The chess framework library is distributed in the hope that it will 13 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with the chess framework library; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22 package free.chess.variants.fischerrandom; 23 24 import free.chess.*; 25 26 27 /** 28 * Implements the Fischer Random wild variant. Quoting an ICC help file: 29 * <PRE> 30 * The usual set of pieces is arranged randomly 31 * on the first and eighth ranks, with bishops on opposite colors, and the 32 * king between the two rooks, and Black's arrangement a mirror of White's. 33 * Castling O-O puts the king at g1 (g8 for Black), the rook at f1 (f8). 34 * Castling O-O-O puts the king at c1 (c8), rook at d1 (d8). 35 * </PRE> 36 * More information is available at 37 * <A HREF="http://www.chessclub.com/help/Fischer-random">http://www.chessclub.com/help/Fischer-random</A> 38 */ 39 40 public class FischerRandom extends ChesslikeGenericVariant{ 41 42 43 44 /** 45 * The sole instance of this class. 46 */ 47 48 private static final FischerRandom INSTANCE = new FischerRandom(); 49 50 51 52 /** 53 * Returns an instance of this class. 54 */ 55 getInstance()56 public static FischerRandom getInstance(){ 57 return INSTANCE; 58 } 59 60 61 62 /** 63 * Creates an instance of FischerRandom. 64 */ 65 FischerRandom()66 private FischerRandom(){ 67 super(Chess.INITIAL_POSITION_FEN /* Not used anyway */, "Fischer random"); 68 } 69 70 71 72 73 /** 74 * Returns <code>true</code> if the move defined by the given arguments is a 75 * short castling move according to the rules of the "fischer random" variant. 76 * Returns <code>false</code> otherwise. The result for an illegal move is 77 * undefined, but it should throw no exceptions. 78 */ 79 isShortCastling(Position pos, Square startingSquare, Square endingSquare, ChessPiece promotionTarget)80 public boolean isShortCastling(Position pos, Square startingSquare, 81 Square endingSquare, ChessPiece promotionTarget){ 82 83 if (promotionTarget!=null) 84 return false; 85 86 ChessPiece movingPiece = (ChessPiece)pos.getPieceAt(startingSquare); 87 ChessPiece takenPiece = (ChessPiece)pos.getPieceAt(endingSquare); 88 89 if (movingPiece == ChessPiece.WHITE_KING){ 90 if (startingSquare.getRank()!=0) 91 return false; 92 else if ((takenPiece!=null)&&(takenPiece!=ChessPiece.WHITE_ROOK)) 93 return false; 94 else if (!endingSquare.equals("g1")) 95 return false; 96 else if (startingSquare.equals("f1")){ 97 if (takenPiece == ChessPiece.WHITE_ROOK) 98 return true; 99 else 100 return false; // This *could* be a castling, but there is currently 101 // no way to indicate whether it is one. 102 } 103 104 int rank = startingSquare.getRank(); 105 int file = startingSquare.getFile()+1; 106 while (file<=7){ 107 Piece piece = pos.getPieceAt(file, rank); 108 if (piece != null){ 109 if (piece == ChessPiece.WHITE_ROOK){ 110 if ((takenPiece == ChessPiece.WHITE_ROOK)&&(!endingSquare.equals(file, rank))) 111 return false; 112 else 113 return true; 114 } 115 else 116 return false; 117 } 118 file++; 119 } 120 } 121 else if (movingPiece == ChessPiece.BLACK_KING){ 122 if (startingSquare.getRank()!=7) 123 return false; 124 else if ((takenPiece!=null)&&(takenPiece!=ChessPiece.BLACK_ROOK)) 125 return false; 126 else if (!endingSquare.equals("g8")) 127 return false; 128 else if (startingSquare.equals("f8")){ 129 if (takenPiece == ChessPiece.BLACK_ROOK) 130 return true; 131 else 132 return false; // This *could* be a castling, but there is currently 133 // no way to indicate whether it is one. 134 } 135 136 int rank = startingSquare.getRank(); 137 int file = startingSquare.getFile()+1; 138 while (file<=7){ 139 Piece piece = pos.getPieceAt(file, rank); 140 if (piece != null){ 141 if (piece == ChessPiece.BLACK_ROOK){ 142 if ((takenPiece == ChessPiece.BLACK_ROOK)&&(!endingSquare.equals(file, rank))) 143 return false; 144 else 145 return true; 146 } 147 else 148 return false; 149 } 150 file++; 151 } 152 } 153 154 return false; 155 } 156 157 158 159 160 /** 161 * Returns <code>true</code> if the move defined by the given arguments is a 162 * long castling move. Returns <code>false</code> otherwise. The result for 163 * an illegal move is undefined, but it should throw no exceptions. 164 */ 165 isLongCastling(Position pos, Square startingSquare, Square endingSquare, ChessPiece promotionTarget)166 public boolean isLongCastling(Position pos, Square startingSquare, 167 Square endingSquare, ChessPiece promotionTarget){ 168 169 if (promotionTarget!=null) 170 return false; 171 172 ChessPiece movingPiece = (ChessPiece)pos.getPieceAt(startingSquare); 173 ChessPiece takenPiece = (ChessPiece)pos.getPieceAt(endingSquare); 174 175 if (movingPiece == ChessPiece.WHITE_KING){ 176 if (startingSquare.getRank()!=0) 177 return false; 178 else if ((takenPiece!=null)&&(takenPiece!=ChessPiece.WHITE_ROOK)) 179 return false; 180 else if (!endingSquare.equals("c1")) 181 return false; 182 else if (startingSquare.equals("b1")) 183 return false; // This *could* be a castling, but there is currently 184 // no way to indicate whether it is one. 185 else if (startingSquare.equals("d1")){ 186 if (takenPiece == ChessPiece.WHITE_ROOK) 187 return true; 188 else 189 return false; // This *could* be a castling, but there is currently 190 // no way to indicate whether it is one. 191 } 192 193 int rank = startingSquare.getRank(); 194 int file = startingSquare.getFile()-1; 195 while (file>=0){ 196 Piece piece = pos.getPieceAt(file, rank); 197 if (piece != null){ 198 if (piece == ChessPiece.WHITE_ROOK){ 199 if ((takenPiece == ChessPiece.WHITE_ROOK)&&(!endingSquare.equals(file, rank))) 200 return false; 201 else 202 return true; 203 } 204 else 205 return false; 206 } 207 file--; 208 } 209 } 210 else if (movingPiece == ChessPiece.BLACK_KING){ 211 if (startingSquare.getRank()!=7) 212 return false; 213 else if ((takenPiece!=null)&&(takenPiece!=ChessPiece.BLACK_ROOK)) 214 return false; 215 else if (!endingSquare.equals("c8")) 216 return false; 217 else if (startingSquare.equals("b8")) 218 return false; // This *could* be a castling, but there is currently 219 // no way to indicate whether it is one. 220 else if (startingSquare.equals("d8")){ 221 if (takenPiece == ChessPiece.BLACK_ROOK) 222 return true; 223 else 224 return false; // This *could* be a castling, but there is currently 225 // no way to indicate whether it is one. 226 } 227 228 int rank = startingSquare.getRank(); 229 int file = startingSquare.getFile()-1; 230 while (file>=0){ 231 Piece piece = pos.getPieceAt(file, rank); 232 if (piece != null){ 233 if (piece == ChessPiece.BLACK_ROOK){ 234 if ((takenPiece == ChessPiece.BLACK_ROOK)&&(!endingSquare.equals(file, rank))) 235 return false; 236 else 237 return true; 238 } 239 else 240 return false; 241 } 242 file--; 243 } 244 } 245 246 return false; 247 } 248 249 250 251 /** 252 * Creates a short castling move for the current player in the specified 253 * position. Short castling must be legal in the specified position. 254 */ 255 createShortCastling(Position pos)256 public Move createShortCastling(Position pos){ 257 checkPosition(pos); 258 259 Player currentPlayer = pos.getCurrentPlayer(); 260 if (currentPlayer.isWhite()){ 261 Square startSquare = findPieceOnRow(pos, ChessPiece.WHITE_KING, 0); 262 if (startSquare == null) 263 throw new IllegalArgumentException("Castling is not allowed in the specified position"); 264 265 return new ChessMove(startSquare, Square.parseSquare("g1"), Player.WHITE_PLAYER, 266 false, true, false, null, -1, null, "O-O"); 267 } 268 else{ 269 Square startSquare = findPieceOnRow(pos, ChessPiece.BLACK_KING, 7); 270 if (startSquare == null) 271 throw new IllegalArgumentException("Castling is not allowed in the specified position"); 272 273 return new ChessMove(startSquare, Square.parseSquare("g8"), Player.BLACK_PLAYER, 274 false, true, false, null, -1, null, "O-O"); 275 } 276 } 277 278 279 280 281 /** 282 * Creates a long castling move for the current player in the specified 283 * position. Long castling must be legal in the specified position. 284 */ 285 createLongCastling(Position pos)286 public Move createLongCastling(Position pos){ 287 checkPosition(pos); 288 289 Player currentPlayer = pos.getCurrentPlayer(); 290 if (currentPlayer.isWhite()){ 291 Square startSquare = findPieceOnRow(pos, ChessPiece.WHITE_KING, 0); 292 if (startSquare == null) 293 throw new IllegalArgumentException("Castling is not allowed in the specified position"); 294 295 return new ChessMove(startSquare, Square.parseSquare("c1"), Player.WHITE_PLAYER, 296 false, false, true, null, -1, null, "O-O"); 297 } 298 else{ 299 Square startSquare = findPieceOnRow(pos, ChessPiece.BLACK_KING, 7); 300 if (startSquare == null) 301 throw new IllegalArgumentException("Castling is not allowed in the specified position"); 302 303 return new ChessMove(startSquare, Square.parseSquare("c8"), Player.BLACK_PLAYER, 304 false, false, true, null, -1, null, "O-O"); 305 } 306 } 307 308 309 310 /** 311 * Returns the square of the specified piece on the specified row, or null if 312 * the specified piece is not on the specified row. 313 */ 314 findPieceOnRow(Position pos, Piece piece, int rank)315 private static Square findPieceOnRow(Position pos, Piece piece, int rank){ 316 for (int i = 0; i < 8; i++) 317 if (piece.equals(pos.getPieceAt(i, rank))) 318 return Square.getInstance(i, rank); 319 320 return null; 321 } 322 323 324 325 /** 326 * Initializes the given position to a random state subject to the constraints 327 * specified in the rules. 328 * 329 * @throws IllegalArgumentException If the given Position's wild variant is 330 * not FischerRandom. 331 */ 332 init(Position pos)333 public void init(Position pos){ 334 checkPosition(pos); 335 336 pos.setFEN(createRandomInitialFEN()); 337 } 338 339 340 341 342 /** 343 * Creates a random initial position subject to the constraints specified in 344 * the rules of Fischer Random. The position is encoded and returned in 345 * FEN format. 346 */ 347 createRandomInitialFEN()348 private static String createRandomInitialFEN(){ 349 StringBuffer pieces = new StringBuffer("--------"); 350 351 int pos; 352 353 // First bishop 354 while(true){ 355 pos = randomInt(8); 356 if ((pos%2 == 0) && (pieces.charAt(pos) == '-')){ 357 pieces.setCharAt(pos, 'B'); 358 break; 359 } 360 } 361 362 // 2nd bishop 363 while(true){ 364 pos = randomInt(8); 365 if ((pos%2 == 1) && (pieces.charAt(pos) == '-')){ 366 pieces.setCharAt(pos, 'B'); 367 break; 368 } 369 } 370 371 // 1st knight 372 while (true){ 373 pos = randomInt(8); 374 if (pieces.charAt(pos) == '-'){ 375 pieces.setCharAt(pos, 'N'); 376 break; 377 } 378 } 379 380 // 2nd knight 381 while (true){ 382 pos = randomInt(8); 383 if (pieces.charAt(pos) == '-'){ 384 pieces.setCharAt(pos, 'N'); 385 break; 386 } 387 } 388 389 // queen 390 while (true){ 391 pos = randomInt(8); 392 if (pieces.charAt(pos) == '-'){ 393 pieces.setCharAt(pos, 'Q'); 394 break; 395 } 396 } 397 398 // 1st rook 399 pos = 0; 400 while (pos < 6){ 401 if (pieces.charAt(pos) == '-') 402 break; 403 pos++; 404 } 405 pieces.setCharAt(pos++, 'R'); 406 407 // king 408 while (pos < 7){ 409 if (pieces.charAt(pos) == '-') 410 break; 411 pos++; 412 } 413 pieces.setCharAt(pos++, 'K'); 414 415 // 2nd rook 416 while (pos < 8){ 417 if (pieces.charAt(pos)=='-') 418 break; 419 pos++; 420 } 421 pieces.setCharAt(pos, 'R'); 422 423 String whitePieces = pieces.toString(); 424 String blackPieces = whitePieces.toLowerCase(); 425 426 return blackPieces + "/pppppppp/8/8/8/8/PPPPPPPP/" + whitePieces + " w KQkq - 0 1"; 427 } 428 429 430 431 432 /** 433 * Returns a random int, in the range [0..max). This method is used 434 * when creating the initial, random position. 435 */ 436 randomInt(int max)437 private static int randomInt(int max){ 438 return (int)(Math.random()*max); 439 } 440 441 442 443 /** 444 * Makes the given ChessMove on the given position. 445 */ 446 makeMove(Move move, Position pos, Position.Modifier modifier)447 public void makeMove(Move move, Position pos, Position.Modifier modifier){ 448 checkPosition(pos); 449 450 if (!(move instanceof ChessMove)) 451 throw new IllegalArgumentException("The given move must be an instance of "+ChessMove.class.getName()); 452 453 ChessMove cmove = (ChessMove)move; 454 Square startingSquare = cmove.getStartingSquare(); 455 Square endingSquare = cmove.getEndingSquare(); 456 ChessPiece movingPiece = (ChessPiece)pos.getPieceAt(startingSquare); 457 458 if (cmove.isCastling()){ 459 int dir = cmove.isShortCastling() ? 1 : -1; 460 int file = startingSquare.getFile() + dir; 461 int rank = startingSquare.getRank(); 462 463 while ((file >= 0) && (file <= 7)){ 464 ChessPiece piece = (ChessPiece)pos.getPieceAt(file, rank); 465 if (piece!=null){ 466 if (!piece.isRook() || !piece.isSameColorAs(movingPiece)) 467 throw new IllegalArgumentException("The given move may not be a castling move"); 468 else 469 break; 470 } 471 file+=dir; 472 } 473 474 int rookStartFile = file; 475 int rookEndFile = cmove.isShortCastling() ? 5 : 3; 476 477 Square rookStartingSquare = Square.getInstance(rookStartFile, startingSquare.getRank()); 478 Square rookEndingSquare = Square.getInstance(rookEndFile, startingSquare.getRank()); 479 ChessPiece rook = (ChessPiece)pos.getPieceAt(rookStartingSquare); 480 481 modifier.setPieceAt(null, startingSquare); 482 modifier.setPieceAt(null, rookStartingSquare); 483 484 modifier.setPieceAt(movingPiece, endingSquare); 485 modifier.setPieceAt(rook, rookEndingSquare); 486 487 modifier.setCurrentPlayer(pos.getCurrentPlayer().getOpponent()); 488 } 489 else 490 super.makeMove(move, pos, modifier); 491 } 492 493 } 494