From c31d6e53c21017204a633d151f7cd3af56f94a4c Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Sun, 8 Dec 2019 12:27:24 +0100 Subject: [PATCH] Update --- .gitignore | 1 + Day01/Day1A.s | 52 +++++++++ Day19/Day19A.c | 128 +++++++++++++++++++++ Day20/Day20.py | 130 +++++++++++++++++++++ Day21/Day21A.cpp | 184 +++++++++++++++++++++++++++++ Day22/day22/Cargo.lock | 4 + Day22/day22/Cargo.toml | 7 ++ Day22/day22/src/grid.rs | 123 ++++++++++++++++++++ Day22/day22/src/main.rs | 31 +++++ Day22/day22/src/state.rs | 41 +++++++ Day23/day23/Cargo.lock | 95 +++++++++++++++ Day23/day23/Cargo.toml | 8 ++ Day23/day23/src/attempts.txt | 1 + Day23/day23/src/main.rs | 217 +++++++++++++++++++++++++++++++++++ 14 files changed, 1022 insertions(+) create mode 100644 Day01/Day1A.s create mode 100644 Day19/Day19A.c create mode 100644 Day20/Day20.py create mode 100644 Day21/Day21A.cpp create mode 100644 Day22/day22/Cargo.lock create mode 100644 Day22/day22/Cargo.toml create mode 100644 Day22/day22/src/grid.rs create mode 100644 Day22/day22/src/main.rs create mode 100644 Day22/day22/src/state.rs create mode 100644 Day23/day23/Cargo.lock create mode 100644 Day23/day23/Cargo.toml create mode 100644 Day23/day23/src/attempts.txt create mode 100644 Day23/day23/src/main.rs diff --git a/.gitignore b/.gitignore index 106dd62..5b9a72e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ *.prof __pycache__ +target/ diff --git a/Day01/Day1A.s b/Day01/Day1A.s new file mode 100644 index 0000000..66c5f9e --- /dev/null +++ b/Day01/Day1A.s @@ -0,0 +1,52 @@ + extern printf + extern scanf + +;;;;;;;;;;;; +;;; TEXT ;;; +;;;;;;;;;;;; + section .text + global main +main: + sub rsp, 8 + + xor rcx, rcx + +.loop: + lea rdi, [rel format] + lea rsi, [rel operation] + lea rdx, [rel count] + xor ax, ax + call scanf + + xor rax, rax + mov eax, [rel count] + cmp byte [rel operation], '+' + je .add + neg rax +.add: + add rcx, rax + + push rcx + lea rdi, [rel message] + mov rsi, rcx + xor ax, ax + call printf + pop rcx + + jmp .loop + + + add rsp, 8 + ret + +;;;;;;;;;;;; +;;; DATA ;;; +;;;;;;;;;;;; + section .data +format: db "%c%d",0 +message: db "%d",10,0 + + section .bss +buffer: resb 64 +operation: resb 1 +count: resb 4 diff --git a/Day19/Day19A.c b/Day19/Day19A.c new file mode 100644 index 0000000..1a92b46 --- /dev/null +++ b/Day19/Day19A.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + uint8_t opcode; + uint32_t a, b, c; +} Instruction; + +Instruction instructions[1024]; + +enum { + ADDR, ADDI, MULR, MULI, BANR, BANI, BORR, BORI, + SETR, SETI, GTIR, GTRI, GTRR, EQIR, EQRI, EQRR, + IP, +}; + +const char *names[] = { + "addr", "addi", "mulr", "muli", "banr", "bani", "borr", "bori", + "setr", "seti", "gtir", "gtri", "gtrr", "eqir", "eqri", "eqrr", + "#ip", +}; + +int instruction_read(Instruction *i, FILE *file) { + char mnemonic[64] = {0}; + size_t count = fscanf(file, "%s %u %u %u", mnemonic, &i->a, &i->b, &i->c); + if (count != 4 && count != 2) + return 0; + + for (int opcode = 0; opcode <= IP; opcode++) { + if (strcmp(names[opcode], mnemonic) == 0) { + i->opcode = opcode; + return 1; + } + } + printf("Unknown mnemonic '%s'\n", mnemonic); + return 0; +} + +void instruction_execute(const Instruction *i, uint32_t *r, uint32_t *ip, uint32_t *ip_r) { + if (*ip_r != -1) + r[*ip_r] = *ip; + + switch (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; + + case IP: *ip_r = i->a; *ip -= 1; break; + + default: assert(0); + } + + if (*ip_r != -1) + *ip = r[*ip_r]; + + *ip += 1; +} + +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"); + + uint32_t registers[6] = {0}; + uint32_t ip_reg = -1; + size_t instr_count = 0; + for (instr_count = 0; instr_count < 1024 && instruction_read(&instructions[instr_count], file); instr_count++) { +#ifdef DEBUG + printf("opcode: %2d (%s), a: %d, b: %d, c: %d\n", instructions[instr_count].opcode, + names[instructions[instr_count].opcode], instructions[instr_count].a, instructions[instr_count].b, + instructions[instr_count].c); +#endif + } + + for (uint32_t i = 0; i < instr_count;) { + Instruction instruction = instructions[i]; +#ifdef DEBUG + const char *name = names[instruction.opcode]; + printf("opcode: %2d (%s), a: %d, b: %d, c: %d\n", instruction.opcode, + name, instruction.a, instruction.b, instruction.c); +#endif + + instruction_execute(&instruction, registers, &i, &ip_reg); + +#ifdef DEBUG + printf("registers: %d %d %d %d %d %d\n", registers[0], registers[1], + registers[2], registers[3], registers[4], registers[5]); +#endif + } + + fclose(file); + + printf("%d\n", registers[0]); + return EXIT_SUCCESS; +} diff --git a/Day20/Day20.py b/Day20/Day20.py new file mode 100644 index 0000000..4106228 --- /dev/null +++ b/Day20/Day20.py @@ -0,0 +1,130 @@ +from collections import deque +from pprint import pprint + +NORTH = 1 +EAST = 2 +SOUTH = 4 +WEST = 8 + +ROOT = 0 +IN_PARENS = 1 +SKIPPING = 2 + +start_pos = 200, 200 +grid = [[0] * start_pos[0] * 2 for _ in range(start_pos[1] * 2)] + +min_x, max_x = start_pos +min_y, max_y = start_pos + + +def print_grid(x_=None, y_=None): + for y in range(min_y, max_y + 1): + for x in range(min_x, max_x + 1): + print(end="@") + print(end="-" if grid[y][x] & NORTH else "@") + print("#") + + for x in range(min_x, max_x + 1): + print(end="|" if grid[y][x] & WEST else "@") + if (x, y) == start_pos: + print(end="X") + elif x == x_ and y == y_: + print(end=".") + else: + print(end=" ") + print("@") + print("@" * ((max_x - min_x) * 2 + 3)) + + +def parse_input(inp): + global min_x, max_x, min_y, max_y + + # print(inp, end="\n\n") + # print("\033[2J\033[;H") + stack = [(0, start_pos[0], start_pos[1])] + while stack: + # print("===================================") + # print(stack, end=" -> ") + start, x, y = stack.pop() + # print(start) + + depth = 0 + skip_to_end = False + for i in range(start, len(inp)): + c = inp[i] + + if depth == 0 and not skip_to_end: + # print(f"{depth} {inp[:i]}\033[4;3{depth+1}m{inp[i]}\033[0m{inp[i+1:]} ({x:>2},{y:>2}) -> ", end="") + + if c == "N": grid[y][x] |= NORTH; y -= 1; grid[y][x] |= SOUTH + elif c == "E": grid[y][x] |= EAST; x += 1; grid[y][x] |= WEST + elif c == "S": grid[y][x] |= SOUTH; y += 1; grid[y][x] |= NORTH + elif c == "W": grid[y][x] |= WEST; x -= 1; grid[y][x] |= EAST + + if x < min_x: min_x = x + if x > max_x: max_x = x + if y < min_y: min_y = y + if y > max_y: max_y = y + + # print(f"({x:>2},{y:>2})") + # print_grid(x, y) + # print() + + + if c == "(" and not skip_to_end: + if depth == 0: + stack.append((i + 1, x, y)) + depth += 1 + elif c == "|" and not skip_to_end: + if depth == 1: + stack.append((i + 1, x, y)) + elif depth == 0: + skip_to_end = True + elif c == ")": + depth -= 1 + if depth == -1: + skip_to_end = False + break + + # print() + # print("\033[0;0H", end="") + # print_grid() + + + +def bfs(): + pos = start_pos + + queue = deque([(pos, 0)]) + seen = set() + lengths = [] + max_len = 0 + long_paths = 0 + + while queue: + (x, y), depth = queue.popleft() + if (x, y) in seen: + continue + seen.add((x, y)) + + if depth > max_len: + max_len = depth + if depth >= 1000: + long_paths += 1 + + if grid[y][x] & WEST: queue.append(((x - 1, y), depth + 1)) + if grid[y][x] & EAST: queue.append(((x + 1, y), depth + 1)) + if grid[y][x] & NORTH: queue.append(((x, y - 1), depth + 1)) + if grid[y][x] & SOUTH: queue.append(((x, y + 1), depth + 1)) + + print("part 1:", max_len) + print("part 2:", long_paths) + + +def main(): + inp = input() + parse_input(inp[1:-1]) + bfs() + + +main() diff --git a/Day21/Day21A.cpp b/Day21/Day21A.cpp new file mode 100644 index 0000000..fdaef1d --- /dev/null +++ b/Day21/Day21A.cpp @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + uint8_t opcode; + int32_t a, b, c; +} Instruction; + +Instruction instructions[1024]; + +enum { + ADDR, ADDI, MULR, MULI, BANR, BANI, BORR, BORI, + SETR, SETI, GTIR, GTRI, GTRR, EQIR, EQRI, EQRR, + IP, +}; + +const char *names[] = { + "addr", "addi", "mulr", "muli", "banr", "bani", "borr", "bori", + "setr", "seti", "gtir", "gtri", "gtrr", "eqir", "eqri", "eqrr", + "#ip", +}; + +int instruction_read(Instruction *i, FILE *file) { + char mnemonic[64] = {0}; + size_t count = fscanf(file, "%s %u %u %u", mnemonic, &i->a, &i->b, &i->c); + if (count != 4 && count != 2) + return 0; + + for (int opcode = 0; opcode <= IP; opcode++) { + if (strcmp(names[opcode], mnemonic) == 0) { + i->opcode = opcode; + return 1; + } + } + printf("Unknown mnemonic '%s'\n", mnemonic); + return 0; +} + +void instruction_execute(const Instruction *i, int32_t *r, int32_t *ip, int32_t *ip_r) { + if (*ip_r != -1) + r[*ip_r] = *ip; + + switch (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; + + case IP: *ip_r = i->a; *ip -= 1; break; + + default: assert(0); + } + + if (*ip_r != -1) + *ip = r[*ip_r]; + + *ip += 1; +} + +int main(int argc, char **argv) { + setlocale(LC_NUMERIC, ""); + if (argc != 2) { + printf("Usage: %s [input]\n", argv[0]); + return EXIT_FAILURE; + } + + FILE *file = fopen(argv[1], "r"); + + int32_t jump_regs[6] = {-1}; + int32_t registers[6] = {INT_MAX, 0}; + int32_t ip_reg = -1; + size_t instr_count = 0; + for (instr_count = 0; instr_count < 1024 && instruction_read(&instructions[instr_count], file); instr_count++) { + if (instructions[instr_count].opcode == IP) { + ip_reg = instructions[instr_count].a; + instr_count -= 1; + continue; + } + + printf("[%2zu] %s %d %d %d\n", instr_count, names[instructions[instr_count].opcode], + instructions[instr_count].a, instructions[instr_count].b, + instructions[instr_count].c); + } + printf("\n"); + + std::unordered_set breakpoints; + bool stepping = true; + + uint64_t instructions_executed = 0; + for (int32_t i = 0; i < instr_count;) { + Instruction instruction = instructions[i]; + + if (breakpoints.find(i) != breakpoints.end()) + stepping = true; + + if (stepping) { + const char *name = names[instruction.opcode]; + printf("[%2d] \033[33m%s %d %d %d\033[0m\r", i, + name, instruction.a, instruction.b, instruction.c); + } + instruction_execute(&instruction, registers, &i, &ip_reg); + if (stepping) { + printf("\033[30C[%d, %d, %d, %d, %d, %d]\n", registers[0], registers[1], + registers[2], registers[3], registers[4], registers[5]); + + read: + printf("> "); + char c; + scanf(" %c", &c); + switch (c) { + case 0: + printf("q\n"); + [[fallthrough]]; + + case 'q': + i = instr_count; + break; + + case 'c': + stepping = false; + break; + + case 'b': + breakpoints.insert(i); + break; + + case 's': + break; + + case '\n': + goto read; + + default: + printf("Unknown command '%c'\n", c); + goto read; + } + } + + instructions_executed += 1; + if (!stepping && instructions_executed % (1 << 20) == 0) { + printf("%'lu %d ", instructions_executed, i); + printf("[%'d, %'d, %'d, %'d, %'d, %'d]\033[K\r", registers[0], registers[1], + registers[2], registers[3], registers[4], registers[5]); + } + } + printf("instructions: %lu\n", instructions_executed); + + fclose(file); + + printf("%d\n", registers[0]); + return EXIT_SUCCESS; +} diff --git a/Day22/day22/Cargo.lock b/Day22/day22/Cargo.lock new file mode 100644 index 0000000..bd8ddfa --- /dev/null +++ b/Day22/day22/Cargo.lock @@ -0,0 +1,4 @@ +[[package]] +name = "Day22" +version = "0.1.0" + diff --git a/Day22/day22/Cargo.toml b/Day22/day22/Cargo.toml new file mode 100644 index 0000000..9de1b5d --- /dev/null +++ b/Day22/day22/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "Day22" +version = "0.1.0" +authors = ["Sijmen Schoon "] +edition = "2018" + +[dependencies] diff --git a/Day22/day22/src/grid.rs b/Day22/day22/src/grid.rs new file mode 100644 index 0000000..13cbd1c --- /dev/null +++ b/Day22/day22/src/grid.rs @@ -0,0 +1,123 @@ +use super::{Point, State, Tool}; +use std::collections::{BinaryHeap, HashMap}; + +const ROCKY: u16 = 0; +const WET: u16 = 1; +const NARROW: u16 = 2; + +fn neighbors((x, y): Point) -> Vec { + vec![(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)] +} + +fn distance((ax, ay): Point, (bx, by): Point) -> usize { + ((ax - bx).abs() + (ay - by).abs()) as usize +} + +fn valid_items(region: u16) -> Vec { + match region { + ROCKY => vec![Tool::Torch, Tool::Climbing], + WET => vec![Tool::None, Tool::Climbing], + NARROW => vec![Tool::None, Tool::Torch], + _ => panic!(), + } +} + +pub struct Grid { + pub grid: Vec>>, + pub depth: usize, + pub target: Point, +} + +impl Grid { + pub fn new(depth: usize, target: Point) -> Self { + Self { + grid: vec![vec![None; depth]; depth], + depth, + target, + } + } + + pub fn erosion(&mut self, pos: Point) -> u16 { + let m = 20183; + let value = match pos { + (0, 0) => self.depth, + _ if pos == self.target => self.depth, + (x, 0) => (x as usize * 16807 + self.depth) % m, + (0, y) => (y as usize * 48271 + self.depth) % m, + (x, y) => { + if let Some(value) = self.grid[y as usize][x as usize] { + value as usize + } else { + let value = usize::from(self.erosion((x - 1, y))) + * usize::from(self.erosion((x, y - 1))); + let value_ = (value + self.depth) % m; + self.grid[y as usize][x as usize] = Some(value_ as u16); + value_ + } + } + } as u16; + + value + } + + fn valid(&self, (x, y): Point) -> bool { + x >= 0 && y >= 0 && x < self.depth as isize && y < self.depth as isize + } + + pub fn path_find(&mut self) -> usize { + let start = (0, 0); + let first = State::new(0, start, distance(start, self.target), Tool::Torch); + + let mut queue = BinaryHeap::new(); + queue.push(first); + + let mut visited = HashMap::new(); + while !queue.is_empty() { + let current = queue.pop().unwrap(); + if visited.contains_key(¤t.key()) && current.depth >= visited[¤t.key()] { + continue; + } + visited.insert(current.key(), current.depth); + + if current.position == self.target && current.tool == Tool::Torch { + return current.depth; + } + + let tools = valid_items(self.erosion(current.position) % 3); + for tool in tools { + if tool != current.tool { + queue.push(State::new( + current.depth + 7, + current.position, + current.distance, + tool, + )); + } + } + + for new_pos in neighbors(current.position) { + if !self.valid(new_pos) { + continue; + } + + let region = self.erosion(new_pos) % 3; + if valid_items(region).contains(¤t.tool) { + let dist = distance(new_pos, self.target); + queue.push(State::new(current.depth + 1, new_pos, dist, current.tool)); + } + } + } + + panic!("did not find path") + } + + pub fn risk(&mut self) -> u16 { + let mut risk = 0; + for y in 0..=self.target.1 { + for x in 0..=self.target.0 { + risk += self.erosion((x, y)) % 3; + } + } + risk + } +} diff --git a/Day22/day22/src/main.rs b/Day22/day22/src/main.rs new file mode 100644 index 0000000..1880982 --- /dev/null +++ b/Day22/day22/src/main.rs @@ -0,0 +1,31 @@ +mod grid; +mod state; +use self::grid::Grid; +use self::state::State; + +use std::env; + +pub type Point = (isize, isize); + +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub enum Tool { + None, + Torch, + Climbing, +} + +fn main() { + let args: Vec<_> = env::args().collect(); + if args.len() != 4 { + println!("Usage: {} [depth] [target_x] [target_y]", args[0]); + return; + } + + let depth = args[1].parse().unwrap(); + let target: Point = (args[2].parse().unwrap(), args[3].parse().unwrap()); + + let mut grid = Grid::new(depth, target); + + println!("part 1: {}", grid.risk()); + println!("part 2: {}", grid.path_find()); +} diff --git a/Day22/day22/src/state.rs b/Day22/day22/src/state.rs new file mode 100644 index 0000000..c366e7c --- /dev/null +++ b/Day22/day22/src/state.rs @@ -0,0 +1,41 @@ +use super::{Point, Tool}; +use std::cmp::Ordering; + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct State { + pub depth: usize, + pub position: Point, + pub distance: usize, + pub tool: Tool, +} + +impl State { + pub fn new(depth: usize, position: Point, distance: usize, tool: Tool) -> Self { + Self { + depth, + position, + distance, + tool, + } + } + + pub fn priority(&self) -> usize { + self.depth + self.distance + } + + pub fn key(&self) -> (Point, Tool) { + (self.position, self.tool) + } +} + +impl Ord for State { + fn cmp(&self, other: &State) -> Ordering { + other.priority().cmp(&self.priority()) + } +} + +impl PartialOrd for State { + fn partial_cmp(&self, other: &State) -> Option { + Some(self.cmp(other)) + } +} diff --git a/Day23/day23/Cargo.lock b/Day23/day23/Cargo.lock new file mode 100644 index 0000000..3c4f059 --- /dev/null +++ b/Day23/day23/Cargo.lock @@ -0,0 +1,95 @@ +[[package]] +name = "aho-corasick" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "day23" +version = "0.1.0" +dependencies = [ + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ucd-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "utf8-ranges" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" +"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" +"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" +"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" diff --git a/Day23/day23/Cargo.toml b/Day23/day23/Cargo.toml new file mode 100644 index 0000000..a56832d --- /dev/null +++ b/Day23/day23/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day23" +version = "0.1.0" +authors = ["Sijmen Schoon "] +edition = "2018" + +[dependencies] +regex = "^1.1.0" diff --git a/Day23/day23/src/attempts.txt b/Day23/day23/src/attempts.txt new file mode 100644 index 0000000..7175dc8 --- /dev/null +++ b/Day23/day23/src/attempts.txt @@ -0,0 +1 @@ +102330992 -> too low diff --git a/Day23/day23/src/main.rs b/Day23/day23/src/main.rs new file mode 100644 index 0000000..76b6244 --- /dev/null +++ b/Day23/day23/src/main.rs @@ -0,0 +1,217 @@ +use regex::Regex; +use std::cmp::Ordering; +use std::collections::BinaryHeap; +use std::io; +use std::io::prelude::*; + +type Point = (i64, i64, i64); + +fn distance((ax, ay, az): Point, (bx, by, bz): Point) -> i64 { + (bx - ax).abs() + (by - ay).abs() + (bz - az).abs() +} + +#[derive(Debug, Eq, PartialEq)] +struct Bot { + position: Point, + radius: i64, +} + +impl Bot { + fn in_range(&self, other: &Self) -> bool { + distance(self.position, other.position) <= self.radius + } +} + +impl Ord for Bot { + fn cmp(&self, other: &Self) -> Ordering { + self.radius.cmp(&other.radius) + } +} + +impl PartialOrd for Bot { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[derive(Debug, Eq, PartialEq)] +struct Node { + min: Point, + max: Point, + count: i64, +} + +impl Node { + fn new(min: Point, max: Point, count: i64) -> Self { + Self { min, max, count } + } + + fn score(&self) -> (i64, i128, i64) { + let area = (self.max.0 - self.min.0) as i128 + * (self.max.1 - self.min.1) as i128 + * (self.max.2 - self.min.2) as i128; + let dist = distance((0, 0, 0), self.min); + + (self.count, -area, -dist) + } +} + +impl Ord for Node { + fn cmp(&self, other: &Self) -> Ordering { + self.score().cmp(&other.score()) + } +} + +impl PartialOrd for Node { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +fn sq_dist(p: &Point, min: &Point, max: &Point) -> i64 { + let mut dist = 0; + if p.0 < min.0 { + dist += (min.0 - p.0) * (min.0 - p.0); + } else if p.0 > max.0 { + dist += (p.0 - max.0) * (p.0 - max.0); + } + if p.1 < min.1 { + dist += (min.1 - p.1) * (min.1 - p.1); + } else if p.1 > max.1 { + dist += (p.1 - max.1) * (p.1 - max.1); + } + if p.2 < min.2 { + dist += (min.2 - p.2) * (min.2 - p.2); + } else if p.2 > max.2 { + dist += (p.2 - max.2) * (p.2 - max.2); + } + dist +} + +fn intersect(b: &Bot, min: &Point, max: &Point) -> bool { + let dist = sq_dist(&b.position, min, max); + dist <= b.radius * b.radius +} + +fn idk(bots: &Vec, min: &Point, max: &Point) -> i64 { + let mut count = 0; + let mut min_dist = std::i64::MAX; + for bot in bots { + if intersect(bot, min, max) { + count += 1; + + let dist = distance((0, 0, 0), bot.position); + if dist < min_dist { + min_dist = dist; + } + } + } + + count +} + +fn main() { + let re = Regex::new(r"^pos=<([-\d]+),([-\d]+),([-\d]+)>, r=(\d+)$").unwrap(); + let mut bots = Vec::new(); + + let mut min = (std::i64::MAX, std::i64::MAX, std::i64::MAX); + let mut max = (std::i64::MIN, std::i64::MIN, std::i64::MIN); + for line in io::stdin().lock().lines() { + let line = line.unwrap(); + if let Some(input) = re.captures(&line) { + let bot = Bot { + position: ( + input[1].parse().unwrap(), + input[2].parse().unwrap(), + input[3].parse().unwrap(), + ), + radius: input[4].parse().unwrap(), + }; + + if bot.position.0 < min.0 { + min.0 = bot.position.0; + } + if bot.position.1 < min.1 { + min.1 = bot.position.1; + } + if bot.position.2 < min.2 { + min.2 = bot.position.2; + } + + if bot.position.0 > max.0 { + max.0 = bot.position.0; + } + if bot.position.1 > max.1 { + max.1 = bot.position.1; + } + if bot.position.2 > max.2 { + max.2 = bot.position.2; + } + + bots.push(bot); + } + } + + let max_bot = bots.iter().max().unwrap(); + let count = bots.iter().filter(|&bot| max_bot.in_range(bot)).count(); + println!("part 1: {}", count); + + let mut queue = BinaryHeap::new(); + queue.push(Node::new(min, max, bots.len() as i64)); + + while !queue.is_empty() { + let node = queue.pop().unwrap(); + println!("{:?}", node); + if node.max.0 - node.min.0 == 1 + && node.max.1 - node.min.1 == 1 + && node.max.2 - node.min.2 == 1 + { + min = node.min; + break; + } + + let half = ( + ((node.max.0 + node.min.0) as f64 / 2.0).ceil() as i64, + ((node.max.1 + node.min.1) as f64 / 2.0).ceil() as i64, + ((node.max.2 + node.min.2) as f64 / 2.0).ceil() as i64, + ); + + for (min, max) in vec![ + ( + (node.min.0, node.min.1, node.min.2), + (half.0, half.1, half.2), + ), + ( + (node.min.0, node.min.1, half.2), + (half.0, half.1, node.max.2), + ), + ( + (node.min.0, half.1, node.min.2), + (half.0, node.max.1, half.2), + ), + ( + (node.min.0, half.1, half.2), + (half.0, node.max.1, node.max.2), + ), + ( + (half.0, node.min.1, node.min.2), + (node.max.0, half.1, half.2), + ), + ( + (half.0, node.min.1, half.2), + (node.max.0, half.1, node.max.2), + ), + ( + (half.0, half.1, node.min.2), + (node.max.0, node.max.1, half.2), + ), + ( + (half.0, half.1, half.2), + (node.max.0, node.max.1, node.max.2), + ), + ] { + queue.push(Node::new(min, max, idk(&bots, &min, &max))); + } + } + println!("part 2: {}", distance((0, 0, 0), min)); +}