From 1b354b007cafa3450d20b71883dc66443d4bce47 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 8 Feb 2022 09:21:19 -0500 Subject: [PATCH] Weighted random generation, completes Part 12 --- game_map.py | 7 --- procgen.py | 121 ++++++++++++++++++++++++++++++++++++++------------ setup_game.py | 5 --- 3 files changed, 92 insertions(+), 41 deletions(-) diff --git a/game_map.py b/game_map.py index e39d6f3..bd4876a 100644 --- a/game_map.py +++ b/game_map.py @@ -127,8 +127,6 @@ class GameWorld: max_rooms: int, room_min_size: int, room_max_size: int, - max_monsters_per_room: int, - max_items_per_room: int, current_floor: int = 0 ): self.engine = engine @@ -141,9 +139,6 @@ class GameWorld: self.room_min_size = room_min_size self.room_max_size = room_max_size - self.max_monsters_per_room = max_monsters_per_room - self.max_items_per_room = max_items_per_room - self.current_floor = current_floor def generate_floor(self) -> None: @@ -157,7 +152,5 @@ class GameWorld: room_max_size=self.room_max_size, map_width=self.map_width, map_height=self.map_height, - max_monsters_per_room=self.max_monsters_per_room, - max_items_per_room=self.max_items_per_room, engine=self.engine, ) diff --git a/procgen.py b/procgen.py index f80e0d4..28a3a64 100644 --- a/procgen.py +++ b/procgen.py @@ -1,7 +1,7 @@ from __future__ import annotations import random -from typing import Iterator, List, Tuple, TYPE_CHECKING +from typing import Dict, Iterator, List, Tuple, TYPE_CHECKING import tcod @@ -11,6 +11,76 @@ import tile_types if TYPE_CHECKING: from engine import Engine + from entity import Entity + +max_items_by_floor = [ + (1, 1), + (4, 2), +] + +max_monsters_by_floor = [ + (1, 2), + (4, 3), + (6, 5), +] + +item_chances: Dict[int, List[Tuple[Entity, int]]] = { + 0: [(entity_factories.health_potion, 35)], + 2: [(entity_factories.confusion_scroll, 10)], + 4: [(entity_factories.lightning_scroll, 25)], + 6: [(entity_factories.fireball_scroll, 25)], +} + +enemy_chances: Dict[int, List[Tuple[Entity, int]]] = { + 0: [(entity_factories.orc, 80)], + 3: [(entity_factories.troll, 15)], + 5: [(entity_factories.troll, 30)], + 7: [(entity_factories.troll, 60)], +} + + +def get_max_value_for_floor( + max_value_by_floor: List[Tuple[int, int]], + floor: int +) -> int: + current_value = 0 + + for floor_minimum, value in max_value_by_floor: + if floor_minimum > floor: + break; + else: + current_value = value; + + return current_value + + +def get_entities_at_random( + weighted_chances_by_floor: Dict[int, List[Tuple[Entity, int]]], + number_of_entities: int, + floor: int, +) -> List[Entity]: + entity_weighted_chances = {} + + for key, values in weighted_chances_by_floor.items(): + if key > floor: + break + else: + for value in values: + entity = value[0] + weighted_chance = value[1] + + entity_weighted_chances[entity] = weighted_chance + + entities = list(entity_weighted_chances.keys()) + entity_weighted_chance_values = list(entity_weighted_chances.values()) + + chosen_entities = random.choices( + entities, + weights=entity_weighted_chance_values, + k=number_of_entities + ) + + return chosen_entities class RectangularRoom: @@ -45,37 +115,32 @@ class RectangularRoom: def place_entities( room: RectangularRoom, dungeon: GameMap, - maximum_monsters: int, - maximum_items: int, + floor_number: int, ) -> None: - number_of_monsters = random.randint(0, maximum_monsters) - number_of_items = random.randint(0, maximum_items) + number_of_monsters = random.randint( + 0, get_max_value_for_floor(max_monsters_by_floor, floor_number) + ) + number_of_items = random.randint( + 0, get_max_value_for_floor(max_items_by_floor, floor_number) + ) - for i in range(number_of_monsters): + monsters: List[Entity] = get_entities_at_random( + enemy_chances, + number_of_monsters, + floor_number + ) + items: List[Entity] = get_entities_at_random( + item_chances, + number_of_items, + floor_number + ) + + for entity in monsters + items: x = random.randint(room.x1 + 1, room.x2 - 1) y = random.randint(room.y1 + 1, room.y2 - 1) if not any(entity.x == x and entity.y == y for entity in dungeon.entities): - if random.random() < 0.8: - entity_factories.orc.spawn(dungeon, x, y) - else: - entity_factories.troll.spawn(dungeon, x, y) - - for i in range(number_of_items): - x = random.randint(room.x1 + 1, room.x2 - 1) - y = random.randint(room.y1 + 1, room.y2 - 1) - - if not any(entity.x == x and entity.y == y for entity in dungeon.entities): - item_chance = random.random() - - if item_chance < 0.7: - entity_factories.health_potion.spawn(dungeon, x, y) - elif item_chance < 0.8: - entity_factories.fireball_scroll.spawn(dungeon, x, y) - elif item_chance < 0.9: - entity_factories.confusion_scroll.spawn(dungeon, x, y) - else: - entity_factories.lightning_scroll.spawn(dungeon, x, y) + entity.spawn(dungeon, x, y) def tunnel_between(start: Tuple[int, int], end: Tuple[int, int]) -> Iterator[Tuple[int, int]]: @@ -103,8 +168,6 @@ def generate_dungeon( room_max_size: int, map_width: int, map_height: int, - max_monsters_per_room: int, - max_items_per_room: int, engine: Engine, ) -> GameMap: """Generate a new dungeon map.""" @@ -143,7 +206,7 @@ def generate_dungeon( center_of_last_room = new_room.center - place_entities(new_room, dungeon, max_monsters_per_room, max_items_per_room) + place_entities(new_room, dungeon, engine.game_world.current_floor) dungeon.tiles[center_of_last_room] = tile_types.down_stairs dungeon.downstairs_location = center_of_last_room diff --git a/setup_game.py b/setup_game.py index a343a67..97eb087 100644 --- a/setup_game.py +++ b/setup_game.py @@ -28,9 +28,6 @@ def new_game() -> Engine: room_min_size = 6 max_rooms = 30 - max_monsters_per_room = 2 - max_items_per_room = 2 - player = copy.deepcopy(entity_factories.player) engine = Engine(player) @@ -42,8 +39,6 @@ def new_game() -> Engine: room_max_size=room_max_size, map_width=map_width, map_height=map_height, - max_monsters_per_room=max_monsters_per_room, - max_items_per_room=max_items_per_room, ) engine.game_world.generate_floor() engine.update_fov()