C# recursive backtracker labyrinth generation & game

The name of the pictureThe name of the pictureThe name of the pictureClash 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);









share|improve this question

















  • 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
















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);









share|improve this question

















  • 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












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);









share|improve this question













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);











share|improve this question












share|improve this question




share|improve this question








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












  • 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















active

oldest

votes











Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















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



































active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes










 

draft saved


draft discarded


























 


draft saved


draft discarded














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













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation