Skip to content

Commit

Permalink
Fix monster weights based on dungeon level
Browse files Browse the repository at this point in the history
  • Loading branch information
leomartius committed Oct 16, 2024
1 parent c27e943 commit edee9e0
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 7 deletions.
33 changes: 29 additions & 4 deletions game/monsters.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,32 @@ def spawn(self, x: int, y: int) -> Actor:
]


def eligible_monsters(depth: int) -> list[MonsterType]:
min_level = max(1, min(depth - 5, len(monsters) - 4))
max_level = max(5, min(depth + 4, len(monsters)))
return [m for m in monsters[min_level - 1:max_level] if m.generate]
def get_monster_types(depth: int) -> tuple[list[MonsterType], list[int]]:
items = []
weights = []
for i, weight in enumerate(_weights(depth)):
if weight > 0 and monsters[i].generate:
items.append(monsters[i])
weights.append(weight)
return items, weights


def _weights(depth: int) -> list[int]:
assert depth >= 1
weights = [0] * 26
under = 0
over = 0
for i in range(depth - 6, depth + 4):
if i < 0:
under += 10
elif i >= len(weights):
over += 10
else:
weights[i] += 10
for i in range(0, 5):
weights[i] += under // 5
for i in range(21, 26):
weights[i] += over // 5
assert len(weights) == 26
assert sum(weights) == 100
return weights
7 changes: 4 additions & 3 deletions game/procgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from game.entity import ArmorItem, Item, WeaponItem
from game.items import get_item_categories
from game.level import Level
from game.monsters import eligible_monsters
from game.monsters import get_monster_types

# hardcoded 80x22 subdivision
H_GRID = [0, 26, 27, 53, 54, 80]
Expand Down Expand Up @@ -247,8 +247,9 @@ def place_gold(room: Room, level: Level) -> None:

def place_monster(room: Room, level: Level) -> None:
x, y = find_empty_spot_in_room(room, level)
m_type = rng.choice(eligible_monsters(level.depth))
monster = m_type.spawn(x, y)
monster_types, weights = get_monster_types(level.depth)
monster_type = rng.choices(monster_types, weights).pop()
monster = monster_type.spawn(x, y)
level.entities.add(monster)


Expand Down
66 changes: 66 additions & 0 deletions tests/test_monsters_weights.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import pytest

import game.monsters


@pytest.mark.skip(reason="Enable to dump the full table.")
def test_dump_weights():
print()
for d in range(1, 33):
weights = game.monsters._weights(d)
print(" ".join(f"{item:2d}" for item in weights))


def test_level_1():
weights = game.monsters._weights(1)
assert weights == [20] * 5 + [0] * 21


def test_level_2_to_5():
for d in range(2, 6):
weights = game.monsters._weights(d)
assert weights == [22 - 2 * d] * 5 + [10] * (d - 1) + [0] * (22 - d)


def test_level_6():
weights = game.monsters._weights(6)
assert weights == [10] * 10 + [0] * 16


def test_level_7_to_21():
for d in range(7, 22):
weights = game.monsters._weights(d)
assert weights == [0] * (d - 6) + [10] * 10 + [0] * (22 - d)


def test_level_22():
weights = game.monsters._weights(22)
assert weights == [0] * 16 + [10] * 10


def test_level_23_to_26():
for d in range(23, 27):
weights = game.monsters._weights(d)
assert weights == [0] * (d - 6) + [10] * (27 - d) + [2 * d - 34] * 5


def test_level_27():
weights = game.monsters._weights(27)
assert weights == [0] * 21 + [20] * 5


def test_level_28_to_31():
for d in range(28, 32):
weights = game.monsters._weights(d)
assert weights == [0] * 21 + [2 * d - 44] * (d - 27) + [2 * d - 34] * (32 - d)


def test_level_32():
weights = game.monsters._weights(32)
assert weights == [0] * 21 + [20] * 5


def test_level_33_and_beyond():
for d in range(33, 100):
weights = game.monsters._weights(d)
assert weights == [0] * 21 + [20] * 5

0 comments on commit edee9e0

Please sign in to comment.