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
bde14db4cc
commit
26f7d67461
@ -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<Color, Position> 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;
|
||||
}
|
||||
|
||||
|
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 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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,10 @@ public abstract class Piece implements Cloneable {
|
||||
List<Move> moves = getPseudolegalMoves(pos);
|
||||
for (Iterator<Move> 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 {
|
||||
|
@ -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<ProcessingResult> {
|
||||
private int miniMax(Board board, List<Move> 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<ProcessingResult> {
|
||||
if (depth == 0) bestMove = move;
|
||||
}
|
||||
|
||||
board.revert(move, capturePiece);
|
||||
board.revert();
|
||||
}
|
||||
return bestValue;
|
||||
}
|
||||
|
Reference in New Issue
Block a user