-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cd71249
commit b65f7d3
Showing
3 changed files
with
207 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Labyrinthian | ||
{ | ||
/// <summary> | ||
/// A wrapper of the <see cref="Labyrinthian.Maze" /> class which supports directed edges. | ||
/// </summary> | ||
/// <remarks> | ||
/// Note that this class does not track changes to the maze edges made outside of it. | ||
/// </remarks> | ||
public class DirectedMaze | ||
{ | ||
private readonly Dictionary<MazeEdge, bool> _edgesDirections; | ||
|
||
/// <summary> | ||
/// Get an original maze. | ||
/// </summary> | ||
public Maze Maze { get; private set; } | ||
|
||
/// <summary> | ||
/// Gets a dictionary which defines a direction of each maze edge. | ||
/// </summary> | ||
/// <remarks> | ||
/// If an edge has a value <see langword="false" />, then direction is Cell1 -> Cell2, | ||
/// otherwise it's Cell2 -> Cell1. | ||
/// </remarks> | ||
public IReadOnlyDictionary<MazeEdge, bool> EdgesDirections => _edgesDirections; | ||
|
||
/// <summary> | ||
/// Construct a directed maze based on a maze. | ||
/// Note that existing edges will be ignored and will not appear in | ||
/// <see cref="EdgesDirections" /> unless added within this class. | ||
/// </summary> | ||
/// <param name="maze">A maze to be based on.</param> | ||
public DirectedMaze(Maze maze) | ||
{ | ||
_edgesDirections = new Dictionary<MazeEdge, bool>(); | ||
Maze = maze; | ||
} | ||
|
||
/// <summary> | ||
/// Add/update a relation cell1 -> cell2 and carve a passage between the cells in the maze. | ||
/// </summary> | ||
/// <param name="cell1">A start cell.</param> | ||
/// <param name="cell2">An end cell.</param> | ||
public void ConnectCells(MazeCell cell1, MazeCell cell2) | ||
{ | ||
// Ensure that cell1.Index < cell2.Index | ||
var edge = MazeEdge.GetMinMax(cell1, cell2); | ||
// Select the right direction | ||
_edgesDirections[edge] = cell1.Index > cell2.Index; | ||
|
||
Maze.ConnectCells(cell1, cell2); | ||
} | ||
|
||
/// <summary> | ||
/// Remove a relation cell1 -> cell2 and create a wall between the cells in the maze. | ||
/// </summary> | ||
/// <remarks> | ||
/// If relation cell2 -> cell1 exists instead of cell1 -> cell2, then nothing will change. | ||
/// </remarks> | ||
/// <param name="cell1">A start cell.</param> | ||
/// <param name="cell2">An end cell.</param> | ||
public void DisconnectCells(MazeCell cell1, MazeCell cell2) | ||
{ | ||
var edge = MazeEdge.GetMinMax(cell1, cell2); | ||
if (_edgesDirections.TryGetValue(edge, out bool value) && | ||
value == cell1.Index > cell2.Index) | ||
{ | ||
_edgesDirections.Remove(edge); | ||
Maze.BlockCells(cell1, cell2); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
namespace Labyrinthian.Generation | ||
{ | ||
/// <summary> | ||
/// An interface for generators which use directed graphs. | ||
/// </summary> | ||
public interface IDirectedGraphGenerator | ||
{ | ||
/// <summary> | ||
/// Get a directed maze. | ||
/// </summary> | ||
public DirectedMaze DirectedMaze { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Labyrinthian.Generation | ||
{ | ||
/// <summary> | ||
/// Maze generator that uses Origin Shift algorithm. | ||
/// Algorithm's author: https://github.com/CaptainLuma. | ||
/// </summary> | ||
public class OriginShiftGeneration : MazeGenerator, IDirectedGraphGenerator | ||
{ | ||
/// <summary> | ||
/// Gets the max algorithm's iterations number. | ||
/// If the value is negative, generator generates a maze forever. | ||
/// </summary> | ||
public int MaxIterations { get; private set; } | ||
|
||
public DirectedMaze DirectedMaze { get; private set; } | ||
|
||
/// <summary> | ||
/// Construct a generator with a default seed. | ||
/// </summary> | ||
/// <param name="maze">Maze used for generation.</param> | ||
/// <param name="initialCell">Cell that will be current at the start of generation(optional).</param> | ||
/// <param name="maxIterations"> | ||
/// Max algorithm's iterations number. If negative, maze doesn't stop generating, | ||
/// if <see langword="null"/> then a default number of iterations is used based on the number of maze cells. | ||
/// </param> | ||
public OriginShiftGeneration(Maze maze, int? maxIterations = null, MazeCell? initialCell = null) : | ||
this(maze, Environment.TickCount, maxIterations, initialCell) | ||
{ } | ||
|
||
/// <summary> | ||
/// Construct a generator with specified seed. | ||
/// </summary> | ||
/// <param name="maze">Maze used for generation.</param> | ||
/// <param name="seed">Seed for random numbers generator.</param> | ||
/// <param name="initialCell">Cell that will be current at the start of generation(optional).</param> | ||
/// <param name="maxIterations"> | ||
/// Max algorithm's iterations number. If negative, maze doesn't stop generating, | ||
/// if <see langword="null"/> then a default number of iterations is used based on the number of maze cells. | ||
/// </param> | ||
public OriginShiftGeneration(Maze maze, int seed, int? maxIterations = null, MazeCell? initialCell = null) : | ||
base(maze, seed, initialCell, true) | ||
{ | ||
DirectedMaze = new DirectedMaze(Maze); | ||
MaxIterations = maxIterations ?? Maze.Cells.Length * 10; | ||
} | ||
|
||
private void ConnectToOrigin(MazeCell origin) | ||
{ | ||
// Use BFS to connect all cells to the origin | ||
Queue<MazeCell> cells = new Queue<MazeCell>(); | ||
cells.Enqueue(origin); | ||
var visited = new MarkedCells(Maze, false); | ||
visited[origin] = true; | ||
while (cells.Count > 0) | ||
{ | ||
var currentCell = cells.Dequeue(); | ||
foreach (var neighbor in currentCell.Neighbors) | ||
{ | ||
if (!visited[neighbor]) | ||
{ | ||
visited[neighbor] = true; | ||
cells.Enqueue(neighbor); | ||
|
||
DirectedMaze.ConnectCells(neighbor, currentCell); | ||
} | ||
} | ||
} | ||
} | ||
|
||
protected override IEnumerable<Maze> Generation() | ||
{ | ||
// https://github.com/CaptainLuma/New-Maze-Generating-Algorithm | ||
|
||
// Choose the initial cell and mark it as origin | ||
SelectedCell ??= GetRandomCell(); | ||
// Make a starting perfect maze | ||
ConnectToOrigin(SelectedCell); | ||
|
||
yield return Maze; | ||
|
||
for (int i = 0; i < MaxIterations || MaxIterations < 0; i++) | ||
{ | ||
// Have the origin node, point to a random neighboring node | ||
int neighborIndex = Rnd.Next(0, SelectedCell.Neighbors.Length); | ||
var selectedNeighbor = SelectedCell.Neighbors[neighborIndex]; | ||
DirectedMaze.ConnectCells(SelectedCell, selectedNeighbor); | ||
|
||
// That neigboring node becomes the new origin node | ||
SelectedCell = selectedNeighbor; | ||
|
||
// Have the new origin node point nowhere | ||
foreach (var neighbor in SelectedCell.Neighbors) | ||
{ | ||
DirectedMaze.DisconnectCells(SelectedCell, neighbor); | ||
} | ||
|
||
yield return Maze; | ||
} | ||
} | ||
|
||
public override Maze Generate() | ||
{ | ||
if (MaxIterations < 0) | ||
{ | ||
throw new InvalidOperationException("To prevent an infinite loop, Generate cannot be called when MaxIterations is set to a negative number."); | ||
} | ||
|
||
return base.Generate(); | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return "Origin Shift"; | ||
} | ||
} | ||
} |