Пример #1
0
        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);
                }
            }
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
        }
Пример #5
0
        public void IsPassableFailsBetweenUnpassableNodes()
        {
            NodePtr start = new NodePtr(1, 1);
            NodePtr end   = new NodePtr(1, 2);

            var isPassable = maze.IsPassable(start, end);

            Assert.IsFalse(isPassable);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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
        }
Пример #8
0
        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
        }
Пример #9
0
        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;
        }
Пример #10
0
        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] = '.';
                }
            }
        }
Пример #11
0
        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++;
                }
            }
        }
Пример #12
0
        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();
                }
            }
        }
Пример #14
0
        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);
            }
        }
Пример #15
0
        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);
        }
Пример #16
0
 public AStarNodePtr(float priority, NodePtr node)
 {
     Priority = priority;
     Node     = node;
 }
Пример #17
0
 /// <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));
 }
Пример #18
0
 /// <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));
 }
Пример #19
0
        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;
                }
            }
        }