Problem with Procedural Cave Generation (Sebastian Lague Tutorial)

I’m on the sixth episode of his tutorial where we create connections between rooms. When I change the minWallRegionSize variable to a value hat turns a region of walls to a room, I’ll get lots of index-out-of-range exceptions. Changing the minRoomSize variable doesn’t throw up errors and works perfectly. The error takes me to the second Room constructor, inside the for loops.

//Right here. In Monodevelop the error is on line 465, but it might be a different line on the post
if(map[x, y] == 1)
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// The MapGenerator class creates a 2d grid of values.
/// The values can be adjusted via other parameters, turning the grid into, what seems like, noise.
/// </summary>
[RequireComponent(typeof(MeshGenerator))]
public class MapGenerator : MonoBehaviour
{
    /// <summary>
    /// The map's width.
    /// </summary>
    public int width = 50;
    /// <summary>
    /// The map's height.
    /// </summary>
    public int height = 50;
    /// <summary>
    /// When the map is randomly filled, it needs to obey a loose guideline of how much should be solid.
    /// </summary>
    [Range(0, 100)]
    public int fillPercentage = 1;
    /// <summary>
    /// If true, the edges of the map will always be solid.
    /// </summary>
    public bool fillEdges = true;
    /// <summary>
    /// This is how thick the edges will be.
    /// </summary>
    public int edgeWidth = 1;
    /// <summary>
    /// Should the rooms be connected?
    /// </summary>
    public bool connectRooms = true;
    /// <summary>
    /// Whether or not the wall regions should be processed.
    /// </summary>
    public bool processWallRegions = false;
    /// <summary>
    /// Any wall region that is made up of less tiles that the minRoomSize will be made empty (set to zero).
    /// </summary>
    public int minWallRegionSize = 50;
    /// <summary>
    /// Whether or not the rooms should be processed.
    /// </summary>
    public bool processRooms = false;
    /// <summary>
    /// Any wall region that is made up of less tiles that the minRoomSize will be made empty (set to zero).
    /// </summary>
    public int minRoomSize = 50;
    /// <summary>
    /// The map will be generated from a seed so that the same map can be recreated via the same number/text.
    /// </summary>
    public string seed;
    /// <summary>
    /// If we don't want to use a custom seed we can have one randomly generated.
    /// </summary>
    public bool useRandomSeed;
   
    [SerializeField]
    /// <summary>
    /// The rules.
    /// </summary>
    public Rules rules;

    /// <summary>
    /// This defines a grid of integers, any tile that is equal to 0 will be empty, and any tile that is equal to 1 will be solid.
    /// This grid of numbers will be filled with random 0's and 1's.
    /// </summary>
    private int[,] map;

    /// <summary>
    /// The mesh generator.
    /// </summary>
    private MeshGenerator meshGenerator;
   
    void Start()
    {
        meshGenerator = GetComponent<MeshGenerator>();
    }
   
    void Update()
    {
        width = Mathf.Clamp(width, 1, Int32.MaxValue);
        height = Mathf.Clamp(height, 1, Int32.MaxValue);
        edgeWidth = Mathf.Clamp(edgeWidth, 1, Int32.MaxValue);

        GenerateMap();
    }

    /// <summary>
    /// Generates the map.
    /// </summary>
    private void GenerateMap()
    {
        map = new int[width, height];
        //RandomFillMap is called to fill map with basic data
        RandomFillMap();

        //A set of rules will be applied to the grid of data
        ApplyRule();

        ProcessRooms();

        //If the user wants the edges to be solid...
        if(fillEdges)
        {
            CreateEdge();
        }
        //Now we've finished all of the rule iterations and have the finished grid of data
        //That data will be sent to the MeshGenerator class to be turned into an actual mesh
        meshGenerator.GenerateMesh(map, 1f);
    }
    private void CreateEdge()
    {
        int[,] borderedMap = new int[width + edgeWidth * 2,height + edgeWidth * 2];
        for(int x = 0; x < borderedMap.GetLength(0); x++)
        {
            for(int y = 0; y < borderedMap.GetLength(1); y++)
            {
                if(x >= edgeWidth && x < width + edgeWidth && y >= edgeWidth && y < height + edgeWidth)
                    borderedMap[x, y] = map[x - edgeWidth, y - edgeWidth];
                else
                    borderedMap[x,y] = 1;
            }
        }
    }
    private void ProcessRooms()
    {
        List<List<Coord>> wallRegions = GetRegions(1);
        foreach(List<Coord> wallRegion in wallRegions)
        {
            if(wallRegion.Count < minWallRegionSize)
            {
                foreach(Coord tile in wallRegion)
                {
                    map[tile.x, tile.y] = 0;
                }
            }
        }
       
        List<List<Coord>> roomRegions = GetRegions(0);
        List<Room> survivingRooms = new List<Room>();
        foreach(List<Coord> roomRegion in roomRegions)
        {
            if(roomRegion.Count < minRoomSize)
            {
                foreach(Coord tile in roomRegion)
                {
                    map[tile.x, tile.y] = 1;
                }
            }
            else
            {
                survivingRooms.Add(new Room(roomRegion, map));
            }
        }
       
        ConnectClosestRooms(survivingRooms);
    }
    private void ConnectClosestRooms(List<Room> allRooms)
    {
        int bestDistance = Int32.MaxValue;
        Coord bestTileA = new Coord();
        Coord bestTileB = new Coord();
        Room bestRoomA = new Room();
        Room bestRoomB = new Room();
        bool possibleConnectionFound = false;

        //We'll go through all the rooms and compare roomA to find the closest one
        foreach(Room roomA in allRooms)
        {
            //We'll set possibleConnectionFound equal to false so that whenever we move to the next room, it doesn't think it has found a good room
            possibleConnectionFound = false;
            foreach(Room roomB in allRooms)
            {
                //Then we'll loop through each index in each room
                for(int tileIndexA = 0; tileIndexA < roomA.edgeTiles.Count; tileIndexA++)
                {
                    if(roomA == roomB)
                    {
                        continue;
                    }
                    if(roomA.IsConnected(roomB))
                    {
                        possibleConnectionFound = false;
                        break;
                    }
                    for(int tileIndexB = 0; tileIndexB < roomB.edgeTiles.Count; tileIndexB++)
                    {
                        Coord tileA = roomA.edgeTiles[tileIndexA];
                        Coord tileB = roomB.edgeTiles[tileIndexB];
                        int distanceBetweenRooms = (int)(Mathf.Pow(tileA.x - tileB.x, 2) + Mathf.Pow(tileA.y - tileB.y, 2));

                        if(distanceBetweenRooms < bestDistance || !possibleConnectionFound)
                        {
                            bestDistance = distanceBetweenRooms;
                            possibleConnectionFound = true;

                            bestRoomA = roomA;
                            bestRoomB = roomB;
                            bestTileA = tileA;
                            bestTileB = tileB;
                        }
                    }
                }
            }
            if(possibleConnectionFound)
                CreatePassage(bestRoomA, bestRoomB, bestTileA, bestTileB);
        }
    }
    private void CreatePassage(Room roomA, Room RoomB, Coord tileA, Coord tileB)
    {
        Room.ConnectRooms(roomA, RoomB);
        Debug.DrawLine(CoordToWorldPoint(tileA), CoordToWorldPoint(tileB), Color.green);
    }

    public Vector3 CoordToWorldPoint(Coord tile)
    {
        return new Vector3(-width / 2 + 0.5f + tile.x, 2f, -height / 2 + 0.5f + tile.y);
    }
    /// <summary>
    /// This method creates the rough data for the ApplyRule method to smooth.
    /// </summary>
    private void RandomFillMap()
    {
        //If a random seed is wanted-
        if(useRandomSeed)
            //Seed is set to the amout of time that the game has been running
            seed = Time.time.ToString();
        //This will return a unique integer, simulating randomness
        //Prng stands for Pseudo Random Number Generator
        System.Random prng = new System.Random(seed.GetHashCode());
       
        //Each index in the maps array of numbers is looped through
        //For every x index-
        for(int x = 0; x < width; x++)
        {
            //Every y index will be iterated through as well
            for(int y = 0; y < height; y++)
            {
                //Using the if/else notation, the current index will be set to 1 if the generated number is less than fillPercentage,
                //otherwise it will be set to 0
                map[x, y] = (prng.Next(0, 100) < fillPercentage) ? 1 : 0;
            }
        }
    }
   
    /// <summary>
    /// ApplyRule lets the user choose and create custom rules that will be applied to the map.
    /// Withought this method, the map would just be boring and look like tv static.
    /// </summary>
    void ApplyRule()
    {
        if(rules.lague.applyRule) rules.lague.ApplyRule(map, this);
        if(rules.mazeWorld.applyRule) rules.mazeWorld.ApplyRule(map, this);
        if(rules.gameOfLife.applyRule) rules.gameOfLife.ApplyRule(map, this);

        if(rules.perlinNoise.applyRule) rules.perlinNoise.ApplyRule(map, this);

        if(rules.customFunction.applyRule) rules.customFunction.ApplyRule(map, this);

        if(rules.quadratic.applyRule) rules.quadratic.ApplyRule(map, this);

        if(rules.logOfXPlusY.applyRule) rules.logOfXPlusY.ApplyRule(map, this);
        if(rules.logOfXTimesY.applyRule) rules.logOfXTimesY.ApplyRule(map, this);
        if(rules.logOfXToTheYthPower.applyRule) rules.logOfXToTheYthPower.ApplyRule(map, this);

        if(rules.sinOfXPlusY.applyRule) rules.sinOfXPlusY.ApplyRule(map, this);
        if(rules.sinOfXTimesY.applyRule) rules.sinOfXTimesY.ApplyRule(map, this);
        if(rules.sinofXToTheYthPower.applyRule) rules.sinofXToTheYthPower.ApplyRule(map, this);

        if(rules.cosOfXPlusY.applyRule) rules.cosOfXPlusY.ApplyRule(map, this);
        if(rules.cosOfXTimesY.applyRule) rules.cosOfXTimesY.ApplyRule(map, this);
        if(rules.cosOfXToTheYthPower.applyRule) rules.cosOfXToTheYthPower.ApplyRule(map, this);

        if(rules.tanOfXPlusY.applyRule) rules.tanOfXPlusY.ApplyRule(map, this);
        if(rules.tanOfXTimesY.applyRule) rules.tanOfXTimesY.ApplyRule(map, this);
        if(rules.tanOfXToTheYthPower.applyRule) rules.tanOfXToTheYthPower.ApplyRule(map, this);
    }
    /// <summary>
    /// This method will look at all of the surrounding indices of a given index and see how have a value of 1 (how many are solid).
    /// </summary>
    /// <returns>The surrounding wall count.</returns>
    /// <param name = "indexX">Index x.</param>
    /// <param name = "indexY">Index y.</param>
    public int GetSurroundingWallCount(int indexX, int indexY)
    {
        //This variable will store the amount of surrounding indices that are solid
        int wallCount = 0;
       
        //A 3x3 grid centered on (indexX, indexY) is looped through
        //Each index is checked to see if it is equal to 1
        for(int nX = (indexX - 1); nX <= indexX + 1; nX++)
        {
            for(int nY = (indexY - 1); nY <= indexY + 1; nY++)
            {
                //Because the surrounding indices will be looped through,
                //the current index cannot be on the edge of the map or in a corner, if it was Unity would give a null error
               
                //If this isn't an edge tile-
                if(IsInMapRange(nX, nY, true))
                {
                    //If the current index isn't the middle one (the one who's neighbors are being checked)-
                    if(nX != indexX || nY != indexY)
                    {
                        //If the current index is 1, 1 will be added
                        //If it is 0, 0 will be added
                        wallCount += map[nX, nY];
                    }
                }
                //Otherwise-
                else
                    //The amount of surrounding walls will be increased
                    wallCount++;
            }
        }
        return wallCount;
    }
    private List<List<Coord>> GetRegions(int tileType)
    {
        //This is the list of lists of Coords that we'll return
        List<List<Coord>> regions = new List<List<Coord>>();
        //This int array will hold all of the tile indices, and whether or not they've been checked (1 = checked, 0 = unchecked)
        int[,] checkedTiles = new int[width, height];

        //Here we'll loop through all of the indices in the map
        for(int x = 0; x < width; x++)
        {
            for(int y = 0; y < height; y++)
            {
                //If the current tile hasn't been checked and it's the correct tileType...
                if(checkedTiles[x, y] == 0 && map[x, y] == tileType)
                {
                    //...We'll create a list of Coords and start the flood-fill algorithm at the current indice's position
                    List<Coord> newRegion = GetRegionTiles(x, y);
                    //...Then we'll add the region we just created to the list of regions
                    regions.Add(newRegion);
                    //...After adding the region, we can intereate through all of it's Coords
                    foreach(Coord tile in newRegion)
                    {
                        //...Add set there corresponding indices in the checkTiles array to equal one (they'll be marked as checked)
                        checkedTiles[tile.x, tile.y] = 1;
                    }
                }
            }
        }
        return regions;
    }
    private List<Coord> GetRegionTiles(int startX, int startY)
    {
        List<Coord> tiles = new List<Coord>();
        //This int array will hold all of the tile indices, and whether or not they've been checked (1 = checked, 0 = unchecked)
        int[,] checkedTiles = new int[width, height];
        //The type of tile to be added to the list of Coords
        int tileType = map[startX, startY];
        //A queue of coordinates to process
        Queue<Coord> queue = new Queue<Coord>();
        //The first tile to be queued up is the start tile
        queue.Enqueue(new Coord(startX, startY));
        //We'll also set the start tile's index to equal one, marking it as checked
        checkedTiles[startX, startY] = 1;

        //While we have items in our queue...
        while(queue.Count > 0)
        {
            //Here we'll create a Coord and set it equal to the oldest item in the queue while simultaneously removing the queue
            Coord tile = queue.Dequeue();
            //Then we'll add the Coord we just created to the list of Coords that we'll return
            tiles.Add(tile);
            //Here we loop through the neighbors above, below, to the right, and to the left
            for(int x = tile.x - 1; x <= tile.x + 1; x++)
            {
                for(int y = tile.y - 1; y <= tile.y + 1; y++)
                {
                    //If the current tile index is actually in the map...
                    if(IsInMapRange(x, y, true) && (y == tile.y || x == tile.x))
                    {
                        //...Then, if this tile hasn't been looked at yet and it's the correct tileType
                        if(checkedTiles[x, y] == 0 && map[x, y] == tileType)
                        {
                            //...We'll set it's index in the checkedTiles array to one, indicating that it's been looked at
                            checkedTiles[x, y] = 1;
                            //...And add the Coord to the queue
                            queue.Enqueue(new Coord(x, y));
                        }
                    }
                }
            }
        }
        return tiles;
    }
    private bool IsInMapRange(int x, int y, bool bordersAreOutOfMap)
    {
        if(bordersAreOutOfMap)
            return x >= edgeWidth && x < width - edgeWidth && y >= edgeWidth && y < height - edgeWidth;
        else
            return x >= 0 && x < width && y >= 0 && y < height;
    }
    public struct Coord
    {
        public int x, y;

        public Coord(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    ///<summary>
    /// This class holds information about a room
    /// </summary>
    public class Room
    {
        /// <summary>
        /// A list of all the tiles in this room.
        /// </summary>
        public List<Coord> tiles;
        /// <summary>
        /// A list of all of the edge tiles.
        /// </summary>
        public List<Coord> edgeTiles;
        /// <summary>
        /// A list of rooms that share a common passage with this room.
        /// </summary>
        public List<Room> connectedRooms;
        /// <summary>
        /// The number of tiles that are in this room
        /// </summary>
        public int roomSize;

        /// <summary>
        /// Initializes an empty instance of the <see cref = "MapGenerator+Room"/> class.
        /// </summary>
        public Room(){}
        /// <summary>
        /// Initializes a new instance of the <see cref = "MapGenerator+Room"/> class, with parameters.
        /// </summary>
        /// <param name = "roomTiles">Room tiles.</param>
        /// <param name = "map">Map.</param>
        public Room(List<Coord> roomTiles, int[,] map)
        {
            //We'll initialize set up all of the variables
            tiles = roomTiles;
            roomSize = tiles.Count;
            connectedRooms = new List<Room>();

            edgeTiles = new List<Coord>();
            //Here we'll loop through every tile in the room
            foreach(Coord tile in tiles)
            {
                //Then we'll index through the four neighbors of the current tle index
                for(int x = tile.x - 1; x <= tile.x + 1; x++)
                {
                    for(int y = tile.y - 1; y <= tile.y + 1; y++)
                    {
                        //...If the tiles x or y value is equal to the current tile's...
                        if(x == tile.x || y == tile.y)
                        {
                            //...Then we know it isn't diagonal to the current tile index
                            //...And if it is a wall tile
                            if(map[x, y] == 1)
                            {
                                //...We'll know it's an edge tile and can add it to the list of edge tiles
                                edgeTiles.Add(tile);
                            }
                        }
                    }
                }
            }
        }
        public static void ConnectRooms(Room roomA, Room roomB)
        {
            roomA.connectedRooms.Add(roomB);
            roomB.connectedRooms.Add(roomA);
        }
        public bool IsConnected(Room otherRoom)
        {
            return connectedRooms.Contains(otherRoom);
        }
    }
    [Serializable]
    public class Rules
    {
        [SerializeField]
        public Lague lague;
        [SerializeField]
        public MazeWorld mazeWorld;
        [SerializeField]
        public GameOfLife gameOfLife;

        [SerializeField]
        public PerlinNoise perlinNoise;

        [SerializeField]
        public CustomFunction customFunction;

        [SerializeField]
        public Quadratic quadratic;

        [SerializeField]
        public LogOfXPlusY logOfXPlusY;
        [SerializeField]
        public LogOfXTimesY logOfXTimesY;
        [SerializeField]
        public LogOfXToTheYthPower logOfXToTheYthPower;

        [SerializeField]
        public SinOfXPlusY sinOfXPlusY;
        [SerializeField]
        public SinOfXTimesY sinOfXTimesY;
        [SerializeField]
        public SinOfXToTheYthPower sinofXToTheYthPower;

        [SerializeField]
        public SinOfXPlusY cosOfXPlusY;
        [SerializeField]
        public SinOfXTimesY cosOfXTimesY;
        [SerializeField]
        public SinOfXToTheYthPower cosOfXToTheYthPower;

        [SerializeField]
        public SinOfXPlusY tanOfXPlusY;
        [SerializeField]
        public SinOfXTimesY tanOfXTimesY;
        [SerializeField]
        public TanOfXToTheYthPower tanOfXToTheYthPower;

        [Serializable]
        public class Lague
        {
            public bool applyRule = false;
            public int iterations = 1;

            [Range(0, 8)]
            public int surroundingWallCountToBeSolid = 4;

            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    iterations = Mathf.Clamp(iterations, 1, Int32.MaxValue);
                    for(int i = 0; i < iterations; i++)
                    {
                        for(int x = 0; x < map.GetLength(0); x++)
                        {
                            for(int y = 0; y < map.GetLength(1); y++)
                            {
                                //We want to know how many neighboring tiles does the current tile index have that are walls
                                //This will be found via the method GetSurroundingWallCount below
                                int surroundingWalls = mapGenerator.GetSurroundingWallCount(x, y);
                               
                                //If the current index is surrounded by a certain amount (ruleValue) of solid tiles it'll be made solid as well
                                if(surroundingWalls > surroundingWallCountToBeSolid)
                                    map[x, y] = 1;
                                //Otherwise it'll be made empty, set to 0
                                else if(surroundingWalls < surroundingWallCountToBeSolid)
                                    map[x, y] = 0;
                            }
                        }
                    }
                }
                return;
            }
        }
        [Serializable]
        public class MazeWorld
        {
            public bool applyRule = false;
            public int iterations = 5;

            [Range(0, 8)]
            public int neutralNeighborCount = 2;

            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    iterations = Mathf.Clamp(iterations, 1, Int32.MaxValue);
                    for(int i = 0; i < iterations; i++)
                    {
                        for(int x = 0; x < map.GetLength(0); x++)
                        {
                            for(int y = 0; y < map.GetLength(1); y++)
                            {
                                //We want to know how many neighboring tiles the current tile index has that are walls
                                //This will be found via the method GetSurroundingWallCount below
                                int surroundingWalls = mapGenerator.GetSurroundingWallCount(x, y);
                               
                                //If the cell is solid-
                                if(map[x, y] == 1)
                                {
                                    //If the cell has less than 2 neighbors-
                                    if(surroundingWalls < (neutralNeighborCount - 1))
                                        //It will be made empty (as if caused by under-population)
                                        map[x, y] = 0;
                                    //Otherwise, if the cell has 2 or 3 neighbors-
                                    else if(surroundingWalls > (neutralNeighborCount - 1) && neutralNeighborCount < 4)
                                        //The cell will be made solid (as if it lived on to the next generation)
                                        map[x, y] = 1;
                                    //Otherwise, the cell has more than three neighbors-
                                    else
                                        //And it will be made empty (as if by over-population)
                                        map[x, y] = 0;
                                }
                                //Otherwise, the index is empty
                                else
                                    //If the cell has 3 neighbors
                                    if(surroundingWalls == neutralNeighborCount)
                                        //It will be made solid (as if by reproduction)
                                        map[x, y] = 1;
                            }
                        }
                    }
                }
                return;
            }
        }
        [Serializable]
        public class GameOfLife
        {
            public bool applyRule = false;
            public int iterations = 1;

            [Range(0, 8)]
            public int neutralNeighborCount = 3;

            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                iterations = Mathf.Clamp(iterations, 1, Int32.MaxValue);
                if(applyRule)
                {
                    for(int i = 0; i < iterations; i++)
                    {
                        for(int x = 0; x < map.GetLength(0); x++)
                        {
                            for(int y = 0; y < map.GetLength(1); y++)
                            {
                                //We want to know how many neighboring tiles does the current tile index have that are walls
                                //This will be found via the method GetSurroundingWallCount below
                                int surroundingWalls = mapGenerator.GetSurroundingWallCount(x, y);
                               
                                //If the cell is solid-
                                if(map[x, y] == 1)
                                {
                                    //If the cell has less than 2 neighbors-
                                    if(surroundingWalls < (neutralNeighborCount - 1))
                                        //It will be made empty (as if caused by under-population)
                                        map[x, y] = 0;
                                    //Otherwise, if the cell has 2 or 3 neighbors-
                                    else if(surroundingWalls > (neutralNeighborCount - 1) && surroundingWalls < (neutralNeighborCount + 1))
                                        //The cell will be made solid (as if it lived on to the next generation)
                                        map[x, y] = 1;
                                    //Otherwise, the cell has more than three neighbors-
                                    else
                                        //And it will be made empty (as if by over-population)
                                        map[x, y] = 1;
                                }
                                //Otherwise, the index is empty
                                else
                                    //If the cell has 3 neighbors
                                    if(surroundingWalls > (neutralNeighborCount - 1) && surroundingWalls < (neutralNeighborCount + 1))
                                        //It will be made solid (as if by reproduction)
                                        map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class PerlinNoise
        {
            public bool applyRule = false;

            public bool erase;
            public Vector2 position;
            public float scale = 10f;

            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    scale = Mathf.Clamp(scale, 1f, float.MaxValue);
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.PerlinNoise((x + position.x) / scale, (y + position.y) / scale);
                           
                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }   
        [Serializable]
        public class CustomFunction
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(-100, 100)]
            public int xExponent = 1;

            public Operator mathOperator;

            [Range(-100, 100)]
            public int yExponent = 1;

            public enum Operator
            {
                Add,
                Multiply,
                Exponent
            }
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value;

                            switch(mathOperator)
                            {
                                case Operator.Add:
                                    value = (x ^ xExponent) + (y ^ yExponent);
                                    break;
                                case Operator.Multiply:
                                    value = (x ^ xExponent) * (y ^ yExponent);
                                    break;
                                case Operator.Exponent:
                                    value = (x ^ xExponent) ^ (y ^ yExponent);
                                    break;
                                default:
                                    value = 1;
                                    break;
                            }
                           
                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class Quadratic
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(-350f, 0f)]
            public float curve = 1f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = ((-y + (Mathf.Sqrt((y^2) - 4 * x * curve)))/(2 * x));
                           
                            if(value > 0.5f)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class LogOfXPlusY
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(1f, 5f)]
            public float bias = 0.5f;

            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Log(x + y);
                           
                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class LogOfXTimesY
        {
            public bool applyRule = false;

            //[Range(0f, 5f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Log(x * y);
                           
                            if(value > bias)
                                map[x, y] = 1;
                        }
                    }
                }
            }
        }
        [Serializable]
        public class LogOfXToTheYthPower
        {
            public bool applyRule = false;

            //[Range(0f, 5f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Log(x ^ y);
                           
                            if(value > bias)
                                map[x, y] = 1;
                        }
                    }
                }
            }
        }
        [Serializable]
        public class SinOfXPlusY
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Sin(x + y);

                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class SinOfXTimesY
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Sin(x * y);

                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class SinOfXToTheYthPower
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Sin(x ^ y);

                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class CosOfXPlusY
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Cos(x + y);

                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class CosOfXTimesY
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Cos(x * y);

                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class CosOfXToTheYthPower
        {
            public bool applyRule = false;

            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Cos(x ^ y);

                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class TanOfXPlusY
        {
            public bool applyRule = false;
           
            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Tan(x + y);
                           
                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class TanOfXTimesY
        {
            public bool applyRule = false;
           
            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Tan(x * y);
                           
                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
        [Serializable]
        public class TanOfXToTheYthPower
        {
            public bool applyRule = false;
           
            public bool erase = false;
            [Range(0f, 1f)]
            public float bias = 0.5f;
           
            public void ApplyRule(int[,] map, MapGenerator mapGenerator)
            {
                if(applyRule)
                {
                    for(int x = 0; x < map.GetLength(0); x++)
                    {
                        for(int y = 0; y < map.GetLength(1); y++)
                        {
                            float value = Mathf.Tan(x ^ y);
                           
                            if(value > bias)
                            {
                                if(erase)
                                    map[x, y] = 0;
                                else
                                    map[x, y] = 1;
                            }
                        }
                    }
                }
            }
        }
    }
}

Hi there. The error is occurring because it is searching for neighbour tiles outside of the map bounds. This is because in your implementation rooms can extend right to the edge of the map. I recommend changing the code inside the for loops of the RandomFillMap method to this:

    // Ensure that all rooms are enclosed in the map area
    if (x == 0 || x == width-1 || y == 0 || y == height -1) {
        map[x,y] = 1;
    }
    else {
    //Using the if/else notation, the current index will be set to 1 if the generated number is less than fillPercentage,
    //otherwise it will be set to 0
        map[x, y] = (prng.Next(0, 100) < fillPercentage) ? 1 : 0;
    }
1 Like
if(x == tile.x || y == tile.y)
{
   //The error points to the line below
   if(map[x, y] == 1)
   {
      edgeTiles.Add(tile);
   }
}

Ok, so I changed the Rule constructor code to check if the current tile exists. But now if I change the minWallRegion (wallRegionThreshold), Unity freezes. Does it have to do with the while-loop? Do I need to clear the queue outside of the while-loop so that when I call the method on the next frame it doesn’t add the same stuff to it again? Though, I guess clearing the queue will prevent it from doing anything

public class Room
    {
        /// <summary>
        /// A list of all the tiles in this room.
        /// </summary>
        public List<Coord> tiles;
        /// <summary>
        /// A list of all of the edge tiles.
        /// </summary>
        public List<Coord> edgeTiles;
        /// <summary>
        /// A list of rooms that share a common passage with this room.
        /// </summary>
        public List<Room> connectedRooms;
        /// <summary>
        /// The number of tiles that are in this room
        /// </summary>
        public int roomSize;

        /// <summary>
        /// Initializes an empty instance of the <see cref = "MapGenerator+Room"/> class.
        /// </summary>
        public Room(){}
        /// <summary>
        /// Initializes a new instance of the <see cref = "MapGenerator+Room"/> class, with parameters.
        /// </summary>
        /// <param name = "roomTiles">Room tiles.</param>
        /// <param name = "map">Map.</param>
        public Room(List<Coord> roomTiles, int[,] map, int edgeWidth)
        {
            //We'll initialize set up all of the variables
            tiles = roomTiles;
            roomSize = tiles.Count;
            connectedRooms = new List<Room>();

            edgeTiles = new List<Coord>();
            //Here we'll loop through every tile in the room
            foreach(Coord tile in tiles)
            {
                //Then we'll index through the four neighbors of the current tle index
                for(int x = tile.x - 1; x <= tile.x + 1; x++)
                {
                    for(int y = tile.y - 1; y <= tile.y + 1; y++)
                    {
                        if(x < map.GetLength(0) - edgeWidth && x >= edgeWidth && y < map.GetLength(1) - edgeWidth && y >= edgeWidth)
                        {
                            //...If the tiles x or y value is equal to the current tile's...
                            if(x == tile.x || y == tile.y)
                            {
                                //...Then we know it isn't diagonal to the current tile index
                                //...And if it is a wall tile
                                if(map[x, y] == 1)
                                {
                                    //...We'll know it's an edge tile and can add it to the list of edge tiles
                                    edgeTiles.Add(tile);
                                }
                            }
                        }
                    }
                }
            }
        }
        public static void ConnectRooms(Room roomA, Room roomB)
        {
            roomA.connectedRooms.Add(roomB);
            roomB.connectedRooms.Add(roomA);
        }
        public bool IsConnected(Room otherRoom)
        {
            return connectedRooms.Contains(otherRoom);
        }
    }

I’ll have a look in the morning, but did you try making the change I suggested? Because that is the root of this error.

Yeah I did, but I still got the error. I’ll try adding it back and seeing if your change, accompanied with mine, fixes it. Once again, I really appreciate your help :slight_smile:
[Edit]
I removed my added code which stops Unity from freezing, but I’m now getting the error again. Here’s the error, just in-case it has changed:
IndexOutOfRangeException: Array index is out of range.
MapGenerator+Room…ctor (System.Collections.Generic.List`1 roomTiles, System.Int32[,] map, Int32 edgeWidth) (at Assets/Code/Cave Generation/MapGenerator.cs:476)
MapGenerator.ProcessRooms () (at Assets/Code/Cave Generation/MapGenerator.cs:159)
MapGenerator.GenerateMap () (at Assets/Code/Cave Generation/MapGenerator.cs:107)
MapGenerator.Update () (at Assets/Code/Cave Generation/MapGenerator.cs:92)

hi when i did the same code that you did in episode 1, the caves wouldn’t even show, the screen just darkened and after a few seconds it switched to gamer view but there was no map. is there anything i could do to help the code work?

The issue I run into is

  • The debug lines never show up
  • Index out of range is thrown seemingly randomly, and always from within ConnectClosestRooms - the chance increases for higher fill percent and reduces if region size thresholds are provided

The offending code (haven’t placed breakpoints to debug yet) is:

private void ProcessRooms(in int wallSizeThreshold, in int roomSizeThreshold)
{
    var wallRegions = GetRegions(TileType.Wall);

    foreach (var wallRegion in wallRegions)
    {
        if (wallRegion.Count < wallSizeThreshold)
        {
            foreach (var tile in wallRegion)
            {
                map[tile.x, tile.y] = TileType.Room;
            }
        }
    }

    var roomRegions = GetRegions(TileType.Room);
    var survivingRooms = new List<Room>();

    foreach (var roomRegion in roomRegions)
    {
        if (roomRegion.Count < roomSizeThreshold)
        {
            foreach (var tile in roomRegion)
            {
                map[tile.x, tile.y] = TileType.Wall;
            }
        }
        else
        {
            survivingRooms.Add(new Room(roomRegion, map));
        }
    }
    ConnectClosestRooms(survivingRooms);
}

void ConnectClosestRooms(in List<Room> allRooms)
{
    int bestDistance = 0;
    (int x, int y) bestTileA = (0, 0), bestTileB = (0, 0);
    var bestRoomA = new Room();
    var bestRoomB = new Room();
    bool possibleConnectionFound = false;

    foreach (var roomA in allRooms)
    {
        possibleConnectionFound = false;

        foreach (var roomB in allRooms)
        {
            if (roomA.IsConnected(roomB))
            {
                possibleConnectionFound = false;
                break;
            }
            if (roomA != roomB)
            {
                for (int tileIndexA = 0; tileIndexA < roomA.edgeTiles.Count; ++tileIndexA)
                {
                    for (int tileIndexB = 0; tileIndexB < roomA.edgeTiles.Count; ++tileIndexB)
                    {
                        var tileA = roomA.edgeTiles[tileIndexA];
                        var tileB = roomB.edgeTiles[tileIndexB];

                        int sqDistanceBetweenRooms = (int)(Mathf.Pow(tileA.x - tileB.x, 2) + Mathf.Pow(tileA.y - tileB.y, 2));
                        if (sqDistanceBetweenRooms < bestDistance || !possibleConnectionFound)
                        {
                            bestDistance = sqDistanceBetweenRooms;
                            possibleConnectionFound = true;
                            bestTileA = tileA;
                            bestTileB = tileB;
                            bestRoomA = roomA;
                            bestRoomB = roomB;
                        }
                    }
                }
            }
        }

        if (possibleConnectionFound)
        {
            CreatePassage(bestRoomA, bestRoomB, bestTileA, bestTileB);
        }
    }
}

The fun thing is that this segment works absolutely normally from the repository code.