/// <summary> /// The SimulationTimerEventHandler method is called when the simulation timer expires. /// It updates the current state of the simulation. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SimulationTimerEventHandler(object sender, EventArgs e) { try { if (SimulationState == SimulationState.Running) { MazeCell robotLocation = MazeCells.First(x => x.ContainsRobot); // Retrieve the current location of the robot. int cellIndex = MazeCells.IndexOf(robotLocation); // Retrieve the index of the cell. // Determine the indexes for the cell's neighbours. int northNeighbourIndex = cellIndex - MazeWidthHeightCells; int eastNeighbourIndex = cellIndex + 1; int southNeighbourIndex = cellIndex + MazeWidthHeightCells; int westNeighbourIndex = cellIndex - 1; // Determine if the cell is on the north/east/south/west edge of the maze - certain neighbours must be ignored if the current cell is on an edge. 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; // Retrieve the cell's neighbours. MazeCell northCell = null; MazeCell eastCell = null; MazeCell southCell = null; MazeCell westCell = null; // North cell. if (!northEdge && IsCellIndexValid(northNeighbourIndex)) { northCell = MazeCells[northNeighbourIndex]; } // East cell. if (!eastEdge && IsCellIndexValid(eastNeighbourIndex)) { eastCell = MazeCells[eastNeighbourIndex]; } // South cell. if (!southEdge && IsCellIndexValid(southNeighbourIndex)) { southCell = MazeCells[southNeighbourIndex]; } // West cell. if (!westEdge && IsCellIndexValid(westNeighbourIndex)) { westCell = MazeCells[westNeighbourIndex]; } // Create a maze segment at the robot's current location. MazeSegment mazeSegment = new MazeSegment(robotLocation, northCell, eastCell, southCell, westCell); _robot.Move(mazeSegment); // Move the robot. SimulationTime = SimulationTime.Add(TimeSpan.FromMilliseconds(Constants.DefaultStepIntervalMilliSeconds)); } } catch (Exception ex) { throw new Exception("Maze.SimulationTimerEventHandler(object sender, EventArgs e): " + ex.ToString()); } }
/// <summary> /// The GenerateNewMaze method is called to generate a new maze. /// </summary> /// <returns></returns> public async Task GenerateNewMaze() { try { if (CanGenerateMaze) { MazeState = MazeState.MazeGenerating; // Clear the maze and stack. ResetMaze(); _mazeGeneratorStack = new Stack <MazeCell>(); // Select a random cell to start. MazeCell startCell = ChooseRandomCell(); startCell.CellState = CellState.Visited; // Generate the new maze. await GenerateNewMaze(startCell); // Set the start/end cells. if (MazeCells.Count > 0) { MazeCells.First().CellType = CellType.Start; MazeCells.Last().CellType = CellType.End; } MazeState = MazeState.MazeGenerated; } } catch (Exception ex) { throw new Exception("Maze.GenerateNewMaze(): " + ex.ToString()); } }
/// <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()); } }