-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy path16.c
257 lines (217 loc) · 5.28 KB
/
16.c
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#include "adventofcode.h"
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define PUZZLE_NAME "Day 16: Chronal Classification"
enum OpCode {
OP_UNKNOWN = -1,
OP_ADD_R,
OP_ADD_I,
OP_MUL_R,
OP_MUL_I,
OP_BAN_R,
OP_BAN_I,
OP_BOR_R,
OP_BOR_I,
OP_SET_R,
OP_SET_I,
OP_GT_IR,
OP_GT_RI,
OP_GT_RR,
OP_EQ_IR,
OP_EQ_RI,
OP_EQ_RR,
};
struct Op {
enum OpCode code;
int a;
int b;
int c;
};
static void exec(const enum OpCode op, const int a, const int b, const int c,
const int in[static 4], int out[static 4]) {
switch (op) {
case OP_ADD_R:
out[c] = in[a] + in[b];
break;
case OP_ADD_I:
out[c] = in[a] + b;
break;
case OP_MUL_R:
out[c] = in[a] * in[b];
break;
case OP_MUL_I:
out[c] = in[a] * b;
break;
case OP_BAN_R:
out[c] = in[a] & in[b];
break;
case OP_BAN_I:
out[c] = in[a] & b;
break;
case OP_BOR_R:
out[c] = in[a] | in[b];
break;
case OP_BOR_I:
out[c] = in[a] | b;
break;
case OP_SET_R:
out[c] = in[a];
break;
case OP_SET_I:
out[c] = a;
break;
case OP_GT_IR:
out[c] = a > in[b] ? 1 : 0;
break;
case OP_GT_RI:
out[c] = in[a] > b ? 1 : 0;
break;
case OP_GT_RR:
out[c] = in[a] > in[b] ? 1 : 0;
break;
case OP_EQ_IR:
out[c] = a == in[b] ? 1 : 0;
break;
case OP_EQ_RI:
out[c] = in[a] == b ? 1 : 0;
break;
case OP_EQ_RR:
out[c] = in[a] == in[b] ? 1 : 0;
break;
case OP_UNKNOWN:
assert(0);
break;
}
}
static void parse_registers(const char line[static 1024], int dest[static 4]) {
for (unsigned i = 0; i < 4; i++) {
dest[i] = line[9 + i * 3] - '0';
}
}
static struct Op parse_op(const char s[static 1024]) {
struct Op op;
op.code = atoi(s);
while (!isspace(*s)) {
s++;
}
op.a = atoi(++s);
while (!isspace(*s)) {
s++;
}
op.b = atoi(++s);
while (!isspace(*s)) {
s++;
}
op.c = atoi(++s);
return op;
}
unsigned solve_pt1(void) {
unsigned cnt = 0;
char buf[1024];
int registers_in[4];
int registers_out[4];
while (fgets(buf, 1024, stdin)) {
if (buf[0] == '\n') {
break;
}
// buf contains initial register state
parse_registers(buf, registers_in);
// next line contains Op
assert(fgets(buf, 1024, stdin) != NULL);
struct Op op = parse_op(buf);
// next line contains final register state
assert(fgets(buf, 1024, stdin) != NULL);
parse_registers(buf, registers_out);
// skip empty line
assert(fgets(buf, 1024, stdin) != NULL);
// perform all possible opcodes
unsigned opcode_matches = 0;
int out[4];
memcpy(out, registers_in, 4 * sizeof(int));
for (enum OpCode o = OP_ADD_R; o <= OP_EQ_RR; o++) {
exec(o, op.a, op.b, op.c, registers_in, out);
if (memcmp(registers_out, out, 4 * sizeof(int)) == 0) {
opcode_matches++;
}
}
if (opcode_matches >= 3) {
cnt++;
}
}
return cnt;
}
int solve_pt2(void) {
char buf[1024];
int registers_in[4];
int registers_out[4];
int opcode_to_usercode[16];
int usercode_to_opcode[16];
memset(opcode_to_usercode, OP_UNKNOWN, 16 * sizeof(*opcode_to_usercode));
memset(usercode_to_opcode, OP_UNKNOWN, 16 * sizeof(*usercode_to_opcode));
enum OpCode match;
rewind(stdin);
// first pass
// determine all opcodes
while (fgets(buf, 1024, stdin)) {
if (buf[0] == '\n') {
break;
}
// buf contains initial register state
parse_registers(buf, registers_in);
// next line contains Op
assert(fgets(buf, 1024, stdin) != NULL);
struct Op op = parse_op(buf);
// next line contains final register state
assert(fgets(buf, 1024, stdin) != NULL);
parse_registers(buf, registers_out);
// skip empty line
assert(fgets(buf, 1024, stdin) != NULL);
// skip if we already know what opcode this is
if (usercode_to_opcode[op.code] != OP_UNKNOWN) {
continue;
}
// perform all possible opcodes
unsigned opcode_matches = 0;
int out[4] = { registers_in[0], registers_in[1], registers_in[2], registers_in[3] };
for (enum OpCode o = OP_ADD_R; o <= OP_EQ_RR; o++) {
if (opcode_to_usercode[o] != OP_UNKNOWN) {
continue;
}
exec(o, op.a, op.b, op.c, registers_in, out);
if (memcmp(registers_out, out, 4 * sizeof(*registers_out)) == 0) {
opcode_matches++;
match = o;
}
}
if (opcode_matches == 1) {
opcode_to_usercode[match] = op.code;
usercode_to_opcode[op.code] = match;
// If we're still missing mappings at this point
// We have to rewind stdin
// But this was not necessary for my puzzle input
//rewind(stdin);
}
}
// skip empty line
assert(fgets(buf, 1024, stdin) != NULL);
memset(registers_in, 0, 4 * sizeof(*registers_in));
while (fgets(buf, 1024, stdin) != NULL) {
struct Op op = parse_op(buf);
exec(usercode_to_opcode[op.code], op.a, op.b, op.c, registers_in, registers_in);
}
return registers_in[0];
}
int main(void) {
clock_t t = clock_time();
unsigned int pt1 = solve_pt1();
int pt2 = solve_pt2();
printf("--- %s ---\n", PUZZLE_NAME);
printf("Part 1: %d\n", pt1);
printf("Part 2: %d\n", pt2);
printf("Time: %.2fms\n", clock_time_since(t));
return EXIT_SUCCESS;
}