diff --git a/day21p1/main.c b/day21p1/main.c index 2ac228f..05a83db 100644 --- a/day21p1/main.c +++ b/day21p1/main.c @@ -204,7 +204,7 @@ int main(int argc, char *argv[]) print_regs(regs, stdout); putchar('\n'); - free(regs); - free(instructions); + g_free(regs); + g_free(instructions); return 0; } diff --git a/day21p2/Makefile b/day21p2/Makefile new file mode 100644 index 0000000..c711967 --- /dev/null +++ b/day21p2/Makefile @@ -0,0 +1,12 @@ +CFLAGS := -Wall -Wextra -std=c17 -D_GNU_SOURCE + +LIBS := glib-2.0 +CFLAGS += $(shell pkg-config --cflags $(LIBS)) +LDLIBS := $(shell pkg-config --libs $(LIBS)) + +.PHONY: all +all: main + +.PHONY: clean +clean: + rm -f main diff --git a/day21p2/input b/day21p2/input new file mode 100644 index 0000000..36bea34 --- /dev/null +++ b/day21p2/input @@ -0,0 +1,32 @@ +#ip 4 +seti 123 0 2 +bani 2 456 2 +eqri 2 72 2 +addr 2 4 4 +seti 0 0 4 +seti 0 8 2 +bori 2 65536 5 +seti 2238642 0 2 +bani 5 255 3 +addr 2 3 2 +bani 2 16777215 2 +muli 2 65899 2 +bani 2 16777215 2 +gtir 256 5 3 +addr 3 4 4 +addi 4 1 4 +seti 27 3 4 +seti 0 8 3 +addi 3 1 1 +muli 1 256 1 +gtrr 1 5 1 +addr 1 4 4 +addi 4 1 4 +seti 25 4 4 +addi 3 1 3 +seti 17 2 4 +setr 3 9 5 +seti 7 9 4 +eqrr 2 0 3 +addr 3 4 4 +seti 5 0 4 diff --git a/day21p2/input.dec b/day21p2/input.dec new file mode 100644 index 0000000..5ba4e6f --- /dev/null +++ b/day21p2/input.dec @@ -0,0 +1,64 @@ +#ip 4 + +; while (123 & 456 != 72); +00: seti 123 0 2 +01: bani 2 456 2 +02: eqri 2 72 2 +03: addr 2 4 4 +04: seti 0 0 4 + +05: seti 0 8 2 ; r2 = 0 +; xref 30 +06: bori 2 65536 5 ; r5 = r2 | 0x10000 +07: seti 2238642 0 2 ; r2 = 2238642 +; xref 27 +08: bani 5 255 3 ; r3 = r5 & 0xff +09: addr 2 3 2 ; r2 = r2 + r3 +10: bani 2 16777215 2 ; r2 = r2 & 0xffffff +11: muli 2 65899 2 ; r2 = r2 * 65899 +12: bani 2 16777215 2 ; r2 = r2 & 0xffffff +; trash = r3 +; keep = r2, r5 + +13: gtir 256 5 3 ; if (0x100 > r5) +14: addr 3 4 4 ; then +15: addi 4 1 4 ; (false) jr +1 (17) +16: seti 27 3 4 ; (true ) jp 27 (28) + +; xref 16 (256 <= r5) +; r5 >>= 8 +17: seti 0 8 3 ; r3 = 0 +18: addi 3 1 1 ; r1 = r3 + 1 +19: muli 1 256 1 ; r1 = r1 * 0x100 +20: gtrr 1 5 1 ; if (r1 > r5) +21: addr 1 4 4 ; then +22: addi 4 1 4 ; .. +23: seti 25 4 4 ; .. +24: addi 3 1 3 ; (false) r3 = r3 + 1 +25: seti 17 2 4 ; (false) jp 17 (18) +26: setr 3 9 5 ; (true ) r5 = r3 +27: seti 7 9 4 ; (true ) jp 07 (08) + +; xref 16 (256 > r5) +28: eqrr 2 0 3 ; if (r2 == r0) +29: addr 3 4 4 ; then +30: seti 5 0 4 ; (false) jp 05 (06) + ; (true ) exit + +; ===== HIGH-LEVEL PSEUDOCODE ===== + +r2 = 0 + +do { + r5 = r2 | 0x10000 + r2 = 2238642 + + while (1) { + // r2 = (r2 + r5) * 65899 + r2 = (((r2 + (r5 & 0xff)) & 0xffffff) * 65899) & 0xffffff + + if (0x100 > r5) break + + r5 >>= 8 + } +} while (r2 != r0) diff --git a/day21p2/main.c b/day21p2/main.c new file mode 100644 index 0000000..8d243b2 --- /dev/null +++ b/day21p2/main.c @@ -0,0 +1,226 @@ +#include +#include + +#include + +#define REGS 6 + +typedef int reg_t; + +enum instr_id { + INSTR_ADDR, + INSTR_ADDI, + INSTR_MULR, + INSTR_MULI, + INSTR_BANR, + INSTR_BANI, + INSTR_BORR, + INSTR_BORI, + INSTR_SETR, + INSTR_SETI, + INSTR_GTIR, + INSTR_GTRI, + INSTR_GTRR, + INSTR_EQIR, + INSTR_EQRI, + INSTR_EQRR, + INSTR_ID_COUNT +}; + +struct instr { + enum instr_id i; + int a; + int b; + int c; +}; + +const char *instr_str[] = { + "addr", + "addi", + "mulr", + "muli", + "banr", + "bani", + "borr", + "bori", + "setr", + "seti", + "gtir", + "gtri", + "gtrr", + "eqir", + "eqri", + "eqrr" +}; + +void instr_interpret(reg_t *r, struct instr *i) +{ + switch (i->i) { + case INSTR_ADDR: + case INSTR_MULR: + case INSTR_BANR: + case INSTR_BORR: + case INSTR_GTRR: + case INSTR_EQRR: + if (i->b >= REGS) return; + // fallthrough + case INSTR_ADDI: + case INSTR_MULI: + case INSTR_BANI: + case INSTR_BORI: + case INSTR_SETR: + case INSTR_GTRI: + case INSTR_EQRI: + if (i->a >= REGS) return; + // fallthrough + case INSTR_SETI: + if (i->c >= REGS) return; + break; + + case INSTR_GTIR: + case INSTR_EQIR: + if (i->b >= REGS) return; + if (i->c >= REGS) return; + break; + + default: + break; + } + + switch (i->i) { + case INSTR_ADDR: r[i->c] = r[i->a] + r[i->b]; break; + case INSTR_ADDI: r[i->c] = r[i->a] + i->b; break; + case INSTR_MULR: r[i->c] = r[i->a] * r[i->b]; break; + case INSTR_MULI: r[i->c] = r[i->a] * i->b; break; + case INSTR_BANR: r[i->c] = r[i->a] & r[i->b]; break; + case INSTR_BANI: r[i->c] = r[i->a] & i->b; break; + case INSTR_BORR: r[i->c] = r[i->a] | r[i->b]; break; + case INSTR_BORI: r[i->c] = r[i->a] | i->b; break; + case INSTR_SETR: r[i->c] = r[i->a]; break; + case INSTR_SETI: r[i->c] = i->a; break; + case INSTR_GTIR: r[i->c] = (i->a > r[i->b]) ? 1 : 0; break; + case INSTR_GTRI: r[i->c] = (r[i->a] > i->b) ? 1 : 0; break; + case INSTR_GTRR: r[i->c] = (r[i->a] > r[i->b]) ? 1 : 0; break; + case INSTR_EQIR: r[i->c] = (i->a == r[i->b]) ? 1 : 0; break; + case INSTR_EQRI: r[i->c] = (r[i->a] == i->b) ? 1 : 0; break; + case INSTR_EQRR: r[i->c] = (r[i->a] == r[i->b]) ? 1 : 0; break; + default: break; + } +} + +struct instr *parse_instructions(const char *fname, int *len, int *ip) +{ + FILE *f = fopen(fname, "r"); + if (!f) { + perror(fname); + return NULL; + } + + GArray *array = g_array_new(FALSE, FALSE, sizeof(struct instr)); + + char *line = NULL; + size_t line_len = 0; + while (getline(&line, &line_len, f) != -1) { + char *args = strchr(line, ' '); + if (!args) continue; + *args++ = '\0'; + + if (strcmp(line, "#ip") == 0) { + *ip = strtol(args, NULL, 0); + continue; + } + + struct instr instr = {0}; + + for (int i = 0; i < INSTR_ID_COUNT; i++) { + if (strcmp(line, instr_str[i]) == 0) { + instr.i = i; + break; + } + } + + sscanf(args, "%d %d %d", &instr.a, &instr.b, &instr.c); + g_array_append_val(array, instr); + } + + free(line); + fclose(f); + + *len = array->len; + return (struct instr *)g_array_free(array, FALSE); +} + +void print_regs(reg_t *regs, FILE *f) +{ + putc('[', f); + for (int i = 0; i < REGS - 1; i++) { + fprintf(f, "%d, ", regs[i]); + } + fprintf(f, "%d]", regs[REGS - 1]); +} + +int main(int argc, char *argv[]) +{ + int debug = 0; + if (argc > 1 && strcmp(argv[1], "-debug") == 0) debug = 1; + + int ip = 0; + int instructions_len = 0; + struct instr *instructions = parse_instructions("input", &instructions_len, &ip); + if (!instructions) return 1; + if (ip >= REGS) ip = 0; + + GHashTable *seen = g_hash_table_new(NULL, NULL); + gpointer last = 0; + + reg_t *regs = g_new0(reg_t, REGS); + while (regs[ip] >= 0 && regs[ip] < instructions_len) { + struct instr *instr = &instructions[regs[ip]]; + + // Day 21 part 2 optimizations... + if (regs[ip] == 17) { + regs[5] >>= 8; + regs[ip] = 8; + continue; + } + + if (debug) { + fprintf(stderr, "ip=%d ", regs[ip]); + print_regs(regs, stderr); + fprintf(stderr, " %s %d %d %d ", + instr_str[instr->i], instr->a, instr->b, instr->c); + } + + instr_interpret(regs, instr); + + if (debug) { + print_regs(regs, stderr); + putc('\n', stderr); + } + + // Day 21 part 2 solution... + if (instr->i == INSTR_EQRR && (instr->a == 0 || instr->b == 0)) { + int reg = instr->a; + if (reg == 0) reg = instr->b; + if (reg != 0 && reg < REGS) { + gpointer val = GINT_TO_POINTER(regs[reg]); + if (g_hash_table_contains(seen, val)) { + printf("Last value: %p\n", last); + break; + } + last = val; + g_hash_table_add(seen, val); + } + } + + regs[ip]++; + } + + print_regs(regs, stdout); + putchar('\n'); + + g_hash_table_destroy(seen); + g_free(regs); + g_free(instructions); + return 0; +}