\$\begingroup\$
\$\endgroup\$
Right now I'm studying Java, and as a part of understanding arrays, I've implemented a Tic-Tac-Toe game based on two-dimensional array.
Any suggestions/comments are much appreciated!
Source code will be below, also available in GitHub repo.
Main.java
package TicTacToe;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
int x = -1;
int y = -1;
System.out.print("Enter name for player No" + Player.playersCount + ": ");
Player player1 = new Player(reader.readLine(), "X");
System.out.print("Enter name for player No" + Player.playersCount + ": ");
Player player2 = new Player(reader.readLine(), "O");
TicTacToe game1 = new TicTacToe(3);
game1.initializeGame(player1, player2);
while (true) {
try {
if (x < 0) {
System.out.print("Enter x: ");
x = Integer.parseInt(reader.readLine());
}
if (y < 0) {
System.out.print("Enter y: ");
y = Integer.parseInt(reader.readLine());
}
} catch (NumberFormatException e) {
System.out.println("Your input is incorrect, value must Integer!");
continue;
}
if (!game1.checkInput(x, y)) continue;
if (!game1.turnAllowed(x, y)) { x = -1; y = -1; continue; }
TicTacToe.turnNumber++;
if (TicTacToe.turnNumber % 2 != 0) {
if (game1.makeATurn(x, y, player1)) {
System.out.println(player1.name + " wins!");
break;
}
} else {
if (game1.makeATurn(x, y, player2)) {
System.out.println(player2.name + " wins!");
break;
}
}
x = -1;
y = -1;
}
}
}
TicTacToe.java
package TicTacToe;
import org.jetbrains.annotations.NotNull;
public class TicTacToe {
static int turnNumber = 0;
String[][] gamingBoard;
public TicTacToe(int n) {
gamingBoard = new String[n][n];
}
void initializeGame(Player player1, Player player2) {
for (int i = 0; i < gamingBoard.length; i++) {
for (int j = 0; j < gamingBoard.length; j++) {
gamingBoard[i][j] = "-";
}
}
printGameBoard();
}
void printGameBoard() {
for (int i = 0; i < gamingBoard.length; i++) {
for (int j = 0; j < gamingBoard.length; j++) {
System.out.print(gamingBoard[i][j]);
}
System.out.println();
}
}
boolean turnAllowed(int x, int y) {
if (gamingBoard[x][y].equals("-")) {
return true;
} else {
System.out.println("Choose another field!");
return false;
}
}
boolean checkInput(int x, int y) {
try {
if (x <= gamingBoard.length - 1 && y <= gamingBoard.length - 1) {
return true;
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: " + e);
}
System.out.println("Your input is incorrect, values must be between 0 and 2");
return false;
}
boolean makeATurn(int x, int y, @NotNull Player player) {
gamingBoard[x][y] = player.playingMark;
printGameBoard();
boolean winner = false;
for (int i = 0; i < gamingBoard[i].length; i++) {
if (!gamingBoard[x][i].equals(player.playingMark))
break;
if (i == gamingBoard.length - 1) {
winner = true;
}
}
for (int i = 0; i < gamingBoard.length; i++) {
if (!gamingBoard[i][y].equals(player.playingMark))
break;
if (i == gamingBoard.length - 1) {
winner = true;
}
}
if (x == y) {
for (int i = 0; i < gamingBoard.length; i++) {
if (!gamingBoard[i][i].equals(player.playingMark))
break;
if (i == gamingBoard.length - 1) {
winner = true;
}
}
}
if (x + y == gamingBoard.length - 1) {
for (int i = 0; i < gamingBoard.length; i++) {
if (!gamingBoard[i][(gamingBoard.length - 1) - i].equals(player.playingMark))
break;
if (i == gamingBoard.length - 1) {
winner = true;
}
}
}
return winner;
}
}
Player.java
package TicTacToe;
public class Player {
String name;
String playingMark;
static int playersCount = 0;
public Player(String name, String playingMark) {
playersCount++;
this.name = name;
this.playingMark = playingMark;
}
}
Thank you!
Denis BurtsevDenis Burtsev
asked Apr 14, 2020 at 12:59
1 Answer 1
\$\begingroup\$
\$\endgroup\$
2
Naming & Readability
Choosen naming mostly conveys purpose (expressive) and follows conventions (camelCase).
Design & Structure
Very good design for a learner:
TicTacToe
represents a game instance currently playing- having a
board
(composition) recording current board state - initializes game (registering 2 players, resetting board)
- checks for allowed moves (target field on board and not occupied)
- executes a player's turn and evaluates if turning player is a winner (horizontal, vertical, if indicated both diagonals in a row)
- having a
Player
represents one player (of minimum required 2), including his mark
Improvements:
- apply information hiding: public getters and setters allow access and modification of private member variables (take advantage of Lombok)
- UI invades gaming-logic! What about a separate
TicTacToeUI
class that is responsible for input (player name, moves) and output (render board, show winner), probably validation (check input). This concept is called Separation of Concerns (similarly also applied as MVC). Beneficial for a later extension, e.g from text-based input (TUI) towards graphical (GUI) or web-based using a REST interface. - Encapsulate round-based iteration inside the game class. So
main
is just responsible for initiating and starting the game (additionally stopping, if an exit-handler may be implemented allowing a long game to be aborted). That is what the Single Responsibility Principal (SRP) suggests. - split long methods like
makeATurn
: onemoveTo(player, x,y)
anotherboolean hasWon(player, x, y)
which callsevaluateHorizontal
, etc. (SRP) in order to check the rows separately.
Stability & Technical Improvement
ArrayIndexOutOfBoundsException
is not thrown incheckInput
. Although it does not check for negative index values (which have a special meaning for the game-loop!)- magic numbers like
-1
should be defined as constant to express purpose in their name - although a class-variable like
static int playerCount
is possible (Beware: counts players of all game instances!), what's the purpose? turnNumber
should not be static, but initialized within constructor
answered Apr 14, 2020 at 15:35
-
\$\begingroup\$ Thank you so much for detailed answer! I'll continue to improve my code following your recommendations. \$\endgroup\$Denis Burtsev– Denis Burtsev2020年04月14日 16:14:14 +00:00Commented Apr 14, 2020 at 16:14
-
1\$\begingroup\$ Continue marks a favourable attitude! I will also improve my answer, if I have refinements to add. \$\endgroup\$hc_dev– hc_dev2020年04月14日 16:18:30 +00:00Commented Apr 14, 2020 at 16:18
lang-java