You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

2D元胞自动机中水体瓦片扩散不均匀问题排查

水体元胞自动机模拟无法均匀扩散的问题

问题描述

基于元胞自动机制实现了简单水体模拟,但水体瓦片无法均匀扩散——右侧存在空闲单元格时,水体却拒绝移动(见截图)。参考多篇指南实现,代码与指南几乎一致,但效果不符。

水体扩散异常截图

代码实现

private const string TITLE = "Water Simulation";

private const int MAP_WIDTH = 80;
private const int MAP_HEIGHT = 20;
private static int[,] map = new int[MAP_HEIGHT, MAP_WIDTH];

private static char cBound = '▓';
private static char cWater = '░';
private static char cSpace = ' ';

static void Main(string[] args)
{
    //WATER SIMULATION\\
    Console.CursorVisible = false;

    Random rnd = new Random();
    int counter = 500;

    while (true)
    {
        //Source of the water
        bool willCreateWaterTile = rnd.Next(0, 101) >= 50 && map[0, MAP_WIDTH / 2] == 0;
        if (willCreateWaterTile && counter > 0)
        {
            map[0, MAP_WIDTH / 2] = 1;
        }

      
        for (int y = MAP_HEIGHT - 1; y >= 0; --y)
        {
            for (int x = 0; x < MAP_WIDTH; ++x)
            {
                if (map[y, x] == 1)
                {
                    bool isFirstCheckLeft = rnd.Next(0, 101) >= 50;

                    if (InBounds(x, y + 1) && map[y + 1, x] == 0)
                    {
                        map[y + 1, x] = 1;
                        map[y, x] = 0;
                    }
                    else if (isFirstCheckLeft)
                    {
                        if (InBounds(x - 1, y) && map[y, x - 1] == 0)
                        {
                            map[y, x - 1] = 1;
                            map[y, x] = 0;
                        }
                        else if (InBounds(x + 1, y) && map[y, x + 1] == 0)
                        {
                            map[y, x + 1] = 1;
                            map[y, x] = 0;
                        }
                    }
                    else
                    {
                        if (InBounds(x + 1, y) && map[y, x + 1] == 0)
                        {
                            map[y, x + 1] = 1;
                            map[y, x] = 0;
                        }
                        else if (InBounds(x - 1, y) && map[y, x - 1] == 0)
                        {
                            map[y, x - 1] = 1;
                            map[y, x] = 0;
                        }                                
                    }
                    //____________________________//
                }
            }
        }

        Display();

        counter--;
    }
}

static bool InBounds(int x, int y)
{
    return x >= 0 && y >= 0 && y < MAP_HEIGHT && x < MAP_WIDTH;
}

问题原因及修复方案

核心问题:原地修改导致迭代干扰

你当前的代码在遍历过程中直接修改了map数组——处理某个元胞时,把它的状态改成0,同时把相邻元胞改成1,但后续遍历到这个被修改的相邻元胞时,会重复处理它,导致同一帧内元胞被多次移动,直接破坏了扩散的均匀性。再加上迭代顺序(从下到上、从左到右)的影响,会让某一侧的扩散被“抢占”,出现单侧无法扩散的情况。

修复方案:用临时缓冲区存储新状态

元胞自动机的正确逻辑是先计算所有元胞的下一个状态,再统一更新,不能在遍历过程中实时修改原数组。具体修改步骤:

  1. 创建和原map同大小的临时数组newMap,初始化为原数组的副本;
  2. 遍历原map的每个元胞,根据原状态计算下一个状态,写入newMap,全程不修改原map;
  3. 遍历结束后,把newMap赋值给map,完成状态更新。

修改后的核心循环代码:

// 创建临时数组存储新状态,避免遍历中修改原数组
int[,] newMap = (int[,])map.Clone();

for (int y = MAP_HEIGHT - 1; y >= 0; --y)
{
    for (int x = 0; x < MAP_WIDTH; ++x)
    {
        if (map[y, x] == 1)
        {
            bool moved = false;
            // 优先向下移动
            if (InBounds(x, y + 1) && map[y + 1, x] == 0)
            {
                newMap[y + 1, x] = 1;
                newMap[y, x] = 0;
                moved = true;
            }
            // 向下不行则尝试左右扩散
            if (!moved)
            {
                // 先收集所有可移动的方向,再随机选择
                List<int> availableDirs = new List<int>();
                if (InBounds(x - 1, y) && map[y, x - 1] == 0) availableDirs.Add(-1);
                if (InBounds(x + 1, y) && map[y, x + 1] == 0) availableDirs.Add(1);
                
                if (availableDirs.Count > 0)
                {
                    int dir = availableDirs[rnd.Next(availableDirs.Count)];
                    newMap[y, x + dir] = 1;
                    newMap[y, x] = 0;
                }
            }
        }
    }
}

// 统一更新原数组
map = newMap;

额外优化点

  • 原代码中counter递减到0后就不再生成新水,但循环还会继续,可以考虑在counter <=0时停止生成逻辑,或者按需重置counter;
  • 优化后的方向选择逻辑,先收集所有可行方向再随机,避免了原代码中“先左后右”的顺序依赖,确保左右扩散的概率真正均等。

内容的提问来源于stack exchange,提问作者kkotu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 17:43:18