This commit is contained in:
Yohann D'ANELLO 2021-01-10 22:08:42 +01:00
parent 01cdea6edc
commit 9df1ac7883
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
4 changed files with 38 additions and 34 deletions

View File

@ -2,7 +2,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from json import JSONDecodeError from json import JSONDecodeError
from random import randint
from typing import Any, Optional, List from typing import Any, Optional, List
import curses import curses
import json import json

View File

@ -4,7 +4,7 @@
from enum import Enum, auto from enum import Enum, auto
from math import ceil, sqrt from math import ceil, sqrt
from itertools import product from itertools import product
from random import choice, choices, randint from random import choice, randint
from typing import List, Optional, Any, Dict, Tuple from typing import List, Optional, Any, Dict, Tuple
from queue import PriorityQueue from queue import PriorityQueue
from functools import reduce from functools import reduce

View File

@ -20,14 +20,15 @@ DEFAULT_PARAMS = {
"max_h_corr": 12, "max_h_corr": 12,
"large_circular_room": .10, "large_circular_room": .10,
"circular_holes": .5, "circular_holes": .5,
"loop_tries" : 40, "loop_tries": 40,
"loop_max" : 5, "loop_max": 5,
"loop_threshold" : 15, "loop_threshold": 15,
"spawn_per_region" : [1, 2], "spawn_per_region": [1, 2],
} }
def dist(level, y1, x1, y2, x2):
copy = [[t for t in l] for l in level] def dist(level: List[List[Tile]], y1: int, x1: int, y2: int, x2: int) -> int:
copy = [[t for t in row] for row in level]
dist = -1 dist = -1
queue, next_queue = [[y1, x1]], [0] queue, next_queue = [[y1, x1]], [0]
while next_queue: while next_queue:
@ -44,6 +45,7 @@ def dist(level, y1, x1, y2, x2):
queue = next_queue queue = next_queue
return -1 return -1
class Generator: class Generator:
def __init__(self, params: dict = None): def __init__(self, params: dict = None):
self.params = params or DEFAULT_PARAMS self.params = params or DEFAULT_PARAMS
@ -89,7 +91,7 @@ class Generator:
level[y - door_y + ry][x - door_x + rx] = Tile.FLOOR level[y - door_y + ry][x - door_x + rx] = Tile.FLOOR
@staticmethod @staticmethod
def add_loop(level: List[List[Tile]], y: int, x: int) -> None: def add_loop(level: List[List[Tile]], y: int, x: int) -> bool:
h, w = len(level), len(level[0]) h, w = len(level), len(level[0])
if level[y][x] != Tile.EMPTY: if level[y][x] != Tile.EMPTY:
return False return False
@ -106,18 +108,21 @@ class Generator:
# if adding the path would make the two tiles significantly closer # if adding the path would make the two tiles significantly closer
# and its sides don't touch already placed terrain, build it # and its sides don't touch already placed terrain, build it
def verify_sides(): def verify_sides() -> bool:
for Dx, Dy in [[dy, dx], [-dy, -dx]]: for delta_x, delta_y in [[dy, dx], [-dy, -dx]]:
for i in range(1, y2-y1+x2-x1): for i in range(1, y2 - y1 + x2 - x1):
if not(0<= y1+Dy+i*dy < h and 0 <= x1+Dx+i*dx < w) or \ if not (0 <= y1 + delta_y + i * dy < h
level[y1+Dy+i*dy][x1+Dx+i*dx].can_walk(): and 0 <= x1 + delta_x + i * dx < w) or \
level[y1 + delta_y + i * dy][x1 + delta_x
+ i * dx]\
.can_walk():
return False return False
return True return True
if dist(level, y1, x1, y2, x2) < 20 and verify_sides(): if dist(level, y1, x1, y2, x2) < 20 and verify_sides():
y, x = y1+dy, x1+dx y, x = y1 + dy, x1 + dx
while level[y][x] == Tile.EMPTY: while level[y][x] == Tile.EMPTY:
level[y][x] = Tile.FLOOR level[y][x] = Tile.FLOOR
y, x = y+dy, x+dx y, x = y + dy, x + dx
return True return True
return False return False
@ -143,7 +148,8 @@ class Generator:
return 0, 0, 0, 0 return 0, 0, 0, 0
@staticmethod @staticmethod
def build_door(room, y, x, dy, dx, length): def build_door(room: List[List[Tile]], y: int, x: int,
dy: int, dx: int, length: int) -> bool:
rh, rw = len(room), len(room[0]) rh, rw = len(room), len(room[0])
# verify we are pointing away from a floor tile # verify we are pointing away from a floor tile
if not(0 <= y - dy < rh and 0 <= x - dx < rw) \ if not(0 <= y - dy < rh and 0 <= x - dx < rw) \
@ -155,7 +161,7 @@ class Generator:
if 0 <= ny < rh and 0 <= nx < rw \ if 0 <= ny < rh and 0 <= nx < rw \
and room[ny][nx] != Tile.EMPTY: and room[ny][nx] != Tile.EMPTY:
return False return False
for i in range(length+1): for i in range(length + 1):
if room[y + i * dy][x + i * dx] != Tile.EMPTY: if room[y + i * dy][x + i * dx] != Tile.EMPTY:
return False return False
for i in range(length): for i in range(length):
@ -164,7 +170,7 @@ class Generator:
@staticmethod @staticmethod
def attach_door(room: List[List[Tile]], h_sup: int, w_sup: int, def attach_door(room: List[List[Tile]], h_sup: int, w_sup: int,
h_off: int, w_off: int) -> Tuple[int, int, int, int]: h_off: int, w_off: int) -> Tuple[int, int, int, int]:
length = h_sup + w_sup length = h_sup + w_sup
dy, dx = 0, 0 dy, dx = 0, 0
if length > 0: if length > 0:
@ -228,7 +234,7 @@ class Generator:
-> Tuple[List[list], int, int, int, int]: -> Tuple[List[list], int, int, int, int]:
return self.create_circular_room() return self.create_circular_room()
def register_spawn_area(self, area:List[List[Tile]]): def register_spawn_area(self, area: List[List[Tile]]) -> None:
spawn_positions = [] spawn_positions = []
for y, line in enumerate(area): for y, line in enumerate(area):
for x, tile in enumerate(line): for x, tile in enumerate(line):
@ -236,13 +242,13 @@ class Generator:
spawn_positions.append([y, x]) spawn_positions.append([y, x])
self.queued_area = spawn_positions self.queued_area = spawn_positions
def update_spawnable(self, y, x): def update_spawnable(self, y: int, x: int) -> None:
if self.queued_area != None: if self.queued_area is not None:
translated_area = [[y+ry, x+rx] for ry, rx in self.queued_area] translated_area = [[y + ry, x + rx] for ry, rx in self.queued_area]
self.spawn_areas.append(translated_area) self.spawn_areas.append(translated_area)
self.queued_area = None self.queued_area = None
def populate(self, rv): def populate(self, rv: Map) -> None:
min_c, max_c = self.params["spawn_per_region"] min_c, max_c = self.params["spawn_per_region"]
for region in self.spawn_areas: for region in self.spawn_areas:
entity_count = randint(min_c, max_c) entity_count = randint(min_c, max_c)
@ -259,7 +265,7 @@ class Generator:
# the starting room must have no corridor # the starting room must have no corridor
mem, self.params["corridor_chance"] = self.params["corridor_chance"], 0 mem, self.params["corridor_chance"] = self.params["corridor_chance"], 0
starting_room, _, _, _, _ = self.create_random_room(spawnable = False) starting_room, _, _, _, _ = self.create_random_room(spawnable=False)
dim_v, dim_h = len(starting_room), len(starting_room[0]) dim_v, dim_h = len(starting_room), len(starting_room[0])
pos_y, pos_x = randint(0, height - dim_v - 1),\ pos_y, pos_x = randint(0, height - dim_v - 1),\
randint(0, width - dim_h - 1) randint(0, width - dim_h - 1)
@ -297,7 +303,7 @@ class Generator:
while tries < self.params["loop_tries"] and \ while tries < self.params["loop_tries"] and \
loops < self.params["loop_max"]: loops < self.params["loop_max"]:
tries += 1 tries += 1
y, x = randint(0, height-1), randint(0, width-1) y, x = randint(0, height - 1), randint(0, width - 1)
loops += self.add_loop(level, y, x) loops += self.add_loop(level, y, x)
# place an exit ladder # place an exit ladder

View File

@ -13,10 +13,10 @@ from squirrelbattle.display.texturepack import TexturePack
class TestBroguelike(unittest.TestCase): class TestBroguelike(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.generator = broguelike.Generator() self.generator = broguelike.Generator()
self.stom = lambda x : Map.load_from_string("0 0\n" + x) self.stom = lambda x: Map.load_from_string("0 0\n" + x)
self.mtos = lambda x : x.draw_string(TexturePack.ASCII_PACK) self.mtos = lambda x: x.draw_string(TexturePack.ASCII_PACK)
def test_dist(self): def test_dist(self) -> None:
m = self.stom(".. ..\n ... ") m = self.stom(".. ..\n ... ")
distance = broguelike.dist(m.tiles, 0, 0, 0, 4) distance = broguelike.dist(m.tiles, 0, 0, 0, 4)
self.assertEqual(distance, 6) self.assertEqual(distance, 6)
@ -37,7 +37,7 @@ class TestBroguelike(unittest.TestCase):
queue += Map.neighbourhood(grid, y, x) queue += Map.neighbourhood(grid, y, x)
return not any([t.can_walk() for row in grid for t in row]) return not any([t.can_walk() for row in grid for t in row])
def test_build_doors(self): def test_build_doors(self) -> None:
m = self.stom(". .\n. .\n. .\n") m = self.stom(". .\n. .\n. .\n")
self.assertFalse(self.generator.build_door(m.tiles, 1, 1, 0, 1, 2)) self.assertFalse(self.generator.build_door(m.tiles, 1, 1, 0, 1, 2))
@ -46,11 +46,10 @@ class TestBroguelike(unittest.TestCase):
self.assertTrue(self.is_connex(m.tiles)) self.assertTrue(self.is_connex(m.tiles))
def test_loops(self) -> None: def test_loops(self) -> None:
m = self.stom(3*".. ..\n") m = self.stom(3 * ".. ..\n")
self.generator.add_loop(m.tiles, 1, 3) self.generator.add_loop(m.tiles, 1, 3)
s = self.mtos(m) s = self.mtos(m)
self.assertEqual(s, ".. ..\n.......\n.. ..") self.assertEqual(s, ".. ..\n.......\n.. ..")
self.assertFalse(self.generator.add_loop(m.tiles, 0, 0)) self.assertFalse(self.generator.add_loop(m.tiles, 0, 0))
m = self.stom("...\n. .\n...") m = self.stom("...\n. .\n...")
self.assertFalse(self.generator.add_loop(m.tiles, 1, 1)) self.assertFalse(self.generator.add_loop(m.tiles, 1, 1))