Big Robot Games - Indie Dev Blog Edition

Somewhere for me to post my musings on Indie Development, Cryptocurrencies and whatever else strikes my fancy.

Cellular Automata

I've been playing with procedural cave map generation lately, and put together a C# class that uses the Cellular Automata technique to fill a grid with true/false noise and apply rules for "growing" the caves (very similar to "Game of Life" rules.)

The rules work like this: 

  1. If you have a "live" cell (value: true) with 4 or more live cells around it, it stays alive (value: true)
  2. If you have a "live" cell (value: true) with 3 or less live cells around it, it dies (value: false)
  3. if you have a "dead" cell (value: false) with 5 or more live cells around it, it becomes alive (value: true)
  4. If you have a "dead" cell (value: false) with 4 or less live cells around it, it stays dead (value: false)

To use it, you'll need to call the constructor to initialize the RNG and CaveMap array, then call the InitializeCaveMap() method once. After that, you'll want to call ApplyRulesToCaveMap() a few times (best/smoothest results for my purposes was about 8 times, but your needs may be different.)

As you can see from the pictures at the bottom, earlier passes show the most drastic change, while later passes have a smoothing effect.

I've cut out the namespace and using statements for brevity, but the class is below. Pretty simple stuff, but feel free to ask any questions in the comments section.

I'd love to hear if and how you might be using it.


class CaveBuilder
{
   public bool[,] CaveMap;
   private System.Random rng;

   public CaveBuilder(int width, int height)
   {
      rng = new Random();
      CaveMap = new bool[width, height];
   }

   public void InitializeCaveMap()
   {
      for (var x = 0; x < CaveMap.GetLength(0); x++)
         for (var y = 0; y < CaveMap.GetLength(1); y++)
            CaveMap[x, y] = (rng.Next(0, 2) == 1);
   }

   public void ApplyRulesToCaveMap()
   {
      for (var x = 0; x < CaveMap.GetLength(0); x++)
         for (var y = 0; y < CaveMap.GetLength(1); y++)
            if (CaveMap[x, y])
               if (CountLiveCells(x, y) >= 4)
                  CaveMap[x, y] = true;
               else
                  CaveMap[x, y] = false;
            else
               if (CountLiveCells(x, y) >= 5)
                  CaveMap[x, y] = true;
               else
                  CaveMap[x, y] = false;
   }

   private int CountLiveCells(int x, int y)
   {
      var total = 0;

      if (CaveMap[Constrain(x - 1,0), Constrain(y - 1,1)]) total++;
      if (CaveMap[x, Constrain(y - 1,1)]) total++;
      if (CaveMap[Constrain(x + 1,0), Constrain(y - 1,1)]) total++;
      if (CaveMap[Constrain(x - 1,0), y]) total++;
      if (CaveMap[Constrain(x + 1,0), y]) total++;
      if (CaveMap[Constrain(x - 1,0), Constrain(y + 1,1)]) total++;
      if (CaveMap[x, Constrain(y + 1,1)]) total++;
      if (CaveMap[Constrain(x + 1,0), Constrain(y + 1,1)]) total++;

      return total;
   }

   private int Constrain(int check, int dimension)
   {
      if (check < 0)
         return 0;

      if (check > CaveMap.GetLength(dimension) - 1) 
         return CaveMap.GetLength(dimension) - 1;

      return check;
   }
}


Random noise, no discernible pattern:


After one pass of rules, still pretty rough:


A couple more passes:


A few more passes, about as smooth as it gets:

Loading