diff --git a/src/main/java/game/Board.java b/src/main/java/game/Board.java index ecd1b8d..d4490ee 100644 --- a/src/main/java/game/Board.java +++ b/src/main/java/game/Board.java @@ -29,6 +29,7 @@ public class Board extends JPanel { Field oldField = null; int saveYCoord; int saveXCoord; + boolean onePlayer; boolean isWhitesTurn = true; InformationBoard infoBoard; Field collisionField; @@ -46,8 +47,9 @@ public class Board extends JPanel { Timer timer; /**(); whiteFiguresList = new ArrayList<>(); + + if(onePlayer) { + opponent = new Opponent(arrayBoard); + } + this.setLayout(new java.awt.GridLayout(8, 8)); boolean black = true; @@ -253,7 +260,16 @@ public void actionPerformed(java.awt.event.ActionEvent event) { System.exit(0); } } - isWhitesTurn = !isWhitesTurn; + + //if playing against computer, have them takeTurn + if(onePlayer) { + opponent.takeTurn(blackFiguresList, whiteFiguresList); + } + //otherwise, switch which player's turn it is + else { + isWhitesTurn = !isWhitesTurn; + } + } } diff --git a/src/main/java/game/Main.java b/src/main/java/game/Main.java index 3d31d77..0a3b7dd 100644 --- a/src/main/java/game/Main.java +++ b/src/main/java/game/Main.java @@ -19,40 +19,40 @@ */ public class Main extends JFrame { public Main() { - - - /// \ref T7_1 Popup will ask users to choose the number of players - int playerChoice = getNumberOfPlayers(); - if(playerChoice == 1) { - startOnePlayer(); - } - else { - startTwoPlayer(); - } - - } - - public void startOnePlayer() { - System.out.println("TODO"); - } - - - public void startTwoPlayer() { + /// \ref T7_1 Popup will ask users to choose the number of players + boolean onePlayer = isOnePlayerGame(); try { - this.add( new Board()); + this.add( new Board(onePlayer)); } catch (IOException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } - //this.setSize(10000,10000); + this.pack(); this.setResizable(false); this.setTitle( "CHESS" ); this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); this.setLocationRelativeTo( null ); + + } + /// \ref T7_2 Popup for users to choose number of players + //will return true for One player, false for Two player + public boolean isOnePlayerGame() { + String[] options = {"1 Player", "2 Players"}; + int playerChoice = JOptionPane.showOptionDialog(null, "Please choose number of Players", + "How Many Players?", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); + if (playerChoice == 0) { + return true; + } + else { + return false; + } + } + public static void main(String[] args) { EventQueue.invokeLater(() -> { Main main = new Main(); diff --git a/src/main/java/game/Opponent.java b/src/main/java/game/Opponent.java new file mode 100644 index 0000000..de8b539 --- /dev/null +++ b/src/main/java/game/Opponent.java @@ -0,0 +1,216 @@ +package game; + +import java.util.ArrayList; + +/// \imp \ref T1_1 \ref T1_2 \ref T1_3 +/// Class that allows the game to be played single player. +/// Holds the methods for the computer choosing a piece to move and moving that piece. +/// These functions allow us to simulate a computer player so that the game can +/// be played without a second person. +public class Opponent { + /// \ref T1_1 \ref T1_2 + ///The computer player must have a list of which pieces it can move so that it can + ///choose a piece to move. This cuts down on time checking possible moves on pieces that + ///cannot move at all. + private ArrayList
moveablePieces = new ArrayList(); + /// \ref T1_1 \ref T1_2 + ///The computer player must know where the player's pieces are in order to choose a proper move + private ArrayList
playerPieces; + /// \ref T1_1 \ref T1_2 + ///The computer player needs to know the current board state in order to choose the + ///correct move to make. + private Field[][] board; + + public Opponent(Field[][] arrayBoard) { + this.board = arrayBoard; + } + + /// \imp \ref T1_1 \ref T1_2 \ref T1_3 + ///The computer player must be able to choose a piece to move, choose where to move it, + ///and then move that piece + public void takeTurn(ArrayList
computerPieces, ArrayList
playerPieces) { + this.playerPieces = playerPieces; + determineMoveablePieces(computerPieces); + choosePieceToMove(); + } + + /// \imp \ref T1_3 + /// The computer player must be able to move their pieces to a new position + public void move(Figure piece, int newXCoord, int newYCoord) { + Field oldField = piece.getField(); + System.out.println("Computer moving piece at " + oldField.getXCord() + ", " + oldField.getYCord()); + System.out.println("Moving to " + newXCoord + ", " + newYCoord); + Field newField = board[newXCoord][newYCoord]; + if(newField.getFigure() != null) { + playerPieces.remove(newField.getFigure()); + } + oldField.getFigure().removeTexture(); + newField.setFigure(piece); + oldField.removeFigure(); + } + + ///\imp \ref T1_1 \ref T1_2 + ///Determine which piece should be moved by the computer. + ///The computer player will use the following rules to determine which piece to move next: + ///1. It will try to get out of check, if necessary + ///2. It will try to take an opponent's piece, if possible. + ///3. It will try to protect it's own pieces, if possible. + ///4. It will move randomly if it cannot do any of the above. + public void choosePieceToMove() { + + if(isInCheck(board)) { + escapeCheck(); + return; + } + + for(Figure piece : moveablePieces) { + if(canTakePlayerPiece(piece)) { + return; + } + } + + for(Figure piece : moveablePieces) { + if(canEscapeFromBeingTaken(piece)) { + return; + } + } + + randomPiece(); + return; + } + + ///\ref T1_1 The computer player must know all pieces that are able to be + ///moved in order to correctly choose which piece it will move + public void determineMoveablePieces(ArrayList
pieces) { + moveablePieces.clear(); + for(Figure piece : pieces) { + if(canMove(piece)) { + moveablePieces.add(piece); + } + } + } + + ///\ref T1_1 The computer player has to be able to determine if the piece is + ///able to move before it can decide which piece to move + public boolean canMove(Figure piece) { + for (int xCoord = 0; xCoord < board.length; xCoord++) { + for(int yCoord = 0; yCoord < board[xCoord].length; yCoord++) { + Field localField = board[xCoord][yCoord]; + if(piece.isMovePossible(localField)) + return true; + } + } + return false; + } + + ///\ref T1_1 \ref T1_2 + ///In order to make the correct move, the computer player + ///must be able to determine whether or not it is currently in check + public boolean isInCheck(Field[][] board) { + for(Figure piece : playerPieces) { + for (int xCoord = 0; xCoord < board.length; xCoord++) { + for(int yCoord = 0; yCoord < board[xCoord].length; yCoord++) { + Field localField = board[xCoord][yCoord]; + if(piece.isMovePossible(localField) && localField.getFigure() instanceof King) { + System.out.println("Computer In check!"); + return true; + } + } + } + } + return false; + } + + ///\ref T1_1 \ref T1_2 + ///In order to choose the correct piece and position to a move, + ///the computer player must first attempt to escape from being in check + public void escapeCheck() { + for(Figure piece : moveablePieces) { + for (int xCoord = 0; xCoord < board.length; xCoord++) { + for(int yCoord = 0; yCoord < board[xCoord].length; yCoord++) { + Field localField = board[xCoord][yCoord]; + if(piece.isMovePossible(localField)) { + Figure temp = localField.getFigure(); + Field oldField = piece.getField(); + if(temp != null) { + playerPieces.remove(temp); + } + oldField.getFigure().removeTexture(); + localField.setFigure(piece); + oldField.removeFigure(); + if(isInCheck(board)) { + if(temp == null) { + localField.getFigure().removeTexture(); + oldField.setFigure(piece); + localField.removeFigure(); + } + else { + localField.setFigure(temp); + oldField.setFigure(piece); + playerPieces.add(temp); + } + } + else { + return; + } + + } + } + } + } + } + + ///\ref T1_1 + ///The computer player has to determine if a piece is able to take one of the + ///player's pieces in order to choose which piece to move + public boolean canTakePlayerPiece(Figure piece) { + for (int xCoord = 0; xCoord < board.length; xCoord++) { + for(int yCoord = 0; yCoord < board[xCoord].length; yCoord++) { + Field localField = board[xCoord][yCoord]; + if(piece.isMovePossible(localField) && localField.getFigure() != null){ + move(piece, xCoord, yCoord); + return true; + } + } + } + return false; + } + + ///\ref T1_1 The computer player has to determine if a piece is able to escape + ///being taken by the player in order to choose which piece to move + public boolean canEscapeFromBeingTaken(Figure piece) { + for(Figure playerPiece : playerPieces) { + if(playerPiece.isMovePossible(piece.getField())) { + for (int xCoord = 0; xCoord < board.length; xCoord++) { + for(int yCoord = 0; yCoord < board[xCoord].length; yCoord++) { + Field field = board[xCoord][yCoord]; + if(piece.isMovePossible(field)) { + move(piece, xCoord, yCoord); + return true; + } + } + } + } + } + return false; + } + + ///\ref T1_1 + ///If none of the other conditions are met, the computer player still has to + ///choose a piece, so it will choose a random piece to move + public void randomPiece() { + Figure piece = moveablePieces.get((int)(Math.random() * moveablePieces.size())); + for (int xCoord = 0; xCoord < board.length; xCoord++) { + for(int yCoord = 0; yCoord < board[xCoord].length; yCoord++) { + Field field = board[xCoord][yCoord]; + if(piece.isMovePossible(field)) { + move(piece, xCoord, yCoord); + return; + } + } + } + } + public void setPlayerPieces(ArrayList
playerPieces) { + this.playerPieces = playerPieces; + } +} diff --git a/src/test/java/game/OpponentTest.java b/src/test/java/game/OpponentTest.java new file mode 100644 index 0000000..1b91ad8 --- /dev/null +++ b/src/test/java/game/OpponentTest.java @@ -0,0 +1,352 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package game; + +import java.util.ArrayList; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * + * @author Ian + */ +public class OpponentTest { + + private static Field[][] boardPositions; + private static Opponent opponent; + private static Board board; + private static ArrayList
whitePieces; + private static ArrayList
blackPieces; + + public OpponentTest() { + try { + setUpClass(); + } catch(Exception e) { + + } + } + + @org.junit.jupiter.api.BeforeAll + public static void setUpClass() throws Exception { + //create empty boardPositions + boardPositions = new Field[8][8]; + opponent = new Opponent(boardPositions); + board = new Board(false); + whitePieces = new ArrayList
(); + blackPieces = new ArrayList
(); + opponent.setPlayerPieces(whitePieces); + } + + @org.junit.jupiter.api.AfterAll + public static void tearDownClass() throws Exception { + } + + @org.junit.jupiter.api.BeforeEach + public void setUp() throws Exception { + boolean black = true; + for(int xCoord = 0; xCoord < 8; xCoord++) { + for(int yCoord = 0; yCoord < 8; yCoord++) { + boardPositions[xCoord][yCoord] = new Field(board, black, xCoord, yCoord); + black = !black; + } + } + } + + + @org.junit.jupiter.api.AfterEach + public void tearDown() throws Exception { + //empty boardPositions + for(int xCoord = 0; xCoord < 8; xCoord++) { + for(int yCoord = 0; yCoord < 8; yCoord++) { + boardPositions[xCoord][yCoord].removeFigure(); + } + } + //empty pieces lists + whitePieces.clear(); + blackPieces.clear(); + } + + /// \ref T1_1 \ref T1_2 \ref T1_3 + @org.junit.jupiter.api.Test + public void testTakeTurn() { + try { + setUp(); + + //opponent will move one piece on its turn + //with only one piece, it moves that piece + Figure queen = new Queen (5, 0, true, boardPositions[5][0]); + boardPositions[5][0].setFigure(queen); + blackPieces.add(queen); + Field queenStart = queen.getField(); + opponent.takeTurn(blackPieces, whitePieces); + Field queenEnd = queen.getField(); + assertTrue(queenStart != queenEnd); + + //with more than that, it moves exactly one piece + opponent.move(queen, 5, 0); + Figure rook = new Rook(0, 0, true, boardPositions[0][0]); + boardPositions[0][0].setFigure(rook); + blackPieces.add(rook); + queenStart = queen.getField(); + Field rookStart = rook.getField(); + + opponent.takeTurn(blackPieces, whitePieces); + queenEnd = queen.getField(); + Field rookEnd = rook.getField(); + assertTrue(queenStart != queenEnd || rookStart != rookEnd); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_3 + public void testMove() { + try { + setUp(); + + Figure piece = new Queen(0, 4, true, boardPositions[0][4]); + boardPositions[0][4].setFigure(piece); + int startY = piece.getField().getYCord(); + //before move tests + assertTrue(boardPositions[0][4].getFigure() != null); + assertTrue(boardPositions[0][5].getFigure() == null); + opponent.move(piece, 0, 5); + int endY = piece.getField().getYCord(); + //after move tests + assertTrue(startY != endY); + assertTrue(boardPositions[0][4].getFigure() == null); + assertTrue(boardPositions[0][5].getFigure() != null); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_1 \ref T1_2 + public void testChoosePieceToMove() { + try { + setUp(); + + Figure king = new King(0, 3, true, boardPositions[0][3]); + boardPositions[0][3].setFigure(king); + Figure pawn = new Pawn(3, 5, true, boardPositions[3][5]); + boardPositions[3][5].setFigure(pawn); + blackPieces.add(pawn); + blackPieces.add(king); + + Figure queen = new Queen(0, 6, false, boardPositions[0][6]); + boardPositions[0][6].setFigure(queen); + whitePieces.add(queen); + + //opponent will get itself out of check + //king moves out of danger from queen + assertTrue(opponent.isInCheck(boardPositions)); + opponent.determineMoveablePieces(blackPieces); + opponent.choosePieceToMove(); + assertFalse(opponent.isInCheck(boardPositions)); + + //no longer in check, opponent will take player piece if possible + //bishop takes queen + opponent.move(king, 1, 2); + assertTrue(boardPositions[0][6].getFigure() == queen); + Figure bishop = new Bishop(1, 5, true, boardPositions[1][5]); + boardPositions[1][5].setFigure(bishop); + blackPieces.add(bishop); + opponent.determineMoveablePieces(blackPieces); + opponent.choosePieceToMove(); + assertTrue(boardPositions[0][6].getFigure() == bishop); + + //opponent will attempt to move pieces that are in danger if it cannot take a piece + //pawn moves out of danger from knight + Figure knight = new Knight(2, 7, false, boardPositions[2][7]); + boardPositions[2][7].setFigure(knight); + whitePieces.add(knight); + opponent.determineMoveablePieces(blackPieces); + opponent.choosePieceToMove(); + assertTrue(boardPositions[1][5].getFigure() == null); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_1 + public void testCanMove() { + try { + setUp(); + + //queen on empty board should be able to move + Figure queen = new Queen(1, 1, false, boardPositions[1][1]); + boardPositions[1][1].setFigure(queen); + assertTrue(opponent.canMove(queen)); + + //bishop in corner blocked by queen should not be able to move + Figure bishop = new Bishop(0, 0, false, boardPositions[0][0]); + boardPositions[0][0].setFigure(bishop); + assertFalse(opponent.canMove(bishop)); + + //once queen moves out of the way, bishop can move + opponent.move(queen, 1, 2); + assertTrue(opponent.canMove(bishop)); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_1 \ref T1_2 + public void testIsInCheck() { + try { + setUp(); + + //king on empty board is not in check + Figure king = new King(2, 2, true, boardPositions[2][2]); + boardPositions[2][2].setFigure(king); + assertFalse(opponent.isInCheck(boardPositions)); + + //queen in front of king puts it in check + Figure queen = new Queen(2, 4, false, boardPositions[2][4]); + boardPositions[2][4].setFigure(queen); + whitePieces.add(queen); + assertTrue(opponent.isInCheck(boardPositions)); + + //king moves to the side, is no longer in check + opponent.move(king, 3, 2); + assertFalse(opponent.isInCheck(boardPositions)); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_1 \ref T1_2 + public void testEscapeCheck() { + try { + setUp(); + + //king in check by queen + Figure king = new King(2, 2, true, boardPositions[2][2]); + boardPositions[2][2].setFigure(king); + Figure queen = new Queen(2, 4, false, boardPositions[2][4]); + boardPositions[2][4].setFigure(queen); + blackPieces.add(king); + whitePieces.add(queen); + opponent.determineMoveablePieces(blackPieces); + int startX = king.getField().getXCord(); + int startY = king.getField().getYCord(); + assertTrue(opponent.isInCheck(boardPositions)); + + opponent.escapeCheck(); + + int endX = king.getField().getXCord(); + int endY = king.getField().getYCord(); + //king's position changes after escapeCheck, and king is no longer in check + assertTrue(endX != startX || endY != startY); + assertFalse(opponent.isInCheck(boardPositions)); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_1 + public void testCanTakePlayerPiece() { + try { + setUp(); + + //queen on empty board can't take a piece + Figure queen = new Queen(2, 2, true, boardPositions[2][2]); + boardPositions[2][2].setFigure(queen); + assertFalse(opponent.canTakePlayerPiece(queen)); + + //queen can take pawn in front of it + Figure pawn = new Pawn(2, 5, false, boardPositions[2][5]); + boardPositions[2][5].setFigure(pawn); + + //queen can take pawn + assertTrue(opponent.canTakePlayerPiece(queen)); + //after taking pawn, queen's new position is pawn's original position + assertTrue(boardPositions[2][5].getFigure() == queen); + assertTrue(queen.getField().getXCord() == 2); + assertTrue(queen.getField().getYCord() == 5); + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + /// \ref T1_1 + public void testCanEscapeFromBeingTaken() { + try { + setUp(); + + + Figure bishop = new Bishop(0, 0, true, boardPositions[0][0]); + boardPositions[0][0].setFigure(bishop); + Figure pawn = new Pawn(1, 1, true, boardPositions[1][1]); + boardPositions[1][1].setFigure(pawn); + blackPieces.add(bishop); + blackPieces.add(pawn); + + //nothing threatening bishop, should return false + assertFalse(opponent.canEscapeFromBeingTaken(bishop)); + + //bishop can't move to escape danger + Figure rook = new Rook(0, 1, false, boardPositions[0][1]); + boardPositions[0][1].setFigure(rook); + whitePieces.add(rook); + assertFalse(opponent.canEscapeFromBeingTaken(bishop)); + + //bishop can move, should return true and move bishop + opponent.move(pawn, 1, 2); + assertTrue(opponent.canEscapeFromBeingTaken(bishop)); + assertTrue(boardPositions[0][0].getFigure() == null); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } + + ///\ref T1_1 + public void testRandomPiece() { + try { + setUp(); + + Figure rook = new Rook(0, 1, true, boardPositions[0][1]); + boardPositions[0][1].setFigure(rook); + Figure pawn = new Pawn(2, 2, true, boardPositions[2][2]); + boardPositions[2][2].setFigure(pawn); + blackPieces.add(rook); + blackPieces.add(pawn); + opponent.determineMoveablePieces(blackPieces); + Field rookStart = rook.getField(); + Field pawnStart = pawn.getField(); + + opponent.randomPiece(); + + Field rookEnd = rook.getField(); + Field pawnEnd = pawn.getField(); + //exactly one of the pieces is moved + assertTrue((rookStart != rookEnd && pawnStart == pawnEnd) || (pawnStart != pawnEnd && rookStart == rookEnd)); + + tearDown(); + } catch(Exception e) { + fail(e); + } + } +}