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すると、プレイヤーは 1 回の攻撃を行うことができ、コンピューターはそのターンを取りません。その後、プレイヤーの攻撃十字線がまだ表示されていても、プレイヤーのターンに戻りません。誰かが助けてくれれば、それは大歓迎です。PaintまたはmouseDownメソッドが含まれていないことをお詫びします。文字制限を超えています。

UIなしfindTargets(プレイヤーとコンピューターのトレードターン)。

UI ありfindTargets(コンピューターはターンを実行できず、プレイヤーは 1 ターンしか実行できません)。

助けてくれてありがとう。

編集:私は問題を分離しました.whileループから抜け出せないようですfindTargets. originのときにループしないようにして問題に対処しても(-1, -1)、最初のヒットでループに巻き込まれます。

編集 2:最初のループにヒットし、無限にループします。なぜか全然伸びませんlim[0].X。メッセージボックスをループに挿入してデータを表示すると、2 回表示され、まだループしているにもかかわらず再表示されません。これがなぜなのか誰か知っていますか?

4

3 に答える 3

1

@jorge-Chibante が述べたように、ドメイン コードとプレゼンテーション コードを分離し、AI がどのように動作するかを検証するための単体テストを作成してから、AI を開発してください。

戦艦ゲームである Ships N' Battles では、確率を使用して AI を構築し、すべての正方形に敵船がある確率の脳マップを作成しました。動作と将来のリファクタリングを検証するのに役立ついくつかのテストを含むすべて。

コードのすべての部分を 1 つの責任だけで保持すれば、作業が楽になります

于 2013-05-20T23:02:40.583 に答える
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;
}
于 2013-05-21T13:36:56.073 に答える