From 0be1d07ef21e4dc17c1908c24c7116b3cb32b85e Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Mon, 30 Dec 2024 22:52:07 +0800 Subject: [PATCH 1/7] docs: update aoc file reader docs --- src/utils/aocInputFile.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/utils/aocInputFile.ts b/src/utils/aocInputFile.ts index 56e99f2..d185356 100644 --- a/src/utils/aocInputFile.ts +++ b/src/utils/aocInputFile.ts @@ -1,6 +1,14 @@ // This code contains file input readers for common AoC input types. import { readFile } from './file.js' +/** + * Definitions for the privimite types (`AOCOutputType`) in which to convert the AoC quiz input + * - `STRING` - String of text: `string` + * - `STRING_ARRAY`- Array of strings: `string[]` + * - `STRING_ARRAY_2D` - 2D array of strings: `string[][]` + * - `NUMBER_ARRAY` - Array of numbers: `number[]` + * - `NUMBER_ARRAY_2D` - 2D array of numbers: `number[][]` + */ export enum AOC_OUTPUT_TYPE { STRING = 'string', STRING_ARRAY = 'string_array', @@ -9,6 +17,10 @@ export enum AOC_OUTPUT_TYPE { NUMBER_ARRAY_2D = '2d_number_array' } +/** + * Input parameters indicating details about the AoC quiz input file. + * @para, + */ type FileInput = { /** @param filePath {string} Full file path to input file */ filePath: string; @@ -16,18 +28,22 @@ type FileInput = { delimiter?: string; } -type Output = string | string[] | string[][] | +/** + * Represents common primitive types in which to convert the AoC quiz input + */ +type AOCOutputType = string | string[] | string[][] | number[] | number[][] /** - * Reads common AoC input text files. - * @typedef param {FileInput} File input definitions - * @param param.filePath {string} Full file path to an input text file - * @param param.type {AOC_OUTPUT_TYPE} Type to convert the input text file - * @param param.delimiter {string} String delimiter + * Reads common AoC input text files and converts them into one of `AOCOutputType` for data processing. + * @typedef {FileInput} param File input definitions + * @param {string} param.filePath Full file path to an input text file + * @param {AOC_OUTPUT_TYPE} param.type Type to convert the input text file. One of `AOC_OUTPUT_TYPE`. + * @param {string} param.delimiter String delimiter to `split()` between characters in the original text file. Defaults to none. + * @returns {AOCOutputType} Input text file converted into one of `AOCOutputType` * @throws {Error} */ -export const readAOCInputFile = (param: FileInput): Output => { +export const readAOCInputFile = (param: FileInput): AOCOutputType => { const file = readFile(param.filePath) const delimiter = param?.delimiter ?? '' From 783a5d04f481ddbcd2f31569c8e2b17b876413c2 Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Mon, 30 Dec 2024 23:05:29 +0800 Subject: [PATCH 2/7] docs: update aoc file reader notes --- src/utils/aocInputFile.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/utils/aocInputFile.ts b/src/utils/aocInputFile.ts index d185356..5739a1e 100644 --- a/src/utils/aocInputFile.ts +++ b/src/utils/aocInputFile.ts @@ -2,7 +2,7 @@ import { readFile } from './file.js' /** - * Definitions for the privimite types (`AOCOutputType`) in which to convert the AoC quiz input + * Definitions of the privimite types (`AOCFileOutput`) in which to convert the AoC quiz input text file. * - `STRING` - String of text: `string` * - `STRING_ARRAY`- Array of strings: `string[]` * - `STRING_ARRAY_2D` - 2D array of strings: `string[][]` @@ -19,10 +19,11 @@ export enum AOC_OUTPUT_TYPE { /** * Input parameters indicating details about the AoC quiz input file. - * @para, + * @param {string} filePath - Full file path to and input text file. + * @param {AOC_OUTPUT_TYPE} type - Type to convert the input text file. One of `AOC_OUTPUT_TYPE`. + * @param {delimiter} delimiter - String delimiter to `split()` between characters in the original text file. Defaults to none. */ -type FileInput = { - /** @param filePath {string} Full file path to input file */ +type AOCFileInput = { filePath: string; type: AOC_OUTPUT_TYPE; delimiter?: string; @@ -31,19 +32,17 @@ type FileInput = { /** * Represents common primitive types in which to convert the AoC quiz input */ -type AOCOutputType = string | string[] | string[][] | +type AOCFileOutput = string | string[] | string[][] | number[] | number[][] /** - * Reads common AoC input text files and converts them into one of `AOCOutputType` for data processing. - * @typedef {FileInput} param File input definitions - * @param {string} param.filePath Full file path to an input text file - * @param {AOC_OUTPUT_TYPE} param.type Type to convert the input text file. One of `AOC_OUTPUT_TYPE`. - * @param {string} param.delimiter String delimiter to `split()` between characters in the original text file. Defaults to none. - * @returns {AOCOutputType} Input text file converted into one of `AOCOutputType` + * Reads common AoC input text files and converts them into one of `AOCFileOutput` for data processing. + * @param {AOCFileInput} param File input definitions. + * - See `AOCFileInput` type for detailed definitions of the properties. + * @returns {AOCFileOutput} Input text file converted into one of `AOCFileOutput` * @throws {Error} */ -export const readAOCInputFile = (param: FileInput): AOCOutputType => { +export const readAOCInputFile = (param: AOCFileInput): AOCFileOutput => { const file = readFile(param.filePath) const delimiter = param?.delimiter ?? '' From fc35b1d5f7d58c57f1c197d6df3155c756b814b3 Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Tue, 31 Dec 2024 23:14:15 +0800 Subject: [PATCH 3/7] feat: day 12 garden groups 1/2 soln, #17 --- README.md | 5 +- src/2024/2024-12-10/lib/utils.ts | 2 +- src/2024/2024-12-11/README.md | 3 +- src/2024/2024-12-12/README.md | 42 +++++++++ src/2024/2024-12-12/assets/gardern_01.png | Bin 0 -> 15928 bytes src/2024/2024-12-12/assets/gardern_02.png | Bin 0 -> 14422 bytes src/2024/2024-12-12/input.txt | 10 +++ src/2024/2024-12-12/input2.txt | 10 +++ src/2024/2024-12-12/lib/garden.ts | 102 ++++++++++++++++++++++ src/2024/2024-12-12/lib/types.ts | 26 ++++++ src/2024/2024-12-12/lib/utils.ts | 40 +++++++++ src/2024/2024-12-12/main.ts | 17 ++++ src/2024/2024-12-12/sample.test.ts | 17 ++++ src/utils/aocInputFile.ts | 4 +- 14 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 src/2024/2024-12-12/README.md create mode 100644 src/2024/2024-12-12/assets/gardern_01.png create mode 100644 src/2024/2024-12-12/assets/gardern_02.png create mode 100644 src/2024/2024-12-12/input.txt create mode 100644 src/2024/2024-12-12/input2.txt create mode 100644 src/2024/2024-12-12/lib/garden.ts create mode 100644 src/2024/2024-12-12/lib/types.ts create mode 100644 src/2024/2024-12-12/lib/utils.ts create mode 100644 src/2024/2024-12-12/main.ts create mode 100644 src/2024/2024-12-12/sample.test.ts diff --git a/README.md b/README.md index dfc92f4..4531894 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ This repository contains solutions and a local development environment for the [Advent of Code](https://adventofcode.com/) event puzzles using TypeScript/JavaScript. -The codes are structured in a way that discusses and walks through the solution steps for the AoC quizzes rather than focusing on AoC's competitive programming. +- The code repository structure follows a way that discusses and walks through the solution steps for the AoC quizzes rather than focusing on AoC's competitive programming. +- The quizzes were solved for fun (unlocking the 2024 AoC Chrismas symbol 🎄) and brain exercise purposes. Therefore, no GPT or AI completion was used for solving, as advised on the AoC website. +- These codes may get occasional optimization updates and solutions using other languages from time to time. ### 🎄 Advent of Code Quiz Information @@ -67,6 +69,7 @@ It follows the directory structure: Each Advent of Code (AOC) event quiz has its folder under **`"/src//"`** containing: - **/lib**: Folder containing main quiz solution logic - **input.txt**: Random quiz input + > _**INFO:** The sample quiz inputs were slightly altered from the original AoC input text and quiz samples as advised on their website_ - **main.ts**: Main program entry point containing quiz answer(s) using random input - **sample.test.ts**: Minimal sample input with expected correct answers - **README.md**: Reference and other notes about the AOC quiz question diff --git a/src/2024/2024-12-10/lib/utils.ts b/src/2024/2024-12-10/lib/utils.ts index dcb90f6..ac34313 100644 --- a/src/2024/2024-12-10/lib/utils.ts +++ b/src/2024/2024-12-10/lib/utils.ts @@ -8,7 +8,7 @@ import type { GridCoordinateSymbol, PointSteps } from './types.js' * @param {number[][]} data - 2D number array containing hiking trail data * @returns {GridCoordinateSymbol} Returns the `poiint` (x,y) coordinate expressed in string and its value */ -export const getCoordinateSymbol = (point: Point, data: number[][]): GridCoordinateSymbol => { +export const getCoordinateSymbol = (point: Point, data: number[][] | string[][]): GridCoordinateSymbol => { return { coordinate: `${point!.x},${point!.y}`, symbol: data[point!.y]![point!.x] as number diff --git a/src/2024/2024-12-11/README.md b/src/2024/2024-12-11/README.md index 1b3ca56..2be84da 100644 --- a/src/2024/2024-12-11/README.md +++ b/src/2024/2024-12-11/README.md @@ -7,8 +7,9 @@ Visit the Advent of Code website for more information on this puzzle at: ## Code -### `utils.ts` - contains helper and utility scripts for the quiz. +### `utils.ts` +- contains helper and utility scripts for the quiz. - `isEvenDigits()` - Checks if a number has an even number of digits - `halfDigit()` - Divides the number into two (2) separate groups (digits) if they have an even number of digits, each group having half the original number's digits. - `arrayToObject()` - Converts an array of numbers to an Object, using the array number elements as keys with a default numeric value of `1` diff --git a/src/2024/2024-12-12/README.md b/src/2024/2024-12-12/README.md new file mode 100644 index 0000000..375e990 --- /dev/null +++ b/src/2024/2024-12-12/README.md @@ -0,0 +1,42 @@ +## Day 12: Garden Groups + +Visit the Advent of Code website for more information on this puzzle at: + +**Source:** https://adventofcode.com/2024/day/12
+**Status:** On-going ⭐ + +## Code + +### `garden.ts` + +- **Garden** class + - A set of methods and properties for calculating Garden, Region, and Plots data + - `Garden.calculatePlot()` - Calculates the perimeter and area of all garden regions in a 2D grid, each defined by an initial symbol at a starting (y,x) coordinate. + - `Garden.calculateFencePrice()` - Calculates the total fencing price of all regions in a garden. + +### `utils.ts` + +- A script containing helper and utility scripts for the quiz +- `findNeighbors()` - Finds all coordinates of the neighboring plots from a specified coordinate (up/down/left/right) +- `isIllegalCoordinate()` - Checks if a point at coordinate (y,x) in a grid is illegal: if it's out of bounds of the grid area or if its symbol differs from the symbol parameter. + + +## Notes + +1. Garden Plot + - only 1 plant + - single letter + - 4 sides + +2. Region + - garden plots that are touching (vertically/horizontally) + +3. Region: Area + - number of garden plots in the region + +4. Region: Perimeter + - no. of sides of garden plots in the region that do not touch another garden plot + +## References + +[[1]](https://en.wikipedia.org/wiki/Flood_fill) Flood Fill Algorithm \ No newline at end of file diff --git a/src/2024/2024-12-12/assets/gardern_01.png b/src/2024/2024-12-12/assets/gardern_01.png new file mode 100644 index 0000000000000000000000000000000000000000..0b09ab10efc5f2a9afe1519a8e0ce480c2f99a40 GIT binary patch literal 15928 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 I@=ehH0t%?KmjD0& literal 0 HcmV?d00001 diff --git a/src/2024/2024-12-12/assets/gardern_02.png b/src/2024/2024-12-12/assets/gardern_02.png new file mode 100644 index 0000000000000000000000000000000000000000..9dda47447a34c350a3c1c03b7d57770b408ab72c GIT binary patch literal 14422 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 b;S9S9!l<-ycYYl>2au$goM@i#vp4?-0r3uR literal 0 HcmV?d00001 diff --git a/src/2024/2024-12-12/input.txt b/src/2024/2024-12-12/input.txt new file mode 100644 index 0000000..5213deb --- /dev/null +++ b/src/2024/2024-12-12/input.txt @@ -0,0 +1,10 @@ +AAAAIICCGG +AAAAIICCCG +XXAAACCGGG +XXACCCJGGG +XXXXCJJCGP +XXIXCCJJPP +XXIIICJJPP +MIIIIIJJPP +MIIISIJPPP +MMMISSJPPP \ No newline at end of file diff --git a/src/2024/2024-12-12/input2.txt b/src/2024/2024-12-12/input2.txt new file mode 100644 index 0000000..df26536 --- /dev/null +++ b/src/2024/2024-12-12/input2.txt @@ -0,0 +1,10 @@ +....II.... +....II.... +.......... +.......... +.......... +..I....... +..III..... +.IIIII.... +.III.I.... +...I...... \ No newline at end of file diff --git a/src/2024/2024-12-12/lib/garden.ts b/src/2024/2024-12-12/lib/garden.ts new file mode 100644 index 0000000..0b7c1a5 --- /dev/null +++ b/src/2024/2024-12-12/lib/garden.ts @@ -0,0 +1,102 @@ +import type { GardenRegionDetails } from './types.js' +import type { GridDimensions } from '@/2024/2024-12-06/lib/grid.types.js' +import type { Point } from '@/2024/2024-12-08/lib/types.js' + +import { findNeighbors, isIllegalCoordinate } from './utils.js' + +/** + * @class Garden + * @description A set of methods and properties for calculating Garden, Region and Plots data + */ +export class Garden { + /** Temporary storage containing processed coordinates */ + processed: string[] = [] + + /** Total fencing price for all regions in the garden */ + totalPrice: number = 0 + + /** + * Calculates the perimeter and area of all garden regions in a 2D grid, each defined + * by an initial symbol at a starting (y,x) coordinate. + * Reference: https://en.wikipedia.org/wiki/Flood_fill + * @param {Point} point - (y,x) coordinate object in a 2D array grid + * @param {string} symbol - Character to check in the `point` coordinate. + * @param {string[][]} grid - 2D string array input + * @returns {GardenRegionDetails} Area and perimeter of a garden region. + */ + calculatePlot (point: Point, symbol: string, grid: GridDimensions, data: string[][]): GardenRegionDetails { + let list: Point[] = [point] + let perimeter = 0 + let area = 0 + + while (list.length > 0) { + const point = list.pop() + const coordStr = `${point!.x},${point!.y}` + + const isIllegal = isIllegalCoordinate({ + point: point as Point, + symbol, + gridMeta: grid, + grid: data + }) + + // Illegal coordinates count as edges (perimeters) + if (isIllegal) { + perimeter += 1 + continue + } + + if (this.processed.includes(coordStr)) continue + this.processed.push(coordStr) + + // Flood-fill included points + area += 1 + + const steps = findNeighbors(point as Point) as Point[] + list = [...list, ...steps] + } + + return { + area, perimeter + } + } + + /** + * Calculates the total fencing price of all regions in a garden. + * @param {string[][]} data - 2D string array input + * @returns {number} Total fencing price + */ + calculateFencePrice (data: string[][]): number { + const gridMeta = { + width: data[0]!.length, + length: data.length + } + + this.processed = [] + + for (let y = 0; y < data.length; y += 1) { + for (let x = 0; x < data[0]!.length; x += 1) { + const coordKey = `${x},${y}` + + if (!this.processed.includes(coordKey)) { + const point = { x, y } + const symbol = data[y]![x] + + const { area, perimeter } = this.calculatePlot( + point, + symbol as string, + gridMeta, + data + ) + + const price = area * perimeter + this.totalPrice += price + + console.log(`A region of ${symbol} plants with price ${area} * ${perimeter} = ${price}`) + } + } + } + + return this.totalPrice + } +} diff --git a/src/2024/2024-12-12/lib/types.ts b/src/2024/2024-12-12/lib/types.ts new file mode 100644 index 0000000..9e01499 --- /dev/null +++ b/src/2024/2024-12-12/lib/types.ts @@ -0,0 +1,26 @@ +import type { GridDimensions } from '@/2024/2024-12-06/lib/grid.types.js' +import type { Point } from '@/2024/2024-12-08/lib/types.js' + +/** + * @type {Object} GardenRegionDetails + * @property {number} area - Area of a garden region - sum of connected plots with similar symbols. + * @property {number} perimeter - Perimeter of a garden region - sum of outer edges per plot. + */ +export type GardenRegionDetails = { + area: number; + perimeter: number; +} + +/** + * @type {Object} IllegalCoordinateParams + * @property {Point} point - (y,x) coordinate object in a 2D array grid + * @property {string} symbol - Character to check in the `point` coordinate. + * @property {GridDimensions} gridMeta - Grid length and width + * @property {string[][]} grid - 2D string array input + */ +export type IllegalCoordinateParams = { + point: Point; + symbol: string; + gridMeta: GridDimensions; + grid: string[][]; +} diff --git a/src/2024/2024-12-12/lib/utils.ts b/src/2024/2024-12-12/lib/utils.ts new file mode 100644 index 0000000..d1d0ac5 --- /dev/null +++ b/src/2024/2024-12-12/lib/utils.ts @@ -0,0 +1,40 @@ +import type { Point } from '@/2024/2024-12-08/lib/types.js' +import type { IllegalCoordinateParams } from './types.js' + +/** + * Finds all coordinates of the neighboring plots from a specified coordinate (up/down/left/right) + * @param {Point} point - (y,x) coordinate object in a 2D array grid + * @param {GridDimensions} gridMeta - grid length and width + * @param {string[][]} grid - 2D string array input + * @returns {PointSteps[]} Array of neighboring similar plants from the given `point` + */ +export const findNeighbors = (point: Point): Point[] | undefined => { + // All 4 possible locations (directions) from a coordinate + const steps = [ + { ...point, x: point.x - 1 }, // left + { ...point, x: point.x + 1 }, // right + { ...point, y: point.y - 1 }, // down + { ...point, y: point.y + 1 } // up + ] + + return steps +} + +/** + * Checks if a point at coordinate (y,x) in a grid is illegal: if it's out of bounds of the grid area + * or if its symbol differs from the symbol parameter. + * @param params {IllegalCoordinateParams} - Object input parameters. + * See the `IllegalCoordinateParams` for more information. + * @returns {boolean} Flag if a coordinate is illegae + */ +export const isIllegalCoordinate = (params: IllegalCoordinateParams): boolean => { + const { point, symbol, gridMeta } = params + + return ( + point.x < 0 || + point.x >= gridMeta.width || + point.y < 0 || + point.y >= gridMeta.length || + params.grid[point.y]![point.x] !== symbol + ) +} diff --git a/src/2024/2024-12-12/main.ts b/src/2024/2024-12-12/main.ts new file mode 100644 index 0000000..3064c63 --- /dev/null +++ b/src/2024/2024-12-12/main.ts @@ -0,0 +1,17 @@ +import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/utils/aocInputFile.js' +import { file } from '@/utils/file.js' + +import { Garden } from './lib/garden.js' + +const input = readAOCInputFile({ + filePath: file(import.meta.url, 'input.txt'), + type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D +}) as string[][] + +const quiz20241231_01 = () => { + const garden = new Garden() + const totalPrice = garden.calculateFencePrice(input) + console.log('---Total fencing price', totalPrice) +} + +quiz20241231_01() diff --git a/src/2024/2024-12-12/sample.test.ts b/src/2024/2024-12-12/sample.test.ts new file mode 100644 index 0000000..cdd8e3e --- /dev/null +++ b/src/2024/2024-12-12/sample.test.ts @@ -0,0 +1,17 @@ +import { test, expect } from 'vitest' +import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/utils/aocInputFile.js' +import { file } from '@/utils/file.js' + +import { Garden } from './lib/garden.js' + +const input = readAOCInputFile({ + filePath: file(import.meta.url, 'input.txt'), + type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D +}) as string[][] + +test('Number of stones after blinking:', () => { + const garden = new Garden() + const totalPrice = garden.calculateFencePrice(input) + + expect(totalPrice).toBe(1930) +}) diff --git a/src/utils/aocInputFile.ts b/src/utils/aocInputFile.ts index 5739a1e..e5efea3 100644 --- a/src/utils/aocInputFile.ts +++ b/src/utils/aocInputFile.ts @@ -19,7 +19,7 @@ export enum AOC_OUTPUT_TYPE { /** * Input parameters indicating details about the AoC quiz input file. - * @param {string} filePath - Full file path to and input text file. + * @param {string} filePath - Full file path to an AoC input text file. * @param {AOC_OUTPUT_TYPE} type - Type to convert the input text file. One of `AOC_OUTPUT_TYPE`. * @param {delimiter} delimiter - String delimiter to `split()` between characters in the original text file. Defaults to none. */ @@ -64,7 +64,7 @@ export const readAOCInputFile = (param: AOCFileInput): AOCFileOutput => { case AOC_OUTPUT_TYPE.NUMBER_ARRAY: return file - .split('') + .split(delimiter) .map(Number) as number[] || [] case AOC_OUTPUT_TYPE.NUMBER_ARRAY_2D: From 8d50da7b8aeaf8cd58b7a1337a54906667d7a2f7 Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Tue, 7 Jan 2025 12:41:17 +0800 Subject: [PATCH 4/7] chore: tsc --noEmit on test --- .github/workflows/test.yaml | 3 +-- package.json | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e859aab..53509dc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -26,8 +26,7 @@ jobs: run: npm run lint - name: Transpile - run: npm run transpile + run: npm run transpile:noemit - name: Test run: npm test - diff --git a/package.json b/package.json index 270121f..c60f7a1 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dev": "vitest", "dev:debug": "vite-node src/sample/sample.ts", "transpile": "tsc -p tsconfig.json && tsc-alias", + "transpile:noemit": "tsc -p tsconfig.json --noEmit", "watch": "tsc -p tsconfig.json --watch", "watch:docker:win": "tsc -p tsconfig.json --watch --watchFile dynamicPriorityPolling --watchDirectory dynamicPriorityPolling", "lint": "eslint \"src/**/*.ts\" *.mjs *.ts", From 1bd8f1f955e2d6ff4e4f17dc825f029bb44178b7 Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Tue, 7 Jan 2025 12:42:20 +0800 Subject: [PATCH 5/7] chore: add common aoc grid type * chore: add async aoc file reader --- src/utils/aocInputFile.ts | 18 +++++++++++++++++- src/utils/grid/utils.ts | 13 +++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/utils/grid/utils.ts diff --git a/src/utils/aocInputFile.ts b/src/utils/aocInputFile.ts index e5efea3..690acae 100644 --- a/src/utils/aocInputFile.ts +++ b/src/utils/aocInputFile.ts @@ -32,7 +32,7 @@ type AOCFileInput = { /** * Represents common primitive types in which to convert the AoC quiz input */ -type AOCFileOutput = string | string[] | string[][] | +export type AOCFileOutput = string | string[] | string[][] | number[] | number[][] /** @@ -76,3 +76,19 @@ export const readAOCInputFile = (param: AOCFileInput): AOCFileOutput => { throw new Error('Unsupported type') } } + +/** + * Reads common AoC input text files asynchronously and converts them into one of `AOCFileOutput` for data processing. + * @param {AOCFileInput} param File input definitions. + * - See `AOCFileInput` type for detailed definitions of the properties. + * @returns {Promise} Input text file converted into one of `AOCFileOutput` + */ +export const readAOCInputFileAsync = (param: AOCFileInput): Promise => { + return new Promise((resolve, reject) => { + try { + resolve(readAOCInputFile(param) as T) + } catch (error) { + reject(error) + } + }) +} diff --git a/src/utils/grid/utils.ts b/src/utils/grid/utils.ts new file mode 100644 index 0000000..084dad2 --- /dev/null +++ b/src/utils/grid/utils.ts @@ -0,0 +1,13 @@ +import type { GridDimensions } from '@/2024/2024-12-06/lib/grid.types.js' + +/** + * Retrieves the length and width of a 2D string or number array + * @param {string[][] | number[][]} data - 2D string or number array + * @returns {GridDimensions} Object containig the length and width of the 2D array + */ +export const getGridDimensions = (data: T[][]): GridDimensions => { + return { + length: data.length, + width: data[0]!.length + } +} From 79242eba1d8d150aa6f9f42e8990b802998bf31e Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Tue, 7 Jan 2025 12:44:33 +0800 Subject: [PATCH 6/7] feat: day 12 garden groups 2/2 soln, #17 --- README.md | 5 +- src/2024/2024-12-12/README.md | 24 ++++- src/2024/2024-12-12/assets/garden_03.png | Bin 0 -> 22569 bytes src/2024/2024-12-12/assets/garden_04.png | Bin 0 -> 8448 bytes src/2024/2024-12-12/input.txt | 14 +-- src/2024/2024-12-12/input2.txt | 15 +-- src/2024/2024-12-12/input3.txt | 10 ++ src/2024/2024-12-12/input4.txt | 5 + src/2024/2024-12-12/input5.txt | 6 ++ src/2024/2024-12-12/lib/garden.ts | 14 ++- src/2024/2024-12-12/lib/types.ts | 14 +++ src/2024/2024-12-12/lib/utils.ts | 120 ++++++++++++++++++++++- src/2024/2024-12-12/lib/wholesale.ts | 102 +++++++++++++++++++ src/2024/2024-12-12/main.ts | 20 +++- src/2024/2024-12-12/sample.test.ts | 61 ++++++++++-- 15 files changed, 369 insertions(+), 41 deletions(-) create mode 100644 src/2024/2024-12-12/assets/garden_03.png create mode 100644 src/2024/2024-12-12/assets/garden_04.png create mode 100644 src/2024/2024-12-12/input3.txt create mode 100644 src/2024/2024-12-12/input4.txt create mode 100644 src/2024/2024-12-12/input5.txt create mode 100644 src/2024/2024-12-12/lib/wholesale.ts diff --git a/README.md b/README.md index 4531894..82f0537 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ ## ✨ adventofcode -This repository contains solutions and a local development environment for the [Advent of Code](https://adventofcode.com/) event puzzles using TypeScript/JavaScript. +This repository contains solutions and a local development environment for the [Advent of Code](https://adventofcode.com/) event puzzles using **TypeScript/JavaScript**. - The code repository structure follows a way that discusses and walks through the solution steps for the AoC quizzes rather than focusing on AoC's competitive programming. - The quizzes were solved for fun (unlocking the 2024 AoC Chrismas symbol 🎄) and brain exercise purposes. Therefore, no GPT or AI completion was used for solving, as advised on the AoC website. -- These codes may get occasional optimization updates and solutions using other languages from time to time. +- These codes may get occasional optimization updates or solutions using other languages from time to time. ### 🎄 Advent of Code Quiz Information @@ -22,6 +22,7 @@ This repository contains solutions and a local development environment for the [ - Day 9: Disk Fragmenter [[link]](/src/2024/2024-12-09/README.md) - Day 10: Hoof It [[link]](/src/2024/2024-12-10/README.md) - Day 11: Plutonian Pebbles [[link]](/src/2024/2024-12-11/README.md) +- Day 12: garden Groups [[link]](/src/2024/2024-12-12/README.md) diff --git a/src/2024/2024-12-12/README.md b/src/2024/2024-12-12/README.md index 375e990..7b758a7 100644 --- a/src/2024/2024-12-12/README.md +++ b/src/2024/2024-12-12/README.md @@ -3,7 +3,7 @@ Visit the Advent of Code website for more information on this puzzle at: **Source:** https://adventofcode.com/2024/day/12
-**Status:** On-going ⭐ +**Status:** Complete ⭐⭐ ## Code @@ -11,14 +11,25 @@ Visit the Advent of Code website for more information on this puzzle at: - **Garden** class - A set of methods and properties for calculating Garden, Region, and Plots data - - `Garden.calculatePlot()` - Calculates the perimeter and area of all garden regions in a 2D grid, each defined by an initial symbol at a starting (y,x) coordinate. - - `Garden.calculateFencePrice()` - Calculates the total fencing price of all regions in a garden. + - `Garden.calculatePlot()` - Calculates the per-plot perimeter and total area of all garden regions in a 2D grid, each defined by an initial symbol at a starting (y,x) coordinate. + - `Garden.calculateFencePrice()` - Calculates the total fencing price of all regions in a garden per connected plot using the formula: area * perimeter (per plot). + +### `wholesale.ts` + +- **WholesaleGarden** class + - A set of methods and properties for calculating the wholesale fencing price of garden regions + - `WholesaleGarden.calculateRegionCorners()` - Calculates the number of corners (sides) of a whole region. + - `WholesaleGarden.calculateFencePrice()` - Calculates the total fencing price of all regions in a garden using the formula: area * perimeter (of whole region). ### `utils.ts` - A script containing helper and utility scripts for the quiz - `findNeighbors()` - Finds all coordinates of the neighboring plots from a specified coordinate (up/down/left/right) - `isIllegalCoordinate()` - Checks if a point at coordinate (y,x) in a grid is illegal: if it's out of bounds of the grid area or if its symbol differs from the symbol parameter. +- `getDiagonalNeighbors()` - Retrieves the four (4) diagonally-aligned (y,x) coordinates and the symbol character from a `Point` in the grid. Substitutes a `"*"` symbol character in the `NeighborPoint.symbol`if the `point` is out of the grid bounds. +- `getCrossNeighbors()` - Retrieves the four (4) horizontal/vertical aligned (y,x) coordinates and the symbol character from a `Point` in the grid. Substitutes a `"*"` symbol character in the `NeighborPoint.symbol`if the `point` is out of the grid bounds. +- `isDiagonal()` - Checks if two (2) `Points` are diagonally-aligned +- `innerCorners()` - Counts the "inner" corners from groups of valid L-shaped `Points` that originate from a `Point` coordinate. ## Notes @@ -37,6 +48,11 @@ Visit the Advent of Code website for more information on this puzzle at: 4. Region: Perimeter - no. of sides of garden plots in the region that do not touch another garden plot +5. Thoughts/tips across the subreddit/google: + - no. of corners = no. of sides 😉 + +``` + ## References -[[1]](https://en.wikipedia.org/wiki/Flood_fill) Flood Fill Algorithm \ No newline at end of file +[[1]](https://en.wikipedia.org/wiki/Flood_fill) Flood Fill Algorithm diff --git a/src/2024/2024-12-12/assets/garden_03.png b/src/2024/2024-12-12/assets/garden_03.png new file mode 100644 index 0000000000000000000000000000000000000000..4f9a13dfcbc3a7a707362001b501fcb05408361d GIT binary patch literal 22569 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 CF|xA& literal 0 HcmV?d00001 diff --git a/src/2024/2024-12-12/input.txt b/src/2024/2024-12-12/input.txt index 5213deb..5e25500 100644 --- a/src/2024/2024-12-12/input.txt +++ b/src/2024/2024-12-12/input.txt @@ -1,10 +1,4 @@ -AAAAIICCGG -AAAAIICCCG -XXAAACCGGG -XXACCCJGGG -XXXXCJJCGP -XXIXCCJJPP -XXIIICJJPP -MIIIIIJJPP -MIIISIJPPP -MMMISSJPPP \ No newline at end of file +ZZZZ +XXCD +XXCC +YYYC \ No newline at end of file diff --git a/src/2024/2024-12-12/input2.txt b/src/2024/2024-12-12/input2.txt index df26536..80ae449 100644 --- a/src/2024/2024-12-12/input2.txt +++ b/src/2024/2024-12-12/input2.txt @@ -1,10 +1,5 @@ -....II.... -....II.... -.......... -.......... -.......... -..I....... -..III..... -.IIIII.... -.III.I.... -...I...... \ No newline at end of file +AAAAA +AXAXA +AAAAA +AXAXA +AAAAA \ No newline at end of file diff --git a/src/2024/2024-12-12/input3.txt b/src/2024/2024-12-12/input3.txt new file mode 100644 index 0000000..5213deb --- /dev/null +++ b/src/2024/2024-12-12/input3.txt @@ -0,0 +1,10 @@ +AAAAIICCGG +AAAAIICCCG +XXAAACCGGG +XXACCCJGGG +XXXXCJJCGP +XXIXCCJJPP +XXIIICJJPP +MIIIIIJJPP +MIIISIJPPP +MMMISSJPPP \ No newline at end of file diff --git a/src/2024/2024-12-12/input4.txt b/src/2024/2024-12-12/input4.txt new file mode 100644 index 0000000..603d5bf --- /dev/null +++ b/src/2024/2024-12-12/input4.txt @@ -0,0 +1,5 @@ +PPPPP +PEEEE +PPPPP +PEEEE +PPPPP \ No newline at end of file diff --git a/src/2024/2024-12-12/input5.txt b/src/2024/2024-12-12/input5.txt new file mode 100644 index 0000000..c7541ac --- /dev/null +++ b/src/2024/2024-12-12/input5.txt @@ -0,0 +1,6 @@ +AAAAAA +AAABBA +AAABBA +ABBAAA +ABBAAA +AAAAAA \ No newline at end of file diff --git a/src/2024/2024-12-12/lib/garden.ts b/src/2024/2024-12-12/lib/garden.ts index 0b7c1a5..7e29e38 100644 --- a/src/2024/2024-12-12/lib/garden.ts +++ b/src/2024/2024-12-12/lib/garden.ts @@ -6,7 +6,7 @@ import { findNeighbors, isIllegalCoordinate } from './utils.js' /** * @class Garden - * @description A set of methods and properties for calculating Garden, Region and Plots data + * @description A set of methods and properties for calculating Garden, Region and per-plot perimeter data */ export class Garden { /** Temporary storage containing processed coordinates */ @@ -16,7 +16,7 @@ export class Garden { totalPrice: number = 0 /** - * Calculates the perimeter and area of all garden regions in a 2D grid, each defined + * Calculates the per-plot perimeter and total area of all garden regions in a 2D grid, each defined * by an initial symbol at a starting (y,x) coordinate. * Reference: https://en.wikipedia.org/wiki/Flood_fill * @param {Point} point - (y,x) coordinate object in a 2D array grid @@ -62,17 +62,19 @@ export class Garden { } /** - * Calculates the total fencing price of all regions in a garden. + * Calculates the total fencing price of all regions in a garden per connected plot using the formula: area * perimeter (per plot). * @param {string[][]} data - 2D string array input + * @param {boolean} log - Flag to display the log messages. Defaults to `false` * @returns {number} Total fencing price */ - calculateFencePrice (data: string[][]): number { + calculateFencePrice (data: string[][], log: boolean = false): number { const gridMeta = { width: data[0]!.length, length: data.length } this.processed = [] + this.totalPrice = 0 for (let y = 0; y < data.length; y += 1) { for (let x = 0; x < data[0]!.length; x += 1) { @@ -92,7 +94,9 @@ export class Garden { const price = area * perimeter this.totalPrice += price - console.log(`A region of ${symbol} plants with price ${area} * ${perimeter} = ${price}`) + if (log) { + console.log(`A region of ${symbol} plants with price ${area} * ${perimeter} = ${price}`) + } } } } diff --git a/src/2024/2024-12-12/lib/types.ts b/src/2024/2024-12-12/lib/types.ts index 9e01499..4b05820 100644 --- a/src/2024/2024-12-12/lib/types.ts +++ b/src/2024/2024-12-12/lib/types.ts @@ -11,6 +11,16 @@ export type GardenRegionDetails = { perimeter: number; } +/** + * @type {Object} GardenRegionDetails + * @property {number} area - Area of a garden region - sum of connected plots with similar symbols. + * @property {number} sides - Number of sides (corners) of a region. + */ +export type GardenRegionSides = { + area: number; + sides: number; +} + /** * @type {Object} IllegalCoordinateParams * @property {Point} point - (y,x) coordinate object in a 2D array grid @@ -24,3 +34,7 @@ export type IllegalCoordinateParams = { gridMeta: GridDimensions; grid: string[][]; } + +export interface NeighborPoint extends Point { + symbol: string; +} diff --git a/src/2024/2024-12-12/lib/utils.ts b/src/2024/2024-12-12/lib/utils.ts index d1d0ac5..e44785d 100644 --- a/src/2024/2024-12-12/lib/utils.ts +++ b/src/2024/2024-12-12/lib/utils.ts @@ -1,5 +1,8 @@ import type { Point } from '@/2024/2024-12-08/lib/types.js' -import type { IllegalCoordinateParams } from './types.js' +import type { IllegalCoordinateParams, NeighborPoint } from './types.js' + +import { getGridDimensions } from '@/utils/grid/utils.js' +import { isOutOfBounds } from '@/2024/2024-12-10/lib/utils.js' /** * Finds all coordinates of the neighboring plots from a specified coordinate (up/down/left/right) @@ -38,3 +41,118 @@ export const isIllegalCoordinate = (params: IllegalCoordinateParams): boolean => params.grid[point.y]![point.x] !== symbol ) } + +/** + * Retrieves the four (4) diagonally-aligned (y,x) coordinates and the symbol character from a `Point` in the grid. + * Substitutes a `"*"` symbol character in the `NeighborPoint.symbol`if the `point` is out of the grid bounds. + * @param {Point} point - (y,x) coordinate object in a 2D array grid + * @param {string[][]} data - 2D string array input + * @returns {NeighborPoint[]} Array of `NeighborPoint` with symbol characters. + */ +export const getDiagonalNeighbors = (point: Point, data: string[][]): NeighborPoint[] => { + const grid = getGridDimensions(data) + + // diagonal coordinates + const diagonals = [ + { x: point.x + 1, y: point.y - 1 }, + { x: point.x - 1, y: point.y - 1 }, + { x: point.x + 1, y: point.y + 1 }, + { x: point.x - 1, y: point.y + 1 } + ] + + return diagonals.reduce((list: NeighborPoint[], pt) => { + const item = { x: pt.x, y: pt.y, symbol: '*' } + + if (!isOutOfBounds(pt, grid)) { + item.symbol = data[pt.y]![pt.x] as string + } + + return [...list, item] + }, []) +} + +/** + * Retrieves the four (4) horizontal/vertical aligned (y,x) coordinates and the symbol character from a `Point` in the grid. + * Substitutes a `"*"` symbol character in the `NeighborPoint.symbol`if the `point` is out of the grid bounds. + * @param {Point} point - (y,x) coordinate object in a 2D array grid + * @param {string[][]} data - 2D string array input + * @returns {NeighborPoint[]} Array of `NeighborPoint` with symbol characters. + */ +export const getCrossNeighbors = (point: Point, data: string[][]): NeighborPoint[] => { + const neighbors = findNeighbors(point) as Point[] + const grid = getGridDimensions(data) + + return neighbors.reduce((list: NeighborPoint[], pt) => { + const item = { x: pt.x, y: pt.y, symbol: '*' } + + if (!isOutOfBounds(pt, grid)) { + item.symbol = data[pt.y]![pt.x] as string + } + + return [...list, item] + }, []) +} + +/** + * Checks if two (2) `Points` are diagonally-aligned + * @param {Point} p1 - (y,x) Start `Point` + * @param {Point} p2 - (y,x) End `Point` + * @returns {boolean} Flag indicating if 2 `Points` are diagonally aligned + */ +export const isDiagonal = (p1: Point, p2: Point): boolean => { + const diff = { + x: Math.abs(p1.x - p2.x), + y: Math.abs(p1.y - p2.y) + } + + return diff.x == 1 && diff.y === 1 +} + +/** + * Counts the "inner" corners from groups of valid L-shaped `Points` that originate from a `Point` coordinate. + * @param {Point} point - Origin (y,x) coordinate object in a 2D array grid + * @param {string} symbol - Character to check in the `point` coordinate. + * @param {string[][]} data - 2D string array input + * @returns {number} Number of inner corners associated with a `Point` + */ +export const innerCorners = ( + point: Point, + symbol: string, + data: string[][] +): number => { + const { x, y } = point + const grid = getGridDimensions(data) + + const diagonals = getDiagonalNeighbors(point, data) + .filter(pt => pt.symbol !== symbol) + + let innerCount = 0 + + for (let i = 0; i < diagonals.length; i += 1) { + const diff = { x: diagonals[i]!.x - x, y: diagonals[i]!.y - y } + + const direction = { + x: diff.x > 0 ? 1 : -1, + y: diff.y > 0 ? 1 : -1 + } + + const vertical = { + x, y: point.y + 1 * direction.y + } + + const horizontal = { + x: point.x + 1 * direction.x, y + } + + if ( + !isOutOfBounds(vertical, grid) && + !isOutOfBounds(horizontal, grid) && + data[vertical.y]![vertical.x] === symbol && + data[horizontal.y]![horizontal.x] === symbol + ) { + innerCount += 1 + } + } + + return innerCount +} diff --git a/src/2024/2024-12-12/lib/wholesale.ts b/src/2024/2024-12-12/lib/wholesale.ts new file mode 100644 index 0000000..7fed7e2 --- /dev/null +++ b/src/2024/2024-12-12/lib/wholesale.ts @@ -0,0 +1,102 @@ +import type { Point } from '@/2024/2024-12-08/lib/types.js' + +import { isOutOfBounds } from '@/2024/2024-12-10/lib/utils.js' +import { getGridDimensions } from '@/utils/grid/utils.js' +import { getCrossNeighbors, innerCorners, isDiagonal } from './utils.js' +import type { GardenRegionSides } from './types.js' + +/** + * @class WholesaleGarden + * @description A set of methods and properties for calculating the wholesale fencing price of garden regions + */ +export class WholesaleGarden { + visited: string[] = [] + + /** + * Calculates the number of corners (sides) of a whole region. + * @param {Point} point - (y,x) coordinate object in a 2D array grid + * @param {string} symbol - Character to check in the `point` coordinate. + * @param {string[][]} data - 2D string array input + * @returns {GardenRegionDetails} Object containing the area and whole region perimeter + */ + calculateRegionCorners (point: Point, symbol: string, data: string[][]): GardenRegionSides { + const stack: Point[] = [point] + const grid = getGridDimensions(data) + + let corners = 0 + let area = 0 + + while (stack.length > 0) { + const pt = stack.pop() as Point + const coordKey = `${pt.x},${pt.y}` + + if (this.visited.includes(coordKey)) continue + + // Vertical/horizontal coordinates data + const neighbors = getCrossNeighbors(pt, data) + const invalidPoints = neighbors.filter(item => item.symbol !== symbol) + + const next = neighbors.filter(item => + !isOutOfBounds(item, grid) && + item.symbol === symbol + ) + + // Coordinates containing other symbols count as corners 2x + if (invalidPoints.length >= 3) { + corners += 2 * Math.floor(invalidPoints.length / 2) + } + + // Diagonally-aligned other symbols (if only 2) count as a corner + if (invalidPoints.length === 2) { + if (isDiagonal(invalidPoints[0] as Point, invalidPoints[1] as Point)) { + corners += 1 + } + } + + // Count the inner corner(s) in L-shaped Points + corners += innerCorners(pt, symbol, data) + area += 1 + + this.visited.push(coordKey) + next.forEach(item => stack.push(item)) + } + + return { + sides: corners, + area + } + } + + /** + * Calculates the total fencing price of all regions in a garden using the formula: area * perimeter (of whole region). + * @param {string[][]} data - 2D string array input + * @param {boolean} log - Flag to display the log messages. Defaults to `false` + * @returns {number} Total fencing price + */ + calculateFencePrice (data: string[][], log: boolean = false): number { + let total = 0 + const grid = getGridDimensions(data) + + this.visited = [] + + for (let y = 0; y < grid.length; y += 1) { + for (let x = 0; x < grid.width; x += 1) { + const point = { x, y } + const pointStr = `${x},${y}` + const symbol = data[y]![x] as string + + if (this.visited.includes(pointStr)) continue + + const { sides, area } = this.calculateRegionCorners(point, symbol, data) + const price = sides * area + total += price + + if (log) { + console.log(`Region ${symbol}: sides ${sides} * ${area} = ${price}`) + } + } + } + + return total + } +} diff --git a/src/2024/2024-12-12/main.ts b/src/2024/2024-12-12/main.ts index 3064c63..41993a2 100644 --- a/src/2024/2024-12-12/main.ts +++ b/src/2024/2024-12-12/main.ts @@ -2,16 +2,34 @@ import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/utils/aocInputFile.js' import { file } from '@/utils/file.js' import { Garden } from './lib/garden.js' +import { WholesaleGarden } from './lib/wholesale.js' const input = readAOCInputFile({ filePath: file(import.meta.url, 'input.txt'), type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D }) as string[][] +/** + * Part 1/2 of the 2024-12-12 quiz + * Counts the total fencing price: area * perimeter per plot + */ const quiz20241231_01 = () => { const garden = new Garden() - const totalPrice = garden.calculateFencePrice(input) + const totalPrice = garden.calculateFencePrice(input, true) + console.log('---Total fencing price', totalPrice) } +/** + * Part 2/2 of the 2024-12-12 quiz + * Counts the wholesale fencing price: area * perimeter of whole region (edges) + */ +const quiz20241231_02 = () => { + const garden = new WholesaleGarden() + const totalPrice = garden.calculateFencePrice(input, true) + + console.log('---Wholesale fencing price', totalPrice) +} + quiz20241231_01() +quiz20241231_02() diff --git a/src/2024/2024-12-12/sample.test.ts b/src/2024/2024-12-12/sample.test.ts index cdd8e3e..1becc11 100644 --- a/src/2024/2024-12-12/sample.test.ts +++ b/src/2024/2024-12-12/sample.test.ts @@ -1,17 +1,62 @@ import { test, expect } from 'vitest' -import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/utils/aocInputFile.js' +import { AOC_OUTPUT_TYPE } from '@/utils/aocInputFile.js' import { file } from '@/utils/file.js' +import { readAOCInputFileAsync } from '@/utils/aocInputFile.js' + import { Garden } from './lib/garden.js' +import { WholesaleGarden } from './lib/wholesale.js' + +const inputFiles = [ + 'input.txt', + 'input2.txt', + 'input3.txt', + 'input4.txt', + 'input5.txt' +] + +// Read input files +const files: Promise[] = [] -const input = readAOCInputFile({ - filePath: file(import.meta.url, 'input.txt'), - type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D -}) as string[][] +for (let i = 0; i < inputFiles.length; i += 1) { + files.push(readAOCInputFileAsync({ + filePath: file(import.meta.url, inputFiles[i] as string), + type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D + })) +} -test('Number of stones after blinking:', () => { +const [ + input, + input2, + input3, + input4, + input5 +] = await Promise.all(files) + +// Test area * perimeter per plot +test('Total fencing price', async () => { const garden = new Garden() - const totalPrice = garden.calculateFencePrice(input) - expect(totalPrice).toBe(1930) + const totalPrice = garden.calculateFencePrice(input as string[][]) + const totalPrice2 = garden.calculateFencePrice(input2 as string[][]) + const totalPrice3 = garden.calculateFencePrice(input3 as string[][]) + + expect(totalPrice).toBe(140) + expect(totalPrice2).toBe(772) + expect(totalPrice3).toBe(1930) +}) + +// Test area * perimeter per region (wholesale) +test('Wholesale fencing price', async () => { + const garden = new WholesaleGarden() + + const totalPrice = garden.calculateFencePrice(input as string[][]) + const totalPrice2 = garden.calculateFencePrice(input2 as string[][]) + const totalPrice4 = garden.calculateFencePrice(input4 as string[][]) + const totalPrice5 = garden.calculateFencePrice(input5 as string[][]) + + expect(totalPrice).toBe(80) + expect(totalPrice2).toBe(436) + expect(totalPrice4).toBe(236) + expect(totalPrice5).toBe(368) }) From aff86ab01ac3038a10482fb914c910a5b7b1ca73 Mon Sep 17 00:00:00 2001 From: weaponsforge Date: Tue, 7 Jan 2025 12:48:54 +0800 Subject: [PATCH 7/7] docs: fix typos --- README.md | 2 +- src/2024/2024-12-12/README.md | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 82f0537..05f923e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This repository contains solutions and a local development environment for the [ - Day 9: Disk Fragmenter [[link]](/src/2024/2024-12-09/README.md) - Day 10: Hoof It [[link]](/src/2024/2024-12-10/README.md) - Day 11: Plutonian Pebbles [[link]](/src/2024/2024-12-11/README.md) -- Day 12: garden Groups [[link]](/src/2024/2024-12-12/README.md) +- Day 12: Garden Groups [[link]](/src/2024/2024-12-12/README.md) diff --git a/src/2024/2024-12-12/README.md b/src/2024/2024-12-12/README.md index 7b758a7..38bafe6 100644 --- a/src/2024/2024-12-12/README.md +++ b/src/2024/2024-12-12/README.md @@ -51,8 +51,6 @@ Visit the Advent of Code website for more information on this puzzle at: 5. Thoughts/tips across the subreddit/google: - no. of corners = no. of sides 😉 -``` - ## References [[1]](https://en.wikipedia.org/wiki/Flood_fill) Flood Fill Algorithm