Compare commits
2 commits
af2e848a66
...
c0407504c4
Author | SHA1 | Date | |
---|---|---|---|
missing | c0407504c4 | ||
missing | 907593e2e1 |
132
src/days/day12.rs
Normal file
132
src/days/day12.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
use std::{
|
||||
cmp::{Ordering, Reverse},
|
||||
collections::{BinaryHeap, HashMap},
|
||||
};
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
struct PuzzleInput {
|
||||
heights: Vec<Vec<u8>>,
|
||||
start: (usize, usize),
|
||||
end: (usize, usize),
|
||||
}
|
||||
|
||||
#[aoc_generator(day12)]
|
||||
fn generator(input: &str) -> PuzzleInput {
|
||||
let mut start = None;
|
||||
let mut end = None;
|
||||
|
||||
let nodes = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(i, l)| {
|
||||
l.chars()
|
||||
.enumerate()
|
||||
.map(|(j, c)| match c {
|
||||
'S' => {
|
||||
assert!(start.replace((i, j)).is_none());
|
||||
1
|
||||
}
|
||||
'E' => {
|
||||
assert!(end.replace((i, j)).is_none());
|
||||
26
|
||||
}
|
||||
'a'..='z' => c as u8 - b'a' + 1,
|
||||
_ => panic!(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
PuzzleInput {
|
||||
heights: nodes,
|
||||
start: start.unwrap(),
|
||||
end: end.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn neighbours(coord: (usize, usize), heights: &[Vec<u8>]) -> impl Iterator<Item = (usize, usize)> {
|
||||
let current_height = heights[coord.0][coord.1];
|
||||
|
||||
let a = (coord.0 + 1, coord.1);
|
||||
let a = heights
|
||||
.get(a.0)
|
||||
.and_then(|v| (v[a.1] >= current_height - 1).then_some(a));
|
||||
|
||||
let b = coord.0.checked_sub(1).map(|v| (v, coord.1));
|
||||
let b = b.filter(|b| heights[b.0][b.1] >= current_height - 1);
|
||||
|
||||
let c = (coord.0, coord.1 + 1);
|
||||
let c = heights[c.0]
|
||||
.get(c.1)
|
||||
.and_then(|v| (*v >= current_height - 1).then_some(c));
|
||||
|
||||
let d = coord.1.checked_sub(1).map(|v| (coord.0, v));
|
||||
let d = d.filter(|d| heights[d.0][d.1] >= current_height - 1);
|
||||
|
||||
a.into_iter().chain(b).chain(c).chain(d)
|
||||
}
|
||||
|
||||
struct CmpByFirst<T, U>(T, U);
|
||||
|
||||
impl<T: PartialEq, U> PartialEq for CmpByFirst<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for CmpByFirst<T, U> {}
|
||||
|
||||
impl<T: PartialOrd, U> PartialOrd for CmpByFirst<T, U> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord, U> Ord for CmpByFirst<T, U> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_dist_map(input: &PuzzleInput) -> HashMap<(usize, usize), u32> {
|
||||
let mut to_visit = BinaryHeap::new();
|
||||
to_visit.push(CmpByFirst(Reverse(0), input.end));
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(input.end, 0);
|
||||
|
||||
while let Some(CmpByFirst(Reverse(dist), coord)) = to_visit.pop() {
|
||||
for neighbour in neighbours(coord, &input.heights) {
|
||||
map.entry(neighbour)
|
||||
.and_modify(|v| *v = (*v).min(dist + 1))
|
||||
.or_insert_with(|| {
|
||||
to_visit.push(CmpByFirst(Reverse(dist + 1), neighbour));
|
||||
|
||||
dist + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
#[aoc(day12, part1)]
|
||||
fn part1(input: &PuzzleInput) -> u32 {
|
||||
fill_dist_map(input)[&input.start]
|
||||
}
|
||||
|
||||
#[aoc(day12, part2)]
|
||||
fn part2(input: &PuzzleInput) -> u32 {
|
||||
let map = fill_dist_map(input);
|
||||
|
||||
input
|
||||
.heights
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, v)| v.iter().enumerate().map(move |(j, v)| ((i, j), v)))
|
||||
.filter_map(|(coord, h)| (*h == 1).then_some(coord))
|
||||
.map(|coord| *map.get(&coord).unwrap_or(&u32::MAX))
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
130
src/days/day13.rs
Normal file
130
src/days/day13.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use std::{cmp::Ordering, fmt::Debug};
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use itertools::{EitherOrBoth, Itertools};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
enum Data {
|
||||
Integer(u32),
|
||||
List(Vec<Data>),
|
||||
}
|
||||
|
||||
impl Debug for Data {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Integer(i) => write!(f, "{i}"),
|
||||
Self::List(l) => f.debug_list().entries(l).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Data {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Data {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
(Data::Integer(a), Data::Integer(b)) => a.cmp(b),
|
||||
(a @ Data::Integer(_), b @ Data::List(_)) => b.cmp(a).reverse(),
|
||||
(a @ Data::List(_), Data::Integer(b)) => {
|
||||
a.cmp(&Data::List(Vec::from([Data::Integer(*b)])))
|
||||
}
|
||||
(Data::List(a), Data::List(b)) => a
|
||||
.iter()
|
||||
.zip_longest(b)
|
||||
.find_map(|els| match els {
|
||||
EitherOrBoth::Both(a, b) => {
|
||||
let res = a.cmp(b);
|
||||
(!res.is_eq()).then_some(res)
|
||||
}
|
||||
EitherOrBoth::Left(_) => Some(Ordering::Greater),
|
||||
EitherOrBoth::Right(_) => Some(Ordering::Less),
|
||||
})
|
||||
.unwrap_or(Ordering::Equal),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
fn parse(s: &str) -> Option<(Self, &str)> {
|
||||
if let Some(mut rest) = s.strip_prefix('[') {
|
||||
let mut vec = Vec::new();
|
||||
|
||||
loop {
|
||||
if let Some(rest) = rest.strip_prefix(']') {
|
||||
return Some((Self::List(vec), rest));
|
||||
}
|
||||
|
||||
let (val, rest2) = Self::parse(rest)?;
|
||||
vec.push(val);
|
||||
|
||||
if let Some(rest) = rest2.strip_prefix(']') {
|
||||
return Some((Self::List(vec), rest));
|
||||
}
|
||||
|
||||
let rest2 = rest2.strip_prefix(',')?;
|
||||
rest = rest2;
|
||||
}
|
||||
} else if s.chars().next()?.is_alphanumeric() {
|
||||
let (num_part, rest) = s
|
||||
.char_indices()
|
||||
.find_map(|(i, c)| (!c.is_numeric()).then_some(s.split_at(i)))
|
||||
.unwrap_or((s, ""));
|
||||
|
||||
Some((Self::Integer(num_part.parse().ok()?), rest))
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[aoc_generator(day13)]
|
||||
fn generator(input: &str) -> Vec<(Data, Data)> {
|
||||
input
|
||||
.lines()
|
||||
.chunks(3)
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
(
|
||||
Data::parse(v.next().unwrap()).unwrap().0,
|
||||
Data::parse(v.next().unwrap()).unwrap().0,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[aoc(day13, part1)]
|
||||
fn part1(input: &[(Data, Data)]) -> usize {
|
||||
input
|
||||
.iter()
|
||||
.positions(|(a, b)| a < b)
|
||||
.map(|v| v + 1) // adjust for one-indexing
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day13, part2)]
|
||||
fn part2(input: &[(Data, Data)]) -> usize {
|
||||
let mut vec = input
|
||||
.iter()
|
||||
.flat_map(|(a, b)| [a.clone(), b.clone()])
|
||||
.map(|v| (v, false))
|
||||
.chain([
|
||||
(Data::parse("[[2]]").unwrap().0, true),
|
||||
(Data::parse("[[6]]").unwrap().0, true),
|
||||
])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
vec.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
let (i, j) = vec
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, (_, is_divider))| is_divider.then_some(i + 1))
|
||||
.next_tuple()
|
||||
.unwrap();
|
||||
|
||||
i * j
|
||||
}
|
|
@ -13,6 +13,8 @@ mod days {
|
|||
// curse you, rustfmt
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
mod day13;
|
||||
}
|
||||
|
||||
aoc_lib! { year = 2022 }
|
||||
|
|
Loading…
Reference in a new issue