Random Walkers


In this tutorial I will be showing you how to use a random walker function to create dungeon maps. A random walker is a computational model used to simulate a random path. It was first introduced by Karl Pearson in 1905 as a mathematical model for Brownian motion, a phenomenon in which particles suspended in a fluid move randomly due to collisions with the fluid molecules.

The walker starts from a given position and takes random steps in any direction. These steps are determined by a set of rules, such as a probability distribution. The path generated by the walker is random and unpredictable, but it can be used to model many different phenomena, such as diffusion, animal movements, or stock prices.

In this tutorial, we will use C# to implement a random walker function that generates a dungeon map. The dungeon will consist of a rectangular grid of tiles, where each tile can be either a wall or a floor. The walker will start from a random position on the grid and move randomly, creating a path of floor tiles. The path will stop when a certain percentage of the tiles on the grid have been visited. The resulting map will be a random dungeon layout that can be used in a role-playing game or other applications.

Define the Tile class that represents a single tile on the dungeon map. Each tile has a character representation, a foreground color, and a background color.

public class Tile 
{
     public char character;
     public ConsoleColor foregroundColor;
     public ConsoleColor backgroundColor;
     public Tile(char p_character, ConsoleColor p_foregroundColor, ConsoleColor p_backgroundColor)
     {
         character = p_character;
         foregroundColor = p_foregroundColor;
         backgroundColor = p_backgroundColor;
     }
     public void Print()
     {
         Console.ForegroundColor = foregroundColor;
         Console.BackgroundColor = backgroundColor;
         Console.Write(character);
     } 
} 

Define the Dungeon class that represents the entire dungeon map. The dungeon consists of a 2D array of tiles, a 2D array of booleans to keep track of the fog of war (i.e., which tiles have been visited by the player), a random number generator, an entrance tuple that stores the position of the entrance to the dungeon, and some other properties such as the name of the dungeon, the total number of tiles, and the width and height of the map.

public class Dungeon 
{
     public string name;
     Tile[,] map;
     public Random rand;
     Tuple<int, int> entrance;
     int tilesTotal;
     int width;
     int height;
     //tiles
     Tile floorTile = new Tile('.', ConsoleColor.Gray, ConsoleColor.Black);
     Tile wallTile;
    public Dungeon(string p_name, int p_width, int p_height, Tile p_wallTile, Tile p_groundTile, float percentage)
     {
         name = p_name;
         rand = new Random(name.GetHashCode());
         wallTile = p_wallTile;
         groundTile = p_groundTile;
         tilesTotal = p_width * p_height;
         width = p_width;
         height = p_height;
         RandomWalk(width, height, percentage);
     }
}

We will need to define a walker class . The purpose of this class is to represent a walker that will move around a grid of tiles. Each walker has an X and Y position, which can be accessed using the "X" and "Y" properties. Additionally, each walker has a "walkerTile" object, which represents the tile that the walker places as it creates a path

class Walker
{
     public int X { get; set; }
     public int Y { get; set; }
     public Tile walkerTile;
     public Walker(int x, int y, int width, int height, Random random, Tile p_walkerTile)
     {
         while (x == 0 && y == 0)
         {
             if (random.Next(0, 2) == 0)
             {
                 x++;
             }
             else
             {
                 y++;
             }
         }
         while (x == width - 1 && y == height - 1)
         {
             if (random.Next(0, 2) == 0)
             {
                 x--;
             }
             else
             {
                 y--;
             }
          }
          X = x;
          Y = y;
          walkerTile = p_walkerTile;
     }
     public void Move(int width, int height, Random random)
     {
         int direction = random.Next(0, 4);
         if (direction == 0)
         {
             X--; 
         }
         else if (direction == 1)
         {
             X++;
         }
         else if (direction == 2)
         {
             Y--;
         }
         else if (direction == 3)
         {
             Y++;
         }
         X = X < 1 ? 1 : X;
         X = X > width - 2 ? width - 2 : X; 
         Y = Y < 1 ? 1 : Y;
         Y = Y > height - 2 ? height - 2 : Y;
     }
 } 

In this code, we have a class called Walker, which has three public properties: X, Y, and walkerTile. The X and Y properties represent the position of the walker in a 2D grid, and the walkerTile property is an instance of a Tile class.

The Walker class has two methods: a constructor and a Move method. The constructor takes six arguments: x and y (the initial position of the walker), width and height (the size of the 2D grid), random (an instance of the Random class), and p_walkerTile (an instance of the Tile class that represents the walker).

The constructor makes sure that the walker starts on the out side edge of the map, excluding the corners. This how I want my map to generate, but you could make the walker start anywhere in the map you want.

The Move method takes three arguments: width, height, and random. It first generates a random integer between 0 and 3 (inclusive), which represents a direction for the walker to move in (up, down, left, or right). Depending on the direction, it updates the X or Y property of the Walker instance.

Finally, the Move method checks if the walker is at the edge of the 2D grid (i.e., if its X or Y property is either 0 or width - 1 for X, or either 0 or height - 1 for Y). If it is, it moves the walker back one

Now that we have a Walker class, we can create a function that will use it to randomly generate a dungeon. We will call this function RandomWalk. The RandomWalk function will take in the width, height, and percentage of the dungeon to generate, as well as the groundTile and wallTile objects to use for the floor and walls of the dungeon.

We will start by initializing an empty 2D array of Tile objects that will represent the dungeon map. We will then set all the tiles to the wallTile object to start with.

Next, we will create a Walker object that will start at a random position on the edge of the map. We will use this Walker object to randomly generate the dungeon by making it walk around the map and setting the tiles it steps on to the groundTile object. We will also save the position of the first tile the Walker steps on as the entrance to the dungeon.

Finally, we will stop generating tiles when the desired percentage of tiles have been converted to groundTile objects.

Here's what the RandomWalk function should look like:

void RandomWalk(int width, int height, float percentage) 
{
     // Initialize the map array
     map = new Tile[width, height];
     for (int y = 0; y < height; y++)
     {
         for (int x = 0; x < width; x++)
         {
             map[x, y] = wallTile;
         }
     }
     // Create the first Walker object
     int randX = rand.Next(0, 2) == 1 ? rand.Next(0, 2) * (width - 1) : rand.Next(1, width - 2);
     int randY = rand.Next(0, 2) == 1 ? rand.Next(0, 2) * (height - 1) : rand.Next(1, height - 2);
     Walker walker = new Walker(randX, randY, width, height, rand, groundTile);
     // Set the entrance tile     map[walker.X, walker.Y] = walker.walkerTile; 
     entrence = new Tuple<int, int>(walker.X, walker.Y);
     // Generate the rest of the dungeon
     int tilesPlaced = 0;
     while (tilesPlaced < tilesTotal * percentage)
     {
         while (map[walker.X, walker.Y] != wallTile)
         {
             walker.Move(width, height, rand);
         }
         map[walker.X, walker.Y] = walker.walkerTile;
         tilesPlaced++;
     }
 } 

Finally, we can print out the generated dungeon by adding the following code to the Dungeon class:

public void Print() 
{
     Console.WriteLine(name + " " + name.GetHashCode());
     for (int y = 0; y < map.GetLength(1); y++)
     {
         for (int x = 0; x < map.GetLength(0); x++)
         {
             map[x, y].Print();
         }
         Console.WriteLine();
     }
} 

This method will iterate over the tiles in the map array and call the Print method on each tile to print it to the console.

Now we can create a new instance of the Dungeon class and print out the generated dungeon. Add the following code to the Main method:

var dungeon = new Dungeon(
    "My Dungeon", 
    30, 
    10, 
    new Tile('#', ConsoleColor.DarkGray), 
    new Tile('.', ConsoleColor.DarkGray), 
    0.3f
    ); 
dungeon.Print(); 

This code will create a new dungeon with a name of "My Dungeon", a size of 30 by 10 tiles, a wall tile with the '#' character and a dark gray color, a ground tile with the '.' character and a dark gray color, a percentage of 0.3 for the amount of tiles to generate, and a maximum amount of money of 100. Then it will print out the generated dungeon to the console.

Congratulations, you have successfully created a random walker algorithm to generate a dungeon! You can now play around with the code to see what happens if you change the size of the dungeon, the percentage of tiles to generate, or the types of tiles used. You could also add new features to the dungeon, such as enemies, traps, or treasure. The possibilities are endless!

Leave a comment

Log in with itch.io to leave a comment.