下面的方法evalBoard()接受brd,并将其参数复制到pos。然后迭代一些可能的值,通过调用makeMove()对pos进行更改。这些对pos.makeMove的调用也以某种方式改变了brd中的值。通过我在evalBoard中标记的区域可以清楚地看到它。为什么会这样?
public Move evalBoard(Board brd) {
// evaluates current board and returns point of best move
Move best;
Move tmp = new Move(-1, -1, LOSE, brd.getPlayer());
if (brd.getPlayer() == PLAYER1) {
best = new Move(-1, -1, LOSE, PLAYER1);
} else {
best = new Move(-1, -1, WIN, PLAYER2);
}
// check if board is empty if so return optimal move
//
if (brd.isEmpty()) {
return (new Move(0, 0, 0, PLAYER1));
}
// iterate through possible moves
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
if (brd.getSpace(x, y) == ' ') {
// copy all of brd to pos
pos=new Board(brd.getBoard(),brd.getPlayer());
tmp.setX(x);
tmp.setY(y);
tmp.setPlayer(brd.getPlayer());
/*
* problem can be seen by stepping through the next line. brd is changed at the same time.
*/
pos.makeMove(tmp);//Problem is here this call is changing brd as well
// evaluate for immediate win or loss
if (brd.getPlayer() == PLAYER1) {
if (pos.win()) {// immediate win
tmp.setValue(WIN);
return tmp;
} else { // not a winning move check it's recursive
// value
pos.setPlayer(PLAYER2);
tmp.copyFrom(evalBoard(pos));
if (tmp.getValue() >= best.getValue()) {
best.copyFrom(tmp);
}
}
} else {
if (pos.win()) {// immediate loss
tmp.setValue(LOSE);
return tmp;
} else {// not a losing move check it's recursive value
pos.setPlayer(PLAYER1);
tmp.copyFrom(evalBoard(pos));
if (tmp.getValue() <= best.getValue()) {
best.copyFrom(tmp);
}
}
}
}
}
}
return best;
}
这是董事会成员:
public class Board {
final boolean PLAYER1=true;
final boolean PLAYER2=false;
public char[][] board = new char[3][3];
public boolean Turn;
public Board(char[][] brd,boolean plr1){
board=brd;
Turn=plr1;
}
public char getPlayerChar(){
if (Turn==PLAYER1){
return 'X';
}else{
return 'O';
}
}
public char[][] getBoard(){
return board;
}
public void setBoard(char[][] brd){
board=brd;
}
public void displayBoard(){
for(int y=0;y<3;y++){
for(int x=0;x<3;x++){
System.out.print(board[x][y]);
if (x<2){
System.out.print(" | ");
}
}
System.out.println("");
if (y<2){
System.out.println("----------");
}
}
}
public void makeMove(Move mv){
//System.out.println("hit");
if (mv.getPlayer()==PLAYER1){
board[mv.getX()][mv.getY()]='X';
}else{
board[mv.getX()][mv.getY()]='O';
}
}
public void setPlayer(boolean plr){
Turn=plr;
}
public boolean getPlayer(){
return Turn;
}
public char getSpace(int x,int y){
return board[x][y];
}
public boolean isEmpty(){
for(int x=0;x<3;x++){
for(int y=0;y<3;y++){
if(!(board[x][y]==' ')){
return false;
}
}
}
return true;
}
public boolean win(){
char plrChar;
//returns true if board is winning position for current player
if (Turn==PLAYER1){
plrChar='X';
}else {
plrChar='O';
}
//Across
for(int y=0;y<3;y++){
if (board[0][y]+board[1][y]+board[2][y]==(plrChar+plrChar+plrChar)){
return true;
}
}
//Up/Down
for(int x=0;x<3;x++){
if (board[x][0]+board[x][1]+board[x][2]==(plrChar+plrChar+plrChar)){
return true;
}
}
//Diagonals
//top left to bottom right
if (board[0][0]+board[1][1]+board[2][2]==(plrChar+plrChar+plrChar)){
return true;
}
//bottom left to top right
if (board[0][2]+board[1][1]+board[2][0]==(plrChar+plrChar+plrChar)){
return true;
}
return false;
}
}
答案 0 :(得分:2)
问题是当你这样做时:
pos=new Board(brd.getBoard(),brd.getPlayer());
您正在使用原始pos
(board
)创建一个新的董事会(Board
)分享其brd
数组。这就是Board
构造函数在这里所做的事情:
public Board(char[][] brd,boolean plr1){
board=brd;
...
当然,由于只有一个数组,当您通过一个Board
进行更新时,更改会通过另一个Board
显示。
如果您不希望旧的和新的Board
实例共享相同的char数组,那么您不应该这样分配。相反,您应该使用嵌套循环将状态从brd
复制到board
。
这是“漏洞抽象”的一个例子。 getBoard
和setBoard
方法允许Board
外部的代码以相当意外的方式破坏Board
实例的状态。这些方法的非泄漏版本和Board
构造函数将复制数组的内容(可能是一个新数组),而不是允许Board
数组实例在抽象边界之外可访问