From f6e40395d9e576148fedc69f9e57e9d3e474e37e Mon Sep 17 00:00:00 2001 From: MikeBlu <92647684+MikeBlu@users.noreply.github.com> Date: Sun, 18 Dec 2022 02:08:35 -0500 Subject: [PATCH] Added cloning, deepcopy, & Board getMoves --- Michael _Edit's_(non_destructive)/Bishop.java | 31 +++ Michael _Edit's_(non_destructive)/Board.java | 228 +++++++++++------- .../ChessPiece.java | 68 ++++++ Michael _Edit's_(non_destructive)/King.java | 79 ++++++ Michael _Edit's_(non_destructive)/Knight.java | 40 +++ Michael _Edit's_(non_destructive)/Pawn.java | 88 +++++++ Michael _Edit's_(non_destructive)/Queen.java | 34 +++ Michael _Edit's_(non_destructive)/Rook.java | 29 +++ 8 files changed, 510 insertions(+), 87 deletions(-) create mode 100644 Michael _Edit's_(non_destructive)/Bishop.java create mode 100644 Michael _Edit's_(non_destructive)/ChessPiece.java create mode 100644 Michael _Edit's_(non_destructive)/King.java create mode 100644 Michael _Edit's_(non_destructive)/Knight.java create mode 100644 Michael _Edit's_(non_destructive)/Pawn.java create mode 100644 Michael _Edit's_(non_destructive)/Queen.java create mode 100644 Michael _Edit's_(non_destructive)/Rook.java diff --git a/Michael _Edit's_(non_destructive)/Bishop.java b/Michael _Edit's_(non_destructive)/Bishop.java new file mode 100644 index 0000000..67a61d5 --- /dev/null +++ b/Michael _Edit's_(non_destructive)/Bishop.java @@ -0,0 +1,31 @@ +package project.chess; + +import java.awt.*; +import java.util.ArrayList; + +import static project.ui.Display.loadImage; + +public class Bishop extends ChessPiece { + + private Image whiteBishop = loadImage("./resources/chess_piece_2_black_bishop.png"); + private Image blackBishop = loadImage("./resources/chess_piece_2_white_bishop.png"); + + public Bishop(Board b, boolean isWhite, int x, int y) { + super(b,isWhite,x,y); + } + + public Image getImage(){ + if (isWhite) + return whiteBishop; + return blackBishop; + } + + @Override + public ArrayList getMoves() { + return new ArrayList(super.getDiagonalMoves(b.size())); + } + + public Bishop clone () { + return new Bishop(this.b,this.isWhite,this.x,this.y); + } +} diff --git a/Michael _Edit's_(non_destructive)/Board.java b/Michael _Edit's_(non_destructive)/Board.java index cef2a1f..d7c578f 100644 --- a/Michael _Edit's_(non_destructive)/Board.java +++ b/Michael _Edit's_(non_destructive)/Board.java @@ -1,87 +1,141 @@ -package project; - -import project.chess.Board; -import project.chess.Move; -import project.ui.Display; - -import java.util.ArrayList; -import java.util.concurrent.TimeUnit; - -public class Main { - public static void main(String[] args) { - Board mainBoard = new Board(); - - int maxDepth = 2; - Board[] bestStates = new Board[maxDepth]; - - minMax(mainBoard,bestStates,maxDepth,true); - - System.out.println("Ai Done Computing"); - - int counter = 0; - - // mainBoard.getMoves(); - - // display stuff // - Display display = new Display(mainBoard); - long frames = 0; - long lastTime = System.currentTimeMillis(); - long frameTime = System.currentTimeMillis(); - while(display.update(mainBoard)){ - display.repaint(); - // limit usage of system resources by slowing the speed down to 60 fps. - while ((System.currentTimeMillis() - frameTime) < 64f){ - Thread.yield(); - } - frameTime = System.currentTimeMillis(); - - // print out the FPS of the display. - frames++; - if (System.currentTimeMillis() - lastTime > 1000){ - System.out.println("FPS: " + frames); - frames = 0; - lastTime = System.currentTimeMillis(); - } - - try { - TimeUnit.SECONDS.sleep(1); - } catch (Exception ignored) {} - - mainBoard = bestStates[counter%maxDepth]; - - counter++; - - } - System.out.println("Hello world!"); - - - } - - public static int minMax (Board position, Board[] bestStates, int depth, boolean maximizing) { - if (depth == 0) { - bestStates[depth] = position; - return position.evaluate(); - } - - System.out.println(depth); - - if (maximizing) { - int maxEval = Integer.MIN_VALUE; - for (Board state: position.getMoves(maximizing) ) { - int eval = minMax(state,bestStates,depth-1, false); - maxEval = Math.max(maxEval,eval); - } - bestStates[depth-1] = position; - return maxEval; - } else { - int minEval = Integer.MAX_VALUE; - for (Board state: position.getMoves(maximizing) ) { - int eval = minMax(state,bestStates,depth-1, true); - minEval = Math.min(minEval,eval); - } - bestStates[depth-1] = position; - return minEval; - } - } - -} +package project.chess; + +import java.util.ArrayList; + +public class Board { + + private final ChessPiece[][] board = new ChessPiece[8][8]; + + /** + * create a basic chess board in default configuration + */ + public Board(){ + for (int i = 0; i < size(); i++) { + board[i][1] = new Pawn(this, true, i, 1); + board[i][size() - 2] = new Pawn(this, false, i, size() - 2); + } + // white + board[0][0] = new Rook(this, true,0, 0); + board[size()-1][0] = new Rook(this, true, size() - 1, 0); + board[1][0] = new Knight(this, true, 1, 0); + board[size()-2][0] = new Knight(this, true, size() - 2, 0); + board[2][0] = new Bishop(this, true, 2, 0); + board[size()-3][0] = new Bishop(this, true, size() - 3, 0); + board[3][0] = new Queen(this, true, 3, 0); + board[size()-4][0] = new King(this, true, size() - 4, 0); + + // black + board[0][size()-1] = new Rook(this, false,0, size()-1); + board[size()-1][size()-1] = new Rook(this, false, size() - 1, size()-1); + board[1][size()-1] = new Knight(this, false, 1, size()-1); + board[size()-2][size()-1] = new Knight(this, false, size() - 2, size()-1); + board[2][size()-1] = new Bishop(this, false, 2, size()-1); + board[size()-3][size()-1] = new Bishop(this, false, size() - 3, size()-1); + board[3][size()-1] = new Queen(this, false, 3, size()-1); + board[size()-4][size()-1] = new King(this, false, size() - 4, size()-1); + } + + public boolean movePiece(Move movingPiece, Move newPos){ + return movePiece(movingPiece.getX(), movingPiece.getY(), newPos.getX(), newPos.getY()); + } + + public boolean movePiece(int x, int y, int newX, int newY){ + // System.out.println(x + " " + y + " || " + newX + " " + newY); + ChessPiece selectedPiece; + // make sure the place we are moving from has a piece + if ((selectedPiece = get(x, y)) == null) + return false; + ArrayList moves; + moves = selectedPiece.getMoves(); + for (Move m : moves){ + // reject the moves that don't correspond to where we want to move to. + if (m.getX() != newX || m.getY() != newY) + continue; + ChessPiece movedPiece = get(m); + // make sure they are of the same color. Since we know this move is the position we want to move to + // we can early exit because we are not allowed to move on top of our own pieces + if (movedPiece != null && selectedPiece.isWhite == movedPiece.isWhite) + return false; + // if we were unable to set the piece down we failed to move the piece + if (!set(m, selectedPiece)) + return false; + // run special conditions. Only matters for pieces which have special conditions, since is defaulted to empty body. + if (movedPiece != null) + selectedPiece.applySpecialMove(m); + set(x, y, null); + return true; + } + return false; + } + + public ArrayList getMoves(boolean isWhitesTurn) { + ArrayList moveStates = new ArrayList<>(); + Board curr; + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if ((board[i][j] = get(i, j)) == null || (isWhitesTurn && !board[i][j].isWhite()) || + (!isWhitesTurn && board[i][j].isWhite()) ) continue; + for (Move pieceMove : board[i][j].getMoves()) { + curr = deepCopy(); + curr.movePiece(board[i][j].x,board[i][j].y,pieceMove.getX(),pieceMove.getY()); + moveStates.add(deepCopy()); + } + } + } + return moveStates; + } + + public int evaluate () { // !!!!!!!! Fix this, add actual heuristic evaluation + return (int)Math.random()*900; + } + + public Board deepCopy () { + Board temp = new Board(); + + for (int i = 0; i < temp.board.length; i++) { + for (int j = 0; j < temp.board[0].length; j++) { + try { + if (this.board[i][j] == null) { + temp.board[i][j] = null; + continue; + } + + temp.board[i][j] = this.board[i][j].clone(); + } catch (Exception e) { e.printStackTrace();} + } + } + + return temp; + } + + public ChessPiece get(Move m){ + return get(m.getX(), m.getY()); + } + + public ChessPiece get(int x, int y){ + if (x < 0 || x >= board.length) + return null; + if (y < 0 || y >= board.length) + return null; + return board[x][y]; + } + + public boolean set(Move m, ChessPiece piece) { + return set(m.getX(), m.getY(), piece); + } + + protected boolean set(int x, int y, ChessPiece piece){ + if (x < 0 || x >= board.length) + return false; + if (y < 0 || y >= board.length) + return false; + board[x][y] = piece; + return true; + } + + public int size(){ + return board.length; + } + +} \ No newline at end of file diff --git a/Michael _Edit's_(non_destructive)/ChessPiece.java b/Michael _Edit's_(non_destructive)/ChessPiece.java new file mode 100644 index 0000000..a3550c2 --- /dev/null +++ b/Michael _Edit's_(non_destructive)/ChessPiece.java @@ -0,0 +1,68 @@ +package project.chess; + +import java.awt.*; +import java.util.ArrayList; + +public abstract class ChessPiece { + + protected Board b; + protected int x, y; + protected boolean isInDanger, isWhite; + protected boolean isFirstMove = true; + + public ChessPiece(Board b, boolean isWhite, int x, int y) { + this.b = b; + this.x = x; + this.y = y; + this.isWhite = isWhite; + } + + public void setInDanger(boolean isInDanger){ + this.isInDanger = isInDanger; + } + + public boolean isWhite(){ + return isWhite; + } + public Move getPosition(){ + return new Move(x, y); + } + + public boolean isFirstMove(){ + return isFirstMove; + } + + public void setMoved(){ + isFirstMove = false; + } + + public abstract ArrayList getMoves(); + public abstract Image getImage(); + public void applySpecialMove(Move moveWithSpecial){} + + protected ArrayList getCardinalMoves(int length){ + ArrayList moves = new ArrayList(); + for (int i = 1; i <= length; i++){ + // cardinals + moves.add(new Move(x - i, y)); + moves.add(new Move(x + i, y)); + moves.add(new Move(x, y - i)); + moves.add(new Move(x, y + i)); + } + return moves; + } + + protected ArrayList getDiagonalMoves(int length){ + ArrayList moves = new ArrayList(); + for (int i = 1; i <= length; i++){ + // cardinals + moves.add(new Move(x - i, y - i)); + moves.add(new Move(x + i, y + i)); + moves.add(new Move(x + i, y - i)); + moves.add(new Move(x - i, y + i)); + } + return moves; + } + + public abstract ChessPiece clone(); +} \ No newline at end of file diff --git a/Michael _Edit's_(non_destructive)/King.java b/Michael _Edit's_(non_destructive)/King.java new file mode 100644 index 0000000..4d9da5f --- /dev/null +++ b/Michael _Edit's_(non_destructive)/King.java @@ -0,0 +1,79 @@ +package project.chess; + +import java.awt.*; +import java.util.ArrayList; + +import static project.ui.Display.loadImage; + +public class King extends ChessPiece { + + private final Image whiteKing = loadImage("./resources/chess_piece_2_black_king.png"); + private final Image blackKing = loadImage("./resources/chess_piece_2_white_king.png"); + + public King(Board b, boolean isWhite, int x, int y) { + super(b,isWhite,x,y); + } + + public Image getImage(){ + if (isWhite) + return whiteKing; + return blackKing; + } + + @Override + public ArrayList getMoves() { + ArrayList moves = new ArrayList(); + moves.addAll(super.getCardinalMoves(1)); + moves.addAll(super.getDiagonalMoves(1)); + if (isFirstMove){ + ChessPiece rook = null; + // castling + if (isWhite){ + if ((rook = b.get(0, 0)) != null && checkIfRookValid(rook)) + moves.add(new Move(2, 0, Move.SpecialConditions.leftCastle)); + if ((rook = b.get(b.size() - 1, 0)) != null && checkIfRookValid(rook)) + moves.add(new Move(b.size() - 2, 0, Move.SpecialConditions.rightCastle)); + } else { + if ((rook = b.get(0, b.size()-1)) != null && checkIfRookValid(rook)) + moves.add(new Move(2, b.size()-1, Move.SpecialConditions.leftCastle)); + if ((rook = b.get(b.size() - 1, b.size()-1)) != null && checkIfRookValid(rook)) + moves.add(new Move(b.size() - 2, b.size()-1, Move.SpecialConditions.rightCastle)); + } + } + return moves; + } + + @Override + public void applySpecialMove(Move moveWithSpecial){ + Move.SpecialConditions specialCondition; + specialCondition = moveWithSpecial.getSpecialCondition(); + if(specialCondition == Move.SpecialConditions.leftCastle) + castleLeft(); + else if (specialCondition == Move.SpecialConditions.rightCastle) + castleRight(); + } + + private void castleRight(){ + // casting has to move the rook on the right size of the king from white's perspective + if (this.isWhite) + b.set(b.size()-3, 0, b.get(b.size()-1, 0)); + else + b.set(b.size()-3, b.size()-1, b.get(b.size()-1, b.size()-1)); + } + + private boolean checkIfRookValid(ChessPiece piece){ + return piece.isFirstMove() && piece instanceof Rook; + } + + private void castleLeft(){ + // casting has to move the rook on the left size of the king from white's perspective + if (this.isWhite) + b.set(3, 0, b.get(0, 0)); + else + b.set(3, b.size()-1, b.get(0, b.size()-1)); + } + + public King clone () { + return new King(this.b,this.isWhite,this.x,this.y); + } +} diff --git a/Michael _Edit's_(non_destructive)/Knight.java b/Michael _Edit's_(non_destructive)/Knight.java new file mode 100644 index 0000000..4fc4610 --- /dev/null +++ b/Michael _Edit's_(non_destructive)/Knight.java @@ -0,0 +1,40 @@ +package project.chess; + +import java.awt.*; +import java.util.ArrayList; + +import static project.ui.Display.loadImage; + +public class Knight extends ChessPiece { + + private Image whiteKnight = loadImage("./resources/chess_piece_2_black_knight.png"); + private Image blackKnight = loadImage("./resources/chess_piece_2_white_knight.png"); + + public Knight(Board b, boolean isWhite, int x, int y) { + super(b,isWhite,x,y); + } + + public Image getImage(){ + if (isWhite) + return whiteKnight; + return blackKnight; + } + + @Override + public ArrayList getMoves() { + ArrayList moves = new ArrayList(); + moves.add(new Move(x + 2, y + 1)); + moves.add(new Move(x + 2, y - 1)); + moves.add(new Move(x - 2, y + 1)); + moves.add(new Move(x - 2, y - 1)); + moves.add(new Move(x - 1, y - 2)); + moves.add(new Move(x + 1, y - 2)); + moves.add(new Move(x - 1, y + 2)); + moves.add(new Move(x + 1, y + 2)); + return moves; + } + + public Knight clone () { + return new Knight(this.b,this.isWhite,this.x,this.y); + } +} diff --git a/Michael _Edit's_(non_destructive)/Pawn.java b/Michael _Edit's_(non_destructive)/Pawn.java new file mode 100644 index 0000000..1dce672 --- /dev/null +++ b/Michael _Edit's_(non_destructive)/Pawn.java @@ -0,0 +1,88 @@ +package project.chess; + +import project.ui.Display; + +import java.awt.*; +import java.util.ArrayList; + +public class Pawn extends ChessPiece { + + private Image white = Display.loadImage("./resources/chess_piece_2_black_pawn.png"); + private Image black = Display.loadImage("./resources/chess_piece_2_white_pawn.png"); + + public Pawn(Board b, boolean isWhite, int x, int y) { + super(b,isWhite,x,y); + } + + public Image getImage(){ + if (isWhite) + return white; + return black; + } + + @Override + public ArrayList getMoves() { + ArrayList moves = new ArrayList(); + if (isWhite) { + moves.add(new Move(x, y + 1)); + if (isFirstMove) + moves.add(new Move(x, y + 2)); + } else { + moves.add(new Move(x, y - 1)); + if (isFirstMove) + moves.add(new Move(x, y - 2)); + } + ChessPiece neighbour = null; + + if (isWhite){ + // En passant + if ((neighbour = b.get(x-1, y)) != null && checkNeighbourEnPassant(neighbour)) + moves.add(new Move(x-1, y + 1, Move.SpecialConditions.leftEnPassant)); + // En passant + if ((neighbour = b.get(x+1, y)) != null && checkNeighbourEnPassant(neighbour)) + moves.add(new Move(x+1, + 1, Move.SpecialConditions.rightEnPassant)); + } else { + // unfortunately have to flip the direction depending on player type + // En passant + if ((neighbour = b.get(x-1, y)) != null && checkNeighbourEnPassant(neighbour)) + moves.add(new Move(x-1, y - 1, Move.SpecialConditions.leftEnPassant)); + // En passant + if ((neighbour = b.get(x+1, y)) != null && checkNeighbourEnPassant(neighbour)) + moves.add(new Move(x+1, - 1, Move.SpecialConditions.rightEnPassant)); + } + + return moves; + } + + private boolean checkNeighbourEnPassant(ChessPiece neighbour){ + return neighbour instanceof Pawn && ((Pawn) neighbour).isFirstMove() && neighbour.isWhite != this.isWhite; + } + + @Override + public void applySpecialMove(Move moveWithSpecial){ + Move.SpecialConditions specialCondition; + specialCondition = moveWithSpecial.getSpecialCondition(); + if(specialCondition == Move.SpecialConditions.leftEnPassant) + enPassantLeft(); + else if (specialCondition == Move.SpecialConditions.rightEnPassant) + enPassantRight(); + } + + private void enPassantLeft(){ + if (isWhite) + b.set(x-1, y, null); + else + b.set(x+1, y, null); + } + + private void enPassantRight(){ + if (isWhite) + b.set(x+1, y, null); + else + b.set(x-1, y, null); + } + + public Pawn clone () { + return new Pawn(this.b,this.isWhite,this.x,this.y); + } +} diff --git a/Michael _Edit's_(non_destructive)/Queen.java b/Michael _Edit's_(non_destructive)/Queen.java new file mode 100644 index 0000000..eed5b77 --- /dev/null +++ b/Michael _Edit's_(non_destructive)/Queen.java @@ -0,0 +1,34 @@ +package project.chess; + +import java.awt.*; +import java.util.ArrayList; + +import static project.ui.Display.loadImage; + +public class Queen extends ChessPiece { + + private Image whiteQueen = loadImage("./resources/chess_piece_2_black_queen.png"); + private Image blackQueen = loadImage("./resources/chess_piece_2_white_queen.png"); + + public Queen(Board b, boolean isWhite, int x, int y) { + super(b,isWhite,x,y); + } + + public Image getImage(){ + if (isWhite) + return whiteQueen; + return blackQueen; + } + + @Override + public ArrayList getMoves() { + ArrayList moves = new ArrayList(); + moves.addAll(super.getCardinalMoves(b.size())); + moves.addAll(super.getDiagonalMoves(b.size())); + return moves; + } + + public Queen clone () { + return new Queen(this.b,this.isWhite,this.x,this.y); + } +} diff --git a/Michael _Edit's_(non_destructive)/Rook.java b/Michael _Edit's_(non_destructive)/Rook.java new file mode 100644 index 0000000..895c8e0 --- /dev/null +++ b/Michael _Edit's_(non_destructive)/Rook.java @@ -0,0 +1,29 @@ +package project.chess; + +import java.awt.*; +import java.util.ArrayList; + +import static project.ui.Display.loadImage; + +public class Rook extends ChessPiece { + + private Image whiteRook = loadImage("./resources/chess_piece_2_black_rook.png"); + private Image blackRook = loadImage("./resources/chess_piece_2_white_rook.png"); + + public Rook(Board b, boolean isWhite, int x, int y) { + super(b,isWhite,x,y); + } + + public Image getImage(){ + return isWhite ? whiteRook : blackRook; + } + + @Override + public ArrayList getMoves() { + return new ArrayList(super.getCardinalMoves(b.size())); + } + + public Rook clone () { + return new Rook(this.b,this.isWhite,this.x,this.y); + } +}