import java.util.*; import javafx.application.*; import javafx.stage.*; import javafx.scene.*; import javafx.scene.control.*; import javafx.scene.canvas.*; import javafx.scene.shape.*; import javafx.scene.layout.*; import javafx.scene.paint.*; import javafx.scene.text.*; import javafx.event.*; import javafx.scene.input.*; import javafx.scene.image.*; import javafx.geometry.*; //Main class and GUI public class Chess extends Application { private ArrayList gameStateHistory; //Stack of GameBoard states for the move undo. private ArrayList moveHistory; //Stack of tuples (moves) so they can be displayed. private ArrayList moveNotationHistory; //Stack of move notations. //User's first click for a move. private Integer clickRow; //null means no first click made currently. private Integer clickCol; private GraphicsContext graphics; private TextArea moveHistoryTextArea; //Load images private static final Image W_ROOK = new Image("img/w_rook_png_shadow_128px.png"); private static final Image B_ROOK = new Image("img/b_rook_png_shadow_128px.png"); private static final Image W_KNIGHT = new Image("img/w_knight_png_shadow_128px.png"); private static final Image B_KNIGHT = new Image("img/b_knight_png_shadow_128px.png"); private static final Image W_BISHOP = new Image("img/w_bishop_png_shadow_128px.png"); private static final Image B_BISHOP = new Image("img/b_bishop_png_shadow_128px.png"); private static final Image W_QUEEN = new Image("img/w_queen_png_shadow_128px.png"); private static final Image B_QUEEN = new Image("img/b_queen_png_shadow_128px.png"); private static final Image W_KING = new Image("img/w_king_png_shadow_128px.png"); private static final Image B_KING = new Image("img/b_king_png_shadow_128px.png"); private static final Image W_PAWN = new Image("img/w_pawn_png_shadow_128px.png"); private static final Image B_PAWN = new Image("img/b_pawn_png_shadow_128px.png"); public static void main(String[] args) { launch(args); } public void start(Stage stage) { //Boilerplate code VBox root = new VBox(); Scene scene = new Scene(root); stage.setTitle("Chess"); stage.setScene(scene); //Set up canvas for the board Canvas board = new Canvas(400, 400); graphics = board.getGraphicsContext2D(); //Setup move history text area moveHistoryTextArea = new TextArea(); moveHistoryTextArea.setFont(Font.font("Courier New", FontWeight.BOLD, 12)); moveHistoryTextArea.setPrefColumnCount("129. b1Rxb8R+ a1Rxa8R++".length()); moveHistoryTextArea.setEditable(false); moveHistoryTextArea.setPrefHeight(400); moveHistoryTextArea.setMinHeight(400); moveHistoryTextArea.setMaxHeight(400); //Horizontal box for board and move history text area HBox hb = new HBox(20, board, moveHistoryTextArea); hb.setAlignment(Pos.CENTER); root.getChildren().add(hb); //Set up buttons Button undo = new Button("Undo"); Button reset = new Button("Reset"); Button aiMove = new Button("AI move"); HBox h = new HBox(20, undo, reset, aiMove); h.setAlignment(Pos.CENTER); h.setPadding(new Insets(10,10,10,10)); root.getChildren().add(h); //Set up empty move history, game history, notation history. gameStateHistory = new ArrayList(); gameStateHistory.add(new GameState()); //Set up the initial board moveHistory = new ArrayList(); moveNotationHistory = new ArrayList(); //No clicks yet clickRow = clickCol = null; //Set up mouse/click events board.setOnMousePressed(this::mouseClick); undo.setOnAction(this::undo); reset.setOnAction(this::reset); aiMove.setOnAction(this::moveAI); //Refresh the board updateScreen(); //Fix mysterious broken width and prevent shrinking window stage.setWidth(664); stage.setMinWidth(stage.getWidth()); stage.setMinHeight(stage.getHeight()); stage.show(); } private void updateScreen() { for(int x = 0; x < 8; x++) { for(int y = 0; y < 8; y++) { graphics.setFill((x + y) % 2 == 0 ? Color.BURLYWOOD : Color.FORESTGREEN); graphics.fillRect(50*x, 50*y, 50, 50); Square s = board().getSquare(x, y); if ( s.getType() == Square.PAWN && s.isWhite()) graphics.drawImage(W_PAWN, 50*x, 50*y, 50, 50); if ( s.getType() == Square.PAWN && !s.isWhite()) graphics.drawImage(B_PAWN, 50*x, 50*y, 50, 50); if ( s.getType() == Square.KNIGHT && s.isWhite()) graphics.drawImage(W_KNIGHT, 50*x, 50*y, 50, 50); if ( s.getType() == Square.KNIGHT && !s.isWhite()) graphics.drawImage(B_KNIGHT, 50*x, 50*y, 50, 50); if ( s.getType() == Square.BISHOP && s.isWhite()) graphics.drawImage(W_BISHOP, 50*x, 50*y, 50, 50); if ( s.getType() == Square.BISHOP && !s.isWhite()) graphics.drawImage(B_BISHOP, 50*x, 50*y, 50, 50); if ( s.getType() == Square.ROOK && s.isWhite()) graphics.drawImage(W_ROOK, 50*x, 50*y, 50, 50); if ( s.getType() == Square.ROOK && !s.isWhite()) graphics.drawImage(B_ROOK, 50*x, 50*y, 50, 50); if ( s.getType() == Square.QUEEN && s.isWhite()) graphics.drawImage(W_QUEEN, 50*x, 50*y, 50, 50); if ( s.getType() == Square.QUEEN && !s.isWhite()) graphics.drawImage(B_QUEEN, 50*x, 50*y, 50, 50); if ( s.getType() == Square.KING && s.isWhite()) graphics.drawImage(W_KING, 50*x, 50*y, 50, 50); if ( s.getType() == Square.KING && !s.isWhite()) graphics.drawImage(B_KING, 50*x, 50*y, 50, 50); } } } private GameState board() { return gameStateHistory.get(gameStateHistory.size()-1); } //Reset to the intial game position. public void reset(ActionEvent e) { System.out.println(board().getAllPossibleMoves()); //Clear out all the histories gameStateHistory = new ArrayList(); gameStateHistory.add(new GameState()); //Set up the initial board moveHistory = new ArrayList(); moveNotationHistory = new ArrayList(); //Clear out clickRow and clickCol clickRow = clickCol = null; moveHistoryTextArea.setText(""); updateScreen(); } //AI makes a move public void moveAI(ActionEvent e) { //Ask the gameState for a list of all legal moves. ArrayList a = board().getAllPossibleMoves(); if (a.size() <= 0) return; //no moves. //Get one of those in the list uniformly at random. int r = (int)(Math.random() * a.size()); makeMove(a.get(r)); } private void makeMove(Move move) { //Clone the gameState object for the current state of the game, tell it to make the move, add it to the move history GameState g = board().getStateAfterMove(move); if (g==null) return; //didn't actually move it; must not have been legal. gameStateHistory.add(g); //add the move to the history as well moveHistory.add(move); //add the notation for it moveNotationHistory.add(move+""); //Refresh the board updateScreen(); } //For the human moving public void mouseClick(MouseEvent e) { //If it is the first click (null check) then record it, updtae the GUI to show highlighting //else attempt the move: int x = (int)(e.getX() / 50); int y = (int)(e.getY() / 50); if (board().getSquare(x,y).isWhite() == board().isWhitesTurn() && board().getSquare(x,y).getType() != Square.EMPTY) { //if clicking on own piece... clickRow = y; clickCol = x; } else if (clickRow != null) { makeMove(new Move(clickCol, clickRow, x, y)); clickRow = clickCol = null; } updateScreen(); } //Undo button for the most recent move public void undo(ActionEvent e) { //Pop those 3 history object things and update the GUI. if (gameStateHistory.size() <= 1) return; //can't pop the initial state. //Pop those 3 history object things and update the GUI. gameStateHistory.remove(gameStateHistory.size() - 1); moveHistory.remove(moveHistory.size() - 1); moveNotationHistory.remove(moveNotationHistory.size() - 1); clickRow = clickCol = null; updateScreen(); } }