为什么这段代码对于(看起来是)相同的输入给出了两个不同的输出?

2024-01-10

我正在尝试为跳棋游戏编写一些人工智能程序。我的程序显示白人玩家的步数为 0,尽管我知道有这样的步数。这GetValidMoves()函数已经过测试,并且可以在代码的其他区域工作。

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

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 是正确的)。

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

板的第一个实例(反序列化之前)是克隆的结果。我会不会克隆错了?是否存在“悬空引用”?


获取有效移动:

    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 不应该有副作用。


要回答您关于有效移动是否会无意中改变棋盘状态的疑问:

            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);

Prints:

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

(相同的输出两次)如您所料。


只是一个大胆的猜测,但您确定调用 GetValidMoves 绝对没有副作用吗?

由于您在调用 GetValidMoves 后对板进行序列化和反序列化,因此 GetValidMoves 似乎以某种方式更改了板(鉴于函数的名称,这对我来说似乎有点奇怪)。所以也许还有其他你没有考虑到的副作用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么这段代码对于(看起来是)相同的输入给出了两个不同的输出? 的相关文章

随机推荐