Skip to content

Commit 526661f

Browse files
authored
Merge pull request #39 from weaponsforge/feat/weaponsforge-13
feat: day 8 resonant collinearity 20241208
2 parents d800db1 + 26e3194 commit 526661f

File tree

9 files changed

+358
-2
lines changed

9 files changed

+358
-2
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## ✨ adventofcode
22

3-
This repository contains solutions and a local development environment for the [Advent of Code](https://adventofcode.com/) event puzzles.
3+
This repository contains solutions and a local development environment for the [Advent of Code](https://adventofcode.com/) event puzzles using TypeScript/JavaScript.
4+
5+
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.
46

57
### 🎄 Advent of Code Quiz Information
68

@@ -14,6 +16,7 @@ This repository contains solutions and a local development environment for the [
1416
- Day 5: Print Queue [[link]](/src/2024/2024-12-05/README.md)
1517
- Day 6: Guard Gallivant [[link]](/src/2024/2024-12-06/README.md)
1618
- Day 7: Bridge Repair [[link]](/src/2024/2024-12-07/README.md)
19+
- Day 8: Resonant Collinearity [[link]](/src/2024/2024-12-08/README.md)
1720

1821
</details>
1922

@@ -110,7 +113,7 @@ Using Node
110113
npm run transpile
111114
node dist/sample/sample.js
112115
```
113-
4. See the [Available Scripts](#available-scripts) section for more information.
116+
4. See the [Available Scripts](#-available-scripts) section for more information.
114117

115118
## ⚡ Alternate Usage
116119

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
## Day 8: Resonant Collinearity
2+
3+
Visit the Advent of Code website for more information on this puzzle at:
4+
5+
**Source:** https://adventofcode.com/2024/day/8<br>
6+
**Status:** Complete ⭐⭐
7+
8+
## Code
9+
10+
### `GridAntinodes.ts`
11+
12+
- `GridAntiNodes` class - Object that tracks and manages `Antennas` and `Antinodes` in a 2D grid array
13+
14+
### `uniqueAntinodes.ts`
15+
16+
- `uniqueAntinodes()` - Counts the unique locations in the grid that contains an antinode
17+
18+
### `allAntinodes.ts`
19+
20+
- `getAntinodesInPath()` - Finds all `Antinode` coordinates along a path within a 2D array (grid) given a `Point`, increment steps and a +/- direction
21+
22+
- `countAllAntinodes()` - Counts the unique locations in the grid that contains all locations of antinodes along a path

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
............
2+
........x...
3+
.....x......
4+
.......x....
5+
....x.......
6+
......B.....
7+
............
8+
............
9+
........B...
10+
.........B..
11+
............
12+
............
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type { Antenna } from './types.js'
2+
3+
/**
4+
* @class GridAntiNodes
5+
* @description Object that tracks `Antennas` and `Antinodes` in a 2D grid array
6+
*/
7+
export class GridAntiNodes {
8+
/** 2D string user input array */
9+
board: string[][] = []
10+
11+
/** Array containing Antenna (y,x) coordinates */
12+
antennas: Antenna[] = []
13+
14+
/** List of unique Antinode (y,x) coordinate strings */
15+
antinodes = new Set<string>()
16+
17+
/** Grid array index pointing to the current antenna in the `this.antennas[]` list */
18+
currentAntIndex: number = 0
19+
20+
/** Grid array index pointing to the next antenna after `this.currentAntIndex` */
21+
nextAntIndex: number = 1
22+
23+
/**
24+
* Creates an instance of the `GridAntiNodes` class
25+
* @constructor
26+
* @param {string[][]} inputFile 2D string array containing grid paths and `Antennas`
27+
*/
28+
constructor(inputFile: string[][]) {
29+
this.board = inputFile
30+
this.findAntennas()
31+
}
32+
33+
/**
34+
* Finds and stores the (x,y) coordinates of valid `Antennas`
35+
* from the 2D string array into the `this.antennas[]` array
36+
*/
37+
findAntennas (): void {
38+
for (let row = 0; row < this.board.length; row += 1) {
39+
const indices: Antenna[] = this.board[row]!.reduce((list: Antenna[], item, index) => {
40+
if (item.match(/^[A-Za-z0-9]+$/g)) {
41+
this.board[row]![index] = item
42+
43+
return [
44+
...list, { frequency: item, x: index, y: row }
45+
]
46+
}
47+
48+
return list
49+
}, [])
50+
51+
this.antennas = [...this.antennas, ...indices]
52+
}
53+
}
54+
55+
/**
56+
* Increments the index counters used in traversing the `Antinodes` list
57+
*/
58+
incrementCursors (): void {
59+
if (this.nextAntIndex === this.antennas.length - 1) {
60+
this.currentAntIndex += 1
61+
this.nextAntIndex = this.currentAntIndex + 1
62+
} else {
63+
this.nextAntIndex += 1
64+
}
65+
}
66+
67+
/**
68+
* Resets the index counters used in traversing the `Antinodes` list
69+
*/
70+
resetCursors (): void {
71+
this.currentAntIndex = 0
72+
this.nextAntIndex = 1
73+
}
74+
75+
/**
76+
* Stores the (x,y) coordinates of an `Antinode`
77+
* @param {string} antinodeCoord String-concatenated (x,y) coordinate of an `Antinode`
78+
*/
79+
storeAntinode (antinodeCoord: string): void {
80+
this.antinodes.add(antinodeCoord)
81+
}
82+
83+
/**
84+
* Prints the 2D grid (board) to the screen
85+
* @param {boolean} withAntinodes Flag to display the `Antinodes` in the grid
86+
*/
87+
printGrid (withAntinodes: boolean = false): void {
88+
if (withAntinodes) {
89+
const printBoard = structuredClone(this.board)
90+
91+
for (const antinode of this.antinodes) {
92+
const coord = (antinode as string).split(',').map(item => Number(item))
93+
const character = this.board[coord[0] as number]![coord[1] as number]
94+
95+
if (character === '.') {
96+
printBoard[coord[0] as number]![coord[1] as number] = '#'
97+
}
98+
}
99+
100+
console.log(printBoard.map(row => row.join(' ')))
101+
} else {
102+
console.log(this.board.map(row => row.join(' ')))
103+
}
104+
}
105+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import type { Antenna, Point } from './types.js'
2+
import { GridAntiNodes } from './GridAntinodes.js'
3+
4+
/**
5+
* Finds all `Antinode` coordinates along a path within a 2D array (grid) given a `Point`, increment steps and a +/- direction
6+
* @param {Point} point (y,x) coordinate of an `Antenna` qualified for creating an `Antinode`
7+
* @param {Point} increments Amount of increments to increase/decrease the (y,x) offsets of a `Point`
8+
* @param {number} direction `+/-` positive or negative direction for increasing/decreasing a `Point`'s coordinates
9+
* @typedef {Object} board Dimensions of the 2D grid array
10+
* @param {number} board.length Length of the 2D array
11+
* @param {number} board.width Width of the 2D array
12+
* @returns {Set<string>} All `Antinode` (y,x) coordinates along the path
13+
*/
14+
const getAntinodesInPath = (
15+
point: Point,
16+
increments: Point,
17+
direction: number,
18+
board: { length: number, width: number }
19+
): Set<string> => {
20+
const antinodes = new Set<string>()
21+
const startPoint = { ...point }
22+
23+
while (
24+
startPoint.x >= 0 && startPoint.x < board.length &&
25+
startPoint.y >= 0 && startPoint.y < board.width
26+
) {
27+
antinodes.add(`${startPoint.y},${startPoint.x}`)
28+
29+
startPoint.x += increments.x * direction
30+
startPoint.y += increments.y * direction
31+
}
32+
33+
return antinodes
34+
}
35+
36+
/**
37+
* Counts the unique locations in the grid that contains all locations of antinodes along a path
38+
* @param {string[][]} inputFile 2D string array containing grid paths and `Antennas`
39+
* @returns {number} Total number of unique antinodes in the grid
40+
*/
41+
export const countAllAntinodes = (inputFile: string[][]): number => {
42+
const grid = new GridAntiNodes(inputFile)
43+
44+
while(grid.currentAntIndex < grid.antennas.length - 1) {
45+
// Antennas
46+
const a1 = grid.antennas[grid.currentAntIndex] as Antenna
47+
const a2 = grid.antennas[grid.nextAntIndex] as Antenna
48+
49+
const gridDimensions = {
50+
length: grid.board.length,
51+
width: grid.board[0]!.length
52+
}
53+
54+
// Skip processing antennas with different frequencies
55+
if (a1.frequency !== a2.frequency) {
56+
grid.incrementCursors()
57+
continue
58+
}
59+
60+
// Antenna coordinate difference
61+
const diff = {
62+
x: a2.x - a1.x,
63+
y: a2.y - a1.y
64+
}
65+
66+
if (
67+
a1.y < inputFile.length && a1.y >= 0 &&
68+
a1.x < inputFile[0]!.length && a1.x >= 0
69+
) {
70+
// Find all aligned antinodes
71+
getAntinodesInPath(a1, diff, -1, gridDimensions)
72+
.forEach(
73+
item => grid.antinodes.add(item)
74+
)
75+
}
76+
77+
if (
78+
a2.y < inputFile.length && a2.y >= 0 &&
79+
a2.x < inputFile[0]!.length && a2.x >= 0
80+
) {
81+
// Find all aligned antinodes
82+
getAntinodesInPath(a2, diff, 1, gridDimensions)
83+
.forEach(
84+
item => grid.antinodes.add(item)
85+
)
86+
}
87+
88+
grid.incrementCursors()
89+
}
90+
91+
grid.printGrid(true)
92+
return grid.antinodes.size
93+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface Point {
2+
x: number;
3+
y: number;
4+
}
5+
6+
export interface Antenna extends Point {
7+
frequency: string;
8+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { Antenna, Point } from './types.js'
2+
import { GridAntiNodes } from './GridAntinodes.js'
3+
4+
/**
5+
* Counts the unique locations in the grid that contains an antinode
6+
* @param {string[][]} inputFile 2D string array containing grid paths and `Antennas`
7+
* @returns {number} Total number of unique antinodes in the grid
8+
*/
9+
export const countAntinodes = (inputFile: string[][]): number => {
10+
const grid = new GridAntiNodes(inputFile)
11+
12+
while(grid.currentAntIndex < grid.antennas.length - 1) {
13+
// Antennas
14+
const a1 = grid.antennas[grid.currentAntIndex] as Antenna
15+
const a2 = grid.antennas[grid.nextAntIndex] as Antenna
16+
17+
// Skip processing antennas with different frequencies
18+
if (a1.frequency !== a2.frequency) {
19+
grid.incrementCursors()
20+
continue
21+
}
22+
23+
// Antenna coordinate difference
24+
const diff = {
25+
x: a2.x - a1.x,
26+
y: a2.y - a1.y
27+
}
28+
29+
// Antinode 1 coordinates
30+
const node1: Point = {
31+
x: a1.x - diff.x,
32+
y: a1.y - diff.y
33+
}
34+
35+
// Antinode 2 coordinates
36+
const node2: Point = {
37+
x: a2.x + diff.x,
38+
y: a2.y + diff.y
39+
}
40+
41+
if (
42+
node1.y < inputFile.length && node1.y >= 0 &&
43+
node1.x < inputFile[0]!.length && node1.x >= 0
44+
) {
45+
grid.storeAntinode(`${node1.y},${node1.x}`)
46+
}
47+
48+
if (
49+
node2.y < inputFile.length && node2.y >= 0 &&
50+
node2.x < inputFile[0]!.length && node2.x >= 0
51+
) {
52+
grid.storeAntinode(`${node2.y},${node2.x}`)
53+
}
54+
55+
grid.incrementCursors()
56+
}
57+
58+
grid.printGrid(true)
59+
return grid.antinodes.size
60+
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import path from 'path'
2+
import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/utils/aocInputFile.js'
3+
import { currentDirectory } from '@/utils/file.js'
4+
5+
import { countAntinodes } from './lib/uniqueAntinodes.js'
6+
import { countAllAntinodes } from './lib/allAntinodes.js'
7+
8+
const input = readAOCInputFile({
9+
filePath: path.join(currentDirectory(import.meta.url), 'input.txt'),
10+
type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D
11+
}) as string[][]
12+
13+
/**
14+
* Part 1/2 of the 2024-12-08 quiz
15+
* Counts the unique locations in the grid that contains an antinode
16+
*/
17+
const quiz20241208_01 = () => {
18+
const count = countAntinodes(input)
19+
console.log('Antinodes in unique locations:', count)
20+
}
21+
22+
/**
23+
* Part 2/2 of the 2024-12-08 quiz
24+
* Counts the unique locations in the grid of all antinodes that contains an antinode
25+
*/
26+
const quiz20241208_02 = () => {
27+
const count = countAllAntinodes(input)
28+
console.log('All Antinodes count:', count)
29+
}
30+
31+
quiz20241208_01()
32+
quiz20241208_02()

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import path from 'path'
2+
import { test, expect } from 'vitest'
3+
4+
import { AOC_OUTPUT_TYPE, readAOCInputFile } from '@/utils/aocInputFile.js'
5+
import { currentDirectory } from '@/utils/file.js'
6+
7+
import { countAntinodes } from './lib/uniqueAntinodes.js'
8+
import { countAllAntinodes } from './lib/allAntinodes.js'
9+
10+
const input = readAOCInputFile({
11+
filePath: path.join(currentDirectory(import.meta.url), 'input.txt'),
12+
type: AOC_OUTPUT_TYPE.STRING_ARRAY_2D
13+
}) as string[][]
14+
15+
test('Antinodes in unique locations', () => {
16+
expect(countAntinodes(input)).toBe(14)
17+
})
18+
19+
test('All antinodes in line', () => {
20+
expect(countAllAntinodes(input)).toBe(34)
21+
})

0 commit comments

Comments
 (0)