Skip to content

feat: day 15 warehouse woes 20241215 #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This repository contains solutions and a local development environment for the [
- Day 12: Garden Groups [[link]](/src/2024/2024-12-12/README.md)
- Day 13: Claw Contraption [[link]](/src/2024/2024-12-13/README.md)
- Day 14: Restroom Redoubt [[link]](/src/2024/2024-12-14/README.md)
- Day 15: Warehouse Woes [[link]](/src/2024/2024-12-15/README.md)

</details>

Expand Down
6 changes: 3 additions & 3 deletions src/2024/2024-12-06/lib/guard.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*
* @enum {string}
* @property {string} UP - Upward direction `"^"`
* @property {string} RIGHT - Upward direction `">"`
* @property {string} DOWN - Upward direction `"v"`
* @property {string} LEFT - Upward direction `"<"`
* @property {string} RIGHT - Right direction `">"`
* @property {string} DOWN - Downward direction `"v"`
* @property {string} LEFT - Left direction `"<"`
*/
export enum GuardDirection {
UP = '^',
Expand Down
95 changes: 95 additions & 0 deletions src/2024/2024-12-15/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
## Day 15: Warehouse Woes

Visit the Advent of Code website for more information on this puzzle at:

**Source:** https://adventofcode.com/2024/day/15<br>
**Status:** Complete ⭐⭐

![part2 animation from AoC online sample](assets/part2.gif)<br>
<sub><i>(Part2 animation from AoC online sample)</i></sub>

## Code

### `robot.ts`

**Robot** class

- Manages the `Robot` object that runs across the grid and moves boxes
- **`findInitialPosition()`** - Finds the robot's initial `Point` position in the 2D grid and stores them in the `this.pos` object
- **`readInstruction()`** - Reads the next instruction and sets the (y,x) direction
- **`walk()`** - Increments the robot's (y,x) coordinate by direction
- **`next()`** - Finds the **next** (y,x) coordinate of the robot or a given `Point` parameter using the robot's current direction.
- **`prev()`** - Finds the robot's **previous** (y,x) coordinate or a given `Point` parameter using the robot's current direction.

### `calculateGPS.ts`

- **`moveBoxes()`** - Moves the robot and boxes across the grid
- **`calculateGPS()`** - Calculates the GPS sum of all boxes in the grid

### `calculateExpandedGPS.ts`

- **`moveExpandedBoxes()`** - Moves the robot and the expanded (2x size) boxes across the grid.
- **`calculateExpandedGPS()`** - Calculates the GPS sum of all expanded boxes in the grid.

### `fileReader.ts`

- **`fileReader()`** - Reads and formats the day 15 quiz input file.

### `fileReaderExpanded.ts`

- **`fileReader()`** - Reads and formats the day 15 - part 2 quiz input file. Expands the tile symbols by 2.

## Notes

### Main Objects

1. GRID
- 2d board array
- contains
- blank space: `.`
- walls: `#`
- box: `O`
- amok robot: = `@`

2. WALLS
- symbol `#`
- surrounds grid edges
- found inside grid
- impassable by: `O`, `@`
- passable: GPS

3. BOX
- symbol `O`
- impassable: `@`, other `O`
- passable: GPS
- can be moved up/down/left/right by `@`

4. ROBOT
- symbol `@`
- has move instructions
- can move up/down/left/rigt
- can push `O`
- cannot move if blocked by unpushable `O`
- cannot move if blocked by `#`

### Algo

#### Phase 1 - Move Boxes

- initialize all pos in grid
- read robot move instruction
- turn + move according to instruction
- if symbol ahead is `O`
- chain pushing preceeding `O`s
- if `O` unmovable, stop
- if symbol ahead is `.`
- move
- if symbol ahead is `#`
- stop
- repeat until end of move instructions

#### Phase 2 - Calculate Box GPS

- for each box,
- GPS = 100 * distance from top + distance from left
- sum all box GPS
Binary file added src/2024/2024-12-15/assets/part2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/2024/2024-12-15/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
########
#..O.O.#
##@.O..#
#...O..#
#.#.O..#
#...O..#
#......#
########

<^>>v>>^v
15 changes: 15 additions & 0 deletions src/2024/2024-12-15/input2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
##########
#..O..O.O#
#......O.#
#.OO..O.O#
#[email protected].#
#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^<<^^^^^
156 changes: 156 additions & 0 deletions src/2024/2024-12-15/lib/calculateExpandedGPS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import type { Point, PointSymbol } from '@/aoc/point/types.js'

import { getCoordinateSymbol, getGridDimensions, isOutOfBounds } from '@/aoc/grid/utils.js'

import {
getBoxStartCoordinates,
getReverseSymbol,
getSymbolDirection,
isExpandedBoxSymbol
} from './utils.js'

import { Robot } from './robot.js'

/**
* Moves the robot and the expanded (2x size) boxes across the grid
* @param {string[][]} grid - 2D string array containing walls, boxes and space data
* @param {string[]} instructions - String array containing the robot's pushBox instructions
* @returns {void}
*/
export const moveExpandedBoxes = (grid: string[][], instructions: string[]): void => {
const dimensions = getGridDimensions(grid)
const robot = new Robot(grid, instructions)

while (robot.instructions.length > 0) {
robot.readInstruction()

let nextPos = robot.next()
let nextSymbol = getCoordinateSymbol(nextPos, grid)?.symbol as string

// Move robot to the next blank space
if (nextSymbol === '.') {
grid[robot.pos.y]![robot.pos.x] = '.'
grid[nextPos.y]![nextPos.x] = robot.symbol
robot.walk()
}

if (isExpandedBoxSymbol(nextSymbol)) {
// Move boxes along the horizontal x-axis
if (robot.direction.x !== 0) {
const boxes: Point[] = [nextPos]

while (!isOutOfBounds(nextPos, dimensions) && isExpandedBoxSymbol(nextSymbol)) {
nextPos = robot.next(nextPos)
nextSymbol = getCoordinateSymbol(nextPos, grid)?.symbol as string

if (isExpandedBoxSymbol(nextSymbol)) {
boxes.push(nextPos)
}
}

// Move groups of boxes if there's a space at their end
if (nextSymbol === '.' && boxes.length > 0) {
for (let i = boxes.length - 1; i >= 0; i -= 1) {
const next = robot.next(boxes[i])
const temp = grid[boxes[i]!.y]![boxes[i]!.x] as string

grid[boxes[i]!.y]![boxes[i]!.x] = '.'
grid[next.y]![next.x] = temp
}

// Move the robot
grid[robot.pos.y]![robot.pos.x] = '.'
robot.walk()
grid[robot.pos.y]![robot.pos.x] = robot.symbol
}
}

// Move boxes along the vertical y-axis
if (robot.direction.y !== 0) {
// 1st box after the robot
const { side1, side2 } = getBoxStartCoordinates(nextPos, grid)
let start = { ...side1 }

const visited: PointSymbol[] = []
const retrace: PointSymbol[] = [side2]
let canMove = true

while (start && isExpandedBoxSymbol(start.symbol)) {
const next = robot.next(start)
const nextSym = getCoordinateSymbol(next, grid)!.symbol

// Store the visited box coordinates
visited.push(start)

if (['.', '#'].includes(nextSym as string)) {
// End of line reached (space or wall): Retrace unvisited connected coordinates
start = retrace.pop() as PointSymbol

// Connected boxes will not move at anytime a wall `#` is encountered
if (nextSym === '#') {
canMove = false
}
} else if (nextSym === start.symbol) {
// Found stack of boxes:
// Move to the next (up/down) coordinate if its symbol is similar to the current symbol
start = { ...next } as PointSymbol
start.symbol = getCoordinateSymbol(start, grid)!.symbol
} else if (nextSym === getReverseSymbol(start.symbol)) {
// Found half-stacked boxes:
// Stack the next coordinate if it's symbol is different from the current symbol
retrace.push({ ...next, symbol: nextSym })

// Move to the next diagonally-aligned half-box symbol coordinate
const xDirection = getSymbolDirection(nextSym)

if (xDirection) {
start = { ...next, x: next.x + xDirection } as PointSymbol
start.symbol = getCoordinateSymbol(start, grid)!.symbol
}
}
}

// Move the boxes
if (canMove) {
visited
.sort((a, b) => {
if (robot.direction.y < 0) {
return (a.y < b.y ? -1 : 1)
} else {
return (a.y > b.y ? -1 : 1)
}
})
.forEach(item => {
const next = robot.next(item)
grid[item.y]![item.x] = '.'
grid[next.y]![next.x] = item.symbol
})

grid[robot.pos.y]![robot.pos.x] = '.'
grid[nextPos.y]![nextPos.x] = robot.symbol
robot.walk()
}
}
}
}
}

/**
* Calculates the GPS sum of all expanded boxes in the grid
* @param {string[][]} grid - 2D string array containing walls, boxes and space data
* @returns {number} GPS sum of all boxes in the grid
*/
export const calculateExpandedGPS = (grid: string[][]): number => {
const dimensions = getGridDimensions(grid)
let sumGPS = 0

for (let y = 0; y < dimensions.length; y += 1) {
for (let x = 0; x < dimensions.width; x += 1) {
if (grid[y]![x] === '[') {
sumGPS += y * 100 + x
}
}
}

return sumGPS
}
79 changes: 79 additions & 0 deletions src/2024/2024-12-15/lib/calculateGPS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Point } from '@/aoc/point/types.js'

import { getCoordinateSymbol, isOutOfBounds } from '@/aoc/grid/utils.js'
import { Robot } from './robot.js'

/**
* Moves the robot and boxes across the grid
* @param {string[][]} grid - 2D string array containing walls, boxes and space data
* @param {string[]} instructions - String array containing the robot's move instructions
* @returns {void}
*/
export const moveBoxes = (grid: string[][], instructions: string[]): void => {
const dimensions = { length: grid.length, width: grid[0]!.length }
const robot = new Robot(grid, instructions)

while (robot.instructions.length > 0) {
robot.readInstruction()

let nextPos = robot.next()
let nextSymbol = getCoordinateSymbol(nextPos, grid)?.symbol

// Move robot to the next blank space
if (nextSymbol === '.') {
grid[robot.pos.y]![robot.pos.x] = '.'
grid[nextPos.y]![nextPos.x] = robot.symbol
robot.walk()
}

// Find connected boxes until a space symbol
if (nextSymbol === 'O') {
const boxes: Point[] = [nextPos]

while (!isOutOfBounds(nextPos, dimensions) && nextSymbol === 'O') {
nextPos = robot.next(nextPos)
nextSymbol = getCoordinateSymbol(nextPos, grid)?.symbol

if (nextSymbol === 'O') {
boxes.push(nextPos)
}
}

// Move groups of boxes if there's a space at their end
if (nextSymbol === '.' && boxes.length > 0) {
for (let i = boxes.length - 1; i >= 0; i -= 1) {
const next = robot.next(boxes[i])

grid[boxes[i]!.y]![boxes[i]!.x] = '.'
grid[next.y]![next.x] = 'O'
}

// Move the robot
grid[robot.pos.y]![robot.pos.x] = '.'
robot.walk()

grid[robot.pos.y]![robot.pos.x] = robot.symbol
}
}
}
}

/**
* Calculates the GPS sum of all boxes in the grid
* @param {string[][]} grid - 2D string array containing walls, boxes and space data
* @returns {number} GPS sum of all boxes in the grid
*/
export const calculateGPS = (grid: string[][]): number => {
const dimensions = { length: grid.length, width: grid[0]!.length }
let sumGPS = 0

for (let y = 0; y < dimensions.length; y += 1) {
for (let x = 0; x < dimensions.width; x += 1) {
if (grid[y]![x] === 'O') {
sumGPS += y * 100 + x
}
}
}

return sumGPS
}
Loading
Loading