Search code examples
c#console-applicationadventure

Beginner making a Console app survival game, unsure on how to generate room positions on a square grid


I'll go over what I have so far and then explain the problem.

I have a class called Room, and each room has an XPos and a YPos, as well as multiple other parameters that aren't relevant. Up until now, I have been assigning these values in the main method, manually making a new Room with specified values. This has gotten impractical and boring, so I decided to try randomly generating the rooms.

I started off by making a method called GenerateRooms, with a return type of List. This is assigned to the rooms list which is communicated with at runtime. GenerateRooms:

private static List<Room> GenerateRooms(int gridSize)
    {
        RandomRoomGenerator randomRoomGenerator = new RandomRoomGenerator();
        int numRooms = gridSize * gridSize;
        List<Room> rooms = new List<Room>();

        for (int i = 0; i < numRooms; i++)
        {
            rooms.Add(randomRoomGenerator.GenerateRoom(gridSize, rooms));
        }

        return rooms;
    }

As you can see, it also takes in a parameter called gridSize. This is the length/width of the world. It is used later in my attempts to generate room positions. Inside GenerateRoom, I assign values to a room constructor. For things like the room's description or items, I haven't had too many problems randomizing, but the position has given me a lot of trouble.

GenerateRoom:

public class RandomRoomGenerator
{
    public Room GenerateRoom(int gridSize, List<Room> rooms)
    {
        RandomRoomDescriptions randomRoomDescriptions = new RandomRoomDescriptions();
        RandomItemGenerator randomItemGenerator = new RandomItemGenerator();
        ItemDB itemDb = new ItemDB();

        string description = randomRoomDescriptions.roomDescriptions[new Random().Next(0, randomRoomDescriptions.roomDescriptions.Count)];

        Room room = new Room(description, GenerateXPos(rooms, gridSize), GenerateYPos(rooms, gridSize), new Random().Next(1, 7), new Random().Next(1, 7), new Random().Next(1, 7), randomItemGenerator.GenerateRandomItems(itemDb));
        return room;
    }

    private int GenerateXPos(List<Room> rooms, int gridSize)
    {
        int xPos = 0;

        foreach (var room in rooms)
        {
            if (room.XPos == xPos)
            {
                xPos++;
                if (xPos == gridSize)
                {
                    return xPos;
                }
            } 
        }


        return xPos;

    }

    private int GenerateYPos(List<Room> rooms, int gridSize)
    {
        int yPos = 0;

        foreach (var room in rooms)
        {
            if (room.XPos == gridSize && room.YPos == yPos)
            {
                yPos++; 
                return yPos;
            }
        }

        return yPos;

    }

With the code you can see here, I am able to create a horizontal line of rooms three rooms long, as well as six stacked rooms at (3, 1).

The problem: I need to make it so that GenerateXPos only iterates through rooms on the current row. I have tried making a variable called YPos in the GenerateRoom class and incrementing it whenever XPos reaches GridSize, but that didn't work.

What I think another solution could be would be removing the need for an X and Y pos in the Room constructor alltogether, and assign the positions in the main method. But that's kinda the last resort. I want to grow my experience and de-mystify coding so I can learn better. If I didn't give enough info, let me know. Here's some relevant classes just in case: Main:

static void Main(string[] args)
    {
        Console.WriteLine("Welcome to the wasteland.");
        List<Room> rooms = GenerateRooms(3);
        var currentRoom = rooms[0];



        Player player = new Player(100, 10, 10, 10, 0,0);
        player.Inventory.Add(player.CurrentWeapon = new Weapon("Fists", "Regular fists.", 1));

        GameState.GameStates currentGameState = GameState.GameStates.Exploring;


        while (player.Health > 0 && player.Hunger < 50 && player.Thirst < 50 && player.Radiation < 50)
        {
            while (currentGameState == GameState.GameStates.Exploring)
            {
                Input.HandleExplorationInput(player, ref currentRoom, rooms, ref currentGameState);

            }

            while (currentGameState == GameState.GameStates.Fighting)
            {
                Input.HandleEncounterInput(player, currentRoom.Enemy, player.CurrentWeapon, ref currentGameState, currentRoom);
            }
        }

        Console.WriteLine("You died!");

    }

Room:

public class Room
{
    public string Description { get; set; }
    public int XPos { get; set; }
    public int YPos { get; set; }
    public int HungerToAdd { get; set; }
    public int ThirstToAdd { get; set; }
    public int RadiationToAdd { get; set; }
    public List<Item> Items { get; set; }
    public Enemy Enemy { get; set; }


    //Description, coord, and stats.
    public Room(string description, int xPos, int yPos, int hungerToAdd, int thirstToAdd, int radiationToAdd)
    {
        this.Description = description;
        this.XPos = xPos;
        this.YPos = yPos;
        this.HungerToAdd = hungerToAdd;
        this.ThirstToAdd = thirstToAdd;
        this.RadiationToAdd = radiationToAdd;
    }

    //Description, coord, stats, and items.
    public Room(string description, int xPos, int yPos, int hungerToAdd, int thirstToAdd, int radiationToAdd, List<Item> items)
    {
        this.Description = description;
        this.XPos = xPos;
        this.YPos = yPos;
        this.Items = items;
        this.HungerToAdd = hungerToAdd;
        this.ThirstToAdd = thirstToAdd;
        this.RadiationToAdd = radiationToAdd;
    }

    //Description, coord, stats, items, and enemy.
    public Room(string description, int xPos, int yPos, int hungerToAdd, int thirstToAdd, int radiationToAdd, List<Item> items, Enemy enemy)
    {
        this.Description = description;
        this.XPos = xPos;
        this.YPos = yPos;
        this.Items = items;
        this.Enemy = enemy;
        this.HungerToAdd = hungerToAdd;
        this.ThirstToAdd = thirstToAdd;
        this.RadiationToAdd = radiationToAdd;

    }

    //Description, coord, stats, and enemy.
    public Room(string description, int xPos, int yPos, int hungerToAdd, int thirstToAdd, int radiationToAdd, Enemy enemy)
    {
        this.Description = description;
        this.XPos = xPos;
        this.YPos = yPos;
        this.Enemy = enemy;
        this.HungerToAdd = hungerToAdd;
        this.ThirstToAdd = thirstToAdd;
        this.RadiationToAdd = radiationToAdd;

    }
}

Solution

  • It seem what you what is a way to create a grid of rooms (cells that have a width and height = gridSize) in which you have several properties. One of which is a room description. This room description should be randomly selected from a list you have (somewhere). For that all you really need to do is create a list (HashSet in this case--see why below) of random integers, whereby the maximum value in the HashSet should be at most the maximum number of elements in your "randomRoomDescriptions" List (or Array?).

    Therefore, your code can be greatly simplified.

    The Room class could look like:

    public class Room
    {
        public string Description { get; set; }
        public int XPos { get; set; }
        public int YPos { get; set; }
        public int HungerToAdd { get; set; }
        public int ThirstToAdd { get; set; }
        public int RadiationToAdd { get; set; }
        public List<int> Items { get; set; }
        public string Enemy { get; set; }
    
        //Description, coord, and stats.
        public Room(string description, int xPos, int yPos)
        {
            CreateDescriptionList();
            Random rand = new Random();
    
            this.Description = description;
            this.XPos = xPos;
            this.YPos = yPos;
            this.HungerToAdd = rand.Next(1, 7);
            this.ThirstToAdd = rand.Next(1, 7);
            this.RadiationToAdd = rand.Next(1, 7);
            //this.Items = do something here!
            //this.Enemy = "Fred"; //Do something here, too!
        }
    }
    

    Now simply populate the Room List "rooms":

            var maxGrid = 3;
            var rooms = CreateRandomRooms(maxGrid);
    

    using the following methods:

        private List<Room> CreateRandomRooms(int grid)
        {
            RandomRoomDescriptions randomRoomDescriptions = new RandomRoomDescriptions();
            var listRooms = new List<Room>();
            var rand = GetRandomRoomIndicies(grid, Room.maxDecriptionEntries);
    
            int index = 0;
            for (int x = 0; x < grid; x++)
            {
                for (int y = 0; y < grid; y++)
                {
                    var i = rand.ElementAt(index);
                    var descr = randomRoomDescriptions.roomDescriptions[i];
                    var room = new Room(descr, x, y);
                    listRooms.Add(room);
                    index++;
                }
            }
    
            return listRooms;
        }
    
    
    
        private HashSet<int> GetRandomRoomIndicies(int grid, int maxRand)
        {
            int minRand = 0;
            int maxCount = grid * grid;
            var rand = new Random();
    
            var randHashset = new HashSet<int>();
    
            while (randHashset.Count < maxCount)
            {
                randHashset.Add(rand.Next(minRand, maxRand));
            }
    
            return randHashset;
        }
    

    The key to this is basically the method "GetRandomRoomIndicies". It creates a HashSet of random integer values (max value = max number of Descriptions in description List). This HashSet will be used to get a random description from your list and use it to describe the Room. While creating the Room a random number generator is used to populate things like "Hunger", "Thirst" and "Radiation". You will need to do something about "Items" and "Enemy".

    The nice thing about the HashSet is it will only allow unique values to be added. So you just keep "Adding" until the collection has reached the size you want.