Drop first version of random walk
This commit is contained in:
parent
c216a6089e
commit
9b853324ad
@ -12,7 +12,7 @@ import sys
|
||||
from .entities.player import Player
|
||||
from .enums import GameMode, KeyValues, DisplayActions
|
||||
from .interfaces import Map, Logs
|
||||
from .mapgeneration import randomwalk, broguelike
|
||||
from .mapgeneration import broguelike
|
||||
from .resources import ResourceManager
|
||||
from .settings import Settings
|
||||
from . import menus
|
||||
|
@ -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
|
@ -5,7 +5,7 @@ import unittest
|
||||
from random import randint
|
||||
|
||||
from squirrelbattle.interfaces import Map, Tile
|
||||
from squirrelbattle.mapgeneration import randomwalk, broguelike
|
||||
from squirrelbattle.mapgeneration import broguelike
|
||||
|
||||
def is_connex(grid):
|
||||
h, w = len(grid), len(grid[0])
|
||||
@ -20,29 +20,10 @@ def is_connex(grid):
|
||||
queue += Map.neighbourhood(grid, y, x)
|
||||
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):
|
||||
def setUp(self) -> None:
|
||||
self.generator = broguelike.Generator()
|
||||
|
||||
|
||||
def test_connexity(self) -> None:
|
||||
m = self.generator.run()
|
||||
self.assertTrue(is_connex(m.tiles))
|
||||
|
Loading…
Reference in New Issue
Block a user