2 Commits

Author SHA1 Message Date
9b164a4d9d Implement 2023 day 24 part 2 2023-12-25 15:00:28 +01:00
5d811a08b6 Implement 2023 day 25 2023-12-25 14:14:48 +01:00
4 changed files with 236 additions and 4 deletions

View File

@@ -9,9 +9,12 @@ edition = "2021"
aho-corasick = "1.1.2"
anyhow = "1.0.75"
clap = { version = "4.4.8", features = ["derive"] }
linfa-linalg = "0.1.0"
linked-hash-map = "0.5.6"
ndarray = "0.15.6"
nom = "7.1.3"
num-integer = "0.1.45"
rand = "0.8.5"
[dev-dependencies]
criterion = "0.5.1"

View File

@@ -1,3 +1,4 @@
use linfa_linalg::qr::QRInto;
use nom::bytes::complete::tag;
use nom::character::complete::space1;
use nom::combinator::map;
@@ -95,8 +96,81 @@ pub fn part1(input: &[u8]) -> anyhow::Result<String> {
part1_parametrized(input, 200000000000000.0, 400000000000000.0)
}
pub fn part2(_input: &[u8]) -> anyhow::Result<String> {
anyhow::bail!("Not implemented")
pub fn part2(input: &[u8]) -> anyhow::Result<String> {
let hail = parse_input(input, parse_hail)?;
let p0 = hail[0].position.map(|v| v as f64);
let p1 = hail[1].position.map(|v| v as f64);
let p2 = hail[2].position.map(|v| v as f64);
let v0 = hail[0].speed.map(|v| v as f64);
let v1 = hail[1].speed.map(|v| v as f64);
let v2 = hail[2].speed.map(|v| v as f64);
let b = ndarray::array![
[(p0[1] * v0[0] - p1[1] * v1[0]) - (p0[0] * v0[1] - p1[0] * v1[1])],
[(p0[1] * v0[0] - p2[1] * v2[0]) - (p0[0] * v0[1] - p2[0] * v2[1])],
[(p0[2] * v0[0] - p1[2] * v1[0]) - (p0[0] * v0[2] - p1[0] * v1[2])],
[(p0[2] * v0[0] - p2[2] * v2[0]) - (p0[0] * v0[2] - p2[0] * v2[2])],
[(p0[2] * v0[1] - p1[2] * v1[1]) - (p0[1] * v0[2] - p1[1] * v1[2])],
[(p0[2] * v0[1] - p2[2] * v2[1]) - (p0[1] * v0[2] - p2[1] * v2[2])],
];
let a = ndarray::array![
[
v1[1] - v0[1],
v0[0] - v1[0],
0.0,
p0[1] - p1[1],
p1[0] - p0[0],
0.0
],
[
v2[1] - v0[1],
v0[0] - v2[0],
0.0,
p0[1] - p2[1],
p2[0] - p0[0],
0.0
],
[
v1[2] - v0[2],
0.0,
v0[0] - v1[0],
p0[2] - p1[2],
0.0,
p1[0] - p0[0]
],
[
v2[2] - v0[2],
0.0,
v0[0] - v2[0],
p0[2] - p2[2],
0.0,
p2[0] - p0[0]
],
[
0.0,
v1[2] - v0[2],
v0[1] - v1[1],
0.0,
p0[2] - p1[2],
p1[1] - p0[1]
],
[
0.0,
v2[2] - v0[2],
v0[1] - v2[1],
0.0,
p0[2] - p2[2],
p2[1] - p0[1]
],
];
let rock = a.qr_into()?.solve_into(b)?;
Ok((rock[[0, 0]] + rock[[1, 0]] + rock[[2, 0]])
.round()
.to_string())
}
#[cfg(test)]
@@ -109,4 +183,9 @@ mod tests {
fn sample_part1() {
assert_eq!("2", part1_parametrized(SAMPLE, 7.0, 27.0).unwrap());
}
#[test]
fn sample_part2() {
assert_eq!("47", part2(SAMPLE).unwrap());
}
}

View File

@@ -1,3 +1,140 @@
pub fn part1(_input: &[u8]) -> anyhow::Result<String> {
anyhow::bail!("Not implemented")
use std::cmp::Reverse;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_until;
use nom::bytes::complete::take_while1;
use nom::combinator::opt;
use nom::multi::fold_many1;
use nom::sequence::terminated;
use nom::IResult;
use rand::seq::SliceRandom;
use rand::thread_rng;
use crate::common::minmax;
use crate::common::parse_input;
type Node<'a> = &'a [u8];
type Graph<'a> = HashMap<Node<'a>, HashSet<Node<'a>>>;
fn parse_graph(mut i: &[u8]) -> IResult<&[u8], Graph> {
fn parse_line<'a>(i: &'a [u8], graph: &mut Graph<'a>) -> IResult<&'a [u8], ()> {
let (i, name) = terminated(take_until(":"), tag(": "))(i)?;
terminated(
fold_many1(
terminated(take_while1(|c: u8| c.is_ascii_alphabetic()), opt(tag(" "))),
|| (),
move |_, other| {
graph.entry(name).or_default().insert(other);
graph.entry(other).or_default().insert(name);
},
),
tag("\n"),
)(i)
}
let mut graph = HashMap::new();
while !i.is_empty() {
let (remain, _) = parse_line(i, &mut graph)?;
i = remain;
}
Ok((i, graph))
}
fn find_path<'a>(
s: &'a [u8],
t: &'a [u8],
graph: &Graph<'a>,
counts: &mut HashMap<(&'a [u8], &'a [u8]), usize>,
) {
let mut todo = VecDeque::new();
let mut prev = HashMap::new();
todo.push_back(s);
prev.insert(s, s);
'outer: while let Some(node) = todo.pop_front() {
for &other in graph[node].iter() {
if let Entry::Vacant(entry) = prev.entry(other) {
entry.insert(node);
if other == t {
break 'outer;
} else {
todo.push_back(other);
}
}
}
}
let mut cur = t;
while let Some(ancestor) = prev.get(cur).copied() {
if cur == ancestor {
return;
}
let (a, b) = minmax(ancestor, cur);
*counts.entry((a, b)).or_default() += 1;
cur = ancestor;
}
unreachable!("Should not get here");
}
fn component_size(s: &[u8], graph: &Graph) -> usize {
let mut visited = HashSet::new();
visited.insert(s);
let mut todo = Vec::new();
todo.push(s);
while let Some(node) = todo.pop() {
todo.extend(graph[node].iter().filter(|&&v| visited.insert(v)));
}
visited.len()
}
pub fn part1(input: &[u8]) -> anyhow::Result<String> {
let mut graph = parse_input(input, parse_graph)?;
let nodes: Vec<_> = graph.keys().copied().collect();
let mut rng = thread_rng();
let mut counts = HashMap::new();
for _ in 0..1000 {
let mut it = nodes.choose_multiple(&mut rng, 2);
let first = *it.next().unwrap();
let second = *it.next().unwrap();
find_path(first, second, &graph, &mut counts);
}
let mut weighted_edges: Vec<_> = counts.into_iter().collect();
weighted_edges.sort_unstable_by_key(|c| Reverse(c.1));
let copy = graph.clone();
for &((a, b), _) in &weighted_edges[..3] {
graph.get_mut(&a).unwrap().remove(&b);
graph.get_mut(&b).unwrap().remove(&a);
}
assert_ne!(graph, copy);
let first_size = component_size(nodes[0], &graph);
Ok((first_size * (graph.len() - first_size)).to_string())
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &[u8] = include_bytes!("samples/25.txt");
#[test]
fn sample_part1() {
assert_eq!("54", part1(SAMPLE).unwrap());
}
}

13
2023/src/samples/25.txt Normal file
View File

@@ -0,0 +1,13 @@
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