|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#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 optimization...
|
|
|
|
if (instr->i == INSTR_GTIR && instr->a == 0x100) {
|
|
|
|
reg_t *reg = ®s[instr->b];
|
|
|
|
if (0x100 <= *reg) {
|
|
|
|
*reg >>= 8;
|
|
|
|
regs[ip] = 8;
|
|
|
|
} else {
|
|
|
|
regs[ip] = 28;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|