def paint(program, start_color): # type: (NewProgram, int) -> Tuple[int, PaintedArea] ''' Run a painting program on the robot ''' directions = [Direction(0, -1), Direction(1, 0), Direction(0, 1), Direction(-1, 0)] painted_area = defaultdict(lambda: defaultdict(lambda: -1)) painted_area[0][0] = start_color position = Point(0, 0) direction = 0 pointer = 0 relative_base = 0 while pointer != -1: current_color = max(painted_area[position.y][position.x], 0) program, output, pointer, relative_base = run_program(program, [current_color], pointer, True, relative_base) painted_area[position.y][position.x] = output[0] if output[1] == 1: direction = (direction + 1) % len(directions) else: direction = (direction - 1) % len(directions) position.x = position.x + directions[direction].x position.y = position.y + directions[direction].y nb_painted = 0 for y in painted_area: for x in painted_area[y]: if painted_area[y][x] > -1: nb_painted += 1 return nb_painted, painted_area
def play(program, fps=0): # type: (NewProgram, int) -> int ''' Play the game until it ends ''' program[0] = 2 pointer = 0 relative_base = 0 input = [] game_map = None while pointer != -1: program, output, pointer, relative_base = run_program( program, input, pointer, True, relative_base) game_map = update_game_map(output, game_map) if fps: print_game_map(game_map) sleep(1 / fps) paddle_pos = find_object(game_map, 3) ball_pos = find_object(game_map, 4) if (ball_pos is None or paddle_pos is None): input = [0] continue input = [(ball_pos.x > paddle_pos.x) - (ball_pos.x < paddle_pos.x)] if fps: print_game_map(update_game_map(output, game_map)) return game_map[Point(-1, 0)]
def display_map(shield_map, vacuum_state=(Point(-1, -1), 0), intersections=[]): # type: (ShieldMap, Tuple[Point, int], List[int]) -> None ''' Prints the map in text mode ''' max_x = max([p.x for p in shield_map if shield_map[p] is not None]) max_y = max([p.y for p in shield_map if shield_map[p] is not None]) for y in range(0, max_y + 1): for x in range(0, max_x + 1): pos = Point(x, y) value = shield_map[Point(x, y)] if pos != vacuum_state[0] else vacuum_state[1] if pos in intersections: value = ord('O') sys.stdout.write(chr(value)) print('')
def display_beam(affected_points, start_x=0, start_y=0): height = max(p.y for p in affected_points) width = max(p.x for p in affected_points) for y in range(start_y, height): for x in range(start_x, width): sys.stdout.write('#' if Point(x, y) in affected_points else '.') print('')
def get_affected_points(program, width=50, height=50): affected_points = set() for y in range(0, height): for x in range(0, width): if get_status(x, y, program): affected_points.add(Point(x, y)) return affected_points
def build_map(cells): # type: (List[int]) -> Tuple[ShieldMap, Tuple[Point, int]] ''' Converts the camera output to a structured map ''' vacuum_state = (Point(-1, -1), 0) shield_map = defaultdict(lambda: 0) x = 0 y = 0 for cell in cells: if cell == 10: y += 1 x = 0 continue pos = Point(x, y) if cell in directions_ascii: vacuum_state = (pos, cell) cell = 35 shield_map[pos] = cell x += 1 return shield_map, vacuum_state
def build_map(program): # type: (Program) -> Tuple[SectionMap, SectionMap, Point] ''' Builds the section map by keeping the wall to the right ''' section_map = defaultdict(lambda: -2, {Point(0, 0): 3}) distance_map = defaultdict(lambda: sys.maxsize, {Point(0, 0): 0}) pointer = 0 relative_base = 0 direction = 1 position = Point(0, 0) oxygen_system_location = None while pointer != -1: program, output, pointer, relative_base = run_program( program, [direction + 1], pointer, True, relative_base) result = output[0] if result == 0: section_map[position + directions[direction]] = -1 # Droid hit a wall, change the direction to keep the wall on the right direction = next_direction[direction] else: position += directions[direction] section_map[position] = max(section_map[position], result) distance_map[position] = min( [distance_map[position + directions[i]] for i in range(0, 4)]) + 1 if result == 2: oxygen_system_location = position # Droid didn't hit a wall, go back to the previous direction to keep the wall on the right direction = next_direction.index(direction) # Back to the start and all direction from the start are known, the map is completed if position == Point(0, 0) and section_map[directions[ 0]] != -2 and section_map[directions[1]] != -2 and section_map[ directions[2]] != -2 and section_map[directions[3]] != -2: break return section_map, distance_map, oxygen_system_location
def update_game_map(output, game_map=None): # type: (List[int], GameMap) -> GameMap ''' Updates the game map with the program's output ''' if game_map is None: game_map = defaultdict(lambda: 0) for i in range(0, len(output) // 3): x = output[3 * i] y = output[3 * i + 1] value = output[3 * i + 2] game_map[Point(x, y)] = value return game_map
def print_map(section_map, droid_position): # type: (SectionMap, Point) -> None ''' Prints the map in text mode. ''' if sys.platform == 'win32': os.system('cls') else: os.system('clear') xs = [p.x for p in section_map] min_x, max_x = min(xs), max(xs) ys = [p.y for p in section_map] min_y, max_y = min(ys), max(ys) symbols = {-2: '░', -1: '█', 1: ' ', 2: '●', 3: '¤', 4: '☺'} for y in range(min_y, max_y + 1): for x in range(min_x, max_x + 1): if Point(x, y) == droid_position: sys.stdout.write(symbols[4]) else: value = section_map[Point(x, y)] sys.stdout.write(symbols[value]) print('')
def find_closest_n_square(program, n=100): beam_starts = {n-1: 0} y = n while True: x = beam_starts[y-1] beam_starts[y] = x while x < 2 * y: # there are some lines without beam at the begining so we limit the number of position to test even if beam is not found if get_status(x, y, program): beam_starts[y] = x # Are other corners of the square in the beam ? if y > n and get_status(x + n - 1, y, program) and get_status(x, y - n + 1, program) and get_status(x + n - 1, y - n + 1, program): return Point(x, y - n + 1) break x += 1 y += 1
def print_game_map(game_map): # type: (GameMap) -> None ''' Print the game map in text mode ''' if sys.platform == 'win32': os.system('cls') else: os.system('clear') symbols = [' ', '█', '░', '▬', '●'] max_x = max([p.x for p in game_map]) max_y = max([p.y for p in game_map]) print(f'Score: {game_map[Point(-1, 0)]}') for y in range(0, max_y + 1): for x in range(0, max_x + 1): value = game_map[Point(x, y)] sys.stdout.write(symbols[value]) print('')
def checks_d17p2(): cells = [ 35, 35, 35, 35, 35, 35, 35, 46, 46, 46, 35, 35, 35, 35, 35, 10, 35, 46, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 46, 35, 10, 35, 46, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 46, 35, 10, 46, 46, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 46, 35, 10, 46, 46, 46, 46, 46, 46, 35, 46, 46, 46, 35, 35, 35, 46, 35, 10, 46, 46, 46, 46, 46, 46, 35, 46, 46, 46, 46, 46, 35, 46, 35, 10, 35, 35, 35, 35, 35, 35, 35, 35, 35, 46, 46, 46, 35, 46, 35, 10, 46, 46, 46, 46, 46, 46, 35, 46, 35, 46, 46, 46, 35, 46, 35, 10, 46, 46, 46, 46, 46, 46, 35, 35, 35, 35, 35, 35, 35, 35, 35, 10, 46, 46, 46, 46, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 10, 46, 46, 46, 46, 35, 35, 35, 35, 35, 35, 35, 35, 35, 46, 46, 10, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 46, 46, 46, 46, 10, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 46, 46, 46, 46, 10, 46, 46, 46, 46, 35, 46, 46, 46, 35, 46, 46, 46, 46, 46, 46, 10, 46, 46, 46, 46, 35, 35, 35, 35, 35, 46, 46, 46, 46, 46, 46, 10 ] system_map, _ = build_map(cells) vacuum_state = (Point(0, 6), ord('^')) instructions = generate_instructions(system_map, vacuum_state) assert instructions == 'R,8,R,8,R,4,R,4,R,8,L,6,L,2,R,4,R,4,R,8,R,8,R,8,L,6,L,2'
import ast, os import sys from collections import defaultdict from typing import Dict, Tuple from Day10 import Point from Day9 import run_program, Program SectionMap = Dict[Point, int] directions = [Point(0, -1), Point(0, 1), Point(-1, 0), Point(1, 0)] next_direction = [ 2, 3, 1, 0 ] # next_direction[i] is the direction to take if the droid hit the wall when moving in # direction i. This keeps the wall to the right. def print_map(section_map, droid_position): # type: (SectionMap, Point) -> None ''' Prints the map in text mode. ''' if sys.platform == 'win32': os.system('cls') else: os.system('clear') xs = [p.x for p in section_map] min_x, max_x = min(xs), max(xs) ys = [p.y for p in section_map] min_y, max_y = min(ys), max(ys)