chessai
college code for ai playing chess in java
git clone https://9o.is/git/chessai.git
ChessRules.java
(11611B)
1 package chess;
2
3 import chess.utils.ChessColor;
4 import chess.utils.ChessDirection;
5 import chess.utils.ChessType;
6
7 import java.util.Arrays;
8
9 /**
10 * Check
11 */
12 public enum ChessRules {
13
14 INSTANCE;
15
16 private ChessState state;
17 private ChessCoordinate src;
18 private ChessCoordinate dest;
19
20 private ChessPiece srcPiece;
21 private ChessPiece destPiece;
22 private ChessType srcType;
23
24 private boolean evaluating = false;
25 private boolean bPawnCanDoubleMove = true;
26 private boolean wPawnCanDoubleMove = true;
27
28 private boolean log = true;
29
30 public void setState(ChessState state) {
31 this.state = state;
32 }
33
34 public void setSrc(ChessCoordinate src) {
35 this.src = src;
36 this.srcPiece = !src.invalid() ?
37 ChessPiece.find(state.get(src)) : null;
38 this.srcType = srcPiece != null ? srcPiece.getType() : null;
39 }
40
41 public void setDest(ChessCoordinate dest) {
42 this.dest = dest;
43 this.destPiece = !dest.invalid() ?
44 ChessPiece.find(state.get(dest)) : null;
45 }
46
47 public void setLog(boolean log) {
48 this.log = log;
49 }
50
51 public void setEvaluating(boolean evaluating) {
52 this.evaluating = evaluating;
53 }
54
55 public void setbPawnCanDoubleMove(boolean bPawnCanDoubleMove) {
56 if(!evaluating)
57 this.bPawnCanDoubleMove = bPawnCanDoubleMove;
58 }
59
60 public void setwPawnCanDoubleMove(boolean wPawnCanDoubleMove) {
61 if(!evaluating)
62 this.wPawnCanDoubleMove = wPawnCanDoubleMove;
63 }
64
65 // CHECK
66 public void applyCaptureRule() {
67 // are the source and destination pieces different colors?
68 if(srcPiece.getColor() != destPiece.getColor() &&
69 srcPiece != ChessPiece.NA &&
70 destPiece != ChessPiece.NA) {
71
72 // CAPTURE!
73 // TODO handle player points here
74
75 System.out.println(ChessRules.class + ": CAPTURED -> " +
76 srcPiece + src + destPiece + dest);
77 }
78 }
79
80 // CHECK
81 public boolean validMove() {
82 return pieceMoves() &&
83 srcHasPiece() &&
84 insideBoard() &&
85 unoccupiedSpace() &&
86 legalSkip() &&
87 validRookMove() &&
88 validKnightMove() &&
89 validBishopMove() &&
90 validQueenMove() &&
91 validKingMove() &&
92 validPawnMove();
93 }
94
95 /*
96 * Checks if the piece moved. It cannot stay in same spot.
97 */
98 private boolean pieceMoves() {
99 if(src.equals(dest)) {
100 logFailure("pieceMoves");
101 return false;
102 }
103 return true;
104 }
105
106 /*
107 * Checks if source coordinate has a chess piece.
108 */
109 private boolean srcHasPiece() {
110 if(srcPiece == ChessPiece.NA) {
111 logFailure("srcHasPiece");
112 return false;
113 }
114 return true;
115 }
116
117 /*
118 * Checks if the destination coordinate is inside the chess
119 * board.
120 */
121 private boolean insideBoard() {
122 if(dest.invalid()) {
123 logFailure("insideBoard");
124 return false;
125 }
126 return true;
127 }
128
129 /*
130 * Checks if player moved a chess piece onto one of his own pieces.
131 */
132 private boolean unoccupiedSpace() {
133 // are the source and destination pieces same color?
134 if(srcPiece.getColor() == destPiece.getColor()) {
135 logFailure("unoccupiedSpace");
136 return false;
137 }
138 return true;
139 }
140
141 /*
142 * Checks if source piece skips over other pieces when moving.
143 */
144 private boolean legalSkip() {
145 // Knight is an exception.
146 if(srcType==ChessType.KNIGHT)
147 return true;
148
149 final int hor = dest.getFile() - src.getFile();
150 final int ver = dest.getRank() - src.getRank();
151
152 int rank = dest.getRank();
153 int file = dest.getFile();
154
155 if(hor != 0) {
156 if(hor > 0) file--;
157 else file++;
158 }
159 if(ver != 0) {
160 if(ver > 0) rank--;
161 else rank++;
162 }
163
164 while (src.getFile() != file || src.getRank() != rank) {
165
166 final byte pieceValue =
167 state.get(new ChessCoordinate((char)file, rank));
168 ChessPiece check = ChessPiece.find(pieceValue);
169
170 if(check != ChessPiece.NA) {
171 logFailure("legalSkip");
172 return false;
173 }
174
175 if(hor != 0) {
176 if(hor > 0) file--;
177 else file++;
178 }
179 if(ver != 0) {
180 if(ver > 0) rank--;
181 else rank++;
182 }
183 }
184 return true;
185 }
186
187 private boolean validRookMove(){
188 //Rank must be the same exclusive or file must be the
189 // same for a rook to move
190 if(srcType==ChessType.ROOK){
191 if(rookMove())
192 return true;
193
194 logFailure("validRookMove");
195 return false;
196 }
197 return true;
198 }
199
200 private boolean validKnightMove(){
201 if(srcType==ChessType.KNIGHT){
202 final int rankDiff = Math.abs(src.getRank()-dest.getRank());
203 final int fileDiff = Math.abs(src.getFile()-dest.getFile());
204
205 if((rankDiff == 2 && fileDiff == 1) ||
206 (rankDiff == 1 && fileDiff == 2)) {
207 return true;
208 }
209
210 logFailure("validKnightMove");
211 return false;
212 }
213 return true;
214 }
215
216 /*
217 * Pawns can also move 2 spaces on first turn
218 * Pawns can also capture en passant
219 * Still functional without these features though
220 */
221 private boolean validPawnMove(){
222 if(srcType==ChessType.PAWN){
223 //Pawn move up one
224 if (srcPiece.getColor() == ChessColor.WHITE){
225
226 if(src.getRank()+2 == dest.getRank() &&
227 src.getFile() == dest.getFile() &&
228 wPawnCanDoubleMove &&
229 destPiece == ChessPiece.NA) {
230 return true;
231 }
232
233 if(src.getRank()+1 == dest.getRank() &&
234 src.getFile() == dest.getFile() &&
235 destPiece == ChessPiece.NA){
236
237 setwPawnCanDoubleMove(false);
238 return true;
239 }
240
241 //Pawn capture
242 if(src.getRank()+1 == dest.getRank() &&
243 Math.abs(src.getFile()-dest.getFile()) == 1 &&
244 srcPiece.getColor() != destPiece.getColor() &&
245 destPiece != ChessPiece.NA) {
246
247 setwPawnCanDoubleMove(false);
248 return true;
249 }
250 } else {
251
252 if(src.getRank() == dest.getRank()+2 &&
253 src.getFile() == dest.getFile() &&
254 bPawnCanDoubleMove &&
255 destPiece == ChessPiece.NA) {
256 return true;
257 }
258
259 if(src.getRank() == dest.getRank()+1 &&
260 src.getFile() == dest.getFile()&&
261 destPiece == ChessPiece.NA) {
262
263 setbPawnCanDoubleMove(false);
264 return true;
265 }
266
267 //Pawn capture
268 if(src.getRank() == dest.getRank()+1 &&
269 Math.abs(src.getFile()-dest.getFile()) == 1 &&
270 srcPiece.getColor() != destPiece.getColor() &&
271 destPiece != ChessPiece.NA){
272
273 setbPawnCanDoubleMove(false);
274 return true;
275 }
276 }
277 logFailure("validPawnMove");
278 return false;
279 }
280 return true;
281 }
282
283 private boolean validBishopMove(){
284 if(srcType==ChessType.BISHOP){
285 if(bishopMove())
286 return true;
287
288 logFailure("validBishopMove");
289 return false;
290 }
291 return true;
292 }
293
294 private boolean validKingMove(){
295 if(srcType==ChessType.KING){
296 if(src.getRank()-dest.getRank() < 2 &&
297 src.getFile()-dest.getFileNumber() < 2) {
298 return true;
299 }
300
301 logFailure("validKingMove");
302 // TODO Moves that get the king captured are invalid
303 return false;
304 }
305 return true;
306 }
307
308 private boolean validQueenMove() {
309 if(srcType == ChessType.QUEEN){
310 if(!(rookMove() ^ bishopMove())) {
311 logFailure("validQueenMove");
312 return false;
313 }
314 }
315 return true;
316 }
317
318 public ChessDirection[] possibleDirections() {
319 if(srcType==ChessType.PAWN) return pawnDirections();
320 if(srcType==ChessType.ROOK) return rookDirections();
321 if(srcType==ChessType.BISHOP) return bishopDirections();
322 if(srcType==ChessType.KNIGHT) return knightDirections();
323 if(srcType==ChessType.QUEEN) return queenDirections();
324 if(srcType==ChessType.KING) return kingDirections();
325 else
326 return new ChessDirection[]{};
327 }
328
329 private ChessDirection[] pawnDirections() {
330 return new ChessDirection[] {
331 ChessDirection.FORWARD,
332 ChessDirection.FORWARD_LEFT,
333 ChessDirection.FORWARD_RIGHT
334 };
335 }
336
337 private ChessDirection[] rookDirections() {
338 return new ChessDirection[] {
339 ChessDirection.FORWARD,
340 ChessDirection.BACK,
341 ChessDirection.LEFT,
342 ChessDirection.RIGHT
343 };
344 }
345
346 private ChessDirection[] bishopDirections() {
347 return new ChessDirection[] {
348 ChessDirection.FORWARD_LEFT,
349 ChessDirection.FORWARD_RIGHT,
350 ChessDirection.BACK_LEFT,
351 ChessDirection.BACK_RIGHT
352 };
353 }
354
355 private ChessDirection[] knightDirections() {
356 return new ChessDirection[] {
357 ChessDirection.KNIGHT_LEFT_FORWARD,
358 ChessDirection.KNIGHT_LEFT_BACK,
359 ChessDirection.KNIGHT_RIGHT_FORWARD,
360 ChessDirection.KNIGHT_RIGHT_BACK,
361 ChessDirection.KNIGHT_FORWARD_LEFT,
362 ChessDirection.KNIGHT_FORWARD_RIGHT,
363 ChessDirection.KNIGHT_BACK_LEFT,
364 ChessDirection.KNIGHT_BACK_RIGHT
365 };
366 }
367
368 private ChessDirection[] queenDirections() {
369 return concat(bishopDirections(), rookDirections());
370 }
371
372 private ChessDirection[] kingDirections() {
373 return queenDirections();
374 }
375
376 private boolean rookMove() {
377 return src.getRank() == dest.getRank() ^
378 src.getFile() == dest.getFile();
379 }
380
381 private boolean bishopMove() {
382 final int rankDiff = Math.abs(src.getRank()-dest.getRank());
383 final int fileDiff = Math.abs(src.getFile()-dest.getFile());
384
385 return rankDiff == fileDiff;
386 }
387
388 private void logFailure(String method) {
389 if(log)
390 System.err.println(ChessRules.class + ": FAILURE -> "+
391 method+" test --- Move: "+
392 srcPiece+src+dest);
393 }
394
395 // TODO where should this be placed?
396 private static <T> T[] concat(T[] first, T[] second) {
397 T[] result = Arrays.copyOf(first, first.length + second.length);
398 System.arraycopy(second, 0, result, first.length, second.length);
399 return result;
400 }
401 }