Drop first version of random walk
This commit is contained in:
		@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 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,29 +20,10 @@ 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()
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    def test_connexity(self) -> None:
 | 
					    def test_connexity(self) -> None:
 | 
				
			||||||
        m = self.generator.run()
 | 
					        m = self.generator.run()
 | 
				
			||||||
        self.assertTrue(is_connex(m.tiles))
 | 
					        self.assertTrue(is_connex(m.tiles))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user