main
Michael Kowalczyk 11 months ago
parent bc00e065e7
commit dab0d64bfc

@ -15,3 +15,11 @@ Designing good classes
* Do not repeat data unless truly necessary.
* Do not give variables more scope than they require.
Immutable objects
-----------------
Benefits:
Thread-safe
Can freely share pointers to objects; no hidden side effects if something changes
Drawback:
If there are lots of changes then immutability may result in memory burn and slowdowns

@ -1,55 +1,168 @@
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 {
private GameBoard board;
public class Chess extends Application {
private ArrayList<GameState> gameStateHistory; //Stack of GameBoard states for the move undo.
private ArrayList<Move> moveHistory; //Stack of tuples (moves) so they can be displayed.
private ArrayList<String> moveNotationHistory; //Stack of move notations.
//User's first click for a move.
private Integer clickRow; //null means no first click made currently.
private Integer clickColumn;
private ArrayList<GameState> gameStateHistory; //Stack of GameBoard states for the move undo.
private ArrayList<Move> moveHistory; //Stack of tuples (moves) so they can be displayed.
private ArrayList<String> moveNotationHistory; //Stack of move notations.
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);
public void start() {
//Set up graphics on the screen, buttons
//Set up the initial board
//Set up empty move history, game history, notation history.
//clickRow and clickCol are null
gameStateHistory = new ArrayList<GameState>();
gameStateHistory.add(new GameState()); //Set up the initial board
moveHistory = new ArrayList<Move>();
moveNotationHistory = new ArrayList<String>();
//No clicks yet
clickRow = clickColumn = null;
//Set up mouse/click events
board.setOnMousePressed(this::mouseClick);
undo.setOnMousePressed(this::undo);
reset.setOnMousePressed(this::reset);
aiMove.setOnMousePressed(this::moveAI);
//Refresh the board
paintBoard();
//Fix mysterious broken width and prevent shrinking window
stage.setWidth(664);
stage.setMinWidth(stage.getWidth());
stage.setMinHeight(stage.getHeight());
stage.show();
}
private void paintBoard() {
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() {
public void reset(MouseEvent e) {
//Set up the initial board
//Clear out clickRo and clickCol
//Clera out all the histories.
}
//AI makes a move
public void moveAI() {
public void moveAI(MouseEvent e) {
//Ask the gameState for a list of all legal moves.
//Get one of those in the list uniformly at random.
makeMove(...);
// makeMove(...);
}
private void makeMove(...) {
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
//add the notation for it
//add the move to the history as well
}
//For the human moving
public void mouseClick() {
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:
makeMove(...);
// makeMove(...);
}
//Undo button for the most recent move
public void undo() {
public void undo(MouseEvent e) {
//Pop those 3 history object things and update the GUI.
}

@ -8,19 +8,45 @@ public class GameState {
public GameState() {
//Set turn to white
isWhitesTurn = true;
//Initialize squares with legal starting position.
boardSquares = new Square[] {
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.ROOK, true), new Square(Square.KNIGHT, true), new Square(Square.BISHOP, true), new Square(Square.QUEEN, true), new Square(Square.KING, true), new Square(Square.BISHOP, true), new Square(Square.KNIGHT, true), new Square(Square.ROOK, true), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), //white
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.PAWN, true), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), //white pawns
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.EMPTY), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.PAWN, false), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), //white pawns
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.ROOK, false), new Square(Square.KNIGHT, false), new Square(Square.BISHOP, false), new Square(Square.QUEEN, false), new Square(Square.KING, false), new Square(Square.BISHOP, false), new Square(Square.KNIGHT, false), new Square(Square.ROOK, false), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), //black
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS), new Square(Square.OUT_OF_BOUNDS),
};
}
public GameState attemptMove(Move m) {
//See if it is legal.
//Change the board state accordingly.
// zero-based index for col and row. Note that -1 and -2 are valid indices into the array, just OUT_OF_BOUNDS.
public Square getSquare(int col, int row) {
return boardSquares[12*2 + 2 + col + row * 12];
}
public Moves[] getAllPossibleMoves() {
public GameState getStateAfterMove(Move m) {
//See if it is legal. If not, return null.
//Create a new object based on this one, update the board state accordingly.
return null;
}
public Move[] getAllPossibleMoves() {
return null;
}
// returns something like "checkmate", "stalemate", etc.
public String getFeedback() {
return null;
}
}

@ -2,12 +2,13 @@
public class Move {
//source and dest coordinates
public Move(...source...dest...) {
public Move(int sourceRow, int sourceColumn, int destinationRow, int destinationColumn) {
}
//returns something like e2-e4 or Qc8++
public String toString() {
return "";
}
}

@ -1,12 +1,42 @@
public class Square {
//Pawn, knight, bishop, rook, queen, king, blank.
//White or black boolean
public static final int OUT_OF_BOUNDS = -1;
public static final int EMPTY = 0;
public static final int PAWN = 1;
public static final int KNIGHT = 2;
public static final int BISHOP = 3;
public static final int ROOK = 4;
public static final int QUEEN = 5;
public static final int KING = 6;
private int type; //one of the 8 constants above.
private boolean isWhite;
//en passant boolean flag (for the square that would be "captured")
//boolean Knowledge of whether or not this piece moved yet (for castling)
//boolean for off-the-board square
public Square(int type) {
this(type, false);
}
public Square(int type, boolean isWhite) {
this.type = type;
this.isWhite = isWhite;
}
//Positive values for white, negative for black, blank square is 0.
public int getValue() {
return 0; //TODO.
}
public int getType() {
return type;
}
public boolean isWhite() {
return isWhite;
}
}

@ -0,0 +1,14 @@
These chess images are from https://opengameart.org/content/chess-pieces-and-board-squares by JohnPablok and are licensed under CC-BY-SA 3.0: https://creativecommons.org/licenses/by-sa/3.0/
You are free to:
Share — copy and redistribute the material in any medium or format for any purpose, even commercially.
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
Attribution — You must give appropriate credit , provide a link to the license, and indicate if changes were made . You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

@ -0,0 +1,22 @@
Writing good methods/functions:
* Naming
* Give descriptive names that say exactly what the routine does (usually verb-noun)
* The name should include or imply the return type
* Lines of code
* Functions over a couple hundred lines of code tend to be more error-prone
* Otherwise, no hard constraints on method length - whatever is natural for the situation
* Parameters
* Use parameter order consistently among different functions
* Document assumptions on parameters
* Document units
* Document zero-based-counting vs one-based-counting if not obvious
* Error handling
* Check values of input parameters
* Use assertions to check for bugs. Use error handling to check for bad input.
* Techniques for error handling:
* Return null or neutral value
* Print or log the error and continue execution
* Throw an exception/error
* Terminate the program
Loading…
Cancel
Save