This commit is contained in:
Jacob Signorovitch
2025-05-27 00:45:16 -04:00
parent 5574f330e9
commit 9e450561f2
5 changed files with 159 additions and 25 deletions
+2
View File
@@ -1,5 +1,7 @@
package chess; package chess;
import java.util.ArrayList;
// An actor is an entity capable of making descisions on the board. // An actor is an entity capable of making descisions on the board.
abstract class Actor { abstract class Actor {
Util.Col col; // The color the Actor is acting as. Util.Col col; // The color the Actor is acting as.
+56 -1
View File
@@ -41,6 +41,11 @@ class Board {
Util.scale, Util.scale, OutlineMode.SOLID, Util.scale, Util.scale, OutlineMode.SOLID,
new Color(0, 150, 200, 150) new Color(0, 150, 200, 150)
); );
else if (info == Info.PROMOTION)
infoImage = new RectangleImage(
Util.scale, Util.scale, OutlineMode.SOLID,
new Color(200, 50, 200, 150)
);
scene.placeImageXY( scene.placeImageXY(
new OverlayImage( new OverlayImage(
@@ -64,6 +69,22 @@ class Board {
); );
} }
// Check for a pawn eligible for promotion.
boolean isPawnPromotionPosition(Coord coord, Piece piece) {
if (piece == null || piece.type != PieceType.PAWN) return false;
boolean isPromotionPosition =
(piece.col == Util.Col.WHITE && coord.y == 0) ||
(piece.col == Util.Col.BLACK && coord.y == 7);
return isPromotionPosition;
}
// Display promotion dialog.
void displayPromotionOptions(Coord coord) {
this.info.put(coord, Info.PROMOTION);
}
WorldImage drawPiece(Piece piece) { WorldImage drawPiece(Piece piece) {
return (piece == null) ? new EmptyImage() : piece.draw(); return (piece == null) ? new EmptyImage() : piece.draw();
} }
@@ -105,6 +126,15 @@ class Board {
this.drop(targ); this.drop(targ);
this.set(dest, piece); this.set(dest, piece);
// Check if a pawn needs promotion.
if (piece.type == PieceType.PAWN &&
isPawnPromotionPosition(dest, piece)) {
// Hold turn til promotion complete.
this.game.setAwaitingPromotion(dest);
return;
}
this.game.changeActive(); this.game.changeActive();
} }
@@ -115,6 +145,31 @@ class Board {
return piece.moves(coord, this); return piece.moves(coord, this);
} }
// Promote pawn to type.
void promotePawn(Coord pawnPos, PieceType promotionType) {
Piece pawn = this.get(pawnPos);
if (pawn == null || pawn.type != PieceType.PAWN) return;
Util.Col color = pawn.col;
Piece newPiece = null;
switch (promotionType) {
case QUEEN: newPiece = new Queen(color); break;
case ROOK: newPiece = new Rook(color); break;
case BISHOP: newPiece = new Bishop(color); break;
case KNIGHT: newPiece = new Knight(color); break;
default: newPiece = new Queen(color); // Default queen because best.
}
// Replace pawn with new piece.
this.set(pawnPos, newPiece);
// Clear promotion indicator.
this.info.remove(pawnPos);
this.game.promotionComplete();
}
// Is the selected space free? (either nothing there, or an enemy piece). // Is the selected space free? (either nothing there, or an enemy piece).
boolean isFree(Coord coord, Util.Col col) { boolean isFree(Coord coord, Util.Col col) {
return this.get(coord) == null || this.get(coord).col != col; return this.get(coord) == null || this.get(coord).col != col;
@@ -234,7 +289,7 @@ class Board {
} }
// Information layered over the board. // Information layered over the board.
enum Info { CANMOVE, WARN, SELECTED } enum Info { CANMOVE, WARN, SELECTED, PROMOTION }
// Measured from top left. // Measured from top left.
class Coord { class Coord {
+70 -15
View File
@@ -11,6 +11,8 @@ class Game extends World {
WorldScene scene; WorldScene scene;
Actor activeActor; Actor activeActor;
Actor winner; Actor winner;
Coord awaitingPromotion; // Tracks pawn awaiting promotion.
boolean promotionMode; // Whether game is in special promotion mode.
Game(Actor white, Actor black, Board board) { Game(Actor white, Actor black, Board board) {
this.white = white; this.white = white;
@@ -18,6 +20,8 @@ class Game extends World {
this.board = board; this.board = board;
this.activeActor = this.white; this.activeActor = this.white;
this.winner = null; this.winner = null;
this.awaitingPromotion = null;
this.promotionMode = false;
} }
Game() { Game() {
@@ -26,6 +30,8 @@ class Game extends World {
this.black = new Player(Util.Col.BLACK, this.board); this.black = new Player(Util.Col.BLACK, this.board);
this.activeActor = this.white; this.activeActor = this.white;
this.winner = null; this.winner = null;
this.awaitingPromotion = null;
this.promotionMode = false;
this.setup(); this.setup();
} }
@@ -53,14 +59,30 @@ class Game extends World {
Util.scale * 4, 3 * Util.scale Util.scale * 4, 3 * Util.scale
); );
} else { } else {
this.scene.placeImageXY( if (this.promotionMode) {
new TextImage( this.scene.placeImageXY(
(this.activeActor.equals(this.white) ? "white" : "black") + new TextImage(
"'s turn", "PROMOTE: [Q]ueen; [R]ook; [B]ishop; "
Util.scale / 2, Color.red + "K[N]ight",
), Util.scale / 3, Color.magenta
Util.scale * 2, 3 * Util.scale ),
); Util.scale * 4, 3 * Util.scale
);
// Highlight pawn being promoted.
if (this.awaitingPromotion != null) {
this.board.displayPromotionOptions(this.awaitingPromotion);
}
} else {
this.scene.placeImageXY(
new TextImage(
(this.activeActor.equals(this.white) ? "white" : "black"
) + "'s turn",
Util.scale / 2, Color.red
),
Util.scale * 2, 3 * Util.scale
);
}
} }
} }
@@ -139,12 +161,16 @@ class Game extends World {
public void onMouseClicked(Posn posn) { public void onMouseClicked(Posn posn) {
// Process clicks if game not over. // Process clicks if game not over.
if (this.winner == null) this.activeActor.click(this.where(posn)); if (this.winner == null && !this.promotionMode)
this.activeActor.click(this.where(posn));
} }
public void onKeyEvent(String key) { public void onKeyEvent(String key) {
// Process keys if game is not over. // Process keys if game is not over.
if (this.winner == null) this.activeActor.key(key); if (this.winner == null) {
if (this.promotionMode) handlePromotionKey(key);
else this.activeActor.key(key);
}
} }
void changeActive() { void changeActive() {
@@ -152,12 +178,41 @@ class Game extends World {
else this.activeActor = this.white; else this.activeActor = this.white;
} }
void setWinner(Actor winner) { // Enter promotion mode.
this.winner = winner; void setAwaitingPromotion(Coord pawnPos) {
System.out.println( this.awaitingPromotion = pawnPos;
(winner.equals(this.white) ? "WHITE" : "BLACK") + " WINS!" this.promotionMode = true;
); this.board.displayPromotionOptions(pawnPos);
} }
// Promotion selection key.
void handlePromotionKey(String key) {
if (this.awaitingPromotion == null) return;
PieceType promotionType = null;
switch (key.toLowerCase()) {
case "q": promotionType = PieceType.QUEEN; break;
case "r": promotionType = PieceType.ROOK; break;
case "b": promotionType = PieceType.BISHOP; break;
case "n":
promotionType = PieceType.KNIGHT;
break; // N for knight because K already taken for vim keys :P.
default: return;
}
if (promotionType != null)
this.board.promotePawn(this.awaitingPromotion, promotionType);
}
// Called when promotion is complete.
void promotionComplete() {
this.awaitingPromotion = null;
this.promotionMode = false;
this.changeActive();
}
void setWinner(Actor winner) { this.winner = winner; }
boolean isGameOver() { return this.winner != null; } boolean isGameOver() { return this.winner != null; }
} }
+6 -6
View File
@@ -56,6 +56,12 @@ class Pawn extends Piece {
return moves; return moves;
} }
// Check if pawn in promotion position.
boolean canPromote(Coord coord) {
return (this.col == Util.Col.WHITE && coord.y == 0) ||
(this.col == Util.Col.BLACK && coord.y == Util.boardW - 1);
}
} }
class Rook extends Piece { class Rook extends Piece {
@@ -121,12 +127,6 @@ class Bishop extends Piece {
ArrayList<Coord> moves(Coord coord, Board board) { ArrayList<Coord> moves(Coord coord, Board board) {
ArrayList<Coord> moves = new ArrayList<Coord>(); ArrayList<Coord> moves = new ArrayList<Coord>();
/*
for (int i = -7; i < 8; i++) {
if (i == 0) continue; // Can't move to own position.
moves.add(new Coord(coord.x + i, coord.y + i));
moves.add(new Coord(coord.x + i, coord.y - i));
}*/
ArrayList<Coord> ne = board.getLineDiagNE(coord, this.col); ArrayList<Coord> ne = board.getLineDiagNE(coord, this.col);
ArrayList<Coord> nw = board.getLineDiagNW(coord, this.col); ArrayList<Coord> nw = board.getLineDiagNW(coord, this.col);
ArrayList<Coord> se = board.getLineDiagSE(coord, this.col); ArrayList<Coord> se = board.getLineDiagSE(coord, this.col);
+25 -3
View File
@@ -2,11 +2,15 @@ package chess;
import java.util.ArrayList; import java.util.ArrayList;
// A human-controlled Actor." // A human-controlled Actor.
class Player extends Actor { class Player extends Actor {
Coord selected; // The currently selected piece. Coord selected; // The currently selected piece.
Coord promotionPending; // Tracks if in a promotion.
Player(Util.Col col, Board board) { super(col, board); } Player(Util.Col col, Board board) {
super(col, board);
this.promotionPending = null;
}
void click(Coord coord) { void click(Coord coord) {
Piece piece = this.board.get(coord); Piece piece = this.board.get(coord);
@@ -34,6 +38,17 @@ class Player extends Actor {
System.out.println( System.out.println(
"clicked on moveable space with something selected" "clicked on moveable space with something selected"
); );
// Check if this is a pawn that'll be promoted.
Piece movingPiece = this.board.get(this.selected);
boolean isPawnPromotion = false;
if (movingPiece != null && movingPiece.type == PieceType.PAWN) {
isPawnPromotion =
(movingPiece.col == Util.Col.WHITE && coord.y % 7 == 0
); // Check if on end of board; modulo is nice here.
if (isPawnPromotion) this.promotionPending = coord;
}
this.board.move(this.selected, coord); this.board.move(this.selected, coord);
this.board.unselect(this.selected); this.board.unselect(this.selected);
this.board.undisplayMoves(moves); this.board.undisplayMoves(moves);
@@ -58,9 +73,16 @@ class Player extends Actor {
} }
void key(String key) { void key(String key) {
// Not my problem.
if (this.promotionPending != null) return;
if (this.selected == null) return; if (this.selected == null) return;
this.board.undisplayMoves(this.board.getMoves(this.selected)); this.board.undisplayMoves(this.board.getMoves(this.selected));
this.board.unselect(this.selected); this.board.unselect(this.selected);
this.selected = null; this.selected = null;
} }
// Call when promotion complete.
void clearPromotionPending() { this.promotionPending = null; }
} }