-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path14.koto
123 lines (109 loc) · 3.14 KB
/
14.koto
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
# https://adventofcode.com/2020/day/14
from enum import make_enum
commands = make_enum "mask", "mem"
int_to_bits = |n, bit_count|
# Assuming n < 2.pow bit_count
x = 2.pow (bit_count - 1)
for _ in 0..bit_count
yield (n / x).floor()
n %= x
x /= 2
bits_to_int = |bits|
initial_multiplier = 2.pow (bits.size() - 1)
result, _ = bits.fold (0, initial_multiplier), |(sum, multiplier), bit|
sum += bit * multiplier
multiplier = (multiplier / 2).floor()
sum, multiplier
result
parse_program = |input|
input.lines()
.each |line|
words = line.split " "
match words.next()
"mask" then
# e.g. mask = 0110xx10...
words.next() # =
mask = words.next()
.chars()
.each |c|
match c
"X" then null
"0" then 0
"1" then 1
.to_tuple()
commands.mask, mask, null
mem then
# e.g. mem[43816] = 123
index = mem[4..]
.split "]"
.next()
.to_number()
words.next() # =
value = words.next().to_number()
commands.mem, index, value
.to_tuple()
apply_mask_to_value = |value, mask|
value_bits = int_to_bits value, 36
bits_to_int value_bits
.zip(mask)
.each |(value_bit, mask_bit)|
if mask_bit
mask_bit
else
value_bit
.to_tuple()
index_decoder = |index, mask|
decoded, floating_bits = (int_to_bits index, 36)
.zip mask
.fold ([], []), |(result, floating_bits), (index_bit, mask_bit)|
result.push match mask_bit
0 then index_bit
1 then 1
null then floating_bits.push result.size()
result, floating_bits
floating_count = floating_bits.size()
bit_permutations = (0..2.pow floating_count)
.each |n| int_to_bits n, floating_count
for bit_permutation in bit_permutations
for i, bit in floating_bits.zip bit_permutation
decoded[i] = bit
yield bits_to_int decoded
run_program = |program, version|
memory = {}
mask = null
for command, value_1, value_2 in program
match command
commands.mask then mask = value_1
commands.mem then
index, value = value_1, value_2
match version
1 then
memory.insert index, (apply_mask_to_value value, mask)
2 then
for decoded_index in index_decoder index, mask
memory.insert decoded_index, value
# The program result is the sum of all values in memory
memory.values().sum()
@main = ||
program = io.extend_path koto.script_dir, "input", "14"
>> io.read_to_string
>> parse_program
print "Part one: ${run_program program, 1}" # 4297467072083
print "Part two: ${run_program program, 2}" # 5030603328768
@tests =
@test part_one: ||
program = parse_program "\
mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0
"
assert_eq (run_program program, 1), 165
@test part_two: ||
program = parse_program "\
mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1
"
assert_eq (run_program program, 2), 208