为什么这个代码给出两个不同的输出(似乎是)相同的输入? [英] Why does this code give two different outputs for (what appears to be) the same inputs?

查看:182
本文介绍了为什么这个代码给出两个不同的输出(似乎是)相同的输入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为一个棋子游戏编程一些AI。我的节目是说白色播放器有0动作,即使我知道有。 GetValidMoves()函数进行测试,并在代码的其他区域工作。

I'm trying to program some AI for a game of checkers. My program is saying there are 0 moves for the white player, even though I know there are. The GetValidMoves() function is tested, and works in other areas of the code.

要尝试隔离该程序,我保存了有问题的板状态,然后将其加载,以查看是否会遇到同样的问题:

To try and isolate the program I saved out the problematic board-state then loaded it back up to see if I would get the same problem:

using(Stream s = File.Open("board.dat", FileMode.Create))
{
    var bf = new BinaryFormatter();
    bf.Serialize(s, board);
}
Debug.WriteLine(board.GetValidMoves(Color.White).Count());

using (Stream s = File.Open("board.dat", FileMode.Open))
{
    var bf = new BinaryFormatter();
    board = (Board)bf.Deserialize(s);
}
Debug.WriteLine(board.GetValidMoves(Color.White).Count());

打印:

0
7

当我期望输出为相同(7是正确的)。

When I would expect the output to be the same (7 is correct).

反序列化后可能会导致这样做开始工作?板子的两个实例似乎是完全一样的...我打印出所有的属性,它们都一样正确。我不知道从哪里去?

What could cause this to start working after deserialization? Both instances of the board appear to be exactly the same... I printed out all the properties and they all same correct. I'm not sure where to go from here?

板的第一个实例(反序列化之前)是克隆的结果。我可以克隆吗?有没有悬挂参考?

The first instance of the board (before deserialization) is the result of a clone. Could I be cloning it wrong? Are there "dangling references"?

GetValidMoves:

    public IEnumerable<Move> GetValidMoves(Color c)
    {
        var jumps = GetJumps(c);
        if (jumps.Any())
            foreach (var j in jumps)
                yield return j;
        else
            foreach (var s in GetSlides(c))
                yield return s;
    }

    public IEnumerable<Move> GetSlides(Color c)
    {
        foreach (int i in Enumerate(c))
            foreach (var s in GetSlides(c, i))
                yield return s;
    }

    public IEnumerable<Move> GetJumps(Color c)
    {
        foreach (int i in Enumerate(c))
            foreach (var j in GetJumps(c, i))
                yield return j;
    }

    public IEnumerable<Move> GetJumps(Color c, int i)
    {
        Checker checker = this[c, i] as Checker;
        bool indentedRow = i % Width < rowWidth;
        int column = i % rowWidth;
        int offset = indentedRow ? 0 : -1;
        bool againstLeft = column == 0;
        bool againstRight = column == rowWidth - 1;
        int moveSW = i + rowWidth + offset;
        int moveSE = moveSW + 1;
        int jumpSW = i + rowWidth * 2 - 1;
        int jumpSE = jumpSW + 2;

        if (!againstLeft && jumpSW < Count && IsEnemy(c, moveSW) && IsEmpty(c, jumpSW))
            yield return new Move(c, i, jumpSW, jump: true, crown: IsCrowned(checker, jumpSW));
        if (!againstRight && jumpSE < Count && IsEnemy(c, moveSE) && IsEmpty(c, jumpSE))
            yield return new Move(c, i, jumpSE, jump: true, crown: IsCrowned(checker, jumpSE));

        if (checker.Class == Class.King)
        {
            int moveNW = i - rowWidth + offset;
            int moveNE = moveNW + 1;
            int jumpNW = i - rowWidth * 2 - 1;
            int jumpNE = jumpNW + 2;

            if (!againstLeft && jumpNW >= 0 && IsEnemy(c, moveNW) && IsEmpty(c, jumpNW))
                yield return new Move(c, i, jumpNW, jump: true);
            if (!againstRight && jumpNE >= 0 && IsEnemy(c, moveNE) && IsEmpty(c, jumpNE))
                yield return new Move(c, i, jumpNE, jump: true);
        }
    }

    public IEnumerable<Move> GetSlides(Color c, int i)
    {
        Checker checker = this[c, i] as Checker;
        bool indentedRow = i % Width < rowWidth;
        int column = i % rowWidth;
        int offset = indentedRow ? 0 : -1;
        bool againstLeft = !indentedRow && column == 0;
        bool againstRight = indentedRow && column == rowWidth - 1;
        int moveSW = i + rowWidth + offset;
        int moveSE = moveSW + 1;

        if (!againstLeft && moveSW < Count && IsEmpty(c, moveSW))
            yield return new Move(c, i, moveSW, crown: IsCrowned(checker, moveSW));
        if (!againstRight && moveSE < Count && IsEmpty(c, moveSE))
            yield return new Move(c, i, moveSE, crown: IsCrowned(checker, moveSE));

        if (checker.Class == Class.King)
        {
            int moveNW = i - rowWidth + offset;
            int moveNE = moveNW + 1;

            if (!againstLeft && moveNW >= 0 && IsEmpty(c, moveNW))
                yield return new Move(c, i, moveNW, crown: IsCrowned(checker, moveNW));
            if (!againstRight && moveNE >= 0 && IsEmpty(c, moveNE))
                yield return new Move(c, i, moveNE, crown: IsCrowned(checker, moveNE));
        }
    }

它不应该有副作用。

It shouldn't have side effects.

要回答有关移动是否有意更改电路板状态的疑问:

To answer your queries about whether valid moves is inadvertently changing the board state:

            var board = new Board(8, 8);
            board.SetUp();
            foreach(var m in board.GetValidMoves(Color.White))
                Console.WriteLine(m);
            Console.WriteLine("---");
            foreach(var m in board.GetValidMoves(Color.White))
                Console.WriteLine(m);

打印:

8-12
8-13
9-13
9-14
10-14
10-15
11-15
---
8-12
8-13
9-13
9-14
10-14
10-15
11-15

(同样的输出两次)正如你所料。 >

(The same output twice) As you'd expect.

推荐答案

实际上,确实错误在 Board.Clone 方法中。我认为序列化/反序列化创建了全新的对象,而我的克隆方法并没有正确克隆一切,而是返回引用。

Pretty sure the bug was in the Board.Clone method actually. I think serializing/deserializing created brand new objects whereas my clone method wasn't cloning everything correctly, but rather returning references.

请参阅如何克隆一个继承的对象?的详细信息。

这篇关于为什么这个代码给出两个不同的输出(似乎是)相同的输入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆