diff --git a/Cargo.toml b/Cargo.toml index 878bce0..77991bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" default-run = "main" +[profile.release] +debug = true + [dependencies] fxhash = "0.2.1" rayon = "1.10.0" diff --git a/src/day15/2.rs b/src/day15/2.rs index e899296..7f0b7e5 100644 --- a/src/day15/2.rs +++ b/src/day15/2.rs @@ -267,7 +267,6 @@ impl Bitmap { self.inner[index] &= !(1 << bit_offset); self.left_bracket[index] &= !(1 << bit_offset); } - } fn main() { diff --git a/src/day17/1.rs b/src/day17/1.rs new file mode 100644 index 0000000..28d54a4 --- /dev/null +++ b/src/day17/1.rs @@ -0,0 +1,125 @@ + +#![feature(test)] +extern crate test; + +const INPUTS: [&str; 7] = [ +"Register A: 0 +Register B: 0 +Register C: 9 + +Program: 2,6", +"Register A: 10 +Register B: 0 +Register C: 0 + +Program: 5,0,5,1,5,4", + + "Register A: 729 +Register B: 0 +Register C: 0 + +Program: 0,1,5,4,3,0", + + + "Register A: 2024 +Register B: 0 +Register C: 0 + +Program: 0,1,5,4,3,0", + +"Register A: 0 +Register B: 29 +Register C: 0 + +Program: 1,7", + + +"Register A: 0 +Register B: 2024 +Register C: 43690 + +Program: 4,0", + + include_str!("./input.txt"), +]; + +pub fn run(input: &str) -> String { + let (p, q) = input.split_once("\n\n").unwrap(); + let (_, q) = q.split_once(' ').unwrap(); + let program = q.trim().split(',').map(|x| x.parse::().unwrap()).collect::>(); + + let mut p = p.lines(); + let mut a = p.next().unwrap().chars().filter(|x| x.is_digit(10)).collect::().parse::().unwrap(); + let mut b = p.next().unwrap().chars().filter(|x| x.is_digit(10)).collect::().parse::().unwrap(); + let mut c = p.next().unwrap().chars().filter(|x| x.is_digit(10)).collect::().parse::().unwrap(); + + let mut out = vec![]; + let mut ip = 0; + + while ip < program.len() { + let opcode = program[ip]; + let operand = program[ip+1]; + ip += 2; + + let combo = |op: u32| { + match op { + 0..=3 => op, + 4 => a, + 5 => b, + 6 => c, + 7 => unreachable!(), + _ => unreachable!(), + } + }; + + match opcode { + 0 => { + let denom = 2u32.pow(combo(operand)); + a /= denom; + } + 1 => { + b ^= operand; + } + 2 => { + b = combo(operand) % 8; + } + 3 => { + if a != 0 { + ip = operand as usize; + } + } + 4 => { + b = b ^ c; + } + 5 => { + out.push(combo(operand)%8); + } + 6 => { + let denom = 2u32.pow(combo(operand)); + b = a / denom; + } + 7 => { + let denom = 2u32.pow(combo(operand)); + c = a / denom; + } + + _ => unreachable!() + } + } + + out.into_iter().map(|x| x.to_string()).collect::>().join(",") +} + +fn main() { + for input in INPUTS.iter() { + println!("answer = {}", run(input)); + } +} + +#[bench] +fn part1(b: &mut test::Bencher) { + b.iter(|| { + let v = run(INPUTS[6]); + test::black_box(v); + }); +} diff --git a/src/day17/2.rs b/src/day17/2.rs new file mode 100644 index 0000000..e56acc1 --- /dev/null +++ b/src/day17/2.rs @@ -0,0 +1,131 @@ + +#![feature(test)] +extern crate test; + +const INPUTS: [&str; 1] = [ + include_str!("./input.txt"), +]; + +pub fn run(input: &str) -> u64 { + let (_, q) = input.split_once("\n\n").unwrap(); + let (_, q) = q.split_once(' ').unwrap(); + let program = q.trim().split(',').map(|x| x.parse::().unwrap()).collect::>(); + + let mut a; + let b = 0; + let c = 0; + let mut out = Vec::new(); + // Digits in program change in multiples of 8 so [1, 8, 64, 512, ...]. + // instead of checking every value of a, check the values where all digits will change + let mut factors = vec![0; program.len()]; + loop { + a = 0; + for (i, f) in factors.iter().enumerate() { + a += 8u64.pow(i as u32) * f; + } + + test(a, b, c, &mut out); + // general_eval(a, b, c, &program, &mut out); + if out == program { + return a; + } + + for i in (0..program.len()).rev() { + if out.len() < i { + factors[i] += 1; + break; + } + if out[i] != program[i] { + factors[i] += 1; + break; + } + } + out.clear(); + } +} + +// 2,4,1,4,7,5,4,1,1,4,5,5,0,3,3,0 +fn test(mut a: u64, mut b: u64, mut c: u64, out: &mut Vec) { + while a != 0 { + b = a % 8; + b ^= 4; + c = a / (1 << b); + b ^= c; + b ^= 4; + out.push(b%8); + a = a / (1 << 3); + } +} + +#[allow(unused)] +fn general_eval(mut a: u64, mut b: u64, mut c: u64, input: &[u64], out: &mut Vec) { + let mut ip = 0; + + while ip < input.len() { + let opcode = input[ip]; + let operand = input[ip+1]; + ip += 2; + + + let combo = |op: u64| { + match op { + 0..=3 => op, + 4 => a, + 5 => b, + 6 => c, + 7 => unreachable!(), + _ => unreachable!(), + } + }; + + match opcode { + 0 => { + let denom = 2u64.pow(combo(operand) as u32); + a /= denom; + } + 1 => { + b ^= operand; + } + 2 => { + b = combo(operand) % 8; + } + 3 => { + if a != 0 { + ip = operand as usize; + } + } + 4 => { + b = b ^ c; + } + 5 => { + out.push(combo(operand)%8); + } + 6 => { + let denom = 2u64.pow(combo(operand) as u32); + b = a / denom; + } + 7 => { + let denom = 2u64.pow(combo(operand) as u32); + c = a / denom; + } + + _ => unreachable!() + } + } + + +} + +fn main() { + for input in INPUTS.iter() { + println!("answer = {}", run(input)); + } +} + +#[bench] +fn part1(b: &mut test::Bencher) { + b.iter(|| { + let v = run(INPUTS[0]); + test::black_box(v); + }); +} diff --git a/src/day18/1.rs b/src/day18/1.rs new file mode 100644 index 0000000..9e27cc4 --- /dev/null +++ b/src/day18/1.rs @@ -0,0 +1,114 @@ +#![feature(slice_split_once)] +#![feature(test)] +extern crate test; + +use std::collections::VecDeque; + +const INPUTS: [&str; 1] = [ + include_str!("./input.txt"), +]; + +pub fn run(input: &str) -> i64 { + let input = input.as_bytes(); + let parse = |x: &[u8]| { + (match x.len() { + 1 => x[0] - b'0', + 2 => (x[0] - b'0') * 10 + x[1] - b'0', + _ => 0 + }) as i64 + }; + let mut visited = Bitmap::new(); + let mut obstacles = Bitmap::new(); + + input.split(|&x| x == b'\n').take(1024).for_each(|x| { + let (a,b) = x.split_once(|&x| x == b',').unwrap(); + let a = parse(a); + let b = parse(b); + + obstacles.set(a,b); + }); + + let best = bfs(&obstacles, &mut visited, (0,0), (70,70)); + + best +} + +fn bfs(obstacles: &Bitmap, visited: &mut Bitmap, (x,y): (i64, i64), (ex, ey): (i64, i64)) -> i64 { + let mut q = VecDeque::new(); + + q.push_back((x,y,0)); + + while let Some((x,y,steps)) = q.pop_front() { + if x == ex && y == ey { + return steps; + } + if visited.get(x,y) { + continue; + } + + visited.set(x,y); + + for (i,j) in [(0,1), (1,0), (0,-1), (-1,0)].iter() { + let x = i + x; + let y = j + y; + + if x < 0 || y < 0 || x > ex || y > ey { + continue; + } + if obstacles.get(x,y) || visited.get(x,y) { + continue; + } + + q.push_back((x,y,steps+1)); + } + } + + -1 +} + +const WORD_SIZE: usize = 64; + +struct Bitmap { + inner: [u64; (71 * 71) / WORD_SIZE + 1], + size: i64, +} + +impl Bitmap { + fn new() -> Self { + Self { inner: [0; (71 * 71) / WORD_SIZE + 1], size: 71 } + } + + const fn get(&self, x: i64, y: i64) -> bool { + let offset = (x * self.size + y) as usize; + let index = offset / WORD_SIZE; + let bit_offset = offset % WORD_SIZE; + + unsafe { + (*self.inner.as_ptr().add(index) >> bit_offset & 1) == 1 + } + } + + fn set(&mut self, x: i64, y: i64) { + let offset = (x * self.size + y) as usize; + let index = offset / WORD_SIZE; + let bit_offset = offset % WORD_SIZE; + + unsafe { + *self.inner.get_unchecked_mut(index) |= 1 << bit_offset; + } + } +} + +fn main() { + for input in INPUTS.iter() { + println!("answer = {:?}", run(input)); + } +} + +#[bench] +fn part1(b: &mut test::Bencher) { + b.iter(|| { + let v = run(INPUTS[0]); + test::black_box(v); + }); +} diff --git a/src/day18/2.rs b/src/day18/2.rs new file mode 100644 index 0000000..798edfd --- /dev/null +++ b/src/day18/2.rs @@ -0,0 +1,135 @@ +#![feature(slice_split_once)] +#![feature(test)] +extern crate test; + +const INPUTS: [&str; 1] = [ + include_str!("./input.txt"), +]; + +pub fn run(input: &str) -> (i64, i64) { + let input = input.as_bytes(); + let parse = |x: &[u8]| { + (match x.len() { + 1 => x[0] - b'0', + 2 => (x[0] - b'0') * 10 + x[1] - b'0', + _ => 0 + }) as i64 + }; + + let bytes = input.split(|&x| x == b'\n').filter(|x| !x.is_empty()).map(|x| { + let (a,b) = x.split_once(|&x| x == b',').unwrap(); + let a = parse(a); + let b = parse(b); + + (a,b) + }).collect::>(); + + let mut visited = Bitmap::new(); + let mut obstacles = Bitmap::new(); + + let mut l = 0; + let mut r = bytes.len()-1; + + while l <= r { + let mid = (l + r) / 2; + + for b in bytes[0..mid].iter() { + obstacles.set(b.0, b.1); + } + + if dfs(&obstacles, &mut visited, (0,0), (70,70)) { + l = mid + 1; + } else { + r = mid - 1; + } + + obstacles.clear(); + visited.clear(); + } + + bytes[r] +} + +fn dfs(obstacles: &Bitmap, visited: &mut Bitmap, (x,y): (i64, i64), (ex, ey): (i64, i64)) -> bool { + let mut stack = vec![]; + + stack.push((x,y)); + + while let Some((x,y)) = stack.pop() { + if x == ex && y == ey { + return true; + } + if visited.get(x,y) { + continue; + } + + visited.set(x,y); + + for (i,j) in [(0,1), (1,0), (0,-1), (-1,0)].iter() { + let x = i + x; + let y = j + y; + + if x < 0 || y < 0 || x > ex || y > ex { + continue; + } + if obstacles.get(x,y) || visited.get(x,y) { + continue; + } + + stack.push((x,y)); + } + } + + false +} + +const WORD_SIZE: usize = 64; + +struct Bitmap { + inner: [u64; (71 * 71) / WORD_SIZE + 1], + size: i64, +} + +impl Bitmap { + fn new() -> Self { + Self { inner: [0; (71 * 71) / WORD_SIZE + 1], size: 71 } + } + + const fn get(&self, x: i64, y: i64) -> bool { + let offset = (x * self.size + y) as usize; + let index = offset / WORD_SIZE; + let bit_offset = offset % WORD_SIZE; + + unsafe { + (*self.inner.as_ptr().add(index) >> bit_offset & 1) == 1 + } + } + + fn set(&mut self, x: i64, y: i64) { + let offset = (x * self.size + y) as usize; + let index = offset / WORD_SIZE; + let bit_offset = offset % WORD_SIZE; + + unsafe { + *self.inner.get_unchecked_mut(index) |= 1 << bit_offset; + } + } + + fn clear(&mut self) { + self.inner.fill(0); + } +} + +fn main() { + for input in INPUTS.iter() { + println!("answer = {:?}", run(input)); + } +} + +#[bench] +fn part2(b: &mut test::Bencher) { + b.iter(|| { + let v = run(INPUTS[0]); + test::black_box(v); + }); +}