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;
|
mod day12;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod day13;
|
mod day13;
|
||||||
|
#[allow(dead_code)]
|
||||||
mod day14;
|
mod day14;
|
||||||
|
mod day15;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
day14::day14();
|
day15::day15();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(day: u8) -> String {
|
pub fn input(day: u8) -> String {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
North,
|
North,
|
||||||
South,
|
South,
|
||||||
|
@ -12,12 +14,25 @@ impl Direction {
|
||||||
pub fn all_variants() -> Vec<Self> {
|
pub fn all_variants() -> Vec<Self> {
|
||||||
vec![Self::North, Self::East, Self::West, Self::South]
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct Map<T>
|
pub struct Map<T>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: Copy + Display,
|
||||||
{
|
{
|
||||||
pub map: Vec<Vec<T>>,
|
pub map: Vec<Vec<T>>,
|
||||||
}
|
}
|
||||||
|
@ -36,7 +51,7 @@ impl Map<char> {
|
||||||
|
|
||||||
impl<T> Map<T>
|
impl<T> Map<T>
|
||||||
where
|
where
|
||||||
T: Copy,
|
T: Copy + Display,
|
||||||
{
|
{
|
||||||
pub fn from_2d_vec(map: Vec<Vec<T>>) -> Self {
|
pub fn from_2d_vec(map: Vec<Vec<T>>) -> Self {
|
||||||
Self { map }
|
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> {
|
pub fn travel_get(&self, x: usize, y: usize, direction: Direction) -> Option<T> {
|
||||||
let (nx, ny) = self.travel(x, y, direction)?;
|
let (nx, ny) = self.travel(x, y, direction)?;
|
||||||
self.get(nx, ny)
|
self.get(nx, ny)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, x: usize, y: usize) -> Option<T> {
|
pub fn travel_and_get(
|
||||||
Some(*self.map.get(y)?.get(x)?)
|
&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) {
|
pub fn set(&mut self, x: usize, y: usize, value: T) {
|
||||||
|
@ -98,6 +123,10 @@ where
|
||||||
self.map.iter().flat_map(|row| row.iter())
|
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)> {
|
pub fn coordinates(&self) -> impl Iterator<Item = (usize, usize)> {
|
||||||
(0..self.width()).cartesian_product(0..self.height())
|
(0..self.width()).cartesian_product(0..self.height())
|
||||||
}
|
}
|
||||||
|
@ -106,3 +135,21 @@ where
|
||||||
self.coordinates().map(|(x, y)| (x, y, &self.map[y][x]))
|
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