Skip to content

Commit 4271061

Browse files
committed
feat: day 15 warehouse woes 1/2 soln, #24
1 parent 2e2c838 commit 4271061

File tree

12 files changed

+319
-4
lines changed

12 files changed

+319
-4
lines changed

src/2024/2024-12-06/lib/guard.types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
*
44
* @enum {string}
55
* @property {string} UP - Upward direction `"^"`
6-
* @property {string} RIGHT - Upward direction `">"`
7-
* @property {string} DOWN - Upward direction `"v"`
8-
* @property {string} LEFT - Upward direction `"<"`
6+
* @property {string} RIGHT - Right direction `">"`
7+
* @property {string} DOWN - Downward direction `"v"`
8+
* @property {string} LEFT - Left direction `"<"`
99
*/
1010
export enum GuardDirection {
1111
UP = '^',

src/2024/2024-12-15/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ Visit the Advent of Code website for more information on this puzzle at:
55
**Source:** https://adventofcode.com/2024/day/15<br>
66
**Status:** On-going ⭐
77

8+
## Code
9+
10+
### `robot.ts`
11+
12+
**Robot** class
13+
14+
- Manages the `Robot` object that runs across the grid and moves boxes
15+
- **`findInitialPosition()`** - Finds the robot's initial `Point` position in the 2D grid and stores them in the `this.pos` object
16+
- **`readInstruction()`** - Reads the next instruction and sets the (y,x) direction
17+
- **`walk()`** - Increments the robot's (y,x) coordinate by direction
18+
- **`next()`** - Finds the next (y,x) coordinate of the robot or a given `Point` parameter.
19+
20+
### `calculateGPS.ts`
21+
22+
- **`moveBoxes()`** - Moves the robot and boxes across the grid
23+
- **`calculateGPS()`** - Calculates the GPS sum of all boxes in the grid
24+
825
## Notes
926

1027
### Main Objects

src/2024/2024-12-15/input.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
########
2+
#..O.O.#
3+
##@.O..#
4+
#...O..#
5+
#.#.O..#
6+
#...O..#
7+
#......#
8+
########
9+
10+
<^>>v>>^v

src/2024/2024-12-15/input2.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
##########
2+
#..O..O.O#
3+
#......O.#
4+
#.OO..O.O#
5+
#..O@..O.#
6+
#O#..O...#
7+
#O..O..O.#
8+
#.OO.O.OO#
9+
#....O...#
10+
##########
11+
12+
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
13+
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
14+
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
15+
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { Point } from '@/aoc/point/types.js'
2+
3+
import { getCoordinateSymbol, isOutOfBounds } from '@/aoc/grid/utils.js'
4+
import { Robot } from './robot.js'
5+
6+
/**
7+
* Moves the robot and boxes across the grid
8+
* @param {string[][]} grid - 2D string array containing walls, boxes and space data
9+
* @param {string[]} instructions - String array containing the robot's move instructions
10+
* @returns {void}
11+
*/
12+
export const moveBoxes = (grid: string[][], instructions: string[]): void => {
13+
const dimensions = { length: grid.length, width: grid[0]!.length }
14+
const robot = new Robot(grid, instructions)
15+
16+
while (robot.instructions.length > 0) {
17+
robot.readInstruction()
18+
19+
let nextPos = robot.next()
20+
let nextSymbol = getCoordinateSymbol(nextPos, grid)?.symbol
21+
22+
// Move robot to the next blank space
23+
if (nextSymbol === '.') {
24+
grid[robot.pos.y]![robot.pos.x] = '.'
25+
grid[nextPos.y]![nextPos.x] = robot.symbol
26+
robot.walk()
27+
}
28+
29+
// Find connected boxes until a space symbol
30+
if (nextSymbol === 'O') {
31+
const boxes: Point[] = [nextPos]
32+
33+
while (!isOutOfBounds(nextPos, dimensions) && nextSymbol === 'O') {
34+
nextPos = robot.next(nextPos)
35+
nextSymbol = getCoordinateSymbol(nextPos, grid)?.symbol
36+
37+
if (nextSymbol === 'O') {
38+
boxes.push(nextPos)
39+
}
40+
}
41+
42+
// Move groups of boxes if there's a space at their end
43+
if (nextSymbol === '.' && boxes.length > 0) {
44+
for (let i = boxes.length - 1; i >= 0; i -= 1) {
45+
const next = robot.next(boxes[i])
46+
47+
grid[boxes[i]!.y]![boxes[i]!.x] = '.'
48+
grid[next.y]![next.x] = 'O'
49+
}
50+
51+
// Move the robot
52+
grid[robot.pos.y]![robot.pos.x] = '.'
53+
robot.walk()
54+
55+
grid[robot.pos.y]![robot.pos.x] = robot.symbol
56+
}
57+
}
58+
}
59+
}
60+
61+
/**
62+
* Calculates the GPS sum of all boxes in the grid
63+
* @param {string[][]} grid - 2D string array containing walls, boxes and space data
64+
* @returns {number} GPS sum of all boxes in the grid
65+
*/
66+
export const calculateGPS = (grid: string[][]): number => {
67+
const dimensions = { length: grid.length, width: grid[0]!.length }
68+
let sumGPS = 0
69+
70+
for (let y = 0; y < dimensions.length; y += 1) {
71+
for (let x = 0; x < dimensions.width; x += 1) {
72+
if (grid[y]![x] === 'O') {
73+
sumGPS += y * 100 + x
74+
}
75+
}
76+
}
77+
78+
return sumGPS
79+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { RobotWarehouseData } from './types.js'
2+
3+
import { AOCOutputType, readAOCInputFile } from '@/aoc/file/aocfile.js'
4+
import { file } from '@/aoc/file/utils.js'
5+
6+
/**
7+
* Reads and formats the day 15 quiz input file.
8+
* @param {string} fileName - Input text filename path relative to this script
9+
* @returns {RobotWarehouseData} Quiz input data
10+
*/
11+
export const fileReader = (fileName: string): RobotWarehouseData => {
12+
const input = readAOCInputFile({
13+
filePath: file(import.meta.url, fileName),
14+
type: AOCOutputType.STRING
15+
}) as string
16+
17+
return input
18+
.split('\n\n')
19+
.reduce((data, item, index) => {
20+
// Grid
21+
if (index === 0) {
22+
return {
23+
...data,
24+
grid: item
25+
.split('\n')
26+
.map(line => line.split(''))
27+
}
28+
}
29+
30+
// Instructions
31+
return {
32+
...data,
33+
instructions: item
34+
.split('')
35+
.reverse()
36+
.filter(direction => direction !== '\n')
37+
}
38+
}, {}) as RobotWarehouseData
39+
}

src/2024/2024-12-15/lib/robot.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { GuardDirection } from '@/2024/2024-12-06/lib/guard.types.js'
2+
import { getGridCoordinate } from '@/aoc/grid/utils.js'
3+
import type { Point } from '@/aoc/point/types.js'
4+
5+
/**
6+
* @class Robot
7+
* @description A set of methods, objects and variables for managing a `Robot`
8+
*/
9+
export class Robot {
10+
/** Symbol representation in the grid */
11+
symbol: string = '@'
12+
13+
/** Current (y,x) grid coordinate */
14+
pos: Point = { x: -1, y: -1 }
15+
16+
/** Direction indicator up/down/left/right */
17+
direction: Point = { x: 0, y: 0 }
18+
19+
/** Current active move instruction symbol */
20+
instruction: GuardDirection | '-' = '-'
21+
22+
/** Moves instructions set */
23+
instructions: string[]
24+
25+
/**
26+
* @constructor
27+
* @param {string[][]} grid - 2D string array grid
28+
*/
29+
constructor (grid: string[][], instructions: string[]) {
30+
this.findInitialPosition(grid)
31+
this.instructions = [...instructions]
32+
}
33+
34+
/**
35+
* Finds the robot's initial `Point` position in the 2D grid, storing them in the `this.pos` object
36+
* @param {string[][]} grid - 2D string array grid
37+
*/
38+
findInitialPosition (grid: string[][]): void {
39+
const start = getGridCoordinate(grid, this.symbol)
40+
41+
this.pos.x = start.x
42+
this.pos.y = start.y
43+
}
44+
45+
/** Reads the next instruction and sets the (y,x) direction */
46+
readInstruction (): void {
47+
this.instruction = this.instructions.pop() as GuardDirection || '-'
48+
49+
switch(this.instruction) {
50+
case GuardDirection.UP:
51+
this.direction = { x: 0, y: -1 }
52+
break
53+
case GuardDirection.DOWN:
54+
this.direction = { x: 0, y: 1 }
55+
break
56+
case GuardDirection.LEFT:
57+
this.direction = { x: -1, y: 0 }
58+
break
59+
case GuardDirection.RIGHT:
60+
this.direction = { x: 1, y: 0 }
61+
break
62+
default:
63+
break
64+
}
65+
}
66+
67+
/** Increments the robot's (y,x) coordinate by direction */
68+
walk (): void {
69+
this.pos.x += this.direction.x
70+
this.pos.y += this.direction.y
71+
}
72+
73+
/**
74+
* Finds the next (y,x) coordinate of the robot or a given `Point` parameter.
75+
* @param {Point} [point] - (Optional) (y,x) coordinate to find the next step coorndinate from
76+
* @returns {Point} Next (y,x) coordinate after 1 step
77+
*/
78+
next (point?: Point): Point {
79+
const x = point?.x || this.pos.x
80+
const y = point?.y || this.pos.y
81+
82+
const nextX = x + this.direction.x
83+
const nextY = y + this.direction.y
84+
85+
return {
86+
x: nextX, y: nextY
87+
}
88+
}
89+
}

src/2024/2024-12-15/lib/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* @type {Object} RobotWarehouseData
3+
* @property {string[][]} grid - 2D string array containng walls, boxes, spaces and robot symbols
4+
* @property {string[]} instructions - String array contaning the robot's move instructions
5+
*/
6+
export type RobotWarehouseData = {
7+
grid: string[][];
8+
instructions: string[];
9+
}

src/2024/2024-12-15/main.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { fileReader } from './lib/fileReader.js'
2+
import { moveBoxes, calculateGPS } from './lib/calculateGPS.js'
3+
4+
/**
5+
* Part 1/2 of the 2024-12-15 quiz
6+
* Moves the robot, boxes and calculates all boxes' GPS
7+
*/
8+
const quiz20241215_01 = () => {
9+
const { grid, instructions } = fileReader('../input.txt')
10+
11+
moveBoxes(grid, instructions)
12+
const gps = calculateGPS(grid)
13+
14+
console.log('---GPS', gps)
15+
}
16+
17+
quiz20241215_01()

src/2024/2024-12-15/sample.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { test, expect } from 'vitest'
2+
3+
import { fileReader } from './lib/fileReader.js'
4+
import { moveBoxes, calculateGPS } from './lib/calculateGPS.js'
5+
6+
test('Part 1: Move boxes and calculate GPS', () => {
7+
const data = fileReader('../input.txt')
8+
const data2 = fileReader('../input2.txt')
9+
10+
moveBoxes(data.grid, data.instructions)
11+
expect(calculateGPS(data.grid)).toBe(2027)
12+
13+
moveBoxes(data2.grid, data2.instructions)
14+
expect(calculateGPS(data2.grid)).toBe(9897)
15+
})

0 commit comments

Comments
 (0)