// goes through all the MazeParts + another horizontal and vertical row // creates a wall if the wall is not open for these mazeparts private void CreateWalls() { for (int x = 0; x < maze.GetLength(0); x++) { for (int y = 0; y < maze.GetLength(1); y++) { MazePart mazePart = maze[x, y]; // wallOpen array goes clockwise starting from the left (0 = left, 1 = top, 2 = right, 3 = bottom) if (!mazePart.wallOpen[(int)Direction.Left]) { CreateWall(Direction.Left, x, y); } if (!mazePart.wallOpen[(int)Direction.Up]) { CreateWall(Direction.Up, x, y); } // for the last vertical row if (x == maze.GetLength(0) - 1 && !mazePart.wallOpen[(int)Direction.Right]) { CreateWall(Direction.Right, x, y); } // for the last horizontal row if (y == 0 && !mazePart.wallOpen[(int)Direction.Down]) { CreateWall(Direction.Down, x, y); } } } }
// will generate a maze with the specified width, height, and chosen algorithm // after the maze is generated a piece of fruit is placed at the end of the longest path // finally the walls are placed public void GenerateMaze(int width, int height, Algorithm algorithm) { if (maze != null) { DestroyPreviousMaze(); } CreateMaze(width, height); fruitMazePart = maze[0, 0]; switch (algorithm) { case Algorithm.Iterative: DepthFirstIterativeImplementation(); break; case Algorithm.Recursive: DepthFirstRecursiveImplementation(maze[0, 0], 1); break; } fruitMazePart.hasFruit = true; fruit = Instantiate(fruitPrefab, fruitMazePart.transform.position, new Quaternion(0, 0, 0, 0)); CreateWalls(); }
public Maze(string input) { var lines = input.SplitNewLine(); _mazeWidth = lines[0].Length; _mazeHeight = lines.Length; _maze = new MazePart[_mazeWidth, _mazeHeight]; for (int y = 0; y < lines.Length; y++) { for (int x = 0; x < lines[y].Length; x++) { char c = lines[y][x]; if (char.IsDigit(c)) { Points.Add((new Vector2i(x, y), int.Parse(c.ToString()))); _maze[x, y] = MazePart.Digit; } else if (c == '#') { _maze[x, y] = MazePart.Wall; } else { _maze[x, y] = MazePart.Empty; } } } }
public void SpawnPlayer(MazePart mazePart) { currentPlayerCharacter = Instantiate(playerCharacterPrefab, mazePart.transform.position, new Quaternion(0, 0, 0, 0)); currentPlayerCharacter.currentMazePart = mazePart; gameStarted = true; // just in case the player generates a 1 x 1 maze CheckForPlayerWinOrLoss(mazePart); }
// will call GameOver function if the player character comes into contact with water (loss) or fruit (win) public void CheckForPlayerWinOrLoss(MazePart potentiallyUnderWaterMazePart) { if (potentiallyUnderWaterMazePart.hasFruit && potentiallyUnderWaterMazePart == currentPlayerCharacter.currentMazePart) { StopWaterSpreadingCoroutines(); gameStarted = false; localUIManager.GameOver(true); } else if (potentiallyUnderWaterMazePart.UnderWater && potentiallyUnderWaterMazePart == currentPlayerCharacter.currentMazePart) { StopWaterSpreadingCoroutines(); gameStarted = false; localUIManager.GameOver(false); } }
// will move the player character in the specified direction, // except if the new place would be outside the grid or there is a wall between the current MazePart and its neighbour private IEnumerator TryMoveInDirection(Direction direction) { enoughTimeSinceLastMovement = false; PlayerCharacter currentPlayerCharacter = localPlayerManager.currentPlayerCharacter; MazePart potentialNewMazePart = NextMazePart(currentPlayerCharacter, direction); if (potentialNewMazePart != null) { currentPlayerCharacter.Move(potentialNewMazePart, direction); } yield return(waitForSeconds); //0.15f "cooldown" period before the player can move again enoughTimeSinceLastMovement = true; }
// iterative implementation of randomised depth first algorithm private void DepthFirstIterativeImplementation() { Stack mazeStack = new Stack(); // mark as visited, push to stack maze[0, 0].visited = true; mazeStack.Push(maze[0, 0]); while (mazeStack.Count > 0) { MazePart currentMazePart = (MazePart)mazeStack.Pop(); List <MazePart> currentMazePartUnvistedNeighbours = currentMazePart.GetAllUnvistedNeighbours(maze); if (currentMazePartUnvistedNeighbours.Count > 0) { // push current cell to stack mazeStack.Push(currentMazePart); // choose unvisited neighbour MazePart chosenNeighbour = currentMazePartUnvistedNeighbours[random.Next(0, currentMazePartUnvistedNeighbours.Count)]; // remove wall chosenNeighbour.RemoveWall(currentMazePart); // mark chosenNeighbour visited chosenNeighbour.visited = true; mazeStack.Push(chosenNeighbour); // keep track of pathLength to put fruit at the end of the longest path chosenNeighbour.pathLength = currentMazePart.pathLength + 1; } // the longest path in the maze gets a piece of fruit as the end goal for the player else { if (currentMazePart.pathLength > fruitMazePart.pathLength) { fruitMazePart = currentMazePart; } } } }
// recursive implementation of randomised depth first algorithm private void DepthFirstRecursiveImplementation(MazePart currentMazePart, int pathLength) { // added a pathLength tracker to put fruit at the end of the longest path after the maze is finished currentMazePart.pathLength = pathLength; if (currentMazePart.pathLength > fruitMazePart.pathLength) { fruitMazePart = currentMazePart; } // mark as visited currentMazePart.visited = true; // read comment in the else for why "true" is put as a condition while (true) { // get a list of all unvisitedNeighbours of this MazePart List <MazePart> currentMazePartUnvistedNeighbours = currentMazePart.GetAllUnvistedNeighbours(maze); if (currentMazePartUnvistedNeighbours.Count > 0) { // choose random unvisted neighbour, then remove it from the list MazePart chosenNeighbour = currentMazePartUnvistedNeighbours[random.Next(0, currentMazePartUnvistedNeighbours.Count)]; currentMazePartUnvistedNeighbours.Remove(chosenNeighbour); // remove wall between chosenNeighbour and currentMazePart currentMazePart.RemoveWall(chosenNeighbour); // call this function with chosenNeighbour DepthFirstRecursiveImplementation(chosenNeighbour, pathLength + 1); } // when there are no more unvisted neighbours for currentMazePart, // break out of the while loop. else { // I understand this isn't the nicest way to do it but it works well, // and currentMazePartUnvistedNeighbours.Count > 0 should not be put as a condition, // because there could be (will be) MazeParts in that list that have been changed (visited) by a deeper call of this function. break; } } }