Search code examples
javadebuggingchess

Chess game gives invalid moves


I'm writing a chess game, but I keep running into a bug. When I ask for valid moves, my program returns some spots that shouldn't be valid.

This is my current code

public enum PieceType {
    PAWN('p', (piece, pieceOnEndTile, board, move) -> {
        PieceTeam team = piece.getTeam();
        int dx = move.getdx();
        int dy = move.getdy();
        final boolean willKill = pieceOnEndTile != null && pieceOnEndTile.getTeam() != team;
        int direction = -1; // to distinguish between the dy of black and white
        if (team == PieceTeam.BLACK) {
            direction = 1; // for black pawns, dy is positive, for white it's
                            // negative
        }
        dy = dy * direction;
        boolean isKilling = willKill && dy == 1 && abs(dx) == 1;
        boolean isMoving1 = dy == 1 && dx == 0 && pieceOnEndTile == null;
        boolean isMoving2 = dy == 2 && dx == 0 && pieceOnEndTile == null && !piece.hasBeenMoved();

        return isKilling || isMoving1 || isMoving2;

    }), BISHOP('b', (piece, pieceOnEndTile, board, move) -> {
        PieceTeam team = piece.getTeam();
        int dx = move.getdx();
        int dy = move.getdy();
        return abs(dy) == abs(dx) && tileIsEmptyOrEnemy(pieceOnEndTile, team) && !pieceInWay(board, move, team);
    }), KNIGHT('k', (piece, pieceOnEndTile, board, move) -> {
        PieceTeam team = piece.getTeam();
        int dx = move.getdx();
        int dy = move.getdy();
        boolean isCorrectMove = (abs(dy) == abs(2 * dx) || (abs(dx) == abs(2 * dy))) && abs(dy) <= 2 && abs(dx) <= 2;

        return isCorrectMove && tileIsEmptyOrEnemy(pieceOnEndTile, team);
    }), ROOK('r', (piece, pieceOnEndTile, board, move) -> {
        PieceTeam team = piece.getTeam();
        int dx = move.getdx();
        int dy = move.getdy();
        return (abs(dy) >= 1 && abs(dx) == 0 || abs(dx) >= 1 && abs(dy) == 0) && tileIsEmptyOrEnemy(pieceOnEndTile, team) && !pieceInWay(board, move, team);
    }), QUEEN('Q', (piece, pieceOnEndTile, board, move) -> {
        PieceTeam team = piece.getTeam();
        int dx = move.getdx();
        int dy = move.getdy();
        return (abs(dy) == abs(dx) || abs(dy) >= 1 && abs(dx) == 0 || abs(dx) >= 1 && abs(dy) == 0) && tileIsEmptyOrEnemy(pieceOnEndTile, team) && !pieceInWay(board, move, team);
    }), KING('K', (piece, pieceOnEndTile, board, move) -> {
        PieceTeam team = piece.getTeam();
        int dx = move.getdx();
        int dy = move.getdy();
        return abs(dy) <= 1 && abs(dx) <= 1 && tileIsEmptyOrEnemy(pieceOnEndTile, team);
    });

    public final IsLegalMove isLegalMove;
    public final char representation;

    private PieceType(final char representation, final IsLegalMove isLegalMove) {
        this.isLegalMove = isLegalMove;
        this.representation = representation;
    }

    private static boolean tileIsEmptyOrEnemy(GamePiece piece, PieceTeam currentTeam) {
        return piece == null || piece.getTeam() != currentTeam;
    }

    private static boolean pieceInWay(GamePiece[][] board, Move move, PieceTeam team) {
        int changerx = (int) Math.signum(move.getdx());
        int changery = (int) Math.signum(move.getdy());
        for (int x = move.start.x + changerx, y = move.start.y + changery; x < move.end.x; x += changerx, y += changery) {
            if (board[y][x] != null) {
                return true;
            }
        }
        return false;
    }

    @FunctionalInterface
    public static interface IsLegalMove {
        public boolean call(GamePiece piece, GamePiece pieceOnEndTile, GamePiece[][] board, Move move);
    }

}

The problem, I think, is in the pieceInWay method. I've been staring at it forever, but I can't figure it out.


Solution

  • I fixed it by writing out actual cases for each of the 8 directions, and it seems to work now.

    public static boolean pieceInWay(final GamePiece[][] board, final Move move) {
            final int changerx = (int) Math.signum(move.getdx());
            final int changery = (int) Math.signum(move.getdy());
            switch (Direction.getFromDelta(changerx, changery)) {
            case DOWN:
                for (int y = move.start.y + 1; y < move.end.y; y++) {
                    if (board[y][move.start.x] != null) {
                        return true;
                    }
                }
                break;
            case DOWN_LEFT:
                for (int y = move.start.y + 1, x = move.start.x - 1; y < move.end.y; y++, x--) {
                    if (board[y][x] != null) {
                        return true;
                    }
                }
                break;
            case DOWN_RIGHT:
                for (int y = move.start.y + 1, x = move.start.x + 1; y < move.end.y; y++, x++) {
                    if (board[y][x] != null) {
                        return true;
                    }
                }
                break;
            case LEFT:
                for (int x = move.start.x - 1; x > move.end.x; x--) {
                    if (board[move.start.y][x] != null) {
                        return true;
                    }
                }
                break;
            case RIGHT:
                for (int x = move.start.x + 1; x < move.end.x; x++) {
                    if (board[move.start.y][x] != null) {
                        return true;
                    }
                }
                break;
            case UP:
                for (int y = move.start.y - 1; y > move.end.y; y--) {
                    if (board[y][move.start.x] != null) {
                        return true;
                    }
                }
                break;
            case UP_LEFT:
                for (int y = move.start.y - 1, x = move.start.x - 1; y > move.end.y; y--, x--) {
                    if (board[y][x] != null) {
                        return true;
                    }
                }
                break;
            case UP_RIGHT:
                for (int y = move.start.y - 1, x = move.start.x + 1; y > move.end.y; y--, x++) {
                    if (board[y][x] != null) {
                        return true;
                    }
                }
                break;
            }
            return false;
        }
    
        @FunctionalInterface
        public static interface IsLegalMove {
            public boolean call(GamePiece piece, GamePiece pieceOnEndTile, GamePiece[][] board, Move move);
        }
    
        private static enum Direction {
            UP(0, -1),
            UP_RIGHT(1, -1),
            RIGHT(1, 0),
            DOWN_RIGHT(1, 1),
            DOWN(0, 1),
            DOWN_LEFT(-1, 1),
            LEFT(-1, 0),
            UP_LEFT(-1, -1);
    
            public final int dx;
            public final int dy;
    
            private Direction(final int dx, final int dy) {
                this.dx = dx;
                this.dy = dy;
            }
    
            public static Direction getFromDelta(final int dx, final int dy) {
                for (final Direction d : values()) {
                    if (d.dx == dx && d.dy == dy) {
                        return d;
                    }
                }
                return null;
            }
        }