Compare commits
2 Commits
7ff46b8f99
...
62ff4680a5
Author | SHA1 | Date |
---|---|---|
|
62ff4680a5 | |
|
965bdca334 |
12
src/day12.rs
12
src/day12.rs
|
@ -3,7 +3,7 @@ use crate::util::maps::*;
|
||||||
const DISCOUNT: bool = true;
|
const DISCOUNT: bool = true;
|
||||||
|
|
||||||
pub fn day12() {
|
pub fn day12() {
|
||||||
let mut plots: Map<char> = Map::from_string(&crate::input(12));
|
let mut plots: World<char> = World::from_string(&crate::input(12));
|
||||||
|
|
||||||
let mut regions: Vec<Region> = vec![];
|
let mut regions: Vec<Region> = vec![];
|
||||||
for (x, y) in plots.coordinates() {
|
for (x, y) in plots.coordinates() {
|
||||||
|
@ -78,20 +78,20 @@ impl Border {
|
||||||
|
|
||||||
struct Region {
|
struct Region {
|
||||||
pub borders: Vec<Border>,
|
pub borders: Vec<Border>,
|
||||||
pub tiles: Map<bool>,
|
pub tiles: World<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Region {
|
impl Region {
|
||||||
fn new(x: usize, y: usize, plots: &Map<char>) -> Self {
|
fn new(x: usize, y: usize, plots: &World<char>) -> Self {
|
||||||
let c = plots.get(x, y).unwrap();
|
let c = plots.get(x, y).unwrap();
|
||||||
let mut tiles = Map::<bool>::from_dimensions(plots.width(), plots.height(), false);
|
let mut tiles = World::<bool>::from_dimensions(plots.width(), plots.height(), false);
|
||||||
|
|
||||||
fn collect_plots(
|
fn collect_plots(
|
||||||
c: char,
|
c: char,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
plots: &Map<char>,
|
plots: &World<char>,
|
||||||
tiles: &mut Map<bool>,
|
tiles: &mut World<bool>,
|
||||||
) -> Vec<(usize, usize)> {
|
) -> Vec<(usize, usize)> {
|
||||||
if plots.get(x, y) != Some(c) || tiles.get(x, y) == Some(true) {
|
if plots.get(x, y) != Some(c) || tiles.get(x, y) == Some(true) {
|
||||||
return vec![];
|
return vec![];
|
||||||
|
|
14
src/day15.rs
14
src/day15.rs
|
@ -40,11 +40,11 @@ pub fn day15() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_on_map(
|
fn move_on_map(
|
||||||
map: &Map<Terrain>,
|
map: &World<Terrain>,
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize,
|
y: usize,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
) -> Option<Map<Terrain>> {
|
) -> Option<World<Terrain>> {
|
||||||
let cascading_move = || {
|
let cascading_move = || {
|
||||||
let (nx, ny) = map.travel(x, y, direction)?;
|
let (nx, ny) = map.travel(x, y, direction)?;
|
||||||
let mut downstream = move_on_map(map, nx, ny, direction)?;
|
let mut downstream = move_on_map(map, nx, ny, direction)?;
|
||||||
|
@ -82,7 +82,7 @@ fn move_on_map(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn double_wide_map(map: Map<Terrain>) -> Map<Terrain> {
|
fn double_wide_map(map: World<Terrain>) -> World<Terrain> {
|
||||||
let new_map = map
|
let new_map = map
|
||||||
.iter_rows()
|
.iter_rows()
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
|
@ -100,7 +100,7 @@ fn double_wide_map(map: Map<Terrain>) -> Map<Terrain> {
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Map::from_2d_vec(new_map)
|
World::from_2d_vec(new_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
|
@ -129,7 +129,7 @@ impl Display for Terrain {
|
||||||
#[grammar = "grammars/day15.pest"]
|
#[grammar = "grammars/day15.pest"]
|
||||||
struct Day15Parser {}
|
struct Day15Parser {}
|
||||||
|
|
||||||
fn parse(input: &str) -> (Map<Terrain>, Vec<Direction>) {
|
fn parse(input: &str) -> (World<Terrain>, Vec<Direction>) {
|
||||||
let (map, directions) = Day15Parser::parse(Rule::input, input)
|
let (map, directions) = Day15Parser::parse(Rule::input, input)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.nth(0)
|
.nth(0)
|
||||||
|
@ -142,7 +142,7 @@ fn parse(input: &str) -> (Map<Terrain>, Vec<Direction>) {
|
||||||
(parse_map(map), parse_directions(directions))
|
(parse_map(map), parse_directions(directions))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_map(input: Pairs<'_, Rule>) -> Map<Terrain> {
|
fn parse_map(input: Pairs<'_, Rule>) -> World<Terrain> {
|
||||||
let map_vec = input
|
let map_vec = input
|
||||||
.map(Pair::<'_, Rule>::into_inner)
|
.map(Pair::<'_, Rule>::into_inner)
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
|
@ -156,7 +156,7 @@ fn parse_map(input: Pairs<'_, Rule>) -> Map<Terrain> {
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Map::from_2d_vec(map_vec)
|
World::from_2d_vec(map_vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_directions(input: Pairs<'_, Rule>) -> Vec<Direction> {
|
fn parse_directions(input: Pairs<'_, Rule>) -> Vec<Direction> {
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
use crate::util::maps::*;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub fn day16() {
|
||||||
|
let input: World<char> = World::from_string(&crate::input(16));
|
||||||
|
let maze: World<bool> = input.map(|&c| c == '.' || c == 'S' || c == 'E');
|
||||||
|
|
||||||
|
let start: Transform = input
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, _, &c)| c == 'S')
|
||||||
|
.map(|(x, y, _)| Transform::new(x, y, Direction::East, &maze))
|
||||||
|
.unwrap();
|
||||||
|
let end: (usize, usize) = input
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, _, &c)| c == 'E')
|
||||||
|
.map(|(x, y, _)| (x, y))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (cheapest_cost, paths) = cheapest_paths(start, end);
|
||||||
|
|
||||||
|
let tiles_crossed: usize = paths
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.sorted_by_key(|p| (p.x, p.y))
|
||||||
|
.dedup_by(|a, b| a.x == b.x && a.y == b.y)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
println!("Found {} shortest paths.", paths.len());
|
||||||
|
println!("Cheapest Path to End: {}", cheapest_cost);
|
||||||
|
println!("Tiles Crossed: {}", tiles_crossed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cheapest_paths(start: Transform, end: (usize, usize)) -> (u64, Vec<Vec<Transform>>) {
|
||||||
|
let paths: Vec<(Vec<Transform>, u64)> = all_paths(start, end)
|
||||||
|
.into_iter()
|
||||||
|
// path -> (path, cost)
|
||||||
|
.map(|path| {
|
||||||
|
let cost = path.windows(2).into_iter().fold(0, |acc, w| {
|
||||||
|
acc + if w[0].direction == w[1].direction {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
1000
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(path, cost)
|
||||||
|
})
|
||||||
|
// keep only the paths tied for cheapest
|
||||||
|
.min_set_by_key(|(_, cost)| *cost);
|
||||||
|
let cheapest_cost = paths[0].1;
|
||||||
|
// (path, cost) -> path
|
||||||
|
let paths = paths.into_iter().map(|(path, _)| path).collect();
|
||||||
|
(cheapest_cost, paths)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_paths(start: Transform, end: (usize, usize)) -> Vec<Vec<Transform>> {
|
||||||
|
let costs = maze_costs(start);
|
||||||
|
|
||||||
|
fn collect_paths<'a, 'b>(
|
||||||
|
costs: &'b HashMap<Transform<'a>, u64>,
|
||||||
|
p: Transform<'a>,
|
||||||
|
) -> Vec<Vec<Transform<'a>>> {
|
||||||
|
let cost_p = costs[&p];
|
||||||
|
|
||||||
|
// cost 0 means this is the start
|
||||||
|
if cost_p == 0 {
|
||||||
|
return vec![vec![p]];
|
||||||
|
};
|
||||||
|
// otherwise we look at all ways that we might get to p
|
||||||
|
p.comes_from()
|
||||||
|
.into_iter()
|
||||||
|
// no cost stored means that x is a silly place to ever come from (a dead end)
|
||||||
|
.filter(|x| costs.contains_key(x))
|
||||||
|
// if cost_x < cost_p, x most be closer to the start than p is
|
||||||
|
// otherwise, we don't want to check x (that would be anti-progress)
|
||||||
|
.filter(|x| costs[x] < cost_p)
|
||||||
|
.flat_map(|x| {
|
||||||
|
// now we recur, getting all paths from start to x
|
||||||
|
let mut paths = collect_paths(costs, x);
|
||||||
|
// and add ourself to the paths, then send the paths up the chain
|
||||||
|
paths.iter_mut().for_each(|path| path.push(p));
|
||||||
|
paths
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// every facing direction at the end may have a different cost
|
||||||
|
let endpoints = costs.keys().filter(|t| t.x == end.0 && t.y == end.1);
|
||||||
|
endpoints
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|&endpoint| collect_paths(&costs, endpoint))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maze_costs(start: Transform) -> HashMap<Transform, u64> {
|
||||||
|
fn calculate_costs<'a, 'b>(costs: &'b mut HashMap<Transform<'a>, u64>, p: Transform<'a>) {
|
||||||
|
// cost to any x adjacent to p is the cost to get to p + the cost to travel p -> x
|
||||||
|
// and if that's cheaper than any previous way we've found, repeat for each x as the new p
|
||||||
|
for (travel_cost, adjacent) in p.goes_to() {
|
||||||
|
let total_cost = costs[&p] + travel_cost;
|
||||||
|
let existing_cost = costs.get(&adjacent).copied().unwrap_or(u64::MAX);
|
||||||
|
if total_cost < existing_cost {
|
||||||
|
costs.insert(adjacent, total_cost);
|
||||||
|
calculate_costs(costs, adjacent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut costs = HashMap::new();
|
||||||
|
costs.insert(start, 0);
|
||||||
|
calculate_costs(&mut costs, start);
|
||||||
|
costs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Transform<'a> {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
direction: Direction,
|
||||||
|
map: &'a World<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Transform<'a> {
|
||||||
|
fn new(x: usize, y: usize, direction: Direction, map: &'a World<bool>) -> Self {
|
||||||
|
Self {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
direction,
|
||||||
|
map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goes_to(self) -> Vec<(u64, Transform<'a>)> {
|
||||||
|
let mut ret: Vec<(u64, Transform)> = vec![
|
||||||
|
self.direction.clockwise(),
|
||||||
|
self.direction.counterclockwise(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|d| self.map.travel_get(self.x, self.y, *d) == Some(true))
|
||||||
|
.map(|d| (1000, Self::new(self.x, self.y, d, self.map)))
|
||||||
|
.collect();
|
||||||
|
if let Some((nx, ny, true)) = self.map.travel_and_get(self.x, self.y, self.direction) {
|
||||||
|
ret.push((1, Self::new(nx, ny, self.direction, self.map)));
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn comes_from(self) -> Vec<Transform<'a>> {
|
||||||
|
let mut ret: Vec<Transform<'a>> = vec![
|
||||||
|
self.direction.clockwise(),
|
||||||
|
self.direction.counterclockwise(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| Self::new(self.x, self.y, d, self.map))
|
||||||
|
.collect();
|
||||||
|
let backward = self
|
||||||
|
.map
|
||||||
|
.travel_and_get(self.x, self.y, self.direction.opposite());
|
||||||
|
if let Some((nx, ny, true)) = backward {
|
||||||
|
ret.push(Self::new(nx, ny, self.direction, self.map));
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq for Transform<'a> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
std::ptr::eq(self.map, other.map)
|
||||||
|
&& self.x == other.x
|
||||||
|
&& self.y == other.y
|
||||||
|
&& self.direction == other.direction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Eq for Transform<'a> {}
|
||||||
|
|
||||||
|
impl<'a> std::hash::Hash for Transform<'a> {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.x.hash(state);
|
||||||
|
self.y.hash(state);
|
||||||
|
self.direction.hash(state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,10 +28,12 @@ mod day12;
|
||||||
mod day13;
|
mod day13;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod day14;
|
mod day14;
|
||||||
|
#[allow(dead_code)]
|
||||||
mod day15;
|
mod day15;
|
||||||
|
mod day16;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
day15::day15();
|
day16::day16();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(day: u8) -> String {
|
pub fn input(day: u8) -> String {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::fmt::Display;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
North,
|
North,
|
||||||
South,
|
South,
|
||||||
|
@ -27,19 +27,37 @@ impl Direction {
|
||||||
pub fn is_parallel(self, other: Self) -> bool {
|
pub fn is_parallel(self, other: Self) -> bool {
|
||||||
self == other || self == other.opposite()
|
self == other || self == other.opposite()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::North => Self::East,
|
||||||
|
Self::East => Self::South,
|
||||||
|
Self::South => Self::West,
|
||||||
|
Self::West => Self::North,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn counterclockwise(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::North => Self::West,
|
||||||
|
Self::West => Self::South,
|
||||||
|
Self::South => Self::East,
|
||||||
|
Self::East => Self::North,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Map<T>
|
pub struct World<T>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: Copy,
|
||||||
{
|
{
|
||||||
pub map: Vec<Vec<T>>,
|
pub map: Vec<Vec<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map<char> {
|
impl World<char> {
|
||||||
pub fn from_string(string: &str) -> Self {
|
pub fn from_string(string: &str) -> Self {
|
||||||
Map::from_2d_vec(
|
World::from_2d_vec(
|
||||||
string
|
string
|
||||||
.lines()
|
.lines()
|
||||||
.filter(|line| !line.is_empty())
|
.filter(|line| !line.is_empty())
|
||||||
|
@ -49,7 +67,7 @@ impl Map<char> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Map<T>
|
impl<T> World<T>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: Copy,
|
||||||
{
|
{
|
||||||
|
@ -134,9 +152,14 @@ where
|
||||||
pub fn enumerate(&self) -> impl Iterator<Item = (usize, usize, &T)> {
|
pub fn enumerate(&self) -> impl Iterator<Item = (usize, usize, &T)> {
|
||||||
self.coordinates().map(|(x, y)| (x, y, &self.map[y][x]))
|
self.coordinates().map(|(x, y)| (x, y, &self.map[y][x]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map<U: Copy, F: Fn(&T) -> U>(&self, f: F) -> World<U> {
|
||||||
|
let v = self.map.iter().map(|row| row.iter().map(|e| f(e)).collect()).collect();
|
||||||
|
World::<U>::from_2d_vec(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Display for Map<T>
|
impl<T> Display for World<T>
|
||||||
where
|
where
|
||||||
T: Copy + Display,
|
T: Copy + Display,
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue