diff --git a/src/2024/2024-12-05/lib/fixOrderingUpdates.ts b/src/2024/2024-12-05/lib/fixOrderingUpdates.ts
index 98a6550..b7001eb 100644
--- a/src/2024/2024-12-05/lib/fixOrderingUpdates.ts
+++ b/src/2024/2024-12-05/lib/fixOrderingUpdates.ts
@@ -10,7 +10,7 @@ type CurrentItem = {
}
/**
- * Finds the next appropriate array index to swap places with the incorrectly placed current item accoring to the "rules"
+ * Finds the next appropriate array index to swap places with the incorrectly placed current item according to the "rules"
* @param rules {Rules} Object containing parsed and formatted rules and updates data
* @param restItems {number[]} "updates" array items content
* @param currentItem {CurrentItem} Object containing the "current" item data in focus: value and array index
@@ -44,30 +44,32 @@ export const fixOrdering = (rules: Rules, unorderedItems: number[]): number[] =>
throw new Error('Invalid item/s')
}
- for (let i = 0; i < unorderedItems.length - 1; i += 1) {
- let currentItem = unorderedItems[i] as number
- const currentItemData = { value: currentItem, index: i }
+ let currentItem: number = -2
- // Swaps incorrectly placed items with target items in the array
- const swapItems = () => {
- const indexToSwap = nextHotSwapIndex(
- rules,
- unorderedItems,
- currentItemData
- )
+ // Swaps incorrectly placed items with target items in the array
+ const swapItems = (activeItem: CurrentItem, activeIndex: number) => {
+ const indexToSwap = nextHotSwapIndex(
+ rules,
+ unorderedItems,
+ activeItem
+ )
- const temp = unorderedItems[indexToSwap] as number
- unorderedItems[indexToSwap] = currentItem
- unorderedItems[i] = temp
- currentItem = temp
+ const temp = unorderedItems[indexToSwap] as number
+ unorderedItems[indexToSwap] = activeItem.value
+ unorderedItems[activeIndex] = temp
+ currentItem = temp
- fixOrdering(rules, unorderedItems)
- }
+ fixOrdering(rules, unorderedItems)
+ }
+
+ for (let i = 0; i < unorderedItems.length - 1; i += 1) {
+ currentItem = unorderedItems[i] as number
+ const currentItemData = { value: currentItem, index: i }
// Correct "update" item should have en entry in the "rules" object
// Swap places with other items if its incorrect
if (rules[currentItem] === undefined) {
- swapItems()
+ swapItems(currentItemData, i)
}
// Get the rest of items after the current item for comparison
@@ -76,7 +78,7 @@ export const fixOrdering = (rules: Rules, unorderedItems: number[]): number[] =>
// Correct item's "rule" should have the after-item entries
// Swap places with other items if its incorrect
if (!afterItems.every(item => rules[currentItem]?.includes(item))) {
- swapItems()
+ swapItems(currentItemData, i)
}
}
diff --git a/src/2024/2024-12-06/README.md b/src/2024/2024-12-06/README.md
new file mode 100644
index 0000000..eb3a023
--- /dev/null
+++ b/src/2024/2024-12-06/README.md
@@ -0,0 +1,25 @@
+## Day 6: Guard Gallivant
+
+Visit the Advent of Code website for more information on this puzzle at:
+
+**Source:** https://adventofcode.com/2024/day/6
+**Status:** Complete ⭐⭐
+
+## Code
+
+1. **grid.ts**
+ - `Grid` - class that has a 2D array object containing paths and obstacles in which a `Guard` runs.
+ - Has functions and methods for managing a `Grid` object
+
+2. **guard.ts**
+ - `Guard` - class representing an object that can walk in the `Grid` map.
+ - Have methods for moving around in the `Grid`.
+
+3. **guardController.ts**
+ - `guardController()` - Runs the `Guard` on the `Grid`, counting distinct positions/steps.
+
+5. **guardControllerLoop.ts**
+ - `gridHasInfiniteLoop()`
+ - Runs the `Guard` on a `Grid` with obstacles, checking the placement of paths and obstacles that will make the `Guard` walk in an infinite loop
+ - `findObstructionPositions()` - Counts the number of positions in the `Grid` in which inserting one (1) obstruction symbol # will cause the `Guard` to walk in an infinite loop.
+ - > _**WARNING:** This is a very slow, unoptimized program execution that takes about ~3 mins to complete using the actual AoC input text._
diff --git a/src/2024/2024-12-06/input.txt b/src/2024/2024-12-06/input.txt
new file mode 100644
index 0000000..6d7bbf6
--- /dev/null
+++ b/src/2024/2024-12-06/input.txt
@@ -0,0 +1,10 @@
+....#.....
+.........#
+..........
+.#........
+..........
+.......#..
+.#..^.....
+.#......#.
+....#.....
+......#...
\ No newline at end of file
diff --git a/src/2024/2024-12-06/lib/grid.ts b/src/2024/2024-12-06/lib/grid.ts
new file mode 100644
index 0000000..38f6019
--- /dev/null
+++ b/src/2024/2024-12-06/lib/grid.ts
@@ -0,0 +1,126 @@
+import { GuardStatus, GuardDirection } from './guard.types.js'
+import type { GuardState } from './guard.types.js'
+
+
+/**
+ * @class Grid
+ * @description Object containing a 2D array of strings and other data in which a `Guard` object runs. Each item represents an open path or obstacle.
+ */
+export class Grid {
+ /** 2D board string array */
+ board: string[][] = []
+
+ /** Board length */
+ length: number = 0
+
+ /** Board width */
+ width: number = 0
+
+ /** Number of distinct positions that a `Guard` can traverse on the board */
+ positionCount: number = 0
+
+ /** Initial (x,y) coordinate starting position of a `Guard` */
+ start: { x: number; y: number; }
+
+ /** Obstruction symbol. Guards turn clockwise if this symbol blocks their next step. */
+ obstruction = '#'
+
+ /** Path symbol. Guards proceed to the next (x,y) coordinate after on this symbol. */
+ pathSymbol = '.'
+
+ /**
+ * @constructor
+ * @param {string[][]} data 2D string array containing paths `"."`, obstacles `"#"` and the `Guard` object
+ * @param obstructionSymbol
+ */
+ constructor (data: string[][], obstructionSymbol: string = '#') {
+ this.board = data
+ this.length = data.length
+ this.width = (data[0])?.length as number
+ this.obstruction = obstructionSymbol
+ this.start = { x: -1, y: -1 }
+
+ this.findGuardStartPosition()
+ }
+
+ /**
+ * Finds the initial position and state of the guard in a 2D board array
+ * @param data {string[][]} 2D string array of the guard board
+ * @returns {GuardState} Initial position and state of the guard
+ */
+ findGuardStartPosition (): GuardState {
+ const GuardDirections: string[] = Object.values(GuardDirection)
+
+ const initialState: GuardState = {
+ direction: null,
+ xPos: -1,
+ yPos: -1,
+ status: GuardStatus.IDLE
+ }
+
+ for (let y = 0; y < this.board.length; y += 1) {
+ const row = this.board[y]
+
+ if (row === undefined) {
+ throw new Error('Undefined row')
+ }
+
+ for (let i = 0; i < GuardDirections.length; i += 1) {
+ const indexOfDirection = row.indexOf(GuardDirections[i] as string)
+
+ if (indexOfDirection >= 0) {
+ initialState.direction = GuardDirections[i] as GuardDirection
+ initialState.xPos = indexOfDirection
+ initialState.yPos = y
+ initialState.status = GuardStatus.ACTIVE
+ break
+ }
+ }
+
+ if (initialState.status === GuardStatus.ACTIVE) break
+ }
+
+ this.start.x = initialState.xPos
+ this.start.y = initialState.yPos
+
+ return initialState
+ }
+
+ /**
+ * Checks if an (x,y) coordinate is outside the grid area
+ * @param {number} x array x coordinate
+ * @param {number} y array y coordinate
+ * @returns {boolean} Flag indicating if the guard is outside the grid
+ */
+ isOutOfBounds (x: number, y: number): boolean {
+ const row = this.board[0] as string[]
+
+ return x < 0 ||
+ x >= row.length ||
+ y < 0 ||
+ y >= this.board.length
+ }
+
+ /**
+ * Marks an (x,y) coordinate in the grid
+ * @param {number} x array x coordinate
+ * @param {number} y array y coordinate
+ * @returns {void}
+ */
+ mark (x: number, y: number): void {
+ const row = this.board[y]
+
+ if (row !== undefined && row[x] !== 'X') {
+ row[x] = 'X'
+ this.positionCount += 1
+ }
+ }
+
+ /**
+ * Displays the guard's distinct positions in the grid
+ */
+ print () {
+ console.clear()
+ console.log(this.board.map(row => row.join(' ')))
+ }
+}
diff --git a/src/2024/2024-12-06/lib/grid.types.ts b/src/2024/2024-12-06/lib/grid.types.ts
new file mode 100644
index 0000000..4c18219
--- /dev/null
+++ b/src/2024/2024-12-06/lib/grid.types.ts
@@ -0,0 +1,10 @@
+/**
+ * `Grid` dimensions properties
+ *
+ * @property {number} length - Length of the grid
+ * @property {number} wwidth - Width of the grid
+ */
+export type GridDimensions = {
+ length: number;
+ width: number;
+}
diff --git a/src/2024/2024-12-06/lib/guard.ts b/src/2024/2024-12-06/lib/guard.ts
new file mode 100644
index 0000000..71e5d2a
--- /dev/null
+++ b/src/2024/2024-12-06/lib/guard.ts
@@ -0,0 +1,65 @@
+import type { GuardState } from './guard.types.js'
+import { GuardDirection, GuardStatus } from './guard.types.js'
+
+/**
+ * @class Guard
+ * @description Game object that can move around in a 2D grid array
+ */
+export class Guard {
+ direction: GuardDirection | null = null
+ xPos: number = 0
+ yPos: number = 0
+ status: GuardStatus = GuardStatus.IDLE
+ steps: number = 0
+
+ /**
+ * Creates an instance of the `Guard` class
+ * @constructor
+ * @param {GuardState} state - Initial state of the guard
+ */
+ constructor (state: GuardState) {
+ this.direction = state.direction
+ this.xPos = state.xPos || 0
+ this.yPos = state.yPos || 0
+ this.status = state.status
+ }
+
+ /**
+ * Turn the guard's direction clockwise by 90 degrees.
+ * If the direction is `null`, does nothing.
+ * @returns {void}
+ */
+ turn (): void {
+ if (this.direction === null) return
+
+ const directions = Object.values(GuardDirection)
+ const currentDirectionIndex = directions.indexOf(this.direction)
+
+ const nextIndex = (currentDirectionIndex + 1) < directions.length
+ ? currentDirectionIndex + 1
+ : 0
+
+ this.direction = directions[nextIndex] as GuardDirection
+ }
+
+ /**
+ * Moves the guard by one (1) coordinate [x,y] position in the `Grid`
+ * @param xDirection {number} x coordinate
+ * @param yDirection {number} y coordinate
+ * @returns {void}
+ */
+ walk (xDirection: number, yDirection: number): void {
+ if (xDirection === undefined || yDirection === undefined) return
+
+ this.xPos += xDirection
+ this.yPos += yDirection
+ this.steps += 1
+ }
+
+ /**
+ * Set's the guard's status to "exited"
+ */
+ exit (): void {
+ this.status = GuardStatus.EXIT
+ }
+}
diff --git a/src/2024/2024-12-06/lib/guard.types.ts b/src/2024/2024-12-06/lib/guard.types.ts
new file mode 100644
index 0000000..4106c64
--- /dev/null
+++ b/src/2024/2024-12-06/lib/guard.types.ts
@@ -0,0 +1,62 @@
+/**
+ * Directions that a `Guard` can face in a `Grid`
+ *
+ * @enum {string}
+ * @property {string} UP - Upward direction `"^"`
+ * @property {string} RIGHT - Upward direction `">"`
+ * @property {string} DOWN - Upward direction `"v"`
+ * @property {string} LEFT - Upward direction `"<"`
+ */
+export enum GuardDirection {
+ UP = '^',
+ RIGHT = '>',
+ DOWN = 'v',
+ LEFT = '<'
+}
+
+/**
+ * Mapping of `GuardDirections` to their corresponding numeric direction vectors.
+ * The vector indicates the direction of movement in the `Grid`
+ *
+ * @constant
+ * @type {Record}
+ * @property {string} [GuardDirection.UP] - Upwards movement direction along the y-axis
+ * @property {string} [GuardDirection.RIGHT] - Right-ise movement direction along the x-axis
+ * @property {string} [GuardDirection.DOWN] - Downwards movement direction along the y-axis
+ * @property {string} [GuardDirection.left] - Left-wise movement direction along the x-axis
+ */
+export const GuardDirectionVector: Record = {
+ [GuardDirection.UP]: -1,
+ [GuardDirection.RIGHT]: 1,
+ [GuardDirection.DOWN]: 1,
+ [GuardDirection.LEFT]: -1
+}
+
+/**
+ * `Guard` activity status in the `Grid` board
+ *
+ * @enum {string}
+ * @property {string} IDLE - Initial status
+ * @property {string} ACTIVE - Indicates active placement on the `Grid`
+ * @property {string} EXIT - Indicates off-Grid placement
+ */
+export enum GuardStatus {
+ IDLE = 'idle',
+ ACTIVE = 'active',
+ EXIT = 'exit'
+}
+
+/**
+ * Properties identifying the current state/status of a `Guard`
+ *
+ * @property {GuardDirection | null} direction Current direction of a `Guard` in the `Grid`
+ * @property {number} xPos x-coordinate of the `Guard` in the `Grid`
+ * @property {number} yPos y-coordinate of the `Guard` in the `Grid`
+ * @property {GuardStatus} status `Guard` activity status
+ */
+export type GuardState = {
+ direction: GuardDirection | null;
+ xPos: number;
+ yPos: number;
+ status: GuardStatus;
+}
diff --git a/src/2024/2024-12-06/lib/guardController.ts b/src/2024/2024-12-06/lib/guardController.ts
new file mode 100644
index 0000000..482fb9f
--- /dev/null
+++ b/src/2024/2024-12-06/lib/guardController.ts
@@ -0,0 +1,64 @@
+import { GuardDirection, GuardStatus } from './guard.types.js'
+import { Guard } from './guard.js'
+import { Grid } from './grid.js'
+
+/**
+ * Runs the `Guard` on the grid, counting distinct positions/steps
+ * @param {string[][]} data 2D string array containing the guards grid paths
+ * @param {boolean} printTrail Flag to display the distinct guard positions in the grid
+ * @returns {number} Number of unique guard positions
+ */
+export const guardController = (data: string[][], printTrail: boolean = false): Grid => {
+ const grid = new Grid(structuredClone(data))
+ const guard = new Guard(grid.findGuardStartPosition())
+
+ while (
+ guard.status !== GuardStatus.EXIT &&
+ !grid.isOutOfBounds(guard.xPos, guard.yPos)
+ ) {
+ let yDirection = 0
+ let xDirection = 0
+
+ // Find the guard's direction
+ switch(guard.direction) {
+ case GuardDirection.UP:
+ yDirection = -1
+ break
+ case GuardDirection.DOWN:
+ yDirection = 1
+ break
+ case GuardDirection.LEFT:
+ xDirection = -1
+ break
+ case GuardDirection.RIGHT:
+ xDirection = 1
+ break
+ default:
+ break
+ }
+
+ if (
+ guard.yPos === grid.board.length - 1 &&
+ guard.xPos === (grid.board[0] as string[])?.length - 1
+ ) {
+ // Exit if the coordinates are at the edge of the Grid
+ guard.exit()
+ }
+
+ const xAhead = guard.xPos + xDirection
+ const yAhead = guard.yPos + yDirection
+ const symbol = grid.board[yAhead]?.[xAhead]
+
+ if (symbol !== grid.obstruction) {
+ // Walk and mark the current coordinates
+ grid.mark(guard.xPos, guard.yPos)
+ guard.walk(xDirection, yDirection)
+ } else {
+ // Turn (rotate clockwise) if the symbol ahead is an obstruction `"#"
+ guard.turn()
+ }
+ }
+
+ if (printTrail) grid.print()
+ return grid
+}
diff --git a/src/2024/2024-12-06/lib/guardControllerLoop.ts b/src/2024/2024-12-06/lib/guardControllerLoop.ts
new file mode 100644
index 0000000..d25429f
--- /dev/null
+++ b/src/2024/2024-12-06/lib/guardControllerLoop.ts
@@ -0,0 +1,124 @@
+import { GuardDirection, GuardStatus } from './guard.types.js'
+import { Guard } from './guard.js'
+import { Grid } from './grid.js'
+import { guardController } from './guardController.js'
+
+/**
+ * Runs the `Guard` on a `Grid` with obstacles, checking the placement of paths and obstacles that will make the `Guard` walk in an infinite loop
+ * @param {string[][]} data 2D string array containing the original `Grid` inserted with a new obstruction symbol `"#"
+ * @param {boolean} printTrail Flag to display the distinct guard positions in the grid
+ * @returns {number} Number of unique guard positions
+ */
+export const gridHasInfiniteLoop = (
+ data: string[][],
+ printTrail: boolean = false
+): boolean => {
+ const grid = new Grid(data, '#')
+ const initialPosition = grid.findGuardStartPosition()
+
+ const guard = new Guard({
+ ...initialPosition,
+ yPos: initialPosition.yPos - 1
+ })
+
+ const trail = []
+ let isLoop = false
+
+ while (
+ guard.status !== GuardStatus.EXIT &&
+ !grid.isOutOfBounds(guard.xPos, guard.yPos) &&
+ !isLoop
+ ) {
+ let yDirection = 0
+ let xDirection = 0
+
+ // Find the guard's direction
+ switch(guard.direction) {
+ case GuardDirection.UP:
+ yDirection = -1
+ break
+ case GuardDirection.DOWN:
+ yDirection = 1
+ break
+ case GuardDirection.LEFT:
+ xDirection = -1
+ break
+ case GuardDirection.RIGHT:
+ xDirection = 1
+ break
+ default:
+ break
+ }
+
+ if (
+ guard.yPos === grid.board.length - 1 &&
+ guard.xPos === (grid.board[0] as string[])?.length - 1
+ ) {
+ // Exit if the coordinates are at the edge of the Grid
+ guard.exit()
+ }
+
+ const xAhead = guard.xPos + xDirection
+ const yAhead = guard.yPos + yDirection
+ const symbol = grid.board[yAhead]?.[xAhead]
+
+ if (symbol !== grid.obstruction) {
+ const coord = `${guard.xPos},${guard.yPos},${guard.direction}`
+
+ if (trail.indexOf(coord) >= 0) {
+ // If the coordinates and direction repeat,
+ // Guard has already entered an infinite loop
+ isLoop = true
+ } else {
+ // Note the (x,y) coordinates and direction
+ trail.push(coord)
+ }
+
+ grid.mark(guard.xPos, guard.yPos)
+ guard.walk(xDirection, yDirection)
+ } else {
+ // Turn (rotate clockwise) if the symbol ahead is an obstruction `"#"
+ guard.turn()
+ }
+ }
+
+ if (printTrail) grid.print()
+ return isLoop
+}
+
+/**
+ * Counts the number of positions in the `Grid` in which inserting one (1)
+ * obstruction symbol # will cause the `Guard` to walk in an infinite loop.
+ * @param {number} data data 2D string array containing the `Grid` paths
+ */
+export const findObstructionPositions = (data: string[][], printGrid: boolean = false) => {
+ let loopCount = 0
+ const paths: Grid = guardController(data)
+ const extras = Array.from({ length: data.length }, () => Array(data.length).fill('.'))
+
+ for (let y = 0; y < paths.board.length; y += 1) {
+ const row = paths.board[y] as string[]
+
+ for (let x = 0; x < row.length; x += 1) {
+ if (row[x] !== 'X') continue
+ if (x === paths.start.x && y === paths.start.y) {
+ continue
+ }
+
+ const xData: string[][] = structuredClone(data)
+ const r = xData[y] as string[]
+ r[x] = '#'
+
+ const isLoop = gridHasInfiniteLoop(xData)
+
+ if (isLoop) {
+ const e = extras[y] as string[]
+ e[x] = 'O'
+ loopCount += 1
+ }
+ }
+ }
+
+ if (printGrid) console.log(extras.map(x => x.join(' ')))
+ return loopCount
+}
diff --git a/src/2024/2024-12-06/main.ts b/src/2024/2024-12-06/main.ts
new file mode 100644
index 0000000..fdd1040
--- /dev/null
+++ b/src/2024/2024-12-06/main.ts
@@ -0,0 +1,37 @@
+import path from 'path'
+import { readAOCInputFile, AOC_OUTPUT_TYPE } from '@/utils/aocInputFile.js'
+import { currentDirectory } from '@/utils/file.js'
+
+import { guardController } from './lib/guardController.js'
+import { findObstructionPositions } from './lib/guardControllerLoop.js'
+
+const file = readAOCInputFile({
+ filePath: path.join(currentDirectory(import.meta.url), 'input.txt'),
+ type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D
+}) as string [][]
+
+/**
+ * Part 1/2 of the 2024-12-06 quiz
+ * Counts the number of distinct guard positions in a grid
+ */
+export const quiz20241206_01 = () => {
+ const grid = guardController(file, true)
+
+ console.log('Distinct guard positions:', grid.positionCount)
+ return grid.positionCount
+}
+
+/**
+ * Part 2/2 of the 2024-12-06 quiz
+ * Counts the number of positions in the Grid in which placing an
+ * obstacle will cause the Guard to walk in an infinite loop
+ */
+export const quiz20241206_02 = () => {
+ const infinitePositions = findObstructionPositions(file)
+
+ console.log('Obstruction positions for infinite walk:', infinitePositions)
+ return infinitePositions
+}
+
+quiz20241206_01()
+quiz20241206_02()
diff --git a/src/2024/2024-12-06/sample.test.ts b/src/2024/2024-12-06/sample.test.ts
new file mode 100644
index 0000000..b46d07d
--- /dev/null
+++ b/src/2024/2024-12-06/sample.test.ts
@@ -0,0 +1,21 @@
+import path from 'path'
+import { test, expect } from 'vitest'
+
+import { readAOCInputFile, AOC_OUTPUT_TYPE } from '@/utils/aocInputFile.js'
+import { currentDirectory } from '@/utils/file.js'
+
+import { guardController } from './lib/guardController.js'
+import { findObstructionPositions } from './lib/guardControllerLoop.js'
+
+const file = readAOCInputFile({
+ filePath: path.join(currentDirectory(import.meta.url), 'input.txt'),
+ type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D
+}) as string [][]
+
+test('Count distinct guard positions', () => {
+ expect(guardController(file).positionCount).toBe(26)
+})
+
+test('Count obstackle positions', () => {
+ expect(findObstructionPositions(file)).toBe(2)
+})
diff --git a/src/utils/aocInputFile.ts b/src/utils/aocInputFile.ts
new file mode 100644
index 0000000..4a0fb5a
--- /dev/null
+++ b/src/utils/aocInputFile.ts
@@ -0,0 +1,64 @@
+// This code contains file input readers for common AoC input types.
+import { readFile } from './file.js'
+
+export enum AOC_OUTPUT_TYPE {
+ STRING = 'string',
+ STRING_ARRAY = 'string_array',
+ STRING_ARRAY_2D = '2d_string_array',
+ NUMBER_ARRAY = 'number_array',
+ NUMBER_ARRAY_2D = '2d_number_array'
+}
+
+type FileInput = {
+ /** @param filePath {string} Full file path to input file */
+ filePath: string;
+ type: AOC_OUTPUT_TYPE;
+ delimiter?: string;
+}
+
+type Output = 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
+ * @param moduleFile {string} File URL of the current module being executed: `"import.meta.url"`
+ * @throws {Error}
+ */
+export const readAOCInputFile = (param: FileInput): Output => {
+ const file = readFile(param.filePath)
+ const delimiter = param?.delimiter ?? ''
+
+ if (file === undefined) {
+ throw new Error('Undefined file')
+ }
+
+ switch(param.type) {
+ case AOC_OUTPUT_TYPE.STRING:
+ return file as string
+
+ case AOC_OUTPUT_TYPE.STRING_ARRAY:
+ return file.split(delimiter) as string[] || []
+
+ case AOC_OUTPUT_TYPE.STRING_ARRAY_2D:
+ return file
+ .split('\n')
+ .map(row => row.split(delimiter)) as string[][] || []
+
+ case AOC_OUTPUT_TYPE.NUMBER_ARRAY:
+ return file
+ .split('')
+ .map(Number) as number[] || []
+
+ case AOC_OUTPUT_TYPE.NUMBER_ARRAY_2D:
+ return file
+ .split('\n')
+ .map(row => row.split(delimiter).map(Number)) as number[][] || []
+
+ default:
+ throw new Error('Unsupported type')
+ }
+}
diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts
index 5748cd4..f8ce6b2 100644
--- a/src/utils/arrays.ts
+++ b/src/utils/arrays.ts
@@ -20,14 +20,18 @@ export const arrangeArray = (order: ARRAY_ORDERING) =>
}
/**
- * Checks if array elements have the same type and has no null or undefined values
+ * Checks if array elements have the same type using `typeof` and has no null or undefined values
* @param items {S[]} array of elements
- * @param type {T} type name of the elements inside the `items` array
+ * @param type {T} primitive type name of the elements inside the array (e.g., number, string, boolean)
* @returns {boolean} Flag indicating if all array elements have the same type
*/
-export const uniformArrayElements = (items: S[], type: T): boolean => {
+export const uniformArrayElements = (
+ items: S[],
+ type: T
+): boolean => {
return (
- items.filter(value => typeof value === type).length === items.length
+ items.filter(value => typeof value === type)
+ .length === items.length
)
}
diff --git a/src/utils/file.ts b/src/utils/file.ts
index 247667b..761ad13 100644
--- a/src/utils/file.ts
+++ b/src/utils/file.ts
@@ -3,9 +3,10 @@ import { dirname } from 'path'
import { fileURLToPath } from 'url'
/**
- * Get the full file path of the current directory.
+ * Get the full file path `"__dirname"` of the current directory of a module file
+ * (scripts running as ESM modules whose package.json has `"type": "module"`)
* @param moduleFile {string} File URL of the current module being executed: `"import.meta.url"`
- * @returns Full file path to the directory of the calling file/module
+ * @returns Full file path to the directory of the calling file/module also know as `__dirname` in CommonJS
*/
export const currentDirectory = (moduleFile: string): string => {
const filePath = fileURLToPath(moduleFile)
@@ -15,7 +16,7 @@ export const currentDirectory = (moduleFile: string): string => {
/**
* Reads file from disk
* @param pathToFile Full file path to a target file
- * @returns {string} String version of the file
+ * @returns {string} String version of the file contents
*/
export const readFile = (pathToFile: string): string => {
return fs.readFileSync(pathToFile, 'utf-8')