Compare commits

..

13 Commits

Author SHA1 Message Date
48c96ce7ea day24: cheat solution used for part 2 submission 2024-12-11 09:54:10 -08:00
342f4c1883 day25: problem 1 solution - cheated with dot 2023-12-24 22:33:20 -08:00
881d0b08ad day24: problem 1 solution 2023-12-23 22:54:21 -08:00
c1eb7761e3 day23: problem 2 solution 2023-12-23 02:54:07 -08:00
98456ed98d day23: problem 1 solution 2023-12-22 22:33:34 -08:00
d6d4c0d056 gitignore 2023-12-22 20:58:14 -08:00
3bb3b3d6b6 day22: cleanup & nice output 2023-12-22 04:19:58 -08:00
dd91259fe2 day22: problem 2 solution.
no help but annoyingly i misread the hint about low/high and didn't submit my *correct* answer for over an hour while I bug hunted!
2023-12-22 03:47:01 -08:00
8495969877 day22: problem 1 solution 2023-12-22 01:32:08 -08:00
190fc92842 day21: problem 2 solution
not proud of this one either, completely cheated and it does not get
exactly the correct error (it was off by one for my input), but the
quadratic solver on Wolfram Alpha was able to do it.
2023-12-21 03:17:11 -08:00
eb6c1f42cd day 21: problem 1 solution
another unsatisfying one where i needed a visual hint to grok what i
should be doing.

a bunch of graph building stuff that wasn't needed for the part 1
solution at all.
2023-12-21 00:34:08 -08:00
512b05f624 day20: problem 2 solution - needed a hint 2023-12-20 01:48:09 -08:00
877101f9a2 day20: problem 1 solution 2023-12-20 00:13:23 -08:00
22 changed files with 3282 additions and 0 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
**/target
input

201
20/Cargo.lock generated Normal file
View File

@ -0,0 +1,201 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day20"
version = "0.1.0"
dependencies = [
"lazy-regex",
"num",
]
[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

10
20/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "day20"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy-regex = "3.1.0"
num = "0.4.1"

58
20/input Normal file
View File

@ -0,0 +1,58 @@
%hm -> cr
%qc -> nd
&dh -> rm
%ph -> zz
%ps -> kc, dt
%qb -> dt
%jl -> vt, tb
%fh -> dm, gr
broadcaster -> np, mg, vd, xr
%zz -> sq
&rm -> rx
%nd -> br
%nx -> vr, vt
%qf -> dt, dv
%np -> xm, ph
%dm -> nf, gr
%sq -> kj
%bv -> fp, xm
%br -> kt
%mg -> dz, gr
&dt -> vd, dv, dh, hm, ks, hd, kq
%ks -> qf
&qd -> rm
%xr -> vt, rn
%vr -> tg, vt
%lc -> xm
%tq -> gr, fh
%cr -> kq, dt
%vd -> dt, ks
%tb -> nx
%dz -> gr, fd
&gr -> dp, mg, fd, qn
%nf -> gr
%dv -> hm
%qj -> lc, xm
%kc -> dt, gf
%gf -> dt, qb
%vh -> xm, sv
%sr -> vt
%fp -> qg, xm
%kj -> vh
%pc -> tq, gr
%kq -> hd
%xd -> xg, gr
%tg -> sr, vt
&bb -> rm
%rn -> vt, qc
%hd -> ps
%qg -> xm, qj
&dp -> rm
%qn -> pc
%kt -> jl
%sv -> bv
&vt -> bb, nd, qc, xr, br, tb, kt
%fd -> mx
&xm -> zz, sv, sq, ph, kj, np, qd
%xg -> gr, qn
%mx -> gr, xd

362
20/src/main.rs Normal file
View File

@ -0,0 +1,362 @@
use std::collections::{BinaryHeap, HashMap, VecDeque};
use std::fmt::Display;
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
use lazy_regex::lazy_regex;
use num::integer::lcm;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
const MODULE_PATTERN: lazy_regex::Lazy<lazy_regex::Regex> = lazy_regex!("^([%&]?)([a-z]+) -> ([a-z, ]+)$");
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
enum PulseType {
Low,
High,
}
impl From<bool> for PulseType {
fn from(value: bool) -> Self {
match value {
true => PulseType::High,
false => PulseType::Low,
}
}
}
impl From<PulseType> for bool {
fn from(value: PulseType) -> Self {
match value {
PulseType::High => true,
PulseType::Low => false,
}
}
}
impl Display for PulseType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PulseType::Low => f.write_str("low"),
PulseType::High => f.write_str("high"),
}
}
}
#[derive(Debug, Clone, Copy)]
enum ModuleKind {
FlipFlop,
Conjunction,
Broadcast,
Button,
}
#[derive(Debug, Clone)]
struct Module {
name: String,
kind: Option<ModuleKind>,
inputs: Vec<String>,
outputs: Vec<String>,
on: bool,
last_input_states: Vec<PulseType>,
}
impl Hash for Module {
fn hash<H: Hasher>(&self, state: &mut H) {
// the only states that change are 'on' and 'last_input_states', the rest are constant once created
self.on.hash(state);
self.last_input_states.hash(state);
}
}
impl Module {
fn new(name: String) -> Self {
Self {
name,
kind: None,
inputs: Vec::new(),
outputs: Vec::new(),
on: false,
last_input_states: Vec::new(),
}
}
fn source_idx(&self, name: &str) -> usize {
self.inputs.iter().position(|in_name| in_name == name).unwrap()
}
fn with_kind(name: String, kind: ModuleKind) -> Self {
let mut m = Self::new(name);
m.kind = Some(kind);
m
}
fn and_destinations(mut self, destinations: &str) -> Self {
for dest in destinations.split(", ") {
self.outputs.push(dest.into());
}
self
}
fn and_source(mut self, source: String) -> Self {
self.add_source(source);
self
}
fn add_source(&mut self, source: String) {
self.inputs.push(source);
self.last_input_states.push(PulseType::Low);
}
fn reset(&mut self) {
// relevant state is on and last_input_states
self.on = false;
for input in &mut self.last_input_states {
*input = PulseType::Low;
}
}
fn state_space(&self) -> u128 {
match self.kind {
Some(ModuleKind::Broadcast) | Some(ModuleKind::Button) => 1,
Some(ModuleKind::FlipFlop) => 2,
Some(ModuleKind::Conjunction) => 1 << self.last_input_states.len(), // 2^n
None => 1
}
}
}
impl From<&str> for Module {
fn from(s: &str) -> Self {
let Some(re_result) = MODULE_PATTERN.captures(s) else {
panic!("unparseable module: {}", s);
};
let (_, [kind, name, destinations]) = re_result.extract();
if name == "broadcaster" {
return Module::with_kind(name.into(), ModuleKind::Broadcast).and_destinations(destinations);
};
match kind {
"%" => Module::with_kind(name.into(), ModuleKind::FlipFlop).and_destinations(destinations),
"&" => Module::with_kind(name.into(), ModuleKind::Conjunction).and_destinations(destinations),
_ => panic!("invalid module kind {}", kind),
}
}
}
#[derive(Debug)]
struct Job {
signal: PulseType,
targets: Vec<String>,
from: String,
priority: usize,
}
impl PartialEq for Job {
fn eq(&self, other: &Self) -> bool {
self.priority == other.priority
}
}
impl Eq for Job {}
impl PartialOrd for Job {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
other.priority.partial_cmp(&self.priority)
}
}
impl Ord for Job {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
}
#[derive(Debug)]
struct Machine {
modules: HashMap<String, Module>,
work_queue: BinaryHeap<Job>,
count_low: u64,
count_high: u64,
}
impl<T: BufRead> From<Lines<T>> for Machine {
fn from(mut lines: Lines<T>) -> Self {
let mut modules = HashMap::from([(
"button".into(),
Module::with_kind("button".into(), ModuleKind::Button).and_destinations("broadcaster"),
)]);
while let Some(Ok(line)) = lines.next() {
let mut module = Module::from(line.as_str());
for output in &module.outputs {
if let Some(output_mod) = modules.get_mut(output) {
// already exists, push to input list
output_mod.add_source(module.name.to_owned());
} else {
// forward declaration of 'unknown' modules
modules.insert(
output.clone(),
Module::new(output.clone()).and_source(module.name.clone()),
);
}
}
if let Some(existing_mod) = modules.get(&module.name) {
module.inputs = existing_mod.inputs.clone(); // might have been pre-prepared, the rest we take ours
module.last_input_states = existing_mod.last_input_states.clone();
}
modules.insert(module.name.clone(), module);
}
Machine {
modules,
work_queue: BinaryHeap::new(),
count_low: 0,
count_high: 0,
}
}
}
impl Machine {
fn press_button(&mut self) {
self.send_pulse(PulseType::Low, "broadcaster", "button", 1);
}
fn send_pulse(&mut self, signal: PulseType, target: &str, from: &str, priority: usize) {
// count pulse when it is received
match signal {
PulseType::Low => self.count_low += 1,
PulseType::High => self.count_high += 1,
};
match self.modules[target].kind {
Some(ModuleKind::Button) | Some(ModuleKind::Broadcast) => {
self.send_all_outputs(PulseType::Low, target, priority)
}
Some(ModuleKind::FlipFlop) => match signal {
PulseType::High => (),
PulseType::Low => {
let new_state = !self.modules.get_mut(target).unwrap().on;
self.modules.get_mut(target).unwrap().on = new_state;
self.send_all_outputs(new_state.into(), target, priority)
}
},
Some(ModuleKind::Conjunction) => {
let target_m = self.modules.get_mut(target).unwrap();
let source_idx = target_m.source_idx(from);
target_m.last_input_states[source_idx] = signal;
if target_m.last_input_states.iter().all(|state| *state == PulseType::High) {
self.send_all_outputs(PulseType::Low, target, priority)
} else {
self.send_all_outputs(PulseType::High, target, priority)
}
}
None => (),
}
}
fn send_all_outputs(&mut self, signal: PulseType, from: &str, priority: usize) {
self.work_queue.push(Job {
signal,
targets: self.modules[from].outputs.iter().map(|op| op.to_owned()).collect(),
from: from.to_owned(),
priority: priority + 1,
})
}
fn run(&mut self) {
while let Some(job) = self.work_queue.pop() {
for target in job.targets {
self.send_pulse(job.signal, &target, &job.from, job.priority);
}
}
}
// the number of cycles it took until the target sends a low pulse, or None if the target node sent no low pulses but the job finished
fn time_to_state(&mut self, goal:&str, state: PulseType) -> Option<u64> {
while let Some(job) = self.work_queue.pop() {
if job.from == goal && job.signal == state {
return Some(self.count_high + self.count_low);
}
for target in job.targets {
self.send_pulse(job.signal, &target, &job.from, job.priority);
}
}
None
}
fn reset(&mut self) {
for (_name, m) in &mut self.modules {
m.reset();
}
self.count_low = 0;
self.count_high = 0;
}
fn state_space(&self) -> u128 {
self.modules.values().map(|m| m.state_space()).product()
}
}
// PROBLEM 1 solution
const PROBLEM1_ITERATIONS: usize = 1000;
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut machine = Machine::from(input);
for _ in 0..PROBLEM1_ITERATIONS {
machine.press_button();
machine.run();
}
machine.count_low * machine.count_high
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u128 {
let mut machine = Machine::from(input);
println!("STATES: {}", machine.state_space());
// Find the rx module and look at its parent(s)
let rx = &machine.modules["rx"];
// my input has a single conjunction as parent, todo implement other possibilities
let con = &machine.modules[&rx.inputs[0]];
// for each input, find how long it takes for it to be High, so the conjunction sends a low to rx
let mut cycles: Vec<_> = Vec::new();
for input in con.inputs.clone() {
print!("searching distance to {}...", input);
machine.reset();
let mut button_count = 0;
loop {
machine.press_button();
button_count += 1;
if let Some(distance) = machine.time_to_state(&input, PulseType::High) {
println!("got {} pulses, {} button presses", distance, button_count);
cycles.push(button_count as u128);
break;
}
}
}
cycles.iter().fold(1, |accum, cycle| lcm(accum, *cycle))
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 11687500);
}
}

465
21/Cargo.lock generated Normal file
View File

@ -0,0 +1,465 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
dependencies = [
"cfg-if",
]
[[package]]
name = "day21"
version = "0.1.0"
dependencies = [
"indicatif",
"polyfit-rs",
"primes",
"rayon",
"test-case",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "nalgebra"
version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1"
dependencies = [
"approx",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "polyfit-rs"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab98d9704c7300e37472a6790a447eaf501d664a1889737faa53c26790d2b697"
dependencies = [
"nalgebra",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "primes"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a61082d8bceecd71a3870e9162002bb75f7ba9c7aa8b76227e887782fef9c8"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
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.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "safe_arch"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354"
dependencies = [
"bytemuck",
]
[[package]]
name = "simba"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "test-case"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.42",
]
[[package]]
name = "test-case-macros"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.42",
"test-case-core",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "wide"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

13
21/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "day21"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indicatif = "0.17.7"
polyfit-rs = "0.2.1"
primes = "0.3.0"
rayon = "1.8.0"
test-case = "3.3.1"

131
21/input Normal file
View File

@ -0,0 +1,131 @@
...................................................................................................................................
..............#...#........#...............#.#.......#....##...........#.#..#......#.....#.........#..##...........................
.#..##..##..#......#.#.#...##......###...##......#.#..#...................#.....#..........#..#................#...#..........#....
.....................#..#.......#.....##.........#.......................................#........#....#.#..........#.#......#.#...
......##...#.....#.......#...#..###..........#..........#......................#........#.............#.....#........#....#........
.................##.................#...#.........#................................#........#...........#............#........#....
..............#.#....#........##.....##..........#.............##.............#...#......#..##..........#.........#.....#...#..#...
..#....#.......#...##..#......#.........#.#..#....#.#..............#........#...#...#...............#.#.....#..#..##......#...#....
...#......##...##..#...#..........#...#..........#..............#...#..........#..#..#..#........#.#.#.#.#...........###...........
.#.#.#...#.#...##............................................#......................#...#......#......##.#.........................
.##.#.........#...#..................##...........#............#..................###...#...........#.................#....#.......
.....................#...#.#................#................#.#........................#...#...##....##....#.#...#.........#...#..
...#.......#...#.........................#.#..................#........##..........................#......##....##............#....
......................#.....................#...............#.......#.#...................#...............#...#........#.##......#.
............................#..........................#........#....#.##..#.......#.##..#...#.#..#...............#.#..#....#......
.......................................................#.#..#.#....#......................#............#....#..#.......#.........#.
.....##........#...............#.......#...............#.#..........##..#...................#................#..#...#.#....#...###.
....#.....................##................#.......#.......#...#.#...#...............#................#...............##..........
....#........#..#.......#.#.#........................#.#...........#.......#..#.........#....#.......#........#.#.#................
......###.....#..#...........#.....#..#..#........#......#.#........###.....##............#..#...........#.........................
.....#...........#...................#..#..........#..#...###.#.........#.##...............#..##..........#.........#..............
.#.........#....#............#.##..........................##.....#........#....#.............#.......#...#..............##.#......
.#..#...#.......#...#.......##.......#.#.............................#...#...#.#...........#...#.................#.........#.#.#...
...#....................#.....##..............#.................#.................#............#......#....#.......#.........#.....
.......#...#.............#.##...#.#...................#.....#..#........#................................#.............#..#...##...
...#.###..............#....#........................#......#...#.........#......#.#....................#........#...#...#.......#..
............#.#...#...........................#....###.###...#........#..#........#...................##.....###......#........#...
.....#...#................#.##..##..................#....#.........#............##...................##................#.....#.....
.#....#..............##...#..................#......#..........##.#....#......##.#...............#....##..............#....#.......
....#.#.##...........##.....#...........#.........#...#........#..#.......#......#................#.#...#....#...#.................
.......#...#...#...#..#.......#..........#..................#..#........#..#.#.....#.#.....................##......................
.....##.........#.......##...#.............#.##.#.#...#.##.#...#......#...#....#.......#..#.........#...........#.##.###.#.....#...
...................#.#.......................#.....#.#......#.....##...#.#....#..#..........#...........#..##....#...#.......#.#...
.....#..#..#.........#...#.#.........#...#.#...##...#......##........#...............#.......#................#.....#...#.#........
........#.....###.....................#.........#.........#.........#....##............#..##.............#.........#..#.......#....
.#.........#....##..##.................#...#................................#...#.....#.........#.......#............#....#......#.
..#.........#.......#..................#..#.#...#........#.#.......#...##.#........#..........#.#..........#..#.....#.........#....
..#...............#...................##.#.......#...#..........#....................##.........#.................#..#......##..#..
.#........#.......#............##...........##........#.......................#................#....................#...###......#.
........###..........#..............#.............#..#.#.#............##...............#........#...#........#...................#.
...#............................#...##.##........#......#..#...................#.#.....#...#.##...............#........#...#.......
..#...##....................#.....##......#.......#..........#......#.........#...#..............#..............#..................
.....#..#....................#....###.....#..#.............#...#....#................#.....#.....................#........#........
.....#.#...#.....#..............##............#....##.#....#.#........##...........#....#.........................#....#......##...
....#..#.....##...........#.#.#.#............#....#.....#.........#........................##.......#.#..............##...#........
...............#..............#................................#....#........#.......#.......##......#.#............#......#....#..
.#......#....##........#.............#..#.#.............#...#..#.........#.........#................#..................#...........
.........#..............#....#......#.....##......#............#....#.........#....#...................#....#.........#......#..#..
..............................#....#.#........#.#.....#..#.................#....#...#..........#............##.......#.#.#....#....
.#..................#....#..#.......#.##..............#...#.......#................#...............................................
.#..##.........................#.....###......#....#......#.....#............#.#......##......##......##....#..........##.....#....
..........................##.#.....#..........................................#.#..#....#..#..................#...........#........
..##.....#.......#....#.........#..........#....................#.........#........###........................#..........#.........
.#.#...............#..........#.......#.....#....##.#..#.##...............#...#...#.#......#.......#.......#.#.............#....##.
..................#...............##.........##..#........#..#..........#.....................#............................#.......
.....#................#...##.......##.........#.....#.#....#.#........#...#..#....#..............#..#....#....#....#...............
..##.#........#...............#.#..#...................##...#......#.#...#..#.....##.................##...#.#...................#..
...#...............#......#.....#.......#......#.........#...#......#...#..#....#..#......#.......#.......#...#.##.#...........##..
..............##..........##..........#..##...............#...........##..#..#..#.#.......#...#.............#......................
..........#.#........................#...#.#..#.#...##..#.#.#.#.......#....#.#.#..............#...#...#........###..##...........#.
......................#...#.#..##.....#....#.....###.#..#.....#..........#........#...........##...#..#...........##..#............
..................................####.#..............#.................#........#.#............##......#.#..........#.............
..................#..........#.............................#...........#..#..#......#................#.......##...##..#............
....................#...#....#.#.#.##......#................#.................#...........#....#..........#....#...##.###..........
.................#.......#...#............###.###..#.......#......#.......#..##......#.#.....#.....................#...............
.................................................................S.................................................................
.......##...........#.........###.......#....###.#.#....#.#....#....#........#.......#...#...#.#.........#...................#.....
.......#...............#............#...#..#.......##...#...#.#............#.............#......##..........#..............#.......
.......##.........#.....................##...#...#.................#...#.......#..####......#.....#..............#..##.............
........#..#.............#.#..##..............#...#.............#.....#......#...#.#.......#...............##...##.................
.........#..#...#..#.#..#..#...................##......#.....#..#...##..............#......#..........#.##...#.#.##.#..............
..............#..........#....................................#.........#.......#..#............##................#................
...........#...#.....#.........#....#..###....#...#.......#....#...............##.....#.......#.#.....#.#....#.#..#................
..#...................#........###..............#.#................#...###...##.............#.###......#...#...#.....#..........#..
.#.#............#.....#....###......................................#..........................#..................#................
................#..#...#................#.................##.................##..........#.......#...#.............#...............
.....##...........#....#.......#......#.#.....#.....#................#.....#.........#..#.................#........#........#......
.#.#.............#..#.#..............#.....#..#.#.....#..#......#.........#.....#.#......#..#.......#.#......##............#..#....
...................#............#....#...........#........#...................#.....##..............#........###...............#...
......###..........###.#.......#.................................................#..#.........#.#...#.........#.#..........#.#.....
..#.................#..#..#...#............#...#...#..##.#....##..#..................##.....#......#.........................#...#.
......................#.#.....#.#...#.......##...........#...#....#.............#.......#.............#.#.#.#......................
......#..#.#..........#....#.........#......#................#.........##.#....#.......#........#.#....................#......#....
..#....#....#...............#..........#...#.#......#...................#.........#..#.#......#..#.........................##....#.
..............#..........##......#.##...#...#.#................#...#..#.......#.#.....#........#...###..##.........#..##.........#.
........##...........................##.......#.#..................#........#.......#.#........#.........................##....#...
..#.#...........#.......................#.#.....#....#.....#...............####...#..............#...#....................#.#......
.......#...#....#.#.......#####...##.....#.#.......#....#.#........#................................#.##..........##....#..#.......
.........#.....#................##.#..................#.....................#...#................#.###.#.......###....#............
............#.#....#........#....#..................#...##.............#............#.......#.....#..#............##...............
....#.....#..........#..............#...#...#........##.....#.#....#....#........##.............................#.#.......#........
..#..#.#.......#.................#................................#......##.#..#..............#.#................#...#.#...........
.....#.....#........................#..#................#......#.......#.........###.#.#.........#.........#.....#.......#.........
.......#.......................................#.......#...............#.#..###...#.#.##.......#...........#.#.....#.##........#...
......##.........#.................................#.........#........##.#................#.#...#.............#...#..#........#....
......#.....#.#.......#...........#..#...........#.........#..#.#.......#.........##.....#....#.#...............#...#...#....#...#.
..#...#.#....#....#..............................#..#...#.......#.#.#..##..................#...............#......#..###..#.#......
.....#...#..#......#.....##...............#..........#..#.#####.......#...........#...................#.....#...#.........#..#.#...
.................#.......#..................#........#.......##...##.#....#.#.............#.............#....#....#................
...........####..#.....#....#.............#.......#...#..#.....#.............#...#....#.............#.#....#.#.............#.......
...#..#..#...#...#.#.....#.......................#....#..###.###.....#.........#....................#....#..#............#.....#...
.#.##....##...#......#.#.....#......................#.##..................#........#..#................#.#.......#.............#...
....##........#.......#..#..#............#..#..#..#...#......###..#..#..#...##...............................#.#........#......#.#.
..#..#..........#..........#..#.#.............#.....#...........#.##.......#............................#...........#...#..........
..#.#..#...#....#.....#.....#...#................#.....#.....#....#..#...#..#....#....#.....................#....#.................
.......#........#...#.............#.................#...#.#.....#.#....#..........#......................#..#............#.........
....#........#........#..............#.........................#...................#..........#.........#..#.........#.............
................#........#.........#............................#.#.............#..##.......#......#..#.................#......#.#.
..#.....#.........#...#..#.#.........................#...#...#...........#.....#...........#........#.............#.....#........#.
....#...#....#......#....#...#..#.................#...##.....#.#............................##...#...#..##.#.....................#.
...#..##...............#............................#...........#......#.#.....#............#........#.##................#.........
................#..#.............#..#..............#.#....#...#.#...#...#.#.##.............................#..#...........#........
.......#.......#....###............#.#.#.....................#.#..#....#.................##.....#.#..........##..#......#..........
.......#..........##.#.......#.##...#...............##.....#..........#................#............#...#........#........#........
...###.#.............................#.....#.#............##..#...#.....###..........#...............................#.....#....#..
....###.#...#.......#.##........#......#..##.#..........#.#...#.#....#.#..............##....#..#...#...........#...#.#..#..........
.....#....#.#..##.....#..........#..#...##..............#.......#.......#.............#..#.....#.......#.....#...##...###.......#..
........##...#...##..#.#.....#..........................#.#.#..#..#...................#....#....#..#....#..........#....##.........
...........##.....#...#........##..#........#....#..........#..#...............................................#......#.#.......#..
...#..##.........##....#...........##...#.......##.........#........#...........#........#.#.##...........#....#.....###...#.......
...#...#........................#..#...#..#........................................#......#..#...#.#....#...........#..............
...#..#.......#..#......##...................#...............#.....#.#.............#.....#.........#.#..##.....#..........#..#.....
....###....#..#.....#.#........#.##.......##...#...............#...............###.###.#...#............##.....#.................#.
....#...#.........#.##............#.....#........#............................................##.#......#..##.........#....###..#..
............#...##..#..........#........#.......#.#..........................#..#.#.......#......#.......................##.#..#...
....#......#....#....#..#.....#........#......#....#..#.#..............................#..#....#............#....#.................
..#.............#...#......#...#.....................#.##...............................##...#.......#........................#.##.
....#....##........#..............#....#.......#.....................................#........#........#...##..........#...........
....#.#.##..#..#....................#.................#.##.............#....#....#.............###...#...#..##....#.....##.........
.............#.......#...............#........#.#.....#..#.#................#.....#..##.....#....#...............#....#......#...#.
...................................................................................................................................

230
21/src/main.rs Normal file
View File

@ -0,0 +1,230 @@
use core::panic;
use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
type Position = (isize, isize);
type WrappedPosition = (usize, usize);
type Offset = (isize, isize);
#[derive(Debug, Clone)]
struct MapTile {
c: char,
}
impl MapTile {
fn new(c: char) -> Self {
Self { c }
}
}
struct GardenMap {
map: Vec<Vec<MapTile>>,
start: Position,
}
impl<T: BufRead> From<Lines<T>> for GardenMap {
fn from(lines: Lines<T>) -> Self {
let map = lines
.map(|line| line.unwrap().chars().map(|c| MapTile::new(c)).collect())
.collect();
let mut new = Self { map, start: (0, 0) };
new.find_start();
new
}
}
const ADJACENCIES: [Offset; 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
impl GardenMap {
fn wrap_pos(&self, pos: &Position) -> WrappedPosition {
let (width, height) = (self.width() as isize, self.height() as isize);
(
if pos.0 < 0 {
(pos.0 + (-pos.0 / width + 1) * width) as usize % self.width()
} else {
pos.0 as usize % self.width()
},
if pos.1 < 0 {
(pos.1 + (-pos.1 / height + 1) * height) as usize % self.height()
} else {
pos.1 as usize % self.height()
},
)
}
fn width(&self) -> usize {
self.map[0].len()
}
fn height(&self) -> usize {
self.map.len()
}
fn at(&self, pos: &Position) -> &MapTile {
let pos = self.wrap_pos(pos);
&self.map[pos.1][pos.0]
}
// return the valid 'moves' from pos
fn adjacent_to(&self, pos: &Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| self.offset_pos(pos, ofs))
.filter(|pos| self.at(pos).c != '#')
.collect()
}
fn offset_pos(&self, pos: &Position, ofs: &Offset) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
return Some((new_pos.0, new_pos.1));
}
fn find_start(&mut self) {
for (y, row) in self.map.iter().enumerate() {
for (x, tile) in row.iter().enumerate() {
if tile.c == 'S' {
self.start = (x as isize, y as isize);
return;
}
}
}
panic!("didn't find the start square!");
}
fn reachable_after(&self, from: &Position, n: usize) -> u64 {
let bar = ProgressBar::new(n as u64).with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
);
let mut visited_after: Vec<HashSet<Position>> = Vec::new();
visited_after.push(HashSet::from([*from]));
for i in 1..n+1 {
visited_after.push(
visited_after[i - 1]
.iter()
.flat_map(|last| self.adjacent_to(last))
.collect(),
);
bar.inc(1);
// if primes::is_prime(i as u64) {
// println!("count after {} steps: {}", i, visited_after[i].len());
// }
}
visited_after[n].len() as u64
}
fn reachable_count_after(&self, from: &Position, n: usize) -> u64 {
let dim = self.width() as f64;
let target_mod = (n % self.width()) as f64;
let x_values:Vec<f64> = vec![target_mod, target_mod + dim, target_mod + 2.*dim];
let y_values:Vec<f64> = x_values.iter().map(|n| self.reachable_after(from, *n as usize) as f64).collect();
let coeffs = polyfit_rs::polyfit_rs::polyfit(
&x_values,
&y_values,
2,
).unwrap();
println!("values: x: {:?} y: {:?}", x_values, y_values);
println!("coefficients: {:?}", coeffs);
let f_n= n as f64;
let result = coeffs[0] + coeffs[1] * f_n + coeffs[2] * f_n.powf(2.0);
result.round() as u64
}
fn draw_with_bounds(&self, from: &Position, to: &Position) {
for row in from.1..to.1 + 1 {
for col in from.0..to.0 + 1 {
print!("{}", self.at(&(col, row)).c);
}
println!();
}
}
}
// fn print_visited(map: &GardenMap, visited: &Vec<Vec<bool>>) {
// for (y, row) in visited.iter().enumerate() {
// for (x, cell) in row.iter().enumerate() {
// print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c });
// }
// println!();
// }
// }
// PROBLEM 1 solution
fn problem1_impl<T: BufRead>(input: Lines<T>, n: usize) -> u64 {
let map = GardenMap::from(input);
// println!("map: {:?} start: {:?}", map.map, &map.start);
// map.draw_with_bounds(
// &(-(map.width() as isize), -(map.height() as isize)),
// &(map.width() as isize * 2 + 1, map.height() as isize * 2 + 1),
// );
map.reachable_count_after(&map.start, n)
}
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, 64)
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, 26501365)
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
use test_case::test_case;
const EXAMPLE: &str = &"...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), 6), 16);
}
#[test_case(6, 16)]
#[test_case(10, 50)]
#[test_case(50, 1594)]
#[test_case(100, 6536)]
#[test_case(500, 167004)]
fn problem2_example(n: usize, expect: u64) {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), n), expect);
}
}

128
22/Cargo.lock generated Normal file
View File

@ -0,0 +1,128 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day22"
version = "0.1.0"
dependencies = [
"itertools",
"ndarray",
"petgraph",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"

11
22/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "day22"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
ndarray = "0.15.6"
petgraph = "0.6.4"

385
22/src/main.rs Normal file
View File

@ -0,0 +1,385 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::prelude::*;
use petgraph::visit::{IntoNodeReferences, Walker};
use std::collections::BinaryHeap;
use std::fmt::{Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::str::FromStr;
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
struct Coord {
x: usize,
y: usize,
z: usize,
}
impl FromStr for Coord {
type Err = Box<dyn std::error::Error>;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let (x, y, z) = value.split(',').next_tuple().unwrap();
Ok(Self {
x: x.parse()?,
y: y.parse()?,
z: z.parse()?,
})
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
struct BrickBlock {
c1: Coord,
c2: Coord,
}
impl std::fmt::Debug for BrickBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"[{},{},{} - {},{},{}]",
self.c1.x, self.c1.y, self.c1.z, self.c2.x, self.c2.y, self.c2.z
)
}
}
impl BrickBlock {
fn bottom_z_plane(&self) -> usize {
std::cmp::min(self.c1.z, self.c2.z)
}
fn top_z_plane(&self) -> usize {
std::cmp::max(self.c1.z, self.c2.z)
}
fn bottom_x_plane(&self) -> usize {
std::cmp::min(self.c1.x, self.c2.x)
}
fn top_x_plane(&self) -> usize {
std::cmp::max(self.c1.x, self.c2.x)
}
fn bottom_y_plane(&self) -> usize {
std::cmp::min(self.c1.y, self.c2.y)
}
fn top_y_plane(&self) -> usize {
std::cmp::max(self.c1.y, self.c2.y)
}
}
impl From<&str> for BrickBlock {
fn from(value: &str) -> Self {
let (c1, c2) = value.split_once('~').unwrap();
Self {
c1: c1.parse().unwrap(),
c2: c2.parse().unwrap(),
}
}
}
impl PartialOrd for BrickBlock {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(other.bottom_z_plane().cmp(&self.bottom_z_plane()))
}
}
// Note this is a reversed ordering
impl Ord for BrickBlock {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bottom_z_plane().cmp(&other.bottom_z_plane())
}
}
type BlockMap = Array3<MapTile>;
type MapTile = Option<BrickBlock>;
struct BlockPile {
blocks: Vec<BrickBlock>,
block_map: Array3<MapTile>,
bounds: (usize, usize, usize),
graph: Graph<BrickBlock, (), Directed, usize>,
}
impl BlockPile {
fn remove_block(&mut self, block: &BrickBlock) {
// loop over the (inclusive) bounding coordinates and remove them all from the map
self.block_map
.slice_mut(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.bottom_z_plane()..block.top_z_plane() + 1,
])
.fill(None);
self.blocks.remove(self.blocks.iter().position(|b| b == block).unwrap());
}
fn add_block(&mut self, block: &BrickBlock) {
// loop over the (inclusive) bounding coordinates and remove them all from the map
self.block_map
.slice_mut(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.bottom_z_plane()..block.top_z_plane() + 1,
])
.fill(Some(block.to_owned()));
self.blocks.push(block.clone());
}
fn blocks_directly_above(&self, block: &BrickBlock) -> Vec<BrickBlock> {
// find the top plane of the block
// get the array range of all squares at z_plane + 1 within the bounds of x & y
// i think there is a built in way in ndarray to do this...
let directly_above = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.top_z_plane() + 1..std::cmp::min(block.top_z_plane() + 2, self.bounds.2)
]);
directly_above.iter().filter_map(|v| v.clone()).unique().collect()
}
fn supported_by(&self, block: &BrickBlock) -> usize {
let z_plane = std::cmp::min(block.c1.z, block.c2.z);
// get the slice of tiles below us
let directly_below = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
z_plane - 1..z_plane // the layer below
]);
directly_below.iter().filter_map(|v| v.clone()).unique().count()
}
fn blocks_above_will_move_if_we_are_gone(&mut self, block: &BrickBlock) -> bool {
self.blocks_directly_above(&block)
.iter()
.map(|b| self.supported_by(b))
.any(|b| b == 1) // block we support will move if we are their only support
}
fn blocks_supported_by_at_all(&self, block: &BrickBlock) -> Vec<BrickBlock> {
self.blocks_directly_above(&block).iter().map(|b| b.clone()).collect()
}
/// Find the plane of the first block directly below us
fn supporting_plane(&self, block: &BrickBlock) -> Option<usize> {
// find the bottom plane of ourselves
let z_plane = std::cmp::min(block.c1.z, block.c2.z);
// get the slice of tiles below us
let directly_below = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
1..z_plane // don't include our own plane
]);
// find the highest top z value of those
let block_below = directly_below
.indexed_iter()
.filter_map(|(idx, v)| if let Some(val) = v { Some((idx, val)) } else { None })
.max_by_key(|(_idx, v)| v.top_z_plane());
if let Some(block) = block_below {
Some(block.1.top_z_plane())
} else {
None
}
}
fn drop_blocks(&mut self) {
// VecDeque doesn't sort and Vec isn't convenient for pushback and popfront, so eh... use a heap.
let mut blocks_to_move = BinaryHeap::from(self.blocks.clone());
while let Some(mut block) = blocks_to_move.pop() {
let z_move = match self.supporting_plane(&block) {
Some(z) if z + 1 != block.bottom_z_plane() => block.bottom_z_plane() - (z + 1),
None if block.bottom_z_plane() != 1 => block.bottom_z_plane() - 1,
_ => {
continue;
} // we are in position already with nothing below us
};
self.remove_block(&block);
block.c1.z -= z_move;
block.c2.z -= z_move;
self.add_block(&block);
blocks_to_move.push(block);
}
}
fn build_graph(&mut self) {
self.blocks.sort_by_key(|b| b.bottom_z_plane());
for b in 0..self.blocks.len() {
self.graph.add_node(self.blocks[b].clone());
}
for b in 0..self.blocks.len() {
let block = &self.blocks[b];
let depends_on_us = self.blocks_supported_by_at_all(block);
for dependent in depends_on_us {
self.graph.add_edge(
b.into(),
self.blocks.iter().position(|b| b == &dependent).unwrap().into(),
(),
);
}
}
}
}
impl<T: BufRead> From<Lines<T>> for BlockPile {
fn from(lines: Lines<T>) -> Self {
let blocks = lines.map(|line| BrickBlock::from(line.unwrap().as_str())).collect_vec();
let mut bounds = (0, 0, 0);
for block in &blocks {
bounds.0 = std::cmp::max(block.top_x_plane() + 1, bounds.0);
bounds.1 = std::cmp::max(block.top_y_plane() + 1, bounds.1);
bounds.2 = std::cmp::max(block.top_z_plane() + 1, bounds.2);
}
let mut new = BlockPile {
blocks: Vec::new(),
bounds,
block_map: BlockMap::from_elem(bounds, None),
graph: Graph::default(),
};
for block in blocks {
new.add_block(&block);
}
new
}
}
impl Display for BlockPile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for z in (0..self.bounds.2).rev() {
let z_plane = self.block_map.slice(s![.., .., z..z+1]);
if z_plane.iter().all(|b| b.is_none()) { continue };
for i in 0..self.bounds.0 {
// XZ view
let y = z_plane.slice(s![i..i + 1, .., ..]);
f.write_char(
match y.iter().enumerate().find_map(|(idx, val)| val.is_some().then(|| idx)) {
None => ' ',
Some(0) | Some(1) => '█',
Some(2) | Some(3) => '▓',
Some(4) | Some(5) => '▒',
Some(6) | Some(7) => '░',
_ => '░',
},
)?;
}
write!(f, " {} ", z)?;
for y in 0..self.bounds.1 {
let x = z_plane.slice(s![.., y..y + 1, ..]);
f.write_char(
match x.iter().enumerate().find_map(|(idx, val)| val.is_some().then(|| idx)) {
None => ' ',
Some(0) | Some(1) => '█',
Some(2) | Some(3) => '▓',
Some(4) | Some(5) => '▒',
Some(6) | Some(7) => '░',
_ => '░',
},
)?;
}
writeln!(f, " {}", z)?;
}
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut pile = BlockPile::from(input);
println!("{}", pile);
println!("dropping blocks!");
pile.drop_blocks();
println!("{}", pile);
let blocks = pile.blocks.clone();
let removable: Vec<_> = blocks
.iter()
.filter(|b| !pile.blocks_above_will_move_if_we_are_gone(*b))
.collect();
removable.len() as u64
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut pile = BlockPile::from(input);
pile.drop_blocks();
pile.build_graph();
println!("{}", pile);
let mut accum = 0;
let fixed_nodes = pile
.graph
.node_references()
.filter(|(_idx, b)| b.bottom_z_plane() == 1)
.map(|(idx, _b)| idx)
.collect_vec();
for node in pile.graph.node_indices() {
// remove links to node's neighbors
let dependents = pile.graph.neighbors(node).collect_vec();
let edges = pile.graph.edges(node).map(|v| v.id()).collect_vec();
for edge in edges {
pile.graph.remove_edge(edge);
}
// find how many nodes are reachable from z = 1 - these won't move
let safe_blocks = fixed_nodes
.iter()
.flat_map(|origin| {
Bfs::new(&pile.graph, *origin)
.iter(&pile.graph)
.map(|n| pile.graph[n].clone())
})
.unique()
.count();
// we are looking for the nodes that *will* disintegrate
accum += pile.graph.node_count() - safe_blocks;
// put the graph back how it was
for neigh in dependents {
pile.graph.add_edge(node, neigh, ());
}
}
accum as u64
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 5);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 7);
}
}

128
23/Cargo.lock generated Normal file
View File

@ -0,0 +1,128 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day23"
version = "0.1.0"
dependencies = [
"itertools",
"ndarray",
"petgraph",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"

11
23/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "day23"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
ndarray = "0.15.6"
petgraph = "0.6.4"

326
23/src/main.rs Normal file
View File

@ -0,0 +1,326 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::algo::all_simple_paths;
use petgraph::prelude::*;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Clone)]
struct Node {
c: char,
pos: Position,
}
impl Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.0, self.pos.1)
}
}
impl Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.0, self.pos.1)
}
}
type Position = (usize, usize);
#[derive(Clone)]
struct ForestMap {
map: Array2<char>,
indexes: HashMap<Position, NodeIndex>,
graph: StableDiGraph<Node, u64>,
start: Position,
end: Position,
}
const ADJACENCIES: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
fn offset_pos(map: &Array2<char>, pos: Position, ofs: (isize, isize)) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
if new_pos.0 >= 0
&& new_pos.0 < map.len_of(Axis(0)) as isize
&& new_pos.1 >= 0
&& new_pos.1 < map.len_of(Axis(1)) as isize
{
Some((new_pos.0 as usize, new_pos.1 as usize))
} else {
None
}
}
fn adjacent_to(map: &Array2<char>, pos: Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| offset_pos(map, pos, *ofs))
.collect()
}
impl<T: BufRead> From<Lines<T>> for ForestMap {
fn from(lines: Lines<T>) -> Self {
let rows = lines.map(|line| line.unwrap().chars().collect_vec()).collect_vec();
let map = Array::from_shape_vec([rows[0].len(), rows.len()], rows.into_iter().flatten().collect_vec())
.unwrap()
.reversed_axes();
let start = (map.slice(s![.., 0]).iter().position(|c| *c == '.').unwrap(), 0);
let end = (
map.slice(s![.., map.len_of(Axis(1)) - 1])
.iter()
.position(|c| *c == '.')
.unwrap(),
map.len_of(Axis(1)) - 1,
);
let mut graph = StableGraph::default();
let mut indexes = HashMap::new();
for (pos, c) in map.indexed_iter() {
if *c != '#' {
indexes.insert(pos, graph.add_node(Node { c: *c, pos }));
}
}
Self {
map,
start,
end,
graph,
indexes,
}
}
}
impl ForestMap {
fn build_graph(&mut self) {
for (pos, c) in self.map.indexed_iter() {
match c {
'#' => continue,
'.' => {
adjacent_to(&self.map, pos).iter().for_each(|adj| {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[adj], 1);
}
});
}
'^' => {
if let Some(adj) = offset_pos(&self.map, pos, (0, -1)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'>' => {
if let Some(adj) = offset_pos(&self.map, pos, (1, 0)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'v' => {
if let Some(adj) = offset_pos(&self.map, pos, (0, 1)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'<' => {
if let Some(adj) = offset_pos(&self.map, pos, (-1, 0)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
c => panic!("invalid map character {}", c),
}
}
}
fn build_graph2(&mut self) {
for (pos, c) in self.map.indexed_iter() {
match c {
'#' => continue,
'.' | '^' | '>' | 'v' | '<' => {
adjacent_to(&self.map, pos).iter().for_each(|adj| {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[adj], 1);
}
});
}
c => panic!("invalid map character {}", c),
}
}
}
// Cull nodes that don't change the topology of the graph and combine their cost
fn simplify_graph(&mut self) {
let mut idxs: Vec<_> = self
.graph
.neighbors(self.indexes[&self.start])
.map(|idx| (self.indexes[&self.start], idx))
.collect();
let mut visited = HashSet::from([self.indexes[&self.start]]);
while let Some((last_idx, cur_idx)) = idxs.pop() {
if !visited.insert(cur_idx) {
continue;
}
let our_neighbors = self.graph.neighbors(cur_idx).collect_vec();
// if we have exactly 2 neighbours, then one is where we came from, and we can shortcut this node with a
// pair of new edges A <-> C and break the existing 4 edges between them
if our_neighbors.len() == 2 {
let next_idx = our_neighbors.iter().find(|n| **n != last_idx).unwrap();
// remove the 4 existing edges
// careful of order of operations, as removing edges invalidates edge indexes
let forward_cost = self
.graph
.remove_edge(self.graph.find_edge(cur_idx, *next_idx).unwrap())
.unwrap();
let last_forward_cost = self
.graph
.remove_edge(self.graph.find_edge(last_idx, cur_idx).unwrap())
.unwrap();
let backward_cost = self
.graph
.remove_edge(self.graph.find_edge(cur_idx, last_idx).unwrap())
.unwrap();
let next_backward_cost = self
.graph
.remove_edge(self.graph.find_edge(*next_idx, cur_idx).unwrap())
.unwrap();
let new_forward_cost = forward_cost + last_forward_cost;
let new_backward_cost = backward_cost + next_backward_cost;
// add edge from last to next
self.graph.add_edge(last_idx, *next_idx, new_forward_cost);
self.graph.add_edge(*next_idx, last_idx, new_backward_cost);
self.graph.remove_node(cur_idx);
// push the next node
idxs.push((last_idx, *next_idx));
} else {
// don't do anything about nodes with > 2 edges, just push them onto the stack, if there are some
idxs.append(
&mut self
.graph
.neighbors(cur_idx)
.into_iter()
.map(|next_idx| (cur_idx, next_idx))
.collect(),
);
}
}
}
}
impl Debug for ForestMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for y in 0..self.map.len_of(Axis(1)) {
for c in self.map.index_axis(Axis(1), y) {
f.write_char(*c)?;
}
writeln!(f)?;
}
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut map = ForestMap::from(input);
map.build_graph();
// println!("{:?}", map);
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| path.len()).unwrap();
longest.len() as u64 - 1
}
fn calc_path_length(map: &ForestMap, path: &Vec<NodeIndex>) -> u64 {
path.iter().tuple_windows().fold(0, |accum, (prev, next)| {
accum + map.graph[map.graph.find_edge(*prev, *next).unwrap()]
})
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut map = ForestMap::from(input);
map.build_graph2();
map.simplify_graph();
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| calc_path_length(&map, &path)).unwrap();
longest.iter().tuple_windows().fold(0, |accum, (prev, next)| {
accum + map.graph[map.graph.find_edge(*prev, *next).unwrap()]
})
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 94);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 154);
}
}

134
24/Cargo.lock generated Normal file
View File

@ -0,0 +1,134 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "day24"
version = "0.1.0"
dependencies = [
"itertools",
"lazy-regex",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

10
24/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "day24"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
lazy-regex = "3.1.0"

24
24/cheat.py Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
from z3 import *
rx, ry, rz = Ints('rx ry rz')
rvx, rvy, rvz = Ints('rvx rvy rvz')
t0, t1, t2 = Ints('t0 t1 t2')
answer = Int('answer')
solve(
rx + t0 * rvx == 225415405941969 + t0 * 23,
ry + t0 * rvy == 400648127977931 + t0 * -204,
rz + t0 * rvz == 79201130433258 + t0 * 617,
rx + t1 * rvx == 353783687623292 + t1 * -80,
ry + t1 * rvy == 138575899489956 + t1 * 156,
rz + t1 * rvz == 318416438572569 + t1 * 21,
rx + t2 * rvx == 215751176267772 + t2 * -120,
ry + t2 * rvy == 376619563956940 + t2 * 126,
rz + t2 * rvz == 230133299986253 + t2 * -352,
answer == rx + ry + rz,
)

205
24/src/main.rs Normal file
View File

@ -0,0 +1,205 @@
use itertools::Itertools;
use std::fmt::Debug;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::ops::Index;
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// Parse
#[derive(Clone, Copy)]
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl Debug for Vec3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}, {}", self.x, self.y, self.z)
}
}
#[derive(Clone, Copy)]
struct Vec2 {
x: f64,
y: f64,
}
impl Debug for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}", self.x, self.y)
}
}
#[derive(Clone)]
struct Hailstone {
pos: Vec3,
vel: Vec3,
}
impl Hailstone {
fn xy_vel_angle(&self) -> f64 {
(self.vel.y / self.vel.x).atan()
}
fn xy_vel_abs(&self) -> f64 {
(self.vel.x.powi(2) + self.vel.y.powi(2)).sqrt()
}
fn xy_slope(&self) -> f64 {
self.vel.y / self.vel.x
}
fn y_crossing(&self) -> f64 {
self.pos.y - self.xy_slope() * self.pos.x
}
fn xy_poi(&self, other: &Hailstone) -> Option<Vec2> {
let our_slope = self.xy_slope();
let other_slope = other.xy_slope();
if our_slope == other_slope || our_slope * other_slope == -1. {
None
} else {
let our_yint = self.y_crossing();
let other_yint = other.y_crossing();
let ratio = (other_yint - our_yint) / (our_slope - other_slope);
Some(Vec2 {
x: ratio,
y: our_slope * ratio + our_yint,
})
}
}
fn xy_point_future(&self, point: &Vec2) -> bool {
// a point will be in the past if the difference between its 'new' position and its 'start' position has a
// different sign than the velocity for any component
let diffs = [point.x - self.pos.x, point.y - self.pos.y];
// for (diff, vel) in diffs.iter().zip([self.vel.x, self.vel.y].iter()) {
// println!(" diff: {:?} vel: {:?} mul: {:?}", diff, vel, diff * vel > 0.);
// }
diffs.iter().zip([self.vel.x, self.vel.y].iter()).any(|(diff, vel)| diff * vel > 0.)
}
}
impl From<&str> for Hailstone {
fn from(value: &str) -> Self {
let (pos, vel) = value.split_once("@").unwrap();
let [px, py, pz] = pos.split(",").map(|s| s.trim().parse::<f64>().unwrap()).collect_vec()[..] else {
panic!()
};
let [vx, vy, vz] = vel.split(",").map(|s| s.trim().parse::<f64>().unwrap()).collect_vec()[..] else {
panic!()
};
Self {
pos: Vec3 { x: px, y: py, z: pz },
vel: Vec3 { x: vx, y: vy, z: vz },
}
}
}
impl Debug for Hailstone {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} @ {:?}", self.pos, self.vel)
}
}
#[derive(Debug)]
struct Hailstones {
stones: Vec<Hailstone>,
}
impl Hailstones {
fn count_xy_pois(&self, bounds: (f64, f64)) -> u64 {
assert!(bounds.0 < bounds.1);
let mut stones = self.stones.clone();
let mut count = 0;
while let Some(stone) = &stones.pop() {
for other in &stones {
let Some(poi) = stone.xy_poi(other) else { continue };
// println!("intersection: {:?} / {:?} @ {:?}", stone, other, poi);
if poi.x >= bounds.0 && poi.x <= bounds.1 && poi.y >= bounds.0 && poi.y <= bounds.1 && stone.xy_point_future(&poi) && other.xy_point_future(&poi) {
count += 1;
}
}
}
count
}
}
impl Index<usize> for Hailstones {
type Output = Hailstone;
fn index(&self, index: usize) -> &Self::Output {
&self.stones[index]
}
}
impl<T: BufRead> From<Lines<T>> for Hailstones {
fn from(value: Lines<T>) -> Self {
let mut stones = Vec::new();
for line in value {
stones.push(Hailstone::from(line.unwrap().as_str()));
}
Hailstones { stones }
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, (200000000000000., 400000000000000.))
}
fn problem1_impl<T: BufRead>(input: Lines<T>, bounds: (f64, f64)) -> u64 {
let stones = Hailstones::from(input);
stones.count_xy_pois(bounds)
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"19, 13, 30 @ -2, 1, -2
18, 19, 22 @ -1, -1, -2
20, 25, 34 @ -2, -2, -4
12, 31, 28 @ -1, -2, -1
20, 19, 15 @ 1, -5, -3";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), (7., 27.)), 2);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}

266
25/Cargo.lock generated Normal file
View File

@ -0,0 +1,266 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [
"cfg-if",
]
[[package]]
name = "day25"
version = "0.1.0"
dependencies = [
"indicatif",
"itertools",
"petgraph",
"rayon",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

12
25/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "day25"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indicatif = "0.17.7"
itertools = "0.12.0"
petgraph = "0.6.4"
rayon = "1.8.0"

171
25/src/main.rs Normal file
View File

@ -0,0 +1,171 @@
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
use itertools::Itertools;
use petgraph::algo::connected_components;
use petgraph::dot::Dot;
use petgraph::prelude::*;
use petgraph::visit::{depth_first_search, DfsEvent, IntoEdgeReferences, IntoNodeIdentifiers, IntoNodeReferences};
use rayon::prelude::*;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
struct Circuit {
graph: UnGraph<String, String>,
nodes: HashMap<String, Node>,
}
#[derive(Debug, Clone)]
struct Node {
targets: Vec<String>,
index: Option<NodeIndex>,
}
impl Node {
fn with_index(mut self, index: NodeIndex) -> Self {
self.index = Some(index);
self
}
}
impl<T: BufRead> From<Lines<T>> for Circuit {
fn from(lines: Lines<T>) -> Self {
let mut graph = UnGraph::default();
let mut nodes = HashMap::new();
for line in lines.map(|v| v.unwrap()) {
let (source, targets_s) = line.split_once(": ").unwrap();
let targets = targets_s.split_whitespace().map(|s| s.to_string()).collect_vec();
nodes.insert(
source.to_string(),
Node {
targets,
index: Some(graph.add_node(source.to_string())),
},
);
}
for (name, node) in nodes.iter_mut() {
for target in &node.targets {
let target_idx = match graph.node_references().find(|n| n.1 == target).map(|n| n.0) {
Some(target) => target,
None => graph.add_node(target.to_string()),
};
graph.add_edge(node.index.unwrap(), target_idx, format!("{}-{}", name, target));
}
}
Self { graph, nodes }
}
}
impl Circuit {
// returns the set of nodes on each side
fn find_critical_edges(&self, removals: usize) -> (Vec<String>, Vec<String>) {
let bar = ProgressBar::new(self.graph.edge_references().combinations(removals).try_len().unwrap() as u64)
.with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
);
// for set in self.graph.edge_references().combinations(removals).progress_with(bar) {
let set = [("zcp", "zjm"), ("rsg", "nsk"), ("jks", "rfg")]
.iter()
.flat_map(|(src, dst)| {
self.graph.edge_references().filter(|e| self.graph[e.source()] == *src && self.graph[e.target()] == *dst || self.graph[e.target()] == *src && self.graph[e.source()] == *dst)
}).collect_vec();
let mut local_graph: StableUnGraph<_, _> = self.graph.clone().into();
for wire in &set {
local_graph.remove_edge(wire.id());
}
let local_graph = UnGraph::from(local_graph);
if connected_components(&local_graph) == 2 {
// each edge will have one leg in each component, so return the set of connected nodes
let mut left = Vec::new();
depth_first_search(&local_graph, Some(set[0].source()), |event| {
if let DfsEvent::Discover(n, t) = event {
left.push(local_graph[n].to_string());
}
});
let mut right = Vec::new();
depth_first_search(&local_graph, Some(set[0].target()), |event| {
if let DfsEvent::Discover(n, t) = event {
right.push(local_graph[n].to_string());
}
});
return (left, right);
}
// }
panic!("no cutline found");
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let circuit = Circuit::from(input);
println!("{:?}", Dot::new(&circuit.graph));
let (left, right) = circuit.find_critical_edges(3);
(left.len() * right.len()) as u64
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"jqt: rhn xhk nvd
rsh: frs pzl lsr
xhk: hfx
cmg: qnr nvd lhk bvb
rhn: xhk bvb hfx
bvb: xhk hfx
pzl: lsr hfx nvd
qnr: nvd
ntq: jqt hfx bvb xhk
nvd: lhk
lsr: lhk
rzs: qnr cmg lsr rsh
frs: qnr lhk lsr";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 54);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}