def cell_auto(matrix: list, alivelimit: int = 4, deadlimit: int = 5) -> list: w, h = dimensions(matrix) copy = deepcopy(matrix) for i in range(w): for j in range(h): cell = matrix[j][i] neighbors = 0 alive = 0 for ii, jj in squares(exclude_center=True): # check if neighbor is within bounds try: c = matrix[j + jj][i + ii] except: pass else: alive += 1 if c == '#': neighbors += 1 if alive < 6: continue if cell == '#': if neighbors < deadlimit: copy[j][i] = '.' elif cell == '.': if neighbors > alivelimit: copy[j][i] = '#' return copy
def open_door(engine, entity): """TODO: render log message when opening a door of multiple doors""" position = engine.positions.find(entity) turn_over = False # get all coordinates surrounding current entity position coordinates = [ (position.x + x, position.y + y) for x, y in squares(exclude_center=True) ] g = join(engine.openables, engine.positions, engine.renders, engine.infos) doors = {} # compare coordinates against entities that can be opened that have a # a position x, y value in the coordinates list. for openable_id, (openable, coordinate, render, info) in g: valid_coordinate = (coordinate.x, coordinate.y) in coordinates if valid_coordinate and not openable.opened: x = coordinate.x - position.x y = coordinate.y - position.y doors[(x, y)] = (openable_id, openable, coordinate, render, info) door_to_open = None if not doors: engine.logger.add(f"No closed doors to open.") elif len(doors) == 1: door_to_open, = doors.values() else: engine.logger.add(f"Which door to open?") engine.screen.render() engine.input_system.process( valid_keypresses=set(keypress_to_direction).union(('escape',)) ) keypress = engine.get_keypress() movement = Movement.keypress_to_direction(keypress) # valid direction keypress but not valid door direction door = doors.get((movement.x, movement.y), None) if not door: engine.logger.add( f"You cancel opening a door. Direction invalid error." ) else: door_to_open = door if door_to_open: door, openable, position, render, info = door_to_open openable.opened = True position.blocks = False # replace info engine.infos.add(door, engine.infos.shared['opened door']) # replace the render engine.renders.add( door, random.choice(engine.renders.shared['opened door']) ) engine.logger.add(f"You open the door.") turn_over = True return turn_over
def ai_system(engine): c = engine.current_turns.find(engine.entity) ai = engine.ais.find(engine.entity) if not c.finished and ai: room = engine.tilemaps.find(engine.room) position = engine.positions.find(engine.entity) spaces = {(x, y) for y in range(1, room.height - 1) for x in range(1, room.width - 1)} moveable = [(x, y) for x, y in squares(exclude_center=True) if (position.x + x, position.y + y) in spaces] engine.movements.add(engine.entity, Movement.random_move(moveable)) c.finished = True
def astar_gui(tiles, start, end, paths=squares): """Note: This is for demo purposes only. Used only in demos/astar2.py""" heap = [] path = {} closed = set() # holds score from current node to neighbor gs = { (start.x, start.y): 0 } # holds score from current node to end node fs = { (start.x, start.y): heuristic((start.x, start.y), (end.x, end.y)) } heappush(heap, (fs[(start.x, start.y)], (start.x, start.y))) while heap: current = heappop(heap)[1] closed.add(current) if current == (end.x, end.y): # found node: return reversed path for p in path: yield p, 2 data = [] while current in path: data.append(current) current = path[current] data.reverse() for d in data: yield d, 3 return for i, j in squares(exclude_center=True): neighbor = (current[0] + i, current[1] + j) new_g = gs[current] + heuristic(current, neighbor) tile = tiles.get(neighbor, None) if not tile or tile in ('#', '+'): continue if neighbor in closed and new_g >= gs.get(neighbor, 0): continue faster = new_g < gs.get(neighbor, 0) unexplored = neighbor not in (i[1] for i in heap) if faster or unexplored: path[neighbor] = current gs[neighbor] = new_g fs[neighbor] = new_g + heuristic(neighbor, (end.x, end.y)) heappush(heap, (fs[neighbor], neighbor)) # no path is found for p in path: yield p, 2
def close_door(engine, entity): """TODO: cannot close door when unit is standing on the cell""" position = engine.positions.find(entity) turn_over = False # get all coordinates surrounding current entity position coordinates = [ (position.x + x, position.y + y) for x, y in squares(exclude_center=True) ] g = join(engine.openables, engine.positions, engine.renders) doors = {} # compare coordinates against entities that can be closed that have a # a position x, y value in the coordinates list. for closeable_id, (closeable, coordinate, render) in g: if (coordinate.x, coordinate.y) in coordinates and closeable.opened: x = coordinate.x - position.x y = coordinate.y - position.y doors[(x, y)] = (closeable_id, closeable, coordinate, render) door_to_close = None if not doors: engine.logger.add(f"No opened door to close.") elif len(doors) == 1: door_to_close, = doors.values() else: engine.logger.add(f"Which door to close?") engine.screen.render() engine.input_system.process( keypresses=set(keypress_to_direction.keys()).union(('escape',)) ) keypress = engine.get_keypress() movement = Movement.keypress_to_direction(keypress) # valid direction keypress but not valid door direction door = doors.get((movement.x, movement.y), None) if not door: engine.logger.add(f"You cancel closing a door.") else: door_to_close = door if door_to_close: door, closeable, position, render = door_to_close closeable.opened = False position.blocks = True engine.renders.add( door, random.choice(engine.renders.shared['closed wooden door']) ) engine.logger.add(f"You close the door.") turn_over = True return turn_over
def add_doors(cave, rooms): # create doors based on specific rules # _ | 0 1 2 # --+------- # 0 | 0 1 2 # 1 | 3 4 5 # 2 | 6 7 8 # if the tile @ 5 has neighbors only at 2/8 or 4/6 that are both walls # and the 5 tile is a floor then the 5 tile can transform into a door transform = set() width, height = dimensions(cave) for r, room in enumerate(rooms): # chance to a closed room if not random.randint(0, 1): continue # x, y = center(room) # cave[y][x] = r+1 for x, y in wall_coordinates(room): if cave[y][x] != '.': continue subset = empty_matrix(3, 3, ' ') # check both cases to generate a door for i, j in squares(exclude_center=True): if x + i > width - 1: continue if y + j > height - 1: continue subset[j + 1][i + 1] = cave[y + j][x + i] added = 0 # 1/7 are walls, 3/5 are floors if (subset[0][1] == '#' and subset[2][1] == '#' and (x - 1, y) not in transform and (x + 1, y) not in transform): transform.add((x, y)) added = 1 # 3/5 are walls, 1/7 are floors elif (subset[1][0] == '#' and subset[1][2] == '#' and (x, y - 1) not in transform and (x, y + 1) not in transform): transform.add((x, y)) added = 2 # print(string(subset), f'{r+1} {cave[y][x]} {added}\n') for x, y in transform: cave[y][x] = '+'
def astar(tiles, start, end): heap = [] path = {} closed = set() # holds score from current node to neighbor gs = {(start.x, start.y): 0} # holds score from current node to end node fs = {(start.x, start.y): octile((start.x, start.y), (end.x, end.y))} heappush(heap, (fs[(start.x, start.y)], (start.x, start.y))) while heap: current = heappop(heap)[1] # found node: return reversed path if current == (end.x, end.y): data = [] while current in path: data.append(current) current = path[current] data.reverse() return data closed.add(current) for i, j in squares(exclude_center=True): neighbor = (current[0] + i, current[1] + j) new_g = gs[current] + octile(current, neighbor) tile = tiles.get(neighbor, None) if not tile or tile in ('#', '+'): continue if neighbor in closed and new_g >= gs.get(neighbor, 0): continue faster = new_g < gs.get(neighbor, 0) unexplored = neighbor not in [i[1] for i in heap] if faster or unexplored: path[neighbor] = current gs[neighbor] = new_g fs[neighbor] = new_g + octile(neighbor, (end.x, end.y)) heappush(heap, (fs[neighbor], neighbor)) return []
def test_squares_exclusive(): assert len(list(squares(exclude_center=True))) == 8
def test_squares_inclusive(): assert len(list(squares())) == 9
def random_move(cls, possible_spaces=None): if not possible_spaces: possible_spaces = [(x, y) for x, y in squares()] index = random.randint(0, len(possible_spaces) - 1) x, y = possible_spaces[index] return cls(x, y)