生命游戏 - 这个算法是什么

时间:2013-11-25 17:56:29

标签: c# winforms algorithm

我创造了一个简化的生活游戏。

这是我发送实际内容的单元格:

class Cell
{
    public Cell(MainWindow correspondingMainWindow){
        this.mainWindow = correspondingMainWindow;
    }

    public bool excluded;

    public Boolean Occupied { get; set; }

    public Control correspondingPanel;

    private int[] coordinates;

    private MainWindow mainWindow;

    public int[] Coordinates
    {
        get { return this.coordinates; }
        set { 
            if(value.Length != 2) 
            {
                throw new ArgumentException();
            }
            else if(value[0] < 0 || value [1] < 0
                     || value[0] > Settings.FIELDWIDTH 
                     || value[1] > Settings.FIELDHEIGHT)
            {
                throw new ArgumentException();
            }
            else{
                correspondingPanel = mainWindow.FieldArea.Controls
                         .Find(String.Format("panel{0}_{1}", value[0], value[1]), true)
                         .FirstOrDefault();
                this.coordinates = value;
            }
        }

    }

    //Surrounding Cells in the 3x3 around the current cell
    //this is to speed up the updating as soon as the algorithm runs on many cells
    public Cell Top { get; set; }
    public Cell TopRight { get; set; }
    public Cell Right { get; set; }
    public Cell BotRight { get; set; }
    public Cell Bot { get; set; }
    public Cell BotLeft { get; set; }
    public Cell Left { get; set; }
    public Cell TopLeft { get; set; }

    public void die()
    {
        this.Occupied = false;
        this.correspondingPanel.BackColor = Color.Beige;
    }

    public void populate()
    {
        this.Occupied = true;
        this.correspondingPanel.BackColor = Color.DarkRed;
    }
}

以下是有问题的算法:

//should return true if there were any changes to any "living" state
bool Algorithm.runOver(Cell target)
{
    if (target.Occupied && !target.excluded)
    {
        target.Right.populate();
        target.Left.populate();
        target.Top.populate();
        target.Bot.populate();

        target.Right.excluded = true;
        target.Left.excluded = true;
        target.Top.excluded = true;
        target.Bot.excluded = true;
        return true;
    }
    else
    {
        return false;
    }
}
每次算法运行完所有单元格一次后,

排除且已经运行将重置为false。这是为了防止在本步行中更新的Cell上调用walkOver()。

现在当我将我的单元格放在我的字段的左上边缘(它继续像简单的蛇形字段)时,标记它并运行算法,它会在第一次运行后停止更改。

它实际上以设计的方式更新Cell,但之后就停止了。

我将我的单元格保存到程序中的静态列表中(我知道我不应该这样,但它是最简单的方法,直到它正常工作)。细胞有一些面板匹配。这些面板位于名为FieldArea的GroupBox中。它们是基于一些静态常量设置动态生成的(您可以在坐标的设置验证中看到)。

我确信这些单元格正确匹配了面板。这个问题似乎发生在从左上角(0,0)到右下角的FieldArea的对角线上。任何其他起点都能正常工作。不知何故,当大量细胞来自“顶部”时,细胞在田地的顶部和左边缘形成边界。

现在的问题是:我做错了什么?为什么我的领域不能正常工作?

2 个答案:

答案 0 :(得分:4)

您必须保留两份“世界”副本,因为在您应用规则时,必须保持不变。如果将规则应用于仅一个世界,这将导致混合配置,其中一些单元格将具有旧状态,而其他单元格将具有新状态。

因此做这样的事情:

private Cell[,] activeWorld = new Cell[w,h];
private Cell[,] hiddenWorld = new Cell[w,h];

Populate(activeWorld);
while (true) {
    Display(activeWorld);
    ApplyRules(activeWorld, hiddenWorld);

    // Swap worlds
    var temp = activeWorld;
    activeWorld = hiddenWorld;
    hiddenWorld = temp;
}

方法ApplyRules必须从'activeWorld'读取单元格并将结果写入hiddenWorld


更新:您的实际设计似乎过度设计了我。一个简单的2-d布尔数组告诉单元是否被占用应该就足够了。不要从优化开始。这很可能导致复杂,难以读取和错误的代码。而是将注意力集中在算法和良好的代码结构上。如果以后遇到性能问题,请分析问题并应用适当的优化。在&gt; 95%的情况下,问题与编码细节和算法无关,而与I / O无关。在这个游戏的情况下,显示单元格可能比应用规则花费更多的时间。并且可能它反过来太快了,你将不得不在游戏循环中添加暂停。

不要试图优化获取周围单元格索引的逻辑。这不值得痛苦。

解决边缘单元问题的一个好方法是环绕世界。让右侧看不见的东西重新出现在左侧,依此类推。为此使用模运算(%)。 x % N总是会产生0 ... N-1范围内的值。在给定x和y坐标的情况下,您可以得到3 x 3单元格的坐标:

for (int dx = -1; dx <= +1; dx++) {
    int i = (x + dx + Width) % Width;
    for (int dy = -1; dy <= +1; dy++) {
        int j = (y + dy + Height) % Height;
        bool occupied = world[i, j];
        ...
    }
}

+ Width+ Height确保我们始终拥有正值。

答案 1 :(得分:1)

好吧,正如我所承诺的那样,这是我本周的个人胜利:

上面提到的算法是“有缺陷的”

不幸的是我排除了没有改变的细胞。当我从“左上角”走过列表时,错误出现在那里。

我将排除移到了die()和populate()函数。

public void die(){
    this.Occupied = false;
    this.correspondingPanel.BackColor = Color.Beige;
    this.excluded = true;
}

然后我还必须确保循环被正确破坏:

public Algorithm.walkOver(Cell target)
{
   if (target.Occupied && !target.excluded)
   {
       bool b = true;

   //if there could no changes be made, we also have to return there were no changes... 
   //else the while loop continues forever and we lose the process :(
       if (target.Right.Occupied && target.Left.Occupied 
           && target.Bot.Occupied && target.Top.Occupied)
       b = false;

       if(!target.Right.Occupied)
           target.Right.populate();

       if (!target.Left.Occupied)
           target.Left.populate();

       if (!target.Top.Occupied)
          target.Top.populate();

       if (!target.Bot.Occupied)
          target.Bot.populate();

       return b;
   }
   else
   {
      return false;
   }
}

我还将重置移动到while循环的开头,因为“chosing”一个单元格将其排除。