Ejemplo n.º 1
0
    def __str__(self):
        output = list()

        for y_val in range(self._target_coord.y_val + 1):
            for x_val in range(self._target_coord.x_val + 1):
                coord = Coord(x_val, y_val)
                if coord == Coord(0, 0):
                    output += 'M'
                elif coord == self._target_coord:
                    output += 'T'
                else:
                    erosion_type = self.get_map_data(coord).erosion_type
                    if erosion_type == self.Type.ROCKY:
                        output += '.'
                    elif erosion_type == self.Type.WET:
                        output += '='
                    elif erosion_type == self.Type.NARROW:
                        output += '|'
                    else:
                        assert False

            output += '\n'

        # Strip final newline.
        return ''.join(output[:-1])
Ejemplo n.º 2
0
def find_largest_power( serial_num, grid_size_min, grid_size_max ):
    grid = compute_grid( serial_num )

    largest_power = calc_power_box( grid, Coord(0, 0), grid_size_min )
    largest_coord = Coord( 0, 0 )
    largest_grid_size = 0

    for grid_size in range( grid_size_min, grid_size_max + 1 ):
        for y_index in range(0, 300 - (grid_size - 1)):
            for x_index in range( 0, 300 - ( grid_size - 1) ):
                coord = Coord( x_index, y_index )
                if x_index == 0 and y_index == 0:
                    power = calc_power_box( grid, coord, grid_size )
                    last_x0_power = power

                elif x_index == 0:
                    power = calc_power_delta_y( last_x0_power, grid, coord, grid_size )
                    last_x0_power = power

                else:
                    power = calc_power_delta_x( power, grid, coord, grid_size )

                if power > largest_power:
                    largest_power = power
                    largest_coord = coord
                    largest_grid_size = grid_size

    return largest_coord, largest_grid_size
Ejemplo n.º 3
0
 def test_get_nearby_coords(self):
     nearby_set = day15.get_nearby_set(Coord(2, 5))
     nearby_list = list(nearby_set)
     day15.sort_coord_list_by_reading_order(nearby_list)
     self.assertEqual(
         [Coord(2, 4), Coord(1, 5),
          Coord(3, 5), Coord(2, 6)], nearby_list)
Ejemplo n.º 4
0
    def find_furthest_room(self):
        # Perform a breadth first search.
        paths = deque([(Coord(0, 0), '')])
        rooms_visited = set(Coord(0, 0))

        num_doors = 0
        num_doors_is_1000_or_more = 0

        while paths:
            current_coord, current_path = paths.popleft()

            for direction, increment in self.direction_mapping.items():
                next_coord = aoc.add_coords(current_coord, increment)
                if next_coord in rooms_visited:
                    continue

                if self._is_door(current_coord, next_coord):
                    next_path = current_path + direction
                    paths.append((next_coord, next_path))
                    num_doors = max(num_doors, len(next_path))

                    # Most of these paths have common beginnings to we only need to re-simulate
                    # if we haven't done next_path already when simulating a previous path.
                    if next_coord not in rooms_visited:
                        rooms_visited.add(next_coord)
                        if num_doors >= 1000:
                            num_doors_is_1000_or_more += 1

        return num_doors, num_doors_is_1000_or_more
Ejemplo n.º 5
0
    def get_map_data(self, coord):
        data = self._map_data.get(coord)

        # Return cached data.
        if data:
            return data

        # Otherwise calculate data to cache.  This will use recursion to
        # calculate most data points.
        if coord == Coord(0,0):
            geologic_index = 0

        elif coord == self._target_coord:
            geologic_index = 0

        elif coord.y_val == 0:
            geologic_index = coord.x_val * 16807

        elif coord.x_val == 0:
            geologic_index = coord.y_val * 48271

        else:
            # Recursively calculate data.
            coord1 = aoc.add_coords(coord, Coord(-1,0))
            coord2 = aoc.add_coords(coord, Coord(0,-1))
            geologic_index = self.get_map_data(coord1).erosion_level * \
                             self.get_map_data(coord2).erosion_level

        erosion_level = (geologic_index + self._depth) % 20183
        erosion_type = self.Type(erosion_level % 3)
        self._map_data[coord] = self.CaveData(erosion_level=erosion_level,
                                              erosion_type=erosion_type)
        return self._map_data[coord]
Ejemplo n.º 6
0
    def _coord_is_in_reservoir(self, coord):
        # We can only check coordinates that aren't yet mapped that are mapped below.
        assert self._ground_map.get(coord) is None
        assert coord.y_val == self._max_coord.y_val or \
            self._ground_map.get(Coord(coord.x_val, coord.y_val + 1)) is not None

        # Check by extending the region to the left and then to the right.
        for increment in (-1, 1):
            current_coord = coord
            while True:
                next_coord = Coord(current_coord.x_val + increment, current_coord.y_val)
                if not self._min_coord.x_val < next_coord.x_val < self._max_coord.x_val:
                    return False

                coord_below = Coord(next_coord.x_val, next_coord.y_val + 1)
                contents = self._ground_map.get(coord_below)
                # There not clay below or resting water below (which has clay below it).
                if contents not in ('#', '~'):
                    return False

                contents = self._ground_map.get(next_coord)
                # There is clay in this direction.
                if contents in ('#', '~'):
                    break

                # Flowing water only happens outside of reservoirs.
                if contents == '|':
                    return False

                current_coord = next_coord

        return True
Ejemplo n.º 7
0
def get_nearby_set(coord):
    return {
        Coord(coord.x_val, coord.y_val - 1),
        Coord(coord.x_val - 1, coord.y_val),
        Coord(coord.x_val + 1, coord.y_val),
        Coord(coord.x_val, coord.y_val + 1)
    }
Ejemplo n.º 8
0
    def __init__(self, input_list):
        line_num = 0
        self.lumber_map = dict()

        x_val = 0  # Make pylint happy.

        for line in input_list:
            for x_val, contents in enumerate(line):
                self.lumber_map[Coord(x_val, line_num)] = contents
            line_num += 1

        self.map_size = Coord(x_val + 1, line_num)
Ejemplo n.º 9
0
    def __init__(self, map_regex_with_detours):
        sys.setrecursionlimit(10000)  # Needed to compile the complex regex.

        map_regex, self._longest_detour = self._remove_detours_from_regex(
            map_regex_with_detours)

        self._map_regex_with_detours = regex.compile(map_regex_with_detours)
        self._map_regex = regex.compile(map_regex)
        # Store the map as a sparse dictionary of doors which will be '-' or '|'.
        self._map_data = dict()
        self._min_coord = Coord(0, 0)
        self._max_coord = Coord(0, 0)
Ejemplo n.º 10
0
def simulate(input_list):
    """
    Simulate both part 1 and part 2 at the same time.
    """

    path1, path2 = parse_input(input_list[0], input_list[1])

    # Walk the first wire path and create a map containing the coordinates the first wire touches.
    # We store the minimum length to the coordinate.
    wire_map = {}
    current_coord = Coord(0, 0)
    length = 0

    for segment in path1:
        direction = segment[0]
        distance = int(segment[1:])

        for _ in range(distance):
            length += 1
            current_coord = aoc.add_coords(current_coord,
                                           NEXT_INCREMENT[direction])
            if current_coord not in wire_map:
                wire_map[current_coord] = length

    # Walk the second wire path checking for intersections with the first wire.  If we intersect,
    # calculate both the minimum distance to the port (0,0) and the minimum wire length to the
    # intersection.
    current_coord = Coord(0, 0)
    length = 0

    min_distance_from_port = None
    min_length = None

    for segment in path2:
        direction = segment[0]
        distance = int(segment[1:])

        for _ in range(distance):
            length += 1
            current_coord = aoc.add_coords(current_coord,
                                           NEXT_INCREMENT[direction])
            if current_coord in wire_map:
                distance_from_port = abs(current_coord.x_val) + abs(
                    current_coord.y_val)

                if min_distance_from_port is None or distance_from_port < min_distance_from_port:
                    min_distance_from_port = distance_from_port

                total_length = wire_map[current_coord] + length
                if min_length is None or total_length < min_length:
                    min_length = total_length

    return min_distance_from_port, min_length
Ejemplo n.º 11
0
    def move(self):
        if self.direction == '>':
            next_location = Coord(self.location.x_val + 1, self.location.y_val)
        elif self.direction == '<':
            next_location = Coord(self.location.x_val - 1, self.location.y_val)
        elif self.direction == '^':
            next_location = Coord(self.location.x_val, self.location.y_val - 1)
        else:  #  self.direction == 'v':
            next_location = Coord(self.location.x_val, self.location.y_val + 1)

        self.location = next_location
        self.turn_me(self.track[next_location])
        return self.location
Ejemplo n.º 12
0
def calc_bound(data):
    coord = data[0].coord

    x_min = x_max = coord.x_val
    y_min = y_max = coord.y_val

    for star_data in data:
        x_min = min(x_min, star_data.coord.x_val)
        x_max = max(x_max, star_data.coord.x_val)
        y_min = min(y_min, star_data.coord.y_val)
        y_max = max(y_max, star_data.coord.y_val)

    return Coord(x_min, y_min), Coord(x_max, y_max)
Ejemplo n.º 13
0
 def risk_level(self):
     level = 0
     for y_val in range(self._target_coord.y_val + 1):
         for x_val in range(self._target_coord.x_val + 1):
             coord = Coord(x_val, y_val)
             level += self.get_map_data(coord).erosion_type
     return level
Ejemplo n.º 14
0
    def simulate(self):
        new_lumber_map = dict()

        for y_index in range(self.map_size.y_val):
            for x_index in range(self.map_size.x_val):
                coord = Coord(x_index, y_index)
                (tree_neighbors,
                 lumberyard_neighbors) = self.count_neighbors(coord)
                current_contents = self.lumber_map[coord]
                new_contents = current_contents
                if current_contents == '.':
                    if tree_neighbors >= 3:
                        new_contents = '|'

                elif current_contents == '|':
                    if lumberyard_neighbors >= 3:
                        new_contents = '#'

                elif current_contents == '#':
                    if lumberyard_neighbors < 1 or tree_neighbors < 1:
                        new_contents = '.'

                else:
                    assert False

                new_lumber_map[coord] = new_contents

        self.lumber_map = new_lumber_map
Ejemplo n.º 15
0
    def _is_door(self, coord1, coord2):
        if coord2 < coord1:
            coord1, coord2 = coord2, coord1
        assert coord1 != coord2
        assert coord1.x_val == coord2.x_val and coord1.y_val == coord2.y_val - 1 or \
               coord1.x_val == coord2.x_val - 1 and coord1.y_val == coord2.y_val

        return Coord(coord1, coord2) in self._map_data
Ejemplo n.º 16
0
def compute_grid( serial_num ):
    grid = [ [0] * 300 for i in range( 300 ) ]

    for x_index in range( 300 ):
        for y_index in range( 300 ):
            grid[ x_index ][ y_index ] = calc_power( serial_num, Coord( x_index, y_index) )

    return grid
Ejemplo n.º 17
0
def parse_input(input_list):

    # Map of velocitys keyed by coordinate.
    data = list()

    # Example input data
    # position=< 9,  1> velocity=< 0,  2>
    regex = r'position=<\s*(-?\d+),\s*(-?\d+)> velocity=<\s*(-?\d+),\s*(-?\d+)>'

    for line in input_list:
        match_obj = re.search(regex, line)
        assert match_obj

        coord = Coord(int(match_obj.group(1)), int(match_obj.group(2)))
        vel = Coord(int(match_obj.group(3)), int(match_obj.group(4)))

        data.append(StarData(coord, vel))

    return data
Ejemplo n.º 18
0
    def generate_map(self):
        final_paths = self._find_paths(Coord(0, 0), '')

        paths_simulated = set()

        # Re-walk the final_paths taking detours.
        for path in final_paths:
            new_path = ''
            coord = Coord(0, 0)
            for char in path:
                new_path += char
                coord = aoc.add_coords(coord, self.direction_mapping[char])

                if new_path not in paths_simulated:
                    self._find_paths(coord,
                                     new_path,
                                     max_depth=self._longest_detour,
                                     take_detours=True)
                    paths_simulated.add(new_path)
Ejemplo n.º 19
0
    def __str__(self):
        output = ''

        for y_index in range(self.map_size.y_val):
            for x_index in range(self.map_size.x_val):
                output += self.lumber_map[Coord(x_index, y_index)]
            output += '\n'

        # Strip final newline.
        return output[:-1]
Ejemplo n.º 20
0
    def __init__(self, input_list):
        # Initialize the map with the spring.
        self._ground_map = dict({self.spring_coord : '+'})
        self._amount_of_water_resting = 0
        self._amount_of_water_flowing = 0

        re_x = re.compile(r'^x=(\d+), y=(\d+)..(\d+)$')
        re_y = re.compile(r'^y=(\d+), x=(\d+)..(\d+)$')

        for line_num, line in enumerate(input_list):
            match = re_x.search(line)
            if match:
                xstart = int(match.group(1))
                xend = int(match.group(1)) + 1
                ystart = int(match.group(2))
                yend = int(match.group(3)) + 1

            match = re_y.search(line)
            if match:
                ystart = int(match.group(1))
                yend = int(match.group(1)) + 1
                xstart = int(match.group(2))
                xend = int(match.group(3)) + 1

            if line_num == 0:
                min_coord_x = xstart
                max_coord_x = xend - 1
                min_coord_y = ystart
                max_coord_y = yend - 1
            else:
                min_coord_x = min(min_coord_x, xstart)
                max_coord_x = max(max_coord_x, xend)
                min_coord_y = min(min_coord_y, ystart)
                max_coord_y = max(max_coord_y, yend)

            for x_coord in range(xstart, xend):
                for y_coord in range(ystart, yend):
                    self._ground_map[Coord(x_coord, y_coord)] = '#'

        # Add a column of output padding for water flow.
        self._min_coord = Coord(min_coord_x - 1, min_coord_y)
        self._max_coord = Coord(max_coord_x, max_coord_y - 1)
Ejemplo n.º 21
0
 def test_is_enemy_nearby(self):
     map_data = day15.parse_input(self.map_data_input_choose_open_tests)
     # Not close
     self.assertFalse(day15.is_enemy_nearby(map_data, 'G', Coord(1, 1)))
     # Is Diagonal
     self.assertFalse(day15.is_enemy_nearby(map_data, 'E', Coord(2, 2)))
     self.assertFalse(day15.is_enemy_nearby(map_data, 'G', Coord(3, 2)))
     self.assertFalse(day15.is_enemy_nearby(map_data, 'G', Coord(1, 2)))
     self.assertFalse(day15.is_enemy_nearby(map_data, 'G', Coord(3, 2)))
     # I am above
     self.assertTrue(day15.is_enemy_nearby(map_data, 'G', Coord(2, 2)))
     # I am below
     self.assertTrue(day15.is_enemy_nearby(map_data, 'E', Coord(1, 2)))
     # I am left
     self.assertTrue(day15.is_enemy_nearby(map_data, 'G', Coord(1, 3)))
     # I am right
     self.assertTrue(day15.is_enemy_nearby(map_data, 'G', Coord(3, 3)))
Ejemplo n.º 22
0
def parse_input(input_list):
    assert input_list[0][0:7] == 'depth: '
    assert input_list[1][0:8] == 'target: '

    index = input_list[0].find(' ')
    depth = int(input_list[0][index+1:])
    index = input_list[1].find(' ')
    index2 = input_list[1].find(',')
    x_val = int(input_list[1][index+1:index2])
    y_val = int(input_list[1][index2+1:])

    return depth, Coord(x_val, y_val)
Ejemplo n.º 23
0
    def get_stats(self):
        tree_acres = 0
        lumberyard_acres = 0

        for y_index in range(self.map_size.y_val):
            for x_index in range(self.map_size.x_val):
                contents = self.lumber_map[Coord(x_index, y_index)]
                if contents == '#':
                    lumberyard_acres += 1
                elif contents == '|':
                    tree_acres += 1

        return tree_acres, lumberyard_acres
Ejemplo n.º 24
0
 def test_get_turn_order(self):
     map_data = day15.parse_input(self.map_data_input_order_tests)
     order = day15.get_turn_order(map_data)
     self.assertEqual([
         Coord(2, 1),
         Coord(4, 1),
         Coord(1, 2),
         Coord(3, 2),
         Coord(5, 2),
         Coord(2, 3),
         Coord(4, 3)
     ], list(map(lambda square: square.coord, order)))
Ejemplo n.º 25
0
    def __str__(self):
        output = list()

        # Add bottom.
        output += '#' * ((
            (self._max_coord.x_val - self._min_coord.x_val + 1) * 2) +
                         1) + '\n'

        for y_val in range(self._max_coord.y_val, self._min_coord.y_val - 1,
                           -1):

            # Draw row containing '|' doors
            output += '#.'
            for x_val in range(self._min_coord.x_val, self._max_coord.x_val):
                coord1 = Coord(x_val, y_val)
                coord2 = Coord(x_val + 1, y_val)
                output += self._map_data.get((coord1, coord2), '#')
                if coord2 == Coord(0, 0):
                    output += 'X'
                else:
                    output += '.'
            output += '#\n'

            # Draw row containing '-' doors.  This will also draw the bottom perimeter wall because
            # it goes one index past self._min_coord.y.
            output += '#'
            for x_val in range(self._min_coord.x_val,
                               self._max_coord.x_val + 1):
                coord1 = Coord(x_val, y_val - 1)
                coord2 = Coord(x_val, y_val)
                output += self._map_data.get((coord1, coord2), '#')
                output += '#'
            output += '\n'

        # Strip final '\n' from output.
        return ''.join(output[:-1])
Ejemplo n.º 26
0
    def count_neighbors(self, coord):
        tree_neighbors = 0
        lumberyard_neighbors = 0

        for y_index in range(coord.y_val - 1, coord.y_val + 2):
            for x_index in range(coord.x_val - 1, coord.x_val + 2):
                neighbor_coord = Coord(x_index, y_index)
                if neighbor_coord == coord:
                    continue

                neighbor_contents = self.lumber_map.get(neighbor_coord)
                if neighbor_contents == '|':
                    tree_neighbors += 1

                elif neighbor_contents == '#':
                    lumberyard_neighbors += 1

        return tree_neighbors, lumberyard_neighbors
Ejemplo n.º 27
0
    def __str__(self):
        output = ''
        row_label_length = len("%d" % self._max_coord.y_val)
        col_label_length = len("%d" % self._max_coord.x_val)
        for y_index in range(0, col_label_length):
            output += "%*s" % ( row_label_length + 1, ' ')
            for x_index in range(self._min_coord.x_val, self._max_coord.x_val + 1):
                col_label = "%*d" % (col_label_length, x_index)
                output += col_label[y_index]
            output += '\n'

        for y_index in range(0, self._max_coord.y_val + 2):
            output += "%*d " % (row_label_length, y_index)
            for x_index in range(self._min_coord.x_val, self._max_coord.x_val + 1):
                coord = Coord(x_index, y_index)
                output += self._ground_map.get(coord, '.')
            output += '\n'

        return output[0:-1]  # Strip final newline.
Ejemplo n.º 28
0
def print_map(map_data, show_hitpoints=False):
    output = ''
    for y_val in range(map_data.size.y_val):
        hit_points = list()
        for x_val in range(map_data.size.x_val):
            square = map_data.grid.get(Coord(x_val, y_val))

            if square:
                output += square.contains
                if square.contains in 'GE':
                    hit_points.append("%s(%d)" %
                                      (square.contains, square.hit_points))
            else:
                output += '.'

        if hit_points and show_hitpoints:
            output += '   ' + ', '.join(hit_points)
        output += '\n'

    return output
Ejemplo n.º 29
0
def parse_input(input_list):

    carts = dict()
    track = dict()

    line_num = 0
    for line in input_list:
        for index, symbol in enumerate(line):
            if symbol in r'\/-|+':
                track[Coord(index, line_num)] = symbol
            elif symbol in 'v^':
                carts[Coord(index, line_num)] = Cart(Coord(index, line_num),
                                                     symbol, track)
                track[Coord(index, line_num)] = '|'
            elif symbol in '<>':
                carts[Coord(index, line_num)] = Cart(Coord(index, line_num),
                                                     symbol, track)
                track[Coord(index, line_num)] = '-'

        line_num += 1

    return carts
Ejemplo n.º 30
0
def parse_input(input_list, elf_attack_power=3):
    # Map is a dictionary with Coord keys.
    map_data = MapData()
    x_val = 0
    y_val = 0
    for y_val, line in enumerate(input_list):
        for x_val, char in enumerate(line):
            if char == '.':
                pass
            elif char == 'G':
                map_data.grid[Coord(x_val, y_val)] = \
                    SquareData('G', 'E', Coord(x_val, y_val), 3, 200)
            elif char == 'E':
                map_data.grid[Coord(x_val, y_val)] = \
                    SquareData('E', 'G', Coord(x_val, y_val), elf_attack_power, 200)
            elif char == '#':
                map_data.grid[Coord(x_val, y_val)] = \
                    SquareData('#', '#', Coord(x_val, y_val), 0, 0)
            else:
                assert False  # Bad input

    map_data.size = Coord(x_val + 1, y_val + 1)
    return map_data