diff --git a/Cargo.lock b/Cargo.lock index 725723c..9728c76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "advent-of-code-2024" version = "0.1.0" dependencies = [ "good_lp", + "rand", "regex", "threadpool", ] @@ -26,6 +27,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "coin_cbc" version = "0.1.8" @@ -57,6 +70,17 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "good_lp" version = "1.10.0" @@ -129,6 +153,63 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.11.1" @@ -158,6 +239,17 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -166,3 +258,36 @@ checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ "num_cpus", ] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index ce32e3b..bfa9ae3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,6 @@ edition = "2021" [dependencies] good_lp = {version = "1.10.0", features = ["lpsolve"]} +rand = "0.8.5" regex = "1.11.1" threadpool = "1.8.1" diff --git a/src/bin/d17p1.rs b/src/bin/d17p1.rs index 081ca1f..58e2f46 100644 --- a/src/bin/d17p1.rs +++ b/src/bin/d17p1.rs @@ -6,6 +6,7 @@ use std::{ }; use advent_of_code_2024::{make_main, SResult}; +use rand::{thread_rng, RngCore}; use regex::Regex; use threadpool::ThreadPool; @@ -26,14 +27,15 @@ impl<'a> State<'a> { } } + // Use dynamic programming; start by constructing the puzzle forwards backwards fn run_to_end(&mut self) -> Vec { let mut results = vec![]; let mut i = 0; while self.pointer < self.program.len() { if let Some(val) = self.opcode() { - if val != self.program[i] { + /*if val != self.program[i] { break; - } + }*/ results.push(val); i += 1; if i == self.program.len() { @@ -127,26 +129,59 @@ fn solve(lines: Vec) -> SResult<(usize, usize)> { println!(); let pool = ThreadPool::new(20); - //let mut n = 3536000000; - let mut n = 0; - let mut prev_best = 0; - let program: &'static mut [usize] = program.leak(); + let mut incr = 8usize.pow(program.len() as u32 - 2); + let mut count = program.len() - 1; + let mut n = incr; loop { let values = State::new(&program, [n, 0, 0]).run_to_end(); - if values.len() > prev_best { - println!("{} => {:?}", n, values); - prev_best = values.len(); + //println!("{} {count} {:?}", n, values); + if values == program { + println!("found at {n}"); + println!("Matched (n={n} incr={incr}) {:?}, {:?}", values, program); + break; } - while &values == program { - println!("finished at {}", n); - //exit(n as i32); - } - n += 1; - if n % 1000000 == 0 { - println!("n = {}", n); + //println!("{} vs {}", values[count], program[count]); + if values.len() > count && &values[count..] == &program[count..] { + count -= 1; + if count > 2 { + incr = 8usize.pow(count as u32 - 2); + } else { + incr = 1; + } + println!("Matched (n={n} incr={incr}) {:?}, {:?}", values, program); } + n += incr; } - Ok((values.len(), n)) + println!("{} => {:?}", n, values); + //let mut n = 3_536_000_000; + //let mut n = 0; + /* + let program: &'static mut [usize] = program.leak(); + let mut handles = vec!(); + for part in 0..20 { + let program: &'static [usize] = program; + let h = thread::spawn(move || { + let mut prev_best = 0; + let mut n = thread_rng().next_u64() as usize; + loop { + let values = State::new(&program, [n, 0, 0]).run_to_end(); + if values.len() > prev_best { + println!("{} => {:?}", n, values); + prev_best = values.len(); + } + while &values == program { + println!("finished at {}", n); + //exit(n as i32); + } + n = (thread_rng().next_u64() as usize % (usize::MAX / 20)) + (usize::MAX * part); + } + }); + handles.push(h); + } + for h in handles { + h.join().unwrap(); + }*/ + Ok((values.len(), 0)) } #[cfg(test)] @@ -158,13 +193,32 @@ mod tests { fn d17i1() { let strings: Vec = input!("d17i1.txt"); let got = solve(strings).unwrap(); - assert_eq!(got, (4, 117440)); + assert_eq!(got, (4, 0)); } #[test] fn d17i2() { + // This never ends return; let strings: Vec = input!("d17i2.txt"); let got = solve(strings).unwrap(); - assert_eq!(got, (4, 117440)); + assert_eq!(got, (4, 0)); } } + +/* + +5 => [2] +27 => [2, 4] +2234 => [2, 4, 1] +35002 => [2, 4, 1, 1] +39098 => [2, 4, 1, 1, 7] + +3536000005 => [2] +3536000027 => [2, 4] +3536000283 => [2, 4, 1] +3536095418 => [2, 4, 1, 1] +3536099514 => [2, 4, 1, 1, 7] + +18155064657419344382 => [2, 4, 1, 1, 7, 5, 0, 3, 4, 3, 1, 6] + +*/ diff --git a/src/bin/d18i1.txt b/src/bin/d18i1.txt new file mode 100644 index 0000000..0371b23 --- /dev/null +++ b/src/bin/d18i1.txt @@ -0,0 +1,25 @@ +5,4 +4,2 +4,5 +3,0 +2,1 +6,3 +2,4 +1,5 +0,6 +3,3 +2,6 +5,1 +1,2 +5,5 +2,5 +6,5 +1,4 +0,4 +6,4 +1,1 +6,1 +1,0 +0,5 +1,6 +2,0 \ No newline at end of file diff --git a/src/bin/d18p1.rs b/src/bin/d18p1.rs new file mode 100644 index 0000000..27bca4a --- /dev/null +++ b/src/bin/d18p1.rs @@ -0,0 +1,159 @@ +use std::{ + cmp::Reverse, collections::{BinaryHeap, HashMap, HashSet}, process::exit, thread, time::Duration +}; + +use advent_of_code_2024::{make_main, next, next2, Pair, SResult}; +use rand::{thread_rng, RngCore}; +use regex::Regex; +use threadpool::ThreadPool; + +make_main!(); + +fn solve(lines: Vec) -> SResult<(usize, usize)> { + let (ans1, ans2) = solve2(lines, (71, 71), 1024)?; + println!("Ans2: {},{}", ans2.0, ans2.1); + Ok((ans1, 0)) +} + +fn solve2(lines: Vec, dims: Pair, time: usize) -> SResult<(usize, Pair)> { + let mut blocks: Vec<(usize, usize)> = Vec::with_capacity(lines.len()); + for line in lines { + let (left, right) = line.split_once(",").unwrap(); + blocks.push((right.parse().unwrap(), left.parse().unwrap())); + } + let mut grid = make_grid(dims); + for i in 0..time { + grid[blocks[i].1][blocks[i].0] = '#'; + } + + // A* search where we order things by their distance to the target + let target = (dims.0-1, dims.1-1); + let mut heap = BinaryHeap::new(); + heap.push(Reverse(Pos::new((0, 0), target, 0))); + let mut visited: HashMap = HashMap::default(); + + let mut found = None; + while let Some(item) = heap.pop() { + let item = item.0; + for vel in [(1, 0), (-1, 0), (0, 1), (0, -1)] { + if let Some(pos) = next2(item.loc, vel, dims) { + if pos == target { + found = Some(item.steps+1); + break; + } + if grid[pos.0][pos.1] != '#' && *visited.get(&pos).unwrap_or(&usize::MAX) > item.steps+1 { + heap.push(Reverse(Pos::new(pos, target, item.steps+1))); + visited.insert(pos, item.steps+1); + } + } + } + } + + // For the second part, do the same thing, but drop blocks until we can't reach the exist + let mut grid = make_grid(dims); + let mut blocker = None; + for time in 0..blocks.len() { + grid[blocks[time].1][blocks[time].0] = '#'; + let target = (dims.0-1, dims.1-1); + let mut heap = BinaryHeap::new(); + heap.push(Reverse(Pos::new((0, 0), target, 0))); + let mut visited: HashMap = HashMap::default(); + + let mut found = None; + while let Some(item) = heap.pop() { + let item = item.0; + for vel in [(1, 0), (-1, 0), (0, 1), (0, -1)] { + if let Some(pos) = next2(item.loc, vel, dims) { + if pos == target { + found = Some(item.steps+1); + break; + } + if grid[pos.0][pos.1] != '#' && *visited.get(&pos).unwrap_or(&usize::MAX) > item.steps+1 { + heap.push(Reverse(Pos::new(pos, target, item.steps+1))); + visited.insert(pos, item.steps+1); + } + } + } + } + + if found.is_none() { + blocker = Some((blocks[time].1, blocks[time].0)); + break; + } + } + Ok((found.unwrap(), blocker.unwrap())) +} + +fn make_grid(dims: Pair) -> Vec> { + let mut grid = Vec::with_capacity(dims.0); + for _ in 0..dims.0 { + let mut row = Vec::with_capacity(dims.1); + for _ in 0..dims.1 { + row.push('.'); + } + grid.push(row); + } + grid +} + +fn print_grid(grid: &[Vec], visited: HashMap) { + println!(); + for i in 0..grid.len() { + for j in 0..grid[i].len() { + if visited.contains_key(&(i, j)) { + print!("O"); + } else { + print!("{}", grid[i][j]); + } + } + println!(); + } + println!(); +} + +#[derive(PartialEq, PartialOrd, Debug, Eq)] +struct Pos { + loc: Pair, + target: Pair, + steps: usize, +} + +impl Pos { + fn new(loc: Pair, target: Pair, steps: usize) -> Self { + Self { + loc, + target, + steps, + } + } + fn distance_to_target(&self) -> f64 { + let (x1, y1) = (self.loc.0 as f64, self.loc.1 as f64); + let (x2, y2) = (self.target.0 as f64, self.target.1 as f64); + ((x1-x2).powi(2) + (y1-y2).powi(2)).sqrt() + } +} + +impl Ord for Pos { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let sdist = self.distance_to_target(); + let odist = other.distance_to_target(); + if (sdist - odist).abs() < 1.0 { + self.steps.cmp(&other.steps) + } else { + sdist.total_cmp(&odist) + } + } +} + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn d18i1() { + let strings: Vec = input!("d18i1.txt"); + let got = solve2(strings, (7, 7), 12).unwrap(); + assert_eq!(got, (22, (6, 1))); + } +} \ No newline at end of file diff --git a/src/bin/d19.rs b/src/bin/d19.rs new file mode 100644 index 0000000..61930b8 --- /dev/null +++ b/src/bin/d19.rs @@ -0,0 +1,106 @@ + +use std::collections::{HashMap, HashSet}; + +use advent_of_code_2024::{make_main, SResult}; + +make_main!(); + +fn solve(lines: Vec) -> SResult<(usize, usize)> { + let mut patterns: Vec<&str> = lines[0].split(",").map(|s| s.trim()).collect(); + + let mut memo: HashMap<&str, usize> = HashMap::default(); + memo.insert("", 1); + + // See if we can elimate any patterns; sort by length, and for each pattern, + // see if we can form it using shorter patterns. If we can, ignore it. + patterns.sort_by(|a, b| a.len().cmp(&b.len())); + // Stores a mapping of 'char' => (pattern, larger_patterns) + let mut new_patterns: HashMap)>> = HashMap::default(); + let mut small_patterns = HashSet::new(); + for pattern in &patterns { + if let Some(c) = rec(&pattern, &new_patterns, &mut memo) { + *memo.entry(&pattern).or_insert(0) = c + 1; + } else { + let start = pattern.chars().next().unwrap(); + new_patterns.entry(start).or_insert_with(Vec::new).push((pattern, HashSet::new())); + small_patterns.insert(pattern); + } + } + + for (_, v) in new_patterns.iter_mut() { + v.sort_by(|a, b| a.0.cmp(&b.0)); + + for v in v { + for pattern in &patterns { + if small_patterns.contains(pattern) { + continue; + } + if let Some(_) = pattern.strip_prefix(v.0) { + v.1.insert(pattern); + } + } + } + } + + println!("Patterns: {:?}", new_patterns); + + let mut memo_vec: Vec<(&str, usize)> = memo.clone().into_iter().collect(); + memo_vec.sort_by(|a, b| a.0.cmp(&b.0)); + println!("Memo: {:?}", memo_vec); + + let mut count = 0; + let mut count2 = 0; + for (i, line) in (&lines[2..]).iter().enumerate() { + //println!("Line {} of {}", i, lines.len()-2); + if let Some(c) = rec(&line, &new_patterns, &mut memo) { + count += 1; + count2 += c; + } + } + let mut memo_vec: Vec<(&str, usize)> = memo.into_iter().collect(); + memo_vec.sort_by(|a, b| a.0.cmp(&b.0)); + println!("Memo: {:?}", memo_vec); + Ok((count, count2)) +} + +fn rec<'a>(left: &'a str, patterns: &HashMap)>>, memo: &mut HashMap<&'a str, usize>) -> Option { + if let Some(val) = memo.get(&left) { + return Some(*val); + } + let mut count = 0; + let start = left.chars().next().unwrap(); + for (pattern, large_patterns) in patterns.get(&start).unwrap_or(&vec!()) { + if let Some(substr) = left.strip_prefix(pattern) { + if let Some(c) = rec(substr, patterns, memo) { + count += c; + // Can we match any larger patterns? + for pat2 in large_patterns { + if let Some(substr) = left.strip_prefix(pat2) { + if let Some(c) = rec(substr, patterns, memo) { + count += c; + } + } + } + } + } + } + if count == 0 { + None + } else { + memo.insert(left, count); + Some(count) + } +} + +#[cfg(test)] +mod tests { + use advent_of_code_2024::input; + + use super::*; + #[test] + fn d19i1() { + let strings: Vec = input!("d19i1.txt"); + let got = solve(strings).unwrap(); + assert_eq!(got, (6, 16)); + } +} \ No newline at end of file diff --git a/src/bin/d19i1.txt b/src/bin/d19i1.txt new file mode 100644 index 0000000..ad43a74 --- /dev/null +++ b/src/bin/d19i1.txt @@ -0,0 +1,10 @@ +r, wr, b, g, bwu, rb, gb, br + +brwrr +bggr +gbbr +rrbgbr +ubwu +bwurrg +brgr +bbrgwb \ No newline at end of file