def _smooth_map(M): """ Smooth a map using cellular automata. If number of walls around the cell (including it) is more than number of floors, replace the cell with a wall. In other case replace the cell with a floor. """ # Already replaced cells must not affect current so we need a copy of the original map M2 = deepcopy(M) for y, line in enumerate(M2.cells[1:-1]): for x, _ in enumerate(line[1:-1]): true_x = x + 1 true_y = y + 1 # Check the number of walls in ORIGINAL map number_of_walls = sum(cell.__class__.__name__ == 'wall_cave' for cell in [ M.cells[true_y][true_x], M.cells[true_y + 1][true_x], M.cells[true_y - 1][true_x], M.cells[true_y][true_x + 1], M.cells[true_y + 1][true_x + 1], M.cells[true_y - 1][true_x + 1], M.cells[true_y][true_x - 1], M.cells[true_y + 1][true_x - 1], M.cells[true_y - 1][true_x - 1], ]) # And set them in smoothed map M2.cells[true_y][true_x] = (C.wall_cave() if number_of_walls >= 5 else C.floor_dirt()) return M2
def dungeon_cellular_simple(w=30, h=30, start_floor_chance=0.55, smooth_level=3): """ Construct the dungeon map using simple cellular automata. Note that this map can contain disconnected areas. Visual ------ Natural-looking cave-like map. Parameters ---------- w : int Map width h : int Map height start_floor_chance : float Chance of the floor cell in the first map fill smooth_level : int Number of sequential smooth functions """ M = Map(w, h, fill_cell=C.wall_cave) # Randomly fill all-but-border cells by floor with start_floor_chance probability for y, line in enumerate(M.cells[1:-1]): for x, _ in enumerate(line[1:-1]): chance = random.random() if chance <= start_floor_chance: M.cells[y + 1][x + 1] = C.floor_dirt() # Sequentially smooth the map smooth_level times for _ in range(smooth_level): M = _smooth_map(M) return M
def building_housebarn(w=30, h=15, material=None): """ Construct the housebarn (also known as longhouse) building interior. Parameters ---------- w : int Map width h : int Map height material : string Wall material. Can be "wooden", "stone" or None. If None, a random material will be chosen. """ # Initial checks. Don't accept: # - Too small buildings # - Too long/square buildings # - Wall types that are not "stone" or "wooden" if w < 10 or h < 10: raise ValueError('Building is too small: w/h < 10') if not material: material = random.choice(['wooden', 'stone']) if material not in ('wooden', 'stone'): raise ValueError('Material should be "stone" or "wooden"') wall_cell_type = C.wall_stone if material == 'stone' else C.wall_plank is_horizontal = True if w >= h else False if is_horizontal: if w < h * 2 or w > h * 3: raise ValueError('Building is too long or too short.') else: if h < w * 2 or h > w * 3: raise ValueError('Building is too long or too short.') # If parameters are vertial, we firstly construct horizontal building then transpose it. # It allows not to use two additional different subtypes of the building which will simplify the code. if not is_horizontal: w, h = h, w M = Map(w, h, fill_cell=C.floor_flagged) # Create outward walls for x in range(w): M[x, 0] = wall_cell_type() M[x, h - 1] = wall_cell_type() for y in range(h): M[0, y] = wall_cell_type() M[w - 1, y] = wall_cell_type() # Randomly choose where the living part is living_left = random.choice([True, False]) living_wall_x = None barn_wall_x = None # Place central doors/corridor and calculate X-positions for vertical walls if w % 2 == 0: M[w // 2, 0] = C.floor_flagged() M[w // 2 - 1, 0] = C.floor_flagged() M[w // 2, h - 1] = C.floor_flagged() M[w // 2 - 1, h - 1] = C.floor_flagged() living_wall_x = (w // 2 - 3) if living_left else (w // 2 + 2) barn_wall_x = (w // 2 + 2) if living_left else (w // 2 - 3) else: M[w // 2, 0] = C.door_closed_wooden() M[w // 2, h - 1] = C.door_closed_wooden() living_wall_x = (w // 2 - 2) if living_left else (w // 2 + 2) barn_wall_x = (w // 2 + 2) if living_left else (w // 2 - 2) # Place vertical walls for i in range(1, h // 3): M[living_wall_x, i] = wall_cell_type() M[living_wall_x, h - i - 1] = wall_cell_type() for i in range(1, h - 1): M[barn_wall_x, i] = C.wall_fence_thin() M[barn_wall_x, h // 2] = C.door_closed_wooden() # Create living room: # Set initial coordinates lx_start = 1 if living_left else living_wall_x + 1 lx_end = living_wall_x - 1 if living_left else w - 1 beds_dx = int((lx_end - lx_start) % 2 == 0 and not living_left) beds_dy = random.choice([0, 1]) # Place beds near walls or at 1 cell from walls for bed_x in range(lx_start + beds_dx, lx_end, 2): M[bed_x, 1 + beds_dy].put(T.furniture_bed_single()) M[bed_x, h - 2 - beds_dy].put(T.furniture_bed_single()) # Place bonfire in the middle of the room or hearth on the side of the room is_bonfire = random.choice([True, False]) if is_bonfire: M[(lx_start + lx_end) // 2, h // 2].put(T.bonfire()) elif living_left: M[1, h // 2].put(T.furniture_hearth()) else: M[w - 2, h // 2].put(T.furniture_hearth()) # Create barn: # Set initial coordinates bx_start = 1 if not living_left else barn_wall_x + 1 bx_end = barn_wall_x - 1 if not living_left else w - 2 # Fill the barn floor with dirt for x in range(bx_start, bx_end + 1): for y in range(1, h - 1): M[x, y] = C.floor_dirt() is_central_barn = random.choice([True, False]) if is_central_barn: # Central barn: stalls in the center, two waterthroughs on the side for y in range(h // 3, h * 2 // 3): M[bx_start + 2, y] = C.wall_fence_thin() for x in range(bx_start + 3, bx_end - 2, 2): M[x, h // 2] = C.wall_fence_thin() M[x, h // 2 - 1].put(T.farm_mangler()) M[x, h // 2 + 1].put(T.farm_mangler()) for y in range(h // 3, h * 2 // 3): M[x + 1, y] = C.wall_fence_thin() for x in range(bx_start + 1, bx_end): M[x, 1].put(T.water_trough()) M[x, h - 2].put(T.water_trough()) else: # Side barn: stalls on the side, one waterthrough in the center for x in range(bx_start, bx_end - 1, 2): M[x, 1].put(T.farm_mangler()) M[x, h - 2].put(T.farm_mangler()) for y in range(1, h // 3): M[x + 1, y] = C.wall_fence_thin() for y in range(h * 2 // 3, h - 1): M[x + 1, y] = C.wall_fence_thin() for x in range(bx_start + 2, bx_end - 1): M[x, h // 2].put(T.water_trough()) # Transpose the building if it should be vertical if not is_horizontal: M.transpose() return M