Skip to content

Commit 36685ad

Browse files
committed
feat: day 13 claw contraption 1/2 soln, #19
1 parent f65bd8e commit 36685ad

File tree

10 files changed

+229
-6
lines changed

10 files changed

+229
-6
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const input = readAOCInputFile({
1313
* Part 1/2 of the 2024-12-12 quiz
1414
* Counts the total fencing price: area * perimeter per plot
1515
*/
16-
const quiz20241231_01 = () => {
16+
const quiz20241212_01 = () => {
1717
const garden = new Garden()
1818
const totalPrice = garden.calculateFencePrice(input, true)
1919

@@ -24,12 +24,12 @@ const quiz20241231_01 = () => {
2424
* Part 2/2 of the 2024-12-12 quiz
2525
* Counts the wholesale fencing price: area * perimeter of whole region (edges)
2626
*/
27-
const quiz20241231_02 = () => {
27+
const quiz20241212_02 = () => {
2828
const garden = new WholesaleGarden()
2929
const totalPrice = garden.calculateFencePrice(input, true)
3030

3131
console.log('---Wholesale fencing price', totalPrice)
3232
}
3333

34-
quiz20241231_01()
35-
quiz20241231_02()
34+
quiz20241212_01()
35+
quiz20241212_02()

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## Day 13: Claw Contraption
2+
3+
Visit the Advent of Code website for more information on this puzzle at:
4+
5+
**Source:** https://adventofcode.com/2024/day/13<br>
6+
**Status:** On-going ⭐
7+
8+
## Code
9+
10+
### `count.ts`
11+
12+
- **`countTokensPrizes()`** - Counts the minimum number of tokens needed to win all possible prizes.
13+
14+
### `utils.ts`
15+
16+
- **`getDeterminant()`** - Counts the determinant of a 2x2 coefficient matrix. Currently only works with a 2x2 number matrix.
17+
- **`coefficientMatrix()`** - Builds a coefficient matrix by replacing column values with the constants. Currently only works with a 2x2 number matrix.
18+
- **`solveEquation()`** - Calculates the number of needed (x,y) button A "and" B presses to reach the prize by solving the 2 equations using Cramer's Rule.
19+
20+
### `fileReader.ts`
21+
22+
- Reads and formats the quiz input TXT file
23+
24+
## References
25+
26+
<sup>[[1]](https://www.youtube.com/watch?v=jBsC34PxzoM)</sup>&nbsp; Cramer's Rule<br>
27+
<sup>[[2]](https://www.youtube.com/playlist?list=PLybg94GvOJ9En46TNCXL2n6SiqRc_iMB8)</sup>&nbsp; Linear Algebra Videos
28+

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Button A: X+94, Y+34
2+
Button B: X+22, Y+67
3+
Prize: X=8400, Y=5400
4+
5+
Button A: X+3, Y+8
6+
Button B: X+4, Y+15
7+
Prize: X=123, Y=456
8+
9+
Button A: X+17, Y+86
10+
Button B: X+84, Y+37
11+
Prize: X=7870, Y=6450
12+
13+
Button A: X+30, Y+9
14+
Button B: X+78, Y+14
15+
Prize: X=777, Y=14344

src/2024/2024-12-13/lib/count.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { MatrixData, StepsTokens } from './types.js'
2+
import { solveEquation } from './utils.js'
3+
4+
/**
5+
* Counts the minimum number of tokens needed to win all possible prizes.
6+
* @param {MatrixData<number[]>} data - Processed quiz data object containing 2D number matrices
7+
* @param {boolean} log - Flag to print the log messages to screen
8+
* @returns {StepsTokens} Object containing the number of tokens and prizes
9+
*/
10+
export const countTokensPrizes = (
11+
data: MatrixData<number[]>,
12+
log: boolean = false
13+
): StepsTokens => {
14+
let countPrizes = 0
15+
let countTokens = 0
16+
17+
for (const key in data) {
18+
const { x, y } = solveEquation(data[key] as number[][])
19+
if (!Number.isInteger(x) || !Number.isInteger(y)) continue
20+
21+
const tokensA = x * 3
22+
const tokensB = y * 1
23+
countPrizes += 1
24+
countTokens += tokensA + tokensB
25+
26+
if (log) {
27+
console.log(`Machine [${key}], Button A: ${x}, Button B: ${y}, TOTAL`, (tokensA + tokensB))
28+
}
29+
}
30+
31+
return {
32+
prizes: countPrizes,
33+
tokens: countTokens
34+
}
35+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { MatrixData } from './types.js'
2+
3+
import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/aoc/file/aocfile.js'
4+
import { file } from '@/aoc/file/utils.js'
5+
6+
/**
7+
* Reads and formats the quiz input data.
8+
* @param {string} fileName - Input file name or relative path
9+
* @returns {MatrixData} Object containing 2D number matrices
10+
*/
11+
export const fileReader = (fileName: string): MatrixData<number[]> => {
12+
const input = readAOCInputFile({
13+
filePath: file(import.meta.url, fileName),
14+
type: AOC_OUTPUT_TYPE.STRING
15+
}) as string
16+
17+
return input
18+
.split('\n\n')
19+
.reduce((list: MatrixData<number[]>, block, id) => {
20+
const row = block.split('\n')
21+
const eq1: number[] = []
22+
const eq2: number[] = []
23+
24+
row.forEach((item, index) => {
25+
const sign = index === row.length - 1 ? '=' : '+'
26+
const xIndex = item.indexOf(`X${sign}`)
27+
const yIndex = item.indexOf(`Y${sign}`)
28+
29+
const x = item.substring(xIndex + 2, item.indexOf(','))
30+
const y = item.substring(yIndex + 2, item.length)
31+
32+
eq1.push(Number(x))
33+
eq2.push(Number(y))
34+
})
35+
36+
list[id] = [eq1, eq2]
37+
return list
38+
}, {})
39+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
/**
3+
* @type {Record<number, T[]>} MatrixData
4+
*/
5+
export type MatrixData<T> = Record<number, T[]>
6+
7+
/**
8+
* @type {Object} StepsTokens
9+
* @property {number} prizes - Number of prizes to win
10+
* @property {number} tokens - Minimum number of tokens to move the A/B buttons
11+
*/
12+
export type StepsTokens = {
13+
prizes: number;
14+
tokens: number;
15+
}

src/2024/2024-12-13/lib/utils.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { Point } from '@/aoc/point/types.js'
2+
3+
/**
4+
* Counts the determinant of a 2x2 coefficient matrix. Currently only works with a 2x2 number matrix.
5+
* TO-DO: Support N-size matrices.
6+
* @param {number[][]} matrix - 2D number matrix input
7+
* @returns {number} Determinant of a 2x2 matrix
8+
*/
9+
export const getDeterminant = (matrix: number[][]): number => {
10+
const eq1 = matrix[0] as number[]
11+
const eq2 = matrix[1] as number[]
12+
13+
return ((eq1[0] as number) * (eq2[1] as number)) -
14+
((eq1[1] as number) * (eq2[0] as number))
15+
}
16+
17+
/**
18+
* Builds a coefficient matrix by replacing column values with the constants.
19+
* Currently only works with a 2x2 number matrix. TO-DO: Support N-size matrices.
20+
* @param {number[][]} matrix - 2D number matrix input
21+
* @param {number} column - Matrix column index to replace with constants. Index starts at `0`.
22+
* @returns {number[][]} Coefficient matrix
23+
*/
24+
export const coefficientMatrix = (
25+
matrix: number[][],
26+
column: number = 0
27+
): number[][] => {
28+
const maxCol = 2
29+
30+
return matrix.reduce((list: number[][], row, y) => {
31+
const items = row.reduce((sublist: number[], item, x) => {
32+
if (x === column) {
33+
sublist.push(matrix[y]![maxCol] as number)
34+
} else if (x !== maxCol) {
35+
sublist.push(item)
36+
}
37+
return sublist
38+
}, [])
39+
return [...list, items]
40+
}, [])
41+
}
42+
43+
/**
44+
* Calculates the number of needed (x,y) button A "and" B presses to reach the prize
45+
* by solving the 2 equations using Cramer's Rule.
46+
* https://www.youtube.com/watch?v=RdLo-9jh2EM. Currently only works with a 2x2 number matrix.
47+
* @param {number[][]} matrix - 2D number matrix input
48+
* @returns {Point} Object containing the number of `x` and `y` button presses
49+
*/
50+
export const solveEquation = (matrix: number[][]): Point => {
51+
const determinant = getDeterminant(matrix)
52+
53+
const xCoefficient = coefficientMatrix(matrix, 0)
54+
const yCoefficient = coefficientMatrix(matrix, 1)
55+
56+
const xD = getDeterminant(xCoefficient)
57+
const yD = getDeterminant(yCoefficient)
58+
59+
const x = xD / determinant
60+
const y = yD / determinant
61+
62+
return { x, y }
63+
}

src/2024/2024-12-13/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 { countTokensPrizes } from './lib/count.js'
3+
4+
const data = fileReader('../input.txt')
5+
6+
/**
7+
* Part 1/2 of the 2024-12-14 quiz
8+
* Counts the minimum number of tokens needed to win all possible prizes.
9+
*/
10+
const quiz20241213_01 = () => {
11+
const { prizes, tokens } = countTokensPrizes(data, true)
12+
13+
console.log('All prizes', prizes)
14+
console.log('Minimum tokens', tokens)
15+
}
16+
17+
quiz20241213_01()

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { test, expect } from 'vitest'
2+
3+
import { fileReader } from './lib/fileReader.js'
4+
import { countTokensPrizes } from './lib/count.js'
5+
6+
const data = fileReader('../input.txt')
7+
8+
test('Number of tokens and prizes', () => {
9+
expect(countTokensPrizes(data).prizes).toBe(2)
10+
expect(countTokensPrizes(data).tokens).toBe(480)
11+
})

src/aoc/grid/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const isOutOfBounds = (point: Point, gridMeta: GridDimensions): boolean =
6767

6868
/**
6969
* Retrieves the four (4) diagonally aligned (y,x) coordinates and the symbol character from a `Point` in the grid.
70-
* Substitutes a `"*"` symbol character in the `PointSymbol.symbol`if the `point` is out of the grid bounds.
70+
* Substitutes a `"*"` symbol character in the `PointSymbol.symbol` to coordinates out of the grid bounds.
7171
* @param {Point} point - (y,x) coordinate object in a 2D array grid
7272
* @param {string[][]} data - 2D string array input
7373
* @returns {PointSymbol[]} Array of `PointSymbol` with symbol characters.
@@ -96,7 +96,7 @@ export const getDiagonalNeighbors = (point: Point, data: string[][]): PointSymbo
9696

9797
/**
9898
* Retrieves the four (4) horizontal/vertical aligned (y,x) coordinates and the symbol character from a `Point` in the grid.
99-
* Substitutes a `"*"` symbol character in the `PointSymbol.symbol`if the `point` is out of the grid bounds.
99+
* Substitutes a `"*"` symbol character in the `PointSymbol.symbol` to coordinates out of the grid bounds.
100100
* @param {Point} point - (y,x) coordinate object in a 2D array grid
101101
* @param {string[][]} data - 2D string array input
102102
* @returns {PointSymbol[]} Array of `PointSymbol` with symbol characters.

0 commit comments

Comments
 (0)