游戏板状态列表的输出显示所有状态相同

时间:2016-02-07 07:40:08

标签: java

我正在建立Tic Tac Toe游戏。我在逻辑中有3个类。

  • Board :定义所有与电路板相关的功能。
  • 游戏:它是使用所有不同类的主类。

我附上了该程序的代码段。

  1. getAllStates :此函数应返回可以浏览的所有可用状态/节点。
  2. 如果我通过Main(Game)Class ar = getAllStates(b,2);
  3. 中的函数调用传递了类板和播放器的对象
  4. 这意味着如果我传递像

    这样的Board实例

    000 000 000

  5. 这应该返回一个ArrayList,其中包含所有可能的状态:

    200
    000
    000
    

    020
    000
    000
    

    002
    000
    000
    

    依此类推

    000
    000
    002
    

    然而,什么时候!执行代码并检查存储在ArrayList中的值,我得到一个包含9个对象的ArrayList,每个对象都带有配置:

    000
    000
    000
    

    守则

        public class Game2 {            
    
            static int player1 = 1;
            static int player2 = 2;
    
            public static void main(String[] args) {            
    
                //playGame();
                ArrayList<Board> ar ;               
                Board b = new Board();
                Computer comp = new Computer();             
                ar = getAllStates(b,2);                     
                //System.out.println(comp.getConfigScore(b));               
            }     
    
            public static ArrayList<Board> getAllStates(Board b, int player)
            {               
                ArrayList<Board> arr = new ArrayList<Board>();
                for(int i = 0;i <3; i ++)
                {
                    for (int j=0;j<3;j++)
                    {
                        if(!b.isPosOccupied(i, j))
                        {        
                            int previousState = b.getVal(i, j);
                            b.setVal(i, j, player); 
                            b.display();                    
                            arr.add(b);
                            b.setVal(i, j, previousState);
                        }                       
                    }                   
                }
    
                System.out.println(arr.size());
                return arr;             
            }           
        }
    
    The Board Class is :
    
    public class Board {        
        // Defines Board Configuration
        static int[][] data = new int[3][3];        
        public Board()
        {           
            for(int i=0;i<data.length;i++ )
            {
                for(int j=0;j<data.length;j++ )
                {
                    data[i][j] = 0;
                }
            }
        }
    
        //Displays Current State of the board    
        public void display()
        {
            System.out.println("Board");            
            for(int i = 0; i< data.length;i++)
            {
                for(int j = 0; j< data.length;j++)
                {
                        System.out.print(data[i][j] + " ");
                }
                System.out.println();
            }
        }       
    
        // Gets the Value on a specific board configuration
        public int getVal(int i, int j)
        {
            return data[i][j];
        }
    
        //Sets the value to a particular board location
        public void setVal(int i, int j,int val)
        {
            data[i][j] = val;
    
        }       
    
        public boolean isBoardFull()
        {
            for(int i=0;i< data.length ; i++)
            {
                for(int j=0;j< data.length ;j++)
                {
                    if(data[i][j] == 0)
                        return false;
                }
            }       
            return true;
        }
    
        public boolean isVictoriousConfig(int player)
        {           
            //Noting down victory rules
            //Horizontal Victory
            if   ( (data[0][0] != 0) && ((data[0][0] == data [0][1]) && (data[0][1] == data [0][2]) && (data[0][2] == player)))
                return true;            
    
            if   ((data[1][0] != 0) && ((data[1][0] == data [1][1]) && (data[1][1] == data [1][2]) && (data[1][2] == player)))
                return true;            
    
            if   ((data[2][0] != 0) && ((data[2][0] == data [2][1]) && (data[2][1] == data [2][2]) && (data[2][2] == player)))
                return true;            
    
            //Vertical Victory          
            if   ( (data[0][0] != 0) && ((data[0][0] == data [1][0]) && (data[1][0] == data [2][0]) && (data[2][0] == player)))
                return true;        
    
            if   ((data[0][1] != 0) && ((data[0][1] == data [1][1]) && (data[1][1] == data [2][1]) && (data[2][1] == player)))
                return true;            
    
            if   ((data[0][2] != 0) && ((data[0][2] == data [1][2]) && (data[1][2] == data [2][2]) && (data[2][2] == player)))
                return true;
    
            //Diagonal Victory          
            if   ( (data[0][0] != 0) && ((data[0][0] == data [1][1]) && (data[1][1] == data [2][2]) && (data[2][2] == player)))
                return true;            
    
            if   ( (data[0][2] != 0) && ((data[0][2] == data [1][1]) && (data[1][1] == data [2][0]) && (data[2][0] == player)))
                return true;
    
            //If none of the victory rules are met. No one has won just yet ;)          
            return false;           
        }
    
        public boolean isPosOccupied(int i, int j)
        {
            if(data[i][j] != 0)
            {
                return true;
            }
            return false;           
        }       
    }
    

    由于我正在更新getAllStates()函数中的板配置以提供所有可能的板配置,我不明白为什么ArrayList()会显示这样的结果。

    我觉得我要么在某处覆盖这个数组列表,要么我没有使用预期的板配置更新数组列表。我真的很感激这里有任何想法。严重卡住了。提前谢谢。

    添加编辑:好的,所以如果我更改我的setter以回馈b的不同对象,它应该可以工作。那么这个修改是对的吗?

    public Board setVal(int i, int j,int val)
        {
            Board newboard = new Board();
            data[i][j] = val;
            newboard.data = data;
            return newboard;
    
        }
    

    在此之后,我会在 getAllStates()功能中添加类似的内容

    Board newboard = b.setVal(i, j, player);                    
    arr.add(newboard);
    

2 个答案:

答案 0 :(得分:2)

您必须克隆电路板,以便在存储以前的状态时反复更改它。否则你正在改变同一个对象。最终得到一个引用同一对象的列表。

您可以这样验证:

ar = getAllStates(b,2);
ar.get(0) == ar.get(1); // returns true

我会覆盖Board.clone()方法并像这样使用它:

arr.add(b.clone()); // replaced arr.add(b);

另一种选择是制作Board个对象immutable。这可以通过一旦变异(通常在setter中)创建另一个对象来完成。因此Board.setVal(...)会返回一个带有新设置值的电路板对象:

public Board setVal(...) {
    // ...
    // TODO: create a clone of the board named newBoard
    // ...
    return newBoard;
}

更改界面后,您可以执行以下操作:

// keeps old object that may still be referenced from changing
b = b.setVal(i, j, player);

注意:为了使对象在概念上不可变,可能需要使其内部对象也是不可变的。如果您使用内部数组表示Board,则需要通过永不更改它来使其变为不可变。

这可以在构造时通过传递数组并立即复制它而丢弃对外部数组的引用:

public Board(int[][] data) {
    this.data = Arrays.copyOf(data, data.length); // copy array
    // do not reference 'data'. Do not expose 'this.data'.
}

答案 1 :(得分:0)

你期望ArrayList在你添加它时拍摄你的电路板的快照,但这不是它的作用。

这样想:你有一块板,它位于X位置。你坐在你的板前面,然后在它上面做一个标记来表示可能的位置。然后你给ArrayList递上一张纸条,上面写着“X位置的纸板”。然后你擦除标记并再制作一个标记。然后你把ArrayList的另一张纸写在“位置X的板子”上。再重复几次。

最后你有一个ArrayList,里面装满了9张纸条,上面写着“X位置的纸板”,这是你一直在标记和重复擦除的同一块纸板。当您输出列表时,它将忠实地输出该单板9次,并且由于您擦除了其上的所有标记,因此每个打印输出都将为空。

要使其正常工作,您必须制作一个全新的电路板,并且每次都将 电路板的引用添加到列表中。