Skip to content

Commit

Permalink
2024 Day 15 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
gchazot committed Dec 15, 2024
1 parent 56e1585 commit 8421d51
Showing 1 changed file with 187 additions and 68 deletions.
255 changes: 187 additions & 68 deletions year_2024/src/day15.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::collections::{HashSet, VecDeque};

pub fn execute() -> String {
let data = aoc_utils::read_lines("input/day15.txt");
let mut warehouse = Warehouse::from_lines(data);
let mut warehouse = Warehouse::from_lines(data.clone(), false);
while warehouse.progress() {}

let part1 = warehouse.checksum();
let part2 = 456;

let mut warehouse2 = Warehouse::from_lines(data, true);
while warehouse2.progress() {}
let part2 = warehouse2.checksum();

format!("{} {}", part1, part2)
}
Expand All @@ -14,18 +18,21 @@ enum Tile {
Wall,
Floor,
Box,
BoxLeft,
BoxRight,
}
struct Warehouse {
size: usize,
size: (usize, usize),
tiles: Vec<Vec<Tile>>,
robot: (usize, usize),
instructions: Vec<(isize, isize)>,
current: usize,
is_part2: bool,
}

impl Warehouse {
fn from_lines(lines: Vec<String>) -> Warehouse {
let size = lines[0].len();
fn from_lines(lines: Vec<String>, is_part2: bool) -> Warehouse {
let input_width = lines[0].len();
let mut tiles = vec![];

let mut robot = (0, 0);
Expand All @@ -37,29 +44,43 @@ impl Warehouse {
break;
}

assert_eq!(line.len(), size);

let row = line
.chars()
.enumerate()
.map(|(i, c)| match c {
'#' => Tile::Wall,
'O' => Tile::Box,
'.' => Tile::Floor,
'@' => {
robot.0 = i;
robot.1 = j;
Tile::Floor
}
_ => unreachable!("Should not be here but {:?}", (j, i)),
})
.collect::<Vec<_>>();
assert_eq!(line.len(), input_width);

let row_iter = line.chars().enumerate().map(|(i, c)| match c {
'#' => Tile::Wall,
'.' => Tile::Floor,
'O' => Tile::Box,
'@' => {
robot.0 = if is_part2 { i * 2 } else { i };
robot.1 = j;
Tile::Floor
}
_ => unreachable!("Should not be here but {:?}", (j, i)),
});

let row = if is_part2 {
row_iter
.flat_map(|tile| match tile {
Tile::Box => vec![Tile::BoxLeft, Tile::BoxRight],
other => vec![other.clone(), other],
})
.collect::<Vec<_>>()
} else {
row_iter.collect::<Vec<_>>()
};

tiles.push(row);

j += 1;
}

let width = if is_part2 {
input_width * 2
} else {
input_width
};
let size = (width, tiles.len());

let mut instructions = vec![];
while j < lines.len() {
let line = &lines[j];
Expand All @@ -82,49 +103,125 @@ impl Warehouse {
robot,
instructions,
current: 0,
is_part2,
}
}

fn next_floor(&self, from: (usize, usize), direction: (isize, isize)) -> isize {
let mut current = from;
let mut result = 0;
while current.0 > 0 && current.1 > 0 && current.0 < self.size && current.1 < self.size {
result += 1;
current.0 = (current.0 as isize + direction.0) as usize;
current.1 = (current.1 as isize + direction.1) as usize;

match self.tiles[current.1][current.0] {
Tile::Box => {
continue;
fn push(
&self,
from: (usize, usize),
direction: (isize, isize),
) -> Option<HashSet<(usize, usize)>> {
let mut result = HashSet::new();

if !self.is_part2 {
let mut next_floor_tile = 0;

let mut current = from;
while current.0 > 0
&& current.1 > 0
&& current.0 < self.size.0
&& current.1 < self.size.1
{
next_floor_tile += 1;
current.0 = (current.0 as isize + direction.0) as usize;
current.1 = (current.1 as isize + direction.1) as usize;

match self.tiles[current.1][current.0] {
Tile::Box => {
continue;
}
Tile::Floor => {
break;
}
Tile::Wall => {
return None;
}
Tile::BoxLeft | Tile::BoxRight => {
panic!("This code does not apply to wide boxes");
}
}
Tile::Floor => {
return result;
}

if next_floor_tile > 1 {
for i in 1..next_floor_tile {
result.insert((
(from.0 as isize + i * direction.0) as usize,
(from.1 as isize + i * direction.1) as usize,
));
}
Tile::Wall => {
return 0;
}
} else {
let mut to_check = VecDeque::from([from]);
while let Some(check) = to_check.pop_front() {
let next = (
(check.0 as isize + direction.0) as usize,
(check.1 as isize + direction.1) as usize,
);
let is_horizontal = direction.1 == 0;
match self.tiles[next.1][next.0] {
Tile::Box => {
panic!("This code does not apply to part 1");
}
Tile::Floor => {
continue;
}
Tile::Wall => {
return None;
}
Tile::BoxLeft | Tile::BoxRight if is_horizontal => {
result.insert(next);
to_check.push_back(next)
}
Tile::BoxRight if !is_horizontal => {
// RHS of a box => deal with it when dealing with LHS
let check_left = (check.0 - 1, check.1);
if !to_check.contains(&check_left) {
to_check.push_front(check_left);
}
}
Tile::BoxLeft if !is_horizontal => {
let next_right = (next.0 + 1, next.1);
result.insert(next);
result.insert(next_right);
to_check.push_back(next);
to_check.push_back(next_right);
}
_ => unreachable!("I think all cases are actually covered"),
}
}
}

return 0;
Some(result)
}

fn step(&mut self, from: (usize, usize), direction: (isize, isize)) -> (usize, usize) {
let distance = self.next_floor(from, direction);
if distance == 0 {
return from;
}
let mut new_position = from;

let result = (
(from.0 as isize + direction.0) as usize,
(from.1 as isize + direction.1) as usize,
);
if distance > 1 {
self.tiles[(from.1 as isize + distance * direction.1) as usize]
[(from.0 as isize + distance * direction.0) as usize] = Tile::Box;
self.tiles[result.1][result.0] = Tile::Floor;
let to_move = self.push(from, direction);

if let Some(to_move) = to_move {
let previous = to_move
.iter()
.map(|(i, j)| {
let prev_tile = self.tiles[*j][*i].clone();
self.tiles[*j][*i] = Tile::Floor;
(*i, *j, prev_tile)
})
.collect::<Vec<_>>();

for (i, j, tile) in previous.into_iter() {
self.tiles[(j as isize + direction.1) as usize]
[(i as isize + direction.0) as usize] = tile;
}

new_position = (
(from.0 as isize + direction.0) as usize,
(from.1 as isize + direction.1) as usize,
);
}
result

new_position
}

fn progress(&mut self) -> bool {
Expand All @@ -144,7 +241,9 @@ impl Warehouse {
.flat_map(|(j, row)| {
row.iter()
.enumerate()
.filter_map(|(i, tile)| matches!(tile, Tile::Box).then_some(i + 100 * j))
.filter_map(|(i, tile)| {
matches!(tile, Tile::Box | Tile::BoxLeft).then_some(i + 100 * j)
})
.collect::<Vec<_>>()
})
.sum()
Expand All @@ -155,7 +254,6 @@ impl Warehouse {
mod tests {
use super::*;
use std::fmt::Display;
use std::ops::IndexMut;

#[test]
fn test_mine() {
Expand All @@ -164,28 +262,42 @@ mod tests {

#[test]
fn test_from_lines() {
let warehouse = Warehouse::from_lines(_example());
assert_eq!(warehouse.size, 10);
let warehouse = Warehouse::from_lines(_example(), false);
assert_eq!(warehouse.size, (10, 10));
assert_eq!(warehouse.instructions.len(), 700);
assert_eq!(warehouse.robot, (4, 4));

let warehouse2 = Warehouse::from_lines(_example(), true);
assert_eq!(warehouse2.size, (20, 10));
assert_eq!(warehouse2.instructions.len(), 700);
assert_eq!(warehouse2.robot, (8, 4));
}

#[test]
fn test_next_floor() {
let warehouse = Warehouse::from_lines(_example());
assert_eq!(warehouse.next_floor((1, 1), (1, 0)), 1);
assert_eq!(warehouse.next_floor((1, 1), (0, 1)), 1);
assert_eq!(warehouse.next_floor((1, 1), (-1, 0)), 0);
assert_eq!(warehouse.next_floor((1, 1), (0, -1)), 0);
assert_eq!(warehouse.next_floor((3, 2), (0, -1)), 0);
assert_eq!(warehouse.next_floor((3, 2), (0, 1)), 3);
assert_eq!(warehouse.next_floor((4, 7), (-1, 0)), 3);
assert_eq!(warehouse.next_floor((4, 7), (1, 0)), 2);
fn test_push() {
let warehouse = Warehouse::from_lines(_example(), false);
assert_eq!(warehouse.push((1, 1), (1, 0)), Some(HashSet::from([])));
assert_eq!(warehouse.push((1, 1), (0, 1)), Some(HashSet::from([])));
assert_eq!(warehouse.push((1, 1), (-1, 0)), None);
assert_eq!(warehouse.push((1, 1), (0, -1)), None);
assert_eq!(warehouse.push((3, 2), (0, -1)), None);
assert_eq!(
warehouse.push((3, 2), (0, 1)),
Some(HashSet::from([(3, 3), (3, 4)]))
);
assert_eq!(
warehouse.push((4, 7), (-1, 0)),
Some(HashSet::from([(3, 7), (2, 7)]))
);
assert_eq!(
warehouse.push((4, 7), (1, 0)),
Some(HashSet::from([(5, 7)]))
);
}

#[test]
fn test_step() {
let mut warehouse = Warehouse::from_lines(_example());
let mut warehouse = Warehouse::from_lines(_example(), false);
let state_0 = warehouse.tiles.clone();

assert_eq!(warehouse.step((1, 1), (1, 0)), (2, 1));
Expand Down Expand Up @@ -227,9 +339,13 @@ mod tests {

#[test]
fn test_progress() {
let mut warehouse = Warehouse::from_lines(_example());
let mut warehouse = Warehouse::from_lines(_example(), false);
while warehouse.progress() {}
assert_eq!(warehouse.checksum(), 10092);

let mut warehouse2 = Warehouse::from_lines(_example(), true);
while warehouse2.progress() {}
assert_eq!(warehouse2.checksum(), 9021);
}

impl Tile {
Expand All @@ -238,6 +354,8 @@ mod tests {
Tile::Floor => '.',
Tile::Box => 'O',
Tile::Wall => '#',
Tile::BoxLeft => '[',
Tile::BoxRight => ']',
}
}
}
Expand All @@ -260,6 +378,7 @@ mod tests {
)
}
}

fn _example() -> Vec<String> {
aoc_utils::read_lines("input/day15-example.txt")
}
Expand Down

0 comments on commit 8421d51

Please sign in to comment.