4

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

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

UI 带有findTargets(电脑不能轮流,玩家只能轮流)。

提前感谢您的帮助。

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

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

4

3 回答 3

3

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

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

例如,你肯定应该有一个 Ship 类,肯定应该有一个 Grid 类,可能还有一个 Shot 类等等。你的 Ship 类肯定应该“插入”到你的 Grid 类。您的 Grid 类不应该分配潜在位置的整个网格,而是应该只分配有效的命中区域,因为其中插入了 Ship 实例。每个没有被 Ship 实例居住的位置显然都是未命中的,因此以单一方法处理所有不包含船舶的位置 -

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

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

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

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

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

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

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

于 2013-05-20T23:41:15.137 回答
1

正如@jorge-Chibante 所提到的,请将您的域代码和您的演示代码分开,并尝试编写一些单元测试来验证您期望您的人工智能应该如何行为,然后开发您的人工智能。

在战舰游戏 Ships N' Battles 中,我使用概率构建了 AI,并制作了每个方格出现敌舰概率的脑图。一切都带有一些测试,以帮助验证行为和任何未来的重构。

让你的代码的每一部分只负责一个责任,你会让你的生活更轻松

于 2013-05-20T23:02:40.583 回答
0

人工智能

在战舰中有 3 种不同的策略模式(我在这里简化了)。

  • 轰炸一个随机方块
  • 围成一圈炸弹
  • 直线轰炸

一开始计算机只需要找到一个尚未被轰炸的随机方块。当他找到一个正方形时,他需要在它周围进行轰炸以试图找出船在哪里。之后,他假设这是船的方向并在一条直线上(两侧)进行炸弹。

您的计算机应该有一个变量来指示他处于哪种模式。

代码

你的代码到处都是,很难阅读。您应该在更多功能中分离您的逻辑。这样更容易测试/阅读并找出问题所在。还要确保你的游戏逻辑和 UI 逻辑是分开的。

public bool IsOccuped(Point target)
public bool AlreadyBombed(Point target)
public bool BombLocation(Point target)
public Point FindNewTarget()

你的电脑移动会好很多

public void ComputerMove()
{
    Point target;

    target = FindNewTarget();

    BombLocation(target);
}

// There are more efficient way of doing this but it's an example.
public Point FindNewTarget()
{
    Point newTarget = new Point();

    do
    {
        newTarget.x = [get random location from 0 to grid width]
        newTarget.y = [get random location from 0 to grid height]   
    }while(AlreadyBombed(newTarget))
}

即使一个函数很小,代码的可修改性也会有很大帮助。

public bool IsOccuped(Point target)
{
    return data[1, target.X, target.Y].hit;
}
于 2013-05-21T13:36:56.073 回答