如何在resetBoard()完成后使用回调来调用computerMove()?基本上我试图使用处理程序的延迟,但我的代码中有一个循环,我相信这是因为computerMove()
在董事会重置时发生。
以下代码:
@Override
public void onClick(View v) {
if (!((Button) v).getText().toString().equals("")) {
return;
}
turnsCount++;
if (playerOneMove) {
((Button) v).setText("X");
((Button) v).setTextColor(playerX);
isGameOver();
}
}
public void isGameOver() {
if (checkGameIsWon()) {
if (playerOneMove) {
player1Wins();
} else {
player2Wins();
}
} else if (turnsCount == 9) {
draw();
} else {
playerOneMove = !playerOneMove;
if (!playerOneMove) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
computerMove();
}
}, random.nextInt(2000 - 1000 + 1000) + 1000);
}
}
}
private void computerMove() {
String[][] field = new String[3][3];
List<Button> emptyButtons = new ArrayList<>();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
field[i][j] = buttons[i][j].getText().toString();
if (field[i][j].equals("")) {
emptyButtons.add(buttons[i][j]);
}
}
}
selectButton = emptyButtons.get(random.nextInt(emptyButtons.size()));
selectButton.setText("O");
selectButton.setTextColor(playerO);
firstComputerMove = false;
turnsCount++;
isGameOver();
}
private void player1Wins() {
playerOnePoints++;
Toast.makeText(this, "Player 1 wins!", Toast.LENGTH_SHORT).show();
updatePointsText();
resetBoard();
}
private void resetBoard() {
final Handler handlerReset = new Handler();
handlerReset.postDelayed(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
buttons[i][j].setText("");
}
}
}
},2000);
}
private boolean checkGameIsWon() {
String[][] field = new String[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
field[i][j] = buttons[i][j].getText().toString();
}
}
for (int i = 0; i < 3; i++) {
if (field[i][0].equals(field[i][1])
&& field[i][0].equals(field[i][2])
&& !field[i][0].equals("")) {
return true;
}
}
for (int i = 0; i < 3; i++) {
if (field[0][i].equals(field[1][i])
&& field[0][i].equals(field[2][i])
&& !field[0][i].equals("")) {
return true;
}
}
if (field[0][0].equals(field[1][1])
&& field[0][0].equals(field[2][2])
&& !field[0][0].equals("")) {
return true;
}
if (field[0][2].equals(field[1][1])
&& field[0][2].equals(field[2][0])
&& !field[0][2].equals("")) {
return true;
}
return false;
}
private void player1Wins() {
playerOnePoints++;
Toast.makeText(this, "Player 1 wins!", Toast.LENGTH_SHORT).show();
updatePointsText();
resetBoard();
}
private void player2Wins() {
playerTwoPoints++;
Toast.makeText(this, "Computer wins!", Toast.LENGTH_SHORT).show();
updatePointsText();
resetBoard();
firstComputerMove = true;
computerMove();
}
private void draw() {
Toast.makeText(this, "Draw!", Toast.LENGTH_SHORT).show();
resetBoard();
playerOneMove = !playerOneMove;
switchPlayerTurn();
if (!playerOneMove){
firstComputerMove = true;
computerMove();
}
}
错误追踪:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mima.tictactoe, PID: 10915
java.lang.IllegalArgumentException: n <= 0: 0
at java.util.Random.nextInt(Random.java:182)
at com.mima.tictactoe.MainActivityPlayer1.computerMove(MainActivityPlayer1.java:163)
at com.mima.tictactoe.MainActivityPlayer1.player2Wins(MainActivityPlayer1.java:340)
at com.mima.tictactoe.MainActivityPlayer1.isGameOver(MainActivityPlayer1.java:129)
at com.mima.tictactoe.MainActivityPlayer1.computerMove(MainActivityPlayer1.java:283)
at com.mima.tictactoe.MainActivityPlayer1.player2Wins(MainActivityPlayer1.java:340)
at com.mima.tictactoe.MainActivityPlayer1.isGameOver(MainActivityPlayer1.java:129)
at com.mima.tictactoe.MainActivityPlayer1.computerMove(MainActivityPlayer1.java:283)
at com.mima.tictactoe.MainActivityPlayer1.player2Wins(MainActivityPlayer1.java:340)
at com.mima.tictactoe.MainActivityPlayer1.isGameOver(MainActivityPlayer1.java:129)
at com.mima.tictactoe.MainActivityPlayer1.computerMove(MainActivityPlayer1.java:283)
at com.mima.tictactoe.MainActivityPlayer1.player2Wins(MainActivityPlayer1.java:340)
at com.mima.tictactoe.MainActivityPlayer1.isGameOver(MainActivityPlayer1.java:129)
at com.mima.tictactoe.MainActivityPlayer1.computerMove(MainActivityPlayer1.java:283)
at com.mima.tictactoe.MainActivityPlayer1.access$200(MainActivityPlayer1.java:20)
at com.mima.tictactoe.MainActivityPlayer1$3.run(MainActivityPlayer1.java:140)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6934)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
答案 0 :(得分:1)
三件事:
Runnable
方法中执行resetBoard()
,该方法在与computerMove()
方法不同的线程中运行该方法的内容;这就是为什么这两个方法同时运行的原因。你的resetBoard()
逻辑是否需要在自己的线程内?你可以摆脱线程,让这个逻辑正常运行,这可以缓解你的问题,除非你某些你需要在这里使用一个单独的线程。computerMove()
和resetBoard()
逻辑的副作用; java.lang.IllegalArgumentException: n <= 0: 0 at java.util.Random.nextInt(Random.java:182)
被抛出是因为您在第182行传递0到随机数函数emptyButtons.get(random.nextInt(emptyButtons.size()));
。条件if (field[i][j].equals(""))
未通过,可能是因为您的resetBoard()
方法没有在computerMove()
被调用时已完成,因此field[i][j]
的值尚未设置为""
。或许更好的方法是在Thread
方法中使用Runnable
而不是resetBoard()
。如果您确定要使用单独的线程,则可以使用wait
和notify
功能。 Check out how to use wait and notify with Threads here
private Runnable resetBoard() {
return new Thread() {
@Override
public void run() {
synchronized(this){
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
buttons[i][j].setText("");
}
}
notify();
}
}
}
}
private void computerMove(Thread resetBoard) {
resetBoard.start();
synchronized(resetBoard){
try{
System.out.println("Resetting board...");
resetBoard.wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("Board is reset.");
// ...
// existing computerMove() logic here
// ...
}
}
public void draw() {
Toast.makeText(this, "Draw!", Toast.LENGTH_SHORT).show();
// resetBoard(); -- don't call here
playerOneMove = !playerOneMove;
switchPlayerTurn();
if (!playerOneMove){
firstComputerMove = true;
// pass the result of resetBoard (new thread) here
computerMove(resetBoard());
}
}
为了将来参考,在执行其他逻辑之前,简单地尝试使用时间延迟来“等待”另一个线程的逻辑继续完成,这绝不是一个好主意。有很多原因可以解释为什么你永远不知道在完成之前需要花多长时间,因此你不应该只是“猜测”并使用类似2000毫秒(2秒)的东西。了解Threads如何在Java中工作,并了解为什么以及何时应该使用Thread生命周期钩子,例如wait
和notify
。