I'm trying to make a Tic-Tac-Toe Android application, of course in Java. After debugging for maybe a few hours, fixing things here and there, I have come upon a problem that I can not solve by myself at this time, with this knowledge. I have deduced that the problem is found in the AndroidPerform() method where at the first line it is asked to find the best move and place it in the appropriately named variable bestMove, which is an object Move.
public void AndroidPerform(){
Move bestMove = AndroidMove(NOUGHT);
placeAMove(bestMove.x, bestMove.y, NOUGHT);
minimaxActivity.setMove(bestMove.x, bestMove.y, NOUGHT);
}
Now, clearly, by looking at the code above we can see that the method AndroidMove(player) is called which is basically a minimax algorithm, or at least it should be. While debugging I've seen that it does find good moves, but when it comes to this line
Move bestMove = AndroidMove(NOUGHT);
It returns null for x and for y and here is where I get my problem which I can't fix. I've provided the two most important classes, actually it is in these classes where all of the work is done. Anyway, hope you can help, how ever it may be, thank you in advance.
MyGame Class
public class MyGame {
// Name-constants to represent the seeds and cell contents
public final int EMPTY = 0;
public final int CROSS = 1;
public final int NOUGHT = 2;
// Name-constants to represent the various states of the game
public final int PLAYING = 0;
public final int CROSS_WON = 1;
public final int NOUGHT_WON = 2;
public final int DRAW = 3;
// The game board and the game status
public static final int ROWS = 3, COLS = 3; // number of rows and columns
public static int[][] board = new int[ROWS][COLS]; // game board in 2D array
// containing (EMPTY, CROSS, NOUGHT)
public static int currentState; // the current state of the game
// (PLAYING, DRAW, CROSS_WON, NOUGHT_WON)
public static int currentPlayer; // the current player (CROSS or NOUGHT)
public static int currentRow, currentCol; // current seed's row and column
public int AndroidPlayer, HumanPlayer;
MinimaxActivity minimaxActivity = new MinimaxActivity();
class Move {
int x, y, score, player;
public Move(int score){
this.score = score;
}
public Move(int x, int y) {
this.x = x;
this.y = y;
}
public Move(int x, int y, int player) {
this.x = x;
this.y = y;
this.player = player;
}
}
public void resetBoard() {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
board[i][j] = 0;
}
}
}
List<Move> availableMoves;
public Move AndroidMove(int player) {
// Computer is always NOUGHT
// Base case
int State = CheckGameState();
if (State == NOUGHT_WON){
return new Move(10);
} else if (State == CROSS_WON){
return new Move(-10);
} else if (State == DRAW){
return new Move(0);
}
List<Move> moves = getAvailableStates();
//if (moves.isEmpty()) return new Move(0);
for (int i = 0; i < ROWS; ++i) {
for (int j = 0; j < COLS; ++j) {
if (board[i][j] == EMPTY){
Move move = new Move(i, j, player);
placeAMove(i, j, player);
if (player == NOUGHT){
move.score = AndroidMove(CROSS).score;
} else {
move.score = AndroidMove(NOUGHT).score;
}
moves.add(move);
placeAMove(i, j, EMPTY);
}
}
}
int bestMove = 0;
if (player == NOUGHT) {
int bestScore = -1000000;
for (int i = 0; i < moves.size(); i++) {
if (moves.get(i).score > bestScore) {
bestMove = i;
bestScore = moves.get(i).score;
}
}
} else {
int bestScore = 1000000;
for (int i = 0; i < moves.size(); i++) {
if (moves.get(i).score < bestScore) {
bestMove = i;
bestScore = moves.get(i).score;
}
}
}
return moves.get(bestMove);
}
public void AndroidPerform(){
Move bestMove = AndroidMove(NOUGHT);
placeAMove(bestMove.x, bestMove.y, NOUGHT);
minimaxActivity.setMove(bestMove.x, bestMove.y, NOUGHT);
}
public void placeAMove(int x, int y, int player) {
board[x][y] = player; //player = 1 for X, 2 for O
}
public void placeAMove(Point point, int player) {
board[point.x][point.y] = player; //player = 1 for X, 2 for O
}
public List<Move> getAvailableStates() {
availableMoves = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (board[i][j] == EMPTY) {
availableMoves.add(new Move(i, j));
}
}
}
return availableMoves;
}
public int CheckGameState() {
/*
0 - Playing
1 - X Won
2 - O Won
3 - Draw
*/
// Check Rows - Horizontal Lines
for (int i = 0; i< ROWS; i++){
if (board[i][0] == CROSS &&
board[i][1] == CROSS &&
board[i][2] == CROSS){
return CROSS_WON;
}
if (board[i][0] == NOUGHT &&
board[i][1] == NOUGHT &&
board[i][2] == NOUGHT){
return NOUGHT_WON;
}
}
// Check Columns - Vertical Lines
for (int i = 0; i< COLS; i++){
if (board[0][i] == CROSS &&
board[1][i] == CROSS &&
board[2][i] == CROSS){
return CROSS_WON;
}
if (board[0][i] == NOUGHT &&
board[1][i] == NOUGHT &&
board[2][i] == NOUGHT){
return NOUGHT_WON;
}
}
// Check Diagonal
if (board[0][0] == CROSS &&
board[1][1] == CROSS &&
board[2][2] == CROSS){
return CROSS_WON;
}
if (board[0][0] == NOUGHT &&
board[1][1] == NOUGHT &&
board[2][2] == NOUGHT){
return NOUGHT_WON;
}
// Check Reverse-Diagonal
if (board[0][2] == CROSS &&
board[1][1] == CROSS &&
board[2][0] == CROSS){
return CROSS_WON;
}
if (board[0][2] == NOUGHT &&
board[1][1] == NOUGHT &&
board[2][0] == NOUGHT){
return NOUGHT_WON;
}
// Check for Tie
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] != CROSS && board[i][j] != NOUGHT){
return PLAYING;
}
}
}
return DRAW;
}
}
MinimaxActivity Class
public class MinimaxActivity extends AppCompatActivity {
private Board BoardGame;
private MyGame myGame;
private Button mBoardButtons[][];
private TextView mInfoTextView;
private TextView mPlayerOneCount;
private TextView mTieCount;
private TextView mPlayerTwoCount;
private TextView mPlayerOneText;
private TextView mPlayerTwoText;
private int mPlayerOneCounter = 0;
private int mTieCounter = 0;
private int mPlayerTwoCounter = 0;
private Button newGame, exitGame;
public int HUMAN = 1;
public int COMPUTER = 2;
Random random;
private int First=0;
private int Counter = 0;
private boolean isGameOver = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
mBoardButtons = new Button[3][3];
mBoardButtons[0][0] = (Button) findViewById(R.id.one);
mBoardButtons[0][1] = (Button) findViewById(R.id.two);
mBoardButtons[0][2] = (Button) findViewById(R.id.three);
mBoardButtons[1][0] = (Button) findViewById(R.id.four);
mBoardButtons[1][1] = (Button) findViewById(R.id.five);
mBoardButtons[1][2] = (Button) findViewById(R.id.six);
mBoardButtons[2][0] = (Button) findViewById(R.id.seven);
mBoardButtons[2][1] = (Button) findViewById(R.id.eight);
mBoardButtons[2][2] = (Button) findViewById(R.id.nine);
newGame = (Button) findViewById(R.id.newGame1);
exitGame = (Button) findViewById(R.id.exitGame1);
// Text Fields
mInfoTextView = (TextView) findViewById(R.id.information);
mPlayerOneCount = (TextView) findViewById(R.id.humanCount);
mTieCount = (TextView) findViewById(R.id.tiesCount);
mPlayerTwoCount = (TextView) findViewById(R.id.androidCount);
mPlayerOneText = (TextView) findViewById(R.id.human);
mPlayerTwoText = (TextView) findViewById(R.id.android);
// Counters
mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter));
mTieCount.setText(Integer.toString(mTieCounter));
mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter));
random = new Random();
exitGame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MinimaxActivity.this.finish();
}
});
final CharSequence[] items = {"Computer", "Player"};
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.setCancelable(false);
alertDialog.setTitle("Who goes first?");
alertDialog.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
if (items[item] == "Computer") {
First = 1; // Computer
} else if (items[item] == "Player") {
First = 2; // Player
}
dialog.dismiss();
//BoardGame = new Board();
myGame = new MyGame();
if (First == 1) {
startNewGame(true); // True For Computer
}
if (First == 2) {
startNewGame(false); // False For Player
}
}
});
alertDialog.show();
newGame.setOnClickListener(new View.OnClickListener() { // FIX STARTNEWGAME
@Override
public void onClick(View v) {
if (Counter % 2 == 0) {
startNewGame(false);
Counter++;
} else {
startNewGame(true);
Counter++;
}
// Here we stop, counter can be used for session concept
}
});
// http://developer.android.com/guide/topics/ui/dialogs.html
// Adding a persistent multiple-choice or single-choice list
}
private void startNewGame(boolean GoesFirst) {
MyResetBoard(); // Look at board reset
mPlayerOneText.setText("Human:");
mPlayerTwoText.setText("Android:");
if(GoesFirst){
// Computer Goes First
mInfoTextView.setText("Android's Turn.");
//myGame.AndroidPerform();
setMove(random.nextInt(3), random.nextInt(3), COMPUTER);
GoesFirst = false;
}else{
//Player Goes First
mInfoTextView.setText("Human's Turn.");
GoesFirst = true;
}
isGameOver = false;
}
private void MyResetBoard(){
myGame.resetBoard();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
mBoardButtons[i][j].setText("");
mBoardButtons[i][j].setEnabled(true);
mBoardButtons[i][j].setOnClickListener(new ButtonClickListener(i,j));
mBoardButtons[i][j].setBackgroundResource(R.drawable.empty);
}
}
}
private class ButtonClickListener implements View.OnClickListener {
int x,y;
public ButtonClickListener(int i, int j) {
this.x = i;
this.y = j;
}
@Override
public void onClick(View v) {
if (!isGameOver){ // If the game is not over
if (mBoardButtons[x][y].isEnabled()){
setMove(x, y, myGame.CROSS); // Human makes a move
int winner = myGame.CheckGameState();
if (winner == myGame.PLAYING) { // If still playing
mInfoTextView.setText(R.string.turn_computer);
myGame.AndroidPerform();
//int move = mGame.getComputerMove();
//setMove(TicTacToeGame.PLAYER_TWO, move);
winner = myGame.CheckGameState();
}
if (winner == myGame.PLAYING){
mInfoTextView.setText(R.string.turn_human);
}
else if (winner == myGame.DRAW) { // If draw
mInfoTextView.setText(R.string.result_tie);
mTieCounter++;
mTieCount.setText(Integer.toString(mTieCounter));
isGameOver = true;
} else if (winner == myGame.CROSS_WON) { // X Won
mInfoTextView.setText(R.string.result_human_wins);
mPlayerOneCounter++;
mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter));
isGameOver = true;
} else if (winner == myGame.NOUGHT_WON){ // O Won
mInfoTextView.setText(R.string.result_android_wins);
mPlayerTwoCounter++;
mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter));
isGameOver = true;
}
}
}
}
}
public void setMove(int x, int y, int player){
myGame.placeAMove(x, y, player);
mBoardButtons[x][y].setEnabled(false);
if (player == 1) {
mBoardButtons[x][y].setBackgroundResource(R.drawable.x);
} else {
mBoardButtons[x][y].setBackgroundResource(R.drawable.o);
}
}
Working Code, changes made in both of the two classes.
MinimaxActivity Class
public class MinimaxActivity extends AppCompatActivity {
private Board BoardGame;
private MyGame myGame;
private Button mBoardButtons[][];
private TextView mInfoTextView;
private TextView mPlayerOneCount;
private TextView mTieCount;
private TextView mPlayerTwoCount;
private TextView mPlayerOneText;
private TextView mPlayerTwoText;
private int mPlayerOneCounter = 0;
private int mTieCounter = 0;
private int mPlayerTwoCounter = 0;
private Button newGame, exitGame;
public int HUMAN = 1;
public int COMPUTER = 2;
Random random;
private int First=0;
private int Counter = 0;
private boolean isGameOver = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play);
mBoardButtons = new Button[3][3];
mBoardButtons[0][0] = (Button) findViewById(R.id.one);
mBoardButtons[0][1] = (Button) findViewById(R.id.two);
mBoardButtons[0][2] = (Button) findViewById(R.id.three);
mBoardButtons[1][0] = (Button) findViewById(R.id.four);
mBoardButtons[1][1] = (Button) findViewById(R.id.five);
mBoardButtons[1][2] = (Button) findViewById(R.id.six);
mBoardButtons[2][0] = (Button) findViewById(R.id.seven);
mBoardButtons[2][1] = (Button) findViewById(R.id.eight);
mBoardButtons[2][2] = (Button) findViewById(R.id.nine);
newGame = (Button) findViewById(R.id.newGame1);
exitGame = (Button) findViewById(R.id.exitGame1);
// Text Fields
mInfoTextView = (TextView) findViewById(R.id.information);
mPlayerOneCount = (TextView) findViewById(R.id.humanCount);
mTieCount = (TextView) findViewById(R.id.tiesCount);
mPlayerTwoCount = (TextView) findViewById(R.id.androidCount);
mPlayerOneText = (TextView) findViewById(R.id.human);
mPlayerTwoText = (TextView) findViewById(R.id.android);
// Counters
mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter));
mTieCount.setText(Integer.toString(mTieCounter));
mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter));
random = new Random();
exitGame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MinimaxActivity.this.finish();
}
});
final CharSequence[] items = {"Computer", "Player"};
final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
alertDialog.setCancelable(false);
alertDialog.setTitle("Who goes first?");
alertDialog.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
if (items[item] == "Computer") {
First = 1; // Computer
} else if (items[item] == "Player") {
First = 2; // Player
}
dialog.dismiss();
myGame = new MyGame(MinimaxActivity.this);
if (First == 1) {
startNewGame(true); // True For Computer
}
if (First == 2) {
startNewGame(false); // False For Player
}
}
});
alertDialog.show();
newGame.setOnClickListener(new View.OnClickListener() { // FIX STARTNEWGAME
@Override
public void onClick(View v) {
if (Counter % 2 == 0) {
startNewGame(false);
Counter++;
} else {
startNewGame(true);
Counter++;
}
}
});
}
private void startNewGame(boolean GoesFirst) {
MyResetBoard(); // Look at board reset
mPlayerOneText.setText("Human:");
mPlayerTwoText.setText("Android:");
if(GoesFirst){
// Computer Goes First
mInfoTextView.setText("Android's Turn.");
setMove(random.nextInt(3), random.nextInt(3), COMPUTER);
GoesFirst = false;
}else{
//Player Goes First
mInfoTextView.setText("Human's Turn.");
GoesFirst = true;
}
isGameOver = false;
}
private void MyResetBoard(){
myGame.resetBoard();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
mBoardButtons[i][j].setText("");
mBoardButtons[i][j].setEnabled(true);
mBoardButtons[i][j].setOnClickListener(new ButtonClickListener(i,j));
mBoardButtons[i][j].setBackgroundResource(R.drawable.empty);
}
}
}
private class ButtonClickListener implements View.OnClickListener {
int x,y;
public ButtonClickListener(int i, int j) {
this.x = i;
this.y = j;
}
@Override
public void onClick(View v) {
if (!isGameOver){ // If the game is not over
if (mBoardButtons[x][y].isEnabled()){
setMove(x, y, HUMAN); // Human makes a move CROSS
int winner = myGame.CheckGameState();
if (winner == myGame.PLAYING) { // If still playing
mInfoTextView.setText(R.string.turn_computer);
int[] result = myGame.move();
setMove(result[0], result[1], COMPUTER);
winner = myGame.CheckGameState();
}
winner = myGame.CheckGameState();
if (winner == myGame.PLAYING){
mInfoTextView.setText(R.string.turn_human);
}
else if (winner == myGame.DRAW) { // If draw
mInfoTextView.setText(R.string.result_tie);
mTieCounter++;
mTieCount.setText(Integer.toString(mTieCounter));
isGameOver = true;
} else if (winner == myGame.CROSS_WON) { // X Won
mInfoTextView.setText(R.string.result_human_wins);
mPlayerOneCounter++;
mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter));
isGameOver = true;
} else if (winner == myGame.NOUGHT_WON){ // O Won
mInfoTextView.setText(R.string.result_android_wins);
mPlayerTwoCounter++;
mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter));
isGameOver = true;
}
}
}
}
}
public void setMove(int x, int y, int player){
myGame.placeAMove(x, y, player);
mBoardButtons[x][y].setEnabled(false);
if (player == 1) {
mBoardButtons[x][y].setBackgroundResource(R.drawable.x);
} else {
mBoardButtons[x][y].setBackgroundResource(R.drawable.o);
}
}
}
MyGame Class
public class MyGame {
// Name-constants to represent the seeds and cell contents
public final int EMPTY = 0;
public final int CROSS = 1;
public final int NOUGHT = 2;
// Name-constants to represent the various states of the game
public final int PLAYING = 0;
public final int CROSS_WON = 1;
public final int NOUGHT_WON = 2;
public final int DRAW = 3;
// The game board and the game status
public static final int ROWS = 3, COLS = 3; // number of rows and columns
public static int[][] board = new int[ROWS][COLS]; // game board in 2D array
// containing (EMPTY, CROSS, NOUGHT)
public static int currentState; // the current state of the game
// (PLAYING, DRAW, CROSS_WON, NOUGHT_WON)
public static int currentPlayer; // the current player (CROSS or NOUGHT)
public static int currentRow, currentCol; // current seed's row and column
MinimaxActivity minimaxActivity;
public MyGame(MinimaxActivity minimaxActivity) {
this.minimaxActivity = minimaxActivity;
}
public void resetBoard() {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
board[i][j] = 0;
}
}
}
/** Get next best move for computer. Return int[2] of {row, col} */
public int[] move() {
int[] result = minimax(2, NOUGHT); // depth, max turn
return new int[] {result[1], result[2]}; // row, col
}
public int[] minimax(int depth, int player){
// Generate possible next moves in a List of int[2] of {row, col}.
List<int[]> nextMoves = generateMoves();
// mySeed(NOUGHT) is maximizing; while oppSeed(CROSS) is minimizing
int bestScore = (player == NOUGHT) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
int currentScore;
int bestRow = -1;
int bestCol = -1;
if (nextMoves.isEmpty() || depth == 0){
bestScore = evaluate();
} else {
for (int[] move : nextMoves){
board[move[0]][move[1]] = player;
if (player == NOUGHT) { // NOUGHT is Maximizing Player
currentScore = minimax(depth - 1, CROSS)[0];
if (currentScore > bestScore) {
bestScore = currentScore;
bestRow = move[0];
bestCol = move[1];
}
} else { // CROSS is Minimizing Player
currentScore = minimax(depth - 1, NOUGHT)[0];
if (currentScore < bestScore) {
bestScore = currentScore;
bestRow = move[0];
bestCol = move[1];
}
}
// Undo move
board[move[0]][move[1]] = EMPTY;
}
}
return new int[] {bestScore, bestRow, bestCol};
}
private int evaluate() {
int score = 0;
// Evaluate score for each of the 8 lines (3 rows, 3 columns, 2 diagonals)
score += evaluateLine(0, 0, 0, 1, 0, 2); // row 0
score += evaluateLine(1, 0, 1, 1, 1, 2); // row 1
score += evaluateLine(2, 0, 2, 1, 2, 2); // row 2
score += evaluateLine(0, 0, 1, 0, 2, 0); // col 0
score += evaluateLine(0, 1, 1, 1, 2, 1); // col 1
score += evaluateLine(0, 2, 1, 2, 2, 2); // col 2
score += evaluateLine(0, 0, 1, 1, 2, 2); // diagonal
score += evaluateLine(0, 2, 1, 1, 2, 0); // alternate diagonal
return score;
}
/** The heuristic evaluation function for the given line of 3 cells
@Return +100, +10, +1 for 3-, 2-, 1-in-a-line for computer.
-100, -10, -1 for 3-, 2-, 1-in-a-line for opponent.
0 otherwise */
private int evaluateLine(int row1, int col1, int row2, int col2, int row3, int col3) {
int score = 0;
// First cell
if (board[row1][col1] == NOUGHT) {
score = 1;
} else if (board[row1][col1] == CROSS) {
score = -1;
}
// Second cell
if (board[row2][col2] == NOUGHT) {
if (score == 1) { // cell1 is mySeed
score = 10;
} else if (score == -1) { // cell1 is oppSeed
return 0;
} else { // cell1 is empty
score = 1;
}
} else if (board[row2][col2] == CROSS) {
if (score == -1) { // cell1 is oppSeed
score = -10;
} else if (score == 1) { // cell1 is mySeed
return 0;
} else { // cell1 is empty
score = -1;
}
}
// Third cell
if (board[row3][col3] == NOUGHT) {
if (score > 0) { // cell1 and/or cell2 is mySeed
score *= 10;
} else if (score < 0) { // cell1 and/or cell2 is oppSeed
return 0;
} else { // cell1 and cell2 are empty
score = 1;
}
} else if (board[row3][col3] == CROSS) {
if (score < 0) { // cell1 and/or cell2 is oppSeed
score *= 10;
} else if (score > 1) { // cell1 and/or cell2 is mySeed
return 0;
} else { // cell1 and cell2 are empty
score = -1;
}
}
return score;
}
private List<int[]> generateMoves() {
List<int[]> nextMoves = new ArrayList<int[]>(); // allocate List
int State = CheckGameState();
// If gameover, i.e., no next move
if (State == 1 || // X Won
State == 2 || // O Won
State == 3) // Draw
{
return nextMoves; // return empty list
}
// Search for empty cells and add to the List
for (int row = 0; row < ROWS; ++row) {
for (int col = 0; col < COLS; ++col) {
if (board[row][col] == EMPTY) {
nextMoves.add(new int[] {row, col});
}
}
}
return nextMoves;
}
public void placeAMove(int x, int y, int player) {
board[x][y] = player; //player = 1 for X, 2 for O
}
public int CheckGameState() {
/*
0 - Playing
1 - X Won
2 - O Won
3 - Draw
*/
// Check Rows - Horizontal Lines
for (int i = 0; i< ROWS; i++){
if (board[i][0] == CROSS &&
board[i][1] == CROSS &&
board[i][2] == CROSS){
return CROSS_WON;
}
if (board[i][0] == NOUGHT &&
board[i][1] == NOUGHT &&
board[i][2] == NOUGHT){
return NOUGHT_WON;
}
}
// Check Columns - Vertical Lines
for (int i = 0; i< COLS; i++){
if (board[0][i] == CROSS &&
board[1][i] == CROSS &&
board[2][i] == CROSS){
return CROSS_WON;
}
if (board[0][i] == NOUGHT &&
board[1][i] == NOUGHT &&
board[2][i] == NOUGHT){
return NOUGHT_WON;
}
}
// Check Diagonal
if (board[0][0] == CROSS &&
board[1][1] == CROSS &&
board[2][2] == CROSS){
return CROSS_WON;
}
if (board[0][0] == NOUGHT &&
board[1][1] == NOUGHT &&
board[2][2] == NOUGHT){
return NOUGHT_WON;
}
// Check Reverse-Diagonal
if (board[0][2] == CROSS &&
board[1][1] == CROSS &&
board[2][0] == CROSS){
return CROSS_WON;
}
if (board[0][2] == NOUGHT &&
board[1][1] == NOUGHT &&
board[2][0] == NOUGHT){
return NOUGHT_WON;
}
// Check for Tie
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] != CROSS && board[i][j] != NOUGHT){
return PLAYING;
}
}
}
return DRAW;
}
}