diff --git a/Cargo.lock b/Cargo.lock index 115827a..6228a61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "bumpalo" version = "3.19.0" @@ -97,6 +103,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "good_lp" +version = "1.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "776aa1ba88ac058e78408c17f4dbff826a51ae08ed6642f71ca0edd7fe9383f3" +dependencies = [ + "fnv", + "microlp", +] + [[package]] name = "indicatif" version = "0.18.3" @@ -126,6 +148,12 @@ version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + [[package]] name = "macros" version = "0.1.0" @@ -134,12 +162,74 @@ dependencies = [ "advent_core", ] +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "microlp" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d1790c73b93164ff65868f63164497cb32339458a9297e17e212d91df62258" +dependencies = [ + "log", + "sprs", +] + +[[package]] +name = "ndarray" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7c9125e8f6f10c9da3aad044cc918cf8784fa34de857b1aa68038eb05a50a9" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -152,6 +242,15 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -170,6 +269,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.11.0" @@ -225,6 +330,24 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "sprs" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dca58a33be2188d4edc71534f8bafa826e787cc28ca1c47f31be3423f0d6e55" +dependencies = [ + "ndarray", + "num-complex", + "num-traits", + "smallvec", +] + [[package]] name = "syn" version = "2.0.111" @@ -349,6 +472,7 @@ version = "0.1.0" dependencies = [ "advent-hack", "advent_core", + "good_lp", "macros", "rayon", "utils", diff --git a/flake.lock b/flake.lock index 5a3f88e..66c78b6 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1765003156, - "narHash": "sha256-k4YrPUhRj7Ciq385vREU57RHiDFycY5RaJwdCOmsLhU=", + "lastModified": 1765252472, + "narHash": "sha256-byMt/uMi7DJ8tRniFopDFZMO3leSjGp6GS4zWOFT+uQ=", "owner": "nix-community", "repo": "fenix", - "rev": "e8361cc010853d17740a63aae00385061ac9de51", + "rev": "8456b985f6652e3eef0632ee9992b439735c5544", "type": "github" }, "original": { @@ -54,11 +54,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764667669, - "narHash": "sha256-7WUCZfmqLAssbDqwg9cUDAXrSoXN79eEEq17qhTNM/Y=", + "lastModified": 1764950072, + "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", "owner": "nixos", "repo": "nixpkgs", - "rev": "418468ac9527e799809c900eda37cbff999199b6", + "rev": "f61125a668a320878494449750330ca58b78c557", "type": "github" }, "original": { @@ -111,11 +111,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1764925941, - "narHash": "sha256-zldc1SrUIhGMdQp+0woSqvBS51Mi8PW6JukONBQXZBY=", + "lastModified": 1765120009, + "narHash": "sha256-nG76b87rkaDzibWbnB5bYDm6a52b78A+fpm+03pqYIw=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "2cbf3587d36dfc7b701ebb744d3dd5064355d04f", + "rev": "5e3e9c4e61bba8a5e72134b9ffefbef8f531d008", "type": "github" }, "original": { diff --git a/justfile b/justfile deleted file mode 100644 index 9e6f9d8..0000000 --- a/justfile +++ /dev/null @@ -1,40 +0,0 @@ - -_default: - @just --list --unsorted --justfile {{justfile()}} - -checkall: - nix flake check - -year := `date +%Y` -day := `nu -c 'date now | format date "%_d" | str trim'` - -# Run a specific part of today's problem -p P in="" ARGS="": - cargo run {{ARGS}} -- solve {{year}}:{{day}}:{{P}} {{in}} - -# Run a specific day and part of this year -dp DP in="" ARGS="": - cargo run {{ARGS}} -- solve {{year}}:{{DP}} {{in}} - -# Run a specific year's day's part -dyp DYP in="" ARGS="": - cargo run {{ARGS}} -- solve {{DYP}} {{in}} - -# Create a new year crate -prep: - cargo run --release -- new {{year}} - -# Test today's solution against examples -test: - cargo test -p y_{{year}} --release day_{{day}}_part_ - -# Test all of this year's solutions against examples -test-all: - cargo test -p y_{{year}} --release - -# Open Editor to today's file -start: - nvim years/{{year}}/src/day_{{day}}.rs - -hakari: - cargo hakari generate diff --git a/years/2025/Cargo.toml b/years/2025/Cargo.toml index 31d4f59..d962e4a 100644 --- a/years/2025/Cargo.toml +++ b/years/2025/Cargo.toml @@ -10,3 +10,6 @@ macros = { path = "../../macros" } rayon = "1.11.0" utils = { path = "../../utils" } advent-hack = { version = "0.1", path = "../../advent-hack" } +good_lp = { version = "1.14.2", features = [ + "microlp", +], default-features = false } diff --git a/years/2025/src/day_10.rs b/years/2025/src/day_10.rs index 68e638c..ed01672 100644 --- a/years/2025/src/day_10.rs +++ b/years/2025/src/day_10.rs @@ -1,15 +1,134 @@ +use std::collections::VecDeque; + use advent_core::{day_stuff, ex_for_day, Day}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; + +use good_lp::*; pub struct Day10; +#[derive(Debug, Clone)] +pub struct Machine { + target_state: usize, + buttons: Vec>, + target_counters: Vec, +} + impl Day for Day10 { - day_stuff!(10, "", ""); + day_stuff!(10, "7", "33", Vec); + + fn part_1(input: Self::Input) -> Option { + let ans = input + .into_par_iter() + .map(|mach| { + let buttons = mach + .buttons + .iter() + .map(|b| b.iter().map(|n| 2_usize.pow(*n as u32)).sum()) + .collect::>(); + + let mut queue: VecDeque<(usize, usize, Option)> = + VecDeque::with_capacity(30); + queue.push_front((0, 0, None)); + let mut guy = None; + while let Some((val, curr, prev)) = queue.pop_front() { + if val == mach.target_state { + guy = Some(curr); + break; + } + + for butt in buttons.iter().copied() { + if prev.is_none_or(|v| v != butt) { + queue.push_back((val ^ butt, curr + 1, Some(butt))); + } + } + } + guy.expect("womp womp") + }) + .sum::(); + + Some(ans.to_string()) + } - fn part_1(_input: Self::Input) -> Option { - None + fn part_2(input: Self::Input) -> Option { + let ans = input + .into_par_iter() + .map(|mach| { + let mut vars = variables!(); + let press_vars = mach + .buttons + .iter() + .map(|_| vars.add(variable().min(0).integer())) + .collect::>(); + + let mut problem = vars + .minimise(press_vars.iter().sum::()) + .using(microlp); + + let mut exprs = vec![0.into_expression(); mach.target_counters.len()]; + for (i, butt) in mach.buttons.iter().enumerate() { + for &x in butt { + exprs[x] += press_vars[i]; + } + } + + for (e, j) in exprs.into_iter().zip(mach.target_counters) { + problem.add_constraint(e.eq(j as f64)); + } + + let sol = problem.solve().expect("womp womp"); + + press_vars.iter().map(|&v| sol.value(v)).sum::() as usize + }) + .sum::(); + + Some(ans.to_string()) } - fn part_2(_input: Self::Input) -> Option { - None + fn parse_input(input: &str) -> Self::Input { + input + .lines() + .map(|l| { + let split = l.split(' ').collect::>(); + let diag = split + .first() + .unwrap() + .chars() + .filter(|c| *c == '.' || *c == '#') + .enumerate() + .map(|(i, d)| { + (if d == '.' { 0_usize } else { 1_usize }) * 2_usize.pow(i as u32) + }) + .sum(); + + let buttons = split + .iter() + .take(split.len() - 1) + .skip(1) + .map(|b| { + b.trim_matches('(') + .trim_matches(')') + .split(',') + .map(|n| n.parse().unwrap()) + .collect() + }) + .collect(); + + let target_counters = split + .last() + .unwrap() + .trim_matches('{') + .trim_matches('}') + .split(',') + .map(|n| n.parse().unwrap()) + .collect(); + + Machine { + target_state: diag, + buttons, + target_counters, + } + }) + .collect() } } diff --git a/years/2025/src/examples/day_10/1.txt b/years/2025/src/examples/day_10/1.txt index e69de29..dd91d7b 100644 --- a/years/2025/src/examples/day_10/1.txt +++ b/years/2025/src/examples/day_10/1.txt @@ -0,0 +1,3 @@ +[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7} +[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2} +[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5} diff --git a/years/2025/src/examples/day_10/2.txt b/years/2025/src/examples/day_10/2.txt index e69de29..dd91d7b 100644 --- a/years/2025/src/examples/day_10/2.txt +++ b/years/2025/src/examples/day_10/2.txt @@ -0,0 +1,3 @@ +[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7} +[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2} +[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}