Solve 15
This commit is contained in:
parent
271d764144
commit
39dd52b5fd
1 changed files with 215 additions and 0 deletions
215
src/bin/15.rs
Normal file
215
src/bin/15.rs
Normal file
|
@ -0,0 +1,215 @@
|
|||
use aoc::*;
|
||||
use glam::IVec2;
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
|
||||
const INPUT: &str = include_str!("../../input/15");
|
||||
|
||||
fn main() {
|
||||
assert_example!(part1, "15-test", 10092);
|
||||
println!("Part 1: {}", part1(INPUT));
|
||||
assert_example!(part2, "15-test", 9021);
|
||||
println!("Part 2: {}", part2(INPUT));
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> i32 {
|
||||
let (mut warehouse, directions) = parse(input, false);
|
||||
warehouse.process(directions);
|
||||
warehouse.gps()
|
||||
}
|
||||
|
||||
fn part2(input: &str) -> i32 {
|
||||
let (mut warehouse, directions) = parse(input, true);
|
||||
warehouse.process(directions);
|
||||
warehouse.gps()
|
||||
}
|
||||
|
||||
fn parse(input: &str, expand: bool) -> (Warehouse, Vec<Direction>) {
|
||||
let (warehouse, directions) = input.split_once("\n\n").unwrap();
|
||||
let warehouse = if expand {
|
||||
let warehouse = expand_warehouse(warehouse);
|
||||
Warehouse::parse(&warehouse)
|
||||
} else {
|
||||
Warehouse::parse(warehouse)
|
||||
};
|
||||
let directions = directions
|
||||
.lines()
|
||||
.flat_map(str::chars)
|
||||
.map(Direction::from)
|
||||
.collect();
|
||||
(warehouse, directions)
|
||||
}
|
||||
|
||||
fn expand_warehouse(warehouse: &str) -> String {
|
||||
warehouse
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'#' => "##",
|
||||
'O' => "[]",
|
||||
'.' => "..",
|
||||
'@' => "@.",
|
||||
'\n' => "\n",
|
||||
other => panic!("unexpected char '{other}'"),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
enum Tile {
|
||||
Wall,
|
||||
Box,
|
||||
BoxL,
|
||||
BoxR,
|
||||
}
|
||||
|
||||
struct Warehouse {
|
||||
tiles: HashMap<IVec2, Tile>,
|
||||
robot: IVec2,
|
||||
}
|
||||
|
||||
impl Warehouse {
|
||||
fn process(&mut self, directions: Vec<Direction>) {
|
||||
for dir in directions {
|
||||
self.do_move(dir);
|
||||
}
|
||||
}
|
||||
|
||||
fn do_move(&mut self, dir: Direction) {
|
||||
if !self.can_move(self.robot, dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.robot += dir.vec();
|
||||
self.push_boxes(self.robot, dir);
|
||||
}
|
||||
|
||||
fn push_boxes(&mut self, at: IVec2, dir: Direction) {
|
||||
match self.tiles.get(&at) {
|
||||
Some(Tile::Box) => {
|
||||
self.push_boxes(at + dir.vec(), dir);
|
||||
self.tiles.remove(&at);
|
||||
self.tiles.insert(at + dir.vec(), Tile::Box);
|
||||
}
|
||||
Some(Tile::BoxL) => {
|
||||
let r = at + IVec2::X;
|
||||
self.push_boxes(at + dir.vec(), dir);
|
||||
if dir.vertical() {
|
||||
self.push_boxes(r + dir.vec(), dir);
|
||||
}
|
||||
self.tiles.remove(&at);
|
||||
if dir.vertical() {
|
||||
self.tiles.remove(&r);
|
||||
}
|
||||
self.tiles.insert(at + dir.vec(), Tile::BoxL);
|
||||
if dir.vertical() {
|
||||
self.tiles.insert(r + dir.vec(), Tile::BoxR);
|
||||
}
|
||||
}
|
||||
Some(Tile::BoxR) => {
|
||||
let l = at - IVec2::X;
|
||||
self.push_boxes(at + dir.vec(), dir);
|
||||
if dir.vertical() {
|
||||
self.push_boxes(l + dir.vec(), dir);
|
||||
}
|
||||
self.tiles.remove(&at);
|
||||
if dir.vertical() {
|
||||
self.tiles.remove(&l);
|
||||
}
|
||||
self.tiles.insert(at + dir.vec(), Tile::BoxR);
|
||||
if dir.vertical() {
|
||||
self.tiles.insert(l + dir.vec(), Tile::BoxL);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn can_move(&self, from: IVec2, dir: Direction) -> bool {
|
||||
let next = from + dir.vec();
|
||||
match self.tiles.get(&next) {
|
||||
None => true,
|
||||
Some(Tile::Wall) => false,
|
||||
Some(Tile::BoxL) if dir.vertical() => {
|
||||
self.can_move(next, dir) && self.can_move(next + IVec2::X, dir)
|
||||
}
|
||||
Some(Tile::BoxR) if dir.vertical() => {
|
||||
self.can_move(next, dir) && self.can_move(next - IVec2::X, dir)
|
||||
}
|
||||
Some(Tile::Box | Tile::BoxL | Tile::BoxR) => self.can_move(next, dir),
|
||||
}
|
||||
}
|
||||
|
||||
fn gps(&self) -> i32 {
|
||||
self.tiles
|
||||
.iter()
|
||||
.map(|(&p, t)| match t {
|
||||
Tile::Box | Tile::BoxL => p.x + p.y * 100,
|
||||
_ => 0,
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> Self {
|
||||
let mut robot = IVec2::splat(-1);
|
||||
|
||||
let tiles = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| line.chars().enumerate().zip(iter::repeat(y)))
|
||||
.flat_map(|((x, c), y)| {
|
||||
let pos = IVec2::new(x as i32, y as i32);
|
||||
match c {
|
||||
'@' => {
|
||||
robot = pos;
|
||||
None
|
||||
}
|
||||
'.' => None,
|
||||
'#' => Some((pos, Tile::Wall)),
|
||||
'O' => Some((pos, Tile::Box)),
|
||||
'[' => Some((pos, Tile::BoxL)),
|
||||
']' => Some((pos, Tile::BoxR)),
|
||||
other => panic!("unexpected tile '{other}' at {pos}"),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self { tiles, robot }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn vec(self) -> IVec2 {
|
||||
match self {
|
||||
Direction::Up => IVec2::new(0, -1),
|
||||
Direction::Down => IVec2::new(0, 1),
|
||||
Direction::Left => IVec2::new(-1, 0),
|
||||
Direction::Right => IVec2::new(1, 0),
|
||||
}
|
||||
}
|
||||
|
||||
fn vertical(self) -> bool {
|
||||
match self {
|
||||
Direction::Up | Direction::Down => true,
|
||||
Direction::Left | Direction::Right => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Direction {
|
||||
fn from(c: char) -> Self {
|
||||
match c {
|
||||
'^' => Direction::Up,
|
||||
'v' => Direction::Down,
|
||||
'<' => Direction::Left,
|
||||
'>' => Direction::Right,
|
||||
other => panic!("unexpected direction '{other}'"),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue