Example #1
0
 def get_seen_in_direction(pos: Tuple[int, int],
                           direction: Tuple[int, int]) -> int:
     """get the first seen seat in the direction"""
     i, j = tuple_add_tuple(pos, direction)
     while 0 <= i < len(data) and 0 <= j < len(data[i]):
         if data[i][j] >= 0:
             break
         else:
             i, j = tuple_add_tuple((i, j), direction)
     return data[i][j] if 0 <= i < len(data) and 0 <= j < len(
         data[i]) else None
Example #2
0
def follow_directions(dirs: List[List[str]]) -> Tuple[int, int]:
    """Follow a given set of directions"""
    pos: Tuple[int, int] = (0, 0)
    for dir in dirs:
        if dir[0] == "forward":
            pos = tuple_add_tuple(pos, (int(dir[1]), 0))
        elif dir[0] == "down":
            pos = tuple_add_tuple(pos, (0, int(dir[1])))
        elif dir[0] == "up":
            pos = tuple_add_tuple(pos, (0, -int(dir[1])))
        else:
            raise Exception("Unknown direction {}".format(dir[0]))
    return pos
Example #3
0
def follow_direction(dirs: str) -> Tuple[int, int]:
    """follow a string of given instructions and change the tile at the end"""
    pos = (0, 0)
    i = 0
    while i < len(dirs):
        # change position
        if dirs[i] == "e" or dirs[i] == "w":
            pos = tuple_add_tuple(pos, directions[dirs[i]])
            i += 1
        else:
            pos = tuple_add_tuple(pos, directions[dirs[i:i + 2]])
            i += 2
    return pos
Example #4
0
 def get_neighbor_in_direction(
         d, pos: Tuple[int, int, int, int], direction: Tuple[int, int, int,
                                                             int]) -> bool:
     """get the neighboring element in the direction"""
     i, j, k, l = tuple_add_tuple(pos, direction)
     return d[i][j][k][l] if 0 <= i < len(d) and 0 <= j < len(
         d[i]) and 0 <= k < len(d[i][j]) and 0 <= l < len(
             d[i][j][k]) else None
Example #5
0
def get_adjacent_tile_colors(colored: Dict[Tuple[int, int], bool],
                             tile: Tuple[int, int]) -> Dict[bool, int]:
    """get the colors of all adjacent tiles"""
    adjacent = {False: 0, True: 0}
    for curr_dir in directions.values():
        new = tuple_add_tuple(curr_dir, tile)
        if new in colored:
            adjacent[colored[new]] += 1
        else:
            adjacent[False] += 1
    return adjacent
Example #6
0
def get_basin_size(height_map: Map, x: int, y: int) -> int:
    """find the size of a basin with minimum at (x,y) [9 does not count]"""
    neighbors = [(1, 0), (-1, 0), (0, 1), (0, -1)]
    basin_points: List[Tuple[int, int]] = [(x, y)]
    new_added = True
    while new_added:
        new_added = False
        for point in basin_points:
            # if current point has neighbors != 9 add them
            if get_lowest_adjacent(height_map, point[0], point[1], exclude=basin_points) < 9:
                # add all points that have not been analyzed yet
                for neighbor in neighbors:
                    neighbor_coord = tuple_add_tuple(point, neighbor)
                    # point hasn't been analyzed yet and is in bounds and is no maximum
                    if neighbor_coord not in basin_points and \
                            0 <= neighbor_coord[0] < len(height_map[0]) and \
                            0 <= neighbor_coord[1] < len(height_map) and \
                            height_map[neighbor_coord[1]][neighbor_coord[0]] < 9:
                        basin_points.append(neighbor_coord)
                        new_added = True
    return len(basin_points)
Example #7
0
def flash_octopus(position: pos, curr_map: List[List[int]],
                  already_flashed: List[pos]) -> int:
    """flash an octopus at position, call recursive if necessary"""
    nof_flashes = 0
    # octopus can flash once every time-step
    if position not in already_flashed:
        # append to flashed and flash
        already_flashed.append(position)
        nof_flashes += 1
        # add +1 to all 8 adjacent octopus
        for adj in adjacent:
            neighbor = tuple_add_tuple(position, adj)
            # if neighbor on board increase
            if 0 <= neighbor[0] < 10 and 0 <= neighbor[1] < 10:
                curr_map[neighbor[1]][neighbor[0]] += 1
                # if this changed neighbor above threshold, call recursive
                if curr_map[neighbor[1]][neighbor[0]] > 9:
                    sub_flashes = flash_octopus(neighbor, curr_map,
                                                already_flashed)
                    nof_flashes += sub_flashes
    return nof_flashes
Example #8
0
def tile_colors_after_n_days(colored: Dict[Tuple[int, int], bool],
                             n: int = 100) -> Dict[Tuple[int, int], bool]:
    """get the number of active tiles after n days"""
    for _ in range(n):
        # work on duplicate of data
        new_colored = deepcopy(colored)
        # add neighboring tiles
        for tile in colored.keys():
            for curr_dir in directions.values():
                new = tuple_add_tuple(curr_dir, tile)
                if not new in new_colored:
                    new_colored[new] = False
        # for every tile check adjacent tiles and calculate changes
        for tile in new_colored.keys():
            adjacent = get_adjacent_tile_colors(colored, tile)
            if new_colored[tile] and (0 == adjacent[True]
                                      or adjacent[True] > 2):
                new_colored[tile] = False
            elif not new_colored[tile] and adjacent[True] == 2:
                new_colored[tile] = True
        colored = new_colored
    return colored
Example #9
0
 def get_neighbor_in_direction(pos: Tuple[int, int],
                               direction: Tuple[int, int]) -> int:
     """get the neighboring element in the direction"""
     i, j = tuple_add_tuple(pos, direction)
     return data[i][j] if 0 <= i < len(data) and 0 <= j < len(
         data[i]) else None
Example #10
0
def probe_step(pos: Position, vel: Velocity) -> (Position, Velocity):
    """given all the params, calculate the next step"""
    new_pos = tuple_add_tuple(pos, vel)
    new_vel = (vel[0] + (-1 if vel[0] > 0 else 0 if vel[0] == 0 else 1),
               vel[1] - 1)
    return new_pos, new_vel
Example #11
0
 def move_wp(self, dist: Tuple[int, int]):
     """move the waypoint in a given direction"""
     self.wp = tuple_add_tuple(self.wp, dist)
Example #12
0
 def move_ship(self, dist: Tuple[int, int]):
     """move ship in the given direction"""
     self.pos = tuple_add_tuple(self.pos, dist)
Example #13
0
def assemble_puzzle(tiles: Dict[int, List[str]]) -> List[str]:
    """get the final puzzle image"""
    # get a dict of all the tile sides
    tile_sides = get_puzzle_sides(tiles)
    # find matching tiles for every tile
    matching_ids = get_puzzle_matching(tile_sides)
    # start from one tile and add all matching ids to the current stack
    curr_key, stack = list(matching_ids.items())[0]
    tile_height = len(tiles[curr_key])
    stack = list([(curr_key, *item)[:4] for item in stack])
    stack: List[Tuple[int, int, int, int]]
    # init first tile position to (0,0) (-> numbers may be negative if its the wrong corner!)
    positions: Dict[int, Tuple[int, int]]
    positions = {curr_key: (0, 0)}
    while len(stack) > 0:
        # take one new item from stack
        old_tile_id, new_tile_id, old_side_pos, new_side_pos = stack[0]
        new_tile = tiles[new_tile_id]
        old_pos = positions[old_tile_id]

        # rotate the tile according to reference tile
        if old_side_pos < 0:
            raise Exception("old id is negative")
        if (old_side_pos + new_side_pos) % 2 != 0:  # rotation by 90° or 270°
            diff = (abs(old_side_pos) - abs(new_side_pos)) % 4
            if diff == -1 or diff == 3:  # 90°
                new_tile = rotate_tile("r90", new_tile)
            else:  # 270°
                new_tile = rotate_tile("r270", new_tile)
            # Special cases -> change direction to keep orientation of sides consistent
            # case positive
            if old_side_pos == 0 and new_side_pos == 1:
                new_tile = rotate_tile("h", new_tile)
            elif old_side_pos == 1 and new_side_pos == 0:
                new_tile = rotate_tile("v", new_tile)
            elif old_side_pos == 2 and new_side_pos == 3:
                new_tile = rotate_tile("h", new_tile)
            elif old_side_pos == 3 and new_side_pos == 2:
                new_tile = rotate_tile("v", new_tile)
            # case negative
            elif old_side_pos == 0 and new_side_pos == -3:
                new_tile = rotate_tile("h", new_tile)
            elif old_side_pos == 1 and new_side_pos == -2:
                new_tile = rotate_tile("v", new_tile)
            elif old_side_pos == 2 and new_side_pos == -1:
                new_tile = rotate_tile("h", new_tile)
            elif old_side_pos == 3 and new_side_pos == -4:
                new_tile = rotate_tile("v", new_tile)

        elif abs(new_side_pos
                 ) % 2 != 0:  # abs = 1, 3 -> match left and right sides
            if old_side_pos == abs(new_side_pos):  # rotate left to right
                new_tile = rotate_tile("h", new_tile)
            if new_side_pos < 0:  # mirror left to right to invert top and bottom sides
                new_tile = rotate_tile("v", new_tile)

        else:  # abs = 0, 2, 4 -> match top and bottom sides
            if old_side_pos == abs(new_side_pos) % 4:  # rotate top to bottom
                new_tile = rotate_tile("v", new_tile)
            if new_side_pos < 0:  # mirror top to bottom to invert left and right sides
                new_tile = rotate_tile("h", new_tile)

        # save tile and tile position
        tiles[new_tile_id] = deepcopy(new_tile)
        diff_pos = (1 if abs(old_side_pos) == 1 else
                    -1 if abs(old_side_pos) == 3 else 0,
                    -1 if abs(old_side_pos) %
                    4 == 0 else 1 if abs(old_side_pos) == 2 else 0)
        #  int id: pos (x,y)
        positions[new_tile_id] = tuple_add_tuple(old_pos, diff_pos)
        # get new tile sides and change orientation of neighbors
        new_tile_sides = get_tile_sides(new_tile)
        tile_sides[new_tile_id] = new_tile_sides
        matching = find_matching_tiles(tile_sides, new_tile_sides, new_tile_id)
        # modify current stack: remove all items that refer to curr item
        stack = [
            stack_item for stack_item in stack if stack_item[1] != new_tile_id
        ]
        # modify future stack: add new edges that are not on stack
        for item in matching:
            if not (item[0]) in positions.keys(
            ):  # skip tiles that have been added yet
                stack.append((new_tile_id, *item)[:4])
    # get top left item (min vals)
    min_x = min(pos[0] for pos in positions.values())
    min_y = min(pos[1] for pos in positions.values())
    max_x = max(pos[0] for pos in positions.values())
    max_y = max(pos[1] for pos in positions.values())
    # put every puzzle piece in its place
    arranged_pieces: List[list] = [[
        None for __ in range(abs(max_x - min_x + 1))
    ] for _ in range(abs(max_y - min_y + 1))]
    for pos_id, pos in positions.items():
        tile_content = tiles[
            pos_id]  # don't know why, but I need to invert all the pieces top to bottom once
        tile_content_cut = tile_content[1:-1]
        tile_content_cut = [line[1:-1] for line in tile_content_cut]
        arranged_pieces[pos[1] - min_y][pos[0] - min_x] = tile_content
        # arranged_pieces[pos[1] - min_y][pos[0] - min_x] = tile_content_cut
    # puzzle is assembled -> remove edges from every tile and assemble it to one big list of strings
    height_wo_borders = tile_height - 2
    assembled = [
        "" for _ in range(abs(max_y - min_y + 1) * height_wo_borders)
    ]  # init to correct length
    for y in range(min_y, max_y + 1):
        for x in range(min_x, max_x + 1):
            pos = (x, y)
            tile_id = get_key_from_val(positions, pos)
            tile_content = tiles[
                tile_id]  # don't know why, but I need to invert all the pieces top to bottom once
            tile_content_cut = tile_content[1:-1]
            tile_content_cut = [line[1:-1] for line in tile_content_cut]
            for i in range(len(tile_content_cut)):
                y_offset = y - min_y
                assembled[i +
                          height_wo_borders * y_offset] += tile_content_cut[i]
    return assembled