Added pawn promotion #11
@ -65,5 +65,5 @@ public class Bishop extends Piece {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.BISHOP; }
|
||||
public int getValue() { return 30; }
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import dev.kske.chess.board.Piece.Color;
|
||||
import dev.kske.chess.board.Piece.Type;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
@ -61,9 +60,6 @@ public class Board {
|
||||
Piece piece = getPos(move);
|
||||
if (piece == null || !piece.isValidMove(move)) return false;
|
||||
else {
|
||||
// Set type after validation
|
||||
if (move.type == Move.Type.UNKNOWN) move.type = Move.Type.NORMAL;
|
||||
|
||||
// Move piece
|
||||
move(move);
|
||||
|
||||
@ -86,40 +82,11 @@ public class Board {
|
||||
Piece piece = getPos(move);
|
||||
Piece capturePiece = getDest(move);
|
||||
|
||||
switch (move.type) {
|
||||
case PAWN_PROMOTION:
|
||||
setPos(move, null);
|
||||
// TODO: Select promotion
|
||||
setDest(move, new Queen(piece.getColor(), this));
|
||||
break;
|
||||
case EN_PASSANT:
|
||||
setDest(move, piece);
|
||||
setPos(move, null);
|
||||
boardArr[move.dest.x][move.dest.y - move.ySign] = null;
|
||||
break;
|
||||
case CASTLING:
|
||||
// Move the king
|
||||
setDest(move, piece);
|
||||
setPos(move, null);
|
||||
|
||||
// Move the rook
|
||||
Move rookMove = move.dest.x == 6 ? new Move(7, move.pos.y, 5, move.pos.y) // Kingside
|
||||
: new Move(0, move.pos.y, 3, move.pos.y); // Queenside
|
||||
setDest(rookMove, getPos(rookMove));
|
||||
setPos(rookMove, null);
|
||||
break;
|
||||
case UNKNOWN:
|
||||
System.err.printf("Move of unknown type %s found!%n", move);
|
||||
case NORMAL:
|
||||
setDest(move, piece);
|
||||
setPos(move, null);
|
||||
break;
|
||||
default:
|
||||
System.err.printf("Move %s of unimplemented type found!%n", move);
|
||||
}
|
||||
// Execute the move
|
||||
move.execute(this);
|
||||
|
||||
// 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 instanceof King) kingPos.put(piece.getColor(), move.getDest());
|
||||
|
||||
// Update log
|
||||
log.add(move, piece, capturePiece);
|
||||
@ -136,57 +103,75 @@ public class Board {
|
||||
Pattern.compile(
|
||||
"^(?<pieceType>[NBRQK])(?:(?<fromFile>[a-h])|(?<fromRank>[1-8])|(?<fromSquare>[a-h][1-8]))?x?(?<toSquare>[a-h][1-8])(?:\\+{0,2}|\\#)$"));
|
||||
patterns.put("pawnCapture",
|
||||
Pattern.compile("^(?<fromFile>[a-h])(?<fromRank>[1-8])?x(?<toSquare>[a-h][1-8])(?<promotedTo>[NBRQK])?(?:\\+{0,2}|\\#)?$"));
|
||||
patterns.put("pawnPush", Pattern.compile("^(?<toSquare>[a-h][1-8])(?<promotedTo>[NBRQK])?(?:\\+{0,2}|\\#)$"));
|
||||
Pattern.compile("^(?<fromFile>[a-h])(?<fromRank>[1-8])?x(?<toSquare>[a-h][1-8])(?<promotedTo>[NBRQ])?(?:\\+{0,2}|\\#)?$"));
|
||||
patterns.put("pawnPush", Pattern.compile("^(?<toSquare>[a-h][1-8])(?<promotedTo>[NBRQ])?(?:\\+{0,2}|\\#)$"));
|
||||
patterns.put("castling", Pattern.compile("^(?<queenside>O-O-O)|(?<kingside>O-O)(?:\\+{0,2}|\\#)?$"));
|
||||
|
||||
patterns.forEach((patternName, pattern) -> {
|
||||
Matcher m = pattern.matcher(sanMove);
|
||||
if (m.find()) {
|
||||
Position pos = null, dest = null;
|
||||
Move.Type moveType = Move.Type.NORMAL;
|
||||
Move move = null;
|
||||
switch (patternName) {
|
||||
case "pieceMove":
|
||||
dest = Position.fromLAN(m.group("toSquare"));
|
||||
if (m.group("fromSquare") != null) pos = Position.fromLAN(m.group("fromSquare"));
|
||||
else {
|
||||
Type type = Type.fromFirstChar(m.group("pieceType").charAt(0));
|
||||
Class<? extends Piece> pieceClass = Piece.fromFirstChar(m.group("pieceType").charAt(0));
|
||||
char file;
|
||||
int rank;
|
||||
if (m.group("fromFile") != null) {
|
||||
file = m.group("fromFile").charAt(0);
|
||||
rank = get(type, file);
|
||||
rank = get(pieceClass, file);
|
||||
pos = Position.fromLAN(String.format("%c%d", file, rank));
|
||||
} else if (m.group("fromRank") != null) {
|
||||
rank = Integer.parseInt(m.group("fromRank").substring(0, 1));
|
||||
file = get(type, rank);
|
||||
file = get(pieceClass, rank);
|
||||
pos = Position.fromLAN(String.format("%c%d", file, rank));
|
||||
} else pos = get(type, dest);
|
||||
} else pos = get(pieceClass, dest);
|
||||
}
|
||||
move = new Move(pos, dest);
|
||||
break;
|
||||
case "pawnCapture":
|
||||
dest = Position.fromLAN(m.group("toSquare"));
|
||||
char file = m.group("fromFile").charAt(0);
|
||||
int rank = m.group("fromRank") == null ? get(Type.PAWN, file) : Integer.parseInt(m.group("fromRank"));
|
||||
int rank = m.group("fromRank") == null ? get(Pawn.class, file) : Integer.parseInt(m.group("fromRank"));
|
||||
|
||||
dest = Position.fromLAN(m.group("toSquare"));
|
||||
pos = Position.fromLAN(String.format("%c%d", file, rank));
|
||||
|
||||
if (m.group("promotedTo") != null) {
|
||||
try {
|
||||
move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0)));
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else move = new Move(pos, dest);
|
||||
break;
|
||||
case "pawnPush":
|
||||
dest = Position.fromLAN(m.group("toSquare"));
|
||||
// TODO: Pawn promotion
|
||||
int step = log.getActiveColor() == Color.WHITE ? 1 : -1;
|
||||
|
||||
// One step forward
|
||||
if (boardArr[dest.x][dest.y + step] != null) pos = new Position(dest.x, dest.y + step);
|
||||
|
||||
// Double step forward
|
||||
else pos = new Position(dest.x, dest.y + 2 * step);
|
||||
|
||||
if (m.group("promotedTo") != null) {
|
||||
try {
|
||||
move = new PawnPromotion(pos, dest, Piece.fromFirstChar(m.group("promotedTo").charAt(0)));
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else move = new Move(pos, dest);
|
||||
break;
|
||||
case "castling":
|
||||
pos = new Position(4, log.getActiveColor() == Color.WHITE ? 7 : 0);
|
||||
dest = new Position(m.group("kingside") != null ? 6 : 2, pos.y);
|
||||
moveType = Move.Type.CASTLING;
|
||||
move = new Castling(pos, dest);
|
||||
break;
|
||||
}
|
||||
move(new Move(pos, dest, moveType));
|
||||
move(move);
|
||||
return;
|
||||
}
|
||||
});
|
||||
@ -198,41 +183,12 @@ public class Board {
|
||||
public void revert() {
|
||||
MoveNode moveNode = log.getLast();
|
||||
Move move = moveNode.move;
|
||||
Piece capturedPiece = moveNode.capturedPiece;
|
||||
|
||||
switch (move.type) {
|
||||
case PAWN_PROMOTION:
|
||||
setPos(move, new Pawn(getDest(move).getColor(), this));
|
||||
setDest(move, capturedPiece);
|
||||
break;
|
||||
case EN_PASSANT:
|
||||
setPos(move, getDest(move));
|
||||
setDest(move, null);
|
||||
boardArr[move.dest.x][move.dest.y - move.ySign] = new Pawn(getPos(move).getColor().opposite(), this);
|
||||
break;
|
||||
case CASTLING:
|
||||
// Move the king
|
||||
setPos(move, getDest(move));
|
||||
setDest(move, null);
|
||||
|
||||
// Move the rook
|
||||
Move rookMove = move.dest.x == 6 ? new Move(5, move.pos.y, 7, move.pos.y) // Kingside
|
||||
: new Move(3, move.pos.y, 0, move.pos.y); // Queenside
|
||||
setDest(rookMove, getPos(rookMove));
|
||||
setPos(rookMove, null);
|
||||
break;
|
||||
case UNKNOWN:
|
||||
System.err.printf("Move of unknown type %s found!%n", move);
|
||||
case NORMAL:
|
||||
setPos(move, getDest(move));
|
||||
setDest(move, capturedPiece);
|
||||
break;
|
||||
default:
|
||||
System.err.printf("Move %s of unimplemented type found!%n", move);
|
||||
}
|
||||
// Revert the move
|
||||
move.revert(this, moveNode.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);
|
||||
if (getPos(move) instanceof King) kingPos.put(getPos(move).getColor(), move.getPos());
|
||||
|
||||
// Update log
|
||||
log.removeLast();
|
||||
@ -379,30 +335,30 @@ public class Board {
|
||||
/**
|
||||
* Searches for a {@link Piece} inside a file (A - H).
|
||||
*
|
||||
* @param type The {@link Type} of the piece to search for
|
||||
* @param pieceClass The class of the piece to search for
|
||||
* @param file The file in which to search for the piece
|
||||
* @return The rank (1 - 8) of the first piece with the specified type and
|
||||
* current color in the file, or {@code -1} if there isn't any
|
||||
*/
|
||||
public int get(Type type, char file) {
|
||||
public int get(Class<? extends Piece> pieceClass, char file) {
|
||||
int x = file - 97;
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (boardArr[x][i] != null && boardArr[x][i].getType() == type && boardArr[x][i].getColor() == log.getActiveColor()) return 8 - i;
|
||||
if (boardArr[x][i] != null && boardArr[x][i].getClass() == pieceClass && boardArr[x][i].getColor() == log.getActiveColor()) return 8 - i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a {@link Piece} inside a rank (1 - 8).
|
||||
*
|
||||
* @param type The {@link Type} of the piece to search for
|
||||
* @param pieceClass The class of the piece to search for
|
||||
* @param rank The rank in which to search for the piece
|
||||
* @return The file (A - H) of the first piece with the specified type and
|
||||
* current color in the file, or {@code -} if there isn't any
|
||||
*/
|
||||
public char get(Type type, int rank) {
|
||||
public char get(Class<? extends Piece> pieceClass, int rank) {
|
||||
int y = rank - 1;
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (boardArr[i][y] != null && boardArr[i][y].getType() == type && boardArr[i][y].getColor() == log.getActiveColor())
|
||||
if (boardArr[i][y] != null && boardArr[i][y].getClass() == pieceClass && boardArr[i][y].getColor() == log.getActiveColor())
|
||||
return (char) (i + 97);
|
||||
return '-';
|
||||
}
|
||||
@ -410,14 +366,14 @@ public class Board {
|
||||
/**
|
||||
* Searches for a {@link Piece} that can move to a {@link Position}.
|
||||
*
|
||||
* @param type The {@link Type} of the piece to search for
|
||||
* @param pieceClass The class of the piece to search for
|
||||
* @param dest The destination that the piece is required to reach
|
||||
* @return The position of a piece that can move to the specified destination
|
||||
*/
|
||||
public Position get(Type type, Position dest) {
|
||||
public Position get(Class<? extends Piece> pieceClass, Position dest) {
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = 0; j < 8; j++)
|
||||
if (boardArr[i][j] != null && boardArr[i][j].getType() == type && boardArr[i][j].getColor() == log.getActiveColor()) {
|
||||
if (boardArr[i][j] != null && boardArr[i][j].getClass() == pieceClass && boardArr[i][j].getColor() == log.getActiveColor()) {
|
||||
Position pos = new Position(i, j);
|
||||
if (boardArr[i][j].isValidMove(new Move(pos, dest))) return pos;
|
||||
}
|
||||
@ -436,13 +392,13 @@ public class Board {
|
||||
* @param move The move from which position to return a piece
|
||||
* @return The piece at the position of the move
|
||||
*/
|
||||
public Piece getPos(Move move) { return get(move.pos); }
|
||||
public Piece getPos(Move move) { return get(move.getPos()); }
|
||||
|
||||
/**
|
||||
* @param move The move from which destination to return a piece
|
||||
* @return The piece at the destination of the move
|
||||
*/
|
||||
public Piece getDest(Move move) { return get(move.dest); }
|
||||
public Piece getDest(Move move) { return get(move.getDest()); }
|
||||
|
||||
/**
|
||||
* Places a piece at the position of a move.
|
||||
@ -450,7 +406,7 @@ public class Board {
|
||||
* @param move The move at which position to place the piece
|
||||
* @param piece The piece to place
|
||||
*/
|
||||
public void setPos(Move move, Piece piece) { set(move.pos, piece); }
|
||||
public void setPos(Move move, Piece piece) { set(move.getPos(), piece); }
|
||||
|
||||
/**
|
||||
* Places a piece at the destination of a move.
|
||||
@ -458,7 +414,7 @@ public class Board {
|
||||
* @param move The move at which destination to place the piece
|
||||
* @param piece The piece to place
|
||||
*/
|
||||
public void setDest(Move move, Piece piece) { set(move.dest, piece); }
|
||||
public void setDest(Move move, Piece piece) { set(move.getDest(), piece); }
|
||||
|
||||
/**
|
||||
* @return The board array
|
||||
|
35
src/dev/kske/chess/board/Castling.java
Normal file
35
src/dev/kske/chess/board/Castling.java
Normal file
@ -0,0 +1,35 @@
|
||||
package dev.kske.chess.board;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
* File: <strong>Castling.java</strong><br>
|
||||
* Created: <strong>2 Nov 2019</strong><br>
|
||||
*
|
||||
* @since Chess v0.5-alpha
|
||||
* @author Kai S. K. Engelbart
|
||||
*/
|
||||
public class Castling extends Move {
|
||||
|
||||
private final Move rookMove;
|
||||
|
||||
public Castling(Position pos, Position dest) {
|
||||
super(pos, dest);
|
||||
rookMove = dest.x == 6 ? new Move(7, pos.y, 5, pos.y) : new Move(0, pos.y, 3, pos.y);
|
||||
}
|
||||
|
||||
public Castling(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); }
|
||||
|
||||
@Override
|
||||
public void execute(Board board) {
|
||||
// Move the king and the rook
|
||||
super.execute(board);
|
||||
rookMove.execute(board);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revert(Board board, Piece capturedPiece) {
|
||||
// Move the king and the rook
|
||||
super.revert(board, capturedPiece);
|
||||
rookMove.revert(board, null);
|
||||
}
|
||||
}
|
35
src/dev/kske/chess/board/EnPassant.java
Normal file
35
src/dev/kske/chess/board/EnPassant.java
Normal file
@ -0,0 +1,35 @@
|
||||
package dev.kske.chess.board;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
* File: <strong>EnPassant.java</strong><br>
|
||||
* Created: <strong>2 Nov 2019</strong><br>
|
||||
*
|
||||
* @since Chess v0.5-alpha
|
||||
* @author Kai S. K. Engelbart
|
||||
*/
|
||||
public class EnPassant extends Move {
|
||||
|
||||
private final Position capturePos;
|
||||
|
||||
public EnPassant(Position pos, Position dest) {
|
||||
super(pos, dest);
|
||||
capturePos = new Position(dest.x, dest.y - ySign);
|
||||
}
|
||||
|
||||
public EnPassant(int xPos, int yPos, int xDest, int yDest) { this(new Position(xPos, yPos), new Position(xDest, yDest)); }
|
||||
|
||||
@Override
|
||||
public void execute(Board board) {
|
||||
super.execute(board);
|
||||
board.set(capturePos, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revert(Board board, Piece capturedPiece) {
|
||||
super.revert(board, capturedPiece);
|
||||
board.set(capturePos, new Pawn(board.get(pos).getColor().opposite(), board));
|
||||
}
|
||||
|
||||
public Position getCapturePos() { return capturePos; }
|
||||
}
|
@ -164,7 +164,7 @@ public class FENString {
|
||||
}
|
||||
|
||||
// Write piece character
|
||||
char p = piece.getType().firstChar();
|
||||
char p = piece.firstChar();
|
||||
sb.append(piece.getColor() == Color.WHITE ? Character.toUpperCase(p) : p);
|
||||
}
|
||||
}
|
||||
|
@ -17,18 +17,9 @@ public class King extends Piece {
|
||||
|
||||
@Override
|
||||
public boolean isValidMove(Move move) {
|
||||
// Castling
|
||||
if (move.xDist == 2 && move.yDist == 0) {
|
||||
if (canCastleKingside()) {
|
||||
move.type = Move.Type.CASTLING;
|
||||
return true;
|
||||
}
|
||||
if (canCastleQueenside()) {
|
||||
move.type = Move.Type.CASTLING;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return move.xDist <= 1 && move.yDist <= 1 && checkDestination(move);
|
||||
return (move.getxDist() == 2 && move.getyDist() == 0
|
||||
&& (move.getDest().x == 6 && canCastleKingside() || move.getDest().x == 2 && canCastleQueenside()))
|
||||
|| move.getxDist() <= 1 && move.getyDist() <= 1 && checkDestination(move);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -42,8 +33,8 @@ public class King extends Piece {
|
||||
}
|
||||
|
||||
// Castling
|
||||
if (canCastleKingside()) moves.add(new Move(pos, new Position(6, pos.y), Move.Type.CASTLING));
|
||||
if (canCastleQueenside()) moves.add(new Move(pos, new Position(2, pos.y), Move.Type.CASTLING));
|
||||
if (canCastleKingside()) moves.add(new Castling(pos, new Position(6, pos.y)));
|
||||
if (canCastleQueenside()) moves.add(new Castling(pos, new Position(2, pos.y)));
|
||||
|
||||
return moves;
|
||||
}
|
||||
@ -72,10 +63,10 @@ public class King extends Piece {
|
||||
|
||||
private boolean canCastle(Position kingPos, Position freeDest, Position rookPos, Position jumpPos) {
|
||||
Piece rook = board.get(rookPos);
|
||||
return rook != null && rook.getType() == Type.ROOK && isFreePath(new Move(kingPos, freeDest))
|
||||
&& !board.isAttacked(kingPos, getColor().opposite()) && !board.isAttacked(jumpPos, getColor().opposite());
|
||||
return rook != null && rook instanceof Rook && isFreePath(new Move(kingPos, freeDest)) && !board.isAttacked(kingPos, getColor().opposite())
|
||||
&& !board.isAttacked(jumpPos, getColor().opposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.KING; }
|
||||
public int getValue() { return 0; }
|
||||
}
|
@ -19,8 +19,8 @@ public class Knight extends Piece {
|
||||
|
||||
@Override
|
||||
public boolean isValidMove(Move move) {
|
||||
return Math.abs(move.xDist - move.yDist) == 1
|
||||
&& (move.xDist == 1 && move.yDist == 2 || move.xDist == 2 && move.yDist == 1) && checkDestination(move);
|
||||
return Math.abs(move.getxDist() - move.getyDist()) == 1
|
||||
&& (move.getxDist() == 1 && move.getyDist() == 2 || move.getxDist() == 2 && move.getyDist() == 1) && checkDestination(move);
|
||||
}
|
||||
|
||||
private void checkAndInsertMove(List<Move> moves, Position pos, int offsetX, int offsetY) {
|
||||
@ -45,5 +45,8 @@ public class Knight extends Piece {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.KNIGHT; }
|
||||
public int getValue() { return 35; }
|
||||
|
||||
@Override
|
||||
public char firstChar() { return 'n'; }
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
|
||||
import dev.kske.chess.board.Piece.Color;
|
||||
import dev.kske.chess.board.Piece.Type;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
@ -78,14 +77,14 @@ public class Log implements Iterable<MoveNode> {
|
||||
* @param capturedPiece The piece captured with the move
|
||||
*/
|
||||
public void add(Move move, Piece piece, Piece capturedPiece) {
|
||||
enPassant = piece.getType() == Type.PAWN && move.yDist == 2 ? new Position(move.pos.x, move.pos.y + move.ySign) : null;
|
||||
enPassant = piece instanceof Pawn && move.getyDist() == 2 ? new Position(move.getPos().x, move.getPos().y + move.getySign()) : null;
|
||||
if (activeColor == Color.BLACK) ++fullmoveNumber;
|
||||
if (piece.getType() == Type.PAWN || capturedPiece != null) halfmoveClock = 0;
|
||||
if (piece instanceof Pawn || capturedPiece != null) halfmoveClock = 0;
|
||||
else++halfmoveClock;
|
||||
activeColor = activeColor.opposite();
|
||||
|
||||
// Disable castling rights if a king or a rook has been moved
|
||||
if (piece.getType() == Type.KING || piece.getType() == Type.ROOK) disableCastlingRights(piece, move.pos);
|
||||
if (piece instanceof King || piece instanceof Rook) disableCastlingRights(piece, move.getPos());
|
||||
|
||||
final MoveNode leaf = new MoveNode(move, capturedPiece, castlingRights.clone(), enPassant, activeColor, fullmoveNumber, halfmoveClock);
|
||||
|
||||
@ -170,11 +169,11 @@ public class Log implements Iterable<MoveNode> {
|
||||
|
||||
private void disableCastlingRights(Piece piece, Position initialPosition) {
|
||||
// Kingside
|
||||
if (piece.getType() == Type.KING || piece.getType() == Type.ROOK && initialPosition.x == 7)
|
||||
if (piece instanceof King || piece instanceof Rook && initialPosition.x == 7)
|
||||
castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_KINGSIDE : MoveNode.BLACK_KINGSIDE] = false;
|
||||
|
||||
// Queenside
|
||||
if (piece.getType() == Type.KING || piece.getType() == Type.ROOK && initialPosition.x == 0)
|
||||
if (piece instanceof King || piece instanceof Rook && initialPosition.x == 0)
|
||||
castlingRights[piece.getColor() == Color.WHITE ? MoveNode.WHITE_QUEENSIDE : MoveNode.BLACK_QUEENSIDE] = false;
|
||||
}
|
||||
|
||||
|
@ -12,52 +12,58 @@ import java.util.Objects;
|
||||
*/
|
||||
public class Move {
|
||||
|
||||
public final Position pos, dest;
|
||||
public final int xDist, yDist, xSign, ySign;
|
||||
public Type type;
|
||||
protected final Position pos, dest;
|
||||
protected final int xDist, yDist, xSign, ySign;
|
||||
|
||||
public Move(Position pos, Position dest, Type type) {
|
||||
public Move(Position pos, Position dest) {
|
||||
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)); }
|
||||
|
||||
public void execute(Board board) {
|
||||
// Move the piece to the move's destination square and clean the old position
|
||||
board.set(dest, board.get(pos));
|
||||
board.set(pos, null);
|
||||
}
|
||||
|
||||
public Move(int xPos, int yPos, int xDest, int yDest) {
|
||||
this(new Position(xPos, yPos), new Position(xDest, yDest));
|
||||
public void revert(Board board, Piece capturedPiece) {
|
||||
// Move the piece to the move's position square and clean the destination
|
||||
board.set(pos, board.get(dest));
|
||||
board.set(dest, capturedPiece);
|
||||
}
|
||||
|
||||
public static Move fromLAN(String move) {
|
||||
return new Move(Position.fromLAN(move.substring(0, 2)),
|
||||
Position.fromLAN(move.substring(2)));
|
||||
Position pos = Position.fromLAN(move.substring(0, 2));
|
||||
Position dest = Position.fromLAN(move.substring(2));
|
||||
if (move.length() == 5) {
|
||||
try {
|
||||
return new PawnPromotion(pos, dest, Piece.fromFirstChar(move.charAt(4)));
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
} else return new Move(pos, dest);
|
||||
}
|
||||
|
||||
public String toLAN() {
|
||||
return pos.toLAN() + dest.toLAN();
|
||||
}
|
||||
public String toLAN() { return getPos().toLAN() + getDest().toLAN(); }
|
||||
|
||||
public boolean isHorizontal() { return yDist == 0; }
|
||||
public boolean isHorizontal() { return getyDist() == 0; }
|
||||
|
||||
public boolean isVertical() { return xDist == 0; }
|
||||
public boolean isVertical() { return getxDist() == 0; }
|
||||
|
||||
public boolean isDiagonal() { return xDist == yDist; }
|
||||
public boolean isDiagonal() { return getxDist() == getyDist(); }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s -> %s", pos, dest);
|
||||
}
|
||||
public String toString() { return toLAN(); }
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(dest, pos, type, xDist, xSign, yDist, ySign);
|
||||
}
|
||||
public int hashCode() { return Objects.hash(getDest(), getPos(), getxDist(), getxSign(), getyDist(), getySign()); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
@ -65,11 +71,19 @@ public class Move {
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
Move other = (Move) obj;
|
||||
return Objects.equals(dest, other.dest) && Objects.equals(pos, other.pos) && type == other.type
|
||||
&& xDist == other.xDist && xSign == other.xSign && yDist == other.yDist && ySign == other.ySign;
|
||||
return Objects.equals(getDest(), other.getDest()) && Objects.equals(getPos(), other.getPos()) && getxDist() == other.getxDist()
|
||||
&& getxSign() == other.getxSign() && getyDist() == other.getyDist() && getySign() == other.getySign();
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
NORMAL, PAWN_PROMOTION, CASTLING, EN_PASSANT, UNKNOWN
|
||||
}
|
||||
public Position getPos() { return pos; }
|
||||
|
||||
public Position getDest() { return dest; }
|
||||
|
||||
public int getxDist() { return xDist; }
|
||||
|
||||
public int getyDist() { return yDist; }
|
||||
|
||||
public int getxSign() { return xSign; }
|
||||
|
||||
public int getySign() { return ySign; }
|
||||
}
|
||||
|
@ -13,40 +13,27 @@ import java.util.List;
|
||||
*/
|
||||
public class Pawn extends Piece {
|
||||
|
||||
public Pawn(Color color, Board board) {
|
||||
super(color, board);
|
||||
}
|
||||
public Pawn(Color color, Board board) { super(color, board); }
|
||||
|
||||
@Override
|
||||
public boolean isValidMove(Move move) {
|
||||
boolean step = move.isVertical() && move.yDist == 1;
|
||||
boolean doubleStep = move.isVertical() && move.yDist == 2;
|
||||
boolean strafe = move.isDiagonal() && move.xDist == 1;
|
||||
boolean enPassant = false;
|
||||
if (getColor() == Color.WHITE) doubleStep &= move.pos.y == 6;
|
||||
else doubleStep &= move.pos.y == 1;
|
||||
boolean step = move.isVertical() && move.getyDist() == 1;
|
||||
boolean doubleStep = move.isVertical() && move.getyDist() == 2;
|
||||
boolean strafe = move.isDiagonal() && move.getxDist() == 1;
|
||||
boolean enPassant = strafe && move.getDest().equals(board.getLog().getEnPassant());
|
||||
if (getColor() == Color.WHITE) doubleStep &= move.getPos().y == 6;
|
||||
else doubleStep &= move.getPos().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;
|
||||
|
||||
// Mark the move as en passant if necessary
|
||||
if (strafe && move.dest.equals(board.getLog().getEnPassant())) {
|
||||
enPassant = true;
|
||||
move.type = Move.Type.EN_PASSANT;
|
||||
}
|
||||
|
||||
return enPassant || (step ^ doubleStep ^ strafe) && move.ySign == (getColor() == Color.WHITE ? -1 : 1)
|
||||
&& isFreePath(move);
|
||||
return enPassant || (step ^ doubleStep ^ strafe) && move.getySign() == (getColor() == Color.WHITE ? -1 : 1) && isFreePath(move);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFreePath(Move move) {
|
||||
// Two steps forward
|
||||
if (move.yDist == 2)
|
||||
return board.getBoardArr()[move.pos.x][move.dest.y - move.ySign] == null && board.getDest(move) == null;
|
||||
if (move.getyDist() == 2)
|
||||
return board.getBoardArr()[move.getPos().x][move.getDest().y - move.getySign()] == null && board.getDest(move) == null;
|
||||
// One step forward
|
||||
else if (move.xDist == 0) return board.getDest(move) == null;
|
||||
else if (move.getxDist() == 0) return board.getDest(move) == null;
|
||||
// Capture move
|
||||
else return board.getDest(move) != null && board.getDest(move).getColor() != getColor();
|
||||
}
|
||||
@ -54,46 +41,46 @@ public class Pawn extends Piece {
|
||||
@Override
|
||||
protected List<Move> getPseudolegalMoves(Position pos) {
|
||||
List<Move> moves = new ArrayList<>();
|
||||
|
||||
int sign = getColor() == Color.WHITE ? -1 : 1;
|
||||
boolean pawnPromotion = sign == 1 && pos.y == 6 || sign == -1 && pos.y == 1;
|
||||
|
||||
// Strafe left
|
||||
if (pos.x > 0) {
|
||||
Move move = new Move(pos, new Position(pos.x - 1, pos.y + sign));
|
||||
if (isFreePath(move)) moves.add(move);
|
||||
}
|
||||
if (pos.x > 0) addMoveIfValid(moves, pos, new Position(pos.x - 1, pos.y + sign), pawnPromotion);
|
||||
|
||||
// Strafe right
|
||||
if (pos.x < 7) {
|
||||
Move move = new Move(pos, new Position(pos.x + 1, pos.y + sign));
|
||||
if (isFreePath(move)) moves.add(move);
|
||||
}
|
||||
if (pos.x < 7) addMoveIfValid(moves, pos, new Position(pos.x + 1, pos.y + sign), pawnPromotion);
|
||||
|
||||
// Step forward
|
||||
if (sign == 1 && pos.y < 7 || sign == -1 && pos.y > 0) {
|
||||
Move move = new Move(pos, new Position(pos.x, pos.y + sign));
|
||||
if (isFreePath(move)) moves.add(move);
|
||||
}
|
||||
if (sign == 1 && pos.y < 7 || sign == -1 && pos.y > 0) addMoveIfValid(moves, pos, new Position(pos.x, pos.y + sign), pawnPromotion);
|
||||
|
||||
// Double step forward
|
||||
if (sign == 1 && pos.y == 1 || sign == -1 && pos.y == 6) {
|
||||
Move move = new Move(pos, new Position(pos.x, pos.y + 2 * sign));
|
||||
if (isFreePath(move)) moves.add(move);
|
||||
}
|
||||
|
||||
// Mark moves as pawn promotion if necessary
|
||||
if (sign == 1 && pos.y == 6 || sign == -1 && pos.y == 1)
|
||||
moves.parallelStream().forEach(m -> m.type = Move.Type.PAWN_PROMOTION);
|
||||
if (sign == 1 && pos.y == 1 || sign == -1 && pos.y == 6) addMoveIfValid(moves, pos, new Position(pos.x, pos.y + 2 * sign), pawnPromotion);
|
||||
|
||||
// Add en passant move if necessary
|
||||
if (board.getLog().getEnPassant() != null) {
|
||||
Move move = new Move(pos, board.getLog().getEnPassant(), Move.Type.EN_PASSANT);
|
||||
if (move.isDiagonal() && move.xDist == 1) moves.add(move);
|
||||
Move move = new EnPassant(pos, board.getLog().getEnPassant());
|
||||
if (move.isDiagonal() && move.getxDist() == 1) moves.add(move);
|
||||
}
|
||||
|
||||
return moves;
|
||||
}
|
||||
|
||||
private void addMoveIfValid(List<Move> moves, Position pos, Position dest, boolean pawnPromotion) {
|
||||
Move move = new Move(pos, dest);
|
||||
if (isFreePath(move)) {
|
||||
if (pawnPromotion) {
|
||||
try {
|
||||
moves.add(new PawnPromotion(pos, dest, Queen.class));
|
||||
moves.add(new PawnPromotion(pos, dest, Rook.class));
|
||||
moves.add(new PawnPromotion(pos, dest, Knight.class));
|
||||
moves.add(new PawnPromotion(pos, dest, Bishop.class));
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else moves.add(move);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.PAWN; }
|
||||
public int getValue() { return 10; }
|
||||
}
|
||||
|
81
src/dev/kske/chess/board/PawnPromotion.java
Normal file
81
src/dev/kske/chess/board/PawnPromotion.java
Normal file
@ -0,0 +1,81 @@
|
||||
package dev.kske.chess.board;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Objects;
|
||||
|
||||
import dev.kske.chess.board.Piece.Color;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
* File: <strong>PawnPromotion.java</strong><br>
|
||||
* Created: <strong>2 Nov 2019</strong><br>
|
||||
*
|
||||
* @since Chess v0.5-alpha
|
||||
* @author Kai S. K. Engelbart
|
||||
*/
|
||||
public class PawnPromotion extends Move {
|
||||
|
||||
private final Class<? extends Piece> promotionPieceClass;
|
||||
private final Constructor<? extends Piece> promotionPieceConstructor;
|
||||
|
||||
public PawnPromotion(Position pos, Position dest, Class<? extends Piece> promotionPieceClass) throws NoSuchMethodException, SecurityException {
|
||||
super(pos, dest);
|
||||
this.promotionPieceClass = promotionPieceClass;
|
||||
|
||||
// Cache piece constructor
|
||||
promotionPieceConstructor = promotionPieceClass.getDeclaredConstructor(Color.class, Board.class);
|
||||
promotionPieceConstructor.setAccessible(true);
|
||||
}
|
||||
|
||||
public PawnPromotion(int xPos, int yPos, int xDest, int yDest, Class<? extends Piece> promotionPiece)
|
||||
throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
|
||||
InstantiationException {
|
||||
this(new Position(xPos, yPos), new Position(xDest, yDest), promotionPiece);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Board board) {
|
||||
try {
|
||||
board.set(pos, promotionPieceConstructor.newInstance(board.get(pos).getColor(), board));
|
||||
super.execute(board);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revert(Board board, Piece capturedPiece) {
|
||||
board.set(dest, new Pawn(board.get(dest).getColor(), board));
|
||||
super.revert(board, capturedPiece);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toLAN() {
|
||||
char promotionPieceChar = '-';
|
||||
try {
|
||||
promotionPieceChar = (char) promotionPieceClass.getMethod("firstChar").invoke(promotionPieceConstructor.newInstance(null, null));
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException
|
||||
| InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return pos.toLAN() + dest.toLAN() + promotionPieceChar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + Objects.hash(promotionPieceClass);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!super.equals(obj)) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
PawnPromotion other = (PawnPromotion) obj;
|
||||
return Objects.equals(promotionPieceClass, other.promotionPieceClass);
|
||||
}
|
||||
}
|
@ -44,8 +44,8 @@ public abstract class Piece implements Cloneable {
|
||||
* @param move The move to check
|
||||
*/
|
||||
protected boolean isFreePath(Move move) {
|
||||
for (int i = move.pos.x + move.xSign, j = move.pos.y + move.ySign; i != move.dest.x
|
||||
|| j != move.dest.y; i += move.xSign, j += move.ySign)
|
||||
for (int i = move.getPos().x + move.getxSign(), j = move.getPos().y + move.getySign(); i != move.getDest().x
|
||||
|| j != move.getDest().y; i += move.getxSign(), j += move.getySign())
|
||||
if (board.getBoardArr()[i][j] != null) return false;
|
||||
return checkDestination(move);
|
||||
}
|
||||
@ -57,9 +57,7 @@ public abstract class Piece implements Cloneable {
|
||||
* @param move The move to check
|
||||
* @return {@code false} if the move's destination is from the same team
|
||||
*/
|
||||
protected final boolean checkDestination(Move move) {
|
||||
return board.getDest(move) == null || board.getDest(move).getColor() != getColor();
|
||||
}
|
||||
protected final boolean checkDestination(Move move) { return board.getDest(move) == null || board.getDest(move).getColor() != getColor(); }
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
@ -72,14 +70,11 @@ public abstract class Piece implements Cloneable {
|
||||
return piece;
|
||||
}
|
||||
|
||||
public abstract Type getType();
|
||||
|
||||
public Color getColor() { return color; }
|
||||
@Override
|
||||
public String toString() { return getClass().getSimpleName(); }
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(color);
|
||||
}
|
||||
public int hashCode() { return Objects.hash(color); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
@ -90,52 +85,50 @@ public abstract class Piece implements Cloneable {
|
||||
return color == other.color;
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
|
||||
KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN;
|
||||
/**
|
||||
* @return the standard value of this {@link Piece} that can be used for board
|
||||
* evaluation
|
||||
*/
|
||||
public abstract int getValue();
|
||||
|
||||
/**
|
||||
* @return The first character of this {@link Type} in algebraic notation and
|
||||
* @return The first character of this {@link Piece} in algebraic notation and
|
||||
* lower case
|
||||
*/
|
||||
public char firstChar() {
|
||||
return this == KNIGHT ? 'n' : Character.toLowerCase(this.toString().charAt(0));
|
||||
}
|
||||
public char firstChar() { return Character.toLowerCase(toString().charAt(0)); }
|
||||
|
||||
public static Type fromFirstChar(char firstChar) {
|
||||
public static Class<? extends Piece> fromFirstChar(char firstChar) {
|
||||
switch (Character.toLowerCase(firstChar)) {
|
||||
case 'k':
|
||||
return KING;
|
||||
return King.class;
|
||||
case 'q':
|
||||
return QUEEN;
|
||||
return Queen.class;
|
||||
case 'r':
|
||||
return ROOK;
|
||||
return Rook.class;
|
||||
case 'n':
|
||||
return KNIGHT;
|
||||
return Knight.class;
|
||||
case 'b':
|
||||
return BISHOP;
|
||||
return Bishop.class;
|
||||
case 'p':
|
||||
return PAWN;
|
||||
return Pawn.class;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Color} of this {@link Piece}
|
||||
*/
|
||||
public Color getColor() { return color; }
|
||||
|
||||
public static enum Color {
|
||||
|
||||
WHITE, BLACK;
|
||||
|
||||
public static Color fromFirstChar(char c) {
|
||||
return Character.toLowerCase(c) == 'w' ? WHITE : BLACK;
|
||||
}
|
||||
public static Color fromFirstChar(char c) { return Character.toLowerCase(c) == 'w' ? WHITE : BLACK; }
|
||||
|
||||
public char firstChar() {
|
||||
return this == WHITE ? 'w' : 'b';
|
||||
}
|
||||
public char firstChar() { return this == WHITE ? 'w' : 'b'; }
|
||||
|
||||
public Color opposite() {
|
||||
return this == WHITE ? BLACK : WHITE;
|
||||
}
|
||||
public Color opposite() { return this == WHITE ? BLACK : WHITE; }
|
||||
}
|
||||
}
|
||||
|
@ -101,5 +101,5 @@ public class Queen extends Piece {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.QUEEN; }
|
||||
public int getValue() { return 90; }
|
||||
}
|
||||
|
@ -65,5 +65,5 @@ public class Rook extends Piece {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() { return Type.ROOK; }
|
||||
public int getValue() { return 50; }
|
||||
}
|
||||
|
@ -5,9 +5,11 @@ import java.awt.event.MouseListener;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import dev.kske.chess.board.Board;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import dev.kske.chess.board.Move;
|
||||
import dev.kske.chess.board.Move.Type;
|
||||
import dev.kske.chess.board.Piece;
|
||||
import dev.kske.chess.board.Piece.Color;
|
||||
import dev.kske.chess.board.Position;
|
||||
import dev.kske.chess.ui.OverlayComponent;
|
||||
@ -25,7 +27,8 @@ public class NaturalPlayer extends Player implements MouseListener {
|
||||
private final OverlayComponent overlayComponent;
|
||||
|
||||
private boolean moveRequested;
|
||||
private Position pos;
|
||||
private Piece selectedPiece;
|
||||
private List<Move> possibleMoves;
|
||||
|
||||
public NaturalPlayer(Color color, OverlayComponent overlayComponent) {
|
||||
super(color);
|
||||
@ -37,43 +40,63 @@ public class NaturalPlayer extends Player implements MouseListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestMove() {
|
||||
moveRequested = true;
|
||||
}
|
||||
public void requestMove() { moveRequested = true; }
|
||||
|
||||
@Override
|
||||
public void cancelMove() {
|
||||
moveRequested = false;
|
||||
}
|
||||
public void cancelMove() { moveRequested = false; }
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
overlayComponent.removeMouseListener(this);
|
||||
}
|
||||
public void disconnect() { overlayComponent.removeMouseListener(this); }
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent evt) {
|
||||
if (!moveRequested) return;
|
||||
if (pos == null) {
|
||||
pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
|
||||
evt.getPoint().y / overlayComponent.getTileSize());
|
||||
if (selectedPiece == null) {
|
||||
|
||||
Board board = new Board(this.board);
|
||||
if (board.get(pos) != null && board.get(pos).getColor() == color) {
|
||||
List<Position> positions = board.getMoves(pos)
|
||||
.stream()
|
||||
.map(move -> move.dest)
|
||||
.collect(Collectors.toList());
|
||||
overlayComponent.displayDots(positions);
|
||||
} else pos = null;
|
||||
// Get selected Piece
|
||||
final Position pos = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize());
|
||||
selectedPiece = board.get(pos);
|
||||
|
||||
// Check if a piece was selected
|
||||
if (selectedPiece != null) {
|
||||
|
||||
// Discard selection if the piece has the wrong color
|
||||
if (selectedPiece.getColor() == color.opposite()) selectedPiece = null;
|
||||
else {
|
||||
|
||||
// Generate all moves possible with the selected piece and display their
|
||||
// destinations
|
||||
possibleMoves = selectedPiece.getMoves(pos);
|
||||
overlayComponent.displayDots(possibleMoves.stream().map(move -> move.getDest()).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Position dest = new Position(evt.getPoint().x / overlayComponent.getTileSize(),
|
||||
evt.getPoint().y / overlayComponent.getTileSize());
|
||||
Position dest = new Position(evt.getPoint().x / overlayComponent.getTileSize(), evt.getPoint().y / overlayComponent.getTileSize());
|
||||
|
||||
overlayComponent.clearDots();
|
||||
// Get all moves leading to the specified destination
|
||||
List<Move> selectedMoves = possibleMoves.stream().filter(m -> m.getDest().equals(dest)).collect(Collectors.toList());
|
||||
if (!selectedMoves.isEmpty()) {
|
||||
Move move;
|
||||
|
||||
// Process pawn promotion if necessary
|
||||
if (selectedMoves.size() > 1) {
|
||||
|
||||
// Let the user select a promotion piece
|
||||
JComboBox<Move> comboBox = new JComboBox<Move>(selectedMoves.toArray(new Move[0]));
|
||||
JOptionPane.showMessageDialog(overlayComponent, comboBox, "Select a promotion", JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
move = selectedMoves.get(comboBox.getSelectedIndex());
|
||||
} else move = selectedMoves.get(0);
|
||||
|
||||
// Tell the game to execute the move
|
||||
moveRequested = false;
|
||||
game.onMove(NaturalPlayer.this, new Move(pos, dest, Type.UNKNOWN));
|
||||
pos = null;
|
||||
game.onMove(NaturalPlayer.this, move);
|
||||
}
|
||||
|
||||
// Discard the selection
|
||||
overlayComponent.clearDots();
|
||||
selectedPiece = null;
|
||||
possibleMoves = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,10 +5,16 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import dev.kske.chess.board.Bishop;
|
||||
import dev.kske.chess.board.Board;
|
||||
import dev.kske.chess.board.King;
|
||||
import dev.kske.chess.board.Knight;
|
||||
import dev.kske.chess.board.Move;
|
||||
import dev.kske.chess.board.Pawn;
|
||||
import dev.kske.chess.board.Piece;
|
||||
import dev.kske.chess.board.Piece.Color;
|
||||
import dev.kske.chess.board.Piece.Type;
|
||||
import dev.kske.chess.board.Queen;
|
||||
import dev.kske.chess.board.Rook;
|
||||
|
||||
/**
|
||||
* Project: <strong>Chess</strong><br>
|
||||
@ -28,35 +34,35 @@ public class MoveProcessor implements Callable<ProcessingResult> {
|
||||
|
||||
private Move bestMove;
|
||||
|
||||
private static final Map<Type, int[][]> positionScores;
|
||||
private static final Map<Class<? extends Piece>, int[][]> positionScores;
|
||||
|
||||
static {
|
||||
positionScores = new HashMap<>();
|
||||
positionScores.put(Type.KING,
|
||||
positionScores.put(King.class,
|
||||
new int[][] { new int[] { -3, -4, -4, -5, -5, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 },
|
||||
new int[] { -3, -4, -4, -5, -4, -4, -4, -3 }, new int[] { -3, -4, -4, -5, -4, -4, -4, -3 },
|
||||
new int[] { -2, -3, -3, -2, -2, -2, -2, -1 }, new int[] { -1, -2, -2, -2, -2, -2, -2, -1 },
|
||||
new int[] { 2, 2, 0, 0, 0, 0, 2, 2 }, new int[] { 2, 3, 1, 0, 0, 1, 3, 2 } });
|
||||
positionScores.put(Type.QUEEN,
|
||||
positionScores.put(Queen.class,
|
||||
new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, -2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
|
||||
new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { 0, 0, 1, 1, 1, 1, 0, -1 },
|
||||
new int[] { -1, 1, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 0, 1, 0, 0, 0, 0, -1 },
|
||||
new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } });
|
||||
positionScores.put(Type.ROOK,
|
||||
positionScores.put(Rook.class,
|
||||
new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
|
||||
new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
|
||||
new int[] { -1, 0, 0, 0, 0, 0, 0, -1 }, new int[] { 0, 0, 0, 1, 1, 0, 0, 0 } });
|
||||
positionScores.put(Type.KNIGHT,
|
||||
positionScores.put(Knight.class,
|
||||
new int[][] { new int[] { -5, -4, -3, -3, -3, -3, -4, -5 }, new int[] { -4, -2, 0, 0, 0, 0, -2, -4 },
|
||||
new int[] { -3, 0, 1, 2, 2, 1, 0, -3 }, new int[] { -3, 1, 2, 2, 2, 2, 1, -3 }, new int[] { -3, 0, 2, 2, 2, 2, 0, -1 },
|
||||
new int[] { -3, 1, 1, 2, 2, 1, 1, -3 }, new int[] { -4, -2, 0, 1, 1, 0, -2, -4 },
|
||||
new int[] { -5, -4, -3, -3, -3, -3, -4, -5 } });
|
||||
positionScores.put(Type.BISHOP,
|
||||
positionScores.put(Bishop.class,
|
||||
new int[][] { new int[] { -2, -1, -1, -1, -1, -1, -1, 2 }, new int[] { -1, 0, 0, 0, 0, 0, 0, -1 },
|
||||
new int[] { -1, 0, 1, 1, 1, 1, 0, -1 }, new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 0, 1, 1, 1, 1, 0, -1 },
|
||||
new int[] { -1, 1, 1, 1, 1, 1, 1, -1 }, new int[] { -1, 1, 0, 0, 0, 0, 1, -1 },
|
||||
new int[] { -2, -1, -1, -1, -1, -1, -1, -2 } });
|
||||
positionScores.put(Type.PAWN,
|
||||
positionScores.put(Pawn.class,
|
||||
new int[][] { new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 5, 5, 5, 5, 5, 5, 5, 5 }, new int[] { 1, 1, 2, 3, 3, 2, 1, 1 },
|
||||
new int[] { 0, 0, 1, 3, 3, 1, 0, 0 }, new int[] { 0, 0, 0, 2, 2, 0, 0, 0 }, new int[] { 0, 0, -1, 0, 0, -1, 0, 0 },
|
||||
new int[] { 0, 1, 1, -2, -2, 1, 1, 0 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0 } });
|
||||
@ -108,25 +114,9 @@ public class MoveProcessor implements Callable<ProcessingResult> {
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = 0; j < 8; j++)
|
||||
if (board.getBoardArr()[i][j] != null && board.getBoardArr()[i][j].getColor() == color) {
|
||||
switch (board.getBoardArr()[i][j].getType()) {
|
||||
case QUEEN:
|
||||
score += 90;
|
||||
break;
|
||||
case ROOK:
|
||||
score += 50;
|
||||
break;
|
||||
case KNIGHT:
|
||||
score += 30;
|
||||
break;
|
||||
case BISHOP:
|
||||
score += 30;
|
||||
break;
|
||||
case PAWN:
|
||||
score += 10;
|
||||
break;
|
||||
}
|
||||
if (positionScores.containsKey(board.getBoardArr()[i][j].getType()))
|
||||
score += positionScores.get(board.getBoardArr()[i][j].getType())[i][color == Color.WHITE ? j : 7 - j];
|
||||
score += board.getBoardArr()[i][j].getValue();
|
||||
if (positionScores.containsKey(board.getBoardArr()[i][j].getClass()))
|
||||
score += positionScores.get(board.getBoardArr()[i][j].getClass())[i][color == Color.WHITE ? j : 7 - j];
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public class TextureUtil {
|
||||
* @return The fitting texture
|
||||
*/
|
||||
public static Image getPieceTexture(Piece piece) {
|
||||
String key = piece.getType().toString().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
|
||||
String key = piece.toString().toLowerCase() + "_" + piece.getColor().toString().toLowerCase();
|
||||
return scaledTextures.get(key);
|
||||
}
|
||||
|
||||
|
@ -48,15 +48,15 @@ public class OverlayComponent extends JComponent {
|
||||
// Draw an arrow representing the last move and mark its position and
|
||||
// destination
|
||||
if (arrow != null) {
|
||||
Point pos = new Point(arrow.pos.x * tileSize + tileSize / 2, arrow.pos.y * tileSize + tileSize / 2);
|
||||
Point dest = new Point(arrow.dest.x * tileSize + tileSize / 2, arrow.dest.y * tileSize + tileSize / 2);
|
||||
Point pos = new Point(arrow.getPos().x * tileSize + tileSize / 2, arrow.getPos().y * tileSize + tileSize / 2);
|
||||
Point dest = new Point(arrow.getDest().x * tileSize + tileSize / 2, arrow.getDest().y * tileSize + tileSize / 2);
|
||||
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
g2d.setStroke(new BasicStroke(3));
|
||||
|
||||
g2d.setColor(Color.yellow);
|
||||
g2d.drawRect(arrow.pos.x * tileSize, arrow.pos.y * tileSize, tileSize, tileSize);
|
||||
g2d.drawRect(arrow.dest.x * tileSize, arrow.dest.y * tileSize, tileSize, tileSize);
|
||||
g2d.drawRect(arrow.getPos().x * tileSize, arrow.getPos().y * tileSize, tileSize, tileSize);
|
||||
g2d.drawRect(arrow.getDest().x * tileSize, arrow.getDest().y * tileSize, tileSize, tileSize);
|
||||
|
||||
Shape arrowShape = createArrowShape(pos, dest);
|
||||
g.setColor(new Color(255, 0, 0, 127));
|
||||
|
Reference in New Issue
Block a user