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