This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center




8年前关闭。




我正在使用 C#.NET 构建一个战舰游戏。它应该使用相当简单的评分机制。没有沉船,如果玩家或计算机得分 17,则他们获胜。如果你击中了,你可以再转一圈。 AI 随机攻击直到命中,此时它会向每个方向攻击一块瓷砖,直到找到趋势,然后继续直线攻击,直到找到死胡同(未被占用的空间或边缘的边缘)如果在计算机攻击的相反方向上有没有被击中的空间,它就会攻击这些空间。它不会瞄准它已经击中的空间或遵循已经遵循的模式。

到目前为止,这是我的 AI。
    int shipCounter = 0, trend = 0;
    static Random rnd = new Random();
    bool gameOver = false, playerTurn = false;
    int[] score = { 0, 0 };

    struct gameData
    {
        public bool occupied, hit, marked;
    }
    gameData[,,] data;



    public void computerMove()
    {
        Point target = seekTarget();

        try
        {
            if (data[1, target.X, target.Y].hit)
                computerMove();
            else
            {
                data[1, target.X, target.Y].hit = true;
                if (data[1, target.X, target.Y].occupied)
                {
                    attacking = true;
                    score[0]++;
                    computerMove();
                }
            }

            playerTurn = true;
        }
        catch (IndexOutOfRangeException)
        { computerMove(); }
    }

    public Point seekTarget()
    {
        Point origin = new Point(-1, -1);

        //find a point that's been hit.
        int x = 0, y = 0;

        while (x < gridSize && y < gridSize)
        {
            if (data[1, x, y].hit && data[1, x, y].occupied && !data[1, x, y].marked)
            {
                origin = new Point(x, y);
                break;
            }
            x++;
            if (x == gridSize && y != gridSize)
            {
                x = 0;
                y++;
            }
        }

        return findTargets(origin);
    }

    public Point findTargets(Point origin)
    {
        Point[] lim = { origin, origin, origin, origin };
        Point[] possibleTargets = { origin, origin, origin, origin };

        //Find the edges.

        while (lim[0].X >= -1 && ((!data[1, lim[0].X, lim[0].Y].hit && !data[1, lim[0].X, lim[0].Y].occupied) || (data[1, lim[0].X, lim[0].Y].hit && data[1, lim[0].X, lim[0].Y].occupied)))
        {
            lim[0].X--;
            if (lim[0].X == -1)
                break;
        }
        while (lim[1].Y >= -1 && ((!data[1, lim[0].X, lim[0].Y].hit && !data[1, lim[0].X, lim[0].Y].occupied) || (data[1, lim[0].X, lim[0].Y].hit && data[1, lim[0].X, lim[0].Y].occupied)))
        {
            lim[1].Y--;
            if (lim[1].Y == -1)
                break;
        }
        while (lim[2].X <= gridSize && ((!data[1, lim[0].X, lim[0].Y].hit && !data[1, lim[0].X, lim[0].Y].occupied) || (data[1, lim[0].X, lim[0].Y].hit && data[1, lim[0].X, lim[0].Y].occupied)))
        {
            lim[2].X++;
            if (lim[2].X == gridSize)
                break;
        }
        while (lim[3].Y <= gridSize && ((!data[1, lim[0].X, lim[0].Y].hit && !data[1, lim[0].X, lim[0].Y].occupied) || (data[1, lim[0].X, lim[0].Y].hit && data[1, lim[0].X, lim[0].Y].occupied)))
        {
            lim[3].Y++;
            if (lim[3].Y == gridSize)
                break;
        }

        //Cell targeting AI

        }
        return new Point(rnd.Next(10), rnd.Next(10));
    }

由于我无法弄清楚出了什么问题,它变得非常困惑。如果我引用 findTargets 函数并让计算机随机攻击,它工作正常。计算机和玩家交易轮流,计算机点击注册。

但是,启用 findTargets 后,玩家可以进行一次攻击,而计算机永远不会轮到它。然后它不会恢复到玩家回合,即使玩家的攻击十字准线仍然可见。如果有人可以提供帮助,将不胜感激。抱歉没有包含 PaintmouseDown 方法,它们超出了字符限制。

没有 findTargets 的 UI(玩家和电脑交易轮流)。

带有 findTargets 的 UI(计算机不能轮流,玩家只能轮流)。

在此先感谢您的帮助。

编辑: 我已经隔离了这个问题,它似乎无法摆脱 findTargets 中的 while 循环。即使我通过在 origin(-1, -1) 时阻止它循环来解决问题,它也会在第一次点击时陷入循环。

编辑 2: 它正在击中第一个循环,并无限循环。出于某种原因,它根本没有增加 lim[0].X。当我在循环中插入一个消息框以显示一些数据时,它显示两次,然后不再出现,即使它仍在循环中。有人知道为什么吗?

最佳答案

您正在使用面向对象的语言 - 在我看来就像 Java。

因此,为了更容易编码、更容易理解、更容易维护、更容易增强您的代码,请尝试使用一些实际对象。

例如,您绝对应该有一个 Ship 类,绝对应该有一个 Grid 类,可能是一个 Shot 类,等等。您的 Ship 类绝对应该“插入”到您的 Grid 类中。您的 Grid 类不应分配潜在位置的整个网格,而应仅分配有效的 Hit 区域,因为 Ship 实例已插入其中。每个没有 Ship 实例居住的位置显然都是一个未命中,所以用一个方法处理所有不包含船的位置 -

Grid 类将完成所有工作——它可以有一个 addShipHorizo​​ntal(Ship, x, y) 和一个 addShipVertical(Ship, x, y)。它肯定应该有一个返回 null 或 Ship 的 hitTest(x, y)。它应该维护一个 Ship 实例的集合,比如 ArrayList,它将在 hitTest(x, y) 方法中迭代。

Ship 应该有一个 PointCount 和一个 Points 集合,当船被传递到 addShipH() 或 addShipV() 方法时,它们会被设置。 Ship 还应该有一个 hitTest(x, y) 方法,如果船居住在指定的 x, y 上,该方法将返回 true。 hitTest(x, y) 将遍历船舶的 Points 集合以寻找匹配项。

当需要拍摄时,在网格上选择您的位置并对其进行 HitTest - 所选位置是否包含 Ship 引用,是的,然后执行 ship.hit(location) 并返回一个新的 Hit() - 否则返回一个新小姐();

将其分解为您实际考虑模拟的对象 - 这称为域模型。然后为每个对象类提供适当的方法,以便实际游戏只是域模型类之间的编排。

不要编写游戏代码——相反,先编写类,然后编写类的方法——当你构建类时,游戏将通过类之间的方法通过它们的交互产生。

从顶部开始,您需要应用程序顶部的哪个类的 2 个实例?答案 - 玩家。 Player 类管理什么?一个网格和一个船舶列表。网格管理什么?船舶及其位置的列表,以及射击的 HitTest 。 Ship 维护什么?它在网格上的位置以及它所在的哪些位置已经/没有被击中。

你会发现如果你用 OO 风格来做,它将是代码的 1/4,并且具有两倍的灵活性。祝你好运!

关于c# - 战舰AI——彻底迷失,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16659061/

10-11 15:39