I am trying to create a simple, local tic tac toe game for some practice in android studio so just for fun I thought I'd add a button that allows the user to undo the last move played, all the way back to before the first move if they wanted.
In order to do this, I have a 2 dimensional array that stores the current state of the board (i.e where each "x" or "o" is with respect to the actual board) that is updated every time someone plays. I am also creating a second arraylist that stores 2 dimensional arrays in order to keep track of the state of the board for every single round. So the first element of the arraylist should contain a 2 dimensional array that corresponds to the state of the board when no player has played, the second element should be after one user has played once and so on.
So what I wanted to happen is that when the user plays, before the state of the board is updated, it should copy the current state of the board into the arraylist containing all states (at the index of the current round) and then the board should be updated as well as the round count. Therefore when the user clicks the undo button, it should revert the board state back to what it was before the board was most recently updated.
My problem is : that it seems like the elements in my arraylist are not being added properly. All elements inside it contain the array to the most recent board state for some reason and I cannot for the life of me figure out why.
For example, if the game just started and I was to click any button on the grid, and then click the undo button immediately after and print the arraylist contents at the 0th index, I should be getting the default values of each grid (i.e. a blank board) but instead I am getting what the board looks like after I have played which is not intended. At no point am I putting that board state in the 0th index as far as I can see.
I show a sample output below where I have played only once and clicked the undo button once. The first set of numbers are being printed from the onclick of the grid button and the second set from the undo button. Numbers 0-9 represent the grid positions and any "X" or "O" represents where a player has played. I should technically be getting 0-9 printed for both sets of numbers since I've only played once, but I am instead getting an "X" where I played for the second set.
Here is the sample output:
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: From button on grid:
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: 0
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: 1
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: 2
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: 3
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: 4
2020-02-03 09:12:14.411 14808-14808/com.example.tictactoe I/Anton: 5
2020-02-03 09:12:14.412 14808-14808/com.example.tictactoe I/Anton: 6
2020-02-03 09:12:14.412 14808-14808/com.example.tictactoe I/Anton: 7
2020-02-03 09:12:14.412 14808-14808/com.example.tictactoe I/Anton: 8
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: From undo button:
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: X
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 1
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 2
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 3
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 4
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 5
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 6
2020-02-03 09:12:16.509 14808-14808/com.example.tictactoe I/Anton: 7
2020-02-03 09:12:16.510 14808-14808/com.example.tictactoe I/Anton: 8
Any help would be greatly appreciated. My code and xml is below:
package com.example.tictactoe;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.tictactoe.Utility.TicTacAlertDialog;
import java.util.ArrayList;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private boolean player1Turn = true;
private int roundCount = 1;
private int player1Points = 0;
private int player2Points = 0;
private static String[][] board;
private ArrayList<String [][]> allBoards;
private int testing = 0;
private TextView player1TextView;
private TextView player2TextView;
AlertDialog.Builder gameOver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
player1TextView = findViewById(R.id.player1);
player2TextView = findViewById(R.id.player2);
gameOver = new AlertDialog.Builder(this);
board = new String[3][3];
allBoards = new ArrayList<String[][]>();
boardSet();
allBoards.add(0,board);
}
//function defines behavior of when a user plays, i.e. clicks any button on the grid
public void playerPlayed(View view) {
Button button = (Button) view;
String userSelect = button.getText().toString();
//adding current board to arraylist containing all board states per round. Using roundcount as index.
allBoards.add(roundCount,board);
Log.i("Anton", "From button on grid: ");
//debugging code to see content of arraylist
for(int a = 0; a <3; a++){
for(int b = 0; b<3;b++){
Log.i("Anton", "\n" + allBoards.get(0)[a][b]);
}
}
//getting i,j index of what button was just clicked on the grid
int i = Character.getNumericValue(button.getTag().toString().charAt(4));
int j = Character.getNumericValue(button.getTag().toString().charAt(5));
//Printing of X or O on buttons depending on button clicked by user
if (userSelect.equals("") && player1Turn) {
button.setText("X");
roundCount++;
} else if (userSelect.equals("") && !player1Turn) {
button.setText("O");
roundCount++;
}
//copying board most recently played button into board state array
board[i][j] = button.getText().toString();
Boolean whoWon = winner();
//checking for winner to display dialog box
if (player1Turn == true && whoWon == true) {
player1TextView.setText("Player 1: " + ++player1Points);
whoWon = false;
boardSet();
//Alerts
new TicTacAlertDialog(this, "Helo", "you won");
gameOver.setMessage("Game over, Player 1 Won");
gameOver.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
gameWinReset();
boardSet();
}
});
AlertDialog gameEnded = gameOver.create();
gameEnded.show();
} else if (player1Turn == false && whoWon == true) {
player2TextView.setText("Player 2: " + ++player2Points);
whoWon = false;
boardSet();
gameOver.setMessage("Game over, Player 2 Won");
gameOver.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
gameWinReset();
boardSet();
}
});
AlertDialog gameEnded = gameOver.create();
gameEnded.show();
} else if (roundCount == 9) {
whoWon = false;
boardSet();
gameOver.setMessage("Game over, it is a draw.");
gameOver.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
gameWinReset();
boardSet();
}
});
AlertDialog gameEnded = gameOver.create();
gameEnded.show();
}
player1Turn = !player1Turn;
}
//function to determine winner of the game
public boolean winner() {
//columns
if (board[0][0].equals(board[1][0]) && board[0][0].equals(board[2][0])) {
return true;
}
if (board[0][1].equals(board[1][1]) && board[0][1].equals(board[2][1])) {
return true;
}
if (board[0][2].equals(board[1][2]) && board[0][2].equals(board[2][2])) {
return true;
}
//rows
if (board[0][0].equals(board[0][1]) && board[0][0].equals(board[0][2])) {
return true;
}
if (board[1][0].equals(board[1][1]) && board[1][0].equals(board[1][2])) {
return true;
}
if (board[2][0].equals(board[2][1]) && board[2][0].equals(board[2][2])) {
return true;
}
//diagonals
if (board[0][0].equals(board[1][1]) && board[0][0].equals(board[2][2])) {
return true;
}
if (board[0][2].equals(board[1][1]) && board[0][2].equals(board[2][0])) {
return true;
}
return false;
}
//reset board button behavior
public void resetBoard(View view) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Button b = (Button) findViewById(getResources().getIdentifier("grid" + i + j, "id", getPackageName()));
b.setText("");
}
}
boardSet();
}
//function to reset the board state array
public void boardSet() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = Integer.toString(testing);
testing++;
}
}
roundCount = 0;
testing = 0;
}
//function to set board to initial blank state after game has ended or been drawn
public void gameWinReset() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Button b = (Button) findViewById(getResources().getIdentifier("grid" + i + j, "id", getPackageName()));
b.setText("");
roundCount = 0;
}
}
boardSet();
}
//undo button behavior
public void undoLast(View view) {
Button eachButton;
Resources res = getResources();
//debugging code for weird arraylist behavior
Log.i("Anton", "From undo button: ");
for(int i = 0; i<3;i++){
for(int j = 0; j<3; j++){
Log.i("Anton", "\n\n" + allBoards.get(0)[i][j]);
}
}
//Resetting board to all blank provided undo button is clicked when round count =1
if(roundCount ==1)
{
//set entire grid to blank
for(int i = 0; i <3; i++)
{
for(int j = 0; j<3;j++)
{
int id = res.getIdentifier("grid" + i + j, "id", getBaseContext().getPackageName());
eachButton = findViewById(id);
eachButton.setText("");
}
}
}
else{
for(int i = 0; i <3; i++)
{
for(int j = 0; j<3;j++)
{
int id = res.getIdentifier("grid" + i + j, "id", getBaseContext().getPackageName());
eachButton = findViewById(id);
String cellValue = allBoards.get(roundCount-2)[i][j];
if(cellValue.equals("X") || cellValue.equals("O"))
{
eachButton.setText(cellValue);
}else{
eachButton.setText("");
}
}
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="horizontal"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="2"
>
<TextView
android:id="@+id/player1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Player 1: 0"
android:textSize="16dp"
android:fontFamily="serif"
/>
<TextView
android:id="@+id/player2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Player 2: 0"
android:textSize="16dp"
android:fontFamily="serif"/>
</LinearLayout>
<Button
android:id="@+id/undo"
android:onClick="undoLast"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/reset_button_drawable"
/>
<Button
android:id="@+id/reset"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@drawable/reset_button_drawable"
android:onClick="resetBoard"
android:text="Reset"
android:textAllCaps="false"
android:textColor="#fff"
android:textSize="20dp"
android:layout_weight="3"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="1">
<Button
android:id="@+id/grid00"
android:tag="grid00"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="playerPlayed"
android:textSize="100dp"
android:textColor="#fff"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
/>
<Button
android:id="@+id/grid01"
android:tag="grid01"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="playerPlayed"
android:text=""
android:textSize="100dp"
android:textColor="#fff"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
/>
<Button
android:id="@+id/grid02"
android:tag="grid02"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:onClick="playerPlayed"
android:text=""
android:textSize="100dp"
android:textColor="#fff"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginBottom="5dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="1">
<Button
android:id="@+id/grid10"
android:tag="grid10"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="100dp"
android:textColor="#fff"
android:onClick="playerPlayed"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
/>
<Button
android:id="@+id/grid11"
android:tag="grid11"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="100dp"
android:textColor="#fff"
android:onClick="playerPlayed"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
/>
<Button
android:id="@+id/grid12"
android:tag="grid12"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="100dp"
android:textColor="#fff"
android:onClick="playerPlayed"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
android:layout_marginRight="5dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="1">
<Button
android:id="@+id/grid20"
android:tag="grid20"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="100dp"
android:textColor="#fff"
android:onClick="playerPlayed"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
/>
<Button
android:id="@+id/grid21"
android:tag="grid21"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="100dp"
android:textColor="#fff"
android:onClick="playerPlayed"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
/>
<Button
android:id="@+id/grid22"
android:tag="grid22"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="100dp"
android:textColor="#fff"
android:onClick="playerPlayed"
android:background="@drawable/button_looks"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
android:layout_marginRight="5dp"
/>
</LinearLayout>
</LinearLayout>