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