From 26f7d6746181cac1c03f8081312376f12bb8fd67 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Wed, 10 Jul 2019 18:54:53 +0200 Subject: [PATCH] Added move history and pawn promotion + Log class for move history + LoggedMove class with piece captured by the logged move - Made move reversion easier + MoveType for recognizing special moves + MoveType determination during move generation and validation --- src/dev/kske/chess/board/Board.java | 74 +++++++++++++------ src/dev/kske/chess/board/Log.java | 55 ++++++++++++++ src/dev/kske/chess/board/Move.java | 12 ++- src/dev/kske/chess/board/Pawn.java | 14 +++- src/dev/kske/chess/board/Piece.java | 6 +- src/dev/kske/chess/game/ai/MoveProcessor.java | 5 +- 6 files changed, 134 insertions(+), 32 deletions(-) create mode 100644 src/dev/kske/chess/board/Log.java diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index b69f5c6..24594a3 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import dev.kske.chess.board.Log.LoggedMove; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Type; @@ -18,10 +19,12 @@ public class Board implements Cloneable { private Piece[][] boardArr; private Map kingPos; + private Log log; public Board() { - boardArr = new Piece[8][8]; - kingPos = new HashMap<>(); + boardArr = new Piece[8][8]; + kingPos = new HashMap<>(); + log = new Log(); initializeDefaultPositions(); } @@ -36,11 +39,11 @@ public class Board implements Cloneable { if (piece == null || !piece.isValidMove(move)) return false; else { // Move piece - Piece capturePiece = move(move); + move(move); // Revert move if it caused a check for its team if (checkCheck(piece.getColor())) { - revert(move, capturePiece); + revert(); return false; } @@ -54,31 +57,57 @@ public class Board implements Cloneable { * @param move The move to execute * @return The captures piece, or null if the move's destination was empty */ - public Piece move(Move move) { + public void move(Move move) { Piece piece = getPos(move); Piece capturePiece = getDest(move); - setDest(move, piece); - setPos(move, null); + + switch (move.type) { + case PAWN_PROMOTION: + setPos(move, null); + // TODO: Select promotion + setDest(move, new Queen(piece.getColor(), this)); + break; + case UNKNOWN: + System.err.printf("Unknown move %s found!%n", move); + case NORMAL: + setDest(move, piece); + setPos(move, null); + } // Update the king's position if the moved piece is the king if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest); - return capturePiece; + // Update log + log.add(move, capturePiece); } /** - * Reverts a move. - * - * @param move The move to revert - * @param capturedPiece The piece that has been captured when the move has been - * applied + * Reverts the last move. */ - public void revert(Move move, Piece capturedPiece) { - setPos(move, getDest(move)); - setDest(move, capturedPiece); + public void revert() { + LoggedMove loggedMove = log.getLast(); + Move move = loggedMove.move; + Piece capturedPiece = loggedMove.capturedPiece; + + switch (move.type) { + case CASTLING: + case EN_PASSANT: + case PAWN_PROMOTION: + setPos(move, new Pawn(getDest(move).getColor(), this)); + setDest(move, capturedPiece); + break; + case UNKNOWN: + System.err.printf("Unknown move %s found!%n", move); + case NORMAL: + setPos(move, getDest(move)); + setDest(move, capturedPiece); + } // Update the king's position if the moved piece is the king if (getPos(move).getType() == Type.KING) kingPos.put(getPos(move).getColor(), move.pos); + + // Update log + log.removeLast(); } /** @@ -129,9 +158,9 @@ public class Board implements Cloneable { if (!getMoves(kingPos.get(color)).isEmpty()) return false; else { for (Move move : getMoves(color)) { - Piece capturePiece = move(move); - boolean check = checkCheck(color); - revert(move, capturePiece); + move(move); + boolean check = checkCheck(color); + revert(); if (!check) return false; } return true; @@ -139,8 +168,7 @@ public class Board implements Cloneable { } public GameState getGameEventType(Color color) { - return checkCheck(color) - ? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK + return checkCheck(color) ? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK : getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL; } @@ -240,9 +268,11 @@ public class Board implements Cloneable { board.boardArr[i][j].board = board; } - board.kingPos = new HashMap<>(); + board.kingPos = new HashMap<>(); board.kingPos.putAll(kingPos); + board.log = (Log) log.clone(); + return board; } diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java new file mode 100644 index 0000000..05554df --- /dev/null +++ b/src/dev/kske/chess/board/Log.java @@ -0,0 +1,55 @@ +package dev.kske.chess.board; + +import java.util.ArrayList; +import java.util.List; + +/** + * Project: Chess
+ * File: Log.java
+ * Created: 09.07.2019
+ * Author: Kai S. K. Engelbart + */ +public class Log implements Cloneable { + + private List moves; + + public Log() { + moves = new ArrayList<>(); + } + + public void add(Move move, Piece capturedPiece) { + moves.add(new LoggedMove(move, capturedPiece)); + } + + public LoggedMove getLast() { + return moves.get(moves.size() - 1); + } + + public void removeLast() { + if (!moves.isEmpty()) moves.remove(moves.size() - 1); + } + + @Override + public Object clone() { + Log log = null; + try { + log = (Log) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + log.moves = new ArrayList<>(); + log.moves.addAll(this.moves); + return log; + } + + public static class LoggedMove { + + public final Move move; + public final Piece capturedPiece; + + public LoggedMove(Move move, Piece capturedPiece) { + this.move = move; + this.capturedPiece = capturedPiece; + } + } +} diff --git a/src/dev/kske/chess/board/Move.java b/src/dev/kske/chess/board/Move.java index 55986b1..003f493 100644 --- a/src/dev/kske/chess/board/Move.java +++ b/src/dev/kske/chess/board/Move.java @@ -10,16 +10,22 @@ public class Move { public final Position pos, dest; public final int xDist, yDist, xSign, ySign; + public Type type; - public Move(Position pos, Position dest) { + public Move(Position pos, Position dest, Type type) { this.pos = pos; this.dest = dest; + this.type = type; xDist = Math.abs(dest.x - pos.x); yDist = Math.abs(dest.y - pos.y); xSign = (int) Math.signum(dest.x - pos.x); ySign = (int) Math.signum(dest.y - pos.y); } + public Move(Position pos, Position dest) { + this(pos, dest, Type.NORMAL); + } + public Move(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); } @@ -34,4 +40,8 @@ public class Move { public String toString() { return String.format("%s -> %s", pos, dest); } + + public static enum Type { + NORMAL, PAWN_PROMOTION, CASTLING, EN_PASSANT, UNKNOWN + } } diff --git a/src/dev/kske/chess/board/Pawn.java b/src/dev/kske/chess/board/Pawn.java index 0406409..1411e31 100644 --- a/src/dev/kske/chess/board/Pawn.java +++ b/src/dev/kske/chess/board/Pawn.java @@ -17,12 +17,17 @@ public class Pawn extends Piece { @Override public boolean isValidMove(Move move) { - // TODO: en passant, pawn promotion + // TODO: en passant boolean step = move.isVertical() && move.yDist == 1; boolean doubleStep = move.isVertical() && move.yDist == 2; boolean strafe = move.isDiagonal() && move.xDist == 1; if (getColor() == Color.WHITE) doubleStep &= move.pos.y == 6; else doubleStep &= move.pos.y == 1; + + // Mark move as pawn promotion if necessary + if (move.ySign == 1 && move.pos.y == 6 || move.ySign == -1 && move.pos.y == 1) + move.type = Move.Type.PAWN_PROMOTION; + return (step ^ doubleStep ^ strafe) && move.ySign == (getColor() == Color.WHITE ? -1 : 1) && isFreePath(move); } @@ -43,8 +48,6 @@ public class Pawn extends Piece { int sign = getColor() == Color.WHITE ? -1 : 1; - if (sign == -1 && pos.y == 1 || sign == 1 && pos.y == 7) return moves; - // Strafe left if (pos.x > 0) { Move move = new Move(pos, new Position(pos.x - 1, pos.y + sign)); @@ -68,6 +71,11 @@ public class Pawn extends Piece { Move move = new Move(pos, new Position(pos.x, pos.y + 2 * sign)); if (isFreePath(move)) moves.add(move); } + + // Mark moves as pawn promotions if necessary + if (sign == 1 && pos.y == 6 || sign == -1 && pos.y == 1) + moves.parallelStream().forEach(m -> m.type = Move.Type.PAWN_PROMOTION); + return moves; } diff --git a/src/dev/kske/chess/board/Piece.java b/src/dev/kske/chess/board/Piece.java index fbb8c0c..71efc89 100644 --- a/src/dev/kske/chess/board/Piece.java +++ b/src/dev/kske/chess/board/Piece.java @@ -23,10 +23,10 @@ public abstract class Piece implements Cloneable { List moves = getPseudolegalMoves(pos); for (Iterator iterator = moves.iterator(); iterator.hasNext();) { Move move = iterator.next(); - Piece capturePiece = board.move(move); + board.move(move); if (board.checkCheck(getColor())) iterator.remove(); - board.revert(move, capturePiece); + board.revert(); } return moves; } @@ -67,7 +67,7 @@ public abstract class Piece implements Cloneable { public Color getColor() { return color; } public static enum Type { - KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN; + KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN } public static enum Color { diff --git a/src/dev/kske/chess/game/ai/MoveProcessor.java b/src/dev/kske/chess/game/ai/MoveProcessor.java index e7c5741..f8f2c24 100644 --- a/src/dev/kske/chess/game/ai/MoveProcessor.java +++ b/src/dev/kske/chess/game/ai/MoveProcessor.java @@ -5,7 +5,6 @@ import java.util.concurrent.Callable; import dev.kske.chess.board.Board; import dev.kske.chess.board.Move; -import dev.kske.chess.board.Piece; import dev.kske.chess.board.Piece.Color; /** @@ -39,7 +38,7 @@ public class MoveProcessor implements Callable { private int miniMax(Board board, List moves, Color color, int depth) { int bestValue = Integer.MIN_VALUE; for (Move move : moves) { - Piece capturePiece = board.move(move); + board.move(move); int teamValue = board.evaluate(color); int enemyValue = board.evaluate(color.opposite()); int valueChange = teamValue - enemyValue; @@ -52,7 +51,7 @@ public class MoveProcessor implements Callable { if (depth == 0) bestMove = move; } - board.revert(move, capturePiece); + board.revert(); } return bestValue; }