我创造了一个简化的生活游戏。
这是我发送实际内容的单元格:
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的对角线上。任何其他起点都能正常工作。不知何故,当大量细胞来自“顶部”时,细胞在田地的顶部和左边缘形成边界。
现在的问题是:我做错了什么?为什么我的领域不能正常工作?
答案 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”一个单元格将其排除。