private void CarvePassage(NodePtr ptr, Maze maze) { Node currentNode = maze[ptr]; currentNode.Visited = true; var neighbours = maze.GetNeighbours(ptr); var cardinalNeighbours = new NodePtr?[4] { neighbours[1], neighbours[3], neighbours[5], neighbours[7] }; IEnumerable <NodePtr?> shuffled = cardinalNeighbours.OrderBy(n => r.Next()).ToArray(); foreach (var node in shuffled.Where(n => n.HasValue).Select(n => (NodePtr)n)) { Node nextNode = maze[node]; if (!nextNode.Visited) { maze.Tunnel(node, ptr); CarvePassage(node, maze); } } }
public ISet <NodePtr> Solve(NodePtr startPoint, NodePtr endPoint) { InitSolver(this.maze); start = startPoint; end = endPoint; bool b = Solve(start.x, start.y); if (!b) { return(null); } var resultSet = new HashSet <NodePtr>(); for (uint y = 0; y < maze.Height; y++) { for (uint x = 0; x < maze.Width; x++) { if (path[x, y]) { resultSet.Add(new NodePtr(x, y)); } } } return(resultSet); }
public void Generate(Maze maze) { uint x = (uint)r.Next(maze.Width); uint y = (uint)r.Next(maze.Height); NodePtr ptr = new NodePtr(x, y); CarvePassage(ptr, maze); }
public void DistanceBetweenGivesExpectedResult() { NodePtr a = new NodePtr(2, 2); NodePtr b = new NodePtr(4, 5); var distance = maze.DistanceBetween(a, b); Assert.IsTrue(distance - 3.60555 < 0.0001); }
public void IsPassableFailsBetweenUnpassableNodes() { NodePtr start = new NodePtr(1, 1); NodePtr end = new NodePtr(1, 2); var isPassable = maze.IsPassable(start, end); Assert.IsFalse(isPassable); }
private bool Solve(uint x, uint y) { if ((x == end.x) && (y == end.y)) { path[x, y] = true; return(true); } if (hasVisted[x, y]) { return(false); } hasVisted[x, y] = true; NodePtr currentPoint = new NodePtr(x, y); if (x != 0) { NodePtr nextPoint = new NodePtr(x - 1, y); if (maze.IsPassable(currentPoint, nextPoint) && Solve(x - 1, y)) { path[x, y] = true; return(true); } } if (x != maze.Width - 1) { NodePtr nextPoint = new NodePtr(x + 1, y); if (maze.IsPassable(currentPoint, nextPoint) && Solve(x + 1, y)) { path[x, y] = true; return(true); } } if (y != 0) { NodePtr nextPoint = new NodePtr(x, y - 1); if (maze.IsPassable(currentPoint, nextPoint) && Solve(x, y - 1)) { path[x, y] = true; return(true); } } if (y != maze.Height - 1) { NodePtr nextPoint = new NodePtr(x, y + 1); if (maze.IsPassable(currentPoint, nextPoint) && Solve(x, y + 1)) { path[x, y] = true; return(true); } } return(false); }
public void GetNeighboursFromBottomRightExtreme() { NodePtr ptr = new NodePtr((uint)mazeWidth - 1, (uint)mazeHeight - 1); var neighbours = maze.GetNeighbours(ptr); /* * + + x * + o x * x x x */ Assert.AreEqual(9, neighbours.Length); // Should still return 9 positions Assert.AreEqual(3, neighbours.Count(n => n.HasValue)); // Should only return 3 valid positions }
public void GetNeighoursFromTopLeftExtreme() { NodePtr ptr = new NodePtr(0, 0); var neighbours = maze.GetNeighbours(ptr); /* * x x x * x o + * x + + */ Assert.AreEqual(9, neighbours.Length); // Should still return 9 positions Assert.AreEqual(3, neighbours.Count(n => n.HasValue)); // Should only return 3 valid positions }
public void NodeIsMarkedAsVisited() { // Visit node and remove some walls NodePtr ptr = new NodePtr((uint)Math.Floor(mazeWidth / 2.0), (uint)Math.Floor(mazeWidth / 2.0)); Node current = maze[ptr]; Assert.IsFalse(maze.IsNodeVisited(ptr)); current.Visited = true; maze[ptr] = current; Assert.IsTrue(maze.IsNodeVisited(ptr)); // Reset storage for future tests current.Walls ^= (ushort)NodeWall.All; maze[ptr] = current; }
private static void WriteSolution(NodePtr start, NodePtr end, ISet <NodePtr> solution) { foreach (var node in solution) { var xOffset = node.x * offset + 1; var yOffset = node.y * offset + 1; if (node.Equals(start) || node.Equals(end)) { display[xOffset, yOffset] = 'O'; } else { display[xOffset, yOffset] = '.'; } } }
public void GetNeighboursFromMiddle() { uint x = (uint)Math.Floor(mazeWidth / 2.0); uint y = (uint)Math.Floor(mazeHeight / 2.0); NodePtr ptr = new NodePtr(x, y); var neighbours = maze.GetNeighbours(ptr); Assert.AreEqual(9, neighbours.Length); // Should always return 9 positions foreach (var node in neighbours) { /* * No neighbour should be inaccessable * e.g. out of bounds */ if (node != null) { Assert.IsInstanceOfType(maze[(NodePtr)node], typeof(Node)); } } long previousNodeCoord = 0; int nullEntries = 0; foreach (var node in neighbours) { // Ensure that results are in row-major order if (node.HasValue) { long nodeCoord = node.Value.x + ((node.Value.y - 1) * mazeWidth); if (previousNodeCoord > 0) { long nodeDiff = nodeCoord - previousNodeCoord; // Diff needs to be within 1+null count, Width-2+null count Assert.IsTrue(nodeDiff == 1 + nullEntries || nodeDiff == mazeWidth - 2 + nullEntries); } previousNodeCoord = nodeCoord; nullEntries = 0; } else { nullEntries++; } } }
public void IsPassableSucceedsBetweenPassableNodes() { NodePtr start = new NodePtr(1, 1); NodePtr end = new NodePtr(1, 2); var startNode = maze[start]; startNode.Walls ^= (ushort)NodeWall.South; maze[start] = startNode; var endNode = maze[end]; endNode.Walls ^= (ushort)NodeWall.North; maze[end] = endNode; var isPassable = maze.IsPassable(start, end); Assert.IsTrue(isPassable); }
public void RecursiveSolverSolvesForGivenMaze() { var actualSolution = new NodePtr[] { new NodePtr(1, 1), new NodePtr(4, 2) }; var solution = solver.Solve(startPoint, endPoint); //Assert.AreEqual(actualSolution.Length, solution.Count); foreach (var node in actualSolution) { if (!solution.Contains(node)) { Assert.Fail(); } } }
public void Generate(Maze maze) { // Starting position uint x = (uint)r.Next(maze.Width); uint y = (uint)r.Next(maze.Height); NodePtr startingPoint = new NodePtr(x, y); maze[startingPoint].Visited = true; // Initialise from starting position var vistitedNodes = new HashSet <NodePtr>(); vistitedNodes.Add(startingPoint); var frontierNodes = new HashSet <NodePtr>(GetFrontierNodes(maze, startingPoint)); // Loop through frontier while (frontierNodes.Count > 0) { var frontierNode = frontierNodes.OrderBy(n => r.NextDouble()).First(); var validFrontierNodeEdges = maze.GetNeighbours(frontierNode) .Where(n => n.HasValue && maze[n.Value].Visited) .Cast <NodePtr>() .Where(n => Math.Abs((int)n.x - frontierNode.x + (int)n.y - frontierNode.y) == 1); if (validFrontierNodeEdges.Count() > 0) { var nodeToTunnelTo = validFrontierNodeEdges.OrderBy(n => r.NextDouble()).FirstOrDefault(); maze.Tunnel(frontierNode, nodeToTunnelTo); } maze[frontierNode].Visited = true; vistitedNodes.Add(frontierNode); frontierNodes.UnionWith(GetFrontierNodes(maze, frontierNode)); frontierNodes.Remove(frontierNode); } }
public ISet <NodePtr> Solve(NodePtr startPoint, NodePtr endPoint) { InitSolver(); priorityQueue = new MinHeap <AStarNodePtr>(maze.MazeSize); priorityQueue.Insert(new AStarNodePtr(0, startPoint)); var cameFrom = new Dictionary <NodePtr, NodePtr?>(); var runningCost = new Dictionary <NodePtr, float>(); cameFrom[startPoint] = null; runningCost[startPoint] = 0; NodePtr last; while (priorityQueue.Size > 0) { var node = priorityQueue.ExtractMin(); last = node.Node; hasVisited[node.Node.x, node.Node.y] = true; if (node.Node.Equals(endPoint)) { cameFrom[last] = node.Node; break; } foreach (var next in maze.GetNeighbours(node.Node) .Where(n => n.HasValue && !hasVisited[n.Value.x, n.Value.y] && maze.IsPassable(node.Node, n.Value)) .Cast <NodePtr>() .Where(fn => Math.Abs((int)fn.x - node.Node.x + (int)fn.y - node.Node.y) == 1)) { var newCost = runningCost[node.Node] + 1; if (!runningCost.Keys.Contains(next) || newCost < runningCost[next]) { runningCost[next] = newCost; var priority = newCost + GetHeuristic(endPoint, next); cameFrom[next] = node.Node; if (next.Equals(endPoint)) { priorityQueue.Empty(); break; } else { priorityQueue.Insert(new AStarNodePtr(priority, next)); } } } } var results = new HashSet <NodePtr>(); var resultNode = endPoint; while (!resultNode.Equals(startPoint)) { results.Add(resultNode); resultNode = cameFrom[resultNode].Value; } results.Add(startPoint); return(results); }
public AStarNodePtr(float priority, NodePtr node) { Priority = priority; Node = node; }
/// <summary> /// Gets the nodes classes as frontier nodes, which contains only nodes /// that are in the cardinal directions outwards from nodes within the maze. /// </summary> /// <param name="maze"></param> /// <param name="node"></param> /// <returns></returns> private static IEnumerable <NodePtr> GetFrontierNodes(Maze maze, NodePtr node) { return(maze.GetNeighbours(node).Where(n => n.HasValue && !maze[n.Value].Visited) .Cast <NodePtr>() .Where(fn => Math.Abs((int)fn.x - node.x + (int)fn.y - node.y) == 1)); }
/// <summary> /// Gets the heuristic which A* will use to make /// decisions on which part of the frontier to /// explore; uses the manhattan distance. /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns>The manhattan distance between two points</returns> private int GetHeuristic(NodePtr a, NodePtr b) { return((int)Math.Abs((int)a.x - b.x) + (int)Math.Abs((int)a.y - b.y)); }
static void Main(string[] args) { Console.CursorVisible = false; r = new Random((int)DateTime.Now.Ticks); maze = new Maze(mazeWidth, mazeHeight); display = new char[mazeWidth * offset, mazeHeight *offset]; Console.WriteLine("Choose a type of maze to generate:\n"); Console.WriteLine("1: Recursive Backtracker"); Console.WriteLine("2: Randomised Prim's Algorithm"); var mazeOption = Console.ReadKey().Key; IMazeGenerator generator; switch (mazeOption) { case ConsoleKey.D2: generator = new PrimsAlgorithm(r); break; case ConsoleKey.D1: default: generator = new RecursiveBacktracker(r); break; } Console.Clear(); Console.WriteLine("Choose a type of solver, if any:\n"); Console.WriteLine("0: No solver"); Console.WriteLine("1: Recursive Solver"); Console.Write("2: A* Solver"); var solverOption = Console.ReadKey().Key; ISolver solver; switch (solverOption) { case ConsoleKey.D1: solver = new RecursiveSolver(maze); break; case ConsoleKey.D2: solver = new AStar(maze); break; default: solver = null; break; } while (true) { maze.InitialiseNodes(); generator.Generate(maze); Console.Clear(); for (int h = 0; h < mazeHeight; h++) { for (int w = 0; w < mazeWidth; w++) { WriteNodeChar(w, h, maze[(uint)w, (uint)h].Walls); } } if (solver != null) { Random r = new Random(); NodePtr startPoint = new NodePtr((uint)maze.Width + 1, (uint)maze.Height + 1); // Purposefully not valid NodePtr endPoint = new NodePtr((uint)maze.Width + 1, (uint)maze.Height + 1); while (true) { //TODO: Probably refactor var startPointIsValid = maze.IsPointValid(startPoint); var endPointIsValid = maze.IsPointValid(endPoint); if (startPointIsValid && endPointIsValid) { break; } if (!startPointIsValid) { startPoint = new NodePtr((uint)r.Next(maze.Width), (uint)r.Next(maze.Height)); } if (!endPointIsValid) { endPoint = new NodePtr((uint)r.Next(maze.Width), (uint)r.Next(maze.Height)); } } var solution = solver.Solve(startPoint, endPoint); if (solution != null) { WriteSolution(startPoint, endPoint, solution); Console.WriteLine(string.Format("Solving for: Start - {0}x, {1}y; End - {2}x, {3}y", startPoint.x, startPoint.y, endPoint.x, endPoint.y)); } else { Console.WriteLine("Cannot find a Solution....."); } } DrawDisplay(); Console.WriteLine(); Console.WriteLine("Press any key to regenerate, or Escape to exit...."); if (Console.ReadKey().Key == ConsoleKey.Escape) { break; } } }