From e2ca1c2f38725050a4f4b788900b16519a0545bb Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 01:01:31 -0700 Subject: [PATCH 1/8] Possibly fix intermittent stalemate issues --- src/Board.java | 2 +- src/ChessAI.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Board.java b/src/Board.java index 0722d8c..287a263 100644 --- a/src/Board.java +++ b/src/Board.java @@ -232,7 +232,7 @@ public class Board { break; } } - } else if (getAllLegalMoves(movedPiece.black).isEmpty()) { + } else if (getAllLegalMoves(!movedPiece.black).isEmpty()) { isGameOver = true; isStalemate = true; } diff --git a/src/ChessAI.java b/src/ChessAI.java index 9d3c1cd..f11128a 100644 --- a/src/ChessAI.java +++ b/src/ChessAI.java @@ -6,10 +6,11 @@ public class ChessAI { public static Move findBestMove(Board board) { int bestScore = Integer.MIN_VALUE; - Move bestMove = null; ArrayList legalMoves = board.getAllLegalMoves(true); + Collections.shuffle(legalMoves); + Move bestMove = legalMoves.get(0); for (Move move : legalMoves) { board.move(move); int score = minimax(board, MAX_DEPTH, Integer.MIN_VALUE, Integer.MAX_VALUE, false); From 3c6db20f06cf5afb1ffd343bb1c9db26d99163a5 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 11:41:18 -0700 Subject: [PATCH 2/8] Improve colors --- src/Board.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Board.java b/src/Board.java index 287a263..7e35e2f 100644 --- a/src/Board.java +++ b/src/Board.java @@ -17,7 +17,10 @@ public class Board { // new Color() takes in an integer representing the color // Colors are represented in hexadecimal, so we can write the hex literal by prefixing the color code with 0x static final Color BLACK = new Color(0x6c595c); - static final Color WHITE = new Color(0xab9b8e); + static final Color WHITE = new Color(0x847875); + static final Color HIGHLIGHT = new Color(0xab9b8e7f, true); + static final Color MOVE_HIGHLIGHT = new Color(0xb65c5f); + static final Color CAPTURE_HIGHLIGHT = new Color(0xafb381); King blackKing; King whiteKing; final DrawingPanel panel; @@ -264,14 +267,14 @@ public class Board { for (int x = y % 2; x < BOARD_SIZE; x += 2) drawRect(x, y); if (dragging != null) { - graphics.setColor(new Color(0, 128, 0, 128)); + graphics.setColor(HIGHLIGHT); for (Move legalMove : legalMoves) drawRect(legalMove.to); if (mousePosition != null) { BoardCoordinate hovering = mousePosition.toBoard(); for (Move legalMove : legalMoves) { if (legalMove.to.equals(hovering)) { - graphics.setColor(get(hovering) == null ? new Color(0, 0, 255, 128) : new Color(255, 0, 0, 128)); + graphics.setColor(get(hovering) == null ? CAPTURE_HIGHLIGHT : MOVE_HIGHLIGHT); drawRect(mousePosition.toBoard()); break; } From e08132376a6882e8243d652310a710b9a9f5721d Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 11:46:09 -0700 Subject: [PATCH 3/8] Add check highlight, fix color names --- src/Board.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Board.java b/src/Board.java index 7e35e2f..8d6125f 100644 --- a/src/Board.java +++ b/src/Board.java @@ -19,8 +19,8 @@ public class Board { static final Color BLACK = new Color(0x6c595c); static final Color WHITE = new Color(0x847875); static final Color HIGHLIGHT = new Color(0xab9b8e7f, true); - static final Color MOVE_HIGHLIGHT = new Color(0xb65c5f); - static final Color CAPTURE_HIGHLIGHT = new Color(0xafb381); + static final Color MOVE_HIGHLIGHT = new Color(0xafb381); + static final Color CAPTURE_HIGHLIGHT = new Color(0xb65c5f); King blackKing; King whiteKing; final DrawingPanel panel; @@ -274,7 +274,7 @@ public class Board { BoardCoordinate hovering = mousePosition.toBoard(); for (Move legalMove : legalMoves) { if (legalMove.to.equals(hovering)) { - graphics.setColor(get(hovering) == null ? CAPTURE_HIGHLIGHT : MOVE_HIGHLIGHT); + graphics.setColor(get(hovering) == null ? MOVE_HIGHLIGHT : CAPTURE_HIGHLIGHT); drawRect(mousePosition.toBoard()); break; } @@ -286,6 +286,10 @@ public class Board { forEachPiece((boardCoordinate, piece) -> { // If piece is the one being dragged, render it at the mouse position // Otherwise, render it at the center of the board tile + if (piece instanceof King && piece.isInCheck(this)) { + graphics.setColor(CAPTURE_HIGHLIGHT); + drawRect(boardCoordinate); + } piece.draw(graphics, panel, boardCoordinate.equals(dragging) ? mousePosition : boardCoordinate.toScreen()); }); From 2c5d9079cde5c1fa59b97f8481a0e0ede24a1726 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 12:24:03 -0700 Subject: [PATCH 4/8] Use threading for AI --- src/Board.java | 16 ++++++++++++---- src/ChessAI.java | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Board.java b/src/Board.java index 8d6125f..0c1f5d9 100644 --- a/src/Board.java +++ b/src/Board.java @@ -25,6 +25,7 @@ public class Board { King whiteKing; final DrawingPanel panel; final Graphics graphics; + public boolean aiThinking = false; // The board is a two-dimensional array of nullable pieces Piece[][] board; @@ -170,6 +171,7 @@ public class Board { setup(); return; } + if (aiThinking) return; // Get board coordinate of mouse click BoardCoordinate coordinate = new ScreenCoordinate(x, y).toBoard(); // If there's no piece there, return @@ -199,12 +201,18 @@ public class Board { move(legalMove); setLastMovedPieceAsMoved(); checkForCheckmate(); + // Clear dragging + dragging = null; + // Redraw without dragging + draw(); if (!isGameOver) { - move(ChessAI.findBestMove(this)); - setLastMovedPieceAsMoved(); - checkForCheckmate(); + try { + ChessAI.move(this); + } catch (Exception e) { + System.out.println(e); + } + return; } - break; } } } diff --git a/src/ChessAI.java b/src/ChessAI.java index f11128a..c047b01 100644 --- a/src/ChessAI.java +++ b/src/ChessAI.java @@ -1,9 +1,32 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; public class ChessAI { private static final int MAX_DEPTH = 3; + public static FutureTask move(Board board) { + Callable callable = new Callable<>() { + @Override + public Void call() throws Exception { + board.aiThinking = true; + Move move = findBestMove(board); + board.move(move); + board.setLastMovedPieceAsMoved(); + board.checkForCheckmate(); + board.draw(); + board.aiThinking = false; + return null; + } + }; + FutureTask future = new FutureTask<>(callable); + Thread thread = new Thread(future); + thread.start(); + return future; + } + public static Move findBestMove(Board board) { int bestScore = Integer.MIN_VALUE; From acb9e05e0736079d33811fa1ee704d9f000ce7d0 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 12:36:46 -0700 Subject: [PATCH 5/8] Fix king control zone, disable checks preventing castling --- src/King.java | 8 ++++---- src/Piece.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/King.java b/src/King.java index 36e62b4..acce730 100644 --- a/src/King.java +++ b/src/King.java @@ -28,8 +28,8 @@ public class King extends Piece { rightRook instanceof Rook && !rightRook.moved && board.get(position.x + 1, position.y) == null && - board.get(position.x + 2, position.y) == null && - !isInCheck(board) + board.get(position.x + 2, position.y) == null // && + // !isInCheck(board) ) { // TODO: Does not take into account squares in castling path being threatened Move rightCastle = new Move(position, new BoardCoordinate(position.x + 2, position.y)); @@ -46,8 +46,8 @@ public class King extends Piece { !leftRook.moved && board.get(position.x - 1, position.y) == null && board.get(position.x - 2, position.y) == null && - board.get(position.x - 3, position.y) == null && - !isInCheck(board) + board.get(position.x - 3, position.y) == null // && + // !isInCheck(board) ) { // TODO: Does not take into account squares in castling path being threatened Move leftCastle = new Move(position, new BoardCoordinate(position.x - 2, position.y)); diff --git a/src/Piece.java b/src/Piece.java index c770688..57fdc6f 100644 --- a/src/Piece.java +++ b/src/Piece.java @@ -61,7 +61,7 @@ public abstract class Piece { outer: for (int y = 0; y < Board.BOARD_SIZE; y++) { for (int x = 0; x < Board.BOARD_SIZE; x++) { Piece piece = board.get(x, y); - if (piece == null || piece.black == black || piece instanceof King) continue; + if (piece == null || piece.black == black) continue; ArrayList legalMoves = piece.getLegalMoves(new BoardCoordinate(x, y), board, false); for (Move legalMove : legalMoves) { Piece pieceAtMove = board.get(legalMove.to); From 851c2f401deaec4a8171b78108211436ab926220 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 12:38:59 -0700 Subject: [PATCH 6/8] Prevent click redraws while AI is thinking --- src/Board.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Board.java b/src/Board.java index 0c1f5d9..03f22a5 100644 --- a/src/Board.java +++ b/src/Board.java @@ -189,6 +189,7 @@ public class Board { } void handleMouseUp(int x, int y) { + if (aiThinking) return; // Get board coordinate of mouse release BoardCoordinate newCoordinate = new ScreenCoordinate(x, y).toBoard(); // Only do something if new coordinate is different from the originating coordinate From 101054091d2b166f87c3ee5af6f99611a910cfec Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 12:39:35 -0700 Subject: [PATCH 7/8] Fix black queen promotion --- src/Pawn.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pawn.java b/src/Pawn.java index 8974601..d719ace 100644 --- a/src/Pawn.java +++ b/src/Pawn.java @@ -24,7 +24,7 @@ public class Pawn extends Piece { if (board.get(position.x + 1, position.y + 1) != null) { possibleMoves.add(new Move(position, new BoardCoordinate(position.x + 1,position.y + 1))); } - for (Move move : possibleMoves) move.isPromotion = position.y + 1 == 0; + for (Move move : possibleMoves) move.isPromotion = position.y + 1 == Board.BOARD_SIZE - 1; } else { if (board.get(position.x, position.y - 1) == null) { possibleMoves.add(new Move(position, new BoardCoordinate(position.x, position.y - 1))); From 9fbc09ea07725f5cc10a8fe86d952173f53b97f1 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Thu, 16 Mar 2023 12:43:19 -0700 Subject: [PATCH 8/8] Clear move history on setup --- src/Board.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Board.java b/src/Board.java index 03f22a5..26b0b63 100644 --- a/src/Board.java +++ b/src/Board.java @@ -63,6 +63,9 @@ public class Board { } public void setup() { + // Clear move history + moveHistory.clear(); + // Initialize board board = new Piece[BOARD_SIZE][BOARD_SIZE];