add cellular automata generator
This commit is contained in:
131
addons/ca_level_generator/CaGenerator.cs
Normal file
131
addons/ca_level_generator/CaGenerator.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
|
||||
namespace Mr.BrickAdventures.Tools.CaLevelGenerator;
|
||||
|
||||
public static class CaGenerator
|
||||
{
|
||||
public static bool[,] Generate(CaGeneratorSettings s)
|
||||
{
|
||||
var rng = new Random(s.Seed);
|
||||
return s.Mode switch
|
||||
{
|
||||
CaMode.Cave => GenerateCave(s, rng),
|
||||
CaMode.Platform => GeneratePlatform(s, rng),
|
||||
CaMode.Terrain => GenerateTerrain(s, rng),
|
||||
_ => GenerateCave(s, rng),
|
||||
};
|
||||
}
|
||||
|
||||
public static bool[,] Smooth(bool[,] grid) => SmoothPass(grid, 5);
|
||||
|
||||
// ── Cave ──────────────────────────────────────────────────────────────
|
||||
private static bool[,] GenerateCave(CaGeneratorSettings s, Random rng)
|
||||
{
|
||||
var grid = RandomFill(s.Width, s.Height, s.FillDensity, rng);
|
||||
if (s.BorderWalls) EnforceBorder(grid);
|
||||
for (int i = 0; i < s.SmoothingPasses; i++)
|
||||
{
|
||||
grid = SmoothPass(grid, 5);
|
||||
if (s.BorderWalls) EnforceBorder(grid);
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
// ── Platform ──────────────────────────────────────────────────────────
|
||||
private static bool[,] GeneratePlatform(CaGeneratorSettings s, Random rng)
|
||||
{
|
||||
int w = s.Width, h = s.Height;
|
||||
var grid = new bool[w, h];
|
||||
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
grid[x, h - 1] = true;
|
||||
grid[x, h - 2] = true;
|
||||
|
||||
for (int y = 0; y < h - 2; y++)
|
||||
{
|
||||
// Density linearly increases from 10% at top to 60% at bottom,
|
||||
// scaled by user's FillDensity relative to default 0.48.
|
||||
float t = (float)y / (h - 3);
|
||||
float rowDensity = (0.1f + t * 0.5f) * (s.FillDensity / 0.48f);
|
||||
grid[x, y] = rng.NextDouble() < rowDensity;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < s.SmoothingPasses; i++)
|
||||
{
|
||||
// Threshold 4 (not 5): horizontal platform groups survive, isolated
|
||||
// vertical pixels collapse into flat platforms.
|
||||
grid = SmoothPass(grid, 4);
|
||||
for (int x = 0; x < w; x++) { grid[x, h - 1] = true; grid[x, h - 2] = true; }
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
// ── Terrain ───────────────────────────────────────────────────────────
|
||||
private static bool[,] GenerateTerrain(CaGeneratorSettings s, Random rng)
|
||||
{
|
||||
int w = s.Width, h = s.Height;
|
||||
var grid = new bool[w, h];
|
||||
|
||||
// Start terrain surface at ~60% from bottom (40% from top)
|
||||
int surfaceRow = (int)(h * 0.4f);
|
||||
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
int delta = rng.Next(-2, 3);
|
||||
surfaceRow = Math.Clamp(surfaceRow + delta, h / 6, h - 2);
|
||||
|
||||
for (int y = surfaceRow; y < h; y++)
|
||||
grid[x, y] = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < s.SmoothingPasses; i++)
|
||||
grid = SmoothPass(grid, 5);
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
// ── Shared helpers ────────────────────────────────────────────────
|
||||
private static bool[,] RandomFill(int w, int h, float density, Random rng)
|
||||
{
|
||||
var grid = new bool[w, h];
|
||||
for (int x = 0; x < w; x++)
|
||||
for (int y = 0; y < h; y++)
|
||||
grid[x, y] = rng.NextDouble() < density;
|
||||
return grid;
|
||||
}
|
||||
|
||||
private static bool[,] SmoothPass(bool[,] grid, int threshold)
|
||||
{
|
||||
int w = grid.GetLength(0), h = grid.GetLength(1);
|
||||
var next = new bool[w, h];
|
||||
for (int x = 0; x < w; x++)
|
||||
for (int y = 0; y < h; y++)
|
||||
next[x, y] = CountSolidNeighbors(grid, x, y) >= threshold;
|
||||
return next;
|
||||
}
|
||||
|
||||
private static int CountSolidNeighbors(bool[,] grid, int cx, int cy)
|
||||
{
|
||||
int w = grid.GetLength(0), h = grid.GetLength(1), count = 0;
|
||||
for (int dx = -1; dx <= 1; dx++)
|
||||
for (int dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
int nx = cx + dx, ny = cy + dy;
|
||||
// Out-of-bounds counts as solid (closes cave edges naturally)
|
||||
if (nx < 0 || nx >= w || ny < 0 || ny >= h) count++;
|
||||
else if (grid[nx, ny]) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void EnforceBorder(bool[,] grid)
|
||||
{
|
||||
int w = grid.GetLength(0), h = grid.GetLength(1);
|
||||
for (int x = 0; x < w; x++) { grid[x, 0] = true; grid[x, h - 1] = true; }
|
||||
for (int y = 0; y < h; y++) { grid[0, y] = true; grid[w - 1, y] = true; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user