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
This commit is contained in:
parent
1b7e3b1986
commit
9c19b13785
@ -5,6 +5,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import dev.kske.chess.board.Log.LoggedMove;
|
||||||
import dev.kske.chess.board.Piece.Color;
|
import dev.kske.chess.board.Piece.Color;
|
||||||
import dev.kske.chess.board.Piece.Type;
|
import dev.kske.chess.board.Piece.Type;
|
||||||
|
|
||||||
@ -18,10 +19,12 @@ public class Board implements Cloneable {
|
|||||||
|
|
||||||
private Piece[][] boardArr;
|
private Piece[][] boardArr;
|
||||||
private Map<Color, Position> kingPos;
|
private Map<Color, Position> kingPos;
|
||||||
|
private Log log;
|
||||||
|
|
||||||
public Board() {
|
public Board() {
|
||||||
boardArr = new Piece[8][8];
|
boardArr = new Piece[8][8];
|
||||||
kingPos = new HashMap<>();
|
kingPos = new HashMap<>();
|
||||||
|
log = new Log();
|
||||||
initializeDefaultPositions();
|
initializeDefaultPositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,11 +39,11 @@ public class Board implements Cloneable {
|
|||||||
if (piece == null || !piece.isValidMove(move)) return false;
|
if (piece == null || !piece.isValidMove(move)) return false;
|
||||||
else {
|
else {
|
||||||
// Move piece
|
// Move piece
|
||||||
Piece capturePiece = move(move);
|
move(move);
|
||||||
|
|
||||||
// Revert move if it caused a check for its team
|
// Revert move if it caused a check for its team
|
||||||
if (checkCheck(piece.getColor())) {
|
if (checkCheck(piece.getColor())) {
|
||||||
revert(move, capturePiece);
|
revert();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,31 +57,57 @@ public class Board implements Cloneable {
|
|||||||
* @param move The move to execute
|
* @param move The move to execute
|
||||||
* @return The captures piece, or null if the move's destination was empty
|
* @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 piece = getPos(move);
|
||||||
Piece capturePiece = getDest(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
|
// Update the king's position if the moved piece is the king
|
||||||
if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest);
|
if (piece.getType() == Type.KING) kingPos.put(piece.getColor(), move.dest);
|
||||||
|
|
||||||
return capturePiece;
|
// Update log
|
||||||
|
log.add(move, capturePiece);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverts a move.
|
* Reverts the last move.
|
||||||
*
|
|
||||||
* @param move The move to revert
|
|
||||||
* @param capturedPiece The piece that has been captured when the move has been
|
|
||||||
* applied
|
|
||||||
*/
|
*/
|
||||||
public void revert(Move move, Piece capturedPiece) {
|
public void revert() {
|
||||||
setPos(move, getDest(move));
|
LoggedMove loggedMove = log.getLast();
|
||||||
setDest(move, capturedPiece);
|
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
|
// 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);
|
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;
|
if (!getMoves(kingPos.get(color)).isEmpty()) return false;
|
||||||
else {
|
else {
|
||||||
for (Move move : getMoves(color)) {
|
for (Move move : getMoves(color)) {
|
||||||
Piece capturePiece = move(move);
|
move(move);
|
||||||
boolean check = checkCheck(color);
|
boolean check = checkCheck(color);
|
||||||
revert(move, capturePiece);
|
revert();
|
||||||
if (!check) return false;
|
if (!check) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -139,8 +168,7 @@ public class Board implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GameState getGameEventType(Color color) {
|
public GameState getGameEventType(Color color) {
|
||||||
return checkCheck(color)
|
return checkCheck(color) ? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
|
||||||
? checkCheckmate(color) ? GameState.CHECKMATE : GameState.CHECK
|
|
||||||
: getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL;
|
: getMoves(color).isEmpty() ? GameState.STALEMATE : GameState.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,9 +268,11 @@ public class Board implements Cloneable {
|
|||||||
board.boardArr[i][j].board = board;
|
board.boardArr[i][j].board = board;
|
||||||
}
|
}
|
||||||
|
|
||||||
board.kingPos = new HashMap<>();
|
board.kingPos = new HashMap<>();
|
||||||
board.kingPos.putAll(kingPos);
|
board.kingPos.putAll(kingPos);
|
||||||
|
|
||||||
|
board.log = (Log) log.clone();
|
||||||
|
|
||||||
return board;
|
return board;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
src/dev/kske/chess/board/Log.java
Normal file
55
src/dev/kske/chess/board/Log.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package dev.kske.chess.board;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Chess</strong><br>
|
||||||
|
* File: <strong>Log.java</strong><br>
|
||||||
|
* Created: <strong>09.07.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class Log implements Cloneable {
|
||||||
|
|
||||||
|
private List<LoggedMove> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,16 +10,22 @@ public class Move {
|
|||||||
|
|
||||||
public final Position pos, dest;
|
public final Position pos, dest;
|
||||||
public final int xDist, yDist, xSign, ySign;
|
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.pos = pos;
|
||||||
this.dest = dest;
|
this.dest = dest;
|
||||||
|
this.type = type;
|
||||||
xDist = Math.abs(dest.x - pos.x);
|
xDist = Math.abs(dest.x - pos.x);
|
||||||
yDist = Math.abs(dest.y - pos.y);
|
yDist = Math.abs(dest.y - pos.y);
|
||||||
xSign = (int) Math.signum(dest.x - pos.x);
|
xSign = (int) Math.signum(dest.x - pos.x);
|
||||||
ySign = (int) Math.signum(dest.y - pos.y);
|
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) {
|
public Move(int xPos, int yPos, int xDest, int yDest) {
|
||||||
this(new Position(xPos, yPos), new Position(xDest, yDest));
|
this(new Position(xPos, yPos), new Position(xDest, yDest));
|
||||||
}
|
}
|
||||||
@ -34,4 +40,8 @@ public class Move {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%s -> %s", pos, dest);
|
return String.format("%s -> %s", pos, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static enum Type {
|
||||||
|
NORMAL, PAWN_PROMOTION, CASTLING, EN_PASSANT, UNKNOWN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,17 @@ public class Pawn extends Piece {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidMove(Move move) {
|
public boolean isValidMove(Move move) {
|
||||||
// TODO: en passant, pawn promotion
|
// TODO: en passant
|
||||||
boolean step = move.isVertical() && move.yDist == 1;
|
boolean step = move.isVertical() && move.yDist == 1;
|
||||||
boolean doubleStep = move.isVertical() && move.yDist == 2;
|
boolean doubleStep = move.isVertical() && move.yDist == 2;
|
||||||
boolean strafe = move.isDiagonal() && move.xDist == 1;
|
boolean strafe = move.isDiagonal() && move.xDist == 1;
|
||||||
if (getColor() == Color.WHITE) doubleStep &= move.pos.y == 6;
|
if (getColor() == Color.WHITE) doubleStep &= move.pos.y == 6;
|
||||||
else doubleStep &= move.pos.y == 1;
|
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);
|
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;
|
int sign = getColor() == Color.WHITE ? -1 : 1;
|
||||||
|
|
||||||
if (sign == -1 && pos.y == 1 || sign == 1 && pos.y == 7) return moves;
|
|
||||||
|
|
||||||
// Strafe left
|
// Strafe left
|
||||||
if (pos.x > 0) {
|
if (pos.x > 0) {
|
||||||
Move move = new Move(pos, new Position(pos.x - 1, pos.y + sign));
|
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));
|
Move move = new Move(pos, new Position(pos.x, pos.y + 2 * sign));
|
||||||
if (isFreePath(move)) moves.add(move);
|
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;
|
return moves;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ public abstract class Piece implements Cloneable {
|
|||||||
List<Move> moves = getPseudolegalMoves(pos);
|
List<Move> moves = getPseudolegalMoves(pos);
|
||||||
for (Iterator<Move> iterator = moves.iterator(); iterator.hasNext();) {
|
for (Iterator<Move> iterator = moves.iterator(); iterator.hasNext();) {
|
||||||
Move move = iterator.next();
|
Move move = iterator.next();
|
||||||
Piece capturePiece = board.move(move);
|
board.move(move);
|
||||||
if (board.checkCheck(getColor()))
|
if (board.checkCheck(getColor()))
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
board.revert(move, capturePiece);
|
board.revert();
|
||||||
}
|
}
|
||||||
return moves;
|
return moves;
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ public abstract class Piece implements Cloneable {
|
|||||||
public Color getColor() { return color; }
|
public Color getColor() { return color; }
|
||||||
|
|
||||||
public static enum Type {
|
public static enum Type {
|
||||||
KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN;
|
KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enum Color {
|
public static enum Color {
|
||||||
|
@ -5,7 +5,6 @@ import java.util.concurrent.Callable;
|
|||||||
|
|
||||||
import dev.kske.chess.board.Board;
|
import dev.kske.chess.board.Board;
|
||||||
import dev.kske.chess.board.Move;
|
import dev.kske.chess.board.Move;
|
||||||
import dev.kske.chess.board.Piece;
|
|
||||||
import dev.kske.chess.board.Piece.Color;
|
import dev.kske.chess.board.Piece.Color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,7 +38,7 @@ public class MoveProcessor implements Callable<ProcessingResult> {
|
|||||||
private int miniMax(Board board, List<Move> moves, Color color, int depth) {
|
private int miniMax(Board board, List<Move> moves, Color color, int depth) {
|
||||||
int bestValue = Integer.MIN_VALUE;
|
int bestValue = Integer.MIN_VALUE;
|
||||||
for (Move move : moves) {
|
for (Move move : moves) {
|
||||||
Piece capturePiece = board.move(move);
|
board.move(move);
|
||||||
int teamValue = board.evaluate(color);
|
int teamValue = board.evaluate(color);
|
||||||
int enemyValue = board.evaluate(color.opposite());
|
int enemyValue = board.evaluate(color.opposite());
|
||||||
int valueChange = teamValue - enemyValue;
|
int valueChange = teamValue - enemyValue;
|
||||||
@ -52,7 +51,7 @@ public class MoveProcessor implements Callable<ProcessingResult> {
|
|||||||
if (depth == 0) bestMove = move;
|
if (depth == 0) bestMove = move;
|
||||||
}
|
}
|
||||||
|
|
||||||
board.revert(move, capturePiece);
|
board.revert();
|
||||||
}
|
}
|
||||||
return bestValue;
|
return bestValue;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user