Day Three, Part One. Structure the codebase a little
This commit is contained in:
1 parent
64a7e8fa79
commit
14a89267fe
10 files changed
+234
-4
No files matched your search
@@ -0,0 +1,74 @@
|
||||
import fs from 'fs';
|
||||
|
||||
const DIGIT_REGEX = new RegExp(/(?=(\d|one|two|three|four|five|six|seven|eight|nine))/g);
|
||||
|
||||
function parseDigit(digit: string): string {
|
||||
switch (digit) {
|
||||
case "one": return "1";
|
||||
case "two": return "2";
|
||||
case "three": return "3";
|
||||
case "four": return "4";
|
||||
case "five": return "5";
|
||||
case "six": return "6";
|
||||
case "seven": return "7";
|
||||
case "eight": return "8";
|
||||
case "nine": return "9";
|
||||
default: return digit;
|
||||
}
|
||||
}
|
||||
|
||||
function parseLine(line: string): number {
|
||||
const matches = line.matchAll(DIGIT_REGEX);
|
||||
|
||||
|
||||
if (!matches) return 0;
|
||||
|
||||
const converted = [...matches].flatMap(((matchArray) => matchArray.map(parseDigit))).filter(digit => digit.length);
|
||||
|
||||
console.log(converted);
|
||||
const str = `${converted[0]}${converted[converted.length - 1]}`;
|
||||
const parsed = parseInt(str, 10);
|
||||
|
||||
if (isNaN(parsed)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
function calculateCalibration(lines: string[]): number {
|
||||
return lines.reduce((calibration, line) => {
|
||||
const parsed = parseLine(line);
|
||||
|
||||
if (isNaN(parsed)) {
|
||||
return calibration;
|
||||
}
|
||||
return calibration + parsed;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function dayOne() {
|
||||
const lines = fs.readFileSync("./inputs/day_one.txt").toString('utf-8').split('\n');
|
||||
console.log(calculateCalibration(lines));
|
||||
}
|
||||
|
||||
function test() {
|
||||
const expected = 83;
|
||||
const result = calculateCalibration([
|
||||
'two1nine',
|
||||
'eightwothree',
|
||||
'abcone2threexyz',
|
||||
'xtwone3four',
|
||||
'4nineeightseven2',
|
||||
'zoneight234',
|
||||
'7pqrstsixteen',
|
||||
'eighthree',
|
||||
'sevenine'
|
||||
])
|
||||
if (result !== expected) {
|
||||
throw new Error(`Expected ${expected} but got ${result}`);
|
||||
}
|
||||
}
|
||||
test();
|
||||
dayOne();
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { calculateMinimumCubePowers, calculatePossibleGames, parseGame } from "./day_two";
|
||||
|
||||
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(parseGame);
|
||||
expect(games).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
rounds: [
|
||||
{ blue: 3, green: 0, red: 4 },
|
||||
{ blue: 6, red: 1, green: 2 },
|
||||
{ blue: 0, red: 0, green: 2 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
rounds: [
|
||||
{ blue: 1, green: 2, red: 0 },
|
||||
{ blue: 4, red: 1, green: 3 },
|
||||
{ blue: 1, red: 0, green: 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
rounds: [
|
||||
{ blue: 6, green: 8, red: 20 },
|
||||
{ blue: 5, red: 4, green: 13 },
|
||||
{ blue: 0, red: 1, green: 5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
rounds: [
|
||||
{ blue: 6, green: 1, red: 3 },
|
||||
{ blue: 0, red: 6, green: 3 },
|
||||
{ blue: 15, red: 14, green: 3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
rounds: [
|
||||
{ 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(parseGame);
|
||||
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(parseGame);
|
||||
expect(calculateMinimumCubePowers(games)).toEqual(2286);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import fs from 'fs';
|
||||
|
||||
export type Cubes = {
|
||||
blue: number;
|
||||
red: number;
|
||||
green: number;
|
||||
}
|
||||
|
||||
export type Game = {
|
||||
id: number;
|
||||
rounds: Cubes[];
|
||||
}
|
||||
|
||||
export const parseGame = (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 {
|
||||
id,
|
||||
rounds
|
||||
}
|
||||
}
|
||||
|
||||
export const calculatePossibleGames = (games: Game[], limits: Cubes): number => {
|
||||
const possibleGames = games.filter(game => {
|
||||
return game.rounds.every(round => {
|
||||
return Object.keys(round).every(color => {
|
||||
return round[color as keyof Cubes] <= limits[color as keyof Cubes];
|
||||
});
|
||||
});
|
||||
});
|
||||
return possibleGames.reduce((total, game) => total + game.id, 0);
|
||||
}
|
||||
|
||||
const minimumCubesRequiredForGame = (game: Game): Cubes => {
|
||||
return game.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);
|
||||
}
|
||||
|
||||
export const calculateMinimumCubePowers = (games: Game[]): number => {
|
||||
const minimumCubes = games.map(minimumCubesRequiredForGame);
|
||||
return minimumCubes.reduce((total, cubes) => {
|
||||
return total + (cubes.blue * cubes.green * cubes.red);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
export const runDayTwo = () => {
|
||||
const input = fs.readFileSync('./inputs/day_two_input.txt', 'utf8').split('\n').filter(Boolean);
|
||||
const games = input.map(parseGame);
|
||||
const limits = {
|
||||
blue: 14,
|
||||
red: 12,
|
||||
green: 13
|
||||
};
|
||||
console.log(calculatePossibleGames(games, limits));
|
||||
console.log(calculateMinimumCubePowers(games));
|
||||
}
|
||||
Reference in new issue
Block a user