130 lines
2.9 KiB
Python
130 lines
2.9 KiB
Python
from intcode import Interpreter, WaitForInput
|
|
|
|
NORTH = 1
|
|
SOUTH = 2
|
|
WEST = 3
|
|
EAST = 4
|
|
|
|
DIRS = [NORTH, SOUTH, EAST, WEST]
|
|
|
|
OPPOSITE = {NORTH : SOUTH,
|
|
SOUTH : NORTH,
|
|
EAST : WEST,
|
|
WEST : EAST
|
|
}
|
|
|
|
WALL = 0
|
|
EMPTY = 1
|
|
OXY = 2
|
|
|
|
ENCODING = {WALL : '#',
|
|
EMPTY : '.',
|
|
OXY : 'O'
|
|
}
|
|
|
|
def move(pos, dir):
|
|
if dir == NORTH:
|
|
return pos[0], pos[1] + 1
|
|
if dir == SOUTH:
|
|
return pos[0], pos[1] - 1
|
|
if dir == EAST:
|
|
return pos[0] + 1, pos[1]
|
|
if dir == WEST:
|
|
return pos[0] - 1, pos[1]
|
|
return NotImplemented
|
|
|
|
class DroidIO:
|
|
def __init__(self):
|
|
self._input = None
|
|
self._output = None
|
|
|
|
def pop_input(self):
|
|
i = self._input
|
|
if i == None:
|
|
raise WaitForInput
|
|
self._input = None
|
|
return i
|
|
|
|
def append_output(self, x):
|
|
self._output = x
|
|
|
|
def flush(self):
|
|
o = self._output
|
|
self._output = None
|
|
return o
|
|
|
|
def write(self, x):
|
|
self._input = x
|
|
|
|
class Droid(Interpreter):
|
|
def __init__(self, program):
|
|
super().__init__(program, DroidIO)
|
|
self.pos = 0, 0
|
|
|
|
def move(self, dir):
|
|
out = self.eval(dir)
|
|
if out != WALL:
|
|
self.pos = move(self.pos, dir)
|
|
return out
|
|
|
|
|
|
class Map(dict):
|
|
def __init__(self, encoding):
|
|
super().__init__()
|
|
self.encoding = encoding
|
|
|
|
def __str__(self):
|
|
xs, ys = tuple(zip(*self.keys()))
|
|
r = str()
|
|
for y in range(min(ys), max(ys)+1):
|
|
for x in range(min(xs), max(xs)+1):
|
|
if x == y == 0:
|
|
r += 'X'
|
|
elif (x, y) not in self:
|
|
r += ' '
|
|
else:
|
|
r += self.encoding[self[(x, y)]]
|
|
r += '\n'
|
|
return r
|
|
|
|
def map_space(droid, map=None):
|
|
if map == None:
|
|
map = Map(ENCODING)
|
|
for dir in DIRS:
|
|
new_pos = move(droid.pos, dir)
|
|
if new_pos in map:
|
|
continue
|
|
object = droid.move(dir)
|
|
map[new_pos] = object
|
|
if object == WALL:
|
|
continue
|
|
map_space(droid, map)
|
|
droid.move(OPPOSITE[dir])
|
|
return map
|
|
|
|
def shortest_paths(map, pos, visited=None):
|
|
if visited == None:
|
|
visited = {pos : 0}
|
|
path = visited[pos] + 1
|
|
for dir in DIRS:
|
|
new = move(pos, dir)
|
|
if map[new] == WALL or (new in visited and visited[new] <= path):
|
|
continue
|
|
visited[new] = path
|
|
shortest_paths(map, new, visited)
|
|
return visited
|
|
|
|
def preproc(puzzle_input):
|
|
program = list(map(int, puzzle_input.split(',')))
|
|
droid = Droid(program)
|
|
area_map = map_space(droid)
|
|
x, y = tuple(filter(lambda x: x[1]==OXY, area_map.items()))[0][0]
|
|
path_map = shortest_paths(area_map, (x, y))
|
|
return path_map
|
|
|
|
def partI(path_map):
|
|
return path_map[(0, 0)]
|
|
|
|
def partII(path_map):
|
|
return max(path_map.values())
|