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")