As the title describes I would like to know how I should filter out invalid moves for a chess piece.
The board is represented with a Map<Position, BaseChessman>
Where Position is an Enum with the position of a chessboard (A1 – H8),
and BaseChessman
is an abstract class which the concrete classes like Rook
, Bishop
, King
etc. inherit from.
Now to the matter at hand, if I want to move a piece I need to check that the move is valid for the type of chess piece.
At the moment I am trying to get all correct moves for the chess piece “Rook”. Let say the Rook at “A1” somehow is now standing where the blue circle is, I can filter out all invalid moves except the Black Queen at “D8”. My question is how should filter out moves which is blocked by another chess piece, like in this case where “D7” blocks “D8”. Can I add som field to the enum from which I can filter out moves which is blocked by another piece? (See image below for clarification)
PS: I know my implemenetation is flawed since im not at the moment checking if the piece im currently wanting to move is blocked.
The board represented with a enum(A hashmap is created from this enum. Key:Position, Value: BaseChessman)
(What is Ghost? It is meant to be a “dummy” class which acts as “None” instead of using null)
public enum Position { A8(new Rook(BLACK)), B8(new Knight(BLACK)), C8(new Bishop(BLACK)), D8(new King(BLACK)), E8(new Queen(BLACK)), F8(new Bishop(BLACK)), G8(new Knight(BLACK)), H8(new Rook(BLACK)), A7(new Pawn(BLACK)), B7(new Pawn(BLACK)), C7(new Pawn(BLACK)), D7(new Pawn(BLACK)), E7(new Pawn(BLACK)), F7(new Pawn(BLACK)), G7(new Pawn(BLACK)), H7(new Pawn(BLACK)), A6(new Ghost(TRANSPARENT)), B6(new Ghost(TRANSPARENT)), C6(new Ghost(TRANSPARENT)), D6(new Ghost(TRANSPARENT)), E6(new Ghost(TRANSPARENT)), F6(new Ghost(TRANSPARENT)), G6(new Ghost(TRANSPARENT)), H6(new Ghost(TRANSPARENT)), A5(new Ghost(TRANSPARENT)), B5(new Ghost(TRANSPARENT)), C5(new Ghost(TRANSPARENT)), D5(new Ghost(TRANSPARENT)), E5(new Ghost(TRANSPARENT)), F5(new Ghost(TRANSPARENT)), G5(new Ghost(TRANSPARENT)), H5(new Ghost(TRANSPARENT)), A4(new Ghost(TRANSPARENT)), B4(new Ghost(TRANSPARENT)), C4(new Ghost(TRANSPARENT)), D4(new Ghost(TRANSPARENT)), E4(new Ghost(TRANSPARENT)), F4(new Ghost(TRANSPARENT)), G4(new Ghost(TRANSPARENT)), H4(new Ghost(TRANSPARENT)), A3(new Ghost(TRANSPARENT)), B3(new Ghost(TRANSPARENT)), C3(new Ghost(TRANSPARENT)), D3(new Ghost(TRANSPARENT)), E3(new Ghost(TRANSPARENT)), F3(new Ghost(TRANSPARENT)), G3(new Ghost(TRANSPARENT)), H3(new Ghost(TRANSPARENT)), A2(new Pawn(WHITE)), B2(new Pawn(WHITE)), C2(new Pawn(WHITE)), D2(new Pawn(WHITE)), E2(new Pawn(WHITE)), F2(new Pawn(WHITE)), G2(new Pawn(WHITE)), H2(new Pawn(WHITE)), A1(new Rook(WHITE)), B1(new Knight(WHITE)), C1(new Bishop(WHITE)), D1(new King(WHITE)), E1(new Queen(WHITE)), F1(new Bishop(WHITE)), G1(new Knight(WHITE)), H1(new Rook(WHITE)); private BaseChessman chessman; private Position(BaseChessman chessman) { this.chessman = chessman; } public BaseChessman getChessman(){ return this.chessman; } }
My function that is supposed to return a list of possible moves for that position. (nextPosition is currently not used)
private List<Position> getPossibleRookMoves(Color playerColor, Map<Position, BaseChessman> mapOfChessboard, Position currentPosition, Position nextPosition){ String position = currentPosition.toString(); String pos1 = position.substring(0, 1); String pos2 = position.substring(1, 2); return mapOfChessboard.keySet() .stream() .filter(pos -> (pos.toString().contains(pos1)) || pos.toString().contains(pos2))//Filter out any row and col that does not match "currentPosition" .filter(pos -> !(pos.toString().equals(position))) //Filter out the spot the piece stands on .filter(pos -> (pos.getChessman().getColor() != playerColor)) //Filter out spots where same color pieces stands on .sorted() .collect(Collectors.toList()); }
How would you solve this? Would you switch to a 2D array instead of a enum and a map?
Advertisement
Answer
I believe your data model is flawed for the problem domain. There are many signs of this in your code: having to decode column and row via position name, storing pieces in your position enum when they are really independent concepts, having to artificially represent an ’empty’ piece, having no easy way to get positions between two positions.
I would suggest the following model:
public enum Position { A1(1, 1), A2(2, 1) .... private final int column; private final int row; } public class Move { private final List<Position> path; } public enum PieceType { PAWN, KNIGHT, BISHOP ... private final Function<Position,Stream<Move>> moveGenerator; public Stream<Move> getMoves(Position origin) { return moveGenerator.apply(origin); } } public enum Colour { WHITE, BLACK; } public class Piece { private final PieceType type; private final Colour colour; } public class Board { private final EnumMap<Position,Piece> pieces; public Stream<Move> getLegalMoves() { return pieces.entrySet().stream() .flatMap(e -> e.getValue().getType().getMoves(e.getKey())) .filter(this::isLegal); } private boolean isLegal(Move move) { Piece piece = pieces.get(move.getOrigin()); return (!pieces.containsKey(move.getDestination()) || pieces.get(move.getDestination()).getColour() != piece.getColour())) && move.getIntermediatePositions().noneMatch(pieces::containsKey)) } }
With this model you don’t need to have separate classes for each type of piece and you can treat every move the same. I’ve left out getters to keep things simple and there would likely need to be additional methods in position to get all positions in the same row, column etc. to help generate the moves.