Skip to content

Instantly share code, notes, and snippets.

@devsleeper
Created June 1, 2022 04:17
Show Gist options
  • Select an option

  • Save devsleeper/d43e4d83b2372d7fc5060868df9c4e3a to your computer and use it in GitHub Desktop.

Select an option

Save devsleeper/d43e4d83b2372d7fc5060868df9c4e3a to your computer and use it in GitHub Desktop.
A (Unty) C# Conversion of the C++ Recursive Backtrack Algorithm provided by javid9x
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SimpleMazeGenerator : MonoBehaviour
{
public GameObject WallPrefab;
public GameObject FloorPrefab;
public GameObject CenterPrefab;
[SerializeField] private int m_nMazeWidth = 40;
[SerializeField] private int m_nMazeHeight = 25;
private BitField[] m_maze;
// Some bit fields for convenience
[Flags]
private enum BitField
{
CELL_PATH_N = 0x01,
CELL_PATH_E = 0x02,
CELL_PATH_S = 0x04,
CELL_PATH_W = 0x08,
CELL_VISITED = 0x10,
}
// Algorithm variables
[SerializeField] private int m_nVisitedCells;
private Stack<Tuple<int, int>> m_stack = new Stack<Tuple<int, int>>(); // (x, z) coordinate pairs
[SerializeField] private int m_nPathWidth = 3;
void Start()
{
// Maze parameters
m_maze = new BitField[m_nMazeWidth * m_nMazeHeight];
// init (unnecessary)
for (int i = 0; i < m_nMazeWidth * m_nMazeHeight; i++)
m_maze[i] = 0x00;
m_nPathWidth = 3;
// Choose a starting cell
int x = UnityEngine.Random.Seed.Next() % m_nMazeWidth;
int y = UnityEngine.Random.Seed.Next() % m_nMazeHeight;
m_stack.Push(Tuple.Create(x, y));
m_maze[y * m_nMazeWidth + x] = BitField.CELL_VISITED;
m_nVisitedCells = 1;
//StartCoroutine(nameof(MazeGeneration));
MazeGeneration();
}
private bool Initialized;
// Called by olcConsoleGameEngine
//IEnumerator MazeGeneration()
void MazeGeneration()
{
while (m_nVisitedCells < m_nMazeWidth * m_nMazeHeight)
{
// Little lambda function to calculate index of a neighbor in a readable way
Func<int, int, int> offset = (int x, int y) =>
{
return (m_stack.Peek().Item2 + y) * m_nMazeWidth + (m_stack.Peek().Item1 + x);
};
// Do Maze Algorithm
if (m_nVisitedCells < m_nMazeWidth * m_nMazeHeight)
{
// Create a set of unvisted neighbours
List<int> neighbours = new List<int>();
// North neighbour
if (m_stack.Peek().Item2 > 0 && (m_maze[offset(0, -1)] & BitField.CELL_VISITED) == 0)
{
neighbours.Add(0);
}
// East neighbour
if (m_stack.Peek().Item1 < m_nMazeWidth - 1 && (m_maze[offset(1, 0)] & BitField.CELL_VISITED) == 0)
{
neighbours.Add(1);
}
// South neighbour
if (m_stack.Peek().Item2 < m_nMazeHeight - 1 && (m_maze[offset(0, 1)] & BitField.CELL_VISITED) == 0)
{
neighbours.Add(2);
}
// West neighbour
if (m_stack.Peek().Item1 > 0 && (m_maze[offset(-1, 0)] & BitField.CELL_VISITED) == 0)
{
neighbours.Add(3);
}
// Are there any neighbours available?
if (neighbours.Count > 0)
{
// Choose one available neighbour at random
int next_cell_dir = neighbours[UnityEngine.Random.Seed.Next() % neighbours.Count];
// Create a path between the neighbour and the current cell
switch (next_cell_dir)
{
case 0: // North
m_maze[offset(0, -1)] |= BitField.CELL_VISITED | BitField.CELL_PATH_S;
m_maze[offset(0, 0)] |= BitField.CELL_PATH_N;
m_stack.Push(Tuple.Create((m_stack.Peek().Item1 + 0), (m_stack.Peek().Item2 - 1)));
break;
case 1: // East
m_maze[offset(+1, 0)] |= BitField.CELL_VISITED | BitField.CELL_PATH_W;
m_maze[offset(0, 0)] |= BitField.CELL_PATH_E;
m_stack.Push(Tuple.Create((m_stack.Peek().Item1 + 1), (m_stack.Peek().Item2 + 0)));
break;
case 2: // South
m_maze[offset(0, +1)] |= BitField.CELL_VISITED | BitField.CELL_PATH_N;
m_maze[offset(0, 0)] |= BitField.CELL_PATH_S;
m_stack.Push(Tuple.Create((m_stack.Peek().Item1 + 0), (m_stack.Peek().Item2 + 1)));
break;
case 3: // West
m_maze[offset(-1, 0)] |= BitField.CELL_VISITED | BitField.CELL_PATH_E;
m_maze[offset(0, 0)] |= BitField.CELL_PATH_W;
m_stack.Push(Tuple.Create((m_stack.Peek().Item1 - 1), (m_stack.Peek().Item2 + 0)));
break;
}
m_nVisitedCells++;
}
else
{
// No available neighbours so backtrack!
m_stack.Pop();
}
}
}
Initialized = true;
DrawMaze();
}
// === DRAWING STUFF ===
void DrawMaze()
{
// Draw Maze
for (int x = 0; x < m_nMazeWidth; x++)
{
for (int y = 0; y < m_nMazeHeight; y++)
{
// Each cell is inflated by m_nPathWidth, so fill it in
for (int py = 0; py < m_nPathWidth; py++)
{
for (int px = 0; px < m_nPathWidth; px++)
{
if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_VISITED))
{
if ((py + px) % 2 == 0) // even cell!
Instantiate(CenterPrefab, new Vector3(x * (m_nPathWidth + 1) + px, 0, y * (m_nPathWidth + 1) + py), CenterPrefab.transform.localRotation);
else
Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + px, 0, y * (m_nPathWidth + 1) + py), FloorPrefab.transform.localRotation);
//Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_WHITE); // Draw Cell
}
else
{
Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + px, 0, y * (m_nPathWidth + 1) + py), FloorPrefab.transform.localRotation);
//Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_BLUE); // Draw Cell
}
}
}
// Draw passageways between cells
for (int p = 0; p < m_nPathWidth; p++)
{
if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_S))
{
Instantiate(WallPrefab, new Vector3(x * (m_nPathWidth + 1) + p, 1, y * (m_nPathWidth + 1) + m_nPathWidth), WallPrefab.transform.localRotation);
//Draw(x * (m_nPathWidth + 1) + p, y * (m_nPathWidth + 1) + m_nPathWidth); // Draw South Passage
}
else if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_E) == false)
Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + p, 0, y * (m_nPathWidth + 1) + m_nPathWidth), FloorPrefab.transform.localRotation);
if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_E))
{
Instantiate(WallPrefab, new Vector3(x * (m_nPathWidth + 1) + m_nPathWidth, 1, y * (m_nPathWidth + 1) + p), WallPrefab.transform.localRotation);
//Draw(x * (m_nPathWidth + 1) + m_nPathWidth, y * (m_nPathWidth + 1) + p); // Draw East Passage
}
else if (m_maze[y * m_nMazeWidth + x].HasFlag(BitField.CELL_PATH_S) == false)
Instantiate(FloorPrefab, new Vector3(x * (m_nPathWidth + 1) + m_nPathWidth, 0, y * (m_nPathWidth + 1) + p), FloorPrefab.transform.localRotation);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment