Skip to content

feat: day 12 garden groups 20241212 #45

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 7 commits into from
Jan 7, 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
3 changes: 1 addition & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +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 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 or solutions using other languages from time to time.

### 🎄 Advent of Code Quiz Information

Expand All @@ -20,6 +22,7 @@ The codes are structured in a way that discusses and walks through the solution
- 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)

</details>

Expand Down Expand Up @@ -67,6 +70,7 @@ It follows the directory structure:
Each Advent of Code (AOC) event quiz has its folder under **`"/src/<YEAR>/<YYYY-MM-DD>"`** 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
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/2024/2024-12-10/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/2024/2024-12-11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
56 changes: 56 additions & 0 deletions src/2024/2024-12-12/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## Day 12: Garden Groups

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

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

## Code

### `garden.ts`

- **Garden** class
- A set of methods and properties for calculating Garden, Region, and Plots data
- `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

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

5. Thoughts/tips across the subreddit/google:
- no. of corners = no. of sides 😉

## References

<sup>[[1]](https://en.wikipedia.org/wiki/Flood_fill)</sup> Flood Fill Algorithm
Binary file added src/2024/2024-12-12/assets/garden_03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/2024/2024-12-12/assets/garden_04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/2024/2024-12-12/assets/gardern_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/2024/2024-12-12/assets/gardern_02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/2024/2024-12-12/input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ZZZZ
XXCD
XXCC
YYYC
5 changes: 5 additions & 0 deletions src/2024/2024-12-12/input2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
AAAAA
AXAXA
AAAAA
AXAXA
AAAAA
10 changes: 10 additions & 0 deletions src/2024/2024-12-12/input3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
AAAAIICCGG
AAAAIICCCG
XXAAACCGGG
XXACCCJGGG
XXXXCJJCGP
XXIXCCJJPP
XXIIICJJPP
MIIIIIJJPP
MIIISIJPPP
MMMISSJPPP
5 changes: 5 additions & 0 deletions src/2024/2024-12-12/input4.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PPPPP
PEEEE
PPPPP
PEEEE
PPPPP
6 changes: 6 additions & 0 deletions src/2024/2024-12-12/input5.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
AAAAAA
AAABBA
AAABBA
ABBAAA
ABBAAA
AAAAAA
106 changes: 106 additions & 0 deletions src/2024/2024-12-12/lib/garden.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
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 per-plot perimeter 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 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
* @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 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[][], 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) {
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

if (log) {
console.log(`A region of ${symbol} plants with price ${area} * ${perimeter} = ${price}`)
}
}
}
}

return this.totalPrice
}
}
40 changes: 40 additions & 0 deletions src/2024/2024-12-12/lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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} 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
* @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[][];
}

export interface NeighborPoint extends Point {
symbol: string;
}
Loading
Loading