day15: part 1 solution + beginning of part 2
Some checks failed
test / AoC 2024 (push) Has been cancelled
Some checks failed
test / AoC 2024 (push) Has been cancelled
This commit is contained in:
parent
003bc3212d
commit
4dfdaca58c
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 8.2 KiB |
@ -1,6 +1,6 @@
|
|||||||
<!-- AOC TILES BEGIN -->
|
<!-- AOC TILES BEGIN -->
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
2024 - 28 ⭐ - Rust
|
2024 - 30 ⭐ - Rust
|
||||||
</h1>
|
</h1>
|
||||||
<a href="src/day1.rs">
|
<a href="src/day1.rs">
|
||||||
<img src=".aoc_tiles/tiles/2024/01.png" width="161px">
|
<img src=".aoc_tiles/tiles/2024/01.png" width="161px">
|
||||||
@ -44,4 +44,7 @@
|
|||||||
<a href="src/day14.rs">
|
<a href="src/day14.rs">
|
||||||
<img src=".aoc_tiles/tiles/2024/14.png" width="161px">
|
<img src=".aoc_tiles/tiles/2024/14.png" width="161px">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="src/day15.rs">
|
||||||
|
<img src=".aoc_tiles/tiles/2024/15.png" width="161px">
|
||||||
|
</a>
|
||||||
<!-- AOC TILES END -->
|
<!-- AOC TILES END -->
|
||||||
|
292
src/day15.rs
Normal file
292
src/day15.rs
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
io::{BufRead, Cursor, Lines},
|
||||||
|
iter,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use aoc_runner_derive::aoc;
|
||||||
|
use grid::{AsCoord2d, Coord2d, Grid};
|
||||||
|
use itertools::{rev, Itertools};
|
||||||
|
|
||||||
|
struct Warehouse {
|
||||||
|
map: Grid<u8>,
|
||||||
|
robot_pos: Coord2d,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Warehouse {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.map.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Warehouse {
|
||||||
|
fn step_robot(&mut self, m: Move) {
|
||||||
|
match m {
|
||||||
|
Move::Left => {
|
||||||
|
let to_left = &self.map.row(self.robot_pos.y()).unwrap()[0..self.robot_pos.x() as usize];
|
||||||
|
let left_chunks = to_left
|
||||||
|
.chunk_by(|a, b| a == b || (*a == b'[' && *b == b']'))
|
||||||
|
.collect_vec();
|
||||||
|
match left_chunks.last().unwrap().last().unwrap() {
|
||||||
|
b'.' => {
|
||||||
|
self.map
|
||||||
|
.swap(&self.robot_pos, (self.robot_pos.x() - 1, self.robot_pos.y()));
|
||||||
|
self.robot_pos.x -= 1
|
||||||
|
}
|
||||||
|
b'O' | b'[' | b']' => {
|
||||||
|
if left_chunks[left_chunks.len() - 2].last().unwrap() == &b'.' {
|
||||||
|
let y = self.robot_pos.y();
|
||||||
|
// swap the whole chunk left
|
||||||
|
for x_target in self.robot_pos.x() - left_chunks.last().unwrap().len() as i64
|
||||||
|
..=self.robot_pos.x() as i64
|
||||||
|
{
|
||||||
|
self.map.swap((x_target, y), (x_target - 1, y));
|
||||||
|
}
|
||||||
|
self.robot_pos.x -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'#' => {}
|
||||||
|
c => panic!("unexpected char {}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move::Right => {
|
||||||
|
let to_right =
|
||||||
|
&self.map.row(self.robot_pos.y()).unwrap()[self.robot_pos.x() as usize + 1..self.map.width()];
|
||||||
|
let right_chunks = to_right
|
||||||
|
.chunk_by(|a, b| a == b || (*a == b'[' && *b == b']'))
|
||||||
|
.collect_vec();
|
||||||
|
match right_chunks[0][0] {
|
||||||
|
b'.' => {
|
||||||
|
self.map
|
||||||
|
.swap(&self.robot_pos, (self.robot_pos.x() + 1, self.robot_pos.y()));
|
||||||
|
self.robot_pos.x += 1
|
||||||
|
}
|
||||||
|
b'O' | b'[' | b']' => {
|
||||||
|
if right_chunks[1][0] == b'.' {
|
||||||
|
let y = self.robot_pos.y();
|
||||||
|
// swap the whole chunk right
|
||||||
|
for x_target in
|
||||||
|
(self.robot_pos.x() + 1..=self.robot_pos.x() + 1 + right_chunks[0].len() as i64).rev()
|
||||||
|
{
|
||||||
|
self.map.swap((x_target, y), (x_target - 1, y));
|
||||||
|
}
|
||||||
|
self.robot_pos.x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'#' => {}
|
||||||
|
c => panic!("unexpected char {}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move::Up => {
|
||||||
|
let to_up = &self.map.col(self.robot_pos.x()).unwrap()[0..self.robot_pos.y() as usize];
|
||||||
|
let up_chunks = to_up.chunk_by(|a, b| a == b).collect_vec();
|
||||||
|
match up_chunks.last().unwrap().last().unwrap() {
|
||||||
|
b'.' => {
|
||||||
|
self.map
|
||||||
|
.swap(&self.robot_pos, (self.robot_pos.x(), self.robot_pos.y() - 1));
|
||||||
|
self.robot_pos.y -= 1
|
||||||
|
}
|
||||||
|
b'O' => {
|
||||||
|
if **up_chunks[up_chunks.len() - 2].last().unwrap() == b'.' {
|
||||||
|
let x = self.robot_pos.x();
|
||||||
|
// swap the whole chunk left
|
||||||
|
for y_target in
|
||||||
|
self.robot_pos.y() - up_chunks.last().unwrap().len() as i64..=self.robot_pos.y() as i64
|
||||||
|
{
|
||||||
|
self.map.swap((x, y_target), (x, y_target - 1));
|
||||||
|
}
|
||||||
|
self.robot_pos.y -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'#' => {}
|
||||||
|
c => panic!("unexpected char {}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Move::Down => {
|
||||||
|
let to_down =
|
||||||
|
&self.map.col(self.robot_pos.x()).unwrap()[self.robot_pos.y() as usize + 1..self.map.height()];
|
||||||
|
let down_chunks = to_down.chunk_by(|a, b| a == b).collect_vec();
|
||||||
|
match down_chunks[0][0] {
|
||||||
|
b'.' => {
|
||||||
|
self.map
|
||||||
|
.swap(&self.robot_pos, (self.robot_pos.x(), self.robot_pos.y() + 1));
|
||||||
|
self.robot_pos.y += 1;
|
||||||
|
}
|
||||||
|
b'O' => {
|
||||||
|
if *down_chunks[1][0] == b'.' {
|
||||||
|
let x = self.robot_pos.x();
|
||||||
|
// swap the whole chunk down
|
||||||
|
for y_target in
|
||||||
|
(self.robot_pos.y() + 1..=self.robot_pos.y() + 1 + down_chunks[0].len() as i64).rev()
|
||||||
|
{
|
||||||
|
self.map.swap((x, y_target), (x, y_target - 1));
|
||||||
|
}
|
||||||
|
self.robot_pos.y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b'#' => {}
|
||||||
|
c => panic!("unexpected char {}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn embiggen(&mut self) {
|
||||||
|
let new_lines = (0..self.map.height())
|
||||||
|
.map(|r| self.map.row(r as i64).unwrap())
|
||||||
|
.map(|row| {
|
||||||
|
row.iter()
|
||||||
|
.flat_map(|c| match c {
|
||||||
|
b'#' => ['#', '#'],
|
||||||
|
b'O' => ['[', ']'],
|
||||||
|
b'.' => ['.', '.'],
|
||||||
|
b'@' => ['@', '.'],
|
||||||
|
c => panic!("unexpected character {}", c),
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
self.map = Grid::from(Cursor::new(new_lines.as_str()));
|
||||||
|
self.robot_pos = self.map.find(&b'@').unwrap().to_coord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Move {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Move {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Left => f.write_str("Left"),
|
||||||
|
Self::Right => f.write_str("Right"),
|
||||||
|
Self::Up => f.write_str("Up"),
|
||||||
|
Self::Down => f.write_str("Down"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for Move {
|
||||||
|
fn from(c: char) -> Self {
|
||||||
|
match c {
|
||||||
|
'<' => Self::Left,
|
||||||
|
'>' => Self::Right,
|
||||||
|
'^' => Self::Up,
|
||||||
|
'v' => Self::Down,
|
||||||
|
c => panic!("invalid move {}", c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MovePlan(Vec<Move>);
|
||||||
|
|
||||||
|
impl FromStr for MovePlan {
|
||||||
|
type Err = Box<dyn std::error::Error>;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(MovePlan(
|
||||||
|
s.chars().filter(|c| *c != '\n').map(|c| Move::from(c)).collect(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str) -> (Warehouse, MovePlan) {
|
||||||
|
let lines = input.lines().collect_vec();
|
||||||
|
let parts = lines.split(|l| l.is_empty()).map(|ls| ls.join("\n")).collect_vec();
|
||||||
|
let map: Grid<u8> = parts[0].parse().unwrap();
|
||||||
|
let wh = Warehouse {
|
||||||
|
robot_pos: map.find(&b'@').unwrap().to_coord(),
|
||||||
|
map,
|
||||||
|
};
|
||||||
|
let moves = parts[1].parse().unwrap();
|
||||||
|
|
||||||
|
(wh, moves)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day15, part1)]
|
||||||
|
pub fn part1(input: &str) -> i64 {
|
||||||
|
let (mut wh, moves) = parse(input);
|
||||||
|
// println!("map:\n {}\nmoves: {:?}", wh, moves);
|
||||||
|
for m in moves.0 {
|
||||||
|
// println!("{}", m);
|
||||||
|
wh.step_robot(m);
|
||||||
|
// println!("{}", wh);
|
||||||
|
}
|
||||||
|
wh.map
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(i, v)| **v == b'O')
|
||||||
|
.map(|(i, _)| wh.map.coord(i as i64).unwrap().y() * 100 + wh.map.coord(i as i64).unwrap().x())
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[aoc(day15, part2)]
|
||||||
|
pub fn part2(input: &str) -> i64 {
|
||||||
|
let (mut wh, moves) = parse(input);
|
||||||
|
wh.embiggen();
|
||||||
|
|
||||||
|
let moves: MovePlan = ">>>>>>>>>>>>".parse().unwrap();
|
||||||
|
|
||||||
|
println!("{}", wh);
|
||||||
|
for m in moves.0 {
|
||||||
|
println!("{}", m);
|
||||||
|
wh.step_robot(m);
|
||||||
|
println!("{}", wh);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
const EXAMPLE1: &str = "########
|
||||||
|
#..O.O.#
|
||||||
|
##@.O..#
|
||||||
|
#...O..#
|
||||||
|
#.#.O..#
|
||||||
|
#...O..#
|
||||||
|
#......#
|
||||||
|
########
|
||||||
|
|
||||||
|
<^^>>>vv<v>>v<<";
|
||||||
|
const EXAMPLE2: &str = "##########
|
||||||
|
#..O..O.O#
|
||||||
|
#......O.#
|
||||||
|
#.OO..O.O#
|
||||||
|
#..O@..O.#
|
||||||
|
#O#..O...#
|
||||||
|
#O..O..O.#
|
||||||
|
#.OO.O.OO#
|
||||||
|
#....O...#
|
||||||
|
##########
|
||||||
|
|
||||||
|
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
|
||||||
|
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
|
||||||
|
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
|
||||||
|
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
|
||||||
|
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
|
||||||
|
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
|
||||||
|
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
|
||||||
|
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
|
||||||
|
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
|
||||||
|
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1(EXAMPLE1), 2028);
|
||||||
|
assert_eq!(part1(EXAMPLE2), 10092);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
// assert_eq!(part2(EXAMPLE1), 0);
|
||||||
|
assert_eq!(part2(EXAMPLE2), 9021);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ pub mod day11;
|
|||||||
pub mod day12;
|
pub mod day12;
|
||||||
pub mod day13;
|
pub mod day13;
|
||||||
pub mod day14;
|
pub mod day14;
|
||||||
|
pub mod day15;
|
||||||
pub mod day2;
|
pub mod day2;
|
||||||
pub mod day3;
|
pub mod day3;
|
||||||
pub mod day4;
|
pub mod day4;
|
||||||
|
@ -152,7 +152,7 @@ impl<T: Clone + Eq + PartialEq + Display + Debug> Grid<T> {
|
|||||||
pub fn height(&self) -> usize {
|
pub fn height(&self) -> usize {
|
||||||
self.data.len() / self.width()
|
self.data.len() / self.width()
|
||||||
}
|
}
|
||||||
fn pos<C: AsCoord2d>(&self, c: &C) -> i64 {
|
pub fn pos<C: AsCoord2d>(&self, c: &C) -> i64 {
|
||||||
c.y() * self.width + c.x()
|
c.y() * self.width + c.x()
|
||||||
}
|
}
|
||||||
pub fn coord(&self, pos: i64) -> Option<(i64, i64)> {
|
pub fn coord(&self, pos: i64) -> Option<(i64, i64)> {
|
||||||
@ -203,13 +203,21 @@ impl<T: Clone + Eq + PartialEq + Display + Debug> Grid<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn row(&self, y: i64) -> Option<&[T]> {
|
pub fn row(&self, y: i64) -> Option<&[T]> {
|
||||||
if y < self.height() as i64 {
|
if y < self.height() as i64 && y >= 0 {
|
||||||
Some(&self.data[self.pos(&(0, y)) as usize..self.pos(&(self.width, y)) as usize])
|
Some(&self.data[self.pos(&(0, y)) as usize..self.pos(&(self.width, y)) as usize])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn col(&self, x: i64) -> Option<Vec<&T>> {
|
||||||
|
if x < self.width() as i64 && x >= 0 {
|
||||||
|
Some((0..self.height()).map(|y| self.get(&(x, y as i64)).unwrap()).collect())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find(&self, haystack: &T) -> Option<(i64, i64)> {
|
pub fn find(&self, haystack: &T) -> Option<(i64, i64)> {
|
||||||
self.coord(
|
self.coord(
|
||||||
self.data
|
self.data
|
||||||
@ -231,6 +239,13 @@ impl<T: Clone + Eq + PartialEq + Display + Debug> Grid<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn swap<A: AsCoord2d, B: AsCoord2d>(&mut self, a: A, b: B) {
|
||||||
|
match (self.valid_pos(&a), self.valid_pos(&b)) {
|
||||||
|
(Some(a), Some(b)) => self.data.swap(a, b),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fn window_compare_impl<const REV: bool>(&self, needle: &[T]) -> Vec<(i64, i64)> {
|
// fn window_compare_impl<const REV: bool>(&self, needle: &[T]) -> Vec<(i64, i64)> {
|
||||||
// if (self.width as usize) < needle.len() {
|
// if (self.width as usize) < needle.len() {
|
||||||
// return Vec::new();
|
// return Vec::new();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user