Browse Source

2019 days 19-25, minus 22 part 2

master
Terrana Ninetailed 1 year ago
parent
commit
5452071c7f
25 changed files with 1287 additions and 148 deletions
  1. +4
    -147
      2019/day17/part2.py
  2. +1
    -0
      2019/day19/input
  3. +24
    -0
      2019/day19/part1.py
  4. +72
    -0
      2019/day19/part2.py
  5. +121
    -0
      2019/day20/input
  6. +111
    -0
      2019/day20/part1.py
  7. +111
    -0
      2019/day20/part2.py
  8. +55
    -0
      2019/day21/bothparts.py
  9. +1
    -0
      2019/day21/input
  10. +100
    -0
      2019/day22/input.txt
  11. +34
    -0
      2019/day22/part1.py
  12. +1
    -0
      2019/day23/input
  13. +68
    -0
      2019/day23/part1.py
  14. +91
    -0
      2019/day23/part2.py
  15. +5
    -0
      2019/day24/input
  16. +56
    -0
      2019/day24/part1.py
  17. +118
    -0
      2019/day24/part2.py
  18. +5
    -0
      2019/day24/testinput
  19. +1
    -0
      2019/day25/input
  20. +33
    -0
      2019/day25/interactive.py
  21. +39
    -0
      2019/day25/map.dot
  22. BIN
      2019/day25/map.png
  23. +174
    -0
      2019/day25/part1.py
  24. +3
    -1
      2019/intcode.py
  25. +59
    -0
      lib/pathfinder.py

+ 4
- 147
2019/day17/part2.py View File

@@ -1,152 +1,9 @@
from collections import deque

import asyncio
import sys

class Intcode:
input_device = None
output_buffer = deque(maxlen=100)
output_device = None
pos = 0
relative_base = 0

def __init__(self, mem, input = []):
self.ops = [self.add, self.mul, self.input, self.output, self.jump_if_true, self.jump_if_false,
self.less_than, self.equals, self.adjust_relative]
self.mem = mem.copy()
self.input_buffer = deque(input, maxlen=100)
self.input_lock = asyncio.Condition()
self.output_lock = asyncio.Condition()

def print_mem(self):
a = [str(x) for x in self.mem.copy()]
a[self.pos] = f'<{a[self.pos]}>'
if self.relative_base < len(self.mem):
a[self.relative_base] = f'*{a[self.relative_base]}'
print('[', ', '.join(a), ']', len(a))
else:
print('[', ', '.join(a), f'] {len(a)}\n*{self.relative_base}')

async def receive(self, value):
async with self.input_lock:
self.input_buffer.append(value)
self.input_lock.notify_all()

async def send(self):
async with self.output_lock:
while not self.output_buffer:
await self.output_lock.wait()
return self.output_buffer.popleft()

async def run(self):
while True:
try:
opcode = self.mem[self.pos] % 100 - 1
if opcode == 98:
break
run = self.ops[opcode]()
if run is not None:
await run
except:
self.print_mem()
raise

def input_args(self, n, offset=0):
args = []
opcode = self.mem[self.pos]
for i in range(offset, offset + n):
pos = self.pos + i + 1
mode = opcode // 10 ** (i + 2) % 10
if mode == 2:
while len(self.mem) <= self.relative_base + self.mem[pos]:
self.mem.append(0)
args.append(self.mem[self.relative_base + self.mem[pos]])
elif mode == 1:
args.append(self.mem[pos])
else:
while len(self.mem) <= self.mem[pos]:
self.mem.append(0)
args.append(self.mem[self.mem[pos]])
return args if n != 1 else args[0]

def output_args(self, n, offset=0):
args = []
opcode = self.mem[self.pos]
for i in range(offset, offset + n):
pos = self.pos + i + 1
mode = opcode // 10 ** (i + 2) % 10
if mode == 2:
args.append(self.relative_base + self.mem[pos])
else:
args.append(self.mem[pos])
last = max(args)
while len(self.mem) <= last:
self.mem.append(0)
return args if n != 1 else args[0]

def add(self):
in1, in2 = self.input_args(2)
out = self.output_args(1, 2)
self.mem[out] = in1 + in2
self.pos += 4

def mul(self):
in1, in2 = self.input_args(2)
out = self.output_args(1, 2)
self.mem[out] = in1 * in2
self.pos += 4

async def input(self):
target = self.output_args(1)
if self.input_device:
self.mem[target] = await self.input_device()
else:
async with self.input_lock:
while not self.input_buffer:
await self.input_lock.wait()
self.mem[target] = self.input_buffer.popleft()
self.pos += 2

async def output(self):
out = self.input_args(1)
if self.output_device:
await self.output_device(out)
else:
async with self.output_lock:
self.output_buffer.append(out)
self.output_lock.notify_all()
self.pos += 2

def jump_if_true(self):
cond, jump = self.input_args(2)
if cond:
self.pos = jump
else:
self.pos += 3

def jump_if_false(self):
cond, jump = self.input_args(2)
if not cond:
self.pos = jump
else:
self.pos += 3

def less_than(self):
in1, in2 = self.input_args(2)
out = self.output_args(1, 2)
self.mem[out] = 1 if in1 < in2 else 0
self.pos += 4

def equals(self):
in1, in2 = self.input_args(2)
out = self.output_args(1, 2)
self.mem[out] = 1 if in1 == in2 else 0
self.pos += 4

def adjust_relative(self):
offset = self.input_args(1)
self.relative_base += offset
self.pos += 2
sys.path.append('../')
from collections import deque
from intcode import Intcode


with open('input') as input:

+ 1
- 0
2019/day19/input View File

@@ -0,0 +1 @@
109,424,203,1,21101,11,0,0,1105,1,282,21102,18,1,0,1106,0,259,1201,1,0,221,203,1,21102,1,31,0,1105,1,282,21101,38,0,0,1106,0,259,20102,1,23,2,21201,1,0,3,21101,1,0,1,21102,57,1,0,1105,1,303,1201,1,0,222,21001,221,0,3,20101,0,221,2,21102,1,259,1,21101,0,80,0,1105,1,225,21101,76,0,2,21102,1,91,0,1106,0,303,2102,1,1,223,21002,222,1,4,21102,1,259,3,21101,0,225,2,21102,225,1,1,21102,1,118,0,1105,1,225,21001,222,0,3,21102,1,54,2,21102,1,133,0,1106,0,303,21202,1,-1,1,22001,223,1,1,21101,148,0,0,1106,0,259,1202,1,1,223,21001,221,0,4,20101,0,222,3,21101,14,0,2,1001,132,-2,224,1002,224,2,224,1001,224,3,224,1002,132,-1,132,1,224,132,224,21001,224,1,1,21101,0,195,0,106,0,108,20207,1,223,2,20101,0,23,1,21101,0,-1,3,21102,1,214,0,1105,1,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,1202,-4,1,249,22102,1,-3,1,21201,-2,0,2,21202,-1,1,3,21101,0,250,0,1106,0,225,22101,0,1,-4,109,-5,2105,1,0,109,3,22107,0,-2,-1,21202,-1,2,-1,21201,-1,-1,-1,22202,-1,-2,-2,109,-3,2105,1,0,109,3,21207,-2,0,-1,1206,-1,294,104,0,99,21201,-2,0,-2,109,-3,2105,1,0,109,5,22207,-3,-4,-1,1206,-1,346,22201,-4,-3,-4,21202,-3,-1,-1,22201,-4,-1,2,21202,2,-1,-1,22201,-4,-1,1,22101,0,-2,3,21102,1,343,0,1106,0,303,1106,0,415,22207,-2,-3,-1,1206,-1,387,22201,-3,-2,-3,21202,-2,-1,-1,22201,-3,-1,3,21202,3,-1,-1,22201,-3,-1,2,22102,1,-4,1,21101,0,384,0,1105,1,303,1106,0,415,21202,-4,-1,-4,22201,-4,-3,-4,22202,-3,-2,-2,22202,-2,-4,-4,22202,-3,-2,-3,21202,-4,-1,-2,22201,-3,-2,1,21202,1,1,-4,109,-5,2106,0,0

+ 24
- 0
2019/day19/part1.py View File

@@ -0,0 +1,24 @@
import asyncio
import sys

sys.path.append('../')
from intcode import Intcode

with open('input') as infile:
program = [int(x) for x in infile.read().strip().split(',')]

async def main():
affected = 0
for x in range(50):
for y in range(50):
computer = Intcode(program)
await computer.receive(x)
await computer.receive(y)
await computer.run()
print('#' if computer.output_buffer[0] else '.', end='')
if computer.output_buffer[0]:
affected += 1
print()
print(affected)

asyncio.run(main())

+ 72
- 0
2019/day19/part2.py View File

@@ -0,0 +1,72 @@
import asyncio
import sys

sys.path.append('../')
from collections import deque
from intcode import Intcode

with open('input') as infile:
program = [int(x) for x in infile.read().strip().split(',')]


async def verify(cx, cy):
for dy in range(100):
for dx in range(100):
computer = Intcode(program)
await computer.receive(cx+dx)
await computer.receive(cy+dy)
await computer.run()
if not computer.output_buffer[0]:
print('Verification failed at', cx+dx, cy+dy)
return False
return True

async def main():
candidates = deque()
y = 800 # This works for my input
first_x = 605
last_width = 0
while True:
found_this_row = False
width = 0
x = first_x
gap = 0
while True:
computer = Intcode(program)
await computer.receive(x)
await computer.receive(y)
await computer.run()
result = computer.output_buffer[0]
#print('#' if result else '.', end='')
if found_this_row:
if result:
width += 1
else:
break
else:
if result:
found_this_row = True
first_x = x
x += last_width - 1
width = last_width
else:
gap += 1
if gap > 10:
break
x += 1
last_width = width
if y % 100 == 0: print(y, first_x, width, len(candidates))
if width >= 100:
while len(candidates) and candidates[0][0] - 99 < first_x:
candidates.popleft()
candidates.append((first_x + width - 1, y))
if candidates[0][1] + 99 <= y:
cx, cy = candidates.popleft()
print(y, first_x, cx, cy)
# verify candidate
if not await verify(first_x, cy): return
print(first_x * 10000 + cy)
break
y += 1

asyncio.run(main())

+ 121
- 0
2019/day20/input View File

@@ -0,0 +1,121 @@
S N I Y A F
D C J J C W
###################################.#########.#######.#########.#######.#####.#####################################
#.......#.#...#.....#...#.....#.........#...#.....#.#.......#...#...........#.....#.................#.#...#.#.#.#.#
###.###.#.###.#####.#.#.#####.###.#####.#.#.#.#.###.#.#########.###.###.#######.###.#####.###.###.###.###.#.#.#.#.#
#...#.#...#.#...#.#...#.#.........#.....#.#.#.#...#.....#.#.....#.#...#.#.#.#.#.......#.#.#.#.#.#.#.#.#...#.#.....#
#.###.#.###.###.#.###.###.###.#####.#####.#.###.#####.#.#.#####.#.###.###.#.#.#.#.#####.###.###.###.#.###.#.#.#####
#.#...#.....................#.#.#.......#.#.........#.#.#.......#.....#.......#.#...#.#...#.#.#.#.......#.....#.#.#
#####.###.#.###.###.#.#.#.#.###.#######.#.#############.#.###.###.#.#.#####.#.#.#####.#.###.#.#.#.###.#.#####.#.#.#
#.........#.#...#...#.#.#.#.#.#.#.......#.#.......#.....#.#.#.#...#.#.#.#...#...#.#.....#...#.....#.#.#.#.....#...#
#.#.#######.#############.###.#.###.#.#.#.#.###.#.#.#.#.#.#.#####.#####.###.#####.#.#####.#####.###.#########.#.#.#
#.#.#...#...#.#.#...........#.......#.#.#.#.#.#.#.#.#.#.#.#...#.....#.....#.....#.....#...#.....#...#...........#.#
###.###.###.#.#.#######.#####.###.#######.#.#.#.#####.###.#.###.###.###.#######.#.#.#####.###.#.#.#.#######.#######
#.#...#.#...#.#...#.#.......#.#...#.#.#.....#.#.....#...#.....#.#...#.#.....#.#...#...........#.#.#.#...#.......#.#
#.#####.#####.#.#.#.###.#########.#.#.#.###.###.#######.#.#.#######.#.###.###.#.###.#.#.###.###.#.###.#####.#.###.#
#...#...........#...#.#.#.#.#.#.....#.#...#.#.....#.#.#.#.#.#.........#.........#...#.#...#...#.#...#.#.....#.#...#
#.#######.#####.###.#.###.#.#.#####.#.#.#######.#.#.#.#.###.###.#.###.#.#.#.#.#.###.#######.#####.###.#######.#.#.#
#.....#...#.#...#.............#.#.....#.#...#...#.#.....#.#...#.#.#.#.#.#.#.#.#...#...#.............#.#.#...#...#.#
#.#########.#####.#######.###.#.#.#########.###.###.###.#.#.#.###.#.#####.#######.###.###.#.#.#.#####.#.#.###.#####
#.#.#.#.#...#.#.#.#.......#.#.....#.....#.........#.#...#...#...#...#.#.......#.#.#.#.#...#.#.#.#.#.#.#...#.......#
#.#.#.#.###.#.#.#####.#.#.#.#####.#.#.#######.#.###.###.#.#.#.###.###.###.#####.###.#.###########.#.#.#.#.###.###.#
#.#...#.....#...#.#...#.#...#.#...#.#.....#...#...#.#...#.#.#.#.#...#.#.#.#.#...#...#.#.....#.#.......#.#...#.#...#
#.###.#.#######.#.#######.###.###.#.###.#######.#####.###.#.#.#.#.###.#.#.#.#.###.#######.###.#.#####.###.###.#####
#...........#.......#.#.#.#.........#.#...#.#.....#.....#.#.#.#...#.#.#...#...#...#...#.#...#.#.#...#...#...#.....#
###.###.###########.#.#.###.###.#.###.#.###.###.#######.#.#######.#.#.###.#.#.#.#.#.###.#.###.#####.#.###.###.#.###
#.....#...#.#.#.......#.#.#.#...#...#...#...#.#.#.......#.#.......#.#.#.....#.#.#.#...#.....#...#...#...#...#.#.#.#
###.#######.#.###.#####.#.#####.#.###.#####.#.#.#.#.#####.###.#.#.#.#.###.###.#.###.###.#.#.#.#####.###.#.###.###.#
#.#.....#.#.#...#...#...#.......#.#.#.....#.#...#.#...#.....#.#.#.......#...#.....#.....#.#...#.#...#.#.#.#.#.#.#.#
#.#.###.#.#.###.#.###.#########.###.###.###.#.#.#.#.#.###.#.###.#######.#######.###.#.#.#######.#.###.#.#.#.#.#.#.#
#.#...#.#...#.#...#.#...#.#...#...#.......#...#.#.#.#.#...#...#.#.......#...........#.#.#...#...#.#.#.....#.......#
#.#.#######.#.#.#.#.#.###.#.#######.###########.#.#######.#######.###########.#############.###.#.#.###.###.###.###
#...#...#.....#.#.#...#.#.#.# S N P R Y M #.#.#.#...#...#.#...#.#.....#
###.#.#####.#.###.###.#.#.#.# J D S A J F #.#.#.#.###.###.#.#######.###
#.#.#.#.#.#.#...........#.#.# #.......#.........#.........#
#.#.#.#.#.#####.###.#####.#.# #.#####.#.###.###.#.#####.###
#...#...#...#.#.#.#.#.#.#...# #.....#.....#.#.#.....#......KQ
###.###.#.###.#.#.#.#.#.#.### #####.###.#####.#.###########
#.#.....#.....#.#.....#.....# #.......#.#.#.#.#...#.......#
#.###.#######.#####.#.#.#.### #.#####.###.#.#.#.#.#######.#
#.#...#.#...#.....#.#.#.#...# SC..#...#.#.#.#.#.#.#.#.#.#.#.#
#.###.#.#.#####.###.#.#.#.### ###.#####.#.#.#.#####.#.#.#.#
ZB....#.#...#.........#...#....UX #.....#...........#.......#..SJ
#.###.###.#####.###.#.####### #.#.#.#.#.#####.###.###.#.#.#
#.................#.#.#.#...# UI..#.#...#.#...#.#.#.#.#.#...#
#.#.###.#####.###.#.###.#.### ###.#.#######.#.#.#.#.#####.#
#.#.#.#.#.#.#...#.#.#.#.....# #.#.#...#.........#.#...#...#
#.#.#.###.#.#########.#.###.# #.#.#######.#####.#.###.#####
#.#.#.#.#.#...#.......#.#...# #.#.#.....#.#.#.......#...#.#
#####.#.#.#.#.#######.#.#.### #.#####.#####.#########.###.#
#.#.#...#...#...#.#.....#...# #...#.....#...............#..RA
#.#.#.###.#####.#.###.#.#.#.# #.#.#.#.###.#############.#.#
JA..............#.......#.#.#..SD NC..#...#.#.....#.....#...#.#.#
#####.###.#.###.############# #.###.#.###.#####.#####.#.#.#
UJ....#.#...#.#...#...#.#......JL #...#.#.#.............#.....#
#.#####.#######.#.#.#.#.###.# #######.#.#.###.#############
#.....#.#.....#.#.#...#...#..FW #.........#.#.#.#.#.#...#...#
#.#########.#####.###.#.##### #.###########.###.#.###.###.#
YM....#...#.#.........#.......# #.#...........#.....#........ND
#.#.#.#.#.#.#####.###.####### ###.#########.#.###.#.#.#####
#.#...#.......#.....#.....#.# #.#.........#...#.#...#.#.#.#
#######.###.###.###########.# #.#########.#####.#.#.###.#.#
#.....#...#.#.#.#.....#.#...# YT..........#.#...#...#...#...#
#.###.#######.###.#####.###.# ###.###.###.#.###.#########.#
#...#...#...#.#.....#.#......XQ #.#.#.........#...#...#...#.#
#.###.###.#.#.#.#.#.#.#.#.#.# #.#.#.#######.#####.###.###.#
#.#...#...#...#.#.#.#.#.#.#.# OY..#.#.#...#.#.#.#.....#.#...#
#.#.###.#.###.###.#.#.#####.# #.#####.###.###.###.#.#.#.#.#
MF..#.....#.#.......#.........# #...#...............#.....#..YT
#.###.#.#####.#.###########.# #.#####.#####.#######.###.#.#
#...#.#.#.#.#.#.#.#.......#.# #...#...#.#...#.#.......#.#.#
#.#######.#.#####.###.###.### #.#.#.###.###.#.#.###.#.#####
#.#.....#.....#...#...#.....# #.#.......#.....#.#...#.#...#
#####.###.#####.###.#.#.###.# ###.###.#####.#############.#
LE......#.#.#.#.#...#.#.#.#...# #.#...#.#.#.#...#...#.....#..XQ
#.#.#.#.#.#.#.###.#.#######.# #.#######.#.###.###.###.#.#.#
#.#.#...............#.#...#..YM ZB....#...#.#...#.#.....#.#...#
#####################.#.##### #.#####.#.###.#####.###.###.#
#.....#...............#.....# #.#...#...#.......#...#...#.#
#.###.#.#########.###.#.###.# #.#.#####.###.#####.#.###.###
#.#.#.#...#.......#...#...#..LE #...................#.....#.#
#.#.#.###.###.###.#.#####.### ###########################.#
#...#...#...#.#...#.#...#...# #.#.........................#
###.###.#.###.###.#.#.#.#.### #.#.#.###.#.#.###.###.###.#.#
YL......#...#.#.#...#...#.....# #.#.#.#.#.#.#.#.....#...#.#.#
###.#######.###.#####.###.#.# #.###.#.#.###.###.#########.#
#.......#.#.....#.#...#...#.# JQ..#.#...#.#...#.....#.......#
###.###.#.###.###.###.#.#.### #.#.###.###.###.###.###.#.###
#.....#...#...#.......#.#.#.# #.........#.#...#.....#.#....JL
###.#########.#####.#######.# Y A I M U K J #.#.###.#####.###.#########.#
#.........#.....#.....#.....# L C J V J Q A #.#.#...#.....#...#.#.......#
#.###.#.#####.#############.#####.#########.###.###########.#######.###.#####.#########.#####.#####.###.#.#####.###
#...#.#...#.....#.......#.#.........#.#.#.#...#.....#.......#.........#.....#.........#...#.#...#...#.....#...#...#
#####.#.#.#######.#####.#.#.###.#.###.#.#.#.#####.#######.#.#.#.#####.###.#.#######.#######.#.#.#.###.###.###.#.###
#.....#.#...#...#...#.#.#...#.#.#.#.........#.....#.......#.#.#...#...#...#.#.#.....#.#...#...#.#...#.#...#.......#
###.#.#####.###.#.###.#.###.#.#.###.###.#######.#######.###.#.#######.###.#.#.#####.#.###.#.#.#######.###.###.###.#
#...#.....#...#...#.......#.#.....#.#...#.....#.#...#.....#.#.#.....#...#.#.....#.........#.#.......#.#.....#.#.#.#
#.#.#.#####.#######.#.#.#####.#.#######.#.#.###.#.#.#.#.#####.#.#.#######.#.###.###.###.###########.#.#.###.###.#.#
#.#.#...#...#.....#.#.#.......#...#.......#.#.#...#.#.#...#.#...#.....#...#...#.#.#.#.#.#...#...#...#.#.#.#.#.....#
###.#.#.#########.#######.#####.#.###.#####.#.#####.#.#####.#####.#######.#.#####.#.#.###.#.#.#####.#####.#######.#
#...#.#.#.................#...#.#.#...#.#.#...#...#.#.#.....#.#.......#...#.#.....#.......#.#.....#.#...#.........#
#.###.#####.###.###.#.#.#.###.#.#####.#.#.#.###.#.#.#.#.#.###.#.#.#.#######.#####.#.###.#.#####.#####.###.###.#.#.#
#.#.....#...#.....#.#.#.#.#.......#...#.....#...#...#...#.#.....#.#.#.....#.#...#.....#.#...............#...#.#.#.#
###.#.#######.#####.#####.###.#.#########.###.#.###.#.#####.#.#######.#.###.#.#####.###.#.#.#.#.###.#.###########.#
#.#.#...#.....#.......#...#.#.#.#.#.....#...#.#...#.#.#...#.#.#.#.#.#.#.........#.#...#.#.#.#.#...#.#.......#.....#
#.#.#.#.#####.#.#####.#.###.###.###.#.###.#.#.#######.#.#.###.#.#.#.###.#.#.#.###.#.###.###.###############.###.#.#
#.#.#.#.#.....#.#...#.#.#.....#.....#...#.#.#.#.#.....#.#.....#...#.....#.#.#...#.....#.#.#...........#.#.....#.#.#
#.###.#####.###.###.#.#.#.###.#.#.###.#.###.#.#.###.#.#####.#.#.###.#.###.###.###.#.#####.#####.#.#.###.#########.#
#.......#.#.#.#.#.....#.#.#.#.#.#.#...#.#...#.....#.#.#...#.#.#...#.#.#.....#...#.#.#.......#...#.#...........#...#
###.#.#.#.###.#.###.#######.#.#####.#######.###.#####.#.#####.#.#####.#.#.#.#.#.###.#.#.#.###.###.###.###.#####.#.#
#...#.#...#.....#...#.#.#.#.#...#...#.#.#...#...#.#.....#...#...#.#.#.#.#.#.#.#.#.....#.#...#.#...#...#...#.#...#.#
###.###.###.#.#.#####.#.#.#.###.#.###.#.#.#.#.#.#.###.###.###.#.#.#.#####.#########.###############.#.#####.###.#.#
#...#.#.#...#.#.#.#.#.#.#.#...........#.#.#.#.#...#.....#.....#.#.#.....#.#...#.....#.#.#...#.#.#.#.#...#.......#.#
###.#.#####.#####.#.#.#.#.#.#######.###.###.#.#######.###.#.#.###.#.#####.#.#######.#.#.#.###.#.#.#######.#.###.#.#
#.#.#...#.#.#.#.............#.#.#...#.......#.......#.#.#.#.#.#.#...#.......#.#.#.#...................#.#.#...#.#.#
#.###.###.###.#######.###.###.#.#.#########.#.#######.#.#####.#.#.#####.#####.#.#.#.###.###############.#####.#####
#.........#.........#.#.....#.....#.....#.#.#...#.#.#...#.....#...#.#...#...#.#...#.#.#.......#.#...#...#.....#...#
#####.###.###.#####.#####.#####.#######.#.#.###.#.#.###.#####.#.#.#.#.###.#.#.#.#.#.#.#.#######.#.###.#.#####.#.###
#.....#.......#.............#...........#...#.......#...#.......#.#.......#...#.#.....#...............#.#.........#
#################################.###.###.#.#######.###.#######.###########.#####.#################################
A U Z M S P U J O
A X Z V C S I Q Y

+ 111
- 0
2019/day20/part1.py View File

@@ -0,0 +1,111 @@
import itertools
import math
import re
import sys

from heapq import heappush, heappop


def breadth_first_search(start, goals, neighbours, step_cost=lambda x,y: 1):
paths = dict()
candidates = [(0, start, tuple())]
best = {}
last = 0
while len(candidates) and len(paths.keys()) < len(goals):
cost, current, path = heappop(candidates)
if current in best and cost > best[current]: continue
last = cost
if current in goals and current not in paths:
path = (*path, current)
paths[current] = (cost, path)
continue
for neighbour in neighbours(current):
new_cost = cost + step_cost(current, neighbour)
if neighbour not in best or best[neighbour] > new_cost:
best[neighbour] = new_cost
heappush(candidates, (new_cost, neighbour, (*path, current)))
return paths


maze = list()

with open('input') as infile:
for line in infile:
maze.append(line[:-1])

portal_regex = re.compile('[A-Z]')
portal_tags = dict()
processed_tags = set()
walls = set()
spaces = set()

for y in range(len(maze)):
for x in range(len(maze[y])):
c = maze[y][x]
coord = (x ,y)
if c == '#':
walls.add(coord)
elif c == '.':
spaces.add(coord)
elif coord not in processed_tags and portal_regex.match(c):
processed_tags.add(coord)
if portal_regex.match(maze[y][x+1]):
processed_tags.add((x+1, y))
portal_tag = c + maze[y][x+1];
portal_y = y
if x > 0 and maze[y][x-1] == '.':
portal_x = x - 1
else:
portal_x = x + 2
else:
processed_tags.add((x, y+1))
portal_tag = c + maze[y+1][x]
portal_x = x
if y > 0 and maze[y-1][x] == '.':
portal_y = y - 1
else:
portal_y = y + 2
portal = (portal_x, portal_y)
portal_tags.setdefault(portal_tag, list()).append(portal)

portals = dict()
portal_tags_reverse = dict()
for tag, p in portal_tags.items():
if len(p) == 2:
p1, p2 = p
portals[p1] = p2
portals[p2] = p1
for pn in p:
portal_tags_reverse[pn] = tag

def neighbours(coord):
x,y = coord
return [(dx, dy) for dx, dy in ((x,y-1), (x-1,y), (x+1,y), (x,y+1)) if (dx, dy) in spaces]

portal_distances = dict()
portal_paths = dict()

for start in portal_tags:
for p1 in portal_tags[start]:
results = breadth_first_search(p1, sum((v for k,v in portal_tags.items() if k != start), list()), neighbours)
for p2, result in results.items():
goal = portal_tags_reverse[p2]
cost, path = result
portal_distances.setdefault(start, dict())
if goal not in portal_distances[start] or portal_distances[start][goal] > cost:
portal_distances[start][goal] = cost
portal_paths.setdefault(start, dict())[goal] = path

result = breadth_first_search('AA', ['ZZ'], lambda current: portal_distances[current].keys(), lambda last, current: portal_distances[last][current])
cost, path = result['ZZ']

pass_through = set()
for i in range(len(path) - 1):
pass_through |= set(portal_paths[path[i]][path[i+1]])
for y in range(len(maze)):
row = maze[y]
for x in range(len(row)):
print('█' if (x,y) in pass_through else row[x], end='')
print()

print(cost + len(path) - 2, path)

+ 111
- 0
2019/day20/part2.py View File

@@ -0,0 +1,111 @@
import itertools
import math
import re
import sys

from heapq import heappush, heappop


def breadth_first_search(start, goals, neighbours, step_cost=lambda x,y: 1):
paths = dict()
candidates = [(0, start, tuple())]
best = {}
last = 0
while len(candidates) and len(paths.keys()) < len(goals):
cost, current, path = heappop(candidates)
if current in best and cost > best[current]: continue
last = cost
if current in goals and current not in paths:
path = (*path, current)
paths[current] = (cost, path)
continue
for neighbour in neighbours(current):
new_cost = cost + step_cost(current, neighbour)
if neighbour not in best or best[neighbour] > new_cost:
best[neighbour] = new_cost
heappush(candidates, (new_cost, neighbour, (*path, current)))
return paths


maze = list()

with open('input') as infile:
for line in infile:
maze.append(line[:-1])

portal_regex = re.compile('[A-Z]')
portal_tags = dict()
processed_tags = set()
walls = set()
spaces = set()

for y in range(len(maze)):
for x in range(len(maze[y])):
c = maze[y][x]
coord = (x ,y)
if c == '#':
walls.add(coord)
elif c == '.':
spaces.add(coord)
elif coord not in processed_tags and portal_regex.match(c):
processed_tags.add(coord)
if portal_regex.match(maze[y][x+1]):
processed_tags.add((x+1, y))
portal_tag = c + maze[y][x+1];
portal_y = y
if x > 0 and maze[y][x-1] == '.':
portal_x = x - 1
else:
portal_x = x + 2
else:
processed_tags.add((x, y+1))
portal_tag = c + maze[y+1][x]
portal_x = x
if y > 0 and maze[y-1][x] == '.':
portal_y = y - 1
else:
portal_y = y + 2
portal = (portal_x, portal_y)
if x == 0 or y == 0 or x == len(maze[y]) - 2 or y == len(maze) - 2:
portal_tags[(portal_tag, 'outer')] = portal
else:
portal_tags[(portal_tag, 'inner')] = portal

def neighbours(coord):
x,y = coord
return [(dx, dy) for dx, dy in ((x,y-1), (x-1,y), (x+1,y), (x,y+1)) if (dx, dy) in spaces]

portal_tags_reverse = {v:k for k,v in portal_tags.items()}
portal_distances = {k:dict() for k in portal_tags.keys()}

for start, p1 in portal_tags.items():
goals = []
for tag, p2 in portal_tags.items():
if tag != start:
goals.append(p2)
results = breadth_first_search(p1, goals, neighbours)
for p2, result in results.items():
goal = portal_tags_reverse[p2]
cost, path = result
if goal not in portal_distances[start] or portal_distances[start][goal] > cost:
portal_distances[start][goal] = cost

def portal_neighbours(current):
tag, inout, layer = current
neighbours = [(*portal, layer) for portal in portal_distances[(tag, inout)].keys()]
if tag != 'AA' and tag != 'ZZ':
if inout == 'inner':
neighbours.append((tag, 'outer', layer + 1))
elif layer > 0:
neighbours.append((tag, 'inner', layer - 1))
return neighbours

def portal_distance(last, current):
if last[0] == current[0]: return 1
return portal_distances[last[:2]][current[:2]]

result = breadth_first_search(('AA', 'outer', 0), [('ZZ', 'outer', 0)], portal_neighbours, portal_distance)
cost, path = result[('ZZ', 'outer', 0)]

print(path)
print(cost)

+ 55
- 0
2019/day21/bothparts.py View File

@@ -0,0 +1,55 @@
import asyncio
import sys

sys.path.append('../')
from intcode import Intcode


with open('input') as infile:
mem = [int(x) for x in infile.read().strip().split(',')]


# jump if not (A and B and C) and D, i.e. if there's a gap in the three steps
# ahead and also somewhere to land
part1asm = '''\
NOT J J
AND A J
AND B J
AND C J
NOT J J
AND D J
WALK
'''

# same as above, but also only jump if E (step after jump) or H (jump after
# jump) so that there's always somewhere to go after jumping
part2asm = '''\
NOT J J
AND A J
AND B J
AND C J
NOT J J
AND D J
NOT E T
NOT T T
OR H T
AND T J
RUN
'''

async def printout(c):
try:
print(chr(c), end='')
except ValueError:
print(c)

async def main(springasm):
computer = Intcode(mem)
computer.output_device = printout
for c in springasm:
await computer.receive(ord(c))
await computer.run()

asyncio.run(main(part1asm))
print('----------')
asyncio.run(main(part2asm))

+ 1
- 0
2019/day21/input
File diff suppressed because it is too large
View File


+ 100
- 0
2019/day22/input.txt View File

@@ -0,0 +1,100 @@
cut -135
deal with increment 38
deal into new stack
deal with increment 29
cut 120
deal with increment 30
deal into new stack
cut -7198
deal into new stack
deal with increment 59
cut -8217
deal with increment 75
cut 4868
deal with increment 29
cut 4871
deal with increment 2
deal into new stack
deal with increment 54
cut 777
deal with increment 40
cut -8611
deal with increment 3
cut -5726
deal with increment 57
deal into new stack
deal with increment 41
deal into new stack
cut -5027
deal with increment 12
cut -5883
deal with increment 45
cut 9989
deal with increment 14
cut 6535
deal with increment 18
cut -5544
deal with increment 29
deal into new stack
deal with increment 64
deal into new stack
deal with increment 41
deal into new stack
deal with increment 6
cut 4752
deal with increment 8
deal into new stack
deal with increment 26
cut -6635
deal with increment 10
deal into new stack
cut -3830
deal with increment 48
deal into new stack
deal with increment 39
cut -4768
deal with increment 65
deal into new stack
cut -5417
deal with increment 15
cut -4647
deal into new stack
cut -3596
deal with increment 17
cut -3771
deal with increment 50
cut 1682
deal into new stack
deal with increment 20
deal into new stack
deal with increment 22
deal into new stack
deal with increment 3
cut 8780
deal with increment 52
cut 7478
deal with increment 9
cut -8313
deal into new stack
cut 742
deal with increment 19
cut 9982
deal into new stack
deal with increment 68
cut 9997
deal with increment 23
cut -240
deal with increment 54
cut -7643
deal into new stack
deal with increment 6
cut -3493
deal with increment 74
deal into new stack
deal with increment 75
deal into new stack
deal with increment 40
cut 596
deal with increment 6
cut -4957
deal into new stack

+ 34
- 0
2019/day22/part1.py View File

@@ -0,0 +1,34 @@
import re


deal_into_new_stack = 'deal into new stack'
cut_n = re.compile('cut (-?\\d+)')
deal_with_increment = re.compile('deal with increment (-?\\d+)')

deck = list(range(10007))

with open('input.txt') as infile:
for instruction in infile:
instruction = instruction.strip()
if instruction == deal_into_new_stack:
deck.reverse()
else:
cut = cut_n.match(instruction)
if cut:
cut_at = int(cut[1])
deck = deck[cut_at:] + deck[:cut_at]
else:
deal = deal_with_increment.match(instruction)
if deal:
increment = int(deal[1])
new_deck = [None] * len(deck)
for n in range(len(deck)):
new_deck[(n * increment) % len(new_deck)] = deck[n]
deck = new_deck
else:
raise Error('Bad instruction: ' + instruction)

for n in range(len(deck)):
if deck[n] == 2019:
print(n)
break

+ 1
- 0
2019/day23/input
File diff suppressed because it is too large
View File


+ 68
- 0
2019/day23/part1.py View File

@@ -0,0 +1,68 @@
import asyncio
import sys

sys.path.append('../')
from collections import deque
from intcode import Intcode

with open('input') as infile:
program = [int(x) for x in infile.read().strip().split(',')]


class IntcodeNic:
def __init__(self, router):
self.router = router
self.input_buffer = deque()
self.input_lock = asyncio.Condition()
self.output_buffer = list()

async def read(self):
async with self.input_lock:
try:
val = self.input_buffer.popleft()
return val
except IndexError:
await asyncio.sleep(0.001) # yield
return -1

async def receive(self, message):
async with self.input_lock:
self.input_buffer.extend(message)

async def send(self, message):
self.output_buffer.append(message)
if len(self.output_buffer) == 3:
await self.router(tuple(self.output_buffer))
self.output_buffer = list()

async def main():
computers = [Intcode(program) for i in range(50)]
nics = list()

final_result = asyncio.Future()
async def router(message):
target = message[0]
if target == 255:
final_result.set_result(message[1:])
else:
try:
await nics[target].receive(message[1:])
except IndexError:
print(message)

nics.extend(IntcodeNic(router) for _ in range(len(computers)))

for i in range(len(computers)):
computers[i].input_device = nics[i].read
computers[i].output_device = nics[i].send
await router((i,i))
running_computers = asyncio.gather(*(c.run() for c in computers))
print(await final_result)
running_computers.cancel()
try:
await running_computers
except asyncio.CancelledError:
pass


asyncio.run(main())

+ 91
- 0
2019/day23/part2.py View File

@@ -0,0 +1,91 @@
import asyncio
import sys

sys.path.append('../')
from collections import deque
from intcode import Intcode

with open('input') as infile:
program = [int(x) for x in infile.read().strip().split(',')]


class IntcodeNic:
def __init__(self, router):
self.router = router
self.input_buffer = deque()
self.input_lock = asyncio.Condition()
self.output_buffer = list()

async def read(self):
async with self.input_lock:
try:
val = self.input_buffer.popleft()
return val
except IndexError:
await asyncio.sleep(0.001) # yield
return -1

async def receive(self, message):
async with self.input_lock:
self.input_buffer.extend(message)

async def send(self, message):
self.output_buffer.append(message)
if len(self.output_buffer) == 3:
await self.router(tuple(self.output_buffer))
self.output_buffer = list()

class NAT:
def __init__(self, router, nics):
self.last = None
self.history = set()
self.nics = nics
self.packet = None
self.result = asyncio.Future()
self.router = router

async def run(self):
while True:
await asyncio.sleep(0.1)
if self.packet and not any(len(nic.input_buffer) for nic in self.nics):
print(f'NAT send {self.packet}')
await self.router((0, *self.packet))
if self.packet[1] == self.last:
return self.last
self.last = self.packet[1]

def receive(self, message):
self.packet = message

async def main():
computers = [Intcode(program) for i in range(50)]
nics = list()

nat = None
async def router(message):
target = message[0]
if target == 255:
nat.receive(message[1:])
else:
try:
await nics[target].receive(message[1:])
except IndexError:
print(message)

nics.extend(IntcodeNic(router) for _ in range(len(computers)))
nat = NAT(router, nics)

for i in range(len(computers)):
computers[i].input_device = nics[i].read
computers[i].output_device = nics[i].send
await router((i,i))
running_computers = asyncio.gather(*(c.run() for c in computers))
print(await nat.run())
running_computers.cancel()
try:
await running_computers
except asyncio.CancelledError:
pass


asyncio.run(main())

+ 5
- 0
2019/day24/input View File

@@ -0,0 +1,5 @@
###..
.##..
#....
##..#
.###.

+ 56
- 0
2019/day24/part1.py View File

@@ -0,0 +1,56 @@
grid = []

with open('input') as infile:
for line in infile:
row = []
grid.append(row)
for c in line:
if c == '#':
row.append(True)
elif c == '.':
row.append(False)

def neighbours(x,y):
neighbours = 0
for nx,ny in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]:
if ny < 0 or nx < 0 or ny >= len(grid) or nx >= len(grid[ny]):
continue
if grid[ny][nx]:
neighbours += 1
return neighbours

def step(grid):
newgrid = []
for y in range(len(grid)):
newrow = []
newgrid.append(newrow)
for x in range(len(grid[y])):
n = neighbours(x,y)
if n == 1:
newrow.append(True)
elif n == 2:
newrow.append(not grid[y][x])
else:
newrow.append(False)
return newgrid

def biodiversity(grid):
cells = sum(grid, list())
bio = 0
for c in range(len(cells)):
if cells[c]:
bio += 2 ** c
return bio

already = set()
while True:
bio = biodiversity(grid)
if bio in already:
for row in grid:
for c in row:
print('#' if c else '.', end='')
print()
print(bio)
break
already.add(bio)
grid = step(grid)

+ 118
- 0
2019/day24/part2.py View File

@@ -0,0 +1,118 @@
class Grid:
grid: list
def __init__(self):
self.grid = [
[False, False, False, False, False],
[False, False, False, False, False],
[False, False, False, False, False],
[False, False, False, False, False],
[False, False, False, False, False]
]
self.parent = None
self.child = None

def total(self):
return sum(sum(self.grid, []))

def grand_total(self):
if self.parent:
return self.parent.grand_total()
else:
return self._grand_total()

def _grand_total(self):
return self.total() + (self.child._grand_total() if self.child else 0)

def row(self, y):
return sum(self.grid[y])

def col(self, x):
return sum(row[x] for row in self.grid)

def neighbours(self, x, y):
neighbours = 0
for nx,ny in [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]:
if ny == -1:
neighbours += self.parent.grid[1][2] if self.parent else 0
elif ny == 5:
neighbours += self.parent.grid[3][2] if self.parent else 0
elif nx == -1:
neighbours += self.parent.grid[2][1] if self.parent else 0
elif nx == 5:
neighbours += self.parent.grid[2][3] if self.parent else 0
elif ny == 2 and nx == 2:
if self.child:
if y == 2:
neighbours += self.child.col(2 + (x-2) * 2)
else:
neighbours += self.child.row(2 + (y-2) * 2)
elif self.grid[ny][nx]:
neighbours += 1
return neighbours

def step(self):
if not self.parent and (self.col(0) == 1 or self.col(4) == 1 or self.row(0) == 1 or self.row(4) == 1):
self.parent = Grid()
self.parent.child = self
if self.parent:
self.parent.step()
else:
self._step()

def _step(self):
newgrid = []
for y in range(len(self.grid)):
newrow = []
newgrid.append(newrow)
for x in range(len(self.grid[y])):
if x == 2 and y == 2:
newrow.append(False)
else:
n = self.neighbours(x,y)
if n == 1:
newrow.append(True)
elif n == 2:
newrow.append(not self.grid[y][x])
else:
newrow.append(False)
if not self.child and (self.grid[1][2] or self.grid[2][1] or self.grid[2][3] or self.grid[3][2]):
self.child = Grid()
self.child.parent = self
if self.child:
self.child._step()
self.grid = newgrid

def print(self, depth=0):
if self.parent:
self.parent.print(depth - 1)
else:
self._print(depth)

def _print(self, depth):
print(f'Depth {depth}:')
for y in range(5):
row = self.grid[y]
for c in row:
print('#' if c else '.', end='')
print()
print()
if self.child:
self.child._print(depth + 1)


grid = Grid()

with open('input') as infile:
y = 0
for line in infile:
for x in range(len(line)):
if line[x] == '#':
grid.grid[y][x] = True
elif line[x] == '.':
grid.grid[y][x] = False
y += 1

for _ in range(200):
grid.step()
# grid.print()
print(grid.grand_total())

+ 5
- 0
2019/day24/testinput View File

@@ -0,0 +1,5 @@
....#
#..#.
#..##
..#..
#....

+ 1
- 0
2019/day25/input
File diff suppressed because it is too large
View File


+ 33
- 0
2019/day25/interactive.py View File

@@ -0,0 +1,33 @@
import asyncio
import sys

sys.path.append('../')
from intcode import Intcode


with open('input') as infile:
mem = [int(x) for x in infile.read().strip().split(',')]

async def printout(c):
try:
print(chr(c), end='')
except ValueError:
print(c)

async def main():
computer = Intcode(mem)
computer.output_device = printout
run = asyncio.create_task(computer.run())
while True:
try:
await asyncio.wait_for(asyncio.shield(run), 0.2)
except asyncio.TimeoutError:
pass # This is expected
if run.done():
return
s = input()
for c in s:
await computer.receive(ord(c))
await computer.receive(10)

asyncio.run(main())

+ 39
- 0
2019/day25/map.dot View File

@@ -0,0 +1,39 @@
strict graph {
"Hull Breach":n -- "Navigation":s
"Hull Breach":e -- "Crew Quarters":w
"Hull Breach":s -- "Kitchen":n
"Navigation":s -- "Hull Breach":n
"Navigation":w -- "Holodeck":e
"Crew Quarters":s -- "Warp Drive Maintenance":n
"Crew Quarters":w -- "Hull Breach":e
"Kitchen":n -- "Hull Breach":s
"Kitchen":e -- "Storage":w
"Kitchen":s -- "Observatory":n
"Storage":n -- "Passages":s
"Storage":e -- "Science Lab":w
"Storage":w -- "Kitchen":e
"Observatory":n -- "Kitchen":s
"Observatory":w -- "Sick Bay":e
"Sick Bay":e -- "Observatory":w
"Holodeck":e -- "Navigation":w
"Warp Drive Maintenance":n -- "Crew Quarters":s
"Passages":s -- "Storage":n
"Passages":w -- "Engineering":e
"Science Lab":n -- "Hallway":s
"Science Lab":s -- "Gift Wrapping Center":n
"Science Lab":w -- "Storage":e
"Hallway":n -- "Stables":s
"Hallway":s -- "Science Lab":n
"Gift Wrapping Center":n -- "Science Lab":s
"Stables":s -- "Hallway":n
"Stables":w -- "Security Checkpoint":e
"Security Checkpoint":n -- "Unreachable":s
"Security Checkpoint":e -- "Stables":w
"Engineering":n -- "Hot Chocolate Fountain":s
"Engineering":e -- "Passages":w
"Engineering":s -- "Corridor":n
"Hot Chocolate Fountain":s -- "Engineering":n
"Corridor":n -- "Engineering":s
"Corridor":s -- "Arcade":n
"Arcade":n -- "Corridor":s
}

BIN
2019/day25/map.png View File

Before After
Width: 994  |  Height: 774  |  Size: 114KB

+ 174
- 0
2019/day25/part1.py View File

@@ -0,0 +1,174 @@
import asyncio
import itertools
import re
import sys

sys.path.append('../')
sys.path.append('../../lib')
from collections import deque
from enum import Enum
from intcode import Intcode
from pathfinder import a_star
from queue import Queue


reverse_direction = {
'north': 'south',
'south': 'north',
'east': 'west',
'west': 'east'
}

room_name_regex = re.compile('== ([\\w ]+) ==')
exits_regex = re.compile('Doors here lead:\n(- (north|south|east|west)\n)+')
items_regex = re.compile('Items here:\n(- .+\n)+')

def get_exits(room_text):
match = exits_regex.search(room_text)
start, end = match.span()
exits = [x[2:] for x in room_text[start + len('Doors here lead:\n'):end-1].split('\n')]
return exits

def get_items(room_text):
match = items_regex.search(room_text)
if not match: return []
start, end = match.span()
items = [x[2:] for x in room_text[start + len('Items here:\n'):end-1].split('\n')]
return items

bad_items = set(['giant electromagnet', 'molten lava'])

class Navigator:
def __init__(self, computer, running):
self.computer = computer
self.input_buffer = list()
self.input_lock = asyncio.Condition()
self.items = []
self.map = {}
self.position = None
self.running = running
self.visited = set()

async def run(self):
await self.build_map()
await self.navigate('Security Checkpoint')
# Let's get down to business
# To defeat
# The plate
for direction, destination in self.map['Security Checkpoint'].items():
if destination == 'Unreachable': break
async def try_plate():
return await self.send(direction)
# Drop everything
for item in self.items:
await self.send('drop ' + item)
for combi_l in map(itertools.combinations, itertools.repeat(self.items), range(1, len(self.items))):
for combi in combi_l:
for item in combi:
await self.send('take ' + item)
result = await self.send(direction)
if 'lighter' not in result and 'heavier' not in result:
print(result)
return
for item in combi:
await self.send('drop ' + item)

async def build_map(self):
room_text = await self.read_response()
self.position = room_name_regex.search(room_text)[1]
for _ in range(20):
room_name = self.position
self.visited.add(room_name)
exits = get_exits(room_text)
for item in get_items(room_text):
if item not in bad_items:
await self.send('take ' + item)
self.items.append(item)
if room_name not in self.map:
self.map[room_name] = {exit:None for exit in exits}
for exit in exits:
if self.map[room_name][exit] is not None: continue
new_room_text = await self.send(exit)
if "You can't go that way" in new_room_text:
self.map[room_name][exit] = 'Unreachable'
continue
new_room_name = room_name_regex.search(new_room_text)[1]
if new_room_name == room_name:
self.map[room_name][exit] = 'Unreachable'
continue
self.map[room_name][exit] = new_room_name
if new_room_name not in self.map:
self.map[new_room_name] = {exit:None for exit in get_exits(new_room_text)}
self.map[new_room_name][reverse_direction[exit]] = room_name
if new_room_name == 'Pressure-Sensitive Floor':
# We get ejected from this room, so skip the going back step
continue
new_room_text = await self.send(reverse_direction[exit])
if "You can't go that way" in new_room_text or room_name_regex.search(new_room_text)[1] != room_name:
print('Exit not bi-directional!', room_name, exit)
return
unvisited = None
for neighbour in self.map[room_name].values():
if neighbour == 'Unreachable': continue
for exit, destination in self.map[neighbour].items():
if destination is None:
unvisited = (neighbour, exit)
break
if not unvisited:
unvisited = await self.find_unvisited()
if not unvisited:
return
room_text = await self.navigate(unvisited[0])

async def find_unvisited(self):
for room, links in self.map.items():
for exit, destination in links.items():
if destination is None:
return (room, exit)

async def navigate(self, destination):
room_name = self.position
path = a_star(room_name, destination, lambda a,b: 1, lambda room: self.map.get(room, {}).values())
for step in path[1:]:
exit_to = {v:k for k,v in self.map[room_name].items()}
new_room_text = await self.send(exit_to[step])
new_room_name = room_name_regex.search(new_room_text)[1]
if new_room_name != step:
raise Exception(f'Navigation error! Went {exit_to[step]} from {room_name} and expected to reach {step} but ended up in {new_room_name}')
room_name = step
self.position = new_room_name
return new_room_text

async def read_response(self):
response_text = ''
while not response_text.endswith('Command?\n'):
await asyncio.sleep(0.05)
async with self.input_lock:
response_text += ''.join(self.input_buffer)
self.input_buffer.clear()
if self.running.done(): break
return response_text

async def receive(self, c):
async with self.input_lock:
self.input_buffer.append(chr(c))

async def send(self, message):
for c in message:
await self.computer.receive(ord(c))
await self.computer.receive(10)
return await self.read_response()


with open('input') as infile:
mem = [int(x) for x in infile.read().strip().split(',')]


async def main():
computer = Intcode(mem)
computer.output_device = lambda c: nav.receive(c)
run = asyncio.create_task(computer.run())
nav = Navigator(computer, run)
await nav.run()

asyncio.run(main())

+ 3
- 1
2019/intcode.py View File

@@ -4,7 +4,6 @@ import asyncio

class Intcode:
input_device = None
output_buffer = deque(maxlen=100)
output_device = None
pos = 0
relative_base = 0
@@ -14,6 +13,7 @@ class Intcode:
self.less_than, self.equals, self.adjust_relative]
self.mem = mem.copy()
self.input_buffer = deque(input, maxlen=100)
self.output_buffer = deque(maxlen=100)
self.input_lock = asyncio.Condition()
self.output_lock = asyncio.Condition()

@@ -47,6 +47,8 @@ class Intcode:
run = self.ops[opcode]()
if run is not None:
await run
except asyncio.CancelledError:
return
except:
self.print_mem()
raise

+ 59
- 0
lib/pathfinder.py View File

@@ -0,0 +1,59 @@
import math

from heapq import heappop, heappush


def a_star(start, goal, h, neighbours, cost = lambda x,y: 1, print_steps=False):
steps = 0
open_set = set([start])
came_from = {}
g = {start: 0}
f = [(0, start)]

while len(open_set):
current = None
for candidate in f:
if candidate[1] in open_set:
current = candidate[1]
break
if current == goal:
path = [current]
while current in came_from:
prev = came_from[current]
path.insert(0, prev)
current = prev
return path
open_set.remove(current)
for neighbour in neighbours(current):
tentative_g = g[current] + cost(current, neighbour)
if tentative_g < g.get(neighbour, math.inf):
came_from[neighbour] = current;
g[neighbour] = tentative_g
heappush(f, (g[neighbour] + h(neighbour, goal), neighbour))
open_set.add(neighbour)
if print_steps:
steps += 1
if steps % 100 == 0: print(steps, len(open_set), g[current], h(current, goal), current)
return None


def breadth_first_search(start, goals, neighbours, step_cost=lambda x,y: 1):
paths = dict()
candidates = [(0, start, tuple())]
best = {}
last = 0
while len(candidates) and len(paths.keys()) < len(goals):
cost, current, path = heappop(candidates)
if current in best and cost > best[current]: continue
last = cost
if current in goals and current not in paths:
path = (*path, current)
paths[current] = (cost, path)
continue
for neighbour in neighbours(current):
new_cost = cost + step_cost(current, neighbour)
if neighbour not in best or best[neighbour] > new_cost:
best[neighbour] = new_cost
heappush(candidates, (new_cost, neighbour, (*path, current)))
return paths

Loading…
Cancel
Save