Skip to content
Snippets Groups Projects
Select Git revision
  • 2f1fdcf1848a3ebc7c3df53cece426904da4d3af
  • main default protected
2 results

Day10.py

Blame
  • Day10.py 6.91 KiB
    import functools
    from typing import List, Tuple
    
    from ImportData import import_data
    
    day: int = 10
    
    # sample_data: List[str] = '''
    # .....
    # .S-7.
    # .|.|.
    # .L-J.
    # .....
    # '''.split('\n')[1:-1]
    # sample_data: List[str] = '''
    # 7-F7-
    # .FJ|7
    # SJLL7
    # |F--J
    # LJ.LJ
    # '''.split('\n')[1:-1]
    # sample_data: List[str] = '''
    # ...........
    # .S-------7.
    # .|F-----7|.
    # .||.....||.
    # .||.....||.
    # .|L-7.F-J|.
    # .|..|.|..|.
    # .L--J.L--J.
    # ...........
    # '''.split('\n')[1:-1]
    # sample_data: List[str] = '''
    # ..........
    # .S------7.
    # .|F----7|.
    # .||....||.
    # .||....||.
    # .|L-7F-J|.
    # .|..||..|.
    # .L--JL--J.
    # ..........
    # '''.split('\n')[1:-1]
    # sample_data: List[str] = '''
    # .F----7F7F7F7F-7....
    # .|F--7||||||||FJ....
    # .||.FJ||||||||L7....
    # FJL7L7LJLJ||LJ.L-7..
    # L--J.L7...LJS7F-7L7.
    # ....F-J..F7FJ|L7L7L7
    # ....L7.F7||L7|.L7L7|
    # .....|FJLJ|FJ|F7|.LJ
    # ....FJL-7.||.||||...
    # ....L---J.LJ.LJLJ...
    # '''.split('\n')[1:-1]
    sample_data: List[str] = '''
    FF7FSF7F7F7F7F7F---7
    L|LJ||||||||||||F--J
    FL-7LJLJ||||||LJL-77
    F--JF--7||LJLJ7F7FJ-
    L---JF-JLJ.||-FJLJJ7
    |F|F-JF---7F7-L7L|7|
    |FFJF7L7F-JF7|JL---7
    7-L-JL7||F7|L7F-7F7|
    L.L7LFJ|||||FJL7||LJ
    L7JLJL-JLJLJL--JLJ.L
    '''.split('\n')[1:-1]
    
    data_structure: type = Tuple[int, int, str, List[str]]
    
    
    def parse_data(data: List[str]) -> data_structure:
        initial_row: int = -1
        initial_column: int = -1
        initial_pipe: str
        number_of_rows = len(data)
        number_of_columns = len(data[0])
        for row in range(number_of_rows):
            for column in range(number_of_columns):
                if data[row][column] == 'S':
                    initial_row = row
                    initial_column = column
        initial_pipe = input('What is the shape of the initial pipe? ')
        return initial_row, initial_column, initial_pipe, data
    
    
    def part1(data: data_structure) -> int:
        row, column, pipe, pipes = data
        previous_locations: List[Tuple[int, int]] = [(row, column), (row, column)]
        current_locations: List[Tuple[int, int]]
        match pipe:
            case '|':
                current_locations = [(row + 1, column), (row - 1, column)]
            case '-':
                current_locations = [(row, column + 1), (row, column - 1)]
            case 'L':
                current_locations = [(row, column + 1), (row - 1, column)]
            case 'J':
                current_locations = [(row, column - 1), (row - 1, column)]
            case '7':
                current_locations = [(row, column - 1), (row + 1, column)]
            case 'F':
                current_locations = [(row, column + 1), (row + 1, column)]
            case _:
                raise ValueError(f'{pipe} at ({row},{column})')
        distance: int = 1
        while current_locations[0] != current_locations[1]:
            distance += 1
            for i in range(len(current_locations)):
                previous_row, previous_column = previous_locations[i]
                row, column = current_locations[i]
                pipe = pipes[row][column]
                previous_locations[i] = current_locations[i]
                row_change = row - previous_row
                column_change = column - previous_column
                match pipe:
                    case '|' | '-':
                        current_locations[i] = (row + row_change, column + column_change)
                    case 'L' | '7':
                        current_locations[i] = (row + column_change, column + row_change)
                    case 'J' | 'F':
                        current_locations[i] = (row - column_change, column - row_change)
                    case _:
                        raise ValueError(f'{pipe} at ({row}, {column})')
        return distance
    
    
    def part2(data: data_structure) -> int:
        row, column, pipe, pipes = data
        # an extra copy of the pipes data, "zoomed in" to allow for gaps between pipes
        tiles: [List[List[str]]] = [['I'] * (2 * len(pipes[0])) for _ in range(2 * len(pipes))]
        # first, draw the main loop on the tiles copy
        previous_location: Tuple[int, int] = (row, column)
        current_location: Tuple[int, int]
        match pipe:
            case '|' | 'F':
                current_location = (row + 1, column)
                tiles[2 * row + 1][2 * column] = 'X'
            case '-' | 'L':
                current_location = (row, column + 1)
                tiles[2 * row][2 * column + 1] = 'X'
            case 'J' | '7':
                current_location = (row, column - 1)
                tiles[2 * row][2 * column - 1] = 'X'
            case _:
                raise ValueError(f'{pipe} at ({row},{column})')
        previous_row, previous_column = previous_location
        row, column = current_location
        while tiles[2 * row][2 * column] != 'X':
            tiles[2 * row][2 * column] = 'X'
            pipe = pipes[row][column]
            previous_location = current_location
            row_change = row - previous_row
            column_change = column - previous_column
            match pipe:
                case '|' | '-':
                    current_location = (row + row_change, column + column_change)
                    tiles[2 * row + row_change][2 * column + column_change] = 'X'
                case 'L' | '7':
                    current_location = (row + column_change, column + row_change)
                    tiles[2 * row + column_change][2 * column + row_change] = 'X'
                case 'J' | 'F':
                    current_location = (row - column_change, column - row_change)
                    tiles[2 * row - column_change][2 * column - row_change] = 'X'
                case 'S':
                    pass
                case _:
                    raise ValueError(f'{pipe} at ({row}, {column})')
            previous_row, previous_column = previous_location
            row, column = current_location
        # now let's create a border
        tiles = [['O'] * (2 * len(pipes[0]))] + tiles + [['O'] * (2 * len(tiles[0]))]
        tiles = [['O'] + tile + ['O'] for tile in tiles]
        # fill in the "outside"
        finished: bool = False
        while not finished:
            finished = True
            for row in range(1, len(tiles) - 1):
                for column in range(1, len(tiles[0]) - 1):
                    if tiles[row][column] == 'I' and (tiles[row - 1][column] == 'O' or tiles[row][column - 1] == 'O' or
                                                      tiles[row + 1][column] == 'O' or tiles[row][column + 1] == 'O'):
                        tiles[row][column] = 'O'
                        finished = False
        # now shrink it back down to size, step 1: remove the border
        # arguably, this step is unnecessary, but it simplifies the expression in step 2
        tiles = [row[1:-1] for row in tiles[1:-1]]
        # shrink it back down to size, step 2: remove the "in between" tiles
        new_tiles: List[List[str]] = []
        for row in range(0, len(tiles), 2):
            new_row: List[str] = []
            for column in range(0, len(tiles[0]), 2):
                new_row.append(tiles[row][column])
            new_tiles.append(new_row)
        # count the "inside"
        return sum([functools.reduce(lambda x, y: x + y, map(lambda x: 1 if x == 'I' else 0, row)) for row in new_tiles])
    
    
    if __name__ == '__main__':
        production_ready = True
        raw_data = import_data(day) if production_ready else sample_data
        print(part1(parse_data(raw_data)))
        print(part2(parse_data(raw_data)))