diff options
Diffstat (limited to 'c1')
| -rw-r--r-- | c1/.vscode/launch.json | 21 | ||||
| -rw-r--r-- | c1/AbstractStrategyGame.java | 47 | ||||
| -rw-r--r-- | c1/Client.java | 36 | ||||
| -rw-r--r-- | c1/ConnectFour.java | 117 | ||||
| -rw-r--r-- | c1/Testing.java | 53 | ||||
| -rw-r--r-- | c1/TicTacToe.java | 132 |
6 files changed, 406 insertions, 0 deletions
diff --git a/c1/.vscode/launch.json b/c1/.vscode/launch.json new file mode 100644 index 0000000..8028593 --- /dev/null +++ b/c1/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "Client", + "request": "launch", + "mainClass": "Client", + "projectName": "c1_b4eddbb1" + } + ] +}
\ No newline at end of file diff --git a/c1/AbstractStrategyGame.java b/c1/AbstractStrategyGame.java new file mode 100644 index 0000000..430aff5 --- /dev/null +++ b/c1/AbstractStrategyGame.java @@ -0,0 +1,47 @@ +import java.util.*; + +/** +* A strategy game where all players have perfect information and no theme +* or narrative around gameplay. +*/ +public abstract class AbstractStrategyGame { + /** + * Constructs and returns a String describing how to play the game. Should include + * any relevant details on how to interpret the game state as returned by toString(), + * how to make moves, the game end condition, and how to win. + */ + public abstract String instructions(); + + /** + * Constructs and returns a String representation of the current game state. + * This representation should contain all information that should be known to + * players at any point in the game, including board state (if any) and scores (if any). + */ + public abstract String toString(); + + /** + * Returns true if the game has ended, and false otherwise. + */ + public boolean isGameOver() { + return getWinner() != -1; + } + + /** + * Returns the index of the player who has won the game, + * or -1 if the game is not over. + */ + public abstract int getWinner(); + + /** + * Returns the index of the player who will take the next turn. + * If the game is over, returns -1. + */ + public abstract int getNextPlayer(); + + /** + * Takes input from the parameter to specify the move the player + * with the next turn wishes to make, then executes that move. + * If any part of the move is illegal, throws an IllegalArgumentException. + */ + public abstract void makeMove(Scanner input); +} diff --git a/c1/Client.java b/c1/Client.java new file mode 100644 index 0000000..3bde980 --- /dev/null +++ b/c1/Client.java @@ -0,0 +1,36 @@ +import java.util.*; + +public class Client { + public static void main(String[] args) { + Scanner console = new Scanner(System.in); + AbstractStrategyGame game = new ConnectFour("dude", "bro"); + + System.out.println(game.instructions()); + System.out.println(); + + while (!game.isGameOver()) { + System.out.println(game); + System.out.printf("Player %d's turn.\n", game.getNextPlayer()); + try { + game.makeMove(console); + } catch (IllegalArgumentException ex) { + System.out.println("**Illegal move: " + ex.getMessage()); + } + /** + * Note - the above structure is a try/catch, which is something + * we've included to help deal with the IllegalArgumentExceptions + * in your abstract strategy game! + * We want to remind you that try/catch is a forbidden feature in 123, + * so you SHOULD NOT INCLUDE IT in any code you submit (other than this file)! + * Please see the Code Quality Guide for more info on this. + */ + } + System.out.println(game); + int winner = game.getWinner(); + if (winner > 0) { + System.out.printf("Player %d wins!\n", winner); + } else { + System.out.println("It's a tie!"); + } + } +} diff --git a/c1/ConnectFour.java b/c1/ConnectFour.java new file mode 100644 index 0000000..ab3e33b --- /dev/null +++ b/c1/ConnectFour.java @@ -0,0 +1,117 @@ +// Nik Johnson +// 4-15-2024 +// TA: Zachary Bi + +import java.util.*; + +public class ConnectFour extends AbstractStrategyGame { + + private char[][] board; + private int currentPlayer; + private String[] players; + private List<Character> playerSymbols; + + public ConnectFour(String player1, String player2) { + this.board = new char[6][7]; + this.players = new String[2]; + this.playerSymbols = new ArrayList<>(); + this.currentPlayer = 0; + players[0] = player1; + players[1] = player2; + playerSymbols.add('X'); + playerSymbols.add('O'); + + // initialize empty board + for (int i = 0; i < board.length; i++) { + for (int n = 0; n < board[i].length; n++) { + board[i][n] = ' '; + } + } + } + + public String instructions() { + String output = ""; + + output += "Welcome to Connect Four! This game consists of a 7x6 grid, each column of"; + output += " which you may choose to 'drop' a disc down by inputting numbers 1-7.\n"; + output += "Player 1's symbol is 'X', Player 2's is 'O'\n"; + output += "To win, you must place 4 of your discs in a row, vertically or horizontally.\n"; + output += "Have fun!"; + + return output; + } + + // constructs game board in readable format + // no parameters + // returns formatted representation of game board + public String toString() { + String output = ""; + for (char[] row : board) { + for (char loc : row) { + output += "[" + loc + "]"; + } + output += "\n"; + } + return output; + } + + + public int getWinner() { + // rows + for (int i = 0; i < board.length; i++) { + for (int j = 0; j <= board[i].length - 4; j++) { + char symbol = board[i][j]; + if (symbol != ' ' && board[i][j + 1] == symbol && board[i][j + 2] == symbol && board[i][j + 3] == symbol) { + return playerSymbols.indexOf(symbol) + 1; + } + } + } + + // columns + for (int j = 0; j < board[0].length; j++) { + for (int i = 0; i <= board.length - 4; i++) { + char symbol = board[i][j]; + if (symbol != ' ' && board[i + 1][j] == symbol && board[i + 2][j] == symbol && board[i + 3][j] == symbol) { + return playerSymbols.indexOf(symbol) + 1; + } + } + } + + return -1; +} + + + + + public int getNextPlayer() { + return this.currentPlayer + 1; + } + + public void makeMove(Scanner input) { + System.out.println(players[getNextPlayer() - 1] + ": Choose a column (1-7)"); + int col = Integer.parseInt(input.next()) - 1; + + if (col < 0 || col > 6) { + throw new IllegalArgumentException("selected column out of bounds"); + } + + boolean notPlaced = true; + for (int n = board.length - 1; n >= 0 && notPlaced; n--) { + if (board[n][col] == ' ') { + if (currentPlayer == 0) { + board[n][col] = 'X'; + } else if (currentPlayer == 1) { + board[n][col] = 'O'; + } + notPlaced = false; + } + } + + if (currentPlayer == 0) { + currentPlayer = 1; + } else if (currentPlayer == 1) { + currentPlayer = 0; + } + } + +} diff --git a/c1/Testing.java b/c1/Testing.java new file mode 100644 index 0000000..34837d7 --- /dev/null +++ b/c1/Testing.java @@ -0,0 +1,53 @@ +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; +import java.util.*; + +public class Testing { + @Test + @DisplayName("EXAMPLE TEST CASE - Small TicTacToe Example") + public void firstCaseTest() { + AbstractStrategyGame g = new TicTacToe(); + + // You can add optional error messages that will be displayed if a test fails + assertEquals(1, g.getNextPlayer(), "Player 1 not next player after construction"); + assertEquals(-1, g.getWinner(), "Winner incorrectly declared after construction"); + assertFalse(g.isGameOver(), "Game over immediately after construction"); + + // Weird way we're going to make moves - make our own scanners NOT + // connected to System.in. Since we can make Scanners over strings this will + // work the exact way and allow us to control input! + g.makeMove(new Scanner("0 0")); + assertEquals(2, g.getNextPlayer(), "Player 2 not next player after a single move"); + assertEquals(-1, g.getWinner(), "Winner incorrectly declared after a single move"); + assertFalse(g.isGameOver(), "Game over immediately after construction"); + + assertThrows(IllegalArgumentException.class, () -> { + // -1 is an illegal move so our code should throw an IllegalArgumentException + g.makeMove(new Scanner("-1 2")); + }, "IllegalArgumentException not thrown for illegal move"); + } + + @Test + @DisplayName("EXAMPLE TEST CASE - Large TicTacToe Example") + public void secondCaseTest() { + // You definitely don't have to get this fancy in your tests! + AbstractStrategyGame g = new TicTacToe(); + + // Going to play a whole game where 1 plays in first row, 2 plays in second row + // No optional error messages - up to you if you want your code to be easier to debug! + for (int i = 0; i < 5; i++) { + int player = (i % 2) + 1; + assertEquals(player, g.getNextPlayer()); + assertFalse(g.isGameOver()); + + int col = i / 2; + g.makeMove(new Scanner(player + " " + col)); + } + + // At this point, 5 moves have been played, player 1 should have three in a row and + // player 2 should have two + assertTrue(g.isGameOver()); + assertEquals(1, g.getWinner()); + assertEquals(-1, g.getNextPlayer()); + } +} diff --git a/c1/TicTacToe.java b/c1/TicTacToe.java new file mode 100644 index 0000000..dd2343a --- /dev/null +++ b/c1/TicTacToe.java @@ -0,0 +1,132 @@ +// **THIS IS AN EXAMPLE IMPLEMENTATION!** +// Brett Wortzman +// CSE 123 +// C0: Abstract Strategy Games +// +// A class to represent a game of tic-tac-toe that implements the +// AbstractStrategyGame interface. +import java.util.*; + +public class TicTacToe extends AbstractStrategyGame { + private char[][] board; + private boolean isXTurn; + + // Constructs a new TicTacToe game. + public TicTacToe() { + board = new char[][]{{'-', '-', '-'}, + {'-', '-', '-'}, + {'-', '-', '-'}}; + isXTurn = true; + } + + // Returns whether or not the game is over. + public boolean isGameOver() { + return getWinner() >= 0; + } + + // Returns the index of the winner of the game. + // 1 if player 1 (X), 2 if player 2 (O), 0 if a tie occurred, + // and -1 if the game is not over. + public int getWinner() { + for (int i = 0; i < board.length; i++) { + // check row i + if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != '-') { + return board[i][0] == 'X' ? 1 : 2; + } + + // check col i + if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != '-') { + return board[0][i] == 'X' ? 1 : 2; + } + } + + // check diagonals + if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != '-') { + return board[0][0] == 'X' ? 1 : 2; + } + if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != '-') { + return board[0][2] == 'X' ? 1 : 2; + } + + // check for tie + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + if (board[i][j] == '-') { + // unfilled space; game not over + return -1; + } + } + } + + // it's a tie! + return 0; + } + + // Returns the index of which player's turn it is. + // 1 if player 1 (X), 2 if player 2 (O), -1 if the game is over + public int getNextPlayer() { + if (isGameOver()) { + return -1; + } + return isXTurn ? 1 : 2; + } + + // Given the input, places an X or an O where + // the player specifies. + // Throws an IllegalArgumentException if the position is + // invalid, whether that be out of bounds or already occupied. + // Board bounds are [0, 2] for both rows and cols. + public void makeMove(Scanner input) { + char currPlayer = isXTurn ? 'X' : 'O'; + + System.out.print("Row? "); + int row = input.nextInt(); + System.out.print("Column? "); + int col = input.nextInt(); + + makeMove(row, col, currPlayer); + isXTurn = !isXTurn; + } + + // Private helper method for makeMove. + // Given a row and col, as well as player index, + // places an X or an O in that row and col. + // Throws an IllegalArgumentException if the position is + // invalid, whether that be out of bounds or already occupied. + // Board bounds are [0, 2] for both rows and cols. + private void makeMove(int row, int col, char player) { + if (row < 0 || row >= board.length || + col < 0 || col >= board[0].length) { + throw new IllegalArgumentException("Invalid board position: " + row + "," + col); + } + + if (board[row][col] != '-') { + throw new IllegalArgumentException("Space already occupied: " + row + "," + col); + } + + board[row][col] = player; + } + + // Returns a String containing instructions to play the game. + public String instructions() { + String result = ""; + result += "Player 1 is X and goes first. Choose where to play by entering a row and\n"; + result += "column number, where (0, 0) is the upper left and (2, 2) is the lower right.\n"; + result += "Spaces show as a - are empty. The game ends when one player marks three spaces\n"; + result += "in a row, in which case that player wins, or when the board is full, in which\n"; + result += "case the game ends in a tie."; + return result; + } + + // Returns a String representation of the current state of the board. + public String toString() { + String result = ""; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board.length; j++) { + result += board[i][j] + " "; + } + result += "\n"; + } + return result; + } +} |
