From 7560bb558833d633cabb20edcc5a9d73d741feb8 Mon Sep 17 00:00:00 2001 From: Timothy Quilling Date: Fri, 15 Dec 2023 11:12:41 -0500 Subject: [PATCH] day 3 solution --- src/day3/typescript/main.test.ts | 21 +- src/day3/typescript/main.ts | 676 +++++++++++++++++++++++++++++-- 2 files changed, 662 insertions(+), 35 deletions(-) diff --git a/src/day3/typescript/main.test.ts b/src/day3/typescript/main.test.ts index 9ba039d..7219665 100644 --- a/src/day3/typescript/main.test.ts +++ b/src/day3/typescript/main.test.ts @@ -5,8 +5,9 @@ import {partA, partB} from './main.ts'; // eslint-disable-line n/file-extension- const day = 3; const answerExample = 4361; -const answerA = 0; -const answerB = 0; +const answerA = 525_911; +const answerB = 75_805_607; +const answerExampleB = 467_835; const input: string[] = readFileSync(`src/day${day}/input.txt`, 'utf8').split('\n'); const inputExample: string[] = readFileSync(`src/day${day}/input_example.txt`, 'utf8').split('\n'); @@ -14,10 +15,14 @@ Deno.test('Example', () => { assertEquals(partA(inputExample), answerExample); }); -// Deno.test('Part A', () => { -// assertEquals(partA(input), answerA); -// }); +Deno.test('Part A', () => { + assertEquals(partA(input), answerA); +}); + +Deno.test('Example B', () => { + assertEquals(partB(inputExample), answerExampleB); +}); -// Deno.test('Part B', () => { -// assertEquals(partB(input), answerB); -// }); +Deno.test('Part B', () => { + assertEquals(partB(input), answerB); +}); diff --git a/src/day3/typescript/main.ts b/src/day3/typescript/main.ts index 5bc38ad..b50f43b 100644 --- a/src/day3/typescript/main.ts +++ b/src/day3/typescript/main.ts @@ -1,39 +1,661 @@ export const partA = (input: string[]): number => { // Part A - // Create a virtual grid, x:140 and y will expand as needed. - // We'll read each line of input for numbers and symbols, mapping their coordinates to the grid. - const grid: string[][] = []; + enum Color { + YELLOW = '\u001B[33m', + RED = '\u001B[31m', + WHITE = '\u001B[37m', + BLUE = '\u001B[34m', + GREEN = '\u001B[32m', + RESET = '\u001B[0m', + } + enum Type { + EMPTY, + SYMBOL, + NUMBER, + NUMBER_PART, + } + type Gridpoint = { + x: number; + y: number; + value: string; + type: Type; + }; + + const grid: Gridpoint[][] = []; + let currentLine = 0; + let currentChar = 0; for (const line of input) { - const row = line.split(''); - grid.push(row); - } - - for (const y of grid) { - const coloredStringArray: string[] = []; - for (const x of y) { - if (Number(x) || x === '0') { - // Check behind and above you for any numbers. - const behind = y[y.indexOf(x) - 1]; - if (Number(behind) || behind === '0') { - // Color yourself yellow. - coloredStringArray.push('\u001B[33m' + x + '\u001B[0m'); - } else { - // Color yourself red. - coloredStringArray.push('\u001B[31m' + x + '\u001B[0m'); + for (const _char of line) { + // Determine the type of the current character. + let type = Type.SYMBOL; + if (_char === '.') { + type = Type.EMPTY; + } else if (/\d/.test(_char)) { + type = Type.NUMBER; + } else if (_char === '\r') { + // This is a carriage return. Skip it. + continue; + } + + const gridpoint: Gridpoint = { + x: currentChar, + y: currentLine, + value: _char, + type, + }; + + if (grid[currentLine] === undefined) { + grid[currentLine] = []; + } + + grid[currentLine].push(gridpoint); + currentChar++; + } + + currentLine++; + currentChar = 0; + } + + for (const line of grid) { + for (const gridpoint of line) { + if (gridpoint.type === Type.SYMBOL) { + // Look for any numbers in a 3x3 around this symbol. + const coords = [ + {x: gridpoint.x - 1, y: gridpoint.y - 1}, + {x: gridpoint.x - 1, y: gridpoint.y}, + {x: gridpoint.x - 1, y: gridpoint.y + 1}, + {x: gridpoint.x, y: gridpoint.y - 1}, + {x: gridpoint.x, y: gridpoint.y + 1}, + {x: gridpoint.x + 1, y: gridpoint.y - 1}, + {x: gridpoint.x + 1, y: gridpoint.y}, + {x: gridpoint.x + 1, y: gridpoint.y + 1}, + ]; + for (const coord of coords) { + try { + const point = grid[coord.y][coord.x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_PART; + // Look right from this number for any other numbers. + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_PART; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + const lookLeft = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_PART; + return lookLeft(x - 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(point.x + 1, point.y); + lookLeft(point.x - 1, point.y); + } + } catch { + // Do nothing. + } } - } else if (x === '.') { - coloredStringArray.push('\u001B[37m' + x + '\u001B[0m'); - } else { - coloredStringArray.push(x); + } + } + } + + // Let's read through our grid and record every consecutive number as an integer. + const enginePartNumbers: number[] = []; + for (const line of grid) { + let possibleNumber = ''; + for (const gridpoint of line) { + if (gridpoint.type === Type.NUMBER_PART) { + possibleNumber += gridpoint.value; + } else if (possibleNumber !== '') { + enginePartNumbers.push(Number.parseInt(possibleNumber, 10)); + possibleNumber = ''; } } - const coloredString = coloredStringArray.join(''); - console.log(coloredString); + if (possibleNumber !== '') { + enginePartNumbers.push(Number.parseInt(possibleNumber, 10)); + possibleNumber = ''; + } } - return 0; // Answer: + const printWithColor = (grid: Gridpoint[][]): void => { + for (const line of grid) { + for (const gridpoint of line) { + switch (gridpoint.type) { + case Type.EMPTY: { + gridpoint.value = `${Color.BLUE}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.NUMBER: { + gridpoint.value = `${Color.YELLOW}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.SYMBOL: { + gridpoint.value = `${Color.RED}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.NUMBER_PART: { + gridpoint.value = `${Color.GREEN}${gridpoint.value}${Color.RESET}`; + + break; + } + // No default + } + + Deno.stdout.writeSync(new TextEncoder().encode(gridpoint.value)); // eslint-disable-line @typescript-eslint/no-unsafe-call + } + + Deno.stdout.writeSync(new TextEncoder().encode('\n')); // eslint-disable-line @typescript-eslint/no-unsafe-call + } + }; + + printWithColor(grid); + + // Add them all up. + let sum = 0; + for (const number of enginePartNumbers) { + sum += number; + } + + console.log(`Sum is ${sum}`); + return sum; // Answer: }; +// ********************************************************************************************************************* +// ********************************************************************************************************************* +// ********************************************************************************************************************* + +// eslint-disable-next-line complexity export const partB = (input: string[]): number => { - return 0; // Answer: + enum Color { + YELLOW = '\u001B[33m', + RED = '\u001B[31m', + WHITE = '\u001B[37m', + BLUE = '\u001B[34m', + GREEN = '\u001B[32m', + PINK = '\u001B[95m', + RESET = '\u001B[0m', + } + enum Type { + EMPTY, + SYMBOL, + NUMBER, + NUMBER_PART, + NUMBER_STAR, + STAR, + } + type Gridpoint = { + x: number; + y: number; + value: string; + type: Type; + }; + + const grid: Gridpoint[][] = []; + let currentLine = 0; + let currentChar = 0; + for (const line of input) { + for (const _char of line) { + // Determine the type of the current character. + let type = Type.SYMBOL; + if (_char === '.') { + type = Type.EMPTY; + } else if (_char === '*') { + type = Type.STAR; + } else if (/\d/.test(_char)) { + type = Type.NUMBER; + } else if (_char === '\r') { + // This is a carriage return. Skip it. + continue; + } + + const gridpoint: Gridpoint = { + x: currentChar, + y: currentLine, + value: _char, + type, + }; + + if (grid[currentLine] === undefined) { + grid[currentLine] = []; + } + + grid[currentLine].push(gridpoint); + currentChar++; + } + + currentLine++; + currentChar = 0; + } + + for (const line of grid) { + for (const gridpoint of line) { + if (gridpoint.type === Type.STAR) { + const coords = [ + {x: gridpoint.x - 1, y: gridpoint.y - 1}, + {x: gridpoint.x - 1, y: gridpoint.y}, + {x: gridpoint.x - 1, y: gridpoint.y + 1}, + {x: gridpoint.x, y: gridpoint.y - 1}, + {x: gridpoint.x, y: gridpoint.y + 1}, + {x: gridpoint.x + 1, y: gridpoint.y - 1}, + {x: gridpoint.x + 1, y: gridpoint.y}, + {x: gridpoint.x + 1, y: gridpoint.y + 1}, + ]; + for (const coord of coords) { + try { + const point = grid[coord.y][coord.x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_STAR; + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_STAR; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + const lookLeft = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_STAR; + return lookLeft(x - 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(point.x + 1, point.y); + lookLeft(point.x - 1, point.y); + } + } catch { + // Do nothing. + } + } + } else if (gridpoint.type === Type.SYMBOL) { + // Look for any numbers in a 3x3 around this symbol. + const coords = [ + {x: gridpoint.x - 1, y: gridpoint.y - 1}, + {x: gridpoint.x - 1, y: gridpoint.y}, + {x: gridpoint.x - 1, y: gridpoint.y + 1}, + {x: gridpoint.x, y: gridpoint.y - 1}, + {x: gridpoint.x, y: gridpoint.y + 1}, + {x: gridpoint.x + 1, y: gridpoint.y - 1}, + {x: gridpoint.x + 1, y: gridpoint.y}, + {x: gridpoint.x + 1, y: gridpoint.y + 1}, + ]; + for (const coord of coords) { + try { + const point = grid[coord.y][coord.x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_PART; + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_PART; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + const lookLeft = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER) { + point.type = Type.NUMBER_PART; + return lookLeft(x - 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(point.x + 1, point.y); + lookLeft(point.x - 1, point.y); + } + } catch { + // Do nothing. + } + } + } + } + } + + const enginePartNumbers: number[] = []; + for (const line of grid) { + let possibleNumber = ''; + for (const gridpoint of line) { + if (gridpoint.type === Type.NUMBER_PART) { + possibleNumber += gridpoint.value; + } else if (possibleNumber !== '') { + enginePartNumbers.push(Number.parseInt(possibleNumber, 10)); + possibleNumber = ''; + } + } + + if (possibleNumber !== '') { + enginePartNumbers.push(Number.parseInt(possibleNumber, 10)); + possibleNumber = ''; + } + } + + const engineStarPartNumbers: number[][] = []; + for (const line of grid) { + for (const gridpoint of line) { + if (gridpoint.type === Type.STAR) { + const possiblePair = []; + + let lookRightCounts = 0; + if (grid[gridpoint.y - 1] !== undefined) { + if (grid[gridpoint.y - 1][gridpoint.x - 1].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y - 1][gridpoint.x - 1].value; + const lookLeft = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber = point.value + possibleNumber; + return lookLeft(x - 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookLeft(gridpoint.x - 2, gridpoint.y - 1); + + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + lookRightCounts++; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x, gridpoint.y - 1); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + let lookRightCounts2 = 0; + if (lookRightCounts === 0 && grid[gridpoint.y - 1][gridpoint.x].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y - 1][gridpoint.x].value; + + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + lookRightCounts2++; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x + 1, gridpoint.y - 1); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + if (lookRightCounts < 2 && lookRightCounts2 === 0 && grid[gridpoint.y - 1][gridpoint.x + 1].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y - 1][gridpoint.x + 1].value; + + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x + 2, gridpoint.y - 1); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + if (grid[gridpoint.y][gridpoint.x - 1].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y][gridpoint.x - 1].value; + + const lookLeft = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber = point.value + possibleNumber; + return lookLeft(x - 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookLeft(gridpoint.x - 2, gridpoint.y); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + if (grid[gridpoint.y][gridpoint.x + 1].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y][gridpoint.x + 1].value; + + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x + 2, gridpoint.y); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + lookRightCounts = 0; + if (grid[gridpoint.y + 1] !== undefined) { + if (grid[gridpoint.y + 1][gridpoint.x - 1].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y + 1][gridpoint.x - 1].value; + const lookLeft = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber = point.value + possibleNumber; + return lookLeft(x - 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookLeft(gridpoint.x - 2, gridpoint.y + 1); + + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + lookRightCounts++; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x, gridpoint.y + 1); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + lookRightCounts2 = 0; + if (lookRightCounts === 0 && grid[gridpoint.y + 1][gridpoint.x].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y + 1][gridpoint.x].value; + + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + lookRightCounts2++; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x + 1, gridpoint.y + 1); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + + if (lookRightCounts < 2 && lookRightCounts2 === 0 && grid[gridpoint.y + 1][gridpoint.x + 1].type === Type.NUMBER_STAR) { + let possibleNumber = grid[gridpoint.y + 1][gridpoint.x + 1].value; + const lookRight = (x: number, y: number): boolean => { + try { + const point = grid[y][x]; + if (point.type === Type.NUMBER_STAR) { + possibleNumber += point.value; + return lookRight(x + 1, y); + } + + return false; + } catch { + return false; + } + }; + + lookRight(gridpoint.x + 2, gridpoint.y + 1); + possiblePair.push(Number.parseInt(possibleNumber, 10)); + } + } + } + + if (possiblePair.length > 1) { + engineStarPartNumbers.push(possiblePair); + } else if (possiblePair.length === 1) { + enginePartNumbers.push(possiblePair[0]); + } + } + } + } + + let starTotal = 0; + for (const pair of engineStarPartNumbers) { + starTotal += pair[0] * pair[1]; + } + + const printWithColor = (grid: Gridpoint[][]): void => { + for (const line of grid) { + for (const gridpoint of line) { + switch (gridpoint.type) { + case Type.EMPTY: { + gridpoint.value = `${Color.BLUE}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.NUMBER: { + gridpoint.value = `${Color.YELLOW}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.SYMBOL: { + gridpoint.value = `${Color.RED}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.NUMBER_PART: { + gridpoint.value = `${Color.GREEN}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.NUMBER_STAR: { + gridpoint.value = `${Color.PINK}${gridpoint.value}${Color.RESET}`; + + break; + } + + case Type.STAR: { + gridpoint.value = `${Color.WHITE}${gridpoint.value}${Color.RESET}`; + + break; + } + // No default + } + + Deno.stdout.writeSync(new TextEncoder().encode(gridpoint.value)); // eslint-disable-line @typescript-eslint/no-unsafe-call + } + + Deno.stdout.writeSync(new TextEncoder().encode('\n')); // eslint-disable-line @typescript-eslint/no-unsafe-call + } + }; + + printWithColor(grid); + + console.log(`Sum of all gear ratios is ${starTotal}`); + return starTotal; // Answer: }; -- 2.51.2