add day 22

This commit is contained in:
timeshifter
2026-07-03 20:59:07 +02:00
parent 672c7de41a
commit de2b9f0ecb
4 changed files with 1188 additions and 0 deletions
+164
View File
@@ -0,0 +1,164 @@
use std::collections::{HashSet, VecDeque};
use itertools::Itertools;
const MAX_X: usize = 33;
const MAX_Y: usize = 30;
fn main() {
let nodes: Vec<_> = std::fs::read_to_string("input.txt")
.unwrap()
.lines()
.skip(2)
.map(Node::parse)
.collect();
// part 1
let result = nodes
.iter()
.permutations(2)
.filter(|vec| is_viable_pair(vec[0], vec[1]))
.count();
println!("{result}"); // 967
// part 2
// let grid = Grid::new(nodes);
// let result = bfs(grid);
let walls: HashSet<_> = nodes
.iter()
// everything that's too large is considered a wall
.filter(|n| n.size > 200)
.map(|n| (n.x, n.y))
.collect();
let goal_pos = (32, 0);
let empty = nodes
.iter()
.filter_map(|n| (n.used == 0).then_some((n.x, n.y)))
.exactly_one()
.unwrap();
let state = State::new(empty, goal_pos);
let result = bfs(state, walls);
println!("{result}");
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct State {
empty_pos: (usize, usize),
goal_pos: (usize, usize),
}
impl State {
fn new(empty_pos: (usize, usize), goal_pos: (usize, usize)) -> Self {
Self {
empty_pos,
goal_pos,
}
}
fn neighbor_states(&self, walls: &HashSet<(usize, usize)>) -> impl Iterator<Item = Self> {
get_neighbor_coordinates(self.empty_pos.0, self.empty_pos.1)
.filter(|coordinates| !walls.contains(coordinates))
.map(|(x, y)| {
let mut new_state = *self;
new_state.empty_pos = (x, y);
if new_state.goal_pos == (x, y) {
new_state.goal_pos = self.empty_pos;
}
new_state
})
}
}
fn bfs(start: State, walls: HashSet<(usize, usize)>) -> usize {
let mut queue = VecDeque::new();
let mut visited = HashSet::new();
visited.insert(start);
queue.push_back((start, 0));
while let Some((state, distance)) = queue.pop_front() {
if state.goal_pos == (0, 0) {
return distance;
}
for state in state.neighbor_states(&walls) {
if !visited.contains(&state) {
visited.insert(state);
queue.push_back((state, distance + 1));
}
}
}
panic!("goal unreachable")
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct Node {
x: usize,
y: usize,
size: usize,
used: usize,
avail: usize,
has_goal_data: bool,
}
impl Node {
fn parse(s: &str) -> Self {
let mut split = s.split_whitespace();
let dev = split.next().unwrap();
let mut dev_split = dev.split('-');
let y_coord = dev_split.next_back().unwrap();
let x_coord = dev_split.nth(1).unwrap();
let x = x_coord.chars().skip(1).collect::<String>().parse().unwrap();
let y = y_coord.chars().skip(1).collect::<String>().parse().unwrap();
let size_s = split.next().unwrap();
let size = parse_number_from_df(size_s);
let used_s = split.next().unwrap();
let used = parse_number_from_df(used_s);
let avail_s = split.next().unwrap();
let avail = parse_number_from_df(avail_s);
Self {
x,
y,
size,
used,
avail,
has_goal_data: false,
}
}
}
fn parse_number_from_df(s: &str) -> usize {
s.chars()
.take_while(|c| c.is_ascii_digit())
.collect::<String>()
.parse()
.unwrap()
}
fn is_viable_pair(from: &Node, to: &Node) -> bool {
from.used > 0 && !(from.x == to.x && from.y == to.y) && from.used < to.avail
}
fn get_neighbor_coordinates(x: usize, y: usize) -> impl Iterator<Item = (usize, usize)> {
[
x.checked_sub(1).map(|x| (x, y)),
y.checked_sub(1).map(|y| (x, y)),
Some((x + 1, y)),
Some((x, y + 1)),
]
.into_iter()
.flatten()
.filter(|(x, y)| *x < MAX_X && *y < MAX_Y)
}