add day 22
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user