-
Notifications
You must be signed in to change notification settings - Fork 0
/
process.py
executable file
·195 lines (153 loc) · 5.12 KB
/
process.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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/env python
"""
Advent of Code 2020: Day 14
"""
import copy
import os
import re
import signal
import sys
from types import FrameType
from typing import List, Mapping
from pathlib import Path
TXT_FILES: List[Path] = [Path(f) for f in os.listdir('./') if '.txt' in f]
TXT_REGEX = re.compile(
'(?P<item>\w+)(\[(?P<address>\d+)])?\s=\s(?P<value>\w+)')
def read_values(file: Path) -> Mapping:
"""
Read netmask and memory values from an input file
:param file: text file
:return: generator of values formatted in a mapping
"""
batch = dict()
for i, l in enumerate(open(file)):
m = TXT_REGEX.match(l)
assert m, f'failed match on line {i}: {l}'
if m['item'] == 'mask':
if batch:
yield batch
value = m['value']
mask: str = ''.join('1' if b == 'X' else '0' for b in value)
overlay: str = ''.join('1' if b == '1' else '0' for b in value)
batch['bitmask'] = int(mask, 2)
batch['overlay'] = int(overlay, 2)
batch['mem_values'] = dict()
elif m['item'] == 'mem':
address = int(m['address'], 10)
value = int(m['value'], 10)
batch['mem_values'][address] = value
else:
raise Exception(l)
yield batch
def read_values_part2(file: Path) -> Mapping:
"""
Read netmask and memory values from an input file
:param file: text file
:return: generator of values formatted in a mapping
"""
batch = dict()
for i, l in enumerate(open(file)):
m = TXT_REGEX.match(l)
assert m, f'failed match on line {i}: {l}'
if m['item'] == 'mask':
if batch:
yield batch
batch['mask'] = m['value']
batch['mem_values'] = dict()
elif m['item'] == 'mem':
address = f'{int(m["address"], 10):036b}'
value = int(m['value'], 10)
batch['mem_values'][address] = value
else:
raise Exception(l)
yield batch
def graph_addr_list(mask: str, index: int) -> List[int]:
"""
Graph possible combinations of address with floating bit
:param mask: mask encoded as a string or ['0', '1', 'X']
:param index: mask bit index
:return: list of possible matching addresses
"""
#tree = list()
if index == len(mask):
return [int(mask, 2)]
bit = mask[index]
if bit != 'X':
tree = graph_addr_list(mask=mask, index=1 + index)
else:
new_mask = list(mask)
new_mask[index] = '0'
new_str = ''.join(new_mask)
tree = graph_addr_list(mask=new_str, index=1 + index)
new_mask[index] = '1'
new_str = ''.join(new_mask)
tree.extend(graph_addr_list(mask=new_str, index=1 + index))
return tree
def dump_batch(batch: Mapping) -> None:
"""
Print batch contents
:batch file: text file
:return: nothing
"""
print(f'Bitmask: {batch["bitmask"]:09x}, overlay: {batch["overlay"]:09x}')
for k, v in batch['mem_values'].items():
print(f'\t@0x{k:09x}: 0x{v:09x}')
def process(file: Path, part_two: bool = False) -> int:
"""
Process input file yielding the submission value
:param file: file containing the input values
:param part_two: true for processing part 2
:return: value to submit
"""
debug = False
mem_map = dict()
if not part_two:
for batch in read_values(file=file):
if debug:
dump_batch(batch=batch)
for addr, data in batch['mem_values'].items():
updated_data = data & batch['bitmask'] | batch['overlay']
mem_map[addr] = updated_data
else:
for batch in read_values_part2(file=file):
mask = batch['mask']
for addr, data in batch['mem_values'].items():
masked_address = ''.join(
addr[i] if b == '0' else b for i, b in enumerate(mask))
float_addresses = graph_addr_list(mask=masked_address, index=0)
for address in float_addresses:
mem_map[address] = data
submission = sum(mem_map.values())
return submission
def main() -> int:
"""
Main function
:return: Shell exit code
"""
for file in TXT_FILES:
submission = process(file=Path(file))
print(f'In file {file}, submission: {submission}')
print(f'Part 2')
for file in TXT_FILES:
submission = process(file=Path(file), part_two=True)
print(f'In file {file}, submission: {submission}')
return 0
def handle_sigint(signal_value: signal.Signals, frame: FrameType) -> None:
"""
Interrupt signal call-back method
:param signal_value: signal (expected SIGINT)
:param frame: current stack frame at the time of signal
:return: nothing
"""
assert signal_value == signal.SIGINT
print(frame.f_locals)
sys.exit(1)
def install_signal_handler() -> None:
"""
Install interrupt signal handler
:return: nothing
"""
signal.signal(signal.SIGINT, handle_sigint)
if __name__ == '__main__':
install_signal_handler()
sys.exit(main())