Skip to content

Commit 6d641cc

Browse files
author
Vladimir Makarov
committed
AOC 2017 - day18
1 parent e56034a commit 6d641cc

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

py/advent-of-code-2017/day18.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from collections import defaultdict, deque
2+
from typing import Union, List, Callable
3+
import operator
4+
5+
Operand = Union[str, int]
6+
7+
8+
class State:
9+
def __init__(self, program=0):
10+
self.position = 0
11+
self.played_sound = 0
12+
self.recovered_sound = 0
13+
self.registers = defaultdict(lambda: 0)
14+
self.registers['p'] = program
15+
self.queue = deque()
16+
self.counterpart = None
17+
self.sent_count = 0
18+
19+
20+
def eval_operand(operand: Operand, registers: dict[str, int]) -> int:
21+
try:
22+
return int(operand)
23+
except ValueError:
24+
return registers[operand]
25+
26+
27+
def modify_register(modification: Callable[[int, int], int]):
28+
def apply_modification(state: State, register: str, value: Operand):
29+
state.registers[register] = modification(state.registers[register], eval_operand(value, state.registers))
30+
state.position += 1
31+
32+
return apply_modification
33+
34+
35+
def perform_sound(state: State, value: Operand):
36+
state.played_sound = eval_operand(value, state.registers)
37+
state.position += 1
38+
39+
40+
def perform_recover(state: State, value: Operand):
41+
if eval_operand(value, state.registers) != 0:
42+
state.recovered_sound = state.played_sound
43+
state.position += 1
44+
45+
46+
def perform_jgz(state: State, condition: Operand, offset: Operand):
47+
if eval_operand(condition, state.registers) > 0:
48+
state.position += eval_operand(offset, state.registers)
49+
else:
50+
state.position += 1
51+
52+
53+
def perform_send(state: State, value: Operand):
54+
state.counterpart.queue.append(eval_operand(value, state.registers))
55+
state.position += 1
56+
state.sent_count += 1
57+
58+
59+
def perform_receive(state: State, value: Operand):
60+
if state.queue:
61+
state.registers[value] = state.queue.popleft()
62+
state.position += 1
63+
64+
65+
instruction_handlers_1 = {
66+
'set': modify_register(lambda old, new: new),
67+
'add': modify_register(operator.add),
68+
'mul': modify_register(operator.mul),
69+
'mod': modify_register(operator.mod),
70+
'snd': perform_sound,
71+
'rcv': perform_recover,
72+
'jgz': perform_jgz
73+
}
74+
75+
76+
instruction_handlers_2 = instruction_handlers_1.copy()
77+
instruction_handlers_2['snd'] = perform_send
78+
instruction_handlers_2['rcv'] = perform_receive
79+
80+
81+
def perform_instruction(state: State, instruction: List[str], handlers: dict[str, Callable]):
82+
[name, *args] = instruction
83+
handlers[name](state, *args)
84+
85+
86+
def part1(instructions: List[List[str]]) -> int:
87+
state = State()
88+
while 0 <= state.position < len(instructions) and state.recovered_sound == 0:
89+
perform_instruction(state, instructions[state.position], instruction_handlers_1)
90+
return state.recovered_sound
91+
92+
93+
def part2(instructions: List[List[str]]) -> int:
94+
p0 = State(0)
95+
p1 = State(1)
96+
p0.counterpart = p1
97+
p1.counterpart = p0
98+
p0_before, p1_before = None, None
99+
while p0_before != p0.position or p1_before != p1.position:
100+
if 0 <= p0.position < len(instructions):
101+
p0_before = p0.position
102+
perform_instruction(p0, instructions[p0.position], instruction_handlers_2)
103+
if 0 <= p1.position < len(instructions):
104+
p1_before = p1.position
105+
perform_instruction(p1, instructions[p1.position], instruction_handlers_2)
106+
return p1.sent_count
107+
108+
109+
instructions = [line.rstrip('\n').split(' ') for line in open('input/day18.txt').readlines()]
110+
111+
print('Part 1:', part1(instructions))
112+
print('Part 2:', part2(instructions))

0 commit comments

Comments
 (0)