-1

以下の 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;
}


}
4

1 に答える 1

2

問題は、これを行うときです:

  pos=new Board(brd.getBoard(),brd.getPlayer());

元のボード( ) と配列を共有する新しいボード ( pos) を作成しています。それがコンストラクターがここで行っていることです:boardBoardbrdBoard

  public Board(char[][] brd,boolean plr1){
      board=brd;
      ...

当然、配列は 1 つしかないため、一方を介して更新するとBoard、変更は他方を介して表示されますBoard

Board古いインスタンスと新しいインスタンスで同じ char 配列を共有したくない場合は、そのように割り当てるべきではありません。代わりに、ネストされたループを使用して状態を から にコピーする必要がありbrdますboard


学ぶべき教訓

これは「漏れやすい抽象化」の例です。getBoardおよびメソッドを使用すると、 の外部のsetBoardコードがインスタンスBoardの状態を損傷する可能性がありBoardます ... かなり予期しない方法で。これらのメソッドの漏れのないバージョンとコンストラクターは、抽象境界の外で配列インスタンスにアクセスできるようにするのBoardではなく、配列の内容を (新しい配列にコピーすることもできます) コピーします。Board

于 2013-03-31T03:43:45.873 に答える