├── .github └── workflows │ └── codeql-analysis.yml ├── README.md ├── SECURITY.md ├── pom.xml └── src ├── main └── java │ ├── Engine.java │ ├── Timer.java │ ├── commons │ ├── Color.java │ ├── LegalMoves.java │ ├── Line.java │ ├── Piece.java │ └── Utils.java │ ├── game │ ├── Board.java │ ├── Cell.java │ └── Move.java │ └── pieces │ ├── Bishop.java │ ├── King.java │ ├── Knight.java │ ├── Pawn.java │ ├── PieceType.java │ ├── Queen.java │ └── Rook.java └── test └── java ├── BishopTest.java ├── BoardTest.java ├── EngineTest.java ├── IntenseTest.java ├── KingTest.java ├── KnightTest.java ├── MinMaxTest.java ├── PawnTest.java ├── QueenTest.java └── RookTest.java /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '28 15 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'java' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chess-engine 2 | 3 | ![Chess Move Generator Tests](https://pbs.twimg.com/media/E_n_APbVEAIHYAw?format=jpg&name=large) 4 | 5 | ## Part 1: 6 | 7 | The chess engine can generate all legal moves for a given position upto an arbitrary depth. 8 | This includes: Move Engine 9 | ``` 10 | -- pawn promotions 11 | -- enpassant moves 12 | -- castling 13 | ``` 14 | 15 | The moves which are marked illegal are: 16 | ``` 17 | -- x-rays to the king 18 | -- castling through check 19 | -- moves of a piece pinned to the king 20 | -- moving into check 21 | -- enpassant after one move 22 | ``` 23 | 24 | The current move generator takes about 2 minutes to go to a depth of 6 ply. 25 | 26 | 27 | ## Part 2: Game Engine (In progress) 28 | 29 | The game engine will use the move generator to select a move. The first version will be using a min-max tree with optimisations to find the best move. 30 | 31 | This includes: 32 | ``` 33 | -- basic heuristic ✅ 34 | -- iterative deepening ✅ 35 | -- alpha-beta pruning ✅ 36 | -- killer heuristic 37 | -- quiescence search 38 | -- null heuristic 39 | -- parallel alpha-beta 40 | ``` 41 | 42 | ## Part 3: Stochastic Chess Engine (In planning) 43 | 44 | The second version of the chess engine will use Monte Carlo Tree Search with basic hueristics. 45 | 46 | I do not hope much from it, since I don't have experience with neural network training and the compute power required to train NNs is usually high (I have a GPU, so not all hope lost). 47 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | chess 8 | gkcs-chess-engine 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 11 17 | 11 18 | 19 | 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | 4.13.1 27 | test 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/Engine.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import game.Move; 4 | 5 | import java.util.Comparator; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.CompletableFuture; 10 | import java.util.concurrent.ExecutionException; 11 | import java.util.concurrent.TimeUnit; 12 | import java.util.concurrent.TimeoutException; 13 | import java.util.stream.Collectors; 14 | 15 | public class Engine { 16 | public static int nodesEvaluated; 17 | public Map transpositionTable = new HashMap<>(); 18 | 19 | public int countAllMoves(final Board board, final int depth) { 20 | return countAllMoves(board, depth, 1000); 21 | } 22 | 23 | public int countAllMoves(final Board board, final int depth, final int printAt) { 24 | final List legalMoves = board.getLegalMoves(); 25 | if (legalMoves.isEmpty()) { 26 | return 0; 27 | } 28 | if (depth == 1) { 29 | return legalMoves.size(); 30 | } 31 | return legalMoves.stream().mapToInt(move -> { 32 | final Board copy = board.copy(); 33 | copy.makeMove(move); 34 | final int countAllMoves = countAllMoves(copy, depth - 1, printAt); 35 | if (depth == printAt) { 36 | System.out.println(getString(move) + ": " + countAllMoves + " " + move + 37 | " " + copy.fenRepresentation()); 38 | } 39 | return countAllMoves; 40 | }).sum(); 41 | } 42 | 43 | public Evaluation minMax(final Board board, final int depth, final int printAt) { 44 | final List legalMoves = board.getLegalMoves(); 45 | nodesEvaluated++; 46 | if (legalMoves.isEmpty() || depth == 0) { 47 | return new Evaluation(null, -board.evaluation(legalMoves.size())); 48 | } 49 | Evaluation bestMove = null; 50 | for (Move move : legalMoves) { 51 | final Board copy = board.copy(); 52 | copy.makeMove(move); 53 | final Evaluation eval = minMax(copy, depth - 1, printAt); 54 | if (depth == printAt) { 55 | System.out.println(getString(move) + ": " + eval.getScore() + " " + move + 56 | " " + copy.fenRepresentation()); 57 | } 58 | if (bestMove == null || bestMove.getScore() > -eval.getScore()) { 59 | bestMove = new Evaluation(move, -eval.getScore()); 60 | if (bestMove.getScore() < 0 && bestMove.getScore() + Integer.MAX_VALUE < 0.0001) { 61 | return bestMove; 62 | } 63 | } 64 | } 65 | return bestMove; 66 | } 67 | 68 | public OutCome alphaBeta(final Board board, final int depth, double alpha, double beta, final int printAt) { 69 | final List legalMoves = board.getLegalMoves(); 70 | nodesEvaluated++; 71 | if (legalMoves.isEmpty() || depth == 0) { 72 | final double score; 73 | if (transpositionTable.containsKey(board.zobristHash)) { 74 | score = transpositionTable.get(board.zobristHash).eval; 75 | } else { 76 | score = board.evaluation(legalMoves.size()); 77 | transpositionTable.put(board.zobristHash, new Result(score, 0)); 78 | } 79 | return new OutCome(board, null, -score); 80 | } 81 | final List outComes = legalMoves.stream().map(move -> { 82 | final Board changedBoard = board.copy(); 83 | changedBoard.makeMove(move); 84 | final double score; 85 | if (transpositionTable.containsKey(changedBoard.zobristHash)) { 86 | score = transpositionTable.get(changedBoard.zobristHash).eval; 87 | } else { 88 | score = changedBoard.evaluation(changedBoard.getLegalMoves().size()); 89 | transpositionTable.put(changedBoard.zobristHash, new Result(score, 0)); 90 | } 91 | return new OutCome(changedBoard, move, score); 92 | }).sorted(Comparator.comparingDouble(OutCome::getScore)).collect(Collectors.toList()); 93 | OutCome bestOutCome = null; 94 | for (final OutCome outCome : outComes) { 95 | final OutCome eval = alphaBeta(outCome.getBoard(), depth - 1, alpha, beta, printAt); 96 | if (depth == printAt) { 97 | System.out.println(getString(outCome.getMove()) + ": " + eval.getScore() + " " + outCome + 98 | " " + outCome.getBoard().fenRepresentation()); 99 | } 100 | if (bestOutCome == null || bestOutCome.getScore() > -eval.getScore()) { 101 | bestOutCome = new OutCome(board, outCome.getMove(), -eval.getScore()); 102 | if (bestOutCome.getScore() < 0 && bestOutCome.getScore() + Integer.MAX_VALUE < 0.0001) { 103 | return bestOutCome; 104 | } 105 | } 106 | if (board.playerToMove.equals(Color.WHITE)) { 107 | if (alpha < -bestOutCome.getScore()) { 108 | alpha = -bestOutCome.getScore(); 109 | } 110 | } else { 111 | if (beta > bestOutCome.getScore()) { 112 | beta = bestOutCome.getScore(); 113 | } 114 | } 115 | if (alpha >= beta) { 116 | break; 117 | } 118 | } 119 | if (!transpositionTable.containsKey(board.zobristHash) || transpositionTable.get(board.zobristHash).depth < depth) { 120 | transpositionTable.put(board.zobristHash, new Result(-bestOutCome.getScore(), depth)); 121 | } 122 | return bestOutCome; 123 | } 124 | 125 | public OutCome iterativeDeepening(final Board board, final long time) { 126 | transpositionTable = new HashMap<>(); 127 | final long start = System.currentTimeMillis(); 128 | int depth = 1; 129 | OutCome evaluation = alphaBeta(board, depth, Integer.MIN_VALUE, Integer.MAX_VALUE, 1000); 130 | while (System.currentTimeMillis() - start < time * 1000 && Math.abs(evaluation.getScore()) - Integer.MAX_VALUE < 0.0001) { 131 | depth++; 132 | System.out.println("DEPTH: " + depth + " EVAL: " + evaluation + " TIME: " + ((System.currentTimeMillis() - start) / 1000)); 133 | try { 134 | int finalDepth = depth; 135 | evaluation = CompletableFuture.supplyAsync(() -> alphaBeta(board, finalDepth, Integer.MIN_VALUE, Integer.MAX_VALUE, 1000)).get(time * 1000 - (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS); 136 | } catch (InterruptedException | ExecutionException | TimeoutException e) { 137 | System.out.println("TIMEOUT: " + depth); 138 | break; 139 | } 140 | } 141 | return evaluation; 142 | } 143 | 144 | private String getString(Move move) { 145 | return move.piece.position.notation() + move.target.notation(); 146 | } 147 | } 148 | 149 | class Evaluation { 150 | private final Move move; 151 | private final double score; 152 | 153 | public Evaluation(Move move, double score) { 154 | this.move = move; 155 | this.score = score; 156 | } 157 | 158 | public double getScore() { 159 | return score; 160 | } 161 | 162 | public Move getMove() { 163 | return move; 164 | } 165 | 166 | @Override 167 | public String toString() { 168 | return "{" + 169 | "move=" + move + 170 | ", score=" + score + 171 | '}'; 172 | } 173 | } 174 | 175 | class OutCome { 176 | private final Board board; 177 | private final Move move; 178 | private final double score; 179 | 180 | public OutCome(Board board, Move move, double score) { 181 | this.move = move; 182 | this.score = score; 183 | this.board = board; 184 | } 185 | 186 | public double getScore() { 187 | return score; 188 | } 189 | 190 | public Move getMove() { 191 | return move; 192 | } 193 | 194 | public Board getBoard() { 195 | return board; 196 | } 197 | 198 | @Override 199 | public String toString() { 200 | return "{" + 201 | "board=\n" + board + 202 | ", move=" + move + 203 | ", score=" + score + 204 | '}'; 205 | } 206 | } 207 | 208 | 209 | class Result { 210 | final double eval; 211 | final int depth; 212 | 213 | public Result(double eval, int depth) { 214 | this.eval = eval; 215 | this.depth = depth; 216 | } 217 | } -------------------------------------------------------------------------------- /src/main/java/Timer.java: -------------------------------------------------------------------------------- 1 | public class Timer { 2 | private long currentTime = System.nanoTime(); 3 | 4 | public long getTimeElapsed() { 5 | final long start = currentTime; 6 | currentTime = System.nanoTime(); 7 | return currentTime - start; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/commons/Color.java: -------------------------------------------------------------------------------- 1 | package commons; 2 | 3 | public enum Color { 4 | WHITE, BLACK; 5 | 6 | public static Color opponent(Color playerToMove) { 7 | return playerToMove == BLACK ? WHITE : BLACK; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/commons/LegalMoves.java: -------------------------------------------------------------------------------- 1 | package commons; 2 | 3 | import game.Move; 4 | 5 | import java.util.List; 6 | import java.util.Set; 7 | 8 | public class LegalMoves { 9 | public final Set moves; 10 | public final Set guards; 11 | 12 | public LegalMoves(Set moves, Set guards) { 13 | this.moves = moves; 14 | this.guards = guards; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/commons/Line.java: -------------------------------------------------------------------------------- 1 | package commons; 2 | 3 | import game.Cell; 4 | import pieces.PieceType; 5 | 6 | public class Line { 7 | public final int rowDiff; 8 | public final int colDiff; 9 | public final boolean isStraight; 10 | public final PieceType minorPieceType; 11 | 12 | public Line(final Cell first, final Cell second) { 13 | int rowDistance = Math.abs(first.row - second.row); 14 | int colDistance = Math.abs(first.col - second.col); 15 | isStraight = rowDistance == 0 || colDistance == 0 || rowDistance == colDistance; 16 | rowDiff = Integer.compare(first.row, second.row); 17 | colDiff = Integer.compare(first.col, second.col); 18 | minorPieceType = rowDiff == 0 || colDiff == 0 ? PieceType.ROOK : PieceType.BISHOP; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/commons/Piece.java: -------------------------------------------------------------------------------- 1 | package commons; 2 | 3 | import game.Board; 4 | import game.Cell; 5 | import game.Move; 6 | import pieces.*; 7 | 8 | import java.util.List; 9 | import java.util.Objects; 10 | import java.util.Set; 11 | 12 | public class Piece { 13 | public final Color color; 14 | public final Cell position; 15 | public final PieceType pieceType; 16 | private static final Piece[][][] pieces = getPieces(); 17 | 18 | private static Piece[][][] getPieces() { 19 | final Piece[][][] pieces = new Piece[2][64][6]; 20 | final Color[] colors = Color.values(); 21 | final PieceType[] pieceTypes = PieceType.values(); 22 | for (int i = 0; i < 2; i++) { 23 | for (int j = 0; j < 64; j++) { 24 | for (int k = 0; k < 6; k++) { 25 | pieces[i][j][k] = new Piece(colors[i], Cell.get((j >> 3), j & 7), pieceTypes[k]); 26 | } 27 | } 28 | } 29 | return pieces; 30 | } 31 | 32 | public static Piece get(Color color, Cell position, PieceType pieceType) { 33 | return pieces[color.ordinal()][(position.row << 3) + position.col][pieceType.ordinal()]; 34 | } 35 | 36 | private Piece(Color color, Cell position, PieceType pieceType) { 37 | this.color = color; 38 | this.position = position; 39 | this.pieceType = pieceType; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object o) { 44 | if (this == o) return true; 45 | if (o == null || getClass() != o.getClass()) return false; 46 | Piece piece = (Piece) o; 47 | return color == piece.color && position.equals(piece.position) && pieceType == piece.pieceType; 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hash(color, position, pieceType); 53 | } 54 | 55 | public Set getMoveList(Board board) { 56 | switch (pieceType) { 57 | case BISHOP: 58 | return Bishop.getMoveList(board, this).moves; 59 | case KNIGHT: 60 | return Knight.getMoveList(board, this).moves; 61 | case ROOK: 62 | return Rook.getMoveList(board, this).moves; 63 | case KING: 64 | return King.getMoveList(board, this).moves; 65 | case QUEEN: 66 | return Queen.getMoveList(board, this).moves; 67 | case PAWN: 68 | return Pawn.getMoveList(board, this).moves; 69 | default: 70 | throw new IllegalStateException(); 71 | } 72 | } 73 | 74 | public LegalMoves getLegalMoves(Board board) { 75 | switch (pieceType) { 76 | case BISHOP: 77 | return Bishop.getMoveList(board, this); 78 | case KNIGHT: 79 | return Knight.getMoveList(board, this); 80 | case ROOK: 81 | return Rook.getMoveList(board, this); 82 | case KING: 83 | return King.getMoveList(board, this); 84 | case QUEEN: 85 | return Queen.getMoveList(board, this); 86 | case PAWN: 87 | return Pawn.getMoveList(board, this); 88 | default: 89 | throw new IllegalStateException(); 90 | } 91 | } 92 | 93 | public String getShortForm() { 94 | switch (pieceType) { 95 | case BISHOP: 96 | return color == Color.WHITE ? "♝" : "♗"; 97 | case KNIGHT: 98 | return color == Color.WHITE ? "♞" : "♘"; 99 | case ROOK: 100 | return color == Color.WHITE ? "♜" : "♖"; 101 | case KING: 102 | return color == Color.WHITE ? "♚" : "♔"; 103 | case QUEEN: 104 | return color == Color.WHITE ? "♛" : "♕"; 105 | case PAWN: 106 | return color == Color.WHITE ? "♟" : "♙"; 107 | default: 108 | throw new IllegalStateException(); 109 | } 110 | } 111 | 112 | 113 | @Override 114 | public String toString() { 115 | return "(" + getShortForm() + 116 | ',' + position + 117 | ')'; 118 | } 119 | 120 | public boolean sameType(PieceType pieceType) { 121 | return pieceType == this.pieceType; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/commons/Utils.java: -------------------------------------------------------------------------------- 1 | package commons; 2 | 3 | import game.Board; 4 | import game.Cell; 5 | import game.Move; 6 | 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | import java.util.function.BiFunction; 10 | 11 | public class Utils { 12 | public static LegalMoves getMoves(final Board board, 13 | final int maxDistance, 14 | final BiFunction isLegal, 15 | final Piece piece) { 16 | final Set moves = new HashSet<>(); 17 | final Set guards = new HashSet<>(); 18 | for (int rowDiff = -1; rowDiff <= 1; rowDiff++) { 19 | for (int colDiff = -1; colDiff <= 1; colDiff++) { 20 | boolean straightRow = rowDiff == 0; 21 | boolean straightCol = colDiff == 0; 22 | if (isLegal.apply(straightRow, straightCol) && !(straightRow && straightCol)) { 23 | int row = piece.position.row + rowDiff, col = piece.position.col + colDiff; 24 | for (int i = 1; i <= maxDistance; i++, row = row + rowDiff, col = col + colDiff) { 25 | if (Utils.withinBoardLimits(row, col)) { 26 | if (board.isEmpty(row, col)) { 27 | moves.add(Move.get(piece, Cell.get(row, col), false)); 28 | } else { 29 | if (board.getPiece(row, col).color != piece.color) { 30 | moves.add(Move.get(piece, Cell.get(row, col), true)); 31 | } else { 32 | guards.add(Move.get(piece, Cell.get(row, col), false)); 33 | } 34 | break; 35 | } 36 | } else { 37 | break; 38 | } 39 | } 40 | } 41 | } 42 | } 43 | return new LegalMoves(moves, guards); 44 | } 45 | 46 | public static boolean withinBoardLimits(final int row, final int col) { 47 | return row < 8 && row >= 0 && col < 8 && col >= 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/game/Board.java: -------------------------------------------------------------------------------- 1 | package game; 2 | 3 | import commons.*; 4 | import pieces.Knight; 5 | import pieces.PieceType; 6 | 7 | import java.util.*; 8 | import java.util.function.BiConsumer; 9 | import java.util.function.Consumer; 10 | import java.util.stream.Collectors; 11 | 12 | public class Board { 13 | public final Map pieces; 14 | public final Map> playerPieces; 15 | public final Map> moves; 16 | public final Map> guards; 17 | public final List moveList; 18 | private final long[] positions; 19 | private int positionIndex; 20 | public final boolean[][] canCastle; 21 | public long zobristHash; 22 | public Color playerToMove; 23 | boolean isThreeFoldRepetition; 24 | int halfMoves; 25 | boolean fiftyMoveDraw; 26 | public boolean inCheck; 27 | public Move previousMove; 28 | private final Piece[] kings; 29 | public static final Map approxValue = new HashMap<>(); 30 | private static long[][][] zobrist = new long[6][2][64]; 31 | private static long zobristSwitchPlayer; 32 | public static long[] zobristCastle = new long[4]; 33 | private static long[] zobristEnpassantFiles = new long[8]; 34 | 35 | static { 36 | approxValue.put(PieceType.PAWN, 1); 37 | approxValue.put(PieceType.KNIGHT, 3); 38 | approxValue.put(PieceType.BISHOP, 3); 39 | approxValue.put(PieceType.ROOK, 5); 40 | approxValue.put(PieceType.QUEEN, 9); 41 | approxValue.put(PieceType.KING, 0); 42 | final Random random = new Random(); 43 | for (int i = 0; i < 6; i++) { 44 | for (int j = 0; j < 2; j++) { 45 | for (int k = 0; k < 64; k++) { 46 | zobrist[i][j][k] = random.nextLong(); 47 | } 48 | } 49 | } 50 | zobristSwitchPlayer = random.nextLong(); 51 | for (int i = 0; i < 4; i++) { 52 | zobristCastle[i] = random.nextLong(); 53 | } 54 | for (int i = 0; i < 8; i++) { 55 | zobristEnpassantFiles[i] = random.nextLong(); 56 | } 57 | } 58 | 59 | public Board() { 60 | pieces = new HashMap<>(); 61 | moves = new HashMap<>(); 62 | guards = new HashMap<>(); 63 | moveList = new ArrayList<>(); 64 | canCastle = new boolean[][]{{true, true}, {true, true}}; 65 | positions = new long[6]; 66 | positionIndex = 0; 67 | playerPieces = new HashMap<>(); 68 | playerPieces.put(Color.WHITE, new ArrayList<>()); 69 | playerPieces.put(Color.BLACK, new ArrayList<>()); 70 | playerToMove = Color.WHITE; 71 | kings = new Piece[2]; 72 | } 73 | 74 | public boolean isEmpty(final int row, final int col) { 75 | return pieces.get(Cell.get(row, col)) == null; 76 | } 77 | 78 | public Piece getPiece(final int row, final int col) { 79 | return pieces.get(Cell.get(row, col)); 80 | } 81 | 82 | public Set getMoveList(final Piece piece) { 83 | final Cell cell = piece.position; 84 | if (pieces.get(cell) != null) { 85 | populateMoves(cell); 86 | return moves.get(cell); 87 | } else { 88 | throw new NullPointerException(); 89 | } 90 | } 91 | 92 | public Set getGuardList(final Piece piece) { 93 | final Cell cell = piece.position; 94 | if (pieces.get(cell) != null) { 95 | populateMoves(cell); 96 | return guards.get(cell); 97 | } else { 98 | throw new NullPointerException(); 99 | } 100 | } 101 | 102 | private void populateMoves(Cell cell) { 103 | if (!moves.containsKey(cell) || !guards.containsKey(cell)) { 104 | final LegalMoves legalMoves = pieces.get(cell).getLegalMoves(this); 105 | moves.put(cell, legalMoves.moves); 106 | guards.put(cell, legalMoves.guards); 107 | } 108 | } 109 | 110 | public static Board getStartBoard() { 111 | final Board board = new Board(); 112 | for (int i = 0; i < 8; i++) { 113 | board.placePawn(1, i, Color.WHITE); 114 | board.placePawn(6, i, Color.BLACK); 115 | } 116 | board.placeRook(0, 0, Color.WHITE); 117 | board.placeRook(0, 7, Color.WHITE); 118 | board.placeRook(7, 0, Color.BLACK); 119 | board.placeRook(7, 7, Color.BLACK); 120 | 121 | board.placeKnight(0, 1, Color.WHITE); 122 | board.placeKnight(0, 6, Color.WHITE); 123 | board.placeKnight(7, 1, Color.BLACK); 124 | board.placeKnight(7, 6, Color.BLACK); 125 | 126 | board.placeBishop(0, 2, Color.WHITE); 127 | board.placeBishop(0, 5, Color.WHITE); 128 | board.placeBishop(7, 2, Color.BLACK); 129 | board.placeBishop(7, 5, Color.BLACK); 130 | 131 | board.placeQueen(0, 3, Color.WHITE); 132 | board.placeQueen(7, 3, Color.BLACK); 133 | 134 | board.placeKing(0, 4, Color.WHITE); 135 | board.placeKing(7, 4, Color.BLACK); 136 | 137 | return board; 138 | } 139 | 140 | public void placePawn(final int row, final int col, final Color color) { 141 | final Cell position = Cell.get(row, col); 142 | final Piece pawn = Piece.get(color, position, PieceType.PAWN); 143 | pieces.put(position, pawn); 144 | playerPieces.get(color).add(pawn); 145 | updateHashForAddition(pawn); 146 | } 147 | 148 | public void placeRook(final int row, final int col, final Color color) { 149 | final Cell position = Cell.get(row, col); 150 | final Piece rook = Piece.get(color, position, PieceType.ROOK); 151 | pieces.put(position, rook); 152 | playerPieces.get(color).add(rook); 153 | updateHashForAddition(rook); 154 | } 155 | 156 | public void placeBishop(final int row, final int col, final Color color) { 157 | final Cell position = Cell.get(row, col); 158 | final Piece bishop = Piece.get(color, position, PieceType.BISHOP); 159 | pieces.put(position, bishop); 160 | playerPieces.get(color).add(bishop); 161 | updateHashForAddition(bishop); 162 | } 163 | 164 | public void placeQueen(final int row, final int col, final Color color) { 165 | final Cell position = Cell.get(row, col); 166 | final Piece queen = Piece.get(color, position, PieceType.QUEEN); 167 | pieces.put(position, queen); 168 | playerPieces.get(color).add(queen); 169 | updateHashForAddition(queen); 170 | } 171 | 172 | public void placeKing(final int row, final int col, final Color color) { 173 | final Cell position = Cell.get(row, col); 174 | final Piece king = Piece.get(color, position, PieceType.KING); 175 | kings[color.ordinal()] = king; 176 | pieces.put(position, king); 177 | playerPieces.get(color).add(king); 178 | updateHashForAddition(king); 179 | } 180 | 181 | public void placeKnight(final int row, final int col, final Color color) { 182 | final Cell position = Cell.get(row, col); 183 | final Piece knight = Piece.get(color, position, PieceType.KNIGHT); 184 | pieces.put(position, knight); 185 | playerPieces.get(color).add(knight); 186 | updateHashForAddition(knight); 187 | } 188 | 189 | public List getLegalMoves() { 190 | final Piece king = getKing(playerToMove); 191 | //better to check for pin from king than discoveries 192 | if (inCheck) { 193 | final Set attackers = attackingKing(king); 194 | final List legalMoves = new ArrayList<>(getMoveList(king)); 195 | if (attackers.size() < 2) { 196 | final Set pinnedToKing = pinnedToKing(king).keySet(); 197 | final List movableExceptKing = playerPieces.get(playerToMove).stream() 198 | .filter(piece -> !pinnedToKing.contains(piece)) 199 | .filter(piece -> !(piece.sameType(PieceType.KING))) 200 | .collect(Collectors.toList()); 201 | final Piece attacker = new ArrayList<>(attackers).get(0); 202 | final Set cellsWithCheck = rayOfCheck(king, attacker); 203 | if (attacker.sameType(PieceType.PAWN)) { 204 | //enPassant can capture a checking pawn 205 | movableExceptKing.stream() 206 | .filter(piece -> piece.sameType(PieceType.PAWN)) 207 | .map(this::getMoveList) 208 | .flatMap(Collection::stream) 209 | .filter(move -> move.captureMove) 210 | .filter(move -> move.captureCell.equals(attacker.position)) 211 | .filter(move -> !move.captureCell.equals(move.target)) 212 | .forEach(legalMoves::add); 213 | } 214 | legalMoves.addAll(movableExceptKing.stream() 215 | .map(this::getMoveList) 216 | .flatMap(Collection::stream) 217 | .filter(move -> cellsWithCheck.contains(move.target)) 218 | .collect(Collectors.toList())); 219 | } 220 | return legalMoves; 221 | } else { 222 | final Map pinnedToKing = pinnedToKing(king); 223 | final List normalMoves = playerPieces.get(playerToMove).stream() 224 | .filter(piece -> !pinnedToKing.containsKey(piece)) 225 | .map(this::getMoveList) 226 | .flatMap(Collection::stream) 227 | .filter(move -> !illegalEnPassant(move, king)) 228 | .collect(Collectors.toList()); 229 | final List pinnedPieceMoves = pinnedToKing.entrySet() 230 | .stream() 231 | .filter(entry -> !entry.getKey().sameType(PieceType.KNIGHT)) 232 | .map(this::getMovesWithinPin) 233 | .flatMap(Collection::stream) 234 | .collect(Collectors.toList()); 235 | normalMoves.addAll(pinnedPieceMoves); 236 | return normalMoves; 237 | } 238 | } 239 | 240 | public Piece getKing(Color color) { 241 | return kings[color.ordinal()]; 242 | } 243 | 244 | private List getMovesWithinPin(Map.Entry pinnedEntry) { 245 | final Piece pinnedPiece = pinnedEntry.getKey(); 246 | final Piece attacker = pinnedEntry.getValue(); 247 | final Piece king = getKing(pinnedPiece.color); 248 | final Set moves = getMoveList(pinnedPiece); 249 | final Line line = new Line(pinnedPiece.position, attacker.position); 250 | final Line inverse = new Line(pinnedPiece.position, king.position); 251 | if (line.isStraight) { 252 | return moves.stream().filter(move -> { 253 | final Line otherLine = new Line(move.piece.position, move.target); 254 | final boolean towardsAttacker = otherLine.isStraight && otherLine.rowDiff == line.rowDiff && otherLine.colDiff == line.colDiff; 255 | final boolean towardsKing = otherLine.isStraight && otherLine.rowDiff == inverse.rowDiff && otherLine.colDiff == inverse.colDiff; 256 | return towardsAttacker || towardsKing; 257 | }).collect(Collectors.toList()); 258 | } 259 | return new ArrayList<>(); 260 | } 261 | 262 | private boolean illegalEnPassant(final Move move, final Piece king) { 263 | if (move.piece.sameType(PieceType.PAWN) && move.captureMove && move.captureCell != move.target) { 264 | //does not expose an attack on the king. 265 | //The king is not discovered with the capture 266 | //ignore the captured and capturing pawn, and then check if the ray is a check. 267 | final Set ignorePawns = new HashSet<>(); 268 | ignorePawns.add(move.piece); 269 | ignorePawns.add(getPiece(move.captureCell.row, move.captureCell.col)); 270 | final Line line = new Line(move.captureCell, king.position); 271 | if (line.isStraight) { 272 | for (int row = king.position.row + line.rowDiff, col = king.position.col + line.colDiff; Utils.withinBoardLimits(row, col); row = row + line.rowDiff, col = col + line.colDiff) { 273 | if (!isEmpty(row, col) && !ignorePawns.contains(getPiece(row, col))) { 274 | final Piece piece = getPiece(row, col); 275 | return piece.color != king.color && (piece.sameType(line.minorPieceType) || piece.sameType(PieceType.QUEEN)); 276 | } else if (move.target.equals(Cell.get(row, col))) { 277 | break; 278 | } 279 | } 280 | } 281 | } 282 | return false; 283 | } 284 | 285 | private Set rayOfCheck(Piece king, Piece attacker) { 286 | final Set stopCheck = new HashSet<>(); 287 | if (attacker.sameType(PieceType.QUEEN) || attacker.sameType(PieceType.ROOK) || attacker.sameType(PieceType.BISHOP)) { 288 | final Line line = new Line(attacker.position, king.position); 289 | if (line.isStraight) { 290 | final int rowLowLimit = Math.min(attacker.position.row, king.position.row), 291 | rowHighLimit = Math.max(attacker.position.row, king.position.row), 292 | colLowLimit = Math.min(attacker.position.col, king.position.col), 293 | colHighLimit = Math.max(attacker.position.col, king.position.col); 294 | for (int i = 1; i < 8; i++) { 295 | final int row = king.position.row + i * line.rowDiff, col = king.position.col + i * line.colDiff; 296 | if (row >= rowLowLimit && row <= rowHighLimit && col >= colLowLimit && col <= colHighLimit) { 297 | stopCheck.add(Cell.get(row, col)); 298 | } 299 | } 300 | stopCheck.remove(king.position); 301 | } 302 | } 303 | stopCheck.add(attacker.position); 304 | return stopCheck; 305 | } 306 | 307 | private Map pinnedToKing(Piece king) { 308 | final Map blockingChecks = new HashMap<>(); 309 | final int kingRow = king.position.row, kingCol = king.position.col; 310 | final Color color = king.color; 311 | for (int rowDiff = -1; rowDiff <= 1; rowDiff++) { 312 | for (int colDiff = -1; colDiff <= 1; colDiff++) { 313 | if (!(rowDiff == 0 && colDiff == 0)) { 314 | final PieceType type = rowDiff == 0 || colDiff == 0 ? PieceType.ROOK : PieceType.BISHOP; 315 | final Reference blockingPiece = new Reference<>(); 316 | for (int k = 1; k < 8; k++) { 317 | final int row = kingRow + (rowDiff * k), col = kingCol + (colDiff * k); 318 | if (Utils.withinBoardLimits(row, col)) { 319 | if (stopFindingPin(row, col, color, blockingPiece, blockingChecks, PieceType.QUEEN, type)) { 320 | break; 321 | } 322 | } else { 323 | break; 324 | } 325 | } 326 | } 327 | } 328 | } 329 | return blockingChecks; 330 | } 331 | 332 | public boolean stopFindingPin(int row, int col, Color color, 333 | Reference blockingPiece, Map blockingChecks, 334 | PieceType... attackerTypes) { 335 | if (!isEmpty(row, col)) { 336 | final Piece piece = getPiece(row, col); 337 | if (blockingPiece.object == null) { 338 | if (piece.color == color) { 339 | blockingPiece.object = piece; 340 | return false; 341 | } else { 342 | return true; 343 | } 344 | } else { 345 | if (piece.color != color && Arrays.stream(attackerTypes).anyMatch(piece::sameType)) { 346 | blockingChecks.put(blockingPiece.object, piece); 347 | } 348 | return true; 349 | } 350 | } 351 | return false; 352 | } 353 | 354 | private Set attackingKing(Piece king) { 355 | final HashSet attackers = new HashSet<>(); 356 | final int kingRow = king.position.row, kingCol = king.position.col; 357 | final Color color = king.color; 358 | for (int rowDiff = -1; rowDiff <= 1; rowDiff++) { 359 | for (int colDiff = -1; colDiff <= 1; colDiff++) { 360 | if (!(rowDiff == 0 && colDiff == 0) && attackers.size() < 2) { 361 | final PieceType type = rowDiff == 0 || colDiff == 0 ? PieceType.ROOK : PieceType.BISHOP; 362 | for (int row = kingRow + rowDiff, col = kingCol + colDiff; Utils.withinBoardLimits(row, col); row = row + rowDiff, col = col + colDiff) { 363 | if (!isEmpty(row, col)) { 364 | final Piece piece = getPiece(row, col); 365 | if (piece.color != color && (piece.sameType(PieceType.QUEEN) || piece.sameType(type))) { 366 | attackers.add(piece); 367 | } 368 | break; 369 | } 370 | } 371 | } 372 | } 373 | } 374 | if (attackers.size() < 2) { 375 | final int pawnAttackRow = color == Color.BLACK ? kingRow - 1 : kingRow + 1; 376 | if (pawnAttackRow >= 0 && pawnAttackRow < 8) { 377 | final Piece leftPawn = kingCol > 0 ? getPiece(pawnAttackRow, kingCol - 1) : null; 378 | final Piece rightPawn = kingCol < 7 ? getPiece(pawnAttackRow, kingCol + 1) : null; 379 | if (rightPawn != null && rightPawn.sameType(PieceType.PAWN) && rightPawn.color != king.color) { 380 | attackers.add(rightPawn); 381 | } else if (leftPawn != null && leftPawn.sameType(PieceType.PAWN) && leftPawn.color != king.color) { 382 | attackers.add(leftPawn); 383 | } 384 | } 385 | for (int i = 0; i < Knight.diff.length && attackers.size() < 2; i++) { 386 | final int row = kingRow + Knight.diff[i][0], col = kingCol + Knight.diff[i][1]; 387 | if (Utils.withinBoardLimits(row, col)) { 388 | final Piece piece = getPiece(row, col); 389 | if (piece != null && piece.sameType(PieceType.KNIGHT) && piece.color != king.color) { 390 | attackers.add(piece); 391 | } 392 | } 393 | } 394 | } 395 | return attackers; 396 | } 397 | 398 | private void updateHashForRemove(Piece piece) { 399 | zobristHash ^= zobrist[piece.pieceType.ordinal()][piece.color.ordinal()][(piece.position.row << 3) + piece.position.col]; 400 | } 401 | 402 | private void updateHashForAddition(Piece piece) { 403 | zobristHash ^= zobrist[piece.pieceType.ordinal()][piece.color.ordinal()][(piece.position.row << 3) + piece.position.col]; 404 | } 405 | 406 | public void makeMove(Move move) { 407 | //remove captured pieces 408 | final Set affectedCells = new HashSet<>(); 409 | if (move.captureMove) { 410 | final Piece delete = pieces.remove(move.captureCell); 411 | playerPieces.get(Color.opponent(move.piece.color)).remove(delete); 412 | updateHashForRemove(delete); 413 | affectedCells.add(move.captureCell); 414 | } 415 | pieces.remove(move.piece.position); 416 | playerPieces.get(move.piece.color).remove(move.piece); 417 | updateHashForRemove(move.piece); 418 | affectedCells.add(move.piece.position); 419 | for (final Cell affectedCell : affectedCells) { 420 | //todo: don't remove all the moves and guards, just the ones affected 421 | updateForClearCell(affectedCell); 422 | } 423 | updateForBlockedCell(move); 424 | for (final Piece king : kings) { 425 | updateKingMoves(move, king); 426 | } 427 | removeUnusedEnpassant(); 428 | moveOrPromote(move); 429 | postMoveUpdates(move); 430 | } 431 | 432 | private void updateKingMoves(Move move, Piece king) { 433 | final Cell target = move.target; 434 | final Cell start = move.piece.position; 435 | final Cell capture = move.captureCell; 436 | removeMoves(king.position); 437 | //add guard moves in vicinity 438 | //occupied cell allows more moves -> 439 | //occupied cell allows less moves -> Opponent long piece moved into square. Remove all squares affected. 440 | //free cell allows more moves -> Impossible 441 | //free cell allows less moves -> Opponent long piece on other side, remove squares affected. 442 | //nearby moves of pawn -> My pawns have no affect. Enemy can guard. 443 | //knight moves -> My knight has no affect. Enemy can guard. 444 | //king moves -> If it's my kind, remove moves. Else can guard. 445 | } 446 | 447 | private void updateForClearCell(final Cell affectedCell) { 448 | longRangeUpdate(affectedCell, (piece) -> { 449 | moves.get(piece.position).remove(Move.get(piece, affectedCell, true)); 450 | guards.get(piece.position).remove(Move.get(piece, affectedCell, false)); 451 | moves.get(piece.position).add(Move.get(piece, affectedCell, false)); 452 | }, 453 | (m, position) -> moves.get(position).add(m), 454 | (m, position) -> guards.get(position).add(m)); 455 | for (final int[] coordinate : Knight.diff) { 456 | final int row = affectedCell.row + coordinate[0]; 457 | final int col = affectedCell.col + coordinate[1]; 458 | if (Utils.withinBoardLimits(row, col)) { 459 | if (!isEmpty(row, col)) { 460 | final Piece knight = getPiece(row, col); 461 | if (knight.sameType(PieceType.KNIGHT)) { 462 | if (moves.get(knight.position) != null) { 463 | moves.get(knight.position).remove(Move.get(knight, affectedCell, true)); 464 | guards.get(knight.position).remove(Move.get(knight, affectedCell, false)); 465 | moves.get(knight.position).add(Move.get(knight, affectedCell, false)); 466 | } 467 | } 468 | } 469 | } 470 | } 471 | for (int i = -2; i <= 2; i++) { 472 | for (int j = -1; j <= 1; j++) { 473 | if (i != 0 || j != 0) { 474 | final int row = affectedCell.row + i; 475 | final int col = affectedCell.col + j; 476 | if (Utils.withinBoardLimits(row, col)) { 477 | if (!isEmpty(row, col)) { 478 | final Piece pawn = getPiece(row, col); 479 | if (pawn.sameType(PieceType.PAWN) && (pawn.color.equals(Color.BLACK) ? i > 0 : i < 0)) { 480 | removeMoves(pawn.position); 481 | } 482 | } 483 | } 484 | } 485 | } 486 | } 487 | } 488 | 489 | private void longRangeUpdate(final Cell affectedCell, 490 | final Consumer initialOperation, 491 | final BiConsumer moveUpdateOperation, 492 | final BiConsumer guardUpdateOperation) { 493 | for (int i = -1; i <= 1; i++) { 494 | for (int j = -1; j <= 1; j++) { 495 | final PieceType type = (i == 0 || j == 0) ? PieceType.ROOK : PieceType.BISHOP; 496 | if (i != 0 || j != 0) { 497 | for (int k = 0; k < 8; k++) { 498 | final int row = affectedCell.row + i * k; 499 | final int col = affectedCell.col + j * k; 500 | if (Utils.withinBoardLimits(row, col)) { 501 | if (!isEmpty(row, col)) { 502 | final Piece piece = getPiece(row, col); 503 | if (piece.sameType(PieceType.QUEEN) || piece.sameType(type)) { 504 | if (moves.get(piece.position) != null) { 505 | initialOperation.accept(piece); 506 | for (int l = 1; l < 8 - k; l++) { 507 | final int clearedRow = affectedCell.row - i * l; 508 | final int clearedCol = affectedCell.col - j * l; 509 | if (Utils.withinBoardLimits(clearedRow, clearedCol)) { 510 | final Cell visibleCell = Cell.get(clearedRow, clearedCol); 511 | if (isEmpty(clearedRow, clearedCol)) { 512 | moveUpdateOperation.accept(Move.get(piece, visibleCell, false), piece.position); 513 | } else { 514 | final Piece nowVisiblePiece = getPiece(clearedRow, clearedCol); 515 | if (nowVisiblePiece.color.equals(piece.color)) { 516 | guardUpdateOperation.accept(Move.get(piece, visibleCell, false), piece.position); 517 | } else { 518 | moveUpdateOperation.accept(Move.get(piece, visibleCell, true), piece.position); 519 | } 520 | break; 521 | } 522 | } else { 523 | break; 524 | } 525 | } 526 | } 527 | } 528 | break; 529 | } 530 | } else { 531 | break; 532 | } 533 | } 534 | } 535 | } 536 | } 537 | } 538 | 539 | private void updateForBlockedCell(final Move move) { 540 | final Cell affectedCell = move.target; 541 | longRangeUpdate(affectedCell, (piece) -> { 542 | moves.get(piece.position).remove(Move.get(piece, affectedCell, true)); 543 | moves.get(piece.position).remove(Move.get(piece, affectedCell, false)); 544 | guards.get(piece.position).remove(Move.get(piece, affectedCell, false)); 545 | if (move.piece.color.equals(piece.color)) { 546 | guards.get(piece.position).add(Move.get(piece, affectedCell, false)); 547 | } else { 548 | moves.get(piece.position).add(Move.get(piece, affectedCell, true)); 549 | } 550 | }, 551 | (m, position) -> moves.get(position).remove(m), 552 | (m, position) -> guards.get(position).remove(m)); 553 | for (final int[] coordinate : Knight.diff) { 554 | final int row = affectedCell.row + coordinate[0]; 555 | final int col = affectedCell.col + coordinate[1]; 556 | if (Utils.withinBoardLimits(row, col)) { 557 | if (!isEmpty(row, col)) { 558 | final Piece knight = getPiece(row, col); 559 | if (knight.sameType(PieceType.KNIGHT)) { 560 | if (moves.get(knight.position) != null) { 561 | moves.get(knight.position).remove(Move.get(knight, affectedCell, true)); 562 | moves.get(knight.position).remove(Move.get(knight, affectedCell, false)); 563 | guards.get(knight.position).remove(Move.get(knight, affectedCell, false)); 564 | if (move.piece.color.equals(knight.color)) { 565 | guards.get(knight.position).add(Move.get(knight, affectedCell, false)); 566 | } else { 567 | moves.get(knight.position).add(Move.get(knight, affectedCell, true)); 568 | } 569 | } 570 | } 571 | } 572 | } 573 | } 574 | removeMoves(affectedCell); 575 | for (int i = -2; i <= 2; i++) { 576 | for (int j = -1; j <= 1; j++) { 577 | if ((i != 0 || j != 0) && (Math.abs(i) + Math.abs(j) <= 2)) { 578 | final int row = affectedCell.row + i; 579 | final int col = affectedCell.col + j; 580 | if (Utils.withinBoardLimits(row, col)) { 581 | if (!isEmpty(row, col)) { 582 | final Piece pawn = getPiece(row, col); 583 | if (pawn.sameType(PieceType.PAWN) && (pawn.color == Color.BLACK ? i > 0 : i < 0)) { 584 | removeMoves(pawn.position); 585 | } 586 | } 587 | } 588 | } 589 | } 590 | } 591 | } 592 | 593 | private void removeUnusedEnpassant() { 594 | if (!moveList.isEmpty()) { 595 | final Move previousMoveOfCurrentPlayer = moveList.get(moveList.size() - 1); 596 | if (PieceType.PAWN.equals(previousMoveOfCurrentPlayer.piece.pieceType)) { 597 | final Piece pawnLastMoved = previousMoveOfCurrentPlayer.piece; 598 | if (Math.abs(previousMoveOfCurrentPlayer.target.row - pawnLastMoved.position.row) == 2) { 599 | final int row = previousMoveOfCurrentPlayer.target.row; 600 | for (int j = -1; j <= 1; j = j + 2) { 601 | int col = previousMoveOfCurrentPlayer.target.col + j; 602 | if (Utils.withinBoardLimits(row, col)) { 603 | if (!isEmpty(row, col)) { 604 | final Piece pawn = getPiece(row, col); 605 | if (pawn.sameType(PieceType.PAWN) && pawn.color != previousMoveOfCurrentPlayer.piece.color) { 606 | removeMoves(pawn.position); 607 | } 608 | } 609 | } 610 | } 611 | } 612 | } 613 | } 614 | } 615 | 616 | private void removeMoves(Cell position) { 617 | moves.remove(position); 618 | guards.remove(position); 619 | } 620 | 621 | private void moveOrPromote(Move move) { 622 | if (move.piece.sameType(PieceType.PAWN) && move.target.row == (playerToMove == Color.BLACK ? 0 : 7)) { 623 | switch (move.promoteTo) { 624 | case QUEEN: 625 | placeQueen(move.target.row, move.target.col, playerToMove); 626 | break; 627 | case ROOK: 628 | placeRook(move.target.row, move.target.col, playerToMove); 629 | break; 630 | case KNIGHT: 631 | placeKnight(move.target.row, move.target.col, playerToMove); 632 | break; 633 | case BISHOP: 634 | placeBishop(move.target.row, move.target.col, playerToMove); 635 | break; 636 | default: 637 | throw new AssertionError(); 638 | } 639 | } else { 640 | final Piece changedPiece = Piece.get(move.piece.color, move.target, move.piece.pieceType); 641 | pieces.put(move.target, changedPiece); 642 | playerPieces.get(move.piece.color).add(changedPiece); 643 | updateHashForAddition(changedPiece); 644 | if (changedPiece.pieceType == PieceType.KING) { 645 | kings[changedPiece.color.ordinal()] = changedPiece; 646 | } 647 | } 648 | } 649 | 650 | public Board copy() { 651 | final Board board = new Board(); 652 | pieces.forEach(board.pieces::put); 653 | playerPieces.forEach((color, pieces) -> board.playerPieces.put(color, new ArrayList<>(pieces))); 654 | moves.forEach((cell, moves) -> board.moves.put(cell, new HashSet<>(moves))); 655 | guards.forEach((cell, moves) -> board.guards.put(cell, new HashSet<>(moves))); 656 | System.arraycopy(positions, 0, board.positions, 0, positions.length); 657 | board.positionIndex = positionIndex; 658 | board.moveList.addAll(moveList); 659 | System.arraycopy(kings, 0, board.kings, 0, 2); 660 | for (int i = 0; i < 2; i++) { 661 | System.arraycopy(canCastle[i], 0, board.canCastle[i], 0, 2); 662 | } 663 | board.previousMove = previousMove; 664 | board.zobristHash = zobristHash; 665 | board.playerToMove = playerToMove; 666 | board.isThreeFoldRepetition = isThreeFoldRepetition; 667 | board.halfMoves = halfMoves; 668 | board.fiftyMoveDraw = fiftyMoveDraw; 669 | board.inCheck = inCheck; 670 | return board; 671 | } 672 | 673 | //todo: idea: Should we compare a list of positions and choose the best? We do not evaluate positions in isolation, 674 | // but rather rank them by comparing the top 20 positions possible 675 | public double evaluation(int availableMoves) { 676 | if (availableMoves == 0) { 677 | if (inCheck) { 678 | return Integer.MIN_VALUE; 679 | } else { 680 | return 0; 681 | } 682 | } 683 | if (isDraw()) { 684 | return 0; 685 | } 686 | return heuristic() + availableMoves / 100.0; 687 | } 688 | 689 | private int heuristic() { 690 | return playerPieces.get(playerToMove).stream().mapToInt(piece -> approxValue.get(piece.pieceType)).sum() 691 | - playerPieces.get(Color.opponent(playerToMove)).stream().mapToInt(piece -> approxValue.get(piece.pieceType)).sum(); 692 | } 693 | 694 | public boolean isDraw() { 695 | if (isThreeFoldRepetition || fiftyMoveDraw) { 696 | return true; 697 | } 698 | final List currentPieces = playerPieces.get(playerToMove); 699 | final List opponentPieces = playerPieces.get(Color.opponent(playerToMove)); 700 | final boolean playerCannotWin = insufficientMaterial(currentPieces); 701 | final boolean opponentCannotWin = insufficientMaterial(opponentPieces); 702 | return (playerCannotWin && opponentCannotWin); 703 | } 704 | 705 | private boolean insufficientMaterial(List opponentPieces) { 706 | if (opponentPieces.size() == 1) { 707 | return true; 708 | } else if (opponentPieces.size() == 2) { 709 | return opponentPieces.stream().filter(piece -> piece.sameType(PieceType.KNIGHT) || piece.sameType(PieceType.BISHOP)).count() == 1; 710 | } else if (opponentPieces.size() == 3) { 711 | return opponentPieces.stream().filter(piece -> piece.sameType(PieceType.KNIGHT)).count() == 2; 712 | } 713 | return false; 714 | } 715 | 716 | private void postMoveUpdates(Move move) { 717 | moveList.add(move); 718 | if (move.piece.sameType(PieceType.PAWN) && Math.abs(move.piece.position.row - move.target.row) == 2) { 719 | zobristHash ^= zobristEnpassantFiles[move.target.col]; 720 | } 721 | if (previousMove != null && previousMove.piece.sameType(PieceType.PAWN) && Math.abs(previousMove.piece.position.row - previousMove.target.row) == 2) { 722 | zobristHash ^= zobristEnpassantFiles[previousMove.target.col]; 723 | } 724 | previousMove = move; 725 | zobristHash ^= zobristSwitchPlayer; 726 | markDraw(move); 727 | castlingAllowance(move); 728 | inCheck = lookForChecks(move); 729 | playerToMove = Color.opponent(move.piece.color); 730 | } 731 | 732 | private void castlingAllowance(Move move) { 733 | if (move.piece.sameType(PieceType.KING)) { 734 | final int color = move.piece.color.ordinal(); 735 | if (canCastle[color][0]) { 736 | zobristHash ^= zobristCastle[color * 2]; 737 | } 738 | if (canCastle[color][1]) { 739 | zobristHash ^= zobristCastle[color * 2 + 1]; 740 | } 741 | canCastle[color][1] = canCastle[color][0] = false; 742 | //take castling into account 743 | if (move.target.row == move.piece.position.row && Math.abs(move.piece.position.col - move.target.col) == 2) { 744 | final int row = color * 7; 745 | if (move.target.col == 6) { 746 | makeMove(Move.get(getPiece(row, 7), Cell.get(row, 5), false)); 747 | } else { 748 | makeMove(Move.get(getPiece(row, 0), Cell.get(row, 3), false)); 749 | } 750 | } 751 | } else { 752 | for (int i = 0; i <= 1; i++) { 753 | for (int j = 0; j <= 1; j++) { 754 | final Cell corner = Cell.get(i * 7, j * 7); 755 | if (move.piece.position.equals(corner) || (move.captureMove && move.captureCell.equals(corner))) { 756 | if (canCastle[i][j]) { 757 | if (move.piece.position.equals(Cell.get(i * 7, j * 7))) { 758 | if (canCastle[i][j]) { 759 | zobristHash ^= zobristCastle[i * 2 + j]; 760 | } 761 | canCastle[i][j] = false; 762 | } 763 | } 764 | } 765 | } 766 | } 767 | } 768 | } 769 | 770 | private void markDraw(Move move) { 771 | positions[positionIndex % positions.length] = zobristHash; 772 | positionIndex++; 773 | int frequency = 0; 774 | for (final long position : positions) { 775 | if (position == zobristHash) { 776 | frequency++; 777 | if (frequency >= 3) { 778 | isThreeFoldRepetition = true; 779 | break; 780 | } 781 | } 782 | } 783 | if (move.captureMove || move.piece.sameType(PieceType.PAWN)) { 784 | halfMoves = 0; 785 | } else { 786 | halfMoves++; 787 | if (halfMoves == 100) { 788 | fiftyMoveDraw = true; 789 | } 790 | } 791 | } 792 | 793 | private boolean lookForChecks(Move move) { 794 | final Set moveList = getMoveList(pieces.get(move.target)); 795 | for (final Move possibleMove : moveList) { 796 | if (possibleMove.captureMove && pieces.get(possibleMove.target).sameType(PieceType.KING)) { 797 | return true; 798 | } 799 | } 800 | final Piece king = kings[Color.opponent(playerToMove).ordinal()]; 801 | return discoveredCheck(king, move.piece.position) || (move.captureMove && move.captureCell != move.target && discoveredCheck(king, move.captureCell)); 802 | } 803 | 804 | private boolean discoveredCheck(Piece king, Cell coordinate) { 805 | //knight and pawn can't give discoveries 806 | final Line line = new Line(coordinate, king.position); 807 | if (line.isStraight) { 808 | for (int row = king.position.row + line.rowDiff, col = king.position.col + line.colDiff; Utils.withinBoardLimits(row, col); row = row + line.rowDiff, col = col + line.colDiff) { 809 | if (!isEmpty(row, col)) { 810 | final Piece piece = pieces.get(Cell.get(row, col)); 811 | return piece.color != king.color && (piece.sameType(PieceType.QUEEN) || piece.sameType(line.minorPieceType)); 812 | } 813 | } 814 | } 815 | return false; 816 | } 817 | 818 | @Override 819 | public String toString() { 820 | final StringBuilder stringBuilder = new StringBuilder(); 821 | for (int i = 7; i >= 0; i--) { 822 | for (int j = 0; j < 8; j++) { 823 | if (isEmpty(i, j)) { 824 | stringBuilder.append((i + j) % 2 == 0 ? "◻" : "◼"); 825 | } else { 826 | stringBuilder.append(getPiece(i, j).getShortForm()); 827 | } 828 | } 829 | stringBuilder.append('\n'); 830 | } 831 | stringBuilder.append("{\npieces=") 832 | .append(pieces).append('\n') 833 | .append("moveList=").append(moveList).append('\n') 834 | .append("inCheck: ").append(inCheck).append("\n}"); 835 | return stringBuilder.toString(); 836 | } 837 | 838 | public String fenRepresentation() { 839 | final StringBuilder stringBuilder = new StringBuilder(); 840 | for (int i = 7; i >= 0; i--) { 841 | int count = 0; 842 | for (int j = 0; j < 8; j++) { 843 | if (isEmpty(i, j)) { 844 | count++; 845 | } else { 846 | if (count > 0) { 847 | stringBuilder.append(count); 848 | count = 0; 849 | } 850 | final Piece piece = getPiece(i, j); 851 | char letter = piece.pieceType.getLetter(); 852 | if (piece.color == Color.WHITE) { 853 | letter = (char) (letter - 'a' + 'A'); 854 | } 855 | stringBuilder.append(letter); 856 | } 857 | } 858 | if (count > 0) { 859 | stringBuilder.append(count); 860 | } 861 | if (i > 0) { 862 | stringBuilder.append('/'); 863 | } 864 | } 865 | stringBuilder.append(' ') 866 | .append(playerToMove == Color.WHITE ? 'w' : 'b') 867 | .append(' ') 868 | .append(castlingAllowanceFen(canCastle)) 869 | .append(' '); 870 | if (!moveList.isEmpty()) { 871 | final Move lastMove = moveList.get(moveList.size() - 1); 872 | if (lastMove.piece.sameType(PieceType.PAWN) && Math.abs(lastMove.piece.position.row - lastMove.target.row) == 2) { 873 | stringBuilder.append((char) (lastMove.target.col + 'a')).append((lastMove.target.row + (lastMove.piece.color == Color.BLACK ? 2 : 0))); 874 | } else { 875 | stringBuilder.append('-'); 876 | } 877 | } else { 878 | stringBuilder.append('-'); 879 | } 880 | return stringBuilder.toString(); 881 | } 882 | 883 | public static Board getBoard(String fen) { 884 | Board board = new Board(); 885 | String[] boardFen = fen.split(" "); 886 | String[] rows = boardFen[0].split("/"); 887 | for (int i = 0; i < 8; i++) { 888 | int index = 0; 889 | for (int j = 0; j < rows[i].length(); j++) { 890 | final char current = rows[i].charAt(j); 891 | if (Character.isDigit(current)) { 892 | index += current - '0'; 893 | } else { 894 | final Color color = Character.isUpperCase(current) ? Color.WHITE : Color.BLACK; 895 | final int row = 7 - i; 896 | switch (Character.toLowerCase(current)) { 897 | case 'p': 898 | board.placePawn(row, index, color); 899 | break; 900 | case 'n': 901 | board.placeKnight(row, index, color); 902 | break; 903 | case 'b': 904 | board.placeBishop(row, index, color); 905 | break; 906 | case 'r': 907 | board.placeRook(row, index, color); 908 | break; 909 | case 'q': 910 | board.placeQueen(row, index, color); 911 | break; 912 | case 'k': 913 | board.placeKing(row, index, color); 914 | break; 915 | } 916 | index++; 917 | } 918 | } 919 | } 920 | board.playerToMove = boardFen[1].equals("w") ? Color.WHITE : Color.BLACK; 921 | if (board.playerToMove == Color.BLACK) { 922 | board.zobristHash ^= zobristSwitchPlayer; 923 | } 924 | board.canCastle[0][1] = boardFen[2].contains("K"); 925 | board.canCastle[0][0] = boardFen[2].contains("Q"); 926 | board.canCastle[1][1] = boardFen[2].contains("k"); 927 | board.canCastle[1][0] = boardFen[2].contains("q"); 928 | for (int i = 0; i < 2; i++) { 929 | for (int j = 0; j < 2; j++) { 930 | if (!board.canCastle[i][j]) { 931 | board.zobristHash ^= zobristCastle[i * 2 + j]; 932 | } 933 | } 934 | } 935 | if (!boardFen[3].equals("-")) { 936 | int rowDiff = boardFen[3].charAt(1) == '6' ? -1 : 1; 937 | final int col = boardFen[3].charAt(0) - 'a'; 938 | final int row = rowDiff == -1 ? 4 : 3; 939 | final Piece pawn = board.getPiece(row, col); 940 | board.moveList.add(Move.get(Piece.get(pawn.color, Cell.get(rowDiff == -1 ? 6 : 1, col), PieceType.PAWN), pawn.position, false)); 941 | board.zobristHash ^= zobristEnpassantFiles[col]; 942 | } 943 | return board; 944 | } 945 | 946 | private String castlingAllowanceFen(boolean[][] canCastle) { 947 | String result = ""; 948 | if (canCastle[0][1]) { 949 | result = result + "K"; 950 | } 951 | if (canCastle[0][0]) { 952 | result = result + "Q"; 953 | } 954 | if (canCastle[1][1]) { 955 | result = result + "k"; 956 | } 957 | if (canCastle[1][0]) { 958 | result = result + "q"; 959 | } 960 | return result.equals("") ? "-" : result; 961 | } 962 | 963 | private static class Reference { 964 | T object; 965 | } 966 | } -------------------------------------------------------------------------------- /src/main/java/game/Cell.java: -------------------------------------------------------------------------------- 1 | package game; 2 | 3 | public class Cell { 4 | public final int row, col; 5 | 6 | private static final Cell[] cells = setBoard(); 7 | 8 | private Cell(int row, int col) { 9 | this.row = row; 10 | this.col = col; 11 | } 12 | 13 | public static Cell get(final int row, final int col) { 14 | return cells[(row << 3) + col]; 15 | } 16 | 17 | private static Cell[] setBoard() { 18 | final Cell[] cells = new Cell[64]; 19 | for (int i = 0; i < 8; i++) { 20 | for (int j = 0; j < 8; j++) { 21 | cells[(i << 3) + j] = new Cell(i, j); 22 | } 23 | } 24 | return cells; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "(" + row + 30 | ',' + col + 31 | ')'; 32 | } 33 | 34 | public String notation() { 35 | return Character.valueOf((char) (col + 'a')).toString() + (row + 1); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/game/Move.java: -------------------------------------------------------------------------------- 1 | package game; 2 | 3 | import commons.Piece; 4 | import pieces.PieceType; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Objects; 9 | 10 | public class Move { 11 | public final Piece piece; 12 | public final Cell target; 13 | public final boolean captureMove; 14 | public final Cell captureCell; 15 | public final PieceType promoteTo; 16 | private final int id; 17 | private static final Map moveMap = new HashMap<>(); 18 | 19 | public static int count = 0; 20 | 21 | public static Move get(Piece piece, Cell end, boolean captureMove) { 22 | return get(piece, end, captureMove, captureMove ? end : null, null); 23 | } 24 | 25 | public static Move get(Piece piece, Cell end, boolean captureMove, Cell captureCell) { 26 | return get(piece, end, captureMove, captureMove ? captureCell : null, null); 27 | } 28 | 29 | public static Move get(Piece piece, Cell end, boolean captureMove, Cell captureCell, PieceType promoteTo) { 30 | final int colorIndex = piece.color.ordinal() << 6; 31 | final int positionIndex = (piece.position.row << 3) + piece.position.col; 32 | final int pieceIndex = (colorIndex + positionIndex) * 6 + piece.pieceType.ordinal(); 33 | final int travelIndex = (end.row << 3) + end.col; 34 | final int captureIndex = captureMove ? ((captureCell.row << 3) + captureCell.col) + 1 : 0; 35 | final int promotionIndex = promoteTo == null ? 0 : promoteTo.ordinal() + 1; 36 | final int index = (((pieceIndex << 9) + travelIndex) * 65 + captureIndex) * 7 + promotionIndex; 37 | if (!moveMap.containsKey(index)) { 38 | moveMap.put(index, new Move(piece, end, captureMove, captureCell, promoteTo, index)); 39 | } 40 | return moveMap.get(index); 41 | } 42 | 43 | private Move(Piece piece, Cell target, boolean captureMove, Cell captureCell, PieceType promoteTo, final int id) { 44 | count++; 45 | this.piece = piece; 46 | this.target = target; 47 | this.captureMove = captureMove; 48 | this.captureCell = captureCell; 49 | this.promoteTo = promoteTo; 50 | this.id = id; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "{" + printMove(piece, target) + 56 | ", captureMove=" + captureMove + 57 | ", captureCell=" + captureCell + 58 | ", promoteTo=" + promoteTo + 59 | '}'; 60 | } 61 | 62 | private String printMove(Piece piece, Cell cell) { 63 | return piece.getShortForm() + "," + piece.position.notation() + cell.notation(); 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | if (o == null || getClass() != o.getClass()) return false; 70 | Move move = (Move) o; 71 | return id == move.id; 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | return id; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/pieces/Bishop.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | import commons.LegalMoves; 4 | import commons.Piece; 5 | import commons.Utils; 6 | import game.Board; 7 | 8 | import java.util.function.BiFunction; 9 | 10 | public class Bishop { 11 | 12 | public static final BiFunction movement = (rowStraight, colStraight) -> !rowStraight && !colStraight; 13 | public static final int MAX_DISTANCE = 7; 14 | 15 | public static LegalMoves getMoveList(Board board, Piece piece) { 16 | return Utils.getMoves(board, MAX_DISTANCE, movement, piece); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/pieces/King.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | import commons.*; 4 | import game.Board; 5 | import game.Cell; 6 | import game.Move; 7 | 8 | import java.util.*; 9 | import java.util.function.BiFunction; 10 | import java.util.stream.Collectors; 11 | 12 | public class King { 13 | 14 | public static final BiFunction movement = (rowStraight, colStraight) -> true; 15 | public static final int MAX_DISTANCE = 1; 16 | 17 | public static LegalMoves getMoveList(Board board, Piece piece) { 18 | final LegalMoves legalMoves = Utils.getMoves(board, MAX_DISTANCE, movement, piece); 19 | filterIllegalMoves(board, legalMoves.moves, piece); 20 | return legalMoves; 21 | } 22 | 23 | private static void filterIllegalMoves(Board board, Set moves, Piece king) { 24 | final Set illegalSquares = getIllegalSquares(board, Color.opponent(king.color), 25 | moves.stream().map(move -> move.target).collect(Collectors.toSet()), king.position); 26 | moves.removeIf(move -> illegalSquares.contains(move.target)); 27 | if (board.inCheck) { 28 | moves.removeIf(move -> xRay(move, board, king)); 29 | } else { 30 | final Move[] castles = castlingMoves(board, king, moves); 31 | for (int i = 0; i < castles.length && castles[i] != null; i++) { 32 | moves.add(castles[i]); 33 | } 34 | } 35 | } 36 | 37 | private static Set getIllegalSquares(final Board board, 38 | final Color opponentColor, 39 | final Set positions, 40 | final Cell kingPosition) { 41 | final Set illegalSquares = new HashSet<>(); 42 | final List pieces = board.playerPieces.get(opponentColor).stream() 43 | .filter(piece -> withinKingRange(kingPosition, piece.pieceType, piece.position)) 44 | .sorted(Comparator.comparingInt(piece -> -Board.approxValue.get(piece.pieceType))) 45 | .collect(Collectors.toList()); 46 | for (final Cell kingMove : positions) { 47 | for (final Piece piece : pieces) { 48 | final int rowDiff = Math.abs(piece.position.row - kingMove.row); 49 | final int colDiff = Math.abs(piece.position.col - kingMove.col); 50 | final int diagDiff = Math.abs(Math.abs(piece.position.col - piece.position.row) - Math.abs(kingMove.col - kingMove.row)); 51 | final int revDiff = Math.abs(piece.position.col + piece.position.row - (kingMove.col + kingMove.row)); 52 | switch (piece.pieceType) { 53 | case BISHOP: 54 | if (diagDiff <= 0 || revDiff <= 0) { 55 | if (board.getMoveList(piece).contains(Move.get(piece, kingMove, false)) 56 | || board.getGuardList(piece).contains(Move.get(piece, kingMove, false))) { 57 | illegalSquares.add(kingMove); 58 | } 59 | } 60 | break; 61 | case ROOK: 62 | if (rowDiff <= 0 || colDiff <= 0) { 63 | if (board.getMoveList(piece).contains(Move.get(piece, kingMove, false)) 64 | || board.getGuardList(piece).contains(Move.get(piece, kingMove, false))) { 65 | illegalSquares.add(kingMove); 66 | } 67 | } 68 | break; 69 | case KING: 70 | if ((rowDiff == 1 || colDiff == 1) && (rowDiff <= 1 && colDiff <= 1)) { 71 | for (int i = -1; i <= 1; i++) { 72 | for (int j = -1; j <= 1; j++) { 73 | final int row = piece.position.row + i; 74 | final int col = piece.position.col + j; 75 | if (Utils.withinBoardLimits(row, col)) { 76 | illegalSquares.add(Cell.get(row, col)); 77 | } 78 | } 79 | } 80 | } 81 | break; 82 | case PAWN: 83 | if (rowDiff == 1 && colDiff == 1 && 84 | (opponentColor.equals(Color.WHITE) ? (kingMove.row > piece.position.row) : (kingMove.row < piece.position.row))) { 85 | illegalSquares.add(kingMove); 86 | } 87 | break; 88 | case KNIGHT: 89 | if (rowDiff <= 2 && colDiff <= 2 && rowDiff + colDiff == 3) { 90 | if (board.getMoveList(piece).contains(Move.get(piece, kingMove, false)) 91 | || board.getGuardList(piece).contains(Move.get(piece, kingMove, false))) { 92 | illegalSquares.add(kingMove); 93 | } 94 | } 95 | break; 96 | case QUEEN: 97 | if (rowDiff <= 0 || colDiff <= 0 || diagDiff <= 0 || revDiff <= 0) { 98 | if (board.getMoveList(piece).contains(Move.get(piece, kingMove, false)) 99 | || board.getGuardList(piece).contains(Move.get(piece, kingMove, false))) { 100 | illegalSquares.add(kingMove); 101 | } 102 | } 103 | break; 104 | } 105 | if (illegalSquares.contains(kingMove)) { 106 | break; 107 | } 108 | } 109 | } 110 | return illegalSquares; 111 | } 112 | 113 | public static boolean withinKingRange(final Cell kingPosition, 114 | final PieceType pieceType, 115 | final Cell piecePosition) { 116 | final int rowDiff = Math.abs(piecePosition.row - kingPosition.row); 117 | final int colDiff = Math.abs(piecePosition.col - kingPosition.col); 118 | final int diagDiff = Math.abs(Math.abs(piecePosition.col - piecePosition.row) - Math.abs(kingPosition.col - kingPosition.row)); 119 | final int revDiff = Math.abs(piecePosition.col + piecePosition.row - (kingPosition.col + kingPosition.row)); 120 | switch (pieceType) { 121 | case BISHOP: 122 | return diagDiff <= 2 || revDiff <= 2; 123 | case ROOK: 124 | return rowDiff <= 1 || colDiff <= 1; 125 | case KING: 126 | return (rowDiff == 2 || colDiff == 2) && (rowDiff <= 2 && colDiff <= 2); 127 | case PAWN: 128 | return rowDiff <= 2 && colDiff <= 2; 129 | case KNIGHT: 130 | return rowDiff <= 3 && colDiff <= 3 && rowDiff + colDiff <= 5; 131 | case QUEEN: 132 | return rowDiff <= 1 || colDiff <= 1 || diagDiff <= 2 || revDiff <= 2; 133 | } 134 | return false; 135 | } 136 | 137 | private static Move[] castlingMoves(final Board board, final Piece king, final Set kingMoves) { 138 | final Move[] castleMoves = new Move[2]; 139 | int castles = 0; 140 | final int row = king.color.ordinal() * 7; 141 | if (king.position.row == row) { 142 | for (int i = 0; i <= 1; i++) { 143 | if (board.canCastle[king.color.ordinal()][i]) { 144 | final Piece rook = board.getPiece(row, i * 7); 145 | if (rook != null && rook.sameType(PieceType.ROOK)) { 146 | final int colDiff = i == 0 ? -1 : 1; 147 | final int col = 4 + colDiff; 148 | final boolean clear = board.isEmpty(row, col) 149 | && kingMoves.stream() 150 | .map(move -> move.target) 151 | .anyMatch(cell -> Cell.get(row, col).equals(cell)); 152 | final Cell kingCastleCell = Cell.get(row, col + colDiff); 153 | if (clear && board.isEmpty(row, col + colDiff) && !getIllegalSquares(board, Color.opponent(king.color), Collections.singleton(kingCastleCell), kingCastleCell).contains(kingCastleCell)) { 154 | if (i == 1 || board.isEmpty(row, 1)) { 155 | castleMoves[castles++] = Move.get(king, Cell.get(row, 4 + 2 * colDiff), false); 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | return castleMoves; 163 | } 164 | 165 | private static boolean xRay(Move move, Board board, Piece king) { 166 | final Line line = new Line(move.piece.position, move.target); 167 | for (int index = 1; index < 8; index++) { 168 | final int row = move.piece.position.row + index * line.rowDiff, col = move.piece.position.col + index * line.colDiff; 169 | if (Utils.withinBoardLimits(row, col)) { 170 | if (!board.isEmpty(row, col)) { 171 | final Piece piece = board.getPiece(row, col); 172 | return piece.color != king.color && (piece.sameType(line.minorPieceType) || piece.sameType(PieceType.QUEEN)); 173 | } 174 | } else { 175 | break; 176 | } 177 | } 178 | return false; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/pieces/Knight.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | import commons.LegalMoves; 4 | import commons.Piece; 5 | import commons.Utils; 6 | import game.Board; 7 | import game.Cell; 8 | import game.Move; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | public class Knight { 14 | public static final int[][] diff = new int[][]{ 15 | {2, 1}, {1, 2}, 16 | {2, -1}, {1, -2}, 17 | {-2, -1}, {-1, -2}, 18 | {-2, 1}, {-1, 2} 19 | }; 20 | 21 | public static LegalMoves getMoveList(Board board, Piece piece) { 22 | final Set moves = new HashSet<>(8); 23 | final Set guards = new HashSet<>(8); 24 | for (final int[] coordinate : diff) { 25 | final int row = piece.position.row + coordinate[0], col = piece.position.col + coordinate[1]; 26 | if (Utils.withinBoardLimits(row, col)) { 27 | if (board.isEmpty(row, col)) { 28 | moves.add(Move.get(piece, Cell.get(row, col), false)); 29 | } else if (board.getPiece(row, col).color != piece.color) { 30 | moves.add(Move.get(piece, Cell.get(row, col), true)); 31 | } else { 32 | guards.add(Move.get(piece, Cell.get(row, col), false)); 33 | } 34 | } 35 | } 36 | return new LegalMoves(moves, guards); 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/pieces/Pawn.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | import commons.Color; 4 | import commons.LegalMoves; 5 | import commons.Piece; 6 | import game.Board; 7 | import game.Cell; 8 | import game.Move; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | public class Pawn { 14 | 15 | public static LegalMoves getMoveList(Board board, Piece piece) { 16 | final Set moves = new HashSet<>(8); 17 | final Set guards = new HashSet<>(8); 18 | final int rowDiff = piece.color == Color.WHITE ? 1 : -1; 19 | final int row = piece.position.row + rowDiff; 20 | if (row < 8 && row >= 0) { 21 | PieceType[] pieces = null; 22 | if (row == 7 || row == 0) { 23 | pieces = new PieceType[]{PieceType.QUEEN, PieceType.ROOK, PieceType.BISHOP, PieceType.KNIGHT}; 24 | } 25 | if (board.isEmpty(row, piece.position.col)) { 26 | addMoves(Cell.get(row, piece.position.col), false, pieces, moves, piece); 27 | } 28 | for (int i = -1; i <= 1; i = i + 2) { 29 | final int col = piece.position.col + i; 30 | if (col < 8 && col >= 0) { 31 | final Piece diag = board.getPiece(row, col); 32 | if (diag != null && diag.color != piece.color) { 33 | addMoves(Cell.get(row, col), true, pieces, moves, piece); 34 | } 35 | if (diag == null || diag.color == piece.color) { 36 | guards.add(Move.get(piece, Cell.get(row, col), false)); 37 | } 38 | } 39 | } 40 | if (piece.position.row == (piece.color == Color.BLACK ? 6 : 1)) { 41 | if (board.isEmpty(row, piece.position.col) 42 | && board.isEmpty(piece.position.row + 2 * rowDiff, piece.position.col)) { 43 | moves.add(Move.get(piece, Cell.get(piece.position.row + 2 * rowDiff, piece.position.col), false)); 44 | } 45 | } 46 | } 47 | addEnPassant(board, rowDiff, moves, piece); 48 | return new LegalMoves(moves, guards); 49 | } 50 | 51 | private static void addEnPassant(Board board, int rowDiff, Set moves, Piece piece) { 52 | if (!board.moveList.isEmpty()) { 53 | final Move lastMove = board.moveList.get(board.moveList.size() - 1); 54 | if (allowsEnPassant(lastMove, piece) && piece.position.row == (piece.color == Color.BLACK ? 3 : 4)) { 55 | final int colDiff = lastMove.target.col - piece.position.col; 56 | final int col = piece.position.col + colDiff; 57 | if (Math.abs(colDiff) == 1 && col < 8 && col >= 0) { 58 | final Piece diag = board.getPiece(piece.position.row, col); 59 | if (diag != null && diag.sameType(PieceType.PAWN) && diag.color != piece.color) { 60 | final Move enPassant = Move.get(piece, Cell.get(piece.position.row + rowDiff, col), true, lastMove.target); 61 | moves.add(enPassant); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | private static boolean allowsEnPassant(final Move move, Piece piece) { 69 | return move.piece.sameType(PieceType.PAWN) && Math.abs(move.piece.position.row - move.target.row) == 2 && Math.abs(move.target.col - piece.position.col) == 1; 70 | } 71 | 72 | private static void addMoves(Cell destination, boolean capture, PieceType[] pieces, Set moves, Piece piece) { 73 | if (pieces == null) { 74 | moves.add(Move.get(piece, destination, capture)); 75 | } else { 76 | for (final PieceType pieceType : pieces) { 77 | moves.add(Move.get(piece, destination, capture, capture ? destination : null, pieceType)); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/pieces/PieceType.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | public enum PieceType { 4 | PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING; 5 | 6 | public char getLetter() { 7 | switch (ordinal()) { 8 | case 0: 9 | return 'p'; 10 | case 1: 11 | return 'n'; 12 | case 2: 13 | return 'b'; 14 | case 3: 15 | return 'r'; 16 | case 4: 17 | return 'q'; 18 | case 5: 19 | return 'k'; 20 | } 21 | throw new RuntimeException(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/pieces/Queen.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | import commons.LegalMoves; 4 | import commons.Piece; 5 | import commons.Utils; 6 | import game.Board; 7 | import game.Move; 8 | 9 | import java.util.HashSet; 10 | import java.util.Set; 11 | import java.util.function.BiFunction; 12 | 13 | public class Queen { 14 | public static final BiFunction movement = (rowStraight, colStraight) -> true; 15 | public static final int MAX_DISTANCE = 7; 16 | 17 | public static LegalMoves getMoveList(Board board, Piece piece) { 18 | return Utils.getMoves(board, MAX_DISTANCE, movement, piece); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/pieces/Rook.java: -------------------------------------------------------------------------------- 1 | package pieces; 2 | 3 | import commons.LegalMoves; 4 | import commons.Piece; 5 | import commons.Utils; 6 | import game.Board; 7 | 8 | import java.util.function.BiFunction; 9 | 10 | 11 | public class Rook { 12 | public static final BiFunction movement = (rowStraight, colStraight) -> rowStraight || colStraight; 13 | public static final int MAX_DISTANCE = 7; 14 | 15 | public static LegalMoves getMoveList(Board board, Piece piece) { 16 | return Utils.getMoves(board, MAX_DISTANCE, movement, piece); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/BishopTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class BishopTest { 7 | @Test 8 | public void moveForward() { 9 | Board board = new Board(); 10 | board.placeBishop(0, 0, Color.BLACK); 11 | System.out.println(board); 12 | System.out.println(board.getPiece(0, 0).getMoveList(board)); 13 | Assert.assertEquals(7, board.getPiece(0, 0).getMoveList(board).size()); 14 | } 15 | 16 | @Test 17 | public void moveAsBishop() { 18 | Board board = new Board(); 19 | board.placeBishop(3, 3, Color.BLACK); 20 | System.out.println(board); 21 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 22 | Assert.assertEquals(13, board.getPiece(3, 3).getMoveList(board).size()); 23 | } 24 | 25 | @Test 26 | public void blockedByPawn() { 27 | Board board = new Board(); 28 | board.placeBishop(3, 3, Color.BLACK); 29 | board.placePawn(4, 4, Color.BLACK); 30 | System.out.println(board); 31 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 32 | Assert.assertEquals(9, board.getPiece(3, 3).getMoveList(board).size()); 33 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 34 | } 35 | 36 | @Test 37 | public void blockedByEnemyPawn() { 38 | Board board = new Board(); 39 | board.placeBishop(3, 3, Color.BLACK); 40 | board.placePawn(4, 4, Color.WHITE); 41 | System.out.println(board); 42 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 43 | Assert.assertEquals(10, board.getPiece(3, 3).getMoveList(board).size()); 44 | } 45 | 46 | @Test 47 | public void surroundedByPieces() { 48 | Board board = new Board(); 49 | board.placeBishop(3, 3, Color.BLACK); 50 | board.placePawn(4, 4, Color.BLACK); 51 | board.placePawn(2, 2, Color.BLACK); 52 | board.placeRook(4, 2, Color.BLACK); 53 | board.placeRook(2, 4, Color.BLACK); 54 | System.out.println(board); 55 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 56 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).isEmpty()); 57 | } 58 | 59 | @Test 60 | public void surroundedByEnemy() { 61 | Board board = new Board(); 62 | board.placeBishop(3, 3, Color.BLACK); 63 | board.placePawn(4, 4, Color.WHITE); 64 | board.placePawn(2, 2, Color.WHITE); 65 | board.placeRook(4, 2, Color.WHITE); 66 | board.placeRook(2, 4, Color.WHITE); 67 | System.out.println(board); 68 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 69 | Assert.assertEquals(4, board.getPiece(3, 3).getMoveList(board).size()); 70 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().allMatch(c -> c.captureMove)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/BoardTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import game.Cell; 4 | import game.Move; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import pieces.PieceType; 8 | 9 | import java.util.List; 10 | import java.util.NoSuchElementException; 11 | 12 | public class BoardTest { 13 | 14 | @Test 15 | public void startingBoard() { 16 | Board board = Board.getStartBoard(); 17 | final List legalMoves = board.getLegalMoves(); 18 | Assert.assertEquals(20, legalMoves.size()); 19 | } 20 | 21 | @Test 22 | public void blockCheck() { 23 | Board board = new Board(); 24 | board.placeKing(2, 5, Color.WHITE); 25 | board.placeRook(2, 0, Color.BLACK); 26 | board.placeKing(4, 4, Color.BLACK); 27 | board.placeBishop(1, 1, Color.WHITE); 28 | board.inCheck = true; 29 | System.out.println(board); 30 | System.out.println(board.getLegalMoves()); 31 | Assert.assertEquals(6, board.getLegalMoves().size()); 32 | } 33 | 34 | @Test 35 | public void captureAndReturnCheck() { 36 | Board board = new Board(); 37 | board.placeKing(2, 5, Color.WHITE); 38 | board.placeRook(2, 2, Color.BLACK); 39 | board.placeKing(4, 4, Color.BLACK); 40 | board.placeBishop(1, 3, Color.WHITE); 41 | board.inCheck = true; 42 | System.out.println(board); 43 | System.out.println(board.getLegalMoves()); 44 | Assert.assertEquals(6, board.getLegalMoves().size()); 45 | Assert.assertFalse(board.isDraw()); 46 | Assert.assertEquals(-2, board.evaluation(board.getLegalMoves().size()), 0.3); 47 | board.makeMove(board.getLegalMoves().stream().filter(c -> c.captureMove).findAny().get()); 48 | System.out.println(board); 49 | System.out.println(board.getLegalMoves()); 50 | Assert.assertTrue(board.inCheck); 51 | Assert.assertEquals(Color.BLACK, board.playerToMove); 52 | Assert.assertTrue(board.isDraw()); 53 | Assert.assertEquals(4, board.getLegalMoves().size()); 54 | } 55 | 56 | @Test 57 | public void checkMate() { 58 | Board board = new Board(); 59 | board.placeKing(7, 5, Color.WHITE); 60 | board.placeRook(7, 2, Color.BLACK); 61 | board.placeRook(6, 3, Color.BLACK); 62 | board.placeKing(4, 4, Color.BLACK); 63 | board.inCheck = true; 64 | System.out.println(board); 65 | System.out.println(board.getLegalMoves()); 66 | Assert.assertTrue(board.getLegalMoves().isEmpty()); 67 | Assert.assertEquals(Integer.MIN_VALUE, board.evaluation(board.getLegalMoves().size()), 0.0001); 68 | } 69 | 70 | @Test 71 | public void staleMate() { 72 | Board board = new Board(); 73 | board.placeKing(7, 5, Color.WHITE); 74 | board.placeRook(6, 2, Color.BLACK); 75 | board.placeRook(3, 4, Color.BLACK); 76 | board.placeKing(7, 7, Color.BLACK); 77 | System.out.println(board); 78 | System.out.println(board.getLegalMoves()); 79 | Assert.assertTrue(board.getLegalMoves().isEmpty()); 80 | Assert.assertEquals(0, board.evaluation(board.getLegalMoves().size()), 0.000001); 81 | } 82 | 83 | @Test 84 | public void checkMateIn1() { 85 | Board board = new Board(); 86 | board.placeKing(7, 5, Color.WHITE); 87 | board.placeRook(6, 2, Color.BLACK); 88 | board.placeRook(3, 4, Color.BLACK); 89 | board.placeKing(7, 7, Color.BLACK); 90 | board.placeQueen(0, 6, Color.WHITE); 91 | System.out.println(board); 92 | System.out.println(board.getLegalMoves()); 93 | Assert.assertEquals(21, board.getLegalMoves().size()); 94 | Assert.assertEquals(-1, board.evaluation(board.getLegalMoves().size()), 0.3); 95 | } 96 | 97 | @Test(expected = NoSuchElementException.class) 98 | public void castleFail() { 99 | Board board = new Board(); 100 | board.placeKing(0, 4, Color.WHITE); 101 | board.placeKing(7, 4, Color.BLACK); 102 | board.placeRook(7, 7, Color.BLACK); 103 | board.placeRook(7, 0, Color.BLACK); 104 | board.placeBishop(7, 2, Color.BLACK); 105 | board.placePawn(6, 5, Color.BLACK); 106 | board.placePawn(6, 6, Color.BLACK); 107 | board.placeRook(4, 7, Color.WHITE); 108 | System.out.println(board); 109 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 110 | Assert.assertEquals(5, board.getPiece(7, 4).getMoveList(board).size()); 111 | Assert.assertTrue(board.getPiece(7, 4).getMoveList(board).stream().anyMatch(c -> Math.abs(c.piece.position.col - c.target.col) == 2)); 112 | board.makeMove(board.getPiece(4, 7).getMoveList(board).stream().filter(c -> c.captureMove).findAny().get()); 113 | System.out.println(board); 114 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 115 | board.makeMove(board.getPiece(7, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 116 | } 117 | 118 | @Test(expected = NoSuchElementException.class) 119 | public void castleFailWithKnightCapture() { 120 | Board board = new Board(); 121 | board.placeKing(0, 4, Color.WHITE); 122 | board.placeKnight(6, 5, Color.WHITE); 123 | board.placeKing(7, 4, Color.BLACK); 124 | board.placeRook(7, 7, Color.BLACK); 125 | board.placeRook(7, 0, Color.BLACK); 126 | board.placeBishop(7, 2, Color.BLACK); 127 | board.placePawn(6, 6, Color.BLACK); 128 | System.out.println(board); 129 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 130 | Assert.assertEquals(5, board.getPiece(7, 4).getMoveList(board).size()); 131 | Assert.assertTrue(board.getPiece(7, 4).getMoveList(board).stream().anyMatch(c -> Math.abs(c.piece.position.col - c.target.col) == 2)); 132 | board.makeMove(board.getPiece(6, 5).getMoveList(board).stream().filter(c -> c.captureMove).findAny().get()); 133 | System.out.println(board); 134 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 135 | board.makeMove(board.getPiece(7, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 136 | } 137 | 138 | @Test 139 | public void pawnIsPinned() { 140 | Board board = new Board(); 141 | board.placeKing(4, 0, Color.WHITE); 142 | board.placeRook(3, 1, Color.WHITE); 143 | board.placePawn(4, 1, Color.WHITE); 144 | board.placePawn(1, 4, Color.WHITE); 145 | board.placePawn(1, 6, Color.WHITE); 146 | board.placeKing(3, 7, Color.BLACK); 147 | board.placeRook(4, 7, Color.BLACK); 148 | board.placePawn(3, 5, Color.BLACK); 149 | board.placePawn(6, 2, Color.BLACK); 150 | board.placePawn(5, 3, Color.BLACK); 151 | System.out.println(board); 152 | System.out.println(board.getPiece(4, 1).getMoveList(board)); 153 | System.out.println(board.getLegalMoves()); 154 | Assert.assertTrue(board.getLegalMoves().stream().filter(move -> move.piece.sameType(PieceType.PAWN)).noneMatch(move -> move.target.row == 5)); 155 | board.makeMove(board.getPiece(1, 4).getMoveList(board).stream().findFirst().get()); 156 | System.out.println(board); 157 | System.out.println(board.getPiece(6, 2).getMoveList(board)); 158 | board.makeMove(board.getPiece(6, 2).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.row - c.target.row) == 2).findAny().get()); 159 | System.out.println(board); 160 | System.out.println(board.getPiece(4, 1).getMoveList(board)); 161 | System.out.println(board.getLegalMoves()); 162 | Assert.assertTrue(board.getLegalMoves().stream().filter(move -> move.piece.sameType(PieceType.PAWN)).noneMatch(move -> move.target.row == 5 && move.captureMove)); 163 | } 164 | 165 | @Test 166 | public void doubleCheck() { 167 | Board board = new Board(); 168 | board.placeKing(4, 0, Color.WHITE); 169 | board.placeRook(3, 1, Color.WHITE); 170 | board.placeKing(3, 7, Color.BLACK); 171 | board.placeRook(1, 0, Color.BLACK); 172 | board.placeKnight(2, 0, Color.BLACK); 173 | System.out.println(board); 174 | System.out.println(board.getPiece(4, 0).getMoveList(board)); 175 | System.out.println(board.getLegalMoves()); 176 | board.playerToMove = Color.BLACK; 177 | board.makeMove(Move.get(board.getPiece(2, 0), Cell.get(3, 2), false)); 178 | System.out.println(board); 179 | System.out.println(board.getLegalMoves()); 180 | Assert.assertTrue(board.getLegalMoves().stream().allMatch(move -> move.piece.sameType(PieceType.KING))); 181 | } 182 | 183 | @Test 184 | public void pawnPromotion() { 185 | Board board = new Board(); 186 | board.placeKing(4, 0, Color.WHITE); 187 | board.placeKing(3, 7, Color.BLACK); 188 | board.placePawn(1, 1, Color.BLACK); 189 | board.playerToMove = Color.BLACK; 190 | System.out.println(board); 191 | System.out.println(board.getPiece(1, 1).getMoveList(board)); 192 | System.out.println(board.getLegalMoves()); 193 | board.makeMove(Move.get(board.getPiece(1, 1), Cell.get(0, 1), false, null, PieceType.QUEEN)); 194 | System.out.println(board); 195 | System.out.println(board.getLegalMoves()); 196 | Assert.assertTrue(board.playerPieces.get(Color.BLACK).stream().anyMatch(piece -> piece.sameType(PieceType.QUEEN))); 197 | } 198 | 199 | @Test 200 | public void pawnPromoteToKnight() { 201 | Board board = new Board(); 202 | board.placeKing(4, 0, Color.WHITE); 203 | board.placeKing(3, 7, Color.BLACK); 204 | board.placePawn(1, 1, Color.BLACK); 205 | board.playerToMove = Color.BLACK; 206 | System.out.println(board); 207 | System.out.println(board.getPiece(1, 1).getMoveList(board)); 208 | System.out.println(board.getLegalMoves()); 209 | board.makeMove(Move.get(board.getPiece(1, 1), Cell.get(0, 1), false, null, PieceType.KNIGHT)); 210 | System.out.println(board); 211 | System.out.println(board.getLegalMoves()); 212 | Assert.assertTrue(board.playerPieces.get(Color.BLACK).stream().anyMatch(piece -> piece.sameType(PieceType.KNIGHT))); 213 | } 214 | 215 | @Test 216 | public void kingAndQueenOut() { 217 | Board board = Board.getStartBoard(); 218 | board = board.copy(); 219 | board.makeMove(board.getLegalMoves() 220 | .stream() 221 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 222 | .filter(c -> c.piece.position.equals(Cell.get(1, 3))) 223 | .filter(c -> c.target.equals(Cell.get(2, 3))) 224 | .findAny() 225 | .get()); 226 | board = board.copy(); 227 | board.makeMove(board.getLegalMoves() 228 | .stream() 229 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 230 | .filter(c -> c.piece.position.equals(Cell.get(6, 4))) 231 | .filter(c -> c.target.equals(Cell.get(5, 4))) 232 | .findAny() 233 | .get()); 234 | board = board.copy(); 235 | board.makeMove(board.getLegalMoves() 236 | .stream() 237 | .filter(c -> c.piece.sameType(PieceType.KING)) 238 | .filter(c -> c.piece.position.equals(Cell.get(0, 4))) 239 | .filter(c -> c.target.equals(Cell.get(1, 3))) 240 | .findAny() 241 | .get()); 242 | board = board.copy(); 243 | board.makeMove(board.getLegalMoves() 244 | .stream() 245 | .filter(c -> c.piece.sameType(PieceType.QUEEN)) 246 | .filter(c -> c.piece.position.equals(Cell.get(7, 3))) 247 | .filter(c -> c.target.equals(Cell.get(6, 4))) 248 | .findAny() 249 | .get()); 250 | System.out.println(board); 251 | System.out.println(board.getLegalMoves()); 252 | Assert.assertEquals(23, board.getLegalMoves().size()); 253 | } 254 | 255 | @Test 256 | public void kingAndKnightOut() { 257 | Board board = Board.getStartBoard(); 258 | board = board.copy(); 259 | board.makeMove(board.getLegalMoves() 260 | .stream() 261 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 262 | .filter(c -> c.piece.position.equals(Cell.get(1, 3))) 263 | .filter(c -> c.target.equals(Cell.get(2, 3))) 264 | .findAny() 265 | .get()); 266 | board = board.copy(); 267 | board.makeMove(board.getLegalMoves() 268 | .stream() 269 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 270 | .filter(c -> c.piece.position.equals(Cell.get(6, 4))) 271 | .filter(c -> c.target.equals(Cell.get(5, 4))) 272 | .findAny() 273 | .get()); 274 | board = board.copy(); 275 | board.makeMove(board.getLegalMoves() 276 | .stream() 277 | .filter(c -> c.piece.sameType(PieceType.KING)) 278 | .filter(c -> c.piece.position.equals(Cell.get(0, 4))) 279 | .filter(c -> c.target.equals(Cell.get(1, 3))) 280 | .findAny() 281 | .get()); 282 | board = board.copy(); 283 | board.makeMove(board.getLegalMoves() 284 | .stream() 285 | .filter(c -> c.piece.sameType(PieceType.KNIGHT)) 286 | .filter(c -> c.piece.position.equals(Cell.get(7, 6))) 287 | .filter(c -> c.target.equals(Cell.get(5, 5))) 288 | .findAny() 289 | .get()); 290 | System.out.println(board); 291 | System.out.println(board.getLegalMoves()); 292 | Assert.assertEquals(23, board.getLegalMoves().size()); 293 | } 294 | 295 | @Test 296 | public void kingAndPawnOut() { 297 | Board board = Board.getStartBoard(); 298 | board = board.copy(); 299 | board.makeMove(board.getLegalMoves() 300 | .stream() 301 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 302 | .filter(c -> c.piece.position.equals(Cell.get(1, 3))) 303 | .filter(c -> c.target.equals(Cell.get(2, 3))) 304 | .findAny() 305 | .get()); 306 | board = board.copy(); 307 | board.makeMove(board.getLegalMoves() 308 | .stream() 309 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 310 | .filter(c -> c.piece.position.equals(Cell.get(6, 6))) 311 | .filter(c -> c.target.equals(Cell.get(4, 6))) 312 | .findAny() 313 | .get()); 314 | board = board.copy(); 315 | board.makeMove(board.getLegalMoves() 316 | .stream() 317 | .filter(c -> c.piece.sameType(PieceType.KING)) 318 | .filter(c -> c.piece.position.equals(Cell.get(0, 4))) 319 | .filter(c -> c.target.equals(Cell.get(1, 3))) 320 | .findAny() 321 | .get()); 322 | board = board.copy(); 323 | board.makeMove(board.getLegalMoves() 324 | .stream() 325 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 326 | .filter(c -> c.piece.position.equals(Cell.get(4, 6))) 327 | .filter(c -> c.target.equals(Cell.get(3, 6))) 328 | .findAny() 329 | .get()); 330 | System.out.println(board); 331 | System.out.println(board.getLegalMoves()); 332 | Assert.assertEquals(22, board.getLegalMoves().size()); 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/test/java/EngineTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import commons.Piece; 3 | import game.Board; 4 | import game.Cell; 5 | import game.Move; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | public class EngineTest { 10 | @Test 11 | public void fenRepresentation() { 12 | Board board = Board.getStartBoard(); 13 | Assert.assertEquals("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -", board.fenRepresentation()); 14 | board.makeMove(Move.get(board.getPiece(1, 4), Cell.get(3, 4), false)); 15 | Assert.assertEquals("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3", board.fenRepresentation()); 16 | board.makeMove(Move.get(board.getPiece(6, 2), Cell.get(4, 2), false)); 17 | Assert.assertEquals("rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6", board.fenRepresentation()); 18 | board.makeMove(Move.get(board.getPiece(0, 6), Cell.get(2, 5), false)); 19 | Assert.assertEquals("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq -", board.fenRepresentation()); 20 | } 21 | 22 | @Test 23 | public void otherBoard() { 24 | Board board = Board.getStartBoard(); 25 | board.makeMove(Move.get(board.getPiece(1, 4), Cell.get(3, 4), false)); 26 | Engine engine = new Engine(); 27 | board = board.copy(); 28 | board.makeMove(Move.get(board.getPiece(6, 3), Cell.get(5, 3), false)); 29 | board = board.copy(); 30 | board.makeMove(Move.get(board.getPiece(3, 4), Cell.get(4, 4), false)); 31 | board = board.copy(); 32 | board.makeMove(Move.get(board.getPiece(6, 5), Cell.get(4, 5), false)); 33 | // board = board.copy(); 34 | // board.makeMove(board.getPiece(4, 4).getMoveList(board).stream().filter(c -> c.captureMove).filter(c -> c.captureCell.equals(Cell.get(4, 5))).findAny().get()); 35 | final int positions = engine.countAllMoves(board, 2); 36 | System.out.println(board); 37 | System.out.println("NUMBER OF POSITIONS: " + positions); 38 | System.out.println(board.fenRepresentation()); 39 | Assert.assertEquals(729, positions); 40 | } 41 | 42 | @Test 43 | public void countMovesAfterMove() { 44 | Board board = Board.getStartBoard(); 45 | Piece piece = board.getPiece(1, 1); 46 | board.makeMove(Move.get(piece, Cell.get(2, 1), false)); 47 | piece = board.getPiece(6, 1); 48 | board.makeMove(Move.get(piece, Cell.get(5, 0), false)); 49 | Engine engine = new Engine(); 50 | System.out.println(board); 51 | Assert.assertEquals("rnbqkbnr/p1pppppp/p7/8/8/1P6/P1PPPPPP/RNBQKBNR w KQkq -", board.fenRepresentation()); 52 | Assert.assertEquals(21, engine.countAllMoves(board, 1)); 53 | } 54 | 55 | @Test 56 | public void countMovesAtPosition31() { 57 | Board board = Board.getBoard("8/2p5/K2p4/1P4kr/1R3p2/8/4P1P1/8 w - -"); 58 | System.out.println(board); 59 | Engine engine = new Engine(); 60 | Assert.assertEquals(1563505, engine.countAllMoves(board, 5)); 61 | } 62 | 63 | @Test 64 | public void countMovesAtPosition32() { 65 | Board board = Board.getBoard("8/1Kp5/3p4/1P4kr/1R3p2/8/4P1P1/8 b - -"); 66 | System.out.println(board); 67 | Engine engine = new Engine(); 68 | Assert.assertEquals(129425, engine.countAllMoves(board, 4)); 69 | } 70 | 71 | @Test 72 | public void countMovesAtPosition33() { 73 | Board board = Board.getBoard("8/1Kp5/3p4/1P3k1r/1R3p2/8/4P1P1/8 w - -"); 74 | System.out.println(board); 75 | Engine engine = new Engine(); 76 | Assert.assertEquals(6416, engine.countAllMoves(board, 3, 3)); 77 | } 78 | 79 | @Test 80 | public void countDoublePawnMovePossibilities() { 81 | Board board = Board.getBoard("8/1Kp5/3p4/1P3k1r/1R2Pp2/8/6P1/8 b - e3"); 82 | board.inCheck = true; 83 | System.out.println(board); 84 | Engine engine = new Engine(); 85 | Assert.assertEquals(120, engine.countAllMoves(board, 2, 2)); 86 | } 87 | 88 | @Test 89 | public void countEnpassantPossibilities() { 90 | Board board = Board.getBoard("8/1Kp5/3p4/1P3k1r/1R6/4p3/6P1/8 w - -"); 91 | System.out.println(board); 92 | Engine engine = new Engine(); 93 | Assert.assertEquals(20, engine.countAllMoves(board, 1)); 94 | } 95 | 96 | 97 | @Test 98 | public void countMovesAtPosition34() { 99 | Board board = Board.getBoard("8/2K5/3p4/1P3k1r/1R3p2/8/4P1P1/8 b - -"); 100 | System.out.println(board); 101 | Engine engine = new Engine(); 102 | Assert.assertEquals(318, engine.countAllMoves(board, 2)); 103 | } 104 | 105 | @Test 106 | public void countMovesAtPosition35() { 107 | Board board = Board.getBoard("8/2K5/3p4/1P2k2r/1R3p2/8/4P1P1/8 w - -"); 108 | System.out.println(board); 109 | Engine engine = new Engine(); 110 | Assert.assertEquals(20, engine.countAllMoves(board, 1)); 111 | } 112 | 113 | @Test 114 | public void countMovesAtPositionRandom() { 115 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R4K1R b kq -"); 116 | System.out.println(board); 117 | Engine engine = new Engine(); 118 | final int positions = engine.countAllMoves(board, 3); 119 | System.out.println("NUMBER OF POSITIONS: " + positions); 120 | Assert.assertEquals(77887, positions); 121 | } 122 | 123 | @Test 124 | public void countMovesAtPositionInCheck() { 125 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBPpP/R4K1R w kq -"); 126 | board.inCheck = true; 127 | System.out.println(board); 128 | Engine engine = new Engine(); 129 | final int positions = engine.countAllMoves(board, 2); 130 | System.out.println("NUMBER OF POSITIONS: " + positions); 131 | Assert.assertEquals(188, positions); 132 | } 133 | 134 | @Test 135 | public void countMovesAtPositionMovesAfterCheck() { 136 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBPpP/R3K2R b kq -"); 137 | System.out.println(board); 138 | Engine engine = new Engine(); 139 | final int positions = engine.countAllMoves(board, 1); 140 | System.out.println("NUMBER OF POSITIONS: " + positions); 141 | Assert.assertEquals(52, positions); 142 | } 143 | 144 | @Test 145 | public void countMovesAtPositionMovesAfterCheck2() { 146 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBPpP/R5KR b kq -"); 147 | System.out.println(board); 148 | Engine engine = new Engine(); 149 | final int positions = engine.countAllMoves(board, 1); 150 | System.out.println("NUMBER OF POSITIONS: " + positions); 151 | Assert.assertEquals(48, positions); 152 | } 153 | 154 | @Test 155 | public void queenToEdge() { 156 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN2Q/1p2P3/2N4p/PPPBBPPP/R3K2R b KQkq -"); 157 | System.out.println(board); 158 | Engine engine = new Engine(); 159 | final int positions = engine.countAllMoves(board, 3); 160 | System.out.println("NUMBER OF POSITIONS: " + positions); 161 | Assert.assertEquals(95034, positions); 162 | } 163 | 164 | @Test 165 | public void queenToDeath() { 166 | Board board = Board.getBoard("r3k1r1/p1ppqpb1/bn2pnp1/3PN2Q/1p2P3/2N4p/PPPBBPPP/R3K2R w KQq -"); 167 | System.out.println(board); 168 | Engine engine = new Engine(); 169 | final int positions = engine.countAllMoves(board, 2); 170 | System.out.println("NUMBER OF POSITIONS: " + positions); 171 | Assert.assertEquals(2079, positions); 172 | } 173 | 174 | @Test 175 | public void queenToDeathBlockCastle() { 176 | Board board = Board.getBoard("r3k1r1/p1ppqpb1/Bn2pnp1/3PN2Q/1p2P3/2N4p/PPPB1PPP/R3K2R b KQq -"); 177 | System.out.println(board); 178 | Engine engine = new Engine(); 179 | final int positions = engine.countAllMoves(board, 1); 180 | System.out.println("NUMBER OF POSITIONS: " + positions); 181 | Assert.assertEquals(32, positions); 182 | } 183 | 184 | @Test 185 | public void queenDeterminedToCommitSuicide() { 186 | Board board = Board.getBoard("r3k1rQ/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N4p/PPPBBPPP/R3K2R b KQq -"); 187 | System.out.println(board); 188 | Engine engine = new Engine(); 189 | final int positions = engine.countAllMoves(board, 1); 190 | System.out.println("NUMBER OF POSITIONS: " + positions); 191 | Assert.assertEquals(38, positions); 192 | } 193 | 194 | @Test 195 | public void rookEndgameWithKing() { 196 | Board board = Board.getBoard("8/2p5/3p4/KP4kr/5p2/8/4P1P1/1R6 w - -"); 197 | System.out.println(board); 198 | Engine engine = new Engine(); 199 | final int positions = engine.countAllMoves(board, 4, 4); 200 | System.out.println("NUMBER OF POSITIONS: " + positions); 201 | Assert.assertEquals(104371, positions); 202 | } 203 | 204 | @Test 205 | public void rookEndgameWithKing8() { 206 | Board board = Board.getBoard("8/2p5/3p4/KP4kr/5pP1/8/4P3/1R6 b - g3"); 207 | System.out.println(board); 208 | Engine engine = new Engine(); 209 | final int positions = engine.countAllMoves(board, 3, 1); 210 | System.out.println("NUMBER OF POSITIONS: " + positions); 211 | Assert.assertEquals(5260, positions); 212 | } 213 | 214 | @Test 215 | public void rookEndgameWithKing8_1() { 216 | Board board = Board.getBoard("8/8/2pp4/KP4kr/5pP1/8/4P3/1R6 w - -"); 217 | System.out.println(board); 218 | Engine engine = new Engine(); 219 | final int positions = engine.countAllMoves(board, 2, 2); 220 | System.out.println("NUMBER OF POSITIONS: " + positions); 221 | Assert.assertEquals(295, positions); 222 | } 223 | 224 | @Test 225 | public void rookEndgameWithKing9() { 226 | Board board = Board.getBoard("8/2p5/3p4/KP4k1/5pP1/8/4P3/1R5r w - -"); 227 | System.out.println(board); 228 | Engine engine = new Engine(); 229 | final int positions = engine.countAllMoves(board, 2, 1); 230 | System.out.println("NUMBER OF POSITIONS: " + positions); 231 | Assert.assertEquals(328, positions); 232 | } 233 | 234 | @Test 235 | public void rookEndgameWithKing10() { 236 | Board board = Board.getBoard("8/2p5/3p4/KP4k1/5pP1/8/4P3/6Rr b - -"); 237 | System.out.println(board); 238 | Engine engine = new Engine(); 239 | final int positions = engine.countAllMoves(board, 1, 1); 240 | System.out.println("NUMBER OF POSITIONS: " + positions); 241 | Assert.assertEquals(16, positions); 242 | } 243 | 244 | @Test 245 | public void rookEndgameWithKingPawn() { 246 | Board board = Board.getBoard("8/2p5/3p4/KP4kr/4Pp2/8/6P1/1R6 b - e3"); 247 | System.out.println(board); 248 | Engine engine = new Engine(); 249 | final int positions = engine.countAllMoves(board, 3, 3); 250 | System.out.println("NUMBER OF POSITIONS: " + positions); 251 | Assert.assertEquals(5503, positions); 252 | } 253 | 254 | @Test 255 | public void rookEndgameWithKingRookMove() { 256 | Board board = Board.getBoard("8/2p5/3p4/KP4k1/4Pp2/7r/6P1/1R6 w - -"); 257 | System.out.println(board); 258 | Engine engine = new Engine(); 259 | final int positions = engine.countAllMoves(board, 2, 2); 260 | System.out.println("NUMBER OF POSITIONS: " + positions); 261 | Assert.assertEquals(412, positions); 262 | } 263 | 264 | @Test 265 | public void rookEndgameWithKingRookMoving() { 266 | Board board = Board.getBoard("7r/2p5/3p4/KP4k1/4Pp2/8/6P1/1R6 w - -"); 267 | System.out.println(board); 268 | Engine engine = new Engine(); 269 | final int positions = engine.countAllMoves(board, 2, 2); 270 | System.out.println("NUMBER OF POSITIONS: " + positions); 271 | Assert.assertEquals(407, positions); 272 | } 273 | 274 | @Test 275 | public void rookEndgameWithKingRookMoveA() { 276 | Board board = Board.getBoard("8/2p5/8/KP1p2kr/4Pp2/8/6P1/1R6 w - -"); 277 | System.out.println(board); 278 | Engine engine = new Engine(); 279 | final int positions = engine.countAllMoves(board, 2, 2); 280 | System.out.println("NUMBER OF POSITIONS: " + positions); 281 | Assert.assertEquals(305, positions); 282 | } 283 | 284 | @Test 285 | public void rookEndgameWithKing2() { 286 | Board board = Board.getBoard("8/2p5/3p4/KP4kr/5p2/8/4P1P1/6R1 b - -"); 287 | System.out.println(board); 288 | Engine engine = new Engine(); 289 | final int positions = engine.countAllMoves(board, 3); 290 | System.out.println("NUMBER OF POSITIONS: " + positions); 291 | Assert.assertEquals(5007, positions); 292 | } 293 | 294 | @Test 295 | public void rookEndgameWithKing3() { 296 | Board board = Board.getBoard("8/2p5/3p4/KP4k1/5p1r/8/4P1P1/6R1 w - -"); 297 | System.out.println(board); 298 | Engine engine = new Engine(); 299 | final int positions = engine.countAllMoves(board, 2); 300 | System.out.println("NUMBER OF POSITIONS: " + positions); 301 | Assert.assertEquals(271, positions); 302 | } 303 | 304 | @Test 305 | public void rookEndgameWithKing4() { 306 | Board board = Board.getBoard("8/2p5/3p4/KP4k1/5pPr/8/4P3/6R1 b - g3"); 307 | System.out.println(board); 308 | Engine engine = new Engine(); 309 | final int positions = engine.countAllMoves(board, 1); 310 | System.out.println("NUMBER OF POSITIONS: " + positions); 311 | Assert.assertEquals(16, positions); 312 | } 313 | 314 | @Test 315 | public void rookEndgameWithKing5() { 316 | Board board = Board.getBoard("8/2p5/3p4/KP4kr/1R3p2/8/4P1P1/8 b - -"); 317 | System.out.println(board); 318 | Engine engine = new Engine(); 319 | final int positions = engine.countAllMoves(board, 3); 320 | System.out.println("NUMBER OF POSITIONS: " + positions); 321 | Assert.assertEquals(5013, positions); 322 | } 323 | 324 | @Test 325 | public void rookEndgameWithKing6() { 326 | Board board = Board.getBoard("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"); 327 | System.out.println(board); 328 | Engine engine = new Engine(); 329 | final int positions = engine.countAllMoves(board, 2); 330 | System.out.println("NUMBER OF POSITIONS: " + positions); 331 | Assert.assertEquals(191, positions); 332 | } 333 | 334 | @Test 335 | public void moveKingInChaos() { 336 | Board board = Board.getBoard("r6r/Ppppkppp/1b3nbN/nPP5/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 w - -"); 337 | System.out.println(board); 338 | Engine engine = new Engine(); 339 | final int positions = engine.countAllMoves(board, 4); 340 | System.out.println("NUMBER OF POSITIONS: " + positions); 341 | Assert.assertEquals(2539008, positions); 342 | } 343 | 344 | @Test 345 | public void moveKingInChaos2() { 346 | Board board = Board.getBoard("r6r/Ppppkppp/1P3nbN/nP6/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 b - -"); 347 | board.inCheck = true; 348 | System.out.println(board); 349 | Engine engine = new Engine(); 350 | final int positions = engine.countAllMoves(board, 3); 351 | System.out.println("NUMBER OF POSITIONS: " + positions); 352 | Assert.assertEquals(9501, positions); 353 | } 354 | 355 | @Test 356 | public void moveKingInChaos20() { 357 | Board board = Board.getBoard("r2k3r/Pppp1ppp/1P3nbN/nP6/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 w - -"); 358 | System.out.println(board); 359 | Engine engine = new Engine(); 360 | final int positions = engine.countAllMoves(board, 2); 361 | System.out.println("NUMBER OF POSITIONS: " + positions); 362 | Assert.assertEquals(1475, positions); 363 | } 364 | 365 | @Test 366 | public void moveKingInChaos21() { 367 | Board board = Board.getBoard("r2k3r/PpPp1ppp/5nbN/nP6/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 b - -"); 368 | board.inCheck = true; 369 | System.out.println(board); 370 | Engine engine = new Engine(); 371 | final int positions = engine.countAllMoves(board, 1); 372 | System.out.println("NUMBER OF POSITIONS: " + positions); 373 | Assert.assertEquals(3, positions); 374 | } 375 | 376 | @Test 377 | public void moveKingInChaos0() { 378 | Board board = Board.getBoard("r6r/Pppp1ppp/1P2knbN/nP6/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 w - -"); 379 | System.out.println(board); 380 | Engine engine = new Engine(); 381 | final int positions = engine.countAllMoves(board, 2); 382 | System.out.println("NUMBER OF POSITIONS: " + positions); 383 | Assert.assertEquals(1614, positions); 384 | } 385 | 386 | @Test 387 | public void moveKingInChaos1() { 388 | Board board = Board.getBoard("r6r/Pppp1ppp/1P2knbN/nP2P3/BB6/q4N2/Pp1P2PP/R2Q1RK1 b - -"); 389 | System.out.println(board); 390 | Engine engine = new Engine(); 391 | final int positions = engine.countAllMoves(board, 1); 392 | System.out.println("NUMBER OF POSITIONS: " + positions); 393 | Assert.assertEquals(51, positions); 394 | } 395 | 396 | @Test 397 | public void moveKingInChaos3() { 398 | Board board = Board.getBoard("r6r/Pp1pkppp/1P3nbN/nPp5/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 w - c6"); 399 | System.out.println(board); 400 | Engine engine = new Engine(); 401 | final int positions = engine.countAllMoves(board, 2); 402 | System.out.println("NUMBER OF POSITIONS: " + positions); 403 | Assert.assertEquals(1550, positions); 404 | } 405 | 406 | @Test 407 | public void moveKingInChaos4() { 408 | Board board = Board.getBoard("r6r/Pp1pkppp/1PP2nbN/n7/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 b - -"); 409 | board.inCheck = true; 410 | System.out.println(board); 411 | Engine engine = new Engine(); 412 | final int positions = engine.countAllMoves(board, 1); 413 | System.out.println("NUMBER OF POSITIONS: " + positions); 414 | Assert.assertEquals(5, positions); 415 | } 416 | 417 | 418 | @Test 419 | public void kingCastling() { 420 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q2/PPPBBPpP/R3K2R b kq -"); 421 | System.out.println(board); 422 | System.out.println(board.getKing(Color.BLACK).getMoveList(board).size()); 423 | System.out.println(board.getKing(Color.BLACK).getMoveList(board)); 424 | } 425 | } 426 | 427 | /* 428 | e2e4: 120 429 | g2g4: 137 430 | 431 | 432 | e2e4: 122 433 | g2g4: 139 434 | */ -------------------------------------------------------------------------------- /src/test/java/IntenseTest.java: -------------------------------------------------------------------------------- 1 | import game.Board; 2 | import game.Move; 3 | import org.junit.Assert; 4 | import org.junit.Ignore; 5 | import org.junit.Test; 6 | 7 | @Ignore 8 | public class IntenseTest { 9 | @Test 10 | public void countMovesAtPosition4() { 11 | Board board = Board.getBoard("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1"); 12 | board.inCheck = true; 13 | System.out.println(board); 14 | Engine engine = new Engine(); 15 | Assert.assertEquals(422333, engine.countAllMoves(board, 4)); 16 | Assert.assertEquals(15833292, engine.countAllMoves(board, 5)); 17 | Assert.assertEquals(706045033, engine.countAllMoves(board, 6)); 18 | } 19 | 20 | @Test 21 | public void countMovesAtPosition5() { 22 | Board board = Board.getBoard("rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8"); 23 | System.out.println(board); 24 | Engine engine = new Engine(); 25 | Assert.assertEquals(62379, engine.countAllMoves(board, 3)); 26 | Assert.assertEquals(2103487, engine.countAllMoves(board, 4)); 27 | Assert.assertEquals(89941194, engine.countAllMoves(board, 5)); 28 | } 29 | 30 | @Test 31 | public void countMovesAtPosition6() { 32 | Board board = Board.getBoard("r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10"); 33 | System.out.println(board); 34 | Engine engine = new Engine(); 35 | final int positions = engine.countAllMoves(board, 5); 36 | System.out.println("NUMBER OF POSITIONS: " + positions); 37 | Assert.assertEquals(164075551, positions); 38 | } 39 | 40 | @Test 41 | public void rookEndgame() { 42 | Board board = Board.getBoard("8/2p5/3p4/KP5r/5p1k/8/4P1P1/1R6 b - -"); 43 | System.out.println(board); 44 | Engine engine = new Engine(); 45 | final int positions = engine.countAllMoves(board, 5); 46 | System.out.println("NUMBER OF POSITIONS: " + positions); 47 | Assert.assertEquals(1160678, positions); 48 | } 49 | 50 | @Test 51 | public void rookEndgameLowDepth() { 52 | Board board = Board.getBoard("8/2p5/3p4/KP5r/5p1k/8/4P1P1/1R6 b - -"); 53 | System.out.println(board); 54 | Engine engine = new Engine(); 55 | final int positions = engine.countAllMoves(board, 4); 56 | System.out.println("NUMBER OF POSITIONS: " + positions); 57 | Assert.assertEquals(69665, positions); 58 | } 59 | 60 | 61 | @Test 62 | public void countMovesAtPosition2() { 63 | Board board = Board.getBoard("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -"); 64 | System.out.println(board); 65 | Engine engine = new Engine(); 66 | Assert.assertEquals(97862, engine.countAllMoves(board, 3)); 67 | Assert.assertEquals(4085603, engine.countAllMoves(board, 4)); 68 | Assert.assertEquals(193690690, engine.countAllMoves(board, 5)); 69 | } 70 | 71 | @Test 72 | public void countMovesAtPosition3() { 73 | Board board = Board.getBoard("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -"); 74 | System.out.println(board); 75 | Engine engine = new Engine(); 76 | Assert.assertEquals(674624, engine.countAllMoves(board, 5)); 77 | Assert.assertEquals(11030083, engine.countAllMoves(board, 6)); 78 | Assert.assertEquals(178633661, engine.countAllMoves(board, 7)); 79 | } 80 | 81 | 82 | @Test 83 | public void countMovesAtPosition30() { 84 | Board board = Board.getBoard("8/2p5/K2p4/1P5r/1R3p1k/8/4P1P1/8 b - -"); 85 | System.out.println(board); 86 | Engine engine = new Engine(); 87 | Assert.assertEquals(3653, engine.countAllMoves(board, 3)); 88 | Assert.assertEquals(59028, engine.countAllMoves(board, 4)); 89 | Assert.assertEquals(968724, engine.countAllMoves(board, 5)); 90 | Assert.assertEquals(16022983, engine.countAllMoves(board, 6)); 91 | } 92 | 93 | @Test 94 | public void countMovesAtPosition301() { 95 | Board board = Board.getBoard("8/2p5/K2p4/1P4kr/1R3p2/8/4P1P1/8 w - -"); 96 | System.out.println(board); 97 | Engine engine = new Engine(); 98 | Assert.assertEquals(270, engine.countAllMoves(board, 2)); 99 | Assert.assertEquals(4666, engine.countAllMoves(board, 3, 3)); 100 | } 101 | 102 | @Test 103 | public void countMovesAtPosition302() { 104 | Board board = Board.getBoard("8/2p5/K2p4/1P4kr/1R3pP1/8/4P3/8 b - g3"); 105 | System.out.println(board); 106 | Engine engine = new Engine(); 107 | Assert.assertEquals(257, engine.countAllMoves(board, 2)); 108 | } 109 | 110 | @Test 111 | public void countMovesAtPosition303() { 112 | Board board = Board.getBoard("8/2p5/K2p4/1P4kr/1R6/6p1/4P3/8 w - -"); 113 | System.out.println(board); 114 | Engine engine = new Engine(); 115 | Assert.assertEquals(16, engine.countAllMoves(board, 1)); 116 | } 117 | 118 | @Test 119 | public void countMoves() { 120 | Board board = Board.getStartBoard(); 121 | Engine engine = new Engine(); 122 | final int positions = engine.countAllMoves(board, 6); 123 | System.out.println("NUMBER OF POSITIONS: " + positions); 124 | Assert.assertEquals(119060324, positions); 125 | System.out.println("NUMBER OF MOVES: " + Move.count); 126 | } 127 | 128 | @Test 129 | public void chaoticPosition() { 130 | Board board = Board.getBoard("r3k2r/Pppp1ppp/1b3nbN/nPP5/BB2P3/q4N2/Pp1P2PP/R2Q1RK1 b kq -"); 131 | System.out.println(board); 132 | Engine engine = new Engine(); 133 | final int positions = engine.countAllMoves(board, 5); 134 | System.out.println("NUMBER OF POSITIONS: " + positions); 135 | Assert.assertEquals(92063670, positions); 136 | } 137 | } 138 | /* 139 | b4g4 140 | b4h4 141 | */ -------------------------------------------------------------------------------- /src/test/java/KingTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class KingTest { 7 | @Test 8 | public void moveForward() { 9 | Board board = new Board(); 10 | board.placeKing(0, 0, Color.BLACK); 11 | board.placeKing(7, 7, Color.WHITE); 12 | System.out.println(board); 13 | System.out.println(board.getPiece(0, 0).getMoveList(board)); 14 | Assert.assertEquals(3, board.getPiece(0, 0).getMoveList(board).size()); 15 | } 16 | 17 | @Test 18 | public void moveAsKing() { 19 | Board board = new Board(); 20 | board.placeKing(3, 3, Color.BLACK); 21 | board.placeKing(3, 7, Color.WHITE); 22 | System.out.println(board); 23 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 24 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 25 | } 26 | 27 | @Test 28 | public void blockedByPawn() { 29 | Board board = new Board(); 30 | board.placeKing(3, 3, Color.BLACK); 31 | board.placePawn(4, 3, Color.BLACK); 32 | board.placeKing(7, 7, Color.WHITE); 33 | System.out.println(board); 34 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 35 | Assert.assertEquals(7, board.getPiece(3, 3).getMoveList(board).size()); 36 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 37 | } 38 | 39 | @Test 40 | public void blockedByEnemyPawn() { 41 | Board board = new Board(); 42 | board.placeKing(3, 3, Color.BLACK); 43 | board.placePawn(4, 3, Color.WHITE); 44 | board.placeKing(7, 7, Color.WHITE); 45 | System.out.println(board); 46 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 47 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 48 | } 49 | 50 | @Test 51 | public void notBlockedByEnemyPawn() { 52 | Board board = new Board(); 53 | board.placeKing(3, 3, Color.BLACK); 54 | board.placePawn(5, 3, Color.WHITE); 55 | board.placeKing(7, 7, Color.WHITE); 56 | System.out.println(board); 57 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 58 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 59 | } 60 | 61 | @Test 62 | public void notBlockedByEnemyPawnInFront() { 63 | Board board = new Board(); 64 | board.placeKing(3, 3, Color.WHITE); 65 | board.placePawn(5, 3, Color.BLACK); 66 | board.placeKing(7, 7, Color.BLACK); 67 | System.out.println(board); 68 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 69 | Assert.assertEquals(6, board.getPiece(3, 3).getMoveList(board).size()); 70 | } 71 | 72 | @Test 73 | public void surroundedByPiecesStraight() { 74 | Board board = new Board(); 75 | board.placeKing(3, 3, Color.BLACK); 76 | board.placePawn(4, 3, Color.BLACK); 77 | board.placePawn(3, 4, Color.BLACK); 78 | board.placeRook(2, 3, Color.BLACK); 79 | board.placeRook(3, 2, Color.BLACK); 80 | board.placeKing(7, 7, Color.WHITE); 81 | System.out.println(board); 82 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 83 | Assert.assertEquals(4, board.getPiece(3, 3).getMoveList(board).size()); 84 | } 85 | 86 | @Test 87 | public void surroundedByEnemyStraight() { 88 | Board board = new Board(); 89 | board.placeKing(3, 3, Color.BLACK); 90 | board.placePawn(4, 3, Color.WHITE); 91 | board.placePawn(3, 4, Color.WHITE); 92 | board.placeRook(2, 3, Color.WHITE); 93 | board.placeRook(3, 2, Color.WHITE); 94 | board.placeKing(7, 7, Color.WHITE); 95 | board.inCheck = true; 96 | System.out.println(board); 97 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 98 | Assert.assertEquals(3, board.getPiece(3, 3).getMoveList(board).size()); 99 | } 100 | 101 | @Test 102 | public void surroundedByPiecesDiag() { 103 | Board board = new Board(); 104 | board.placeKing(3, 3, Color.BLACK); 105 | board.placePawn(4, 4, Color.BLACK); 106 | board.placePawn(2, 2, Color.BLACK); 107 | board.placeRook(4, 2, Color.BLACK); 108 | board.placeRook(2, 4, Color.BLACK); 109 | board.placeKing(7, 7, Color.WHITE); 110 | System.out.println(board); 111 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 112 | Assert.assertEquals(4, board.getPiece(3, 3).getMoveList(board).size()); 113 | } 114 | 115 | @Test 116 | public void surroundedByEnemyDiag() { 117 | Board board = new Board(); 118 | board.placeKing(3, 3, Color.BLACK); 119 | board.placePawn(4, 4, Color.WHITE); 120 | board.placePawn(2, 2, Color.WHITE); 121 | board.placeRook(4, 2, Color.WHITE); 122 | board.placeRook(2, 4, Color.WHITE); 123 | board.placeKing(7, 7, Color.WHITE); 124 | board.inCheck = true; 125 | System.out.println(board); 126 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 127 | Assert.assertEquals(2, board.getPiece(3, 3).getMoveList(board).size()); 128 | } 129 | 130 | @Test 131 | public void surroundedByTeam() { 132 | Board board = new Board(); 133 | board.placeKing(3, 3, Color.WHITE); 134 | board.placePawn(4, 4, Color.WHITE); 135 | board.placePawn(2, 2, Color.WHITE); 136 | board.placeRook(4, 2, Color.WHITE); 137 | board.placeRook(2, 4, Color.WHITE); 138 | board.placePawn(4, 3, Color.WHITE); 139 | board.placePawn(3, 4, Color.WHITE); 140 | board.placeRook(2, 3, Color.WHITE); 141 | board.placeRook(3, 2, Color.WHITE); 142 | board.placeKing(7, 7, Color.BLACK); 143 | System.out.println(board); 144 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 145 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).isEmpty()); 146 | } 147 | 148 | @Test 149 | public void surroundedByEnemy() { 150 | Board board = new Board(); 151 | board.placeKing(3, 3, Color.BLACK); 152 | board.placePawn(4, 4, Color.WHITE); 153 | board.placePawn(2, 2, Color.WHITE); 154 | board.placeRook(4, 2, Color.WHITE); 155 | board.placeRook(2, 4, Color.WHITE); 156 | board.placePawn(4, 3, Color.WHITE); 157 | board.placePawn(3, 4, Color.WHITE); 158 | board.placeRook(2, 3, Color.WHITE); 159 | board.placeRook(3, 2, Color.WHITE); 160 | board.placeKing(7, 7, Color.WHITE); 161 | System.out.println(board); 162 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 163 | Assert.assertEquals(1, board.getPiece(3, 3).getMoveList(board).size()); 164 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().allMatch(c -> c.captureMove)); 165 | } 166 | 167 | @Test 168 | public void canCastleLeft() { 169 | Board board = new Board(); 170 | board.canCastle[0][1] = false; 171 | board.placeKing(0, 4, Color.WHITE); 172 | board.placeKing(7, 4, Color.BLACK); 173 | board.placeRook(0, 0, Color.WHITE); 174 | System.out.println(board); 175 | System.out.println(board.getPiece(0, 4).getMoveList(board)); 176 | Assert.assertEquals(6, board.getPiece(0, 4).getMoveList(board).size()); 177 | Assert.assertTrue(board.getPiece(0, 4).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 178 | board.makeMove(board.getPiece(0, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 179 | System.out.println(board); 180 | System.out.println(board.getPiece(0, 2).getMoveList(board)); 181 | Assert.assertEquals(4, board.getPiece(0, 2).getMoveList(board).size()); 182 | Assert.assertEquals(11, board.getPiece(0, 3).getMoveList(board).size()); 183 | } 184 | 185 | @Test 186 | public void canCastleRight() { 187 | Board board = new Board(); 188 | board.canCastle[0][0] = false; 189 | board.placeKing(0, 4, Color.WHITE); 190 | board.placeKing(7, 4, Color.BLACK); 191 | board.placeRook(0, 7, Color.WHITE); 192 | System.out.println(board); 193 | System.out.println(board.getPiece(0, 4).getMoveList(board)); 194 | Assert.assertEquals(6, board.getPiece(0, 4).getMoveList(board).size()); 195 | Assert.assertTrue(board.getPiece(0, 4).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 196 | board.makeMove(board.getPiece(0, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 197 | System.out.println(board); 198 | System.out.println(board.getPiece(0, 6).getMoveList(board)); 199 | Assert.assertEquals(4, board.getPiece(0, 6).getMoveList(board).size()); 200 | Assert.assertEquals(12, board.getPiece(0, 5).getMoveList(board).size()); 201 | } 202 | 203 | @Test 204 | public void canCastleBothSides() { 205 | Board board = new Board(); 206 | board.placeKing(0, 4, Color.WHITE); 207 | board.placeKing(7, 4, Color.BLACK); 208 | board.placeRook(0, 7, Color.WHITE); 209 | board.placeRook(0, 0, Color.WHITE); 210 | System.out.println(board); 211 | System.out.println(board.getPiece(0, 4).getMoveList(board)); 212 | Assert.assertEquals(7, board.getPiece(0, 4).getMoveList(board).size()); 213 | Assert.assertTrue(board.getPiece(0, 4).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 214 | } 215 | 216 | @Test 217 | public void castleBlockedLeftSide() { 218 | Board board = new Board(); 219 | board.placeKing(0, 4, Color.WHITE); 220 | board.placeKing(7, 4, Color.BLACK); 221 | board.placeRook(0, 7, Color.WHITE); 222 | board.placeRook(0, 0, Color.WHITE); 223 | board.placeBishop(0, 2, Color.WHITE); 224 | board.placePawn(1, 6, Color.WHITE); 225 | board.placePawn(1, 7, Color.WHITE); 226 | System.out.println(board); 227 | System.out.println(board.getPiece(0, 4).getMoveList(board)); 228 | Assert.assertEquals(6, board.getPiece(0, 4).getMoveList(board).size()); 229 | Assert.assertTrue(board.getPiece(0, 4).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 230 | board.makeMove(board.getPiece(0, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 231 | System.out.println(board); 232 | System.out.println(board.getPiece(0, 6).getMoveList(board)); 233 | Assert.assertEquals(2, board.getPiece(0, 6).getMoveList(board).size()); 234 | Assert.assertEquals(9, board.getPiece(0, 5).getMoveList(board).size()); 235 | } 236 | 237 | @Test 238 | public void castleBlockedByCheck() { 239 | Board board = new Board(); 240 | board.placeKing(0, 4, Color.WHITE); 241 | board.placeKing(7, 4, Color.BLACK); 242 | board.placeRook(0, 7, Color.WHITE); 243 | board.placeRook(0, 0, Color.WHITE); 244 | board.placeBishop(0, 2, Color.WHITE); 245 | board.placePawn(1, 6, Color.WHITE); 246 | board.placePawn(1, 7, Color.WHITE); 247 | board.placeRook(5, 5, Color.BLACK); 248 | System.out.println(board); 249 | System.out.println(board.getPiece(0, 4).getMoveList(board)); 250 | Assert.assertEquals(3, board.getPiece(0, 4).getMoveList(board).size()); 251 | Assert.assertTrue(board.getPiece(0, 4).getMoveList(board).stream().noneMatch(c -> Math.abs(c.piece.position.col - c.target.col) == 2)); 252 | } 253 | 254 | @Test 255 | public void castleNotBlockedByCheck() { 256 | Board board = new Board(); 257 | board.placeKing(0, 4, Color.WHITE); 258 | board.placeKing(7, 4, Color.BLACK); 259 | board.placeRook(0, 7, Color.WHITE); 260 | board.placeRook(0, 0, Color.WHITE); 261 | board.placeBishop(0, 2, Color.WHITE); 262 | board.placePawn(1, 5, Color.WHITE); 263 | board.placePawn(1, 6, Color.WHITE); 264 | board.placeRook(5, 7, Color.BLACK); 265 | System.out.println(board); 266 | System.out.println(board.getPiece(0, 4).getMoveList(board)); 267 | Assert.assertEquals(5, board.getPiece(0, 4).getMoveList(board).size()); 268 | Assert.assertTrue(board.getPiece(0, 4).getMoveList(board).stream().anyMatch(c -> Math.abs(c.piece.position.col - c.target.col) == 2)); 269 | board.makeMove(board.getPiece(0, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 270 | System.out.println(board); 271 | System.out.println(board.getPiece(0, 6).getMoveList(board)); 272 | Assert.assertTrue(board.getPiece(0, 6).getMoveList(board).isEmpty()); 273 | Assert.assertEquals(2, board.getPiece(0, 5).getMoveList(board).size()); 274 | } 275 | 276 | @Test 277 | public void castleBlockedByCheckBlack() { 278 | Board board = new Board(); 279 | board.placeKing(0, 4, Color.WHITE); 280 | board.placeKing(7, 4, Color.BLACK); 281 | board.placeRook(7, 7, Color.BLACK); 282 | board.placeRook(7, 0, Color.BLACK); 283 | board.placeBishop(7, 2, Color.BLACK); 284 | board.placePawn(6, 6, Color.BLACK); 285 | board.placePawn(6, 7, Color.BLACK); 286 | board.placeRook(4, 5, Color.WHITE); 287 | System.out.println(board); 288 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 289 | Assert.assertEquals(3, board.getPiece(7, 4).getMoveList(board).size()); 290 | Assert.assertTrue(board.getPiece(7, 4).getMoveList(board).stream().noneMatch(c -> Math.abs(c.piece.position.col - c.target.col) == 2)); 291 | } 292 | 293 | @Test 294 | public void castleNotBlockedByCheckBlack() { 295 | Board board = new Board(); 296 | board.placeKing(0, 4, Color.WHITE); 297 | board.placeKing(7, 4, Color.BLACK); 298 | board.placeRook(7, 7, Color.BLACK); 299 | board.placeRook(7, 0, Color.BLACK); 300 | board.placeBishop(7, 2, Color.BLACK); 301 | board.placePawn(6, 5, Color.BLACK); 302 | board.placePawn(6, 6, Color.BLACK); 303 | board.placeRook(4, 7, Color.WHITE); 304 | System.out.println(board); 305 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 306 | Assert.assertEquals(5, board.getPiece(7, 4).getMoveList(board).size()); 307 | Assert.assertTrue(board.getPiece(7, 4).getMoveList(board).stream().anyMatch(c -> Math.abs(c.piece.position.col - c.target.col) == 2)); 308 | board.makeMove(board.getPiece(7, 4).getMoveList(board).stream().filter(c -> Math.abs(c.piece.position.col - c.target.col) == 2).findAny().get()); 309 | System.out.println(board); 310 | System.out.println(board.getPiece(7, 6).getMoveList(board)); 311 | Assert.assertTrue(board.getPiece(7, 6).getMoveList(board).isEmpty()); 312 | Assert.assertEquals(2, board.getPiece(7, 5).getMoveList(board).size()); 313 | } 314 | 315 | @Test 316 | public void kingInProximity() { 317 | Board board = new Board(); 318 | board.placeKing(2, 5, Color.WHITE); 319 | board.placeRook(2, 0, Color.BLACK); 320 | board.placeKing(4, 4, Color.BLACK); 321 | board.inCheck = true; 322 | System.out.println(board); 323 | System.out.println(board.getPiece(2, 5).getMoveList(board)); 324 | Assert.assertEquals(4, board.getPiece(2, 5).getMoveList(board).size()); 325 | } 326 | 327 | 328 | @Test 329 | public void canCastleAsBlack() { 330 | Board board = new Board(); 331 | board.canCastle[0][0] = board.canCastle[0][1] = false; 332 | board.placeKing(0, 4, Color.WHITE); 333 | board.placeKing(7, 4, Color.BLACK); 334 | board.placeRook(7, 7, Color.BLACK); 335 | board.placeRook(7, 0, Color.BLACK); 336 | board.placePawn(6, 3, Color.BLACK); 337 | board.placePawn(6, 4, Color.BLACK); 338 | board.placePawn(6, 5, Color.BLACK); 339 | System.out.println(board); 340 | System.out.println(board.getPiece(7, 4).getMoveList(board)); 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /src/test/java/KnightTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class KnightTest { 7 | @Test 8 | public void moveForward() { 9 | Board board = new Board(); 10 | board.placeKnight(0, 0, Color.BLACK); 11 | System.out.println(board); 12 | System.out.println(board.getPiece(0, 0).getMoveList(board)); 13 | Assert.assertEquals(2, board.getPiece(0, 0).getMoveList(board).size()); 14 | } 15 | 16 | @Test 17 | public void moveAsKnight() { 18 | Board board = new Board(); 19 | board.placeKnight(3, 3, Color.BLACK); 20 | System.out.println(board); 21 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 22 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 23 | } 24 | 25 | @Test 26 | public void blockedByPawn() { 27 | Board board = new Board(); 28 | board.placeKnight(3, 3, Color.BLACK); 29 | board.placePawn(4, 3, Color.BLACK); 30 | board.placePawn(5, 4, Color.BLACK); 31 | System.out.println(board); 32 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 33 | Assert.assertEquals(7, board.getPiece(3, 3).getMoveList(board).size()); 34 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 35 | } 36 | 37 | @Test 38 | public void blockedByEnemyPawn() { 39 | Board board = new Board(); 40 | board.placeKnight(3, 3, Color.BLACK); 41 | board.placePawn(4, 3, Color.WHITE); 42 | board.placePawn(5, 4, Color.WHITE); 43 | System.out.println(board); 44 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 45 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 46 | } 47 | 48 | @Test 49 | public void notBlockedByEnemyPawn() { 50 | Board board = new Board(); 51 | board.placeKnight(3, 3, Color.BLACK); 52 | board.placePawn(5, 3, Color.WHITE); 53 | System.out.println(board); 54 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 55 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 56 | } 57 | 58 | @Test 59 | public void surroundedByPiecesFriendly() { 60 | Board board = new Board(); 61 | board.placeKnight(3, 3, Color.BLACK); 62 | board.placePawn(5, 4, Color.BLACK); 63 | board.placePawn(4, 5, Color.BLACK); 64 | board.placeRook(2, 5, Color.BLACK); 65 | board.placeRook(1, 2, Color.BLACK); 66 | System.out.println(board); 67 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 68 | Assert.assertEquals(4, board.getPiece(3, 3).getMoveList(board).size()); 69 | } 70 | 71 | @Test 72 | public void surroundedByEnemyPieces() { 73 | Board board = new Board(); 74 | board.placeKnight(3, 3, Color.BLACK); 75 | board.placePawn(5, 4, Color.WHITE); 76 | board.placePawn(4, 5, Color.WHITE); 77 | board.placeRook(2, 5, Color.WHITE); 78 | board.placeRook(1, 2, Color.WHITE); 79 | System.out.println(board); 80 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 81 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 82 | } 83 | 84 | @Test 85 | public void surroundedByPiecesDiag() { 86 | Board board = new Board(); 87 | board.placeKnight(3, 3, Color.BLACK); 88 | board.placePawn(4, 4, Color.BLACK); 89 | board.placePawn(2, 2, Color.BLACK); 90 | board.placeRook(4, 2, Color.BLACK); 91 | board.placeRook(2, 4, Color.BLACK); 92 | System.out.println(board); 93 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 94 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 95 | } 96 | 97 | @Test 98 | public void surroundedByEnemyDiag() { 99 | Board board = new Board(); 100 | board.placeKnight(3, 3, Color.BLACK); 101 | board.placePawn(4, 4, Color.WHITE); 102 | board.placePawn(2, 2, Color.WHITE); 103 | board.placeRook(4, 2, Color.WHITE); 104 | board.placeRook(2, 4, Color.WHITE); 105 | System.out.println(board); 106 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 107 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 108 | } 109 | 110 | @Test 111 | public void surroundedByTeam() { 112 | Board board = new Board(); 113 | board.placeKnight(3, 3, Color.WHITE); 114 | board.placePawn(4, 4, Color.WHITE); 115 | board.placePawn(2, 2, Color.WHITE); 116 | board.placeRook(4, 2, Color.WHITE); 117 | board.placeRook(2, 4, Color.WHITE); 118 | board.placePawn(4, 3, Color.WHITE); 119 | board.placePawn(3, 4, Color.WHITE); 120 | board.placeRook(2, 3, Color.WHITE); 121 | board.placeRook(3, 2, Color.WHITE); 122 | System.out.println(board); 123 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 124 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 125 | } 126 | 127 | @Test 128 | public void surroundedByEnemy() { 129 | Board board = new Board(); 130 | board.placeKnight(3, 3, Color.BLACK); 131 | board.placePawn(4, 4, Color.WHITE); 132 | board.placePawn(2, 2, Color.WHITE); 133 | board.placeRook(4, 2, Color.WHITE); 134 | board.placeRook(2, 4, Color.WHITE); 135 | board.placePawn(4, 3, Color.WHITE); 136 | board.placePawn(3, 4, Color.WHITE); 137 | board.placeRook(2, 3, Color.WHITE); 138 | board.placeRook(3, 2, Color.WHITE); 139 | System.out.println(board); 140 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 141 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 142 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/test/java/MinMaxTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import game.Cell; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | import pieces.PieceType; 7 | 8 | public class MinMaxTest { 9 | 10 | @Test 11 | public void mateInOne() { 12 | Board board = new Board(); 13 | board.placeKing(7, 0, Color.BLACK); 14 | board.placeKing(1, 0, Color.WHITE); 15 | board.placeRook(6, 4, Color.WHITE); 16 | board.placeRook(2, 5, Color.WHITE); 17 | System.out.println(board.fenRepresentation()); 18 | System.out.println(board); 19 | Engine engine = new Engine(); 20 | final Evaluation bestMove = engine.minMax(board, 1, 1); 21 | System.out.println(bestMove.getMove()); 22 | System.out.println(bestMove.getScore()); 23 | board.makeMove(bestMove.getMove()); 24 | System.out.println(board); 25 | Assert.assertEquals(bestMove.getMove().target, Cell.get(7, 5)); 26 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.ROOK)); 27 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 28 | } 29 | 30 | @Test 31 | public void mateInThree() { 32 | Board board = Board.getBoard("7k/4K1pp/7N/8/8/8/8/B7 w - - 0 1"); 33 | System.out.println(board); 34 | Engine engine = new Engine(); 35 | final Evaluation bestMove = engine.minMax(board, 5, 5); 36 | System.out.println(bestMove.getMove()); 37 | System.out.println(bestMove.getScore()); 38 | board.makeMove(bestMove.getMove()); 39 | System.out.println(board); 40 | Assert.assertEquals(bestMove.getMove().target, Cell.get(5, 5)); 41 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.BISHOP)); 42 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 43 | } 44 | 45 | @Test 46 | public void mateInTwo() { 47 | Board board = Board.getBoard("7k/4K2p/5p1N/8/8/8/8/8 w - - 0 2"); 48 | System.out.println(board); 49 | Engine engine = new Engine(); 50 | final Evaluation bestMove = engine.minMax(board, 3, 3); 51 | System.out.println(bestMove.getMove()); 52 | System.out.println(bestMove.getScore()); 53 | board.makeMove(bestMove.getMove()); 54 | System.out.println(board); 55 | Assert.assertEquals(bestMove.getMove().target, Cell.get(7, 5)); 56 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.KING)); 57 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 58 | } 59 | 60 | @Test 61 | public void complexPosition() { 62 | Board board = Board.getBoard("r4rk1/1bp1q1p1/1p5p/3n4/p2Ppp2/2P3QN/PPB2PPP/R3R1K1 w - - 0 22"); 63 | System.out.println(board); 64 | Engine engine = new Engine(); 65 | final OutCome bestMove = engine.iterativeDeepening(board, 10); 66 | System.out.println(bestMove.getMove()); 67 | System.out.println(bestMove.getScore()); 68 | board.makeMove(bestMove.getMove()); 69 | System.out.println(board); 70 | Assert.assertEquals(Cell.get(5, 6), bestMove.getMove().target); 71 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.QUEEN)); 72 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 73 | } 74 | 75 | @Test 76 | public void winQueen() { 77 | Board board = Board.getBoard("3r1k2/5p2/p4N1p/1p4p1/2pq4/6P1/1P3Q1P/4R1K1 w - - 2 33"); 78 | System.out.println(board); 79 | Engine engine = new Engine(); 80 | final OutCome bestMove = engine.iterativeDeepening(board, 20); 81 | System.out.println(bestMove.getMove()); 82 | System.out.println(bestMove.getScore()); 83 | board.makeMove(bestMove.getMove()); 84 | System.out.println(board); 85 | Assert.assertEquals(Cell.get(7, 4), bestMove.getMove().target); 86 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.ROOK)); 87 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 88 | } 89 | 90 | @Test 91 | public void endGamePgn() { 92 | Board board = Board.getBoard("2k5/8/5R2/8/8/8/3K4/4R3 w - - 5 6"); 93 | System.out.println(board); 94 | Engine engine = new Engine(); 95 | final OutCome bestMove = engine.iterativeDeepening(board, 20); 96 | System.out.println(bestMove.getMove()); 97 | Assert.assertEquals(Integer.MIN_VALUE, bestMove.getScore(), 0.000001); 98 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 99 | } 100 | 101 | @Test 102 | public void hardMateIn3() { 103 | Board board = Board.getBoard("8/8/2P1Q3/3P3R/3k4/8/2K5/8 w - - 0 1"); 104 | System.out.println(board); 105 | Engine engine = new Engine(); 106 | final OutCome bestMove = engine.iterativeDeepening(board, 20); 107 | System.out.println(bestMove.getMove()); 108 | Assert.assertEquals(Cell.get(6, 5), bestMove.getMove().target); 109 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.QUEEN)); 110 | Assert.assertEquals(Integer.MIN_VALUE, bestMove.getScore(), 0.000001); 111 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 112 | } 113 | 114 | @Test 115 | public void mateIn4() { 116 | Board board = Board.getBoard("7R/r1p1q1pp/3k4/1p1n1Q2/3N4/8/1PP2PPP/2B3K1 w - - 1 0"); 117 | System.out.println(board); 118 | Engine engine = new Engine(); 119 | final OutCome bestMove = engine.alphaBeta(board, 7, Integer.MIN_VALUE, Integer.MAX_VALUE, 7); 120 | System.out.println(bestMove.getMove()); 121 | Assert.assertEquals(Cell.get(7, 3), bestMove.getMove().target); 122 | Assert.assertTrue(bestMove.getMove().piece.sameType(PieceType.ROOK)); 123 | Assert.assertEquals(Integer.MIN_VALUE, bestMove.getScore(), 0.000001); 124 | System.out.println("NODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 125 | } 126 | 127 | @Test 128 | public void countMovesAtPosition4MinMax() { 129 | Board board = Board.getBoard("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1"); 130 | board.inCheck = true; 131 | System.out.println(board); 132 | Engine engine = new Engine(); 133 | Engine.nodesEvaluated = 0; 134 | final Evaluation evaluation = engine.minMax(board, 4, 4); 135 | System.out.println(evaluation + " \nNODES: " + Engine.nodesEvaluated); 136 | } 137 | 138 | @Test 139 | public void countMovesAtPosition4AlphaBeta() { 140 | Board board = Board.getBoard("r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1"); 141 | board.inCheck = true; 142 | System.out.println(board); 143 | Engine engine = new Engine(); 144 | Engine.nodesEvaluated = 0; 145 | final OutCome evaluation = engine.alphaBeta(board, 4, Integer.MIN_VALUE, Integer.MAX_VALUE, 4); 146 | System.out.println(evaluation + " \nNODES: " + Engine.nodesEvaluated + " TRANSPOSITIONS: " + engine.transpositionTable.size()); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /src/test/java/PawnTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import commons.Piece; 3 | import game.Board; 4 | import game.Cell; 5 | import game.Move; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import pieces.PieceType; 9 | 10 | import java.util.stream.Collectors; 11 | 12 | public class PawnTest { 13 | @Test 14 | public void checkStartingPositions() { 15 | final Board board = Board.getStartBoard(); 16 | System.out.println(board); 17 | Assert.assertTrue(board.moveList.isEmpty()); 18 | Assert.assertEquals(16, board.pieces.values().stream().filter(piece -> piece.sameType(PieceType.PAWN)).count()); 19 | Assert.assertEquals(8, board.pieces.values().stream() 20 | .filter(piece -> piece.sameType(PieceType.PAWN)) 21 | .filter(piece -> piece.color == Color.BLACK) 22 | .count()); 23 | Assert.assertEquals(8, board.pieces.values().stream() 24 | .filter(piece -> piece.sameType(PieceType.PAWN)) 25 | .filter(piece -> piece.color == Color.WHITE) 26 | .count()); 27 | 28 | } 29 | 30 | @Test 31 | public void moveForward() { 32 | final Board board = new Board(); 33 | board.placePawn(2, 1, Color.WHITE); 34 | System.out.println(board); 35 | Assert.assertEquals(1, board.getPiece(2, 1).getMoveList(board).size()); 36 | } 37 | 38 | @Test 39 | public void moveForwardDouble() { 40 | final Board board = new Board(); 41 | board.placePawn(1, 1, Color.WHITE); 42 | System.out.println(board); 43 | Assert.assertEquals(2, board.getPiece(1, 1).getMoveList(board).size()); 44 | } 45 | 46 | @Test 47 | public void cantMoveForwardOpponent() { 48 | final Board board = new Board(); 49 | board.placePawn(2, 1, Color.WHITE); 50 | board.placePawn(3, 1, Color.BLACK); 51 | System.out.println(board); 52 | Assert.assertTrue(board.getPiece(2, 1).getMoveList(board).isEmpty()); 53 | } 54 | 55 | @Test 56 | public void cantMoveForwardSameColor() { 57 | final Board board = new Board(); 58 | board.placePawn(2, 1, Color.WHITE); 59 | board.placePawn(3, 1, Color.WHITE); 60 | System.out.println(board); 61 | Assert.assertTrue(board.getPiece(2, 1).getMoveList(board).isEmpty()); 62 | } 63 | 64 | @Test 65 | public void canCapture() { 66 | final Board board = new Board(); 67 | board.placePawn(2, 1, Color.WHITE); 68 | board.placePawn(3, 1, Color.BLACK); 69 | board.placePawn(3, 2, Color.BLACK); 70 | System.out.println(board); 71 | Assert.assertEquals(1, board.getPiece(2, 1).getMoveList(board).size()); 72 | System.out.println(board.getPiece(2, 1).getMoveList(board)); 73 | } 74 | 75 | @Test 76 | public void canCaptureBlack() { 77 | final Board board = new Board(); 78 | board.placePawn(6, 6, Color.BLACK); 79 | board.placePawn(5, 5, Color.WHITE); 80 | System.out.println(board); 81 | System.out.println(board.getPiece(6, 6).getMoveList(board)); 82 | Assert.assertEquals(3, board.getPiece(6, 6).getMoveList(board).size()); 83 | } 84 | 85 | @Test 86 | public void canCaptureBlackBlocked() { 87 | final Board board = new Board(); 88 | board.placePawn(6, 6, Color.BLACK); 89 | board.placePawn(5, 6, Color.BLACK); 90 | board.placePawn(5, 5, Color.WHITE); 91 | System.out.println(board); 92 | System.out.println(board.getPiece(6, 6).getMoveList(board)); 93 | Assert.assertEquals(1, board.getPiece(6, 6).getMoveList(board).size()); 94 | } 95 | 96 | @Test 97 | public void canCaptureEnPassant() { 98 | final Board board = new Board(); 99 | board.placePawn(4, 6, Color.WHITE); 100 | board.placePawn(4, 5, Color.BLACK); 101 | board.placePawn(4, 7, Color.BLACK); 102 | board.moveList.add(Move.get(Piece.get(Color.BLACK, Cell.get(6, 5), PieceType.PAWN), Cell.get(4, 5), false)); 103 | System.out.println(board); 104 | System.out.println(board.getPiece(4, 6).getMoveList(board)); 105 | Assert.assertEquals(2, board.getPiece(4, 6).getMoveList(board).size()); 106 | } 107 | 108 | @Test 109 | public void cannotCaptureEnPassantOnNextTurn() { 110 | final Board board = new Board(); 111 | board.placePawn(4, 6, Color.WHITE); 112 | board.placePawn(4, 5, Color.BLACK); 113 | board.placePawn(4, 7, Color.BLACK); 114 | System.out.println(board); 115 | System.out.println(board.getPiece(4, 6).getMoveList(board)); 116 | Assert.assertEquals(1, board.getPiece(4, 6).getMoveList(board).size()); 117 | } 118 | 119 | @Test 120 | public void captureOnEdge() { 121 | Board board = Board.getStartBoard(); 122 | board = board.copy(); 123 | board.makeMove(board.getLegalMoves() 124 | .stream() 125 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 126 | .filter(c -> c.piece.position.equals(Cell.get(1, 0))) 127 | .filter(c -> c.target.equals(Cell.get(2, 0))) 128 | .findAny() 129 | .get()); 130 | board = board.copy(); 131 | board.makeMove(board.getLegalMoves() 132 | .stream() 133 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 134 | .filter(c -> c.piece.position.equals(Cell.get(6, 0))) 135 | .filter(c -> c.target.equals(Cell.get(5, 0))) 136 | .findAny() 137 | .get()); 138 | board = board.copy(); 139 | board.makeMove(board.getLegalMoves() 140 | .stream() 141 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 142 | .filter(c -> c.piece.position.equals(Cell.get(1, 1))) 143 | .filter(c -> c.target.equals(Cell.get(3, 1))) 144 | .findAny() 145 | .get()); 146 | board = board.copy(); 147 | board.makeMove(board.getLegalMoves() 148 | .stream() 149 | .filter(c -> c.piece.sameType(PieceType.PAWN)) 150 | .filter(c -> c.piece.position.equals(Cell.get(5, 0))) 151 | .filter(c -> c.target.equals(Cell.get(4, 0))) 152 | .findAny() 153 | .get()); 154 | System.out.println(board); 155 | System.out.println(board.getLegalMoves().stream().map(Move::toString).collect(Collectors.joining("\n"))); 156 | System.out.println(board.fenRepresentation()); 157 | Assert.assertEquals(20, board.getLegalMoves().size()); 158 | } 159 | } 160 | 161 | /* 162 | b4a5 163 | */ 164 | -------------------------------------------------------------------------------- /src/test/java/QueenTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class QueenTest { 7 | @Test 8 | public void moveForward() { 9 | Board board = new Board(); 10 | board.placeQueen(0, 0, Color.BLACK); 11 | System.out.println(board); 12 | System.out.println(board.getPiece(0, 0).getMoveList(board)); 13 | Assert.assertEquals(21, board.getPiece(0, 0).getMoveList(board).size()); 14 | } 15 | 16 | @Test 17 | public void moveAsQueen() { 18 | Board board = new Board(); 19 | board.placeQueen(3, 3, Color.BLACK); 20 | System.out.println(board); 21 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 22 | Assert.assertEquals(27, board.getPiece(3, 3).getMoveList(board).size()); 23 | } 24 | 25 | @Test 26 | public void blockedByPawn() { 27 | Board board = new Board(); 28 | board.placeQueen(3, 3, Color.BLACK); 29 | board.placePawn(4, 3, Color.BLACK); 30 | System.out.println(board); 31 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 32 | Assert.assertEquals(23, board.getPiece(3, 3).getMoveList(board).size()); 33 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 34 | } 35 | 36 | @Test 37 | public void blockedByEnemyPawn() { 38 | Board board = new Board(); 39 | board.placeQueen(3, 3, Color.BLACK); 40 | board.placePawn(4, 3, Color.WHITE); 41 | System.out.println(board); 42 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 43 | Assert.assertEquals(24, board.getPiece(3, 3).getMoveList(board).size()); 44 | } 45 | 46 | @Test 47 | public void surroundedByPiecesStraight() { 48 | Board board = new Board(); 49 | board.placeQueen(3, 3, Color.BLACK); 50 | board.placePawn(4, 3, Color.BLACK); 51 | board.placePawn(3, 4, Color.BLACK); 52 | board.placeRook(2, 3, Color.BLACK); 53 | board.placeRook(3, 2, Color.BLACK); 54 | System.out.println(board); 55 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 56 | Assert.assertEquals(13, board.getPiece(3, 3).getMoveList(board).size()); 57 | } 58 | 59 | @Test 60 | public void surroundedByEnemyStraight() { 61 | Board board = new Board(); 62 | board.placeQueen(3, 3, Color.BLACK); 63 | board.placePawn(4, 3, Color.WHITE); 64 | board.placePawn(3, 4, Color.WHITE); 65 | board.placeRook(2, 3, Color.WHITE); 66 | board.placeRook(3, 2, Color.WHITE); 67 | System.out.println(board); 68 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 69 | Assert.assertEquals(17, board.getPiece(3, 3).getMoveList(board).size()); 70 | } 71 | 72 | @Test 73 | public void surroundedByPiecesDiag() { 74 | Board board = new Board(); 75 | board.placeQueen(3, 3, Color.BLACK); 76 | board.placePawn(4, 4, Color.BLACK); 77 | board.placePawn(2, 2, Color.BLACK); 78 | board.placeRook(4, 2, Color.BLACK); 79 | board.placeRook(2, 4, Color.BLACK); 80 | System.out.println(board); 81 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 82 | Assert.assertEquals(14, board.getPiece(3, 3).getMoveList(board).size()); 83 | } 84 | 85 | @Test 86 | public void surroundedByEnemyDiag() { 87 | Board board = new Board(); 88 | board.placeQueen(3, 3, Color.BLACK); 89 | board.placePawn(4, 4, Color.WHITE); 90 | board.placePawn(2, 2, Color.WHITE); 91 | board.placeRook(4, 2, Color.WHITE); 92 | board.placeRook(2, 4, Color.WHITE); 93 | System.out.println(board); 94 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 95 | Assert.assertEquals(18, board.getPiece(3, 3).getMoveList(board).size()); 96 | } 97 | 98 | @Test 99 | public void surroundedByTeam() { 100 | Board board = new Board(); 101 | board.placeQueen(3, 3, Color.WHITE); 102 | board.placePawn(4, 4, Color.WHITE); 103 | board.placePawn(2, 2, Color.WHITE); 104 | board.placeRook(4, 2, Color.WHITE); 105 | board.placeRook(2, 4, Color.WHITE); 106 | board.placePawn(4, 3, Color.WHITE); 107 | board.placePawn(3, 4, Color.WHITE); 108 | board.placeRook(2, 3, Color.WHITE); 109 | board.placeRook(3, 2, Color.WHITE); 110 | System.out.println(board); 111 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 112 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).isEmpty()); 113 | } 114 | 115 | @Test 116 | public void surroundedByEnemy() { 117 | Board board = new Board(); 118 | board.placeQueen(3, 3, Color.BLACK); 119 | board.placePawn(4, 4, Color.WHITE); 120 | board.placePawn(2, 2, Color.WHITE); 121 | board.placeRook(4, 2, Color.WHITE); 122 | board.placeRook(2, 4, Color.WHITE); 123 | board.placePawn(4, 3, Color.WHITE); 124 | board.placePawn(3, 4, Color.WHITE); 125 | board.placeRook(2, 3, Color.WHITE); 126 | board.placeRook(3, 2, Color.WHITE); 127 | System.out.println(board); 128 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 129 | Assert.assertEquals(8, board.getPiece(3, 3).getMoveList(board).size()); 130 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().allMatch(c -> c.captureMove)); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/test/java/RookTest.java: -------------------------------------------------------------------------------- 1 | import commons.Color; 2 | import game.Board; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class RookTest { 7 | @Test 8 | public void moveForward() { 9 | Board board = new Board(); 10 | board.placeRook(0, 0, Color.BLACK); 11 | System.out.println(board); 12 | System.out.println(board.getPiece(0, 0).getMoveList(board)); 13 | Assert.assertEquals(14, board.getPiece(0, 0).getMoveList(board).size()); 14 | } 15 | 16 | @Test 17 | public void moveAsRook() { 18 | Board board = new Board(); 19 | board.placeRook(3, 3, Color.BLACK); 20 | System.out.println(board); 21 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 22 | Assert.assertEquals(14, board.getPiece(3, 3).getMoveList(board).size()); 23 | } 24 | 25 | @Test 26 | public void blockedByPawn() { 27 | Board board = new Board(); 28 | board.placeRook(3, 3, Color.BLACK); 29 | board.placePawn(4, 3, Color.BLACK); 30 | System.out.println(board); 31 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 32 | Assert.assertEquals(10, board.getPiece(3, 3).getMoveList(board).size()); 33 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().noneMatch(c -> c.captureMove)); 34 | } 35 | 36 | @Test 37 | public void blockedByEnemyPawn() { 38 | Board board = new Board(); 39 | board.placeRook(3, 3, Color.BLACK); 40 | board.placePawn(4, 3, Color.WHITE); 41 | System.out.println(board); 42 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 43 | Assert.assertEquals(11, board.getPiece(3, 3).getMoveList(board).size()); 44 | } 45 | 46 | @Test 47 | public void surroundedByPieces() { 48 | Board board = new Board(); 49 | board.placeRook(3, 3, Color.BLACK); 50 | board.placePawn(4, 3, Color.BLACK); 51 | board.placePawn(3, 4, Color.BLACK); 52 | board.placeRook(2, 3, Color.BLACK); 53 | board.placeRook(3, 2, Color.BLACK); 54 | System.out.println(board); 55 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 56 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).isEmpty()); 57 | } 58 | 59 | @Test 60 | public void surroundedByEnemy() { 61 | Board board = new Board(); 62 | board.placeRook(3, 3, Color.BLACK); 63 | board.placePawn(4, 3, Color.WHITE); 64 | board.placePawn(3, 4, Color.WHITE); 65 | board.placeRook(2, 3, Color.WHITE); 66 | board.placeRook(3, 2, Color.WHITE); 67 | System.out.println(board); 68 | System.out.println(board.getPiece(3, 3).getMoveList(board)); 69 | Assert.assertEquals(4, board.getPiece(3, 3).getMoveList(board).size()); 70 | Assert.assertTrue(board.getPiece(3, 3).getMoveList(board).stream().allMatch(c -> c.captureMove)); 71 | } 72 | } 73 | --------------------------------------------------------------------------------