Day 15
This commit is contained in:
parent
290789d5f5
commit
ea1bbcc089
|
@ -0,0 +1,172 @@
|
|||
use crate::util::maps::*;
|
||||
use itertools::Itertools;
|
||||
use pest::{
|
||||
iterators::{Pair, Pairs},
|
||||
Parser,
|
||||
};
|
||||
use pest_derive::Parser;
|
||||
use std::fmt::Display;
|
||||
|
||||
const DOUBLE_WIDE: bool = true;
|
||||
|
||||
pub fn day15() {
|
||||
let (mut map, directions) = parse(&crate::input(15));
|
||||
if DOUBLE_WIDE {
|
||||
map = double_wide_map(map);
|
||||
}
|
||||
|
||||
let (mut x, mut y, _) = map
|
||||
.enumerate()
|
||||
.find(|(_, _, &t)| t == Terrain::Robot)
|
||||
.unwrap();
|
||||
|
||||
for direction in directions {
|
||||
if let Some(new_map) = move_on_map(&map, x, y, direction) {
|
||||
map = new_map;
|
||||
(x, y) = map.travel(x, y, direction).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let sum_gps: usize = map
|
||||
.enumerate()
|
||||
.filter(|(_, _, &t)| {
|
||||
t == Terrain::Block(None) || t == Terrain::Block(Some(Direction::East))
|
||||
})
|
||||
.map(|(x, y, _)| y * 100 + x)
|
||||
.sum();
|
||||
|
||||
println!("{}", map);
|
||||
println!("Sum of GPS Coordinates: {}", sum_gps);
|
||||
}
|
||||
|
||||
fn move_on_map(
|
||||
map: &Map<Terrain>,
|
||||
x: usize,
|
||||
y: usize,
|
||||
direction: Direction,
|
||||
) -> Option<Map<Terrain>> {
|
||||
let cascading_move = || {
|
||||
let (nx, ny) = map.travel(x, y, direction)?;
|
||||
let mut downstream = move_on_map(map, nx, ny, direction)?;
|
||||
downstream.set(nx, ny, map.get(x, y).unwrap());
|
||||
downstream.set(x, y, Terrain::Empty);
|
||||
Some(downstream)
|
||||
};
|
||||
|
||||
match map.get(x, y).unwrap() {
|
||||
Terrain::Wall => None,
|
||||
Terrain::Empty => Some(map.clone()),
|
||||
Terrain::Block(None) => (cascading_move)(),
|
||||
Terrain::Block(Some(d)) if d.is_parallel(direction) => (cascading_move)(),
|
||||
Terrain::Robot => (cascading_move)(),
|
||||
Terrain::Block(Some(Direction::West)) => {
|
||||
let (partner_x, partner_y) = map.travel(x, y, Direction::West).unwrap();
|
||||
move_on_map(map, partner_x, partner_y, direction)
|
||||
}
|
||||
Terrain::Block(Some(Direction::East)) => {
|
||||
let (partner_x, partner_y) = map.travel(x, y, Direction::East).unwrap();
|
||||
|
||||
let mut downstream = (cascading_move)()?;
|
||||
let (partner_nx, partner_ny) = map.travel(partner_x, partner_y, direction).unwrap();
|
||||
downstream = move_on_map(&downstream, partner_nx, partner_ny, direction)?;
|
||||
downstream.set(
|
||||
partner_nx,
|
||||
partner_ny,
|
||||
map.get(partner_x, partner_y).unwrap(),
|
||||
);
|
||||
downstream.set(partner_x, partner_y, Terrain::Empty);
|
||||
|
||||
Some(downstream)
|
||||
}
|
||||
Terrain::Block(Some(_)) => unimplemented!("North/South blocks are not implemented."),
|
||||
}
|
||||
}
|
||||
|
||||
fn double_wide_map(map: Map<Terrain>) -> Map<Terrain> {
|
||||
let new_map = map
|
||||
.iter_rows()
|
||||
.map(|row| {
|
||||
row.iter()
|
||||
.flat_map(|&t| match t {
|
||||
Terrain::Wall => vec![Terrain::Wall; 2],
|
||||
Terrain::Empty => vec![Terrain::Empty; 2],
|
||||
Terrain::Block(None) => vec![
|
||||
Terrain::Block(Some(Direction::East)),
|
||||
Terrain::Block(Some(Direction::West)),
|
||||
],
|
||||
Terrain::Robot => vec![Terrain::Robot, Terrain::Empty],
|
||||
Terrain::Block(Some(_)) => unreachable!(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
Map::from_2d_vec(new_map)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
enum Terrain {
|
||||
Wall,
|
||||
Empty,
|
||||
Block(Option<Direction>),
|
||||
Robot,
|
||||
}
|
||||
|
||||
impl Display for Terrain {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self {
|
||||
Terrain::Robot => "@",
|
||||
Terrain::Wall => "#",
|
||||
Terrain::Empty => ".",
|
||||
Terrain::Block(Some(Direction::East)) => "[",
|
||||
Terrain::Block(Some(Direction::West)) => "]",
|
||||
Terrain::Block(_) => "O",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "grammars/day15.pest"]
|
||||
struct Day15Parser {}
|
||||
|
||||
fn parse(input: &str) -> (Map<Terrain>, Vec<Direction>) {
|
||||
let (map, directions) = Day15Parser::parse(Rule::input, input)
|
||||
.unwrap()
|
||||
.nth(0)
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.map(Pair::<'_, Rule>::into_inner)
|
||||
.collect_tuple()
|
||||
.unwrap();
|
||||
|
||||
(parse_map(map), parse_directions(directions))
|
||||
}
|
||||
|
||||
fn parse_map(input: Pairs<'_, Rule>) -> Map<Terrain> {
|
||||
let map_vec = input
|
||||
.map(Pair::<'_, Rule>::into_inner)
|
||||
.map(|row| {
|
||||
row.map(|terrain| match terrain.as_rule() {
|
||||
Rule::wall => Terrain::Wall,
|
||||
Rule::empty => Terrain::Empty,
|
||||
Rule::block => Terrain::Block(None),
|
||||
Rule::robot => Terrain::Robot,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
Map::from_2d_vec(map_vec)
|
||||
}
|
||||
|
||||
fn parse_directions(input: Pairs<'_, Rule>) -> Vec<Direction> {
|
||||
input
|
||||
.map(|d| match d.as_rule() {
|
||||
Rule::north => Direction::North,
|
||||
Rule::west => Direction::West,
|
||||
Rule::east => Direction::East,
|
||||
Rule::south => Direction::South,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect()
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
_WHITESPACE = _{ " " }
|
||||
NEWLINE = _{ "\n" }
|
||||
|
||||
wall = @{ "#" }
|
||||
empty = @{ "." }
|
||||
block = @{ "O" }
|
||||
robot = @{ "@" }
|
||||
space = _{ wall | empty | block | robot }
|
||||
row = { space+ ~ NEWLINE+ }
|
||||
map = { row+ }
|
||||
|
||||
north = @{ "^" }
|
||||
west = @{ "<" }
|
||||
east = @{ ">" }
|
||||
south = @{ "v" }
|
||||
direction_list = { ((north | west | east | south) ~ NEWLINE*)+ }
|
||||
|
||||
eoi = _{ !ANY }
|
||||
input = { SOI ~ map ~ direction_list ~ eoi }
|
|
@ -26,10 +26,12 @@ mod day11;
|
|||
mod day12;
|
||||
#[allow(dead_code)]
|
||||
mod day13;
|
||||
#[allow(dead_code)]
|
||||
mod day14;
|
||||
mod day15;
|
||||
|
||||
fn main() {
|
||||
day14::day14();
|
||||
day15::day15();
|
||||
}
|
||||
|
||||
pub fn input(day: u8) -> String {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Direction {
|
||||
North,
|
||||
South,
|
||||
|
@ -12,12 +14,25 @@ impl Direction {
|
|||
pub fn all_variants() -> Vec<Self> {
|
||||
vec![Self::North, Self::East, Self::West, Self::South]
|
||||
}
|
||||
|
||||
pub fn opposite(self) -> Self {
|
||||
match self {
|
||||
Self::North => Self::South,
|
||||
Self::East => Self::West,
|
||||
Self::West => Self::East,
|
||||
Self::South => Self::North,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_parallel(self, other: Self) -> bool {
|
||||
self == other || self == other.opposite()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Map<T>
|
||||
where
|
||||
T: Copy,
|
||||
T: Copy + Display,
|
||||
{
|
||||
pub map: Vec<Vec<T>>,
|
||||
}
|
||||
|
@ -36,7 +51,7 @@ impl Map<char> {
|
|||
|
||||
impl<T> Map<T>
|
||||
where
|
||||
T: Copy,
|
||||
T: Copy + Display,
|
||||
{
|
||||
pub fn from_2d_vec(map: Vec<Vec<T>>) -> Self {
|
||||
Self { map }
|
||||
|
@ -69,13 +84,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize) -> Option<T> {
|
||||
Some(*self.map.get(y)?.get(x)?)
|
||||
}
|
||||
|
||||
pub fn travel_get(&self, x: usize, y: usize, direction: Direction) -> Option<T> {
|
||||
let (nx, ny) = self.travel(x, y, direction)?;
|
||||
self.get(nx, ny)
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize) -> Option<T> {
|
||||
Some(*self.map.get(y)?.get(x)?)
|
||||
pub fn travel_and_get(
|
||||
&self,
|
||||
x: usize,
|
||||
y: usize,
|
||||
direction: Direction,
|
||||
) -> Option<(usize, usize, T)> {
|
||||
let (nx, ny) = self.travel(x, y, direction)?;
|
||||
Some((nx, ny, self.get(nx, ny)?))
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: usize, y: usize, value: T) {
|
||||
|
@ -98,6 +123,10 @@ where
|
|||
self.map.iter().flat_map(|row| row.iter())
|
||||
}
|
||||
|
||||
pub fn iter_rows(&self) -> impl Iterator<Item = &Vec<T>> {
|
||||
self.map.iter()
|
||||
}
|
||||
|
||||
pub fn coordinates(&self) -> impl Iterator<Item = (usize, usize)> {
|
||||
(0..self.width()).cartesian_product(0..self.height())
|
||||
}
|
||||
|
@ -106,3 +135,21 @@ where
|
|||
self.coordinates().map(|(x, y)| (x, y, &self.map[y][x]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Display for Map<T>
|
||||
where
|
||||
T: Copy + Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut final_string = "".to_string();
|
||||
for row in self.map.iter() {
|
||||
let s = row.iter().fold("".to_string(), |mut acc, e| {
|
||||
acc.push_str(&format!("{}", e));
|
||||
acc
|
||||
});
|
||||
final_string.push_str("\n");
|
||||
final_string.push_str(&s);
|
||||
}
|
||||
f.write_str(&final_string)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue