Day Three, Part One. Structure the codebase a little
This commit is contained in:
parent
64a7e8fa79
commit
14a89267fe
3
index.ts
Normal file
3
index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { runDayThree } from "./src/day_three";
|
||||||
|
|
||||||
|
runDayThree();
|
@ -4,16 +4,20 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.10",
|
"@types/jest": "^29.5.10",
|
||||||
|
"@types/lodash": "^4.14.202",
|
||||||
"@types/node": "^20.10.1",
|
"@types/node": "^20.10.1",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.3.2"
|
"typescript": "^5.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/day_three.test.ts
Normal file
23
src/day_three.test.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Engine } from "./day_three";
|
||||||
|
|
||||||
|
const schematic = `467..114..
|
||||||
|
...*......
|
||||||
|
..35..633.
|
||||||
|
......#...
|
||||||
|
617*......
|
||||||
|
.....+.58.
|
||||||
|
..592.....
|
||||||
|
......755.
|
||||||
|
...$.*....
|
||||||
|
.664.598..`;
|
||||||
|
|
||||||
|
|
||||||
|
// const schematic = `.....+.58.
|
||||||
|
// ..592.....`;
|
||||||
|
|
||||||
|
describe('Day Three', () => {
|
||||||
|
it('should calculate the sum of all part numbers', () => {
|
||||||
|
const engine = Engine.create(schematic);
|
||||||
|
expect(engine.sumPartNumbers()).toEqual(4361);
|
||||||
|
});
|
||||||
|
});
|
75
src/day_three.ts
Normal file
75
src/day_three.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { takeWhile } from 'lodash';
|
||||||
|
import fs from "fs";
|
||||||
|
import {calculateMinimumCubePowers, calculatePossibleGames, parseGame} from "./day_two";
|
||||||
|
|
||||||
|
export class Engine {
|
||||||
|
constructor(private readonly partNumbers: number[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static create(input: string): Engine {
|
||||||
|
const lines = input.split('\n').filter(Boolean);
|
||||||
|
const partRegex = /\d+/g;
|
||||||
|
const symbolRegex = /[^a-zA-Z\d\.]/g;
|
||||||
|
|
||||||
|
const parts : number[][] = new Array(lines.length).fill(0).map(() => new Array(lines[0].length));
|
||||||
|
const symbols: string[][] = new Array(lines.length).fill(0).map(() => new Array(lines[0].length));
|
||||||
|
|
||||||
|
lines.forEach((line, lineNumber) => {
|
||||||
|
const matches = [...line.matchAll(partRegex)];
|
||||||
|
matches.map(match => {
|
||||||
|
if (match && match.index !== undefined) {
|
||||||
|
const parsedNumber = parseInt(match[0], 10);
|
||||||
|
for (let i = match.index; i < match.index + match[0].length; i++) {
|
||||||
|
parts[lineNumber][i] = parsedNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
lines.forEach((line, lineNumber) => {
|
||||||
|
const matches = [...line.matchAll(symbolRegex)];
|
||||||
|
|
||||||
|
matches.map(match => {
|
||||||
|
if (match && match.index !== undefined) {
|
||||||
|
symbols[lineNumber][match.index] = match[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log(symbols.length, parts.length)
|
||||||
|
|
||||||
|
const partsList = symbols.flatMap((row, rowIndex) =>
|
||||||
|
row.map((symbol, index) => {
|
||||||
|
if (!symbol) return symbol;
|
||||||
|
|
||||||
|
const partIndex = [
|
||||||
|
[rowIndex - 1, index - 1],
|
||||||
|
[rowIndex - 1, index],
|
||||||
|
[rowIndex - 1, index + 1],
|
||||||
|
[rowIndex, index - 1],
|
||||||
|
[rowIndex, index + 1],
|
||||||
|
[rowIndex + 1, index - 1],
|
||||||
|
[rowIndex + 1, index],
|
||||||
|
[rowIndex + 1, index + 1]
|
||||||
|
];
|
||||||
|
return Array.from(new Set(partIndex.filter(([rowNum, col]) => rowNum >= 0 && rowNum <= symbols.length && index >= 0 && index <= row.length)
|
||||||
|
.map(([rowNum, column]) => {
|
||||||
|
return parts[rowNum][column]
|
||||||
|
}).filter(Boolean)))
|
||||||
|
.reduce((total, val) => total + val,0);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return new Engine(partsList as number[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sumPartNumbers(): number {
|
||||||
|
return this.partNumbers.reduce((total, partNumber) => total + partNumber, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const runDayThree = () => {
|
||||||
|
const input = fs.readFileSync('./inputs/day_three_input.txt', 'utf8');
|
||||||
|
const engine = Engine.create(input)
|
||||||
|
|
||||||
|
console.log(engine.sumPartNumbers());
|
||||||
|
}
|
63
src/day_two.alternative.test.ts
Normal file
63
src/day_two.alternative.test.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { Game, calculatePossibleGames, calculateMinimumCubePowers } from "./day_two.alternative";
|
||||||
|
|
||||||
|
const partOneInput = `Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
||||||
|
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|
||||||
|
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|
||||||
|
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|
||||||
|
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green`
|
||||||
|
|
||||||
|
describe('Day Two', () => {
|
||||||
|
it('should parse a game', () => {
|
||||||
|
const games = partOneInput.split('\n').map(Game.parse);
|
||||||
|
expect(games).toEqual([
|
||||||
|
new Game(1, [
|
||||||
|
{ blue: 3, green: 0, red: 4 },
|
||||||
|
{ blue: 6, red: 1, green: 2 },
|
||||||
|
{ blue: 0, red: 0, green: 2 }
|
||||||
|
]
|
||||||
|
),
|
||||||
|
new Game(2, [
|
||||||
|
{ blue: 1, green: 2, red: 0 },
|
||||||
|
{ blue: 4, red: 1, green: 3 },
|
||||||
|
{ blue: 1, red: 0, green: 1 }
|
||||||
|
]
|
||||||
|
),
|
||||||
|
new Game(
|
||||||
|
3,
|
||||||
|
[
|
||||||
|
{ blue: 6, green: 8, red: 20 },
|
||||||
|
{ blue: 5, red: 4, green: 13 },
|
||||||
|
{ blue: 0, red: 1, green: 5 }
|
||||||
|
]
|
||||||
|
),
|
||||||
|
new Game(
|
||||||
|
4,
|
||||||
|
[
|
||||||
|
{ blue: 6, green: 1, red: 3 },
|
||||||
|
{ blue: 0, red: 6, green: 3 },
|
||||||
|
{ blue: 15, red: 14, green: 3 }
|
||||||
|
]
|
||||||
|
),
|
||||||
|
new Game(
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
{ blue: 1, green: 3, red: 6 },
|
||||||
|
{ blue: 2, red: 1, green: 2 },
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate which games should only have been possible with the number of cubes', () => {
|
||||||
|
const games = partOneInput.split('\n').map(Game.parse);
|
||||||
|
expect(calculatePossibleGames(games, {
|
||||||
|
blue: 14,
|
||||||
|
red: 12,
|
||||||
|
green: 13
|
||||||
|
})).toEqual(8);
|
||||||
|
});
|
||||||
|
it('should find the minimum cubes in a set and sum the powers of the cubes', () => {
|
||||||
|
const games = partOneInput.split('\n').map(Game.parse);
|
||||||
|
expect(calculateMinimumCubePowers(games)).toEqual(2286);
|
||||||
|
});
|
||||||
|
});
|
54
src/day_two.alternative.ts
Normal file
54
src/day_two.alternative.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { Cubes } from "./day_two";
|
||||||
|
|
||||||
|
export class Game {
|
||||||
|
constructor(
|
||||||
|
public readonly id: number,
|
||||||
|
private readonly rounds: Cubes[]
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static parse(input: string): Game {
|
||||||
|
const [gameId, roundsInput] = input.split(':');
|
||||||
|
const id = parseInt(gameId.split(' ')[1], 10);
|
||||||
|
|
||||||
|
const rounds = roundsInput.split(';').map(round => {
|
||||||
|
return round.trim().split(',').map(cube => cube.trim()).reduce((total, cube) => {
|
||||||
|
const [amount, color] = cube.split(' ');
|
||||||
|
total[color as keyof Cubes] = parseInt(amount, 10);
|
||||||
|
return total;
|
||||||
|
}, {blue: 0, green: 0, red: 0} as Cubes);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Game(id, rounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private minimumCubesRequired(): Cubes {
|
||||||
|
return this.rounds.reduce((total, round) => {
|
||||||
|
Object.keys(round).forEach(color => {
|
||||||
|
total[color as keyof Cubes] = Math.max(total[color as keyof Cubes], round[color as keyof Cubes]);
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
}, {blue: 0, green: 0, red: 0} as Cubes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public calculateMinimumCubePowers(): number {
|
||||||
|
const minimumCubes = this.minimumCubesRequired();
|
||||||
|
return minimumCubes.blue * minimumCubes.green * minimumCubes.red;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isPossible(limits: Cubes): boolean {
|
||||||
|
return this.rounds.every(round => {
|
||||||
|
return Object.keys(round).every(color => {
|
||||||
|
return round[color as keyof Cubes] <= limits[color as keyof Cubes];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calculatePossibleGames = (games: Game[], limits: Cubes): number => {
|
||||||
|
const possibleGames = games.filter(game => game.isPossible(limits));
|
||||||
|
return possibleGames.reduce((total, game) => total + game.id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calculateMinimumCubePowers = (games: Game[]): number => {
|
||||||
|
return games.reduce((total, game) => total + game.calculateMinimumCubePowers(), 0);
|
||||||
|
}
|
@ -66,5 +66,3 @@ export const runDayTwo = () => {
|
|||||||
console.log(calculatePossibleGames(games, limits));
|
console.log(calculatePossibleGames(games, limits));
|
||||||
console.log(calculateMinimumCubePowers(games));
|
console.log(calculateMinimumCubePowers(games));
|
||||||
}
|
}
|
||||||
|
|
||||||
runDayTwo();
|
|
10
yarn.lock
10
yarn.lock
@ -658,6 +658,11 @@
|
|||||||
expect "^29.0.0"
|
expect "^29.0.0"
|
||||||
pretty-format "^29.0.0"
|
pretty-format "^29.0.0"
|
||||||
|
|
||||||
|
"@types/lodash@^4.14.202":
|
||||||
|
version "4.14.202"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
|
||||||
|
integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "20.10.2"
|
version "20.10.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.2.tgz#32a5e8228357f57714ad28d52229ab04880c2814"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.2.tgz#32a5e8228357f57714ad28d52229ab04880c2814"
|
||||||
@ -1736,6 +1741,11 @@ lodash.memoize@4.x:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||||
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
|
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
|
||||||
|
|
||||||
|
lodash@^4.17.21:
|
||||||
|
version "4.17.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
lru-cache@^5.1.1:
|
lru-cache@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||||
|
Loading…
Reference in New Issue
Block a user