Dag 16 aoc af, vrij elegant en snel
This commit is contained in:
242
advent_of_code/2024/16/src/main.rs
Normal file
242
advent_of_code/2024/16/src/main.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user