diff --git a/Day15/Day15.py b/Day15/Day15.py new file mode 100644 index 0000000..eddc05a --- /dev/null +++ b/Day15/Day15.py @@ -0,0 +1,242 @@ +from pprint import pprint +from collections import deque +import fileinput +import time + + +class Elf: + power = 3 + locations = set() + deaths = 0 + + def __init__(self): + self.health = 200 + self.targeted = False + + def __repr__(self): + return "E" + + +class Goblin: + enemy = Elf + power = 3 + locations = set() + deaths = 0 + + def __init__(self): + self.health = 200 + self.targeted = False + + def __repr__(self): + return "G" + + +Elf.enemy = Goblin + + +class Wall: + enemy = None + + def __init__(self): + self.targeted = False + + def __repr__(self): + return "#" + + +class Empty: + enemy = None + + def __init__(self): + self.targeted = False + + def __repr__(self): + return "." if not self.targeted else "\033[33m?\033[0m" + + +NEIGHBORS = [(0, -1), (-1, 0), (1, 0), (0, 1)] + + +def read(): + field = [] + Elf.deaths = 0 + Goblin.deaths = 0 + Elf.locations.clear() + Goblin.locations.clear() + for y, line in enumerate(fileinput.input()): + row = [] + + for x, char in enumerate(line.rstrip()): + if char == "E": + row.append(Elf()) + Elf.locations.add((x, y)) + elif char == "G": + row.append(Goblin()) + Goblin.locations.add((x, y)) + elif char == "#": + row.append(Wall()) + elif char == ".": + row.append(Empty()) + else: + raise ValueError(char) + + field.append(row) + + return field + + +def distance(a, b): + (ax, ay), (bx, by) = a, b + return abs(ax - bx) + abs(ay - by) + + +def find_closest(field, pos, targets): + width = len(field[0]) + height = len(field) + + queue = deque([[pos]]) + seen = {pos} + paths = [] + + while queue: + path = queue.popleft() + x, y = path[-1] + + if (x, y) in targets: + paths.append(path[1:]) + + for dx, dy in NEIGHBORS: + x_, y_ = x + dx, y + dy + if 0 <= x_ < width and 0 <= y_ < height \ + and type(field[y_][x_]) is Empty \ + and (x_, y_) not in seen: + queue.append(path + [(x_, y_)]) + seen.add((x_, y_)) + + if not paths or [] in paths: + return None + + return min(paths, key=lambda path: (len(path), path[-1][1], path[-1][0], path[0][1], path[0][0])) + + + +def move(field, x, y, value): + for row in field: + for col in row: + col.targeted = False + + targets = [] + for tx, ty in value.enemy.locations: + targets.extend([(tx + dx, ty + dy) for dx, dy in NEIGHBORS]) + + if not targets: + raise StopIteration + + for tx, ty in targets: + field[ty][tx].targeted = True + + closest = find_closest(field, (x, y), targets) + # print_field(field, highlight=(x, y), path=closest) + # time.sleep(0.3) + + if closest: + tx, ty = closest[0] + if type(field[ty][tx]) is Empty: + # print(f"{field[y][x]}@{x},{y} moves to {tx},{ty}") + type(value).locations.discard((x, y)) + type(value).locations.add((tx, ty)) + field[ty][tx] = field[y][x] + field[y][x] = Empty() + return tx, ty + return x, y + + +def attack(field, x, y, value): + width = len(field[0]) + height = len(field) + + neighbors = [(field[y + y_][x + x_], (x + x_, y + y_)) + for x_, y_ in NEIGHBORS + if 0 <= y + y_ < width and 0 <= x + x_ < width and \ + type(field[y + y_][x + x_]) is value.enemy] + if not neighbors: + return + + target, (tx, ty) = \ + min(neighbors, key=lambda x: (x[0].health, x[1][1], x[1][0])) + # print(f"{value}@{x},{y}:{value.health} attacks " + # f"{target}@{tx},{ty}:{target.health}->{target.health - value.power}") + target.health -= value.power + + if target.health <= 0: + # print(f"{target}@{tx,ty} dies :(") + type(target).locations.discard((tx, ty)) + type(target).deaths += 1 + field[ty][tx] = Empty() + + +def print_field(field, steps=None, highlight=None, path=None): + if steps is not None: + print(f"round={steps}") + for y, row in enumerate(field): + units = [] + for x, value in enumerate(row): + if highlight == (x, y): + print(end="\033[31;1m") + if path and (x, y) in path: + print(end="\033[44;1m") + + print(value, end="\033[0m") + + if type(value) in (Goblin, Elf): + units.append(value) + + etc = sorted(units, key=lambda unit: (str(unit), unit.health)) + print(" ", ", ".join(f"{x}({x.health})" for x in etc)) + print() + + +def step(field, steps): + # print_field(field, steps) + seen_units = set() + for y, row in enumerate(field): + for x, value in enumerate(row): + if type(value) not in (Elf, Goblin) or value in seen_units: + continue + seen_units.add(value) + + tx, ty = move(field, x, y, value) + attack(field, tx, ty, value) + + +def main(): + field = read() + + steps = 0 + while True: + try: + step(field, steps) + except StopIteration: + total_health = 0 + for row in field: + for value in row: + if type(value) in (Elf, Goblin) and value.health > 0: + total_health += value.health + + print("Rounds:", steps) + print("Health:", total_health) + print("Elf Deaths:", Elf.deaths) + print(total_health * steps) + break + + steps += 1 + + +while True: + main() + if Elf.deaths == 0: + print("yay") + break + + Elf.power += 1 + print("oh no :(, trying", Elf.power) diff --git a/Day16/Day16A.c b/Day16/Day16A.c new file mode 100644 index 0000000..e8d9911 --- /dev/null +++ b/Day16/Day16A.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include + +typedef struct { + uint8_t before[4]; + uint8_t opcode; + uint8_t a, b, c; + uint8_t after[4]; +} Sample; + +int sample_read(Sample *sample, FILE *file) { + int read = 0; + read += + fscanf(file, + "Before: [%" SCNu8 ", %" SCNu8 ", %" SCNu8 ", %" SCNu8 "]\n", + &sample->before[0], &sample->before[1], &sample->before[2], + &sample->before[3]); + + read += fscanf(file, "%" SCNu8 " %" SCNu8 " %" SCNu8 " %" SCNu8 "\n", + &sample->opcode, &sample->a, &sample->b, &sample->c); + + read += + fscanf(file, + "After: [%" SCNu8 ", %" SCNu8 ", %" SCNu8 ", %" SCNu8 "]\n\n", + &sample->after[0], &sample->after[1], &sample->after[2], + &sample->after[3]); + + return read == 12; +} + +void sample_print(const Sample sample) { + printf( + "{\n" + " before: [%d, %d, %d, %d],\n" + " opcode: %d, a: %d, b: %d, c: %d,\n" + " after: [%d, %d, %d, %d]\n" + "}\n", + sample.before[0], sample.before[1], sample.before[2], sample.before[3], + sample.opcode, sample.a, sample.b, sample.c, + sample.after[0], sample.after[1], sample.after[2], sample.after[3] + ); +} + +int sample_possible_opcodes(const Sample s) { + int possible_opcodes = 0; + + // Addition + if (s.after[s.c] == s.before[s.a] + s.before[s.b]) { + printf("addr, "); + possible_opcodes += 1; + } + if (s.after[s.c] == s.before[s.a] + s.b) { + printf("addi, "); + possible_opcodes += 1; + } + + // Multiplication + if (s.after[s.c] == s.before[s.a] * s.before[s.b]) { + printf("mulr, "); + possible_opcodes += 1; + } + if (s.after[s.c] == s.before[s.a] * s.b) { + printf("muli, "); + possible_opcodes += 1; + } + + // Bitwise AND + if (s.after[s.c] == (s.before[s.a] & s.before[s.b])) { + printf("banr, "); + possible_opcodes += 1; + } + if (s.after[s.c] == (s.before[s.a] & s.b)) { + printf("bani, "); + possible_opcodes += 1; + } + + // Bitwise OR + if (s.after[s.c] == (s.before[s.a] | s.before[s.b])) { + printf("borr, "); + possible_opcodes += 1; + } + if (s.after[s.c] == (s.before[s.a] | s.b)) { + printf("bori, "); + possible_opcodes += 1; + } + + // Assignment + if (s.after[s.c] == s.before[s.a]) { + printf("setr, "); + possible_opcodes += 1; + } + if (s.after[s.c] == s.a) { + printf("seti, "); + possible_opcodes += 1; + } + + // Greater-than testing + if (s.after[s.c] == (s.a > s.before[s.b])) { + printf("gtir, "); + possible_opcodes += 1; + } + if (s.after[s.c] == (s.before[s.a] > s.b)) { + printf("gtri, "); + possible_opcodes += 1; + } + if (s.after[s.c] == (s.before[s.a] > s.before[s.b])) { + printf("gtrr, "); + possible_opcodes += 1; + } + + // Equality testing + if (s.after[s.c] == (s.a == s.before[s.b])) { + printf("eqir, "); + possible_opcodes += 1; + } + if (s.after[s.c] == (s.before[s.a] == s.b)) { + printf("eqri, "); + possible_opcodes += 1; + } + if (s.after[s.c] == (s.before[s.a] == s.before[s.b])) { + printf("eqrr, "); + possible_opcodes += 1; + } + + return possible_opcodes; +} + +int main(int argc, char **argv) { + if (argc != 2) { + printf("Usage: %s [input]\n", argv[0]); + return EXIT_FAILURE; + } + + FILE *file = fopen(argv[1], "r"); + Sample sample = {0}; + + int gt3 = 0, samples = 0; + while (sample_read(&sample, file)) { + sample_print(sample); + int possible_opcodes = sample_possible_opcodes(sample); + printf("%d possible opcodes.\n\n", possible_opcodes); + if (possible_opcodes >= 3) { + gt3 += 1; + } + samples += 1; + } + printf("%d samples\n", samples); + printf("%d\n", gt3); + + fclose(file); + return EXIT_SUCCESS; +} diff --git a/Day16/Day16B.c b/Day16/Day16B.c new file mode 100644 index 0000000..3ff0515 --- /dev/null +++ b/Day16/Day16B.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include + +typedef struct { + uint8_t opcode; + uint32_t a, b, c; +} Instruction; + +typedef struct { + uint32_t before[4]; + Instruction instruction; + uint32_t after[4]; +} Sample; + +enum { + ADDR, ADDI, MULR, MULI, + BANR, BANI, BORR, BORI, + SETR, SETI, GTIR, GTRI, + GTRR, EQIR, EQRI, EQRR, +}; + +const char *names[] = { + "addr", "addi", "mulr", "muli", + "banr", "bani", "borr", "bori", + "setr", "seti", "gtir", "gtri", + "gtrr", "eqir", "eqri", "eqrr", +}; + +uint16_t table[16] = { + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, +}; + +int opcodes[16]; + +int sample_read(Sample *s, FILE *file) { + int read = 0; + + read = + fscanf(file, "Before: [%u, %u, %u, %u]\n", + &s->before[0], &s->before[1], &s->before[2], &s->before[3]); + if (read != 4) return 0; + + Instruction *i = &s->instruction; + read = fscanf(file, "%" SCNu8 " %u %u %u\n", + &i->opcode, &i->a, &i->b, &i->c); + if (read != 4) return 0; + + read = + fscanf(file, "After: [%u, %u, %u, %u]\n\n", + &s->after[0], &s->after[1], &s->after[2], &s->after[3]); + if (read != 4) return 0; + + return 1; +} + +uint16_t sample_possible_opcodes(const Sample s) { + uint16_t possible_opcodes = 0; + + Instruction i = s.instruction; + // Addition + if (s.after[i.c] == s.before[i.a] + s.before[i.b]) possible_opcodes |= 1 << ADDR; + if (s.after[i.c] == s.before[i.a] + i.b) possible_opcodes |= 1 << ADDI; + // Multiplication + if (s.after[i.c] == s.before[i.a] * s.before[i.b]) possible_opcodes |= 1 << MULR; + if (s.after[i.c] == s.before[i.a] * i.b) possible_opcodes |= 1 << MULI; + // Bitwise AND + if (s.after[i.c] == (s.before[i.a] & s.before[i.b])) possible_opcodes |= 1 << BANR; + if (s.after[i.c] == (s.before[i.a] & i.b)) possible_opcodes |= 1 << BANI; + // Bitwise OR + if (s.after[i.c] == (s.before[i.a] | s.before[i.b])) possible_opcodes |= 1 << BORR; + if (s.after[i.c] == (s.before[i.a] | i.b)) possible_opcodes |= 1 << BORI; + // Assignment + if (s.after[i.c] == s.before[i.a]) possible_opcodes |= 1 << SETR; + if (s.after[i.c] == i.a) possible_opcodes |= 1 << SETI; + // Greater-than testing + if (s.after[i.c] == (i.a > s.before[i.b])) possible_opcodes |= 1 << GTIR; + if (s.after[i.c] == (s.before[i.a] > i.b)) possible_opcodes |= 1 << GTRI; + if (s.after[i.c] == (s.before[i.a] > s.before[i.b])) possible_opcodes |= 1 << GTRR; + // Equality testing + if (s.after[i.c] == (i.a == s.before[i.b])) possible_opcodes |= 1 << EQIR; + if (s.after[i.c] == (s.before[i.a] == i.b)) possible_opcodes |= 1 << EQRI; + if (s.after[i.c] == (s.before[i.a] == s.before[i.b])) possible_opcodes |= 1 << EQRR; + + return possible_opcodes; +} + +int bits_set(uint16_t n) { + int bit = -1; + for (int i = 0; i < 16; i++) { + if ((n >> i) & 1) { + if (bit != -1) return -1; + bit = i; + } + } + return bit; +} + +void find_opcodes() { + int unknown = 16; + while (unknown > 0) { + for (int opcode = 0; opcode < 16; opcode++) { + int bit = bits_set(table[opcode]); + if (bit == -1) continue; + + // Set every row in this column to 0 + for (int j = 0; j < 16; j++) + table[j] &= ~(1 << bit); + + opcodes[opcode] = bit; + unknown -= 1; + } + } + + for (int i = 0; i < 16; i++) { + printf("%d: %s\n", i, names[opcodes[i]]); + } +} + +int instruction_read(Instruction *i, FILE *file) { + return fscanf(file, "%" SCNu8 " %u %u %u\n", + &i->opcode, &i->a, &i->b, &i->c) == 4; +} + +void instruction_execute(const Instruction *i, uint32_t *r) { + switch (opcodes[i->opcode]) { + // Addition + case ADDR: r[i->c] = r[i->a] + r[i->b]; break; + case ADDI: r[i->c] = r[i->a] + i->b; break; + + // Multiplication + case MULR: r[i->c] = r[i->a] * r[i->b]; break; + case MULI: r[i->c] = r[i->a] * i->b; break; + + // Bitwise AND + case BANR: r[i->c] = (r[i->a] & r[i->b]); break; + case BANI: r[i->c] = (r[i->a] & i->b); break; + + // Bitwise OR + case BORR: r[i->c] = (r[i->a] | r[i->b]); break; + case BORI: r[i->c] = (r[i->a] | i->b); break; + + // Assignment + case SETR: r[i->c] = r[i->a]; break; + case SETI: r[i->c] = i->a; break; + + // Greater-than testing + case GTIR: r[i->c] = (i->a > r[i->b]); break; + case GTRI: r[i->c] = (r[i->a] > i->b); break; + case GTRR: r[i->c] = (r[i->a] > r[i->b]); break; + + // Equality testing + case EQIR: r[i->c] = (i->a == r[i->b]); break; + case EQRI: r[i->c] = (r[i->a] == i->b); break; + case EQRR: r[i->c] = (r[i->a] == r[i->b]); break; + + default: assert(0); + } +} + +int main(int argc, char **argv) { + if (argc != 2) { + printf("Usage: %s [input]\n", argv[0]); + return EXIT_FAILURE; + } + + FILE *file = fopen(argv[1], "r"); + Sample sample = {0}; + + while (sample_read(&sample, file)) { + uint16_t possible_opcodes = sample_possible_opcodes(sample); + table[sample.instruction.opcode] &= possible_opcodes; + } + find_opcodes(); + + uint32_t registers[4] = {0}; + Instruction instruction = {0}; + while (instruction_read(&instruction, file)) { + const char *name = names[opcodes[instruction.opcode]]; + printf("opcode: %2d (%s), a: %d, b: %d, c: %d\n", instruction.opcode, + name, instruction.a, instruction.b, instruction.c); + instruction_execute(&instruction, registers); + printf("registers: %d %d %d %d\n", registers[0], registers[1], + registers[2], registers[3]); + } + + fclose(file); + return EXIT_SUCCESS; +}