-
Notifications
You must be signed in to change notification settings - Fork 0
/
intcode.py
171 lines (147 loc) · 5.02 KB
/
intcode.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from util import get_input
from collections import defaultdict
class Program:
def __init__(self, day):
self.day = day
self.program_initial = make_program(get_input(day, split=True))
self.program = self.program_initial.copy()
self.ip = 0
self.rb = 0
self.output_buffer = []
self.finished = False
def run(self, input_buffer=None):
if input_buffer is None:
input_buffer = []
self.finished, self.output_buffer, self.program, self.ip, self.rb = execute_intcode(
self.program,
self.ip,
self.rb,
input_buffer
)
def reset(self):
self.program = self.program_initial.copy()
self.ip = 0
self.rb = 0
self.output_buffer = []
self.finished = False
def restart(self, input_buffer=None):
self.reset()
self.run(input_buffer)
def copy(self):
copy = Program(self.day)
copy.program = self.program.copy()
copy.ip = self.ip
copy.rb = self.rb
copy.output_buffer = self.output_buffer.copy()
copy.finsihed = self.finished
return copy
def make_program(program_list):
program_dict = defaultdict(int)
for i in range(len(program_list)):
program_dict[i] = int(program_list[i])
return program_dict
def get_modes(full_opcode, p):
# input:
# full opcode: ABCDE
# A = mode of parameter 3
# B = mode of parameter 2
# C = mode of parameter 1
# DE = opcode
# p: number of parameters (modes needed)
# return:
# p = 1: [C]
# p = 2: [C, B]
# p = 3: [C, B, A]
return [(full_opcode // 10 ** (i + 2)) % 10 for i in range(p)]
def get_addresses(program, ip, modes, rb=0):
# 0 = position
# 1 = immediate
# 2 = relative
addresses = []
for mode in modes:
ip += 1
if mode == 0:
addresses.append(program[ip])
elif mode == 1:
addresses.append(ip)
else:
addresses.append(program[ip] + rb)
return addresses
def execute_intcode(program, ip=0, rb=0, input_buffer=None):
if input_buffer is None:
input_buffer = []
output_buffer = []
input_index = 0
while True:
full_opcode = program[ip]
opcode = full_opcode % 100
if opcode in [99]:
# 0-parameter operations
# 99 = halt
return True, output_buffer, program, ip, rb
elif opcode in [3, 4, 9]:
# 1-parameter operations
# 3 = store
# 4 = output
# 9 = adjust relative base
parameter_count = 1
modes = get_modes(full_opcode, parameter_count)
addresses = get_addresses(program, ip, modes, rb)
p1 = addresses[0]
if opcode == 3:
if input_index == len(input_buffer):
return False, output_buffer, program, ip, rb
program[p1] = input_buffer[input_index]
input_index += 1
elif opcode == 4:
output_buffer.append(program[p1])
else:
rb = rb + program[p1]
ip += parameter_count + 1
elif opcode in [5, 6]:
# 2-parameter operations
# 5 = jump-if-true
# 6 = jump-if-false
parameter_count = 2
modes = get_modes(full_opcode, parameter_count)
addresses = get_addresses(program, ip, modes, rb)
p1 = addresses[0]
p2 = addresses[1]
if (opcode == 5 and program[p1] != 0) or (opcode == 6 and program[p1] == 0):
ip = program[p2]
else:
ip += parameter_count + 1
elif opcode in [1, 2, 7, 8]:
# 3-parameter operations
# 1 = add
# 2 = mult
# 7 = less than
# 8 = equals
parameter_count = 3
modes = get_modes(full_opcode, parameter_count)
addresses = get_addresses(program, ip, modes, rb)
val_1 = program[addresses[0]]
val_2 = program[addresses[1]]
store = addresses[2]
if opcode == 1:
program[store] = val_1 + val_2
elif opcode == 2:
program[store] = val_1 * val_2
elif opcode == 7 and val_1 < val_2:
program[store] = 1
elif opcode == 8 and val_1 == val_2:
program[store] = 1
else:
program[store] = 0
ip += parameter_count + 1
else:
return
def execute_until_input(program_state, input_buffer=None):
program, ip, relative_base = program_state
return execute_intcode(program, input_buffer, ip, relative_base)
def execute_program_io(program, input_buffer=None):
_, output_buffer, _ = execute_intcode(program, input_buffer)
return output_buffer
def execute_program(program):
_, _, program_state = execute_intcode(program)
return program_state[0]