如何在C#中生成随机填充的各种大小的单元格

时间:2018-08-07 04:42:20

标签: c# arrays algorithm for-loop

在二维空间中,我需要创建一种特殊的泛洪填充方法,该方法将根据一组预定义对象(下图中的红色,蓝色,绿色和黄色对象)生成随机数组。

edit:任何单个“填充对象”都不应与另一个对象重叠,并且它们的放置应该看起来是随机的,但是可以为每个对象添加某种形式的出现概率是可以的。甚至可能将红色1x1以外的所有对象放到满足一定容量的位置,然后用红色填充其余的对象。

enter image description here

现在想象一个随机结果,该结果如下所示。

edit:我提供的结果图像显示,所有内容始终都用红色方块分隔开,这当然是可能的,但不是范围的必要部分。我以这种方式绘制图像是为了消除任何观念,即红色以外的某些颜色对象被认为可与其他类似颜色的对象“连接”在一起。换句话说,我不希望人们认为3x3的绿色对象可以成为6x3的算法。应该将其视为随机分开放置的两个单独的3x3。为了解决@jdweng的评论,部分解决方案很可能需要在FillObject类中添加某种形式的出现概率字段,然后始终将1x1红色正方形视为构成空间的对象。

enter image description here

这是我到目前为止提出的代码。我很难过这个,直到最后。我熟悉标准的洪水填充,但不确定如何使用随机大小的“填充对象”。

//Defined objects that make up the grid fill
public class FillObject
{
    public int xDim { get; set; }
    public int yDim { get; set; }

    public string color { get; set; }

    public FillObject(int x, int y, string c)
    {
        xDim = x;
        yDim = y;
        color = c;
    }
}

//container for information about a position on the grid
public class GridPosition
{
    public int xPosition { get; set; }
    public int yPosition { get; set; }

    public string color { get; set; }
}

public class GridGenerator
{
    //declare the intitial possible fill objects
    public static FillObject[] fillObjects = new FillObject[]
    {
        new FillObject(1, 1, "red"),
        new FillObject(2, 2, "blue"),
        new FillObject(3, 3, "green"),
        new FillObject(4, 4, "yellow")
    };

    //Generic list of grid positions
    public List<GridPosition> grid = new List<GridPosition>();

    //Method to generate random grid filled with the fill objects
    public void GenerateGrid(int startX, int startY, int endX, int endY)
    {
        //Iterate through grid and set each "cell" to a color based on the original fill objects
        for(int x = startX; x <= endX; x += 1)
        {
            for(int y = startY; y <= endY; y += 1)
            {
                grid.Add(new GridPosition());
            }
        }
    }
}

我考虑过只是遍历for循环并测试已创建位置的列表,然后利用随机机会“填充”填充对象(如果它们合适),但是在每个循环中测试列表似乎效率不高进行迭代,以找到填充位置的存在,然后测试每个对象的偏移量也会出现问题。可能是一种递归方法,该方法以最大的“ fillObject”开头,然后递归地进行测试和填充,但同样看起来非常麻烦。是否有一些聪明的方法可以在我看不到的网格循环中执行此操作?

编辑:这是明确的目标

  • 创建一个方法,该方法生成一个List对象,该对象 包含网格上的每个位置。

  • 每个网格位置应包含如下位置:int x和int y GridPosition类中的字段。

  • 生成的GridPosition对象应遵循原始的
    FillObject结构。

  • 每个对象的出现概率都很好,一切都
    剩下的可以用红色的1x1对象填充。

  • 相同颜色的对象可以相邻(即使结果
    图片未显示此内容)

  • 理想情况下,这是一次完成的操作(例如洪水填充),但是
    递归是另一种可能性。

  • 真正的随机性不是必需的,只需要看起来有些
    随机,但也不应出现图案。

1 个答案:

答案 0 :(得分:1)

好吧,今天早上我有一个免费的小时,所以我对此感到一阵刺痛。为了快速起见,我使用了unsafefixed和指针。我不确定您是否需要图像,但是它也可以与多维数组一起使用。

FillObject

public class FillObject
{
   public FillObject(int width, Color color)
   {
      Width = width;
      Color = color.ToArgb();
   }

   public int Color { get; set; }

   public int Width { get; set; }
}

扩展方法

public static class Extensions
{
   public static Color NextColor(this Random r) => Color.FromArgb(r.Next(256), r.Next(256), r.Next(256));

   public static FillObject GetObject(this List<FillObject> objects, int maxSize, Random rand)
   {
      var nextObjs = objects.Where(o => o.Width <= maxSize)
                              .ToList();
      return nextObjs[rand.Next(nextObjs.Count)];
   }
}

帮助方法

public static unsafe bool GetMaxSize(int x, int y, Size size, int* array, int pad, out int maxSize)
{

   maxSize = 0;

   //we cant be within pad distance of anything else or the edges
   for (var y2 = y - pad; y2 < y + pad; y2++)
      for (var x2 = x - pad; x2 < x + pad; x2++)
         if (y2 < 0 || x2 < 0 || y2 >= size.Height || x2 >= size.Width || *(array + x2 + y2 * size.Width) != 0)
            return false;

   // so what do we have left
   for (var y2 = y - pad; y2 < y + 1; y2++)
   {
      var max = maxSize > 0 ? maxSize : _largest + pad;
      maxSize = 0;
      for (var x2 = x; x2 < size.Width && x2 < x + max; x2++, maxSize++)
         if (*(array + x2 + y2 * size.Width) != 0)
            break;
   }

   // safety first
   maxSize = Math.Min(maxSize - pad, Math.Min(size.Width - x - pad, size.Height - y - pad));

   return maxSize > 0;
}

主要方法

public static unsafe void Generate(Size size, int* array, int pad)
{
   for (var y = 0 + pad; y < size.Height; y++)
      for (var x = 0 + pad; x < size.Width; x++)
      {
         // no maxSize bail
         if (!GetMaxSize(x, y, size, array, pad, out var maxSize))
            continue;

         // pick a random one
         var nextObj = _objects.GetObject(maxSize, _rand);

         // draw the thing
         for (var y2 = y; y2 < y + nextObj.Width; y2++)
            for (var x2 = x; x2 < x + nextObj.Width; x2++)
               *(array + x2 + y2 * size.Width) = nextObj.Color;

         // start again outside that width
         // remembering the loop will increment this anyway 
         x += nextObj.Width + pad - 1;
      }
}

用法

private static unsafe void Main(string[] args)
{

   _objects = Enumerable.Range(0, 25)
                        .Select((i, i1) => new FillObject(i, _rand.NextColor()))
                        .ToList();

   _largest = _objects.Max(x => x.Width);

   var size = _rand.Next(100, 100);

   using (var bmp = new Bitmap(size, size, PixelFormat.Format32bppPArgb))
   {
      var bitmapData = bmp.LockBits(new Rectangle(0, 0, size, size), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
      var data = (int*)bitmapData.Scan0;
      Generate(new Size(size, size), data, 1);
      bmp.UnlockBits(bitmapData);
      bmp.Save(@"D:\TestImage.bmp");
   }
}

测试100x100 5块1填充

enter image description here

enter image description here

测试100x100 5块2填充

enter image description here

测试100x100 10块2填充

enter image description here

测试100x100 b25会锁定2个填充

enter image description here

测试100x100 25块1个填充

enter image description here

测试100x100 25块0填充

enter image description here

摘要

好吧,前提是

  • 要从xy的整个平面上移动
  • 确定可用空间
  • 获取适合的对象列表
  • 选择一个随机的
  • 绘制

但是,如果您使用规范(即随机变量),则会发现存在偏差。就是说,在第一行它可以容纳任何喜欢的东西。但是,随后的行将受到限制,因此它将偏向较小的块。然后它会向远处偏移最小的黑色以填充图像。

此外,还有一个对角线效果的偏差,这基本上只是试图将其放入下一行的随机块中的结果。

可能会有更多的微优化来使它更快,但是它非常好并且可以在毫秒内运行

无论如何,我想这是一个很好的起点,您可以添加胡椒粉和盐调味

祝你好运