I am looking for some feedback regarding my Java Tic-Tac-Toe implementation.
Player.java
class Player {
private String coordinate;
private String name;
public Player(String coordinate, String name ) {
this.coordinate = coordinate;
this.name = name;
}
public String getCoordinate() {
return this.coordinate;
}
public String getName() {
return this.name;
}
}
TicTacToe.java
import java.util.Scanner;
import java.util.Set;
import java.util.HashSet;
import java.util.Hashtable;
public class TicTacToe {
public static Hashtable<String,String> coordinates = new Hashtable<String, String>() {
{
put("X", "notselected");
put("Y", "notselected");
}
};
public static Scanner input = new Scanner(System.in);
public static String[][] matrix = new String[][]{
{ "O", "O", "O" },
{ "O", "O", "O"},
{ "O", "O", "O" },
};
public static void printMatrix() {
System.out.println("This is how the matrix looks like at the moment:\n");
for(int i = 0 ; i < matrix.length; i ++ ) {
for(int j = 0; j < matrix.length; j++ ) {
System.out.print(matrix[i][j] + " " );
}
System.out.println();
}
System.out.println();
}
public static Player getPlayerInfo() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Player name:");
String playerName = input.next();
if ( coordinates.get("X").equals("selected")) {
System.out.println("Y is the only coordinate being left.");
return new Player("Y", playerName);
} else if ( coordinates.get("Y").equals("selected")) {
System.out.println("X is the only coordinate being left.");
return new Player("X", playerName);
} else {
System.out.println("Enter your coordinate:");
String coordinate = input.next();
coordinates.put(coordinate, "selected");
return new Player(coordinate, playerName);
}
}
public static boolean hasAPlayerWon(Player player ) {
if (isRowSolved(player.getCoordinate())) {
System.out.println("Row is solved");
return true;
} else if (isColumnSolved(player.getCoordinate())) {
System.out.println("Column is solved");
return true;
} else if (isLeftDiagonalSolved(player.getCoordinate())) {
System.out.println("Diagonal is solved");
return true;
} else if (isRightDiagonalSolved(player.getCoordinate())) {
System.out.println("Right diagona is solved");
return true;
}
return false;
}
private static boolean isRowSolved(String coordinate ) {
for(int i = 0; i < matrix.length; i ++) {
Set<String> elements = new HashSet<String>();
for(int j = 0; j < matrix[i].length; j++) {
elements.add(matrix[i][j]);
}
if (elements.contains(coordinate) && elements.size() == 1) {
return true;
}
}
return false;
}
private static boolean isColumnSolved(String coordinate) {
for(int i = 0; i < matrix.length; i ++) {
Set<String> elements = new HashSet<String>();
for(int j = 0; j < matrix[i].length; j++) {
elements.add(matrix[j][i]);
}
if (elements.contains(coordinate) && elements.size() == 1) {
return true;
}
}
return false;
}
private static boolean isLeftDiagonalSolved(String coordinate) {
int leftDiagonalIndex = 0;
HashSet<String> leftDiagonalElements = new HashSet<String>();
while(leftDiagonalIndex < matrix.length) {
leftDiagonalElements.add(matrix[leftDiagonalIndex][leftDiagonalIndex]);
leftDiagonalIndex += 1;
}
if(leftDiagonalElements.size() == 1 && matrix[leftDiagonalIndex-1][leftDiagonalIndex-1].equals(coordinate)) {
return true;
}
return false;
}
private static boolean isRightDiagonalSolved(String coordinate) {
int rightDiagonalIndexRow = 0;
int rightDiagonalIndexColumn = 2;
HashSet<String> rightDiagonalElements = new HashSet<String>();
while(rightDiagonalIndexRow < matrix.length) {
rightDiagonalElements.add(matrix[rightDiagonalIndexRow][rightDiagonalIndexColumn]);
rightDiagonalIndexRow += 1;
rightDiagonalIndexColumn -= 1;
}
if(rightDiagonalElements.size() == 1 && matrix[rightDiagonalIndexRow-1][rightDiagonalIndexColumn+1].equals(coordinate)) {
return true;
}
return false;
}
public static boolean isMatrixUnsolvable() {
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[0].length; j++) {
if(matrix[i][j].equals("O")) {
return false;
}
}
}
return true;
}
public static void evaluateInput(Player player) throws InterruptedException {
Thread.sleep(1000);
System.out.println(player.getName() + " enter your indexes, seperated by comma:");
String[] indexes = input.next().split(",");
int row = Integer.valueOf(indexes[0]);
int column = Integer.valueOf(indexes[1]);
if(!matrix[row][column].equals("O")) {
System.out.println("Sorry, you are missing your turn.");
System.out.println("Either you already marked the square or you are tring to override a value");
return;
}
matrix[row][column] = player.getCoordinate();
if(hasAPlayerWon(player )) {
System.out.println("Congratulations " + player.getName() + ", you WON!");
System.exit(0);
}
if(isMatrixUnsolvable( )) {
System.out.println("Matrix can't be resolved! Thank you for playing");
System.exit(0);
}
printMatrix();
}
public static void main(String[] args) throws InterruptedException {
printMatrix();
Player firstPlayer = getPlayerInfo();
Player secondPlayer = getPlayerInfo();
while (true) {
evaluateInput(firstPlayer);
evaluateInput(secondPlayer);
}
}
}
My concerns are:
When using
Thread.sleep()
I prefer to declare the exception in the method declaration, because a try/catch is never going to fail. I mean I have simply 1 thread. Is this ok?I know that a recommended SOLID principle for a method is to do a single task. Basically I was thinking to implement a method called
checkIfRowOrColumnIsSolved
, so that I do not do 2 separate methods. Is this a good name for a method? Does it not imply that the method is actually doing 2 things, instead of 1?I'm looking for feedback how the game could be written in smaller chunks and with better logic. I am not looking for any Java 1.8/1.9 abstraction improvements at this point.