java - 数组似乎正在更新自己
问题描述
我正在尝试创建一个简单的本地井字游戏,以便在 android studio 中进行一些练习,所以只是为了好玩,我想我会添加一个按钮,允许用户撤消上一次播放的动作,一直回到第一次之前如果他们愿意,可以移动。
为了做到这一点,我有一个二维数组来存储棋盘的当前状态(即每个“x”或“o”相对于实际棋盘的位置),每次有人玩时都会更新。我还创建了第二个存储二维数组的数组列表,以便跟踪每一轮的棋盘状态。因此,arraylist 的第一个元素应该包含一个二维数组,该数组对应于没有玩家玩过时棋盘的状态,第二个元素应该是一个用户玩过一次后的状态,依此类推。
所以我想要发生的是,当用户玩游戏时,在更新棋盘状态之前,它应该将棋盘的当前状态复制到包含所有状态的数组列表中(在当前回合的索引处),然后是棋盘应该更新以及轮数。因此,当用户单击撤消按钮时,它应该将板状态恢复到板最近更新之前的状态。
我的问题是:我的数组列表中的元素似乎没有被正确添加。出于某种原因,其中的所有元素都包含最近板状态的数组,我一生都无法弄清楚为什么。
例如,如果游戏刚开始,我要单击网格上的任何按钮,然后立即单击撤消按钮并在第 0 个索引处打印 arraylist 内容,我应该得到每个网格的默认值(即空白板)但相反,我在玩过之后得到了板的外观,这不是预期的。据我所知,我绝不会将那个董事会状态放在第 0 个索引中。
我在下面显示了一个示例输出,其中我只播放了一次并单击了一次撤消按钮。第一组数字是通过单击网格按钮打印的,第二组数字是从撤消按钮打印的。数字 0-9 代表网格位置,任何“X”或“O”代表玩家玩过的位置。从技术上讲,我应该为两组号码打印 0-9,因为我只玩过一次,但我在第二组玩的地方得到了一个“X”。
这是示例输出:
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
任何帮助将不胜感激。我的代码和xml如下:
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>
解决方案
您需要board
在添加时克隆allBoards
- 您当前正在保存对一个棋盘状态的引用(在每次移动时),并且每次移动都会改变,因此所有条目allBoards
似乎都会改变 - 这条线(和类似的) 是你的问题:
allBoards.add(0,board);
因此,无论您在哪里allBoards.add(slot,board);
替换为cloneBoard(slot)
在第二个片段中找到的调用...(请注意对深度克隆方法的引用)。
例子...
前
public class Main
{
private static String[][] board;
private static ArrayList<String[][]> allBoards = new ArrayList<>();
public static void main(String[] args) {
board = new String[3][3];
setBoard("A");
allBoards.add(0,board);
setBoard("B");
allBoards.add(1,board);
dumpAllBoards();
}
private static void dumpAllBoards() {
int bCnt = 0;
for (String[][] b : allBoards) {
System.out.print(bCnt+": ");
for (int i = 0; i < 9; i++) {
System.out.print(b[i/3][i%3]+" ");
}
System.out.println();
bCnt++;
}
}
private static void setBoard(String v) {
for (int i = 0; i < 9; i++) {
board[i/3][i%3] = v;
}
}
}
印刷:
0: B B B B B B B B B
1: B B B B B B B B B
后
public class Main
{
private static String[][] board;
private static ArrayList<String[][]> allBoards = new ArrayList<>();
public static void main(String[] args) {
board = new String[3][3];
setBoard("A");
cloneBoard(0);
setBoard("B");
cloneBoard(1);
dumpAllBoards();
}
// omitted dumpAllBoards - see previous
private static void cloneBoard(int slot) {
allBoards.add(slot,deepCopyStrMatrix(board));
}
// copied from: https://stackoverflow.com/a/9106176/2711811
public static String[][] deepCopyStrMatrix(String[][] input) {
if (input == null)
return null;
String[][] result = new String[input.length][];
for (int r = 0; r < input.length; r++) {
result[r] = input[r].clone();
}
return result;
}
}
印刷:
0: A A A A A A A A A
1: B B B B B B B B B
推荐阅读
- swift - SF Symbols 分层、调色板和多色渲染模式颜色?
- python - 用边际图绘制 Python Plotly ECDF 子图
- linq - Cannot compose correct Linq query for a class having collection properties - getting EfCore5 translation errors
- php - 如何提高 mysql COUNT 查询的速度?
- android - Android: is it possible to mirror app screen on car screen?
- regex - 希望结合到 Google Sheets ArrayFormula 中的 REGEXMATCH 查询 - 如果包含 X 并且不包含 Y
- c# - 添加函数作为对象的属性
- linux - 从 shell 脚本将 VS Code 调试器附加到 C# 进程
- java - 如何在 Tomcat 服务器上编写 Jar 文件,以启动主类并获取参数。并响应繁重的客户端应用程序
- javascript - 使用 jquery 获取附加的 href 链接