战舰AI - 完全丢失

时间:2013-05-20 21:58:55

标签: c# .net

我正在使用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的用户界面(玩家和计算机交易转向)。

使用findTargets的用户界面(计算机无法轮流,玩家只需转一圈)。

提前感谢您的帮助。

编辑:我已经解决了这个问题,它似乎无法突破findTargets中的while循环。即使我通过在origin(-1, -1)时停止循环来解决问题,它也会在第一次点击的循环中被捕获。

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

3 个答案:

答案 0 :(得分:3)

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

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

例如,你肯定应该有一个Ship类,肯定应该有一个Grid类,可能是一个Shot类等。你的Ship类肯定应该“插件”到你的Grid类。您的Grid类不应该分配潜在位置的整个网格,而应该只分配有效的Hit区域,因为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集合,当船舶被传递到addShipH()或addShipV()方法时,它们会被设置。 Ship还应该有一个hitTest(x,y)方法,如果船只位于指定的x,y,则返回true。 hitTest(x,y)将遍历船舶的Points集合寻找匹配。

当需要拍摄时,在网格上选择您的位置并点击测试它 - 所选位置是否包含Ship参考,是,然后执行ship.hit(位置)并返回新的Hit() - 否则返回一个新的小姐();

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

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

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

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

答案 1 :(得分:1)

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

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

让代码的每个部分只承担一项责任,让您的生活更轻松

答案 2 :(得分:0)

AI

在战舰中有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;
}