Dag 16 aoc af, vrij elegant en snel

This commit is contained in:
2024-12-19 16:41:15 +01:00
parent 98a82b523e
commit 6c949c0657
4 changed files with 405 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
use std::collections::{HashMap, HashSet};
use nom::{
character::complete::{multispace1, not_line_ending},
multi::separated_list1,
IResult, Parser,
};
#[derive(Debug, Clone, PartialEq, Eq)]
enum Tile {
Wall,
Empty,
Start,
End,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Direction {
North,
East,
West,
South,
}
fn parse(input: &str) -> IResult<&str, Vec<Vec<Tile>>> {
use Tile::*;
let (input, result) = separated_list1(
multispace1,
not_line_ending.map(|s: &str| {
s.chars()
.map(|c| match c {
'#' => Wall,
'.' => Empty,
'S' => Start,
'E' => End,
_ => unreachable!(),
})
.collect()
}),
)(input)?;
Ok((input, result))
}
fn move_one(pos: (usize, usize), dir: Direction) -> (usize, usize) {
use Direction::*;
let (x, y) = pos;
match dir {
North => (x, y - 1),
East => (x + 1, y),
West => (x - 1, y),
South => (x, y + 1),
}
}
fn dirs_to_checked(dir: Direction) -> Vec<Direction> {
use Direction::*;
match dir {
North => vec![North, East, West],
East => vec![East, North, South],
West => vec![West, North, South],
South => vec![South, East, West],
}
}
fn solve_1(input: &str) -> u64 {
use Direction::*;
use Tile::*;
let (_, map) = parse(input).unwrap();
let mut start = (0, 0);
let mut end = (0, 0);
for (j, row) in map.iter().enumerate() {
for (i, t) in row.iter().enumerate() {
if *t == Start {
start = (i, j);
} else if *t == End {
end = (i, j);
}
}
}
let mut stack = HashMap::new();
let mut visited = HashSet::new();
let mut points = 0;
stack.insert(0, vec![(start, East)]);
visited.insert((start, East));
loop {
if let Some(ss) = stack.get(&points) {
for &((x, y), dir) in ss.clone().iter() {
if (x, y) == end {
return points;
}
for dn in dirs_to_checked(dir) {
let (xn, yn) = move_one((x, y), dn);
if !visited.contains(&((xn, yn), dn)) {
match map[yn][xn] {
Wall => {}
_ => {
let pn = points + if dir == dn { 1 } else { 1001 };
let mut sn = match stack.get(&pn) {
None => vec![],
Some(snn) => snn.clone(),
};
sn.push(((xn, yn), dn));
stack.insert(pn, sn);
}
};
}
}
visited.insert(((x, y), dir));
}
}
points += 1;
}
}
fn solve_2(input: &str) -> u64 {
use Direction::*;
use Tile::*;
let (_, map) = parse(input).unwrap();
let mut start = (0, 0);
let mut end = (0, 0);
for (j, row) in map.iter().enumerate() {
for (i, t) in row.iter().enumerate() {
if *t == Start {
start = (i, j);
} else if *t == End {
end = (i, j);
}
}
}
let mut stack = HashMap::new();
let mut visited = HashSet::new();
let mut points = 0;
let mut found = vec![];
stack.insert(0, vec![(start, East, vec![])]);
visited.insert((start, East));
loop {
let maybe_s = stack.get(&points);
match maybe_s {
Some(ss) => {
let cs = ss.to_owned();
for ((x, y), dir, b) in cs.iter() {
if (*x, *y) == end {
found.push(b.to_owned());
}
for dn in dirs_to_checked(*dir) {
let (xn, yn) = move_one((*x, *y), dn);
if !visited.contains(&((xn, yn), dn)) {
match map[yn][xn] {
Wall => {}
_ => {
let pn = points + if *dir == dn { 1 } else { 1001 };
let mut sn = match stack.get(&pn) {
None => vec![],
Some(snn) => snn.clone(),
};
let mut bn = b.clone();
bn.push((*x, *y));
sn.push(((xn, yn), dn, bn));
stack.insert(pn, sn);
}
};
}
}
visited.insert(((*x, *y), *dir));
}
if found.len() > 0 {
let mut found_set = HashSet::new();
for ff in found {
for f in ff {
found_set.insert(f);
}
}
// + 1 because of the end tile not being in the sets
return found_set.len() as u64 + 1;
}
}
None => {}
}
points += 1;
}
}
fn main() {
println!("Hello, this is Patrick!");
let input = include_str!("../input.txt");
let result_1 = solve_1(input);
println!("The optimal path costs {}", result_1);
let result_2 = solve_2(input);
println!("The number of the best tiles are {}", result_2);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_1() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_1(test_input), 7036);
}
#[test]
fn test_2() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_2(test_input), 45);
}
}