#include #include #include #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; }