wat heeft deze guy met plattegrondjes, aoc dag 15

This commit is contained in:
2024-12-18 17:35:58 +01:00
parent 0a039cb562
commit 98a82b523e
4 changed files with 491 additions and 0 deletions

View File

@@ -0,0 +1,392 @@
use std::collections::HashSet;
use nom::{
branch::alt,
character::complete::{char, multispace1},
multi::{many1, separated_list1},
sequence::separated_pair,
IResult, Parser,
};
#[derive(Debug, Clone, PartialEq, Eq)]
enum Tile {
Empty,
Wall,
Box,
Player,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum UpgradedTile {
Empty,
Wall,
Player,
LeftBox,
RightBox,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Move {
Up,
Right,
Down,
Left,
}
fn parse(input: &str) -> IResult<&str, (Vec<Vec<Tile>>, Vec<Move>)> {
let (input, result) = separated_pair(
separated_list1(
multispace1,
many1(alt((char('#'), char('.'), char('@'), char('O'))).map(|c| {
use Tile::*;
match c {
'#' => Wall,
'.' => Empty,
'@' => Player,
'O' => Box,
_ => unreachable!(),
}
})),
),
multispace1,
separated_list1(
multispace1,
many1(alt((char('^'), char('>'), char('<'), char('v'))).map(|c| {
use Move::*;
match c {
'^' => Up,
'>' => Right,
'<' => Left,
'v' => Down,
_ => unreachable!(),
}
})),
)
.map(|x| x.into_iter().flatten().collect()),
)(input)?;
Ok((input, result))
}
fn calc_score(map: &Vec<Vec<Tile>>) -> u64 {
let mut score = 0;
for (j, row) in map.iter().enumerate() {
for (i, t) in row.iter().enumerate() {
if *t == Tile::Box {
score += 100 * j + i;
}
}
}
score as u64
}
fn calc_upgraded_score(map: &Vec<Vec<UpgradedTile>>) -> u64 {
let mut score = 0;
for (j, row) in map.iter().enumerate() {
for (i, t) in row.iter().enumerate() {
if *t == UpgradedTile::LeftBox {
score += 100 * j + i;
}
}
}
score as u64
}
fn move_one(coord: &(usize, usize), m: &Move) -> (usize, usize) {
use Move::*;
let (x, y) = *coord;
match m {
Up => (x, y - 1),
Right => (x + 1, y),
Down => (x, y + 1),
Left => (x - 1, y),
}
}
fn move_player(map: &mut Vec<Vec<Tile>>, pos: &mut (usize, usize), m: Move) {
use Tile::*;
let (x, y) = *pos;
assert_eq!(map[y][x], Player);
let (tx, ty) = move_one(&pos, &m);
match map[ty][tx] {
Empty => {
map[y][x] = Empty;
map[ty][tx] = Player;
*pos = (tx, ty);
}
Wall => {}
Box => {
let (mut bx, mut by) = (tx, ty);
while map[by][bx] == Box {
(bx, by) = move_one(&(bx, by), &m);
}
match map[by][bx] {
Empty => {
map[by][bx] = Box;
map[ty][tx] = Player;
map[y][x] = Empty;
*pos = (tx, ty);
}
Wall => {}
Box => unreachable!(),
Player => unreachable!(),
}
}
Player => unreachable!(),
}
}
fn solve_1(input: &str) -> u64 {
use Tile::Player;
let (_, (mut map, moves)) = parse(input).unwrap();
let (mut x, mut y) = (0, 0);
for (j, row) in map.iter().enumerate() {
let mut found = false;
for (i, t) in row.iter().enumerate() {
if *t == Player {
x = i;
y = j;
found = true;
break;
}
}
if found {
break;
}
}
let mut pos = (x, y);
for m in moves {
move_player(&mut map, &mut pos, m);
}
calc_score(&map)
}
fn upgrade_map(map: &Vec<Vec<Tile>>) -> Vec<Vec<UpgradedTile>> {
let mut new_map = vec![];
for row in map {
let mut new_row = vec![];
for t in row {
match t {
Tile::Empty => {
new_row.push(UpgradedTile::Empty);
new_row.push(UpgradedTile::Empty);
}
Tile::Wall => {
new_row.push(UpgradedTile::Wall);
new_row.push(UpgradedTile::Wall);
}
Tile::Box => {
new_row.push(UpgradedTile::LeftBox);
new_row.push(UpgradedTile::RightBox);
}
Tile::Player => {
new_row.push(UpgradedTile::Player);
new_row.push(UpgradedTile::Empty);
}
}
}
new_map.push(new_row);
}
new_map
}
fn rev_move(m: &Move) -> Move {
use Move::*;
match m {
Up => Down,
Right => Left,
Down => Up,
Left => Right,
}
}
fn upgraded_move_player(map: &mut Vec<Vec<UpgradedTile>>, pos: &mut (usize, usize), m: Move) {
use Move::*;
use UpgradedTile::*;
let (x, y) = *pos;
assert_eq!(map[y][x], Player);
let (tx, ty) = move_one(&pos, &m);
// These are all the tiles that need to be checked yet
let mut stack = vec![];
// These are all the tiles we have checked, in order to shift them if needed
let mut visited = HashSet::new();
match map[ty][tx] {
Empty => {}
Wall => return,
Player => unreachable!(),
LeftBox => {
if m == Up || m == Down {
stack.push((tx, ty));
stack.push((tx + 1, ty));
visited.insert(((tx, ty), LeftBox));
visited.insert(((tx + 1, ty), RightBox));
} else if m == Right {
let (mut bx, mut by) = (tx, ty);
while map[by][bx] == LeftBox || map[by][bx] == RightBox {
visited.insert(((bx, by), map[by][bx].clone()));
(bx, by) = move_one(&(bx, by), &m);
}
match map[by][bx] {
Empty => {}
Wall => return,
_ => unreachable!(),
}
} else {
unreachable!();
}
}
RightBox => {
if m == Up || m == Down {
stack.push((tx, ty));
stack.push((tx - 1, ty));
visited.insert(((tx, ty), RightBox));
visited.insert(((tx - 1, ty), LeftBox));
} else if m == Left {
let (mut bx, mut by) = (tx, ty);
while map[by][bx] == LeftBox || map[by][bx] == RightBox {
visited.insert(((bx, by), map[by][bx].clone()));
(bx, by) = move_one(&(bx, by), &m);
}
match map[by][bx] {
Empty => {}
Wall => return,
_ => unreachable!(),
}
} else {
unreachable!();
}
}
}
while !stack.is_empty() {
let (sx, sy) = stack.pop().unwrap_or_default();
let (bx, by) = move_one(&(sx, sy), &m);
match map[by][bx] {
Empty => {}
Wall => return,
Player => unreachable!(),
LeftBox => {
if visited.insert(((bx, by), LeftBox)) {
stack.push((bx, by));
}
if visited.insert(((bx + 1, by), RightBox)) {
stack.push((bx + 1, by));
}
}
RightBox => {
if visited.insert(((bx, by), RightBox)) {
stack.push((bx, by));
}
if visited.insert(((bx - 1, by), LeftBox)) {
stack.push((bx - 1, by));
}
}
}
}
let old_map = map.clone();
// Because of early returns in case of wall and empty and the empty stack in the case of a
// horizontal move, we can always assume we hit a box somewhere along the way and we have
// to move all those boxes here
for ((vx, vy), vt) in visited.iter() {
let (px, py) = move_one(&(*vx, *vy), &rev_move(&m));
if !visited.contains(&((px, py), old_map[py][px].clone())) {
map[*vy][*vx] = Empty;
}
let (nx, ny) = move_one(&(*vx, *vy), &m);
map[ny][nx] = vt.clone();
}
map[y][x] = Empty;
map[ty][tx] = Player;
*pos = (tx, ty);
}
fn solve_2(input: &str) -> u64 {
use UpgradedTile::Player;
let (_, (map, moves)) = parse(input).unwrap();
let mut map = upgrade_map(&map);
let (mut x, mut y) = (0, 0);
for (j, row) in map.iter().enumerate() {
let mut found = false;
for (i, t) in row.iter().enumerate() {
if *t == Player {
x = i;
y = j;
found = true;
break;
}
}
if found {
break;
}
}
let mut pos = (x, y);
for m in moves {
upgraded_move_player(&mut map, &mut pos, m);
}
calc_upgraded_score(&map)
}
fn main() {
println!("Hello, this is Patrick!");
let input = include_str!("../input.txt");
let result_1 = solve_1(input);
println!("The sum of all boxes' GPS coordinates is {}", result_1);
let result_2 = solve_2(input);
println!("The sum of all big boxes' GPS coordinates is {}", 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), 10092);
}
#[test]
fn test_2() {
let test_input = include_str!("../test.txt");
assert_eq!(solve_2(test_input), 9021);
}
}