C# recursive backtracker labyrinth generation & game
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
3
down vote
favorite
So for over a weekend I decided to write something rather simple. For maze generation I've followed this algorithm and aside from that I've made a little game, to go from A to B.
Everything works fine but I'm curious whether it's possible to improve/simplify my code or not? If you have any thoughts on readability or efficiency - please share.
Oh and also... Is there a way to properly restart game when it quits to menu? Obviously, I can't use constructor on IGame, and because of that I've made Initialize()
method, but is that ok? Because menu is in a loop Game object keeps all values from previous play... Is there a workaround?
Direction:
namespace Labyrinth
enum Direction
Top,
Right,
Bottom,
Left
Interfaces:
using System;
namespace Labyrinth
// Menu and start of the game
public interface IMainUserInterface
void StartGame(IGameLoop gameLoop, IGame game);
// Game process
public interface IGameLoop
void Run(IGame game);
// This interface should be implemented by game object
public interface IGame
void Initialize();
void DisplayField();
void DisplayPlayer();
void HandleKey(ConsoleKeyInfo cki);
bool IsWon();
MainUserInterface:
using System;
namespace Labyrinth
class MainUserInterface : IMainUserInterface
public void StartGame(IGameLoop gameLoop, IGame game)
Console.CursorVisible = false;
do
string key;
do
Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)
case "1":
game.Initialize();
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;
Console.Clear();
while (true);
GameLoop:
using System;
using System.Diagnostics;
using System.Threading;
namespace Labyrinth
class GameLoop : IGameLoop
Stopwatch stopwatch;
public void Run(IGame game)
stopwatch = new Stopwatch();
// Display generation of a labyrinth
game.DisplayField();
// Start timer
stopwatch.Start();
do
if (Console.KeyAvailable == true)
game.HandleKey(Console.ReadKey(true));
game.DisplayPlayer();
while (!game.IsWon());
// Stop timer
stopwatch.Stop();
Thread.Sleep(500);
TimeSpan time = stopwatch.Elapsed;
string elapsedTime = string.Format("0:00:1:00.2:00", time.Minutes, time.Seconds, time.Milliseconds / 10);
// Show elapsed time
Console.WriteLine("nYou've completed a labyrinth in 0!n", elapsedTime);
Console.WriteLine("Press any button to continue or Escape to quit to menu.");
ConsoleKeyInfo cki = Console.ReadKey(true);
Console.Clear();
if (cki.Key != ConsoleKey.Escape)
game.Initialize();
Run(game);
Game:
using System;
namespace Labyrinth
class Game : IGame
public int Height get;
public int Width get;
public char PlayerSym get;
public int GenSpeed get;
Labyrinth labyrinth;
Player player;
Cell Start get;
Cell End get;
public Game(int height, int width, char playerSym, int genSpeed)
Height = height;
Width = width;
PlayerSym = playerSym;
GenSpeed = genSpeed;
Initialize();
Start = new Cell(1, 1);
End = new Cell(Height - 2, Width - 2);
public void Initialize()
labyrinth = new Labyrinth(Height, Width, ConsoleColor.DarkGreen, ConsoleColor.DarkBlue);
player = new Player(PlayerSym, labyrinth.Walls);
// Display field, each time generating a new one
public void DisplayField()
labyrinth.Generate(GenSpeed);
Start.Display(ConsoleColor.Green, ConsoleColor.Green);
End.Display(ConsoleColor.Red, ConsoleColor.Red);
// Display player cell
public void DisplayPlayer()
player.Display(ConsoleColor.Green, ConsoleColor.DarkMagenta);
// Move player
public void HandleKey(ConsoleKeyInfo cki)
player.Erase(labyrinth.FieldColor, labyrinth.FieldColor);
player.HandleKey(cki);
// Game is won when player gets to end cell
public bool IsWon()
return player.IsCollidingWith(End);
Point:
using System;
namespace Labyrinth
abstract class Point : Color
// Point value and coordinates
public virtual char Value get; set;
public virtual int X get; set;
public virtual int Y get; set;
public Point(char value, int x, int y)
Value = value;
X = x;
Y = y;
// Display point with certain colors
public virtual void Display(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(Value.ToString(), fgColor, bgColor);
// Erase point with certain colors
public virtual void Erase(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(" ", fgColor, bgColor);
// Check if point colliding with another point
public virtual bool IsCollidingWith(Point p)
return X == p.X && Y == p.Y;
Cell:
using System;
namespace Labyrinth
class Cell : Point
// 4 walls on each direction
public bool walls;
public bool isVisited;
public Cell(int x, int y) : base(' ', x, y)
walls = new bool true, true, true, true ;
isVisited = false;
public void Display(ConsoleColor bgColor)
// Not displaying cell that hasn't been visited
if (isVisited)
Display(Console.ForegroundColor, bgColor);
else
Display(Console.ForegroundColor, Console.BackgroundColor);
// Displaying available walls around each cell
DisplayWalls(bgColor);
void DisplayWalls(ConsoleColor bgColor)
Cell c = null;
for (int i = 0; i < walls.Length; i++)
if (i == (int)Direction.Top)
c = new Cell(X - 1, Y);
else if (i == (int)Direction.Right)
c = new Cell(X, Y + 1);
else if (i == (int)Direction.Bottom)
c = new Cell(X + 1, Y);
else if (i == (int)Direction.Left)
c = new Cell(X, Y - 1);
// If wall is enabled - don't display anything
// If wall is disabled - display as normal cell
if (walls[i])
c.Display(Console.ForegroundColor, Console.BackgroundColor);
else
c.Display(Console.ForegroundColor, bgColor);
public void Highlight()
Display(ConsoleColor.Green, ConsoleColor.Green);
Player:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Labyrinth
class Player : Point
List<Cell> walls;
// Walls initialization, value and coordinates go to base constructor
public Player(char value, List<Cell> walls) : base(value, 1, 1)
this.walls = walls;
// Handle pressed key
public void HandleKey(ConsoleKeyInfo cki)
switch (cki.Key)
case ConsoleKey.D:
case ConsoleKey.RightArrow:
Y += 1;
if (IsCollidingWithWall())
Y -= 1;
break;
case ConsoleKey.A:
case ConsoleKey.LeftArrow:
Y -= 1;
if (IsCollidingWithWall())
Y += 1;
break;
case ConsoleKey.S:
case ConsoleKey.DownArrow:
X += 1;
if (IsCollidingWithWall())
X -= 1;
break;
case ConsoleKey.W:
case ConsoleKey.UpArrow:
X -= 1;
if (IsCollidingWithWall())
X += 1;
break;
// Check if walls list contains player coordinates
bool IsCollidingWithWall()
return walls.Any(c => c.IsCollidingWith(this));
Color:
using System;
namespace Labyrinth
abstract class Color
// Any hereditary class can use this function to display string in color
protected void ColorDisplay(string str, ConsoleColor fgColor, ConsoleColor bgColor)
ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;
Console.ForegroundColor = fgColor;
Console.BackgroundColor = bgColor;
Console.Write(str);
Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;
Labyrinth:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Labyrinth
class Labyrinth
// Field parameters
public int Height get;
public int Width get;
// Cells for player to move on
public List<Cell> Cells get;
// Walls to form an actual labyrinth
public List<Cell> Walls get;
// Beginning generation with this cell
Cell currentCell;
// Stack for recursive generation algorithm
Stack<Cell> stack = new Stack<Cell>();
// Colors
public ConsoleColor FieldColor get;
public ConsoleColor WallsColor get;
public Labyrinth(int height, int width, ConsoleColor fieldColor, ConsoleColor wallsColor)
Cells = new List<Cell>();
Walls = new List<Cell>();
// Setting an odd number even if it's even
Height = height % 2 == 0 ? height - 1 : height;
Width = width % 2 == 0 ? width - 1 : width;
// Adding cells
for (int i = 1; i < Height; i += 2)
for (int j = 1; j < Width; j += 2)
Cells.Add(new Cell(i, j));
// Adding walls
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
Walls.Add(new Cell(i, j));
// Adding colors
FieldColor = fieldColor;
WallsColor = wallsColor;
// Always beginning generation with a first cell (1, 1)
currentCell = Cells.First();
// Generating a labyrinth with latency to see the process
public void Generate(int latency)
do
// Always marking current cell as visited
currentCell.isVisited = true;
// Getting random neighbor cell as a next one
Cell nextCell = GetNeighbor(currentCell);
// If there is at least one available neighbor - remove walls between current and next cell
if (nextCell != null)
RemoveWalls(currentCell, nextCell);
// Removing wall that is equal to current cell
foreach (Cell wall in Walls)
if (wall.X == currentCell.X && wall.Y == currentCell.Y)
Walls.Remove(wall);
break;
currentCell.Display(FieldColor);
// If there is available next cell - pushing current cell to stack and assigning next to current
// Else - backtracking to cell that has at least one available neighbor
if (nextCell != null)
stack.Push(currentCell);
currentCell = nextCell;
else if (stack.Count > 0)
currentCell = stack.Pop();
// Highlight current cell
currentCell.Display(ConsoleColor.Green, ConsoleColor.Green);
Thread.Sleep(latency);
// Algorithm is done when current cell is back at the beginning
while (!IsCompleted());
// Display walls
foreach (Cell c in Walls)
c.Display(WallsColor, WallsColor);
void RemoveWalls(Cell a, Cell b)
// Assigning coordinates of a wall between a and b
int x = (a.X != b.X) ? (a.X > b.X ? a.X - 1 : a.X + 1) : a.X;
int y = (a.Y != b.Y) ? (a.Y > b.Y ? a.Y - 1 : a.Y + 1) : a.Y;
// Removing wall
foreach (Cell wall in Walls)
if (wall.X == x && wall.Y == y)
Walls.Remove(wall);
break;
// Disabling corresponding wall for each cell
if (a.X - b.X == 2)
a.walls[(int)Direction.Top] = false;
b.walls[(int)Direction.Bottom] = false;
else if (a.X - b.X == -2)
a.walls[(int)Direction.Bottom] = false;
b.walls[(int)Direction.Top] = false;
if (a.Y - b.Y == 2)
a.walls[(int)Direction.Left] = false;
b.walls[(int)Direction.Right] = false;
else if (a.Y - b.Y == -2)
a.walls[(int)Direction.Right] = false;
b.walls[(int)Direction.Left] = false;
Cell GetNeighbor(Cell cell)
Random rand = new Random();
List<Cell> neighbors = new List<Cell>();
// Assigning available neighbor cells
Cell top = (cell.X - 2 > 0) ? Cells.Find(c => c.X == cell.X - 2 && c.Y == cell.Y) : null;
Cell right = (cell.Y + 2 < Width - 1) ? Cells.Find(c => c.Y == cell.Y + 2 && c.X == cell.X) : null;
Cell bottom = (cell.X + 2 < Height - 1) ? Cells.Find(c => c.X == cell.X + 2 && c.Y == cell.Y) : null;
Cell left = (cell.Y - 2 > 0) ? Cells.Find(c => c.Y == cell.Y - 2 && c.X == cell.X) : null;
if (top != null && !top.isVisited)
neighbors.Add(top);
if (right != null && !right.isVisited)
neighbors.Add(right);
if (bottom != null && !bottom.isVisited)
neighbors.Add(bottom);
if (left != null && !left.isVisited)
neighbors.Add(left);
// Returning random neigbor from a list
if (neighbors.Count > 0)
int index = rand.Next(neighbors.Count);
return neighbors[index];
// Else return no neighbor
return null;
// If stack is empty then labyrinth is generated successfully
bool IsCompleted()
return stack.Count == 0;
Program:
using System;
namespace Labyrinth
class Program
static void Main()
Console.CursorVisible = false;
IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game(21, 35, '@', 10);
userInterface.StartGame(gameLoop, game);
c# algorithm recursion generator backtracking
add a comment |Â
up vote
3
down vote
favorite
So for over a weekend I decided to write something rather simple. For maze generation I've followed this algorithm and aside from that I've made a little game, to go from A to B.
Everything works fine but I'm curious whether it's possible to improve/simplify my code or not? If you have any thoughts on readability or efficiency - please share.
Oh and also... Is there a way to properly restart game when it quits to menu? Obviously, I can't use constructor on IGame, and because of that I've made Initialize()
method, but is that ok? Because menu is in a loop Game object keeps all values from previous play... Is there a workaround?
Direction:
namespace Labyrinth
enum Direction
Top,
Right,
Bottom,
Left
Interfaces:
using System;
namespace Labyrinth
// Menu and start of the game
public interface IMainUserInterface
void StartGame(IGameLoop gameLoop, IGame game);
// Game process
public interface IGameLoop
void Run(IGame game);
// This interface should be implemented by game object
public interface IGame
void Initialize();
void DisplayField();
void DisplayPlayer();
void HandleKey(ConsoleKeyInfo cki);
bool IsWon();
MainUserInterface:
using System;
namespace Labyrinth
class MainUserInterface : IMainUserInterface
public void StartGame(IGameLoop gameLoop, IGame game)
Console.CursorVisible = false;
do
string key;
do
Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)
case "1":
game.Initialize();
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;
Console.Clear();
while (true);
GameLoop:
using System;
using System.Diagnostics;
using System.Threading;
namespace Labyrinth
class GameLoop : IGameLoop
Stopwatch stopwatch;
public void Run(IGame game)
stopwatch = new Stopwatch();
// Display generation of a labyrinth
game.DisplayField();
// Start timer
stopwatch.Start();
do
if (Console.KeyAvailable == true)
game.HandleKey(Console.ReadKey(true));
game.DisplayPlayer();
while (!game.IsWon());
// Stop timer
stopwatch.Stop();
Thread.Sleep(500);
TimeSpan time = stopwatch.Elapsed;
string elapsedTime = string.Format("0:00:1:00.2:00", time.Minutes, time.Seconds, time.Milliseconds / 10);
// Show elapsed time
Console.WriteLine("nYou've completed a labyrinth in 0!n", elapsedTime);
Console.WriteLine("Press any button to continue or Escape to quit to menu.");
ConsoleKeyInfo cki = Console.ReadKey(true);
Console.Clear();
if (cki.Key != ConsoleKey.Escape)
game.Initialize();
Run(game);
Game:
using System;
namespace Labyrinth
class Game : IGame
public int Height get;
public int Width get;
public char PlayerSym get;
public int GenSpeed get;
Labyrinth labyrinth;
Player player;
Cell Start get;
Cell End get;
public Game(int height, int width, char playerSym, int genSpeed)
Height = height;
Width = width;
PlayerSym = playerSym;
GenSpeed = genSpeed;
Initialize();
Start = new Cell(1, 1);
End = new Cell(Height - 2, Width - 2);
public void Initialize()
labyrinth = new Labyrinth(Height, Width, ConsoleColor.DarkGreen, ConsoleColor.DarkBlue);
player = new Player(PlayerSym, labyrinth.Walls);
// Display field, each time generating a new one
public void DisplayField()
labyrinth.Generate(GenSpeed);
Start.Display(ConsoleColor.Green, ConsoleColor.Green);
End.Display(ConsoleColor.Red, ConsoleColor.Red);
// Display player cell
public void DisplayPlayer()
player.Display(ConsoleColor.Green, ConsoleColor.DarkMagenta);
// Move player
public void HandleKey(ConsoleKeyInfo cki)
player.Erase(labyrinth.FieldColor, labyrinth.FieldColor);
player.HandleKey(cki);
// Game is won when player gets to end cell
public bool IsWon()
return player.IsCollidingWith(End);
Point:
using System;
namespace Labyrinth
abstract class Point : Color
// Point value and coordinates
public virtual char Value get; set;
public virtual int X get; set;
public virtual int Y get; set;
public Point(char value, int x, int y)
Value = value;
X = x;
Y = y;
// Display point with certain colors
public virtual void Display(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(Value.ToString(), fgColor, bgColor);
// Erase point with certain colors
public virtual void Erase(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(" ", fgColor, bgColor);
// Check if point colliding with another point
public virtual bool IsCollidingWith(Point p)
return X == p.X && Y == p.Y;
Cell:
using System;
namespace Labyrinth
class Cell : Point
// 4 walls on each direction
public bool walls;
public bool isVisited;
public Cell(int x, int y) : base(' ', x, y)
walls = new bool true, true, true, true ;
isVisited = false;
public void Display(ConsoleColor bgColor)
// Not displaying cell that hasn't been visited
if (isVisited)
Display(Console.ForegroundColor, bgColor);
else
Display(Console.ForegroundColor, Console.BackgroundColor);
// Displaying available walls around each cell
DisplayWalls(bgColor);
void DisplayWalls(ConsoleColor bgColor)
Cell c = null;
for (int i = 0; i < walls.Length; i++)
if (i == (int)Direction.Top)
c = new Cell(X - 1, Y);
else if (i == (int)Direction.Right)
c = new Cell(X, Y + 1);
else if (i == (int)Direction.Bottom)
c = new Cell(X + 1, Y);
else if (i == (int)Direction.Left)
c = new Cell(X, Y - 1);
// If wall is enabled - don't display anything
// If wall is disabled - display as normal cell
if (walls[i])
c.Display(Console.ForegroundColor, Console.BackgroundColor);
else
c.Display(Console.ForegroundColor, bgColor);
public void Highlight()
Display(ConsoleColor.Green, ConsoleColor.Green);
Player:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Labyrinth
class Player : Point
List<Cell> walls;
// Walls initialization, value and coordinates go to base constructor
public Player(char value, List<Cell> walls) : base(value, 1, 1)
this.walls = walls;
// Handle pressed key
public void HandleKey(ConsoleKeyInfo cki)
switch (cki.Key)
case ConsoleKey.D:
case ConsoleKey.RightArrow:
Y += 1;
if (IsCollidingWithWall())
Y -= 1;
break;
case ConsoleKey.A:
case ConsoleKey.LeftArrow:
Y -= 1;
if (IsCollidingWithWall())
Y += 1;
break;
case ConsoleKey.S:
case ConsoleKey.DownArrow:
X += 1;
if (IsCollidingWithWall())
X -= 1;
break;
case ConsoleKey.W:
case ConsoleKey.UpArrow:
X -= 1;
if (IsCollidingWithWall())
X += 1;
break;
// Check if walls list contains player coordinates
bool IsCollidingWithWall()
return walls.Any(c => c.IsCollidingWith(this));
Color:
using System;
namespace Labyrinth
abstract class Color
// Any hereditary class can use this function to display string in color
protected void ColorDisplay(string str, ConsoleColor fgColor, ConsoleColor bgColor)
ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;
Console.ForegroundColor = fgColor;
Console.BackgroundColor = bgColor;
Console.Write(str);
Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;
Labyrinth:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Labyrinth
class Labyrinth
// Field parameters
public int Height get;
public int Width get;
// Cells for player to move on
public List<Cell> Cells get;
// Walls to form an actual labyrinth
public List<Cell> Walls get;
// Beginning generation with this cell
Cell currentCell;
// Stack for recursive generation algorithm
Stack<Cell> stack = new Stack<Cell>();
// Colors
public ConsoleColor FieldColor get;
public ConsoleColor WallsColor get;
public Labyrinth(int height, int width, ConsoleColor fieldColor, ConsoleColor wallsColor)
Cells = new List<Cell>();
Walls = new List<Cell>();
// Setting an odd number even if it's even
Height = height % 2 == 0 ? height - 1 : height;
Width = width % 2 == 0 ? width - 1 : width;
// Adding cells
for (int i = 1; i < Height; i += 2)
for (int j = 1; j < Width; j += 2)
Cells.Add(new Cell(i, j));
// Adding walls
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
Walls.Add(new Cell(i, j));
// Adding colors
FieldColor = fieldColor;
WallsColor = wallsColor;
// Always beginning generation with a first cell (1, 1)
currentCell = Cells.First();
// Generating a labyrinth with latency to see the process
public void Generate(int latency)
do
// Always marking current cell as visited
currentCell.isVisited = true;
// Getting random neighbor cell as a next one
Cell nextCell = GetNeighbor(currentCell);
// If there is at least one available neighbor - remove walls between current and next cell
if (nextCell != null)
RemoveWalls(currentCell, nextCell);
// Removing wall that is equal to current cell
foreach (Cell wall in Walls)
if (wall.X == currentCell.X && wall.Y == currentCell.Y)
Walls.Remove(wall);
break;
currentCell.Display(FieldColor);
// If there is available next cell - pushing current cell to stack and assigning next to current
// Else - backtracking to cell that has at least one available neighbor
if (nextCell != null)
stack.Push(currentCell);
currentCell = nextCell;
else if (stack.Count > 0)
currentCell = stack.Pop();
// Highlight current cell
currentCell.Display(ConsoleColor.Green, ConsoleColor.Green);
Thread.Sleep(latency);
// Algorithm is done when current cell is back at the beginning
while (!IsCompleted());
// Display walls
foreach (Cell c in Walls)
c.Display(WallsColor, WallsColor);
void RemoveWalls(Cell a, Cell b)
// Assigning coordinates of a wall between a and b
int x = (a.X != b.X) ? (a.X > b.X ? a.X - 1 : a.X + 1) : a.X;
int y = (a.Y != b.Y) ? (a.Y > b.Y ? a.Y - 1 : a.Y + 1) : a.Y;
// Removing wall
foreach (Cell wall in Walls)
if (wall.X == x && wall.Y == y)
Walls.Remove(wall);
break;
// Disabling corresponding wall for each cell
if (a.X - b.X == 2)
a.walls[(int)Direction.Top] = false;
b.walls[(int)Direction.Bottom] = false;
else if (a.X - b.X == -2)
a.walls[(int)Direction.Bottom] = false;
b.walls[(int)Direction.Top] = false;
if (a.Y - b.Y == 2)
a.walls[(int)Direction.Left] = false;
b.walls[(int)Direction.Right] = false;
else if (a.Y - b.Y == -2)
a.walls[(int)Direction.Right] = false;
b.walls[(int)Direction.Left] = false;
Cell GetNeighbor(Cell cell)
Random rand = new Random();
List<Cell> neighbors = new List<Cell>();
// Assigning available neighbor cells
Cell top = (cell.X - 2 > 0) ? Cells.Find(c => c.X == cell.X - 2 && c.Y == cell.Y) : null;
Cell right = (cell.Y + 2 < Width - 1) ? Cells.Find(c => c.Y == cell.Y + 2 && c.X == cell.X) : null;
Cell bottom = (cell.X + 2 < Height - 1) ? Cells.Find(c => c.X == cell.X + 2 && c.Y == cell.Y) : null;
Cell left = (cell.Y - 2 > 0) ? Cells.Find(c => c.Y == cell.Y - 2 && c.X == cell.X) : null;
if (top != null && !top.isVisited)
neighbors.Add(top);
if (right != null && !right.isVisited)
neighbors.Add(right);
if (bottom != null && !bottom.isVisited)
neighbors.Add(bottom);
if (left != null && !left.isVisited)
neighbors.Add(left);
// Returning random neigbor from a list
if (neighbors.Count > 0)
int index = rand.Next(neighbors.Count);
return neighbors[index];
// Else return no neighbor
return null;
// If stack is empty then labyrinth is generated successfully
bool IsCompleted()
return stack.Count == 0;
Program:
using System;
namespace Labyrinth
class Program
static void Main()
Console.CursorVisible = false;
IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game(21, 35, '@', 10);
userInterface.StartGame(gameLoop, game);
c# algorithm recursion generator backtracking
1
Wow. A lot to digest in one sitting. One very minor comment: Top and Bottom are not directions as much as they are locations. I would suggest Up and Down instead.
â Rick Davin
Mar 1 at 20:12
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
So for over a weekend I decided to write something rather simple. For maze generation I've followed this algorithm and aside from that I've made a little game, to go from A to B.
Everything works fine but I'm curious whether it's possible to improve/simplify my code or not? If you have any thoughts on readability or efficiency - please share.
Oh and also... Is there a way to properly restart game when it quits to menu? Obviously, I can't use constructor on IGame, and because of that I've made Initialize()
method, but is that ok? Because menu is in a loop Game object keeps all values from previous play... Is there a workaround?
Direction:
namespace Labyrinth
enum Direction
Top,
Right,
Bottom,
Left
Interfaces:
using System;
namespace Labyrinth
// Menu and start of the game
public interface IMainUserInterface
void StartGame(IGameLoop gameLoop, IGame game);
// Game process
public interface IGameLoop
void Run(IGame game);
// This interface should be implemented by game object
public interface IGame
void Initialize();
void DisplayField();
void DisplayPlayer();
void HandleKey(ConsoleKeyInfo cki);
bool IsWon();
MainUserInterface:
using System;
namespace Labyrinth
class MainUserInterface : IMainUserInterface
public void StartGame(IGameLoop gameLoop, IGame game)
Console.CursorVisible = false;
do
string key;
do
Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)
case "1":
game.Initialize();
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;
Console.Clear();
while (true);
GameLoop:
using System;
using System.Diagnostics;
using System.Threading;
namespace Labyrinth
class GameLoop : IGameLoop
Stopwatch stopwatch;
public void Run(IGame game)
stopwatch = new Stopwatch();
// Display generation of a labyrinth
game.DisplayField();
// Start timer
stopwatch.Start();
do
if (Console.KeyAvailable == true)
game.HandleKey(Console.ReadKey(true));
game.DisplayPlayer();
while (!game.IsWon());
// Stop timer
stopwatch.Stop();
Thread.Sleep(500);
TimeSpan time = stopwatch.Elapsed;
string elapsedTime = string.Format("0:00:1:00.2:00", time.Minutes, time.Seconds, time.Milliseconds / 10);
// Show elapsed time
Console.WriteLine("nYou've completed a labyrinth in 0!n", elapsedTime);
Console.WriteLine("Press any button to continue or Escape to quit to menu.");
ConsoleKeyInfo cki = Console.ReadKey(true);
Console.Clear();
if (cki.Key != ConsoleKey.Escape)
game.Initialize();
Run(game);
Game:
using System;
namespace Labyrinth
class Game : IGame
public int Height get;
public int Width get;
public char PlayerSym get;
public int GenSpeed get;
Labyrinth labyrinth;
Player player;
Cell Start get;
Cell End get;
public Game(int height, int width, char playerSym, int genSpeed)
Height = height;
Width = width;
PlayerSym = playerSym;
GenSpeed = genSpeed;
Initialize();
Start = new Cell(1, 1);
End = new Cell(Height - 2, Width - 2);
public void Initialize()
labyrinth = new Labyrinth(Height, Width, ConsoleColor.DarkGreen, ConsoleColor.DarkBlue);
player = new Player(PlayerSym, labyrinth.Walls);
// Display field, each time generating a new one
public void DisplayField()
labyrinth.Generate(GenSpeed);
Start.Display(ConsoleColor.Green, ConsoleColor.Green);
End.Display(ConsoleColor.Red, ConsoleColor.Red);
// Display player cell
public void DisplayPlayer()
player.Display(ConsoleColor.Green, ConsoleColor.DarkMagenta);
// Move player
public void HandleKey(ConsoleKeyInfo cki)
player.Erase(labyrinth.FieldColor, labyrinth.FieldColor);
player.HandleKey(cki);
// Game is won when player gets to end cell
public bool IsWon()
return player.IsCollidingWith(End);
Point:
using System;
namespace Labyrinth
abstract class Point : Color
// Point value and coordinates
public virtual char Value get; set;
public virtual int X get; set;
public virtual int Y get; set;
public Point(char value, int x, int y)
Value = value;
X = x;
Y = y;
// Display point with certain colors
public virtual void Display(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(Value.ToString(), fgColor, bgColor);
// Erase point with certain colors
public virtual void Erase(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(" ", fgColor, bgColor);
// Check if point colliding with another point
public virtual bool IsCollidingWith(Point p)
return X == p.X && Y == p.Y;
Cell:
using System;
namespace Labyrinth
class Cell : Point
// 4 walls on each direction
public bool walls;
public bool isVisited;
public Cell(int x, int y) : base(' ', x, y)
walls = new bool true, true, true, true ;
isVisited = false;
public void Display(ConsoleColor bgColor)
// Not displaying cell that hasn't been visited
if (isVisited)
Display(Console.ForegroundColor, bgColor);
else
Display(Console.ForegroundColor, Console.BackgroundColor);
// Displaying available walls around each cell
DisplayWalls(bgColor);
void DisplayWalls(ConsoleColor bgColor)
Cell c = null;
for (int i = 0; i < walls.Length; i++)
if (i == (int)Direction.Top)
c = new Cell(X - 1, Y);
else if (i == (int)Direction.Right)
c = new Cell(X, Y + 1);
else if (i == (int)Direction.Bottom)
c = new Cell(X + 1, Y);
else if (i == (int)Direction.Left)
c = new Cell(X, Y - 1);
// If wall is enabled - don't display anything
// If wall is disabled - display as normal cell
if (walls[i])
c.Display(Console.ForegroundColor, Console.BackgroundColor);
else
c.Display(Console.ForegroundColor, bgColor);
public void Highlight()
Display(ConsoleColor.Green, ConsoleColor.Green);
Player:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Labyrinth
class Player : Point
List<Cell> walls;
// Walls initialization, value and coordinates go to base constructor
public Player(char value, List<Cell> walls) : base(value, 1, 1)
this.walls = walls;
// Handle pressed key
public void HandleKey(ConsoleKeyInfo cki)
switch (cki.Key)
case ConsoleKey.D:
case ConsoleKey.RightArrow:
Y += 1;
if (IsCollidingWithWall())
Y -= 1;
break;
case ConsoleKey.A:
case ConsoleKey.LeftArrow:
Y -= 1;
if (IsCollidingWithWall())
Y += 1;
break;
case ConsoleKey.S:
case ConsoleKey.DownArrow:
X += 1;
if (IsCollidingWithWall())
X -= 1;
break;
case ConsoleKey.W:
case ConsoleKey.UpArrow:
X -= 1;
if (IsCollidingWithWall())
X += 1;
break;
// Check if walls list contains player coordinates
bool IsCollidingWithWall()
return walls.Any(c => c.IsCollidingWith(this));
Color:
using System;
namespace Labyrinth
abstract class Color
// Any hereditary class can use this function to display string in color
protected void ColorDisplay(string str, ConsoleColor fgColor, ConsoleColor bgColor)
ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;
Console.ForegroundColor = fgColor;
Console.BackgroundColor = bgColor;
Console.Write(str);
Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;
Labyrinth:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Labyrinth
class Labyrinth
// Field parameters
public int Height get;
public int Width get;
// Cells for player to move on
public List<Cell> Cells get;
// Walls to form an actual labyrinth
public List<Cell> Walls get;
// Beginning generation with this cell
Cell currentCell;
// Stack for recursive generation algorithm
Stack<Cell> stack = new Stack<Cell>();
// Colors
public ConsoleColor FieldColor get;
public ConsoleColor WallsColor get;
public Labyrinth(int height, int width, ConsoleColor fieldColor, ConsoleColor wallsColor)
Cells = new List<Cell>();
Walls = new List<Cell>();
// Setting an odd number even if it's even
Height = height % 2 == 0 ? height - 1 : height;
Width = width % 2 == 0 ? width - 1 : width;
// Adding cells
for (int i = 1; i < Height; i += 2)
for (int j = 1; j < Width; j += 2)
Cells.Add(new Cell(i, j));
// Adding walls
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
Walls.Add(new Cell(i, j));
// Adding colors
FieldColor = fieldColor;
WallsColor = wallsColor;
// Always beginning generation with a first cell (1, 1)
currentCell = Cells.First();
// Generating a labyrinth with latency to see the process
public void Generate(int latency)
do
// Always marking current cell as visited
currentCell.isVisited = true;
// Getting random neighbor cell as a next one
Cell nextCell = GetNeighbor(currentCell);
// If there is at least one available neighbor - remove walls between current and next cell
if (nextCell != null)
RemoveWalls(currentCell, nextCell);
// Removing wall that is equal to current cell
foreach (Cell wall in Walls)
if (wall.X == currentCell.X && wall.Y == currentCell.Y)
Walls.Remove(wall);
break;
currentCell.Display(FieldColor);
// If there is available next cell - pushing current cell to stack and assigning next to current
// Else - backtracking to cell that has at least one available neighbor
if (nextCell != null)
stack.Push(currentCell);
currentCell = nextCell;
else if (stack.Count > 0)
currentCell = stack.Pop();
// Highlight current cell
currentCell.Display(ConsoleColor.Green, ConsoleColor.Green);
Thread.Sleep(latency);
// Algorithm is done when current cell is back at the beginning
while (!IsCompleted());
// Display walls
foreach (Cell c in Walls)
c.Display(WallsColor, WallsColor);
void RemoveWalls(Cell a, Cell b)
// Assigning coordinates of a wall between a and b
int x = (a.X != b.X) ? (a.X > b.X ? a.X - 1 : a.X + 1) : a.X;
int y = (a.Y != b.Y) ? (a.Y > b.Y ? a.Y - 1 : a.Y + 1) : a.Y;
// Removing wall
foreach (Cell wall in Walls)
if (wall.X == x && wall.Y == y)
Walls.Remove(wall);
break;
// Disabling corresponding wall for each cell
if (a.X - b.X == 2)
a.walls[(int)Direction.Top] = false;
b.walls[(int)Direction.Bottom] = false;
else if (a.X - b.X == -2)
a.walls[(int)Direction.Bottom] = false;
b.walls[(int)Direction.Top] = false;
if (a.Y - b.Y == 2)
a.walls[(int)Direction.Left] = false;
b.walls[(int)Direction.Right] = false;
else if (a.Y - b.Y == -2)
a.walls[(int)Direction.Right] = false;
b.walls[(int)Direction.Left] = false;
Cell GetNeighbor(Cell cell)
Random rand = new Random();
List<Cell> neighbors = new List<Cell>();
// Assigning available neighbor cells
Cell top = (cell.X - 2 > 0) ? Cells.Find(c => c.X == cell.X - 2 && c.Y == cell.Y) : null;
Cell right = (cell.Y + 2 < Width - 1) ? Cells.Find(c => c.Y == cell.Y + 2 && c.X == cell.X) : null;
Cell bottom = (cell.X + 2 < Height - 1) ? Cells.Find(c => c.X == cell.X + 2 && c.Y == cell.Y) : null;
Cell left = (cell.Y - 2 > 0) ? Cells.Find(c => c.Y == cell.Y - 2 && c.X == cell.X) : null;
if (top != null && !top.isVisited)
neighbors.Add(top);
if (right != null && !right.isVisited)
neighbors.Add(right);
if (bottom != null && !bottom.isVisited)
neighbors.Add(bottom);
if (left != null && !left.isVisited)
neighbors.Add(left);
// Returning random neigbor from a list
if (neighbors.Count > 0)
int index = rand.Next(neighbors.Count);
return neighbors[index];
// Else return no neighbor
return null;
// If stack is empty then labyrinth is generated successfully
bool IsCompleted()
return stack.Count == 0;
Program:
using System;
namespace Labyrinth
class Program
static void Main()
Console.CursorVisible = false;
IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game(21, 35, '@', 10);
userInterface.StartGame(gameLoop, game);
c# algorithm recursion generator backtracking
So for over a weekend I decided to write something rather simple. For maze generation I've followed this algorithm and aside from that I've made a little game, to go from A to B.
Everything works fine but I'm curious whether it's possible to improve/simplify my code or not? If you have any thoughts on readability or efficiency - please share.
Oh and also... Is there a way to properly restart game when it quits to menu? Obviously, I can't use constructor on IGame, and because of that I've made Initialize()
method, but is that ok? Because menu is in a loop Game object keeps all values from previous play... Is there a workaround?
Direction:
namespace Labyrinth
enum Direction
Top,
Right,
Bottom,
Left
Interfaces:
using System;
namespace Labyrinth
// Menu and start of the game
public interface IMainUserInterface
void StartGame(IGameLoop gameLoop, IGame game);
// Game process
public interface IGameLoop
void Run(IGame game);
// This interface should be implemented by game object
public interface IGame
void Initialize();
void DisplayField();
void DisplayPlayer();
void HandleKey(ConsoleKeyInfo cki);
bool IsWon();
MainUserInterface:
using System;
namespace Labyrinth
class MainUserInterface : IMainUserInterface
public void StartGame(IGameLoop gameLoop, IGame game)
Console.CursorVisible = false;
do
string key;
do
Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)
case "1":
game.Initialize();
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;
Console.Clear();
while (true);
GameLoop:
using System;
using System.Diagnostics;
using System.Threading;
namespace Labyrinth
class GameLoop : IGameLoop
Stopwatch stopwatch;
public void Run(IGame game)
stopwatch = new Stopwatch();
// Display generation of a labyrinth
game.DisplayField();
// Start timer
stopwatch.Start();
do
if (Console.KeyAvailable == true)
game.HandleKey(Console.ReadKey(true));
game.DisplayPlayer();
while (!game.IsWon());
// Stop timer
stopwatch.Stop();
Thread.Sleep(500);
TimeSpan time = stopwatch.Elapsed;
string elapsedTime = string.Format("0:00:1:00.2:00", time.Minutes, time.Seconds, time.Milliseconds / 10);
// Show elapsed time
Console.WriteLine("nYou've completed a labyrinth in 0!n", elapsedTime);
Console.WriteLine("Press any button to continue or Escape to quit to menu.");
ConsoleKeyInfo cki = Console.ReadKey(true);
Console.Clear();
if (cki.Key != ConsoleKey.Escape)
game.Initialize();
Run(game);
Game:
using System;
namespace Labyrinth
class Game : IGame
public int Height get;
public int Width get;
public char PlayerSym get;
public int GenSpeed get;
Labyrinth labyrinth;
Player player;
Cell Start get;
Cell End get;
public Game(int height, int width, char playerSym, int genSpeed)
Height = height;
Width = width;
PlayerSym = playerSym;
GenSpeed = genSpeed;
Initialize();
Start = new Cell(1, 1);
End = new Cell(Height - 2, Width - 2);
public void Initialize()
labyrinth = new Labyrinth(Height, Width, ConsoleColor.DarkGreen, ConsoleColor.DarkBlue);
player = new Player(PlayerSym, labyrinth.Walls);
// Display field, each time generating a new one
public void DisplayField()
labyrinth.Generate(GenSpeed);
Start.Display(ConsoleColor.Green, ConsoleColor.Green);
End.Display(ConsoleColor.Red, ConsoleColor.Red);
// Display player cell
public void DisplayPlayer()
player.Display(ConsoleColor.Green, ConsoleColor.DarkMagenta);
// Move player
public void HandleKey(ConsoleKeyInfo cki)
player.Erase(labyrinth.FieldColor, labyrinth.FieldColor);
player.HandleKey(cki);
// Game is won when player gets to end cell
public bool IsWon()
return player.IsCollidingWith(End);
Point:
using System;
namespace Labyrinth
abstract class Point : Color
// Point value and coordinates
public virtual char Value get; set;
public virtual int X get; set;
public virtual int Y get; set;
public Point(char value, int x, int y)
Value = value;
X = x;
Y = y;
// Display point with certain colors
public virtual void Display(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(Value.ToString(), fgColor, bgColor);
// Erase point with certain colors
public virtual void Erase(ConsoleColor fgColor, ConsoleColor bgColor)
Console.SetCursorPosition(Y, X);
ColorDisplay(" ", fgColor, bgColor);
// Check if point colliding with another point
public virtual bool IsCollidingWith(Point p)
return X == p.X && Y == p.Y;
Cell:
using System;
namespace Labyrinth
class Cell : Point
// 4 walls on each direction
public bool walls;
public bool isVisited;
public Cell(int x, int y) : base(' ', x, y)
walls = new bool true, true, true, true ;
isVisited = false;
public void Display(ConsoleColor bgColor)
// Not displaying cell that hasn't been visited
if (isVisited)
Display(Console.ForegroundColor, bgColor);
else
Display(Console.ForegroundColor, Console.BackgroundColor);
// Displaying available walls around each cell
DisplayWalls(bgColor);
void DisplayWalls(ConsoleColor bgColor)
Cell c = null;
for (int i = 0; i < walls.Length; i++)
if (i == (int)Direction.Top)
c = new Cell(X - 1, Y);
else if (i == (int)Direction.Right)
c = new Cell(X, Y + 1);
else if (i == (int)Direction.Bottom)
c = new Cell(X + 1, Y);
else if (i == (int)Direction.Left)
c = new Cell(X, Y - 1);
// If wall is enabled - don't display anything
// If wall is disabled - display as normal cell
if (walls[i])
c.Display(Console.ForegroundColor, Console.BackgroundColor);
else
c.Display(Console.ForegroundColor, bgColor);
public void Highlight()
Display(ConsoleColor.Green, ConsoleColor.Green);
Player:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Labyrinth
class Player : Point
List<Cell> walls;
// Walls initialization, value and coordinates go to base constructor
public Player(char value, List<Cell> walls) : base(value, 1, 1)
this.walls = walls;
// Handle pressed key
public void HandleKey(ConsoleKeyInfo cki)
switch (cki.Key)
case ConsoleKey.D:
case ConsoleKey.RightArrow:
Y += 1;
if (IsCollidingWithWall())
Y -= 1;
break;
case ConsoleKey.A:
case ConsoleKey.LeftArrow:
Y -= 1;
if (IsCollidingWithWall())
Y += 1;
break;
case ConsoleKey.S:
case ConsoleKey.DownArrow:
X += 1;
if (IsCollidingWithWall())
X -= 1;
break;
case ConsoleKey.W:
case ConsoleKey.UpArrow:
X -= 1;
if (IsCollidingWithWall())
X += 1;
break;
// Check if walls list contains player coordinates
bool IsCollidingWithWall()
return walls.Any(c => c.IsCollidingWith(this));
Color:
using System;
namespace Labyrinth
abstract class Color
// Any hereditary class can use this function to display string in color
protected void ColorDisplay(string str, ConsoleColor fgColor, ConsoleColor bgColor)
ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;
Console.ForegroundColor = fgColor;
Console.BackgroundColor = bgColor;
Console.Write(str);
Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;
Labyrinth:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Labyrinth
class Labyrinth
// Field parameters
public int Height get;
public int Width get;
// Cells for player to move on
public List<Cell> Cells get;
// Walls to form an actual labyrinth
public List<Cell> Walls get;
// Beginning generation with this cell
Cell currentCell;
// Stack for recursive generation algorithm
Stack<Cell> stack = new Stack<Cell>();
// Colors
public ConsoleColor FieldColor get;
public ConsoleColor WallsColor get;
public Labyrinth(int height, int width, ConsoleColor fieldColor, ConsoleColor wallsColor)
Cells = new List<Cell>();
Walls = new List<Cell>();
// Setting an odd number even if it's even
Height = height % 2 == 0 ? height - 1 : height;
Width = width % 2 == 0 ? width - 1 : width;
// Adding cells
for (int i = 1; i < Height; i += 2)
for (int j = 1; j < Width; j += 2)
Cells.Add(new Cell(i, j));
// Adding walls
for (int i = 0; i < Height; i++)
for (int j = 0; j < Width; j++)
Walls.Add(new Cell(i, j));
// Adding colors
FieldColor = fieldColor;
WallsColor = wallsColor;
// Always beginning generation with a first cell (1, 1)
currentCell = Cells.First();
// Generating a labyrinth with latency to see the process
public void Generate(int latency)
do
// Always marking current cell as visited
currentCell.isVisited = true;
// Getting random neighbor cell as a next one
Cell nextCell = GetNeighbor(currentCell);
// If there is at least one available neighbor - remove walls between current and next cell
if (nextCell != null)
RemoveWalls(currentCell, nextCell);
// Removing wall that is equal to current cell
foreach (Cell wall in Walls)
if (wall.X == currentCell.X && wall.Y == currentCell.Y)
Walls.Remove(wall);
break;
currentCell.Display(FieldColor);
// If there is available next cell - pushing current cell to stack and assigning next to current
// Else - backtracking to cell that has at least one available neighbor
if (nextCell != null)
stack.Push(currentCell);
currentCell = nextCell;
else if (stack.Count > 0)
currentCell = stack.Pop();
// Highlight current cell
currentCell.Display(ConsoleColor.Green, ConsoleColor.Green);
Thread.Sleep(latency);
// Algorithm is done when current cell is back at the beginning
while (!IsCompleted());
// Display walls
foreach (Cell c in Walls)
c.Display(WallsColor, WallsColor);
void RemoveWalls(Cell a, Cell b)
// Assigning coordinates of a wall between a and b
int x = (a.X != b.X) ? (a.X > b.X ? a.X - 1 : a.X + 1) : a.X;
int y = (a.Y != b.Y) ? (a.Y > b.Y ? a.Y - 1 : a.Y + 1) : a.Y;
// Removing wall
foreach (Cell wall in Walls)
if (wall.X == x && wall.Y == y)
Walls.Remove(wall);
break;
// Disabling corresponding wall for each cell
if (a.X - b.X == 2)
a.walls[(int)Direction.Top] = false;
b.walls[(int)Direction.Bottom] = false;
else if (a.X - b.X == -2)
a.walls[(int)Direction.Bottom] = false;
b.walls[(int)Direction.Top] = false;
if (a.Y - b.Y == 2)
a.walls[(int)Direction.Left] = false;
b.walls[(int)Direction.Right] = false;
else if (a.Y - b.Y == -2)
a.walls[(int)Direction.Right] = false;
b.walls[(int)Direction.Left] = false;
Cell GetNeighbor(Cell cell)
Random rand = new Random();
List<Cell> neighbors = new List<Cell>();
// Assigning available neighbor cells
Cell top = (cell.X - 2 > 0) ? Cells.Find(c => c.X == cell.X - 2 && c.Y == cell.Y) : null;
Cell right = (cell.Y + 2 < Width - 1) ? Cells.Find(c => c.Y == cell.Y + 2 && c.X == cell.X) : null;
Cell bottom = (cell.X + 2 < Height - 1) ? Cells.Find(c => c.X == cell.X + 2 && c.Y == cell.Y) : null;
Cell left = (cell.Y - 2 > 0) ? Cells.Find(c => c.Y == cell.Y - 2 && c.X == cell.X) : null;
if (top != null && !top.isVisited)
neighbors.Add(top);
if (right != null && !right.isVisited)
neighbors.Add(right);
if (bottom != null && !bottom.isVisited)
neighbors.Add(bottom);
if (left != null && !left.isVisited)
neighbors.Add(left);
// Returning random neigbor from a list
if (neighbors.Count > 0)
int index = rand.Next(neighbors.Count);
return neighbors[index];
// Else return no neighbor
return null;
// If stack is empty then labyrinth is generated successfully
bool IsCompleted()
return stack.Count == 0;
Program:
using System;
namespace Labyrinth
class Program
static void Main()
Console.CursorVisible = false;
IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game(21, 35, '@', 10);
userInterface.StartGame(gameLoop, game);
c# algorithm recursion generator backtracking
edited Mar 1 at 1:52
asked Feb 27 at 22:11
Glitch
756
756
1
Wow. A lot to digest in one sitting. One very minor comment: Top and Bottom are not directions as much as they are locations. I would suggest Up and Down instead.
â Rick Davin
Mar 1 at 20:12
add a comment |Â
1
Wow. A lot to digest in one sitting. One very minor comment: Top and Bottom are not directions as much as they are locations. I would suggest Up and Down instead.
â Rick Davin
Mar 1 at 20:12
1
1
Wow. A lot to digest in one sitting. One very minor comment: Top and Bottom are not directions as much as they are locations. I would suggest Up and Down instead.
â Rick Davin
Mar 1 at 20:12
Wow. A lot to digest in one sitting. One very minor comment: Top and Bottom are not directions as much as they are locations. I would suggest Up and Down instead.
â Rick Davin
Mar 1 at 20:12
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188485%2fc-recursive-backtracker-labyrinth-generation-game%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
Wow. A lot to digest in one sitting. One very minor comment: Top and Bottom are not directions as much as they are locations. I would suggest Up and Down instead.
â Rick Davin
Mar 1 at 20:12