diff --git a/Dockerfile b/Dockerfile index 9d30af8..ac6aa28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,6 @@ FROM base AS production ENV NODE_ENV=production COPY --from=build /opt/app/dist /opt/app/dist COPY package*.json ./ -RUN npm install && npm prune --production USER user EXPOSE 9229 CMD ["sh"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index d3ed5a9..0d16fdf 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -7,7 +7,6 @@ services: dockerfile: Dockerfile target: production volumes: - - ./output:/opt/app/output - /opt/app/node_modules ports: - "9229:9229" diff --git a/src/2024/2024-12-01/README.md b/src/2024/2024-12-01/README.md index aef2b62..ee7b98b 100644 --- a/src/2024/2024-12-01/README.md +++ b/src/2024/2024-12-01/README.md @@ -4,3 +4,11 @@ Visit the Advent of Code website for more information on this puzzle at: **Source:** https://adventofcode.com/2024/day/1
**Status:** Complete ⭐⭐ + +
+ +| Code | Description | +| --- | --- | +| **listTotalDistance.ts** | Calculates the total distance between the smallest value-pairs from array `a` and array `b`. | +| **similarityScore.ts** | Calculates the similarity scores of elements in array `a` with array `b` by multiplying each element in array `a` with the total number of its duplicates in array `b`. | +| **fileReader.ts** | Reads a two-column (2) number input separated by three (3) space characters from a text file. | \ No newline at end of file diff --git a/src/2024/2024-12-01/lib/fileReader.ts b/src/2024/2024-12-01/lib/fileReader.ts index ef1c371..f3aa32a 100644 --- a/src/2024/2024-12-01/lib/fileReader.ts +++ b/src/2024/2024-12-01/lib/fileReader.ts @@ -24,7 +24,6 @@ export const fileReader = (): arrayLists => { const pair = item.split(' ') if (pair[0] === undefined || pair[1] === undefined) { - console.log(pair) throw new Error('Undefined value') } diff --git a/src/2024/2024-12-02/README.md b/src/2024/2024-12-02/README.md index c524b39..da71723 100644 --- a/src/2024/2024-12-02/README.md +++ b/src/2024/2024-12-02/README.md @@ -4,3 +4,10 @@ Visit the Advent of Code website for more information on this puzzle at: **Source:** https://adventofcode.com/2024/day/2
**Status:** Complete ⭐⭐ + +
+ +| Code | Description | +| --- | --- | +| **countSafeReports.ts** | Contains functions for counting the number of "safe" reports with or without a problem dampener. | +| **fileReader.ts** | Reads the quiz's input file into two (2) string arrays | \ No newline at end of file diff --git a/src/2024/2024-12-03/README.md b/src/2024/2024-12-03/README.md index 614782b..d1b8226 100644 --- a/src/2024/2024-12-03/README.md +++ b/src/2024/2024-12-03/README.md @@ -3,4 +3,10 @@ Visit the Advent of Code website for more information on this puzzle at: **Source:** https://adventofcode.com/2024/day/3
-**Status:** Completed ⭐⭐ +**Status:** Complete ⭐⭐ + +
+ +| Code | Description | +| --- | --- | +| **extractMultiply.ts** | Contains functions that parse and sum the multiplication result of `mul(x,y)` sequences from corrupted (garbled) text. | \ No newline at end of file diff --git a/src/2024/2024-12-04/README.md b/src/2024/2024-12-04/README.md new file mode 100644 index 0000000..7cea49e --- /dev/null +++ b/src/2024/2024-12-04/README.md @@ -0,0 +1,14 @@ +## Day 4: Ceres Search + +Visit the Advent of Code website for more information on this puzzle at: + +**Source:** https://adventofcode.com/2024/day/4
+**Status:** Complete ⭐⭐ + +
+ +| Code | Description | +| --- | --- | +| **wordCheckerUtils.ts** | Contains functions for checking the existence of a sequence of characters (words) vertically, horizontally, and diagonally in a 2x2 character array. | +| **wordCount.ts** | Counts the number of occurrences of a word in a 2x2 character array. | +| **xmasCount.ts** | Counts the number of (2x) crossed "MAS" words overlapping at the letter "A" a 2x2 character array. | diff --git a/src/2024/2024-12-04/input.txt b/src/2024/2024-12-04/input.txt new file mode 100644 index 0000000..90ebe63 --- /dev/null +++ b/src/2024/2024-12-04/input.txt @@ -0,0 +1,9 @@ +SAMPLEONLY +REPLACEITW +ACTUALAOC. +INPUTEXT.. +ABCDEFGHIJ +XMASXMASXM +GMUMSHSGSH +MXAXAXMARD +ASHSGJM2MX \ No newline at end of file diff --git a/src/2024/2024-12-04/lib/wordCheckerUtils.ts b/src/2024/2024-12-04/lib/wordCheckerUtils.ts new file mode 100644 index 0000000..babaae6 --- /dev/null +++ b/src/2024/2024-12-04/lib/wordCheckerUtils.ts @@ -0,0 +1,105 @@ +export type CharacterArray = string[][] + +interface CoordinateBase { + x: number; + y: number; +} + +interface Coordinate extends CoordinateBase { + direction: 1 | -1; +} + +interface CoordinateData { + xDirection: 1 | -1; + yDirection: 1 | -1; + data: CharacterArray +} + +/** + * Finds the `WORD` input parameter in the vertical directions (up/down) from an (x,y) coordinate + * @typedef coord {Coordinate} Coordinate object + * @param coord.x {number} x coordinate (array x index) + * @param coord.y {number} y coordinate (array y index) + * @param coord.direction {number} Up or down letter-checking direction + * - `-1` for "up" going to (0,0) + * - `1` for "down" going to (N,N) from the `coord` + * @param fullData {CharacterArray} 2D array of strings consisting of letters per item + * @param WORD {string} Word to find + * @returns {boolean} Flag indicating the existence of the `WORD` + */ +export const checkVertical = (coord: Coordinate, fullData: CharacterArray, WORD: string): boolean => { + let yIndex = coord.y + let subWord = '' + + if (!Array.isArray(fullData)) return false + + for (let i = 0; i < WORD.length; i += 1) { + if (yIndex < 0) break + if (yIndex >= fullData.length) break + + subWord += (fullData[yIndex])?.[coord.x] + yIndex += (coord.direction * 1) + } + + return subWord === WORD +} + +/** + * Finds the `WORD` input parameter in the horizontal directions (left/right) from an (x,y) coordinate + * @typedef coord {Coordinate} Coordinate object + * @param coord.x {number} x coordinate (array x index) + * @param coord.y {number} y coordinate (array y index) + * @param coord.direction {number} `-1` for "left" going to (0,0) or `1` for "right" going to (N,N) from the `coord` + * @param rowData {string[]} Array of row-wise letters from a 2D array + * @returns {boolean} Flag indicating the existence of the `WORD` + * @param WORD {string} Word to find + */ +export const checkHorizontal = (coord: Coordinate, rowData: string[], WORD: string): boolean => { + let xIndex = coord.x + let subWord = '' + + if (!Array.isArray(rowData)) return false + + for (let i = 0; i < WORD.length; i += 1) { + if (xIndex < 0) break + if (xIndex >= rowData.length) break + + subWord += rowData[xIndex] + xIndex += (coord.direction * 1) + } + + return subWord === WORD +} + +/** + * Finds the `WORD` input parameter in the horizontal directions from an (x,y) coordinate + * @typedef coord {Coordinate} Coordinate object + * @param coord.x {number} x coordinate (array x index) + * @param coord.y {number} y coordinate (array y index) + * @typedef param {CoordinateData} Direction and data object + * @param param.xDirection {number} `-1` for "left" going to (0,0) or `1` for "right" going to (N,N) from the `coord` + * @param param.yDirection {number} `-1` for "up" going to (0,0) or `1` for "down" going to (N,N) from the `coord` + * @param param.data {CharacterArray} 2D string array containing the input text + * @returns {boolean} Flag indicating the existence of the `WORD` + * @param WORD {string} Word to find + */ +export const checkDiagonal = (coord: CoordinateBase, param: CoordinateData, WORD: string): boolean => { + let xIndex = coord.x + let yIndex = coord.y + let subWord = '' + + if (!Array.isArray(param.data)) return false + if (param.data[0] === undefined) return false + + for (let i = 0; i < WORD.length; i += 1) { + if (xIndex < 0 || xIndex >= param.data[0].length) break + if (yIndex < 0 || yIndex >= param.data.length) break + + subWord += (param.data[yIndex])?.[xIndex] + + xIndex += (param.xDirection * 1) + yIndex += (param.yDirection * 1) + } + + return subWord === WORD +} diff --git a/src/2024/2024-12-04/lib/wordCount.ts b/src/2024/2024-12-04/lib/wordCount.ts new file mode 100644 index 0000000..463aeb3 --- /dev/null +++ b/src/2024/2024-12-04/lib/wordCount.ts @@ -0,0 +1,82 @@ +import { checkVertical, checkHorizontal, checkDiagonal } from './wordCheckerUtils.js' + +/** + * Counts the number of occurence of the `WORD` from a set of input + * @param data {CharacterArray} 2D string array containing the input text + * @param WORD_TO_FIND {string} Word to find + * @returns {number} Number of `WORD`s + */ +export const wordCount = (data: string[][], WORD_TO_FIND: string): number => { + let xmasCount = 0 + const firstLetter = WORD_TO_FIND[0] + + for (let y = 0; y < data.length; y += 1) { + const row = data[y] + + if (!Array.isArray(row)) continue + + if (row.filter( + item => typeof item !== 'string').length > 0 + ) continue + + for (let x = 0; x < row.length; x += 1) { + const letter = row[x] + + if (letter !== firstLetter) continue + + // Check WORD_TO_FIND down + if (checkVertical( + { x, y, direction: 1 }, data, WORD_TO_FIND)) xmasCount += 1 + + // Check WORD_TO_FIND up + if (checkVertical( + { x, y, direction: -1 }, data, WORD_TO_FIND)) xmasCount += 1 + + // Check WORD_TO_FIND right + if (checkHorizontal( + { x, y, direction: 1 }, row, WORD_TO_FIND)) xmasCount += 1 + + // Check WORD_TO_FIND left + if (checkHorizontal( + { x, y, direction: -1 }, row, WORD_TO_FIND)) xmasCount += 1 + + // Check WORD_TO_FIND diagonally right-up + if (checkDiagonal( + { x, y }, + { xDirection: 1, yDirection: -1, data }, + WORD_TO_FIND + )) { + xmasCount += 1 + } + + // Check WORD_TO_FIND diagonally right-down + if (checkDiagonal( + { x, y }, + { xDirection: 1, yDirection: 1, data }, + WORD_TO_FIND + )) { + xmasCount += 1 + } + + // Check WORD_TO_FIND diagonally left-up + if (checkDiagonal( + { x, y }, + { xDirection: -1, yDirection: -1, data }, + WORD_TO_FIND + )) { + xmasCount += 1 + } + + // Check WORD_TO_FIND diagonally left-down + if (checkDiagonal( + { x, y }, + { xDirection: -1, yDirection: 1, data }, + WORD_TO_FIND + )) { + xmasCount += 1 + } + } + } + + return xmasCount +} diff --git a/src/2024/2024-12-04/lib/xmasCount.ts b/src/2024/2024-12-04/lib/xmasCount.ts new file mode 100644 index 0000000..db52f07 --- /dev/null +++ b/src/2024/2024-12-04/lib/xmasCount.ts @@ -0,0 +1,56 @@ +import { checkDiagonal } from './wordCheckerUtils.js' + +/** + * Counts the number of (2x) crossed "MAS" words overlapping at the letter "A" + * @param data {CharacterArray} 2D string array containing the input text + * @param WORD_TO_FIND {string} Word to find ("MAS") + * @returns {number} Number of overlapping crossed-over ("MAS") `WORD`s + */ +export const countMASword = (data: string[][], WORD_TO_FIND: string = 'MAS'): number => { + const wordLength = WORD_TO_FIND.length + const middleZeroBasedIndex = (Math.floor(wordLength / 2) + wordLength % 2) - 1 + const offset = middleZeroBasedIndex + const reverseWord = WORD_TO_FIND.split('').reverse().join('') + + let count = 0 + + for (let y = 0; y < data.length; y += 1) { + const row = data[y] + + if (row === undefined) continue + + for (let x = 0; x < row.length; x += 1) { + const letter = row[x] + + if (letter !== WORD_TO_FIND[middleZeroBasedIndex]) continue + + if ( + (checkDiagonal( + { x: x + offset, y: y - offset }, + { xDirection: -1, yDirection: 1, data }, + WORD_TO_FIND + ) || + checkDiagonal( + { x: x + offset, y: y - offset }, + { xDirection: -1, yDirection: 1, data }, + reverseWord) + ) && + + (checkDiagonal( + { x: x + offset, y: y + offset }, + { xDirection: -1, yDirection: -1, data }, + WORD_TO_FIND + ) || + checkDiagonal( + { x: x + offset, y: y + offset }, + { xDirection: -1, yDirection: -1, data }, + reverseWord) + ) + ) { + count += 1 + } + } + } + + return count +} diff --git a/src/2024/2024-12-04/main.ts b/src/2024/2024-12-04/main.ts new file mode 100644 index 0000000..5fefdc7 --- /dev/null +++ b/src/2024/2024-12-04/main.ts @@ -0,0 +1,23 @@ +import path from 'path' +import { currentDirectory, readFile } from '@/utils/file.js' +import { wordCount } from './lib/wordCount.js' +import { countMASword } from './lib/xmasCount.js' + +const directory = currentDirectory(import.meta.url) + +const data: string[][] = readFile(path.join(directory, 'input.txt')) + .split('\n') + .map(row => row.split('')) + +const quiz20241204_01 = () => { + const count = wordCount(data, 'XMAS') + console.log('"XMAS" word count:', count) +} + +const quiz20241204_02 = () => { + const count = countMASword(data, 'MAS') + console.log('"MAS-only" word count:', count) +} + +quiz20241204_01() +quiz20241204_02() diff --git a/src/2024/2024-12-04/sample.test.ts b/src/2024/2024-12-04/sample.test.ts new file mode 100644 index 0000000..e2b4fdd --- /dev/null +++ b/src/2024/2024-12-04/sample.test.ts @@ -0,0 +1,19 @@ +import { test, expect } from 'vitest' +import { wordCount } from './lib/wordCount.js' +import { countMASword } from './lib/xmasCount.js' + +const data: string[][] = `ABCDEFGHIJ +XMASXMASXM +GMUMSHLGWH +MXAXAXMHRD +ASHSGJM2AX` + .split('\n') + .map(row => row.split('')) + +test('Count "XMAS" words - demo', () => { + expect(wordCount(data, 'XMAS')).toBe(4) +}) + +test('Count "MAS-only" crossed words - demo', () => { + expect(countMASword(data, 'MAS')).toBe(1) +})