113 lines
4.2 KiB
JavaScript
113 lines
4.2 KiB
JavaScript
import fs from 'node:fs'
|
|
const data = fs.readFileSync('input.txt', ({ encoding: "utf-8" }), data => data).split('\n')
|
|
|
|
const paper = []
|
|
|
|
for (let y = 0; y < data.length; y++) {
|
|
for (let x = 0; x < data[y].length; x++) {
|
|
const char = data[y][x]
|
|
paper.push({ pos: { x: x, y: y }, char: char })
|
|
}
|
|
}
|
|
|
|
class Puzzle {
|
|
#directions = []
|
|
#diagonals = []
|
|
constructor() {
|
|
this.i = 0
|
|
this.solutions = 0
|
|
this.#directions = [
|
|
[-1, -1], [-1, 0], [-1, +1],
|
|
[0, -1], [0, +1],
|
|
[+1, -1], [+1, 0,], [+1, +1]
|
|
]
|
|
this.#diagonals = [
|
|
[-1, -1], [-1, +1]
|
|
]
|
|
}
|
|
shootInDirection(y, x, dy, dx, length) {
|
|
const positions = []
|
|
for (let i = 1; i <= length; i++) {
|
|
positions.push([y + (dy * i), x + (dx * i)])
|
|
}
|
|
return positions
|
|
}
|
|
isCharacterAt(char, x, y) {
|
|
const solutions = paper.filter(character => character.pos.x === x && character.pos.y === y && character.char === char)
|
|
return solutions.length === 0 ? false : true
|
|
}
|
|
|
|
findCharacter(c) {
|
|
return [...paper.filter(character => character.char === c)]
|
|
}
|
|
|
|
isInBoundary(x, y) {
|
|
let max_width = data[0].length
|
|
let max_height = data.length
|
|
let within_width = x >= 0 && x <= max_width ? true : false
|
|
let within_height = y >= 0 && y <= max_height ? true : false
|
|
return within_width && within_height ? true : false
|
|
}
|
|
|
|
checkForWord(coords, word) {
|
|
for (let i = 0; i < coords.length; i++) {
|
|
let [y, x] = coords[i]
|
|
if (!this.isInBoundary(x, y)) return false
|
|
if (!this.isCharacterAt(word[i + 1], x, y)) return false
|
|
}
|
|
return true
|
|
}
|
|
checkForWord2(d1, d2, word) {
|
|
const [y1, x1] = d1
|
|
const [y2, x2] = d2
|
|
|
|
if (!this.isInBoundary(x1, y1)) return false
|
|
if (!this.isInBoundary(x2, y2)) return false
|
|
const tl = this.isCharacterAt([...word][1], x1, y1) && this.isCharacterAt([...word][2], x2, y2) ? true : false
|
|
const tr = this.isCharacterAt([...word][2], x1, y1) && this.isCharacterAt([...word][1], x2, y2) ? true : false
|
|
return tl || tr
|
|
}
|
|
opposite(a) {
|
|
return -a
|
|
}
|
|
solve(puzzle) {
|
|
console.log("--- solve part 1 ".padEnd(50, "-"))
|
|
const word = puzzle.split('')
|
|
const letter_positions = word.map(letter => { return { letter: letter, pos: this.findCharacter(letter).map(l => l.pos) } })
|
|
const starting_letter = letter_positions[0]
|
|
let sum = 0
|
|
for (let i = 0; i < starting_letter.pos.length; i++) {
|
|
for (let j = 0; j < this.#directions.length; j++) {
|
|
let [dy, dx] = this.#directions[j]
|
|
let coords = this.shootInDirection(starting_letter.pos[i].y, starting_letter.pos[i].x, dy, dx, word.length - 1)
|
|
if (this.checkForWord(coords, word)) sum += 1
|
|
}
|
|
}
|
|
let total_words_possible = starting_letter.pos.length
|
|
console.log(sum, "of possible", total_words_possible)
|
|
}
|
|
|
|
solve2(puzzle) {
|
|
console.log("--- solve part 2 ".padEnd(50, "-"))
|
|
const word = puzzle.split('')
|
|
const letter_positions = word.map(letter => { return { letter: letter, pos: this.findCharacter(letter).map(l => l.pos) } })
|
|
const starting_letter = letter_positions[0]
|
|
let sum = 0
|
|
for (let i = 0; i < starting_letter.pos.length; i++) {
|
|
let letters_in_both_dirs = true
|
|
for (let j = 0; j < this.#diagonals.length; j++) {
|
|
let [dy, dx] = this.#diagonals[j]
|
|
let upper = this.shootInDirection(starting_letter.pos[i].y, starting_letter.pos[i].x, dy, dx, (word.length - 1) / 2)
|
|
let lower = this.shootInDirection(starting_letter.pos[i].y, starting_letter.pos[i].x, this.opposite(dy), this.opposite(dx), (word.length - 1) / 2)
|
|
if (!this.checkForWord2(upper.flat(), lower.flat(), word)) letters_in_both_dirs = false
|
|
}
|
|
sum += letters_in_both_dirs ? 1 : 0
|
|
}
|
|
let total_words_possible = starting_letter.pos.length
|
|
console.log(sum, "of possible", total_words_possible)
|
|
}
|
|
}
|
|
|
|
const p = new Puzzle()
|
|
p.solve("XMAS")
|
|
p.solve2("AMS") |