diff --git a/Day 43, 45, 46 (chess application design)/Chess.class b/Day 43, 45, 46 (chess application design)/Chess.class index a37f7e6..3fffbee 100644 Binary files a/Day 43, 45, 46 (chess application design)/Chess.class and b/Day 43, 45, 46 (chess application design)/Chess.class differ diff --git a/Day 43, 45, 46 (chess application design)/Chess.java b/Day 43, 45, 46 (chess application design)/Chess.java index c1bd178..77784a8 100644 --- a/Day 43, 45, 46 (chess application design)/Chess.java +++ b/Day 43, 45, 46 (chess application design)/Chess.java @@ -23,7 +23,7 @@ public class Chess extends Application { //User's first click for a move. private Integer clickRow; //null means no first click made currently. - private Integer clickColumn; + private Integer clickCol; private GraphicsContext graphics; private TextArea moveHistoryTextArea; @@ -87,7 +87,7 @@ public class Chess extends Application { moveNotationHistory = new ArrayList(); //No clicks yet - clickRow = clickColumn = null; + clickRow = clickCol = null; //Set up mouse/click events board.setOnMousePressed(this::mouseClick); @@ -96,7 +96,7 @@ public class Chess extends Application { aiMove.setOnAction(this::moveAI); //Refresh the board - paintBoard(); + updateScreen(); //Fix mysterious broken width and prevent shrinking window stage.setWidth(664); @@ -106,7 +106,7 @@ public class Chess extends Application { stage.show(); } - private void paintBoard() { + 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); @@ -134,37 +134,76 @@ public class Chess extends Application { //Reset to the intial game position. public void reset(ActionEvent e) { - System.out.println(board().getAllPossibleMoves() ); - //Set up the initial board - //Clear out clickRo and clickCol - //Clera out all the histories. + 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. -// makeMove(...); + 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 - //add the notation for it + 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); -// makeMove(...); + 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(); } } \ No newline at end of file diff --git a/Day 43, 45, 46 (chess application design)/GameState.class b/Day 43, 45, 46 (chess application design)/GameState.class index d1724e9..d46ce43 100644 Binary files a/Day 43, 45, 46 (chess application design)/GameState.class and b/Day 43, 45, 46 (chess application design)/GameState.class differ diff --git a/Day 43, 45, 46 (chess application design)/GameState.java b/Day 43, 45, 46 (chess application design)/GameState.java index 10fa681..41b255b 100644 --- a/Day 43, 45, 46 (chess application design)/GameState.java +++ b/Day 43, 45, 46 (chess application design)/GameState.java @@ -32,15 +32,36 @@ public class GameState { } + //Construct a new GameState based on a previous state and a move made on that previous state. + private GameState(GameState prevState, Move move) { + ArrayList moves = prevState.getAllPossibleMoves(); + if (!moves.contains(move)) { + throw new IllegalArgumentException("Move "+move+" not available for given game state."); + } + + isWhitesTurn = !prevState.isWhitesTurn; + boardSquares = new Square[prevState.boardSquares.length]; + for(int i = 0; i < boardSquares.length; i++) { + boardSquares[i] = new Square(prevState.boardSquares[i]); + } + boardSquares[move.getDestIndex()] = new Square(boardSquares[move.getSourceIndex()]); + boardSquares[move.getSourceIndex()] = new Square(Square.EMPTY); + + } + + // 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 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; + try { + return new GameState(this, m); //Create a new object based on this one, update the board state accordingly. + } + catch (IllegalArgumentException e) { + return null; //If it is not a legal move, return null. + } } public ArrayList getAllPossibleMoves() { @@ -49,6 +70,7 @@ public class GameState { //Loop through all squares for(int index = 0; index < boardSquares.length; index++) { int type = boardSquares[index].getType(); + boolean w = boardSquares[index].isWhite(); if (type == Square.OUT_OF_BOUNDS) continue; if (type == Square.EMPTY) continue; @@ -58,6 +80,8 @@ public class GameState { if (type == Square.BISHOP) moves.addAll(findAllMovesFor(index, new int[] {13,-13,11,-11}, true)); if (type == Square.ROOK) moves.addAll(findAllMovesFor(index, new int[] {1,-1,12,-12}, true)); if (type == Square.KNIGHT) moves.addAll(findAllMovesFor(index, new int[] {10,14,23,25,-10,-14,-23,-25}, false)); + if (type == Square.PAWN && w) moves.addAll(findAllMovesFor(index, new int[] {12}, false)); + if (type == Square.PAWN && !w) moves.addAll(findAllMovesFor(index, new int[] {-12}, false)); } return moves; @@ -65,21 +89,28 @@ public class GameState { private ArrayList findAllMovesFor(int index, int[] directions, boolean canMoveMultipleSquares) { ArrayList moves = new ArrayList(); + if (boardSquares[index].isWhite() != isWhitesTurn) return moves; //don't move other person's pieces! + //loop through the directions, see if they work - return the resulting arraylist. for(int dir : directions) { - int destIndex = index + dir; - int type = boardSquares[destIndex].getType(); - if (boardSquares[index].isWhite() != isWhitesTurn) continue; //don't move other person's pieces! - - if (type == Square.OUT_OF_BOUNDS) continue; - if (type == Square.EMPTY || boardSquares[destIndex].isWhite() != isWhitesTurn) { - moves.add(new Move(index, destIndex)); - } + int destIndex = index; + do { + destIndex += dir; + int type = boardSquares[destIndex].getType(); + if (type == Square.OUT_OF_BOUNDS) break; + if (type == Square.EMPTY || boardSquares[destIndex].isWhite() != isWhitesTurn) { + moves.add(new Move(index, destIndex)); + } + } while(boardSquares[destIndex].getType() == Square.EMPTY && canMoveMultipleSquares); } return moves; } + public boolean isWhitesTurn() { + return isWhitesTurn; + } + // returns something like "checkmate", "stalemate", etc. public String getFeedback() { return null; diff --git a/Day 43, 45, 46 (chess application design)/Move.class b/Day 43, 45, 46 (chess application design)/Move.class index f4ea5be..e71a805 100644 Binary files a/Day 43, 45, 46 (chess application design)/Move.class and b/Day 43, 45, 46 (chess application design)/Move.class differ diff --git a/Day 43, 45, 46 (chess application design)/Move.java b/Day 43, 45, 46 (chess application design)/Move.java index 9cd2fe9..081a502 100644 --- a/Day 43, 45, 46 (chess application design)/Move.java +++ b/Day 43, 45, 46 (chess application design)/Move.java @@ -1,25 +1,33 @@ public class Move { //source and dest coordinates - int startIndex; + int sourceIndex; int destIndex; - - public Move(int startIndex, int destIndex) { - this.startIndex = startIndex; + public Move(int sourceIndex, int destIndex) { + this.sourceIndex = sourceIndex; this.destIndex = destIndex; } - public Move(int sourceRow, int sourceColumn, int destinationRow, int destinationColumn) { - //TODO + public Move(int sourceColumn, int sourceRow, int destColumn, int destRow) { + sourceIndex = sourceRow * 12 + sourceColumn + 2 + 12*2; + destIndex = destRow * 12 + destColumn + 2 + 12*2; + } + + public int getSourceIndex() { + return sourceIndex; + } + + public int getDestIndex() { + return destIndex; } public int getSourceRow() { - return startIndex / 12 - 2; + return sourceIndex / 12 - 2; } public int getSourceCol() { - return startIndex % 12 - 2; + return sourceIndex % 12 - 2; } public int getDestRow() { @@ -31,10 +39,23 @@ public class Move { return destIndex % 12 - 2; } + private String letterFor(int col) { + return "abcdefgh".substring(col, col+1); + } - //returns something like e2-e4 or Qc8++ + //returns something like e2-e4 public String toString() { - return "("+getSourceRow()+","+getSourceCol() + ") -> " + "("+getDestRow()+","+getDestCol() + ")"; + return letterFor(getSourceCol())+(getSourceRow()+1) + "-" + letterFor(getDestCol())+(getDestRow()+1); + } + + public boolean equals(Object obj) { + if (obj instanceof Move) { + Move move = (Move)obj; + return (move.sourceIndex == sourceIndex && move.destIndex == destIndex); + } + else { + return false; //not even a move object + } } } diff --git a/Day 43, 45, 46 (chess application design)/Square.class b/Day 43, 45, 46 (chess application design)/Square.class index 937a38a..6f73094 100644 Binary files a/Day 43, 45, 46 (chess application design)/Square.class and b/Day 43, 45, 46 (chess application design)/Square.class differ diff --git a/Day 43, 45, 46 (chess application design)/Square.java b/Day 43, 45, 46 (chess application design)/Square.java index 5a4bae5..28ca1f1 100644 --- a/Day 43, 45, 46 (chess application design)/Square.java +++ b/Day 43, 45, 46 (chess application design)/Square.java @@ -17,6 +17,11 @@ public class Square { //boolean Knowledge of whether or not this piece moved yet (for castling) //boolean for off-the-board square + public Square(Square s) { + type = s.type; + isWhite = s.isWhite; + } + public Square(int type) { this(type, false); } diff --git a/Day 43, 45, 46 (chess application design)/defensive programming.txt b/Day 43, 45, 46 (chess application design)/defensive programming.txt new file mode 100644 index 0000000..ee82712 --- /dev/null +++ b/Day 43, 45, 46 (chess application design)/defensive programming.txt @@ -0,0 +1,21 @@ + +Cloning: the default choice is a deep copy (although be aware of memory usage). + +Common options for handling errors: + * Close program + * Throw exception + * Use a reasonable or neutral value + * Log the error + * Print the error + * Do nothing + [* Use an assert if you want an error thrown, but only during development] + +Example of an assert (in Java, assertions are enabled by running it with the -ea flag) + lat = ... //calculation happens here + + //We want an error thrown if it's out of bounds, but only during development + assert (lat >= -90 && lat <= 90) : "Latitude out of bounds"; + + if (lat > 90) lat = 90; + if (lat < -90) lat = -90; + \ No newline at end of file