Exemple #1
0
        /// <summary>
        /// The IsCellOnTheEdge method is called to determine if the provided cell is on the edge of the maze grid.
        /// </summary>
        /// <param name="cell"></param>
        /// <returns></returns>
        private bool IsCellOnTheEdge(MazeCell cell)
        {
            try
            {
                if (cell == null)
                {
                    throw new Exception("cell can not be null.");
                }

                int cellIndex = MazeCells.IndexOf(cell);   // Retrieve the index of the cell.

                // Determine if the current cell is on the north/east/south/west edge of the maze.
                bool northEdge = cellIndex < MazeWidthHeightCells ? true : false;
                bool eastEdge  = ((cellIndex + 1) % MazeWidthHeightCells) == 0 ? true : false;
                bool westEdge  = (cellIndex % MazeWidthHeightCells) == 0 ? true : false;
                bool southEdge = (cellIndex + MazeWidthHeightCells) >= (MazeWidthHeightCells * MazeWidthHeightCells) ? true : false;

                return(northEdge || eastEdge || westEdge || southEdge);
            }
            catch (Exception ex)
            {
                throw new Exception("Maze.IsCellOnTheEdge(MazeCell cell): " + ex.ToString());
            }
        }
Exemple #2
0
        /// <summary>
        /// The Move method is called to move the robot.
        /// The Trémaux's algorithm is used to control the robot.
        /// https://en.wikipedia.org/wiki/Maze_solving_algorithm#Tr%C3%A9maux's_algorithm
        /// </summary>
        /// <param name="mazeSegment"></param>
        public void Move(MazeSegment mazeSegment)
        {
            try
            {
                if (mazeSegment == null)
                {
                    throw new Exception("maze segment can not be null.");
                }

                if (CurrentLocation == null)
                {
                    // The robot is not in the maze - do nothing.
                    return;
                }

                // If the robot is NOT at a junction, mark the location.
                if (mazeSegment.SegmentType != SegmentType.Junction)
                {
                    CurrentLocation.MarkCell();
                }

                // If the robot is at a dead-end (except for the start of the maze), mark the location a second time.
                if (mazeSegment.SegmentType == SegmentType.DeadEnd && CurrentLocation.CellRole != CellRole.Start)
                {
                    CurrentLocation.MarkCell();
                }

                // Turn the robot, based on the type of maze segment.
                CurrentDirection = mazeSegment.ChooseDirection(CurrentDirection);

                // Attempt to move the robot forwards.
                MazeCell newCurrentLocation = null;
                if (CurrentDirection == Direction.North && mazeSegment.NorthCell.CellMark != CellMark.Twice)
                {
                    newCurrentLocation = mazeSegment.NorthCell;
                }
                else if (CurrentDirection == Direction.East && mazeSegment.EastCell.CellMark != CellMark.Twice)
                {
                    newCurrentLocation = mazeSegment.EastCell;
                }
                else if (CurrentDirection == Direction.South && mazeSegment.SouthCell.CellMark != CellMark.Twice)
                {
                    newCurrentLocation = mazeSegment.SouthCell;
                }
                else if (CurrentDirection == Direction.West && mazeSegment.WestCell.CellMark != CellMark.Twice)
                {
                    newCurrentLocation = mazeSegment.WestCell;
                }

                // Update the current location.
                CurrentLocation.ContainsRobot = false;
                CurrentLocation = newCurrentLocation != null ? newCurrentLocation : CurrentLocation;
                CurrentLocation.ContainsRobot = true;

                if (CurrentLocation.CellRole == CellRole.End)
                {
                    OnReachedTheEnd?.Invoke();  // The robot has reached the end of the maze.
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Robot.Move(MazeSegment mazeSegment): " + ex.ToString());
            }
        }
Exemple #3
0
        /// <summary>
        /// The GenerateNewMaze method is called to generate a new maze.
        /// The Randomized Prim's algorithm is used to generate the maze.
        /// https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Prim's_algorithm
        /// </summary>
        /// <returns></returns>
        public async Task GenerateNewMaze()
        {
            try
            {
                if (CanGenerateMaze)
                {
                    ResetMaze();                                           // Clear the maze.

                    SimulationState = SimulationState.MazeGenerating;      // Update the simulation state.

                    List <MazeCell> frontierCells = new List <MazeCell>(); // Create a collection of maze walls.

                    // Select a random cell to start.
                    MazeCell initialCell = ChooseRandomCell();
                    initialCell.CellType = CellType.Passage;
                    frontierCells.AddRange(RetrieveFrontierNeighbours(initialCell));  // Add the neighbouring cells to the frontier.

                    while (frontierCells.Count > 0)
                    {
                        // Choose a random cell from the frontier.
                        MazeCell currentCell = frontierCells.ElementAt(_randomNumberGenerator.Next(frontierCells.Count));
                        frontierCells.Remove(currentCell);  // Remove this cell from the frontier.

                        if (currentCell.CellType == CellType.Passage)
                        {
                            // If the cell is already a passage, move on to the next frontier cell.
                            continue;
                        }

                        // Retrieve the "passage" neighbours for the current cell (these neighbours are already part of the maze).
                        List <MazeCell> passageNeighbours = RetrievePassageNeighbours(currentCell);
                        if (passageNeighbours.Count > 0)
                        {
                            // Select a random "passage" neighbour.
                            MazeCell selectedNeighbour = passageNeighbours.ElementAt(_randomNumberGenerator.Next(passageNeighbours.Count));

                            // Connect the current cell to the selected neighbour.
                            ConnectCells(currentCell, selectedNeighbour);
                            currentCell.CellType = CellType.Passage;

                            // Retrieve the frontier neighbours for the current cell, and add them to the frontier.
                            frontierCells.AddRange(RetrieveFrontierNeighbours(currentCell));
                        }

                        await Task.Delay(Constants.MazeGenerationDelayMilliSeconds);
                    }

                    // Set the start/end cells.
                    MazeCell startCell = MazeCells.First(x => x.CellType == CellType.Passage);
                    startCell.CellRole = CellRole.Start;
                    MazeCell endCell = MazeCells.Last(x => x.CellType == CellType.Passage);
                    endCell.CellRole = CellRole.End;

                    // Place the robot at the start cell.
                    _robot.SetLocation(startCell);

                    SimulationState = SimulationState.MazeGenerated;    // Update the simulation state.
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Maze.GenerateNewMaze(): " + ex.ToString());
            }
        }
        /// <summary>
        /// Constructor.
        /// Creates a new MazeSegment from a center cell and four neighbouring cells.
        /// </summary>
        public MazeSegment(MazeCell centerCell, MazeCell northCell, MazeCell eastCell, MazeCell southCell, MazeCell westCell)
        {
            try
            {
                CenterCell = centerCell ?? throw new Exception("Center cell can not be null."); // Assign the center cell.

                // Assign the neighbour cells.
                if (northCell == null && eastCell == null && southCell == null && westCell == null)
                {
                    throw new Exception("All four neighbour cells can not be null.");
                }
                NorthCell = northCell == null ? new MazeCell()
                {
                    CellType = CellType.Wall
                } : northCell;
                EastCell = eastCell == null ? new MazeCell()
                {
                    CellType = CellType.Wall
                } : eastCell;
                SouthCell = southCell == null ? new MazeCell()
                {
                    CellType = CellType.Wall
                } : southCell;
                WestCell = westCell == null ? new MazeCell()
                {
                    CellType = CellType.Wall
                } : westCell;

                // Build the list of cells not visited.
                _cellsNotVisited = new List <Direction>();
                if (NorthCell.CellType == CellType.Passage && NorthCell.CellMark == CellMark.None)
                {
                    _cellsNotVisited.Add(Direction.North);
                }
                if (EastCell.CellType == CellType.Passage && EastCell.CellMark == CellMark.None)
                {
                    _cellsNotVisited.Add(Direction.East);
                }
                if (SouthCell.CellType == CellType.Passage && SouthCell.CellMark == CellMark.None)
                {
                    _cellsNotVisited.Add(Direction.South);
                }
                if (WestCell.CellType == CellType.Passage && WestCell.CellMark == CellMark.None)
                {
                    _cellsNotVisited.Add(Direction.West);
                }

                // Build the list of cells visited once.
                _cellsVisitedOnce = new List <Direction>();
                if (NorthCell.CellType == CellType.Passage && NorthCell.CellMark == CellMark.Once)
                {
                    _cellsVisitedOnce.Add(Direction.North);
                }
                if (EastCell.CellType == CellType.Passage && EastCell.CellMark == CellMark.Once)
                {
                    _cellsVisitedOnce.Add(Direction.East);
                }
                if (SouthCell.CellType == CellType.Passage && SouthCell.CellMark == CellMark.Once)
                {
                    _cellsVisitedOnce.Add(Direction.South);
                }
                if (WestCell.CellType == CellType.Passage && WestCell.CellMark == CellMark.Once)
                {
                    _cellsVisitedOnce.Add(Direction.West);
                }

                SegmentType = DetermineSegmentType();   // Determine the segment type.
            }
            catch (Exception ex)
            {
                throw new Exception("MazeSegment(MazeCell centerCell, MazeCell northCell, MazeCell eastCell, MazeCell southCell, MazeCell westCell): " + ex.ToString());
            }
        }