def compute2(data): logging.getLogger().setLevel(logging.INFO) painter = Intcode.from_input(data) hull_map = defaultdict(lambda: 1) position = Point(0, 0) direction = Directions.UP paint(painter, hull_map, position, direction) x_s = [p.x for p in hull_map.keys()] y_s = [p.y for p in hull_map.keys()] hull_rect = Rectangle( left=min(x_s), right=max(x_s), top=min(y_s), bottom=max(y_s), ) out = [] for y in range(hull_rect.top, hull_rect.bottom + 1): line = '' for x in range(hull_rect.left, hull_rect.right + 1): line += '#' if hull_map[Point(x, y)] == 1 else ' ' out.append(line) return '\n' + '\n'.join(out)
def test_parse(self): input = dedent("""\ .# .|""") assert_that( parse(input), { Point(0, 0): States.OPEN, Point(1, 0): States.LUMBERYARD, Point(0, 1): States.OPEN, Point(1, 1): States.TREES, })
def test_extended_neighbors(self): assert_that(Point(2, 2).extended_neighbors, is_([ Point(1, 1), Point(2, 1), Point(3, 1), Point(1, 2), Point(3, 2), Point(1, 3), Point(2, 3), Point(3, 3) ]))
def test_next_state_trees_stays_trees(self): area = parse( dedent("""\ ... .|# #..""")) assert_that(next_state(area, Point(1, 1)), is_(States.TREES))
def test_next_state_open_stays_open(self): area = parse( dedent("""\ ... |.. ..|""")) assert_that(next_state(area, Point(1, 1)), is_(States.OPEN))
def test_next_state_open_becomes_trees_surrounded_by_trees(self): area = parse( dedent("""\ .|. |.. ..|""")) assert_that(next_state(area, Point(1, 1)), is_(States.TREES))
def test_next_state_lumberyard_becomes_open_when_not_surrounded2(self): area = parse( dedent("""\ |.. |#. |..""")) assert_that(next_state(area, Point(1, 1)), is_(States.OPEN))
def test_next_state_trees_becomes_lumberyard_surrounded_by_lumberyards( self): area = parse( dedent("""\ #.. #|. #..""")) assert_that(next_state(area, Point(1, 1)), is_(States.LUMBERYARD))
def show_graphic(soil): top = min(pos[Y] for pos in soil.keys()) bottom = max(pos[Y] for pos in soil.keys()) left = min(pos[X] for pos in soil.keys()) right = max(pos[X] for pos in soil.keys()) print("size", right - left, bottom - top) img = Image.new('RGB', (right - left + 1, bottom - top + 1), "black") # Create a new black image pixels = img.load() top_left = Point(left, top) colors = { CLAY: (139, 69, 19), RUNNING_WATER: (127, 127, 255), STAGNANT_WATER: (0, 0, 255), } for point, content in soil.items(): pixels[(Point(*point) - top_left).tuple()] = colors[content] img.show()
def test_next_state_lumberyard_stays_lumberyard_if_close_to_lumberyard_and_trees( self): area = parse( dedent("""\ |.. .#. ..#""")) assert_that(next_state(area, Point(1, 1)), is_(States.LUMBERYARD))
def parse(data): asteroids = {} w, h = 0, 0 for y, line in enumerate(data.strip().splitlines()): for x, char in enumerate(line): if char == ASTEROID: asteroids[Point(x, y)] = 0 w = x h = y return asteroids, w + 1, h + 1
def compute2(data, lazer=Point(20, 18), stop_at=200): # from compute 1 asteroids, w, h = parse(data) killed = [] biggest_side = max(lazer.x, w - 1 - lazer.x, lazer.y, h - 1 - lazer.y) size = lcm(*list(range(1, biggest_side + 1))) # lazer_map = Rectangle( # left=lazer.x - lcm(*list(range(1, lazer.x + 1))), # right=lazer.x + lcm(*list(range(1, w - lazer.x))), # top=lazer.y - lcm(*list(range(1, lazer.y + 1))), # bottom=lazer.y + lcm(*list(range(1, h - lazer.y))), # ) lazer_map = Rectangle( left=lazer.x - size, right=lazer.x + size, top=lazer.y - size, bottom=lazer.y + size, ) real_map = Rectangle( left=0, right=w - 1, top=0, bottom=h - 1, ) while True: for target in lazer_map.perimeter(Point(lazer.x, lazer_map.top)): for step in lazer.raytrace(target): if step not in real_map: break if step in asteroids: asteroids.pop(step) killed.append(step) logging.debug(f'Destroyed {step}, killed={len(killed)}') if len(killed) == stop_at: return step.x * 100 + step.y break logging.debug('Full circle done')
def compute(data): logging.getLogger().setLevel(logging.INFO) painter = Intcode.from_input(data) hull_map = defaultdict(lambda: 0) position = Point(0, 0) direction = Directions.UP paint(painter, hull_map, position, direction) return len(hull_map.items())
def test_parse(): data = """\ .#..# ..... ##### ....# ...##""" assert_that( parse(data), is_(({ Point(1, 0): 0, Point(4, 0): 0, Point(0, 2): 0, Point(1, 2): 0, Point(2, 2): 0, Point(3, 2): 0, Point(4, 2): 0, Point(4, 3): 0, Point(3, 4): 0, Point(4, 4): 0, }, 5, 5)))
def test_parse(self): grid, fighters = parse( dedent("""\ ####### #.G...# #...EG# #.#.#G# #..G#E# #.....# #######""")) assert_that( fighters, is_([ Fighter("G", Point(2, 1)), Fighter("E", Point(4, 2)), Fighter("G", Point(5, 2)), Fighter("G", Point(5, 3)), Fighter("G", Point(3, 4)), Fighter("E", Point(5, 4)), ])) assert_that(grid[2, 1], is_(fighters[0])) assert_that(grid[3, 1], is_(None)) assert_that(grid, not_(has_key((2, 3))))
def parse(data): fighters = [] grid = {} for y, line in enumerate(data.split("\n")): for x, char in enumerate(line): if char == ".": grid[x, y] = None elif char in ["E", "G"]: fighter = Fighter(char, Point(x, y)) grid[x, y] = fighter fighters.append(fighter) return grid, fighters
def propagate(point: Point, to: [Directions]): # print("Propagating from", point) soil[point.tuple()] = RUNNING_WATER if point.y == bottom: return True down = point + Directions.DOWN.value if soil.get(down.tuple()) is RUNNING_WATER: return True elif soil.get(down.tuple()) is None: if propagate(down, to=(Directions.LEFT, Directions.RIGHT)): return True propagations = [flow_to(point, direction) for direction in to] if not any(propagations): soil[point.tuple()] = STAGNANT_WATER return False not_stagant(point, Directions.LEFT) not_stagant(point, Directions.RIGHT) return True
def flow_all(soil): sys.setrecursionlimit(10000) top = min(pos[Y] for pos in soil.keys()) bottom = max(pos[Y] for pos in soil.keys()) start = Point(500, top - 1) def propagate(point: Point, to: [Directions]): # print("Propagating from", point) soil[point.tuple()] = RUNNING_WATER if point.y == bottom: return True down = point + Directions.DOWN.value if soil.get(down.tuple()) is RUNNING_WATER: return True elif soil.get(down.tuple()) is None: if propagate(down, to=(Directions.LEFT, Directions.RIGHT)): return True propagations = [flow_to(point, direction) for direction in to] if not any(propagations): soil[point.tuple()] = STAGNANT_WATER return False not_stagant(point, Directions.LEFT) not_stagant(point, Directions.RIGHT) return True def flow_to(point, direction): new_point = point + direction.value content = soil.get(new_point.tuple()) if content is RUNNING_WATER: return True elif content is None: return propagate(new_point, (direction, )) return False def not_stagant(point, direction): next_point = point + direction.value while soil.get(next_point.tuple()) is STAGNANT_WATER: soil[next_point.tuple()] = RUNNING_WATER next_point += direction.value propagate(start + Directions.DOWN.value, (Directions.LEFT, Directions.RIGHT))
return Point(self.right, self.top) @property def bottom_left(self): return Point(self.left, self.bottom) @property def bottom_right(self): return Point(self.right, self.bottom) def __contains__(self, item): return self.left <= item.x <= self.right and self.top <= item.y <= self.bottom @pytest.mark.parametrize('rect,start,points', [ (Rectangle(0, 1, 0, 1), Point(0, 0), [Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]), (Rectangle(0, 2, 0, 2), Point(1, 0), [ Point(1, 0), Point(2, 0), Point(2, 1), Point(2, 2), Point(1, 2), Point(0, 2), Point(0, 1), Point(0, 0) ]), ]) def test_perimeter(rect, start, points): assert_that(list(rect.perimeter(start)), points)
def get_neighbors(self, point: Point): for x, y in neighbors: pos = Point(point.x + x, point.y + y) if pos.tuple() in self.grid: yield pos
import sys import pytest from hamcrest import assert_that, is_ from y2018 import Directions, Point ORIGIN = Point(0, 0) def compute(data): wire1, wire2 = list(map(parse_path, data.strip().split('\n'))) wire1_points, _ = crawl(wire1) wire2_points, _ = crawl(wire2) intersections = wire1_points.intersection(wire2_points) intersections.remove(ORIGIN) ordered = sorted(intersections, key=lambda p: p.manhattan_dist(ORIGIN)) return ordered[0].manhattan_dist(ORIGIN) def compute2(data): wire1, wire2 = list(map(parse_path, data.strip().split('\n'))) wire1_points, steps_to1 = crawl(wire1) wire2_points, steps_to2 = crawl(wire2) intersections = wire1_points.intersection(wire2_points)
def top_right(self): return Point(self.right, self.top)
def bottom_left(self): return Point(self.left, self.bottom)
for y, line in enumerate(data.strip().splitlines()): for x, char in enumerate(line): if char == ASTEROID: asteroids[Point(x, y)] = 0 w = x h = y return asteroids, w + 1, h + 1 @pytest.mark.parametrize('val,expect1, expect2', [ ("""\ .#..# ..... ##### ....# ...##""", Point(3, 4), 8), ("""\ ......#.#. #..#.#.... ..#######. .#.#.###.. .#..#..... ..#....#.# #..#....#. .##.#..### ##...#..#. .#....####""", Point(5, 8), 33), ("""\ #.#...#.#. .###....#. .#....#...
def bottom_right(self): return Point(self.right, self.bottom)
def test_extended_neighbors(self): assert_that(Point(2, 2).extended_neighbors, is_([ Point(1, 1), Point(2, 1), Point(3, 1), Point(1, 2), Point(3, 2), Point(1, 3), Point(2, 3), Point(3, 3) ])) @pytest.mark.parametrize('p1, p2, steps', [ (Point(0,0), Point(0, 1), []), (Point(0,0), Point(0, 2), [Point(0, 1)]), (Point(1,0), Point(0, 0), []), (Point(2,0), Point(0, 0), [Point(1, 0)]), (Point(0,0), Point(1, 1), []), (Point(0,0), Point(2, 2), [Point(1, 1)]), (Point(0,0), Point(3, 9), [Point(1, 3), Point(2, 6)]), (Point(3,9), Point(0, 0), [Point(2, 6), Point(1, 3)]), ]) def test_raytrace(p1, p2, steps): assert_that(list(p1.raytrace(p2)), is_(steps)) @pytest.mark.parametrize('direction,rotation,result', [ (Directions.UP, Rotations.CW, Directions.RIGHT), (Directions.RIGHT, Rotations.CW, Directions.DOWN),
def test_direction_to(self): assert_that(Point(2, 2).direction_to(Point(2, 1)), is_(Directions.UP)) assert_that(Point(2, 2).direction_to(Point(3, 2)), is_(Directions.RIGHT)) assert_that(Point(2, 2).direction_to(Point(1, 2)), is_(Directions.LEFT)) assert_that(Point(2, 2).direction_to(Point(2, 3)), is_(Directions.DOWN))
def parse(input): area = {} for y, line in enumerate(input.split("\n")): for x, state in enumerate(line): area[Point(x, y)] = States(state) return area
def test_next_state_no_neighbors_dont_count(self): area = parse(dedent("""\ #""")) assert_that(next_state(area, Point(0, 0)), is_(States.OPEN))
def top_left(self): return Point(self.left, self.top)