mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Compare commits
6 Commits
37c578d7cc
...
ffe067b122
| Author | SHA1 | Date | |
|---|---|---|---|
| ffe067b122 | |||
| 949de03b24 | |||
| a891d08b21 | |||
| ca054363bf | |||
| 65c6340061 | |||
| da61b8d541 |
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use anyhow::Context;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take_until;
|
||||
@@ -11,6 +12,7 @@ use nom::multi::fold_many1;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::sequence::delimited;
|
||||
use nom::IResult;
|
||||
use num_integer::Integer;
|
||||
|
||||
use crate::common::parse_input;
|
||||
|
||||
@@ -141,8 +143,82 @@ pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
||||
Ok((low_pulses * high_pulses).to_string())
|
||||
}
|
||||
|
||||
pub fn part2(_input: &[u8]) -> anyhow::Result<String> {
|
||||
anyhow::bail!("Not implemented")
|
||||
/// This solution is entirely based on the structure of the graph. The only node pointing into the
|
||||
/// rx node is a conjunction, which itself has a few conjunctions into then that work like binary
|
||||
/// counters.
|
||||
///
|
||||
/// In the end, I compute the period of each of those binary counters, and then the solution is the
|
||||
/// lowest common multiple of all of those.
|
||||
pub fn part2(input: &[u8]) -> anyhow::Result<String> {
|
||||
let mut cables = parse_input(input, parse_cables)?;
|
||||
|
||||
let mut todo = VecDeque::new();
|
||||
let mut last_pulse: HashMap<u32, bool> = HashMap::new();
|
||||
|
||||
let into_rx = cables
|
||||
.iter()
|
||||
.find_map(|(id, entry)| {
|
||||
if entry.dest.contains(&convert_name(b"rx")) {
|
||||
Some(*id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.context("Cannot find node pointing into rx")?;
|
||||
|
||||
let Node::Conjunction(into) = &cables[&into_rx].node else {
|
||||
anyhow::bail!("Node pointing into rx is not a conjunction");
|
||||
};
|
||||
|
||||
// In theory this data structure should be a set, in practice it contains 4 elements and a Vec
|
||||
// is nice and easy.
|
||||
let mut awaiting = into.clone();
|
||||
let mut lcm = 1;
|
||||
|
||||
for press in 1u64.. {
|
||||
todo.push_back((false, convert_name(b"broadcaster")));
|
||||
|
||||
while let Some((pulse, pos)) = todo.pop_front() {
|
||||
let Some(cable) = cables.get_mut(&pos) else {
|
||||
// Sometimes cables aren't real, and that's okay
|
||||
continue;
|
||||
};
|
||||
|
||||
let next_pulse = match &mut cable.node {
|
||||
Node::FlipFlop(state) => {
|
||||
if pulse {
|
||||
// Ignore, nothing to be done since it's a high pulse
|
||||
continue;
|
||||
} else {
|
||||
*state = !*state;
|
||||
*state
|
||||
}
|
||||
}
|
||||
Node::Conjunction(inwards) => {
|
||||
// Need to deal with the check outside the match otherwise lifetime issues :(
|
||||
!inwards
|
||||
.iter()
|
||||
.all(|source| *last_pulse.get(source).unwrap_or(&false))
|
||||
}
|
||||
Node::Broadcaster => pulse,
|
||||
};
|
||||
|
||||
last_pulse.insert(pos, next_pulse);
|
||||
for &other in &cable.dest {
|
||||
todo.push_back((next_pulse, other));
|
||||
if next_pulse && other == into_rx && awaiting.contains(&pos) {
|
||||
lcm = press.lcm(&lcm);
|
||||
awaiting.retain(|f| f != &pos);
|
||||
|
||||
if awaiting.is_empty() {
|
||||
return Ok(lcm.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::bail!("Somehow counted to infinity")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,7 +1,169 @@
|
||||
pub fn part1(_input: &[u8]) -> anyhow::Result<String> {
|
||||
anyhow::bail!("Not implemented")
|
||||
use std::collections::HashSet;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::common::Grid;
|
||||
use crate::common::IndexSet;
|
||||
|
||||
fn part1_parametrized(input: &[u8], steps: usize) -> anyhow::Result<String> {
|
||||
let map = Grid::new(input)?;
|
||||
let mut visited = IndexSet::with_capacity(map.width() * map.height());
|
||||
let mut todo = VecDeque::new();
|
||||
let start = map.find(b'S').context("Couldn't find starting point")?;
|
||||
todo.push_back((0, start));
|
||||
visited.insert(start.1 * map.width() + start.1);
|
||||
let mut count = (steps + 1) % 2;
|
||||
|
||||
while let Some((dist, (x, y))) = todo.pop_front() {
|
||||
let mut enqueue = |x, y| {
|
||||
if map[(y, x)] != b'#' && visited.insert(y * map.width() + x) {
|
||||
if dist < steps {
|
||||
todo.push_back((dist + 1, (x, y)));
|
||||
}
|
||||
|
||||
if (dist + 1) % 2 == steps % 2 {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if x > 0 {
|
||||
enqueue(x - 1, y);
|
||||
}
|
||||
|
||||
if y > 0 {
|
||||
enqueue(x, y - 1);
|
||||
}
|
||||
|
||||
if x + 1 < map.width() {
|
||||
enqueue(x + 1, y);
|
||||
}
|
||||
|
||||
if y + 1 < map.height() {
|
||||
enqueue(x, y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count.to_string())
|
||||
}
|
||||
|
||||
pub fn part2(_input: &[u8]) -> anyhow::Result<String> {
|
||||
anyhow::bail!("Not implemented")
|
||||
pub fn part1(input: &[u8]) -> anyhow::Result<String> {
|
||||
part1_parametrized(input, 64)
|
||||
}
|
||||
|
||||
fn compute_infinimap<const L: usize>(
|
||||
input: &[u8],
|
||||
steps_groups: [i64; L],
|
||||
) -> anyhow::Result<[i64; L]> {
|
||||
let map = Grid::new(input)?;
|
||||
let width = map.width() as i64;
|
||||
let height = map.height() as i64;
|
||||
|
||||
let start = map.find(b'S').context("No starting point?")?;
|
||||
|
||||
let mut visited = HashSet::new();
|
||||
visited.insert((start.0 as i64, start.1 as i64));
|
||||
let mut todo = VecDeque::new();
|
||||
todo.push_back((0, (start.0 as i64, start.1 as i64)));
|
||||
|
||||
let mut final_counts = [0; L];
|
||||
let mut counts = [1, 0];
|
||||
let mut i = 0;
|
||||
|
||||
while let Some((dist, (x, y))) = todo.pop_front() {
|
||||
if dist == steps_groups[i] {
|
||||
final_counts[i] = counts[(steps_groups[i] % 2) as usize];
|
||||
i += 1;
|
||||
if i >= steps_groups.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mut enqueue = |x, y| {
|
||||
let map_x = (((x % width) + width) % width) as usize;
|
||||
let map_y = (((y % height) + height) % height) as usize;
|
||||
// println!("{x} → {map_x}, {y} → {map_y}");
|
||||
|
||||
if map[(map_y, map_x)] != b'#' && visited.insert((x, y)) {
|
||||
todo.push_back((dist + 1, (x, y)));
|
||||
counts[((dist + 1) % 2) as usize] += 1;
|
||||
}
|
||||
};
|
||||
|
||||
enqueue(x - 1, y);
|
||||
enqueue(x + 1, y);
|
||||
enqueue(x, y - 1);
|
||||
enqueue(x, y + 1);
|
||||
}
|
||||
|
||||
Ok(final_counts)
|
||||
}
|
||||
|
||||
pub fn part2(input: &[u8]) -> anyhow::Result<String> {
|
||||
// This is wrong for things that aren't the input but it works for me, so…
|
||||
let dests = [65, 65 + 131, 65 + 2 * 131];
|
||||
|
||||
let counts = compute_infinimap(input, dests)?;
|
||||
|
||||
// Stolen set of equations, this fits a quadratic equation to a three points at x = 0, 1, 2
|
||||
let a = 0;
|
||||
let b = 0;
|
||||
let c = 1;
|
||||
let d = counts[0];
|
||||
let e = 1;
|
||||
let f = 1;
|
||||
let g = 1;
|
||||
let h = counts[1];
|
||||
let i = 2 * 2;
|
||||
let j = 2;
|
||||
let k = 1;
|
||||
let l = counts[2];
|
||||
|
||||
let delta = (a * f * k) + (b * g * i) + (c * e * j) - (c * f * i) - (a * g * j) - (b * e * k);
|
||||
let a_numerator =
|
||||
(d * f * k) + (b * g * l) + (c * h * j) - (c * f * l) - (d * g * j) - (b * h * k);
|
||||
let b_numerator =
|
||||
(a * h * k) + (d * g * i) + (c * e * l) - (c * h * i) - (a * g * l) - (d * e * k);
|
||||
let c_numerator =
|
||||
(a * f * l) + (b * h * i) + (d * e * j) - (d * f * i) - (a * h * j) - (b * e * l);
|
||||
|
||||
let a = a_numerator / delta;
|
||||
let b = b_numerator / delta;
|
||||
let c = c_numerator / delta;
|
||||
|
||||
let x = 26501365 / 131;
|
||||
|
||||
let result = a * (x * x) + b * x + c;
|
||||
|
||||
Ok(result.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE: &[u8] = include_bytes!("samples/21.txt");
|
||||
|
||||
#[test]
|
||||
fn sample_part1() {
|
||||
assert_eq!("16", part1_parametrized(SAMPLE, 6).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_infinimap() {
|
||||
let inputs = [
|
||||
6, 10, 50, 100,
|
||||
// 500
|
||||
// 1000
|
||||
// 5000
|
||||
];
|
||||
let expected = [
|
||||
16, 50, 1594, 6536,
|
||||
// 167004,
|
||||
// 668697,
|
||||
// 16733044,
|
||||
];
|
||||
|
||||
assert_eq!(expected, compute_infinimap(SAMPLE, inputs).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
11
2023/src/samples/21.txt
Normal file
11
2023/src/samples/21.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
...........
|
||||
.....###.#.
|
||||
.###.##..#.
|
||||
..#.#...#..
|
||||
....#.#....
|
||||
.##..S####.
|
||||
.##..#...#.
|
||||
.......##..
|
||||
.##.#.####.
|
||||
.##..##.##.
|
||||
...........
|
||||
Reference in New Issue
Block a user