2
\$\begingroup\$

I had a Tic Tac Toe assignment for class and the program seems to work fine but I feel like the exception/input handling could be done in a much better way. Is this a good way to approach the structure of the app? Is it okay to use try/catch a lot like in here or is it better to try and avoid it? Really newbie question but I'd love your feedback, thanks!

public class TicTacToeApp {
 static Scanner in = new Scanner(System.in);
 public static void main(String[] args) {
 char[] gameBoard = new char[9];
 Arrays.fill(gameBoard, ' ');
 char playerOne = ' ';
 char playerTwo = ' ';
 System.out.println("Welcome to Tic-Tac-Toe!");
 try {
 playerOne = getPlayerOne(playerOne);
 playerTwo = getPlayerTwo(playerOne);
 System.out.println("Player one plays with: " + playerOne);
 System.out.println("Player two plays with: " + playerTwo);
 displayBoard(gameBoard);
 do {
 System.out.println(playerOne + "'s turn: choose a position from 1-9");
 getPosition(gameBoard, playerOne);
 displayBoard(gameBoard);
 if (gameOver(gameBoard, playerOne, playerTwo)) break;
 System.out.println(playerTwo + "'s turn: choose a position from 1-9");
 getPosition(gameBoard, playerTwo);
 displayBoard(gameBoard);
 } while (!gameOver(gameBoard, playerOne, playerTwo));
 } catch (IllegalArgumentException e) {
 System.out.println(e.getMessage());
 }
 }
 /** Displays the board in the console
 *
 * @param gameBoard The char[] used for the game
 */
 public static void displayBoard(char[] gameBoard) {
 System.out.println(gameBoard[0] + " | " + gameBoard[1] + " | " + gameBoard[2]);
 System.out.println("---------");
 System.out.println(gameBoard[3] + " | " + gameBoard[4] + " | " + gameBoard[5]);
 System.out.println("---------");
 System.out.println(gameBoard[6] + " | " + gameBoard[7] + " | " + gameBoard[8]);
 }
 /** Gets the mark choice for player one, 'X' or 'O'
 *
 * @param playerOne the player to be assigned with a mark
 * @return the mark that playerOne will play with
 */
 public static char getPlayerOne(char playerOne) {
 System.out.println("Player one please pick 'X' or 'O' ");
 while (playerOne != 'X' && playerOne != 'O') {
 playerOne = in.nextLine().toUpperCase().charAt(0);
 if (playerOne != 'X' && playerOne != 'O') {
 System.out.println("Please pick either X or O");
 }
 }
 return playerOne;
 }
 /** Assigns the remaining mark to player two
 *
 * @param playerOne the mark player one has chosen
 * @return the mark that remains for player two
 */
 public static char getPlayerTwo(char playerOne) {
 if (playerOne == 'X') return 'O';
 else return 'X';
 }
 /** Checks for victory conditions for either player
 *
 * @param gameBoard the char[] used for the game
 * @param player the player to check for
 * @return true if the player has won, false otherwise
 */
 public static boolean isVictory(char[] gameBoard, char player) {
 return ((gameBoard[0] == player) && (gameBoard[1] == player) && (gameBoard[2] == player)) ||
 ((gameBoard[3] == player) && (gameBoard[4] == player) && (gameBoard[5] == player)) ||
 ((gameBoard[6] == player) && (gameBoard[7] == player) && (gameBoard[8] == player)) ||
 ((gameBoard[0] == player) && (gameBoard[3] == player) && (gameBoard[6] == player)) ||
 ((gameBoard[1] == player) && (gameBoard[4] == player) && (gameBoard[7] == player)) ||
 ((gameBoard[2] == player) && (gameBoard[5] == player) && (gameBoard[8] == player)) ||
 ((gameBoard[0] == player) && (gameBoard[4] == player) && (gameBoard[8] == player)) ||
 ((gameBoard[2] == player) && (gameBoard[4] == player) && (gameBoard[6] == player));
 }
 /** Assigns a mark to a position on the board
 *
 * @param gameBoard the char[] used for the game
 * @param player the player whose turn it is to place a mark
 */
 public static void getPosition(char[] gameBoard, char player) {
 int position = 1;
 do {
 try {
 position = Integer.parseInt(in.nextLine());
 if (position < 1 || position > 9) {
 System.out.println("Please enter a number from 1-9");
 position = Integer.parseInt(in.nextLine());
 }
 if (gameBoard[position - 1] != ' ') {
 throw new IllegalArgumentException("Position is occupied. Please choose another position");
 } else {
 gameBoard[position - 1] = player;
 break;
 }
 } catch (IllegalArgumentException e) {
 System.out.println(e.getMessage());
 }
 } while (gameBoard[position - 1] != ' ');
 }
 /** Checks if the board is full
 *
 * @param gameBoard the char[] used for the game
 * @return true if the board is full, false otherwise
 */
 public static boolean fullBoard(char[] gameBoard) {
 for (char c : gameBoard) {
 if (c == ' ') {
 return false;
 }
 }
 return true;
 }
 /** Checks for end of game conditions (victory or draw)
 *
 * @param gameBoard the char[] used for the game
 * @param playerOne First player
 * @param playerTwo second player
 * @return True and a message if the game has ended, False otherwise
 */
 public static boolean gameOver(char[] gameBoard, char playerOne, char playerTwo) {
 boolean gameOver = false;
 if (isVictory(gameBoard, playerOne)) {
 System.out.println(playerOne + "'s win!");
 gameOver = true;
 } else if (isVictory(gameBoard, playerTwo)) {
 System.out.println(playerTwo + "'s win!");
 gameOver = true;
 } else if (fullBoard(gameBoard)) {
 System.out.println("Game ended in a draw");
 gameOver = true;
 }
 return gameOver;
 }
}
Toby Speight
87.1k14 gold badges104 silver badges322 bronze badges
asked Jun 4, 2023 at 18:45
\$\endgroup\$
1
  • \$\begingroup\$ For future reference, your post title should explain what your code does. \$\endgroup\$ Commented Jun 4, 2023 at 18:50

1 Answer 1

2
\$\begingroup\$

Overall, the structure of the app is reasonable. Methods are named clearly and do what the name suggests. It misses some of the benefits of object-oriented programming because all the pieces that make up the state of the game (like the game board) are declared in main and must be passed to each other method.* Additionally, methods should generally have the most restrictive viable access modifier; in this case, all the methods except main can be private instead of public.

Regarding exceptions, in my experience, there are usually better ways to handle errors in self-contained code like this app. In this case, the try/catch in getPosition makes sense as it catches both the NumberFormatException from Integer.parseInt and the IllegalArgumentException your code throws. However, the try/catch in main is unnecessary since getPosition already catches the exceptions that may be thrown.


*Full object-oriented programming generally requires more than 1 class in Java due to the static modifier of the main method.

answered Jun 10, 2023 at 5:25
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.