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:
		| @@ -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