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.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