Advent of Code 2018
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

262 lines
6.8 KiB

#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#define REGS 4
typedef long 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;
};
struct sample {
reg_t before[REGS];
reg_t after[REGS];
struct instr instr;
};
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 sample *parse_samples(const char *fname, int *len,
struct instr **program, int *program_len)
{
FILE *f = fopen(fname, "r");
if (!f) {
perror(fname);
return NULL;
}
GArray *array = g_array_new(FALSE, FALSE, sizeof(struct sample));
// Haha what is proper parsing
struct sample sample;
while (fscanf(f,
"Before: [%ld, %ld, %ld, %ld]\n"
"%d %d %d %d\n"
"After: [%ld, %ld, %ld, %ld]\n\n",
&sample.before[0], &sample.before[1],
&sample.before[2], &sample.before[3],
(int *)&sample.instr.i, &sample.instr.a,
&sample.instr.b, &sample.instr.c,
&sample.after[0], &sample.after[1],
&sample.after[2], &sample.after[3]
) == 12) {
g_array_append_val(array, sample);
}
fscanf(f, "\n\n");
GArray *prog_array = g_array_new(FALSE, FALSE, sizeof(struct instr));
struct instr instr;
while (fscanf(f, "%d %d %d %d\n",
(int *)&instr.i, &instr.a, &instr.b, &instr.c) == 4) {
g_array_append_val(prog_array, instr);
}
fclose(f);
*program_len = prog_array->len;
*program = (struct instr *)g_array_free(prog_array, FALSE);
*len = array->len;
return (struct sample *)g_array_free(array, FALSE);
}
int main()
{
int samples_len;
struct instr *program;
int program_len;
struct sample *samples = parse_samples("input", &samples_len,
&program, &program_len);
if (!samples) return 1;
reg_t *regs = g_new0(reg_t, REGS);
enum instr_id *mapping = g_new(enum instr_id, INSTR_ID_COUNT);
for (enum instr_id *map = mapping;
map < mapping + INSTR_ID_COUNT; map++) {
*map = INSTR_ID_COUNT;
}
int changed = 1;
while (changed) {
changed = 0;
for (enum instr_id id = 0; id < INSTR_ID_COUNT; id++) {
if (mapping[id] != INSTR_ID_COUNT) continue;
int possible[INSTR_ID_COUNT] = {[0 ... INSTR_ID_COUNT - 1] = 1};
for (enum instr_id *map = mapping;
map < mapping + INSTR_ID_COUNT; map++) {
if (*map != INSTR_ID_COUNT) possible[*map] = 0;
}
for (struct sample *sample = samples;
sample < samples + samples_len; sample++) {
if (sample->instr.i != id) continue;
struct instr instr;
memcpy(&instr, &sample->instr, sizeof(struct instr));
for (enum instr_id i = 0; i < INSTR_ID_COUNT; i++) {
if (!possible[i]) continue;
instr.i = i;
memcpy(regs, sample->before, sizeof(reg_t) * REGS);
instr_interpret(regs, &instr);
if (memcmp(regs, sample->after, sizeof(reg_t) * REGS) != 0) {
possible[i] = 0;
}
}
}
printf("%02d: ", id);
for (enum instr_id i = 0; i < INSTR_ID_COUNT; i++) {
if (possible[i]) printf("%s ", instr_str[i]);
}
putchar('\n');
int tally = 0;
for (int *i = possible; i < possible + INSTR_ID_COUNT; i++) tally += *i;
if (!tally) {
fprintf(stderr, "Instruction %d could not be identified!\n", id);
goto error;
}
if (tally != 1) continue;
for (enum instr_id i = 0; i < INSTR_ID_COUNT; i++) {
if (possible[i]) {
mapping[id] = i;
break;
}
}
changed = 1;
printf("-- %02d: %s (%02d)\n", id, instr_str[mapping[id]], mapping[id]);
}
}
putchar('\n');
for (int i = 0; i < INSTR_ID_COUNT; i++) {
printf("%02d: %s (%02d)\n", i, instr_str[mapping[i]], mapping[i]);
}
memset(regs, 0, sizeof(reg_t) * REGS);
for (struct instr *instr = program;
instr < program + program_len; instr++) {
if (instr->i >= INSTR_ID_COUNT) {
fprintf(stderr, "ERROR: Invalid instruction ID %d\n", (int)instr->i);
goto error;
}
instr->i = mapping[instr->i];
instr_interpret(regs, instr);
}
printf("%lu\n", regs[0]);
error:
g_free(mapping);
g_free(regs);
g_free(program);
g_free(samples);
return 0;
}