Drop first version of random walk

This commit is contained in:
Yohann D'ANELLO 2021-01-08 16:16:42 +01:00
parent c216a6089e
commit 9b853324ad
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
3 changed files with 3 additions and 145 deletions

View File

@ -12,7 +12,7 @@ import sys
from .entities.player import Player from .entities.player import Player
from .enums import GameMode, KeyValues, DisplayActions from .enums import GameMode, KeyValues, DisplayActions
from .interfaces import Map, Logs from .interfaces import Map, Logs
from .mapgeneration import randomwalk, broguelike from .mapgeneration import broguelike
from .resources import ResourceManager from .resources import ResourceManager
from .settings import Settings from .settings import Settings
from . import menus from . import menus

View File

@ -1,123 +0,0 @@
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
# SPDX-License-Identifier: GPL-3.0-or-later
from enum import auto, Enum
from random import choice, random, randint
from typing import Tuple
from ..interfaces import Map, Tile
DEFAULT_PARAMS = {
"split_chance": .15,
"turn_chance": .5,
"death_chance": .1,
"max_walkers": 15,
"width": 100,
"height": 100,
"fill": .4,
"no_lone_walls": False,
}
class Directions(Enum):
up = auto()
down = auto()
left = auto()
right = auto()
class Walker:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
self.dir = choice(list(Directions))
def random_turn(self) -> None:
self.dir = choice(list(Directions))
def next_pos(self) -> Tuple[int, int]:
if self.dir == Directions.up:
return self.x, self.y + 1
elif self.dir == Directions.down:
return self.x, self.y - 1
elif self.dir == Directions.right:
return self.x + 1, self.y
elif self.dir == Directions.left:
return self.x - 1, self.y
def move_in_bounds(self, width: int, height: int) -> None:
nx, ny = self.next_pos()
if 0 < nx < width-1 and 0 < ny < height-1:
self.x, self.y = nx, ny
def split(self) -> "Walker":
child = Walker(self.x, self.y)
child.dir = self.dir
return child
class Generator:
def __init__(self, params: dict = DEFAULT_PARAMS):
self.params = params
def run(self) -> Map:
width, height = self.params["width"], self.params["height"]
walkers = [Walker(width // 2, height // 2)]
grid = [[Tile.EMPTY for _ in range(width)] for _ in range(height)]
count = 0
while count < self.params["fill"] * width * height:
# because we can't add or remove walkers while looping over the pop
# we need lists to keep track of what will be the walkers for the
# next iteration of the main loop
next_walker_pop = []
for walker in walkers:
if grid[walker.y][walker.x] == Tile.EMPTY:
count += 1
grid[walker.y][walker.x] = Tile.FLOOR
if random() < self.params["turn_chance"]:
walker.random_turn()
walker.move_in_bounds(width, height)
if random() > self.params["death_chance"]:
next_walker_pop.append(walker)
# we make sure to never kill all walkers
if not next_walker_pop:
next_walker_pop.append(choice(walkers))
# we use a second loop for spliting so we're not bothered by cases
# like a walker not spliting because we hit the population cap even
# though the next one would have died and freed a place
# not a big if it happened though
for walker in walkers:
if len(next_walker_pop) < self.params["max_walkers"]:
if random() < self.params["split_chance"]:
next_walker_pop.append(walker.split())
walkers = next_walker_pop
start_x, start_y = -1, -1
while grid[start_y][start_x] != Tile.FLOOR or start_x == -1:
start_x, start_y = randint(0, width - 1), randint(0, height - 1)
result = Map(width, height, grid, start_y, start_x)
# post-processing: add walls
for x in range(width):
for y in range(height):
if grid[y][x] == Tile.EMPTY:
c = sum([1 if grid[j][i] == Tile.FLOOR else 0 for j, i in Map.neighbourhood(grid, y, x)])
if c == 4 and self.params["no_lone_walls"]:
result.tiles[y][x] = Tile.FLOOR
elif c > 0:
result.tiles[y][x] = Tile.WALL
for x in range(width):
for y in [0, height-1]:
if grid[y][x] == Tile.FLOOR:
grid[y][x] = Tile.WALL
for y in range(height):
for x in [0, width-1]:
if grid[y][x] == Tile.FLOOR:
grid[y][x] = Tile.WALL
return result

View File

@ -5,7 +5,7 @@ import unittest
from random import randint from random import randint
from squirrelbattle.interfaces import Map, Tile from squirrelbattle.interfaces import Map, Tile
from squirrelbattle.mapgeneration import randomwalk, broguelike from squirrelbattle.mapgeneration import broguelike
def is_connex(grid): def is_connex(grid):
h, w = len(grid), len(grid[0]) h, w = len(grid), len(grid[0])
@ -20,25 +20,6 @@ def is_connex(grid):
queue += Map.neighbourhood(grid, y, x) queue += Map.neighbourhood(grid, y, x)
return not(any([any([t.can_walk() for t in l]) for l in grid])) return not(any([any([t.can_walk() for t in l]) for l in grid]))
class TestRandomWalk(unittest.TestCase):
def setUp(self) -> None:
#we set no_lone_walls to true for 100% coverage
params = randomwalk.DEFAULT_PARAMS
params["no_lone_walls"] = True
self.generator = randomwalk.Generator(params = params)
def test_starting(self) -> None:
"""
Create a map and check that the whole map is accessible from the starting position using a
depth-first search
"""
m = self.generator.run()
self.assertTrue(m.tiles[m.start_y][m.start_x].can_walk())
def test_connexity(self) -> None:
m = self.generator.run()
self.assertTrue(is_connex(m.tiles))
class TestBroguelike(unittest.TestCase): class TestBroguelike(unittest.TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.generator = broguelike.Generator() self.generator = broguelike.Generator()