generated from eric/adventofcode2023
Compare commits
15 Commits
8223e82097
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
514227fefc | ||
|
|
cb5a3599fd | ||
|
|
c7392f0453 | ||
|
|
f06e5b5f64 | ||
|
|
7bbedb47a5 | ||
|
|
42b2af4452 | ||
|
|
462508c90f | ||
|
|
83911a58ec | ||
|
|
ea72130ebc | ||
|
|
2cd532c419 | ||
|
|
3f9cfd121a | ||
|
|
f17d99bec1 | ||
|
|
06de865c12 | ||
|
|
26d828fe1a | ||
|
|
a466e30de5 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Advent of Code input files
|
# Advent of Code input files
|
||||||
input.txt
|
input.txt
|
||||||
sample.txt
|
sample*.txt
|
||||||
|
|
||||||
# ---> Node
|
# ---> Node
|
||||||
# Logs
|
# Logs
|
||||||
|
|||||||
26
01/code.js
26
01/code.js
@@ -29,12 +29,32 @@ console.log(zeroCount);
|
|||||||
|
|
||||||
// Part Two
|
// Part Two
|
||||||
|
|
||||||
for (const element of inputArray) {
|
let zeroCount2 = 0;
|
||||||
|
let currentPosition2 = 50;
|
||||||
|
|
||||||
|
for (const element of inputArray) {
|
||||||
|
const instruction = element;
|
||||||
|
const direction = instruction[0];
|
||||||
|
const steps = Number.parseInt(instruction.slice(1));
|
||||||
|
|
||||||
|
if (direction === 'L') {
|
||||||
|
for (let i = 1; i < steps + 1; i++) {
|
||||||
|
currentPosition2 -= 1;
|
||||||
|
if (currentPosition2 === 0 || (currentPosition2 % 100) === 0) {
|
||||||
|
zeroCount2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (let i = 1; i < steps + 1; i++) {
|
||||||
|
currentPosition2 += 1;
|
||||||
|
if (currentPosition2 === 0 || (currentPosition2 % 100) === 0) {
|
||||||
|
zeroCount2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(zeroCount2);
|
||||||
console.log();
|
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
|
|
||||||
|
|||||||
56
02/code.js
56
02/code.js
@@ -1,25 +1,65 @@
|
|||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
const inputArray = readFileSync('sample.txt').toString().split("\n");
|
// const inputArray = readFileSync('sample.txt').toString().split(",");
|
||||||
// const inputArray = readFileSync('input.txt').toString().split("\n");
|
const inputArray = readFileSync('input.txt').toString().split(",");
|
||||||
|
|
||||||
// Part One
|
// Part One
|
||||||
|
console.time("part1");
|
||||||
|
let sum = 0;
|
||||||
|
|
||||||
for (const element of inputArray) {
|
for (const element of inputArray) {
|
||||||
const contents = element.split(" ");
|
const contents = element.split("-");
|
||||||
|
const start = Number.parseInt(contents[0]);
|
||||||
|
const end = Number.parseInt(contents[1]);
|
||||||
|
|
||||||
|
for (let sku = start; sku <= end; sku++) {
|
||||||
|
const skuString = sku.toString();
|
||||||
|
const digits = skuString.length
|
||||||
|
if (digits % 2 === 0) {
|
||||||
|
if (skuString.slice(0, digits / 2) === skuString.slice(digits / 2)) {
|
||||||
|
sum += sku;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
console.log();
|
}
|
||||||
|
}
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(sum);
|
||||||
|
|
||||||
|
|
||||||
// Part Two
|
// Part Two
|
||||||
|
|
||||||
for (const element of inputArray) {
|
console.time("part2");
|
||||||
const contents = element.split(" ");
|
let sum2 = 0;
|
||||||
|
const repeats = [];
|
||||||
|
|
||||||
|
for (const element of inputArray) {
|
||||||
|
const contents = element.split("-");
|
||||||
|
const start = Number.parseInt(contents[0]);
|
||||||
|
const end = Number.parseInt(contents[1]);
|
||||||
|
|
||||||
|
for (let sku = start; sku <= end; sku++) {
|
||||||
|
const skuString = sku.toString();
|
||||||
|
const digits = skuString.length
|
||||||
|
const maxRepeatLength = Math.floor(digits / 2);
|
||||||
|
for (let repeatLength = 1; repeatLength <= maxRepeatLength; repeatLength++) {
|
||||||
|
if (digits % repeatLength === 0) {
|
||||||
|
const maxRepeats = digits / repeatLength;
|
||||||
|
const repeatPattern = skuString.slice(0, repeatLength);
|
||||||
|
const repeatedPattern = repeatPattern.repeat(maxRepeats);
|
||||||
|
if (repeatedPattern === skuString) {
|
||||||
|
repeats.push(sku);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log();
|
const uniqueRepeats = [...new Set(repeats)];
|
||||||
|
|
||||||
|
for (const repeat of uniqueRepeats) {
|
||||||
|
sum2 += Number.parseInt(repeat);
|
||||||
|
}
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(sum2);
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
|
|||||||
162
03/code.js
162
03/code.js
@@ -1,25 +1,169 @@
|
|||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
const inputArray = readFileSync('sample.txt').toString().split("\n");
|
// const inputArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
// const inputArray = readFileSync('input.txt').toString().split("\n");
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
// Part One
|
// Part One
|
||||||
|
|
||||||
for (const element of inputArray) {
|
console.time("part1");
|
||||||
const contents = element.split(" ");
|
let sum = 0;
|
||||||
|
|
||||||
|
for (const element of inputArray) {
|
||||||
|
const length = element.length;
|
||||||
|
let firstDigitLocation = 0;
|
||||||
|
let firstDigit = 1;
|
||||||
|
let lastDigit = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < length - 1; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > firstDigit) {
|
||||||
|
firstDigit = digit;
|
||||||
|
firstDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = firstDigitLocation + 1; i < length; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > lastDigit) {
|
||||||
|
lastDigit = digit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const joltage = (firstDigit * 10) + lastDigit;
|
||||||
|
sum += joltage;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log();
|
console.timeEnd("part1");
|
||||||
|
console.log(sum);
|
||||||
|
|
||||||
// Part Two
|
// Part Two
|
||||||
|
|
||||||
for (const element of inputArray) {
|
console.time("part2");
|
||||||
const contents = element.split(" ");
|
let sum2 = 0;
|
||||||
|
|
||||||
|
for (const element of inputArray) {
|
||||||
|
const length = element.length;
|
||||||
|
let firstDigitLocation = 0;
|
||||||
|
let firstDigit = 1;
|
||||||
|
let secondDigitLocation = 1;
|
||||||
|
let secondDigit = 0;
|
||||||
|
let thirdDigitLocation = 2;
|
||||||
|
let thirdDigit = 0;
|
||||||
|
let fourthDigitLocation = 3;
|
||||||
|
let fourthDigit = 0;
|
||||||
|
let fifthDigitLocation = 4;
|
||||||
|
let fifthDigit = 0;
|
||||||
|
let sixthDigitLocation = 5;
|
||||||
|
let sixthDigit = 0;
|
||||||
|
let seventhDigitLocation = 6;
|
||||||
|
let seventhDigit = 0;
|
||||||
|
let eighthDigitLocation = 7;
|
||||||
|
let eighthDigit = 0;
|
||||||
|
let ninthDigitLocation = 8;
|
||||||
|
let ninthDigit = 0;
|
||||||
|
let tenthDigitLocation = 9;
|
||||||
|
let tenthDigit = 0;
|
||||||
|
let eleventhDigitLocation = 10;
|
||||||
|
let eleventhDigit = 0;
|
||||||
|
let lastDigit = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < length - 11; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > firstDigit) {
|
||||||
|
firstDigit = digit;
|
||||||
|
firstDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = firstDigitLocation + 1; i < length - 10; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > secondDigit) {
|
||||||
|
secondDigit = digit;
|
||||||
|
secondDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = secondDigitLocation + 1; i < length - 9 ; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > thirdDigit) {
|
||||||
|
thirdDigit = digit;
|
||||||
|
thirdDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = thirdDigitLocation + 1; i < length - 8; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > fourthDigit) {
|
||||||
|
fourthDigit = digit;
|
||||||
|
fourthDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = fourthDigitLocation + 1; i < length - 7; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > fifthDigit) {
|
||||||
|
fifthDigit = digit;
|
||||||
|
fifthDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = fifthDigitLocation + 1; i < length - 6; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > sixthDigit) {
|
||||||
|
sixthDigit = digit;
|
||||||
|
sixthDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = sixthDigitLocation + 1; i < length - 5; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > seventhDigit) {
|
||||||
|
seventhDigit = digit;
|
||||||
|
seventhDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = seventhDigitLocation + 1; i < length - 4; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > eighthDigit) {
|
||||||
|
eighthDigit = digit;
|
||||||
|
eighthDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = eighthDigitLocation + 1; i < length - 3; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > ninthDigit) {
|
||||||
|
ninthDigit = digit;
|
||||||
|
ninthDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = ninthDigitLocation + 1; i < length - 2; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > tenthDigit) {
|
||||||
|
tenthDigit = digit;
|
||||||
|
tenthDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = tenthDigitLocation + 1; i < length - 1; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > eleventhDigit) {
|
||||||
|
eleventhDigit = digit;
|
||||||
|
eleventhDigitLocation = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = eleventhDigitLocation + 1; i < length; i++) {
|
||||||
|
const digit = Number.parseInt(element[i]);
|
||||||
|
if (digit > lastDigit) {
|
||||||
|
lastDigit = digit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log();
|
const joltage = (firstDigit * 100000000000) +
|
||||||
|
(secondDigit * 10000000000) +
|
||||||
|
(thirdDigit * 1000000000) +
|
||||||
|
(fourthDigit * 100000000) +
|
||||||
|
(fifthDigit * 10000000) +
|
||||||
|
(sixthDigit * 1000000) +
|
||||||
|
(seventhDigit * 100000) +
|
||||||
|
(eighthDigit * 10000) +
|
||||||
|
(ninthDigit * 1000) +
|
||||||
|
(tenthDigit * 100) +
|
||||||
|
(eleventhDigit * 10) +
|
||||||
|
lastDigit;
|
||||||
|
sum2 += joltage;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(sum2);
|
||||||
// functions
|
// functions
|
||||||
|
|||||||
143
04/code.js
Normal file
143
04/code.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = [].concat(sampleArray);
|
||||||
|
} else {
|
||||||
|
usedArray = [].concat(inputArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
let sum = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < usedArray.length; i++) {
|
||||||
|
let row = usedArray[i];
|
||||||
|
for (let j = 0; j < row.length; j++) {
|
||||||
|
let cell = row[j];
|
||||||
|
if (cell === '@') {
|
||||||
|
let openSides = 0;
|
||||||
|
const left = j > 0 ? row[j - 1] : '.';
|
||||||
|
const right = j < row.length - 1 ? row[j + 1] : '.';
|
||||||
|
const up = i > 0 ? usedArray[i - 1][j] : '.';
|
||||||
|
const down = i < usedArray.length - 1 ? usedArray[i + 1][j] : '.';
|
||||||
|
const upperLeft = i > 0 && j > 0 ? usedArray[i - 1][j - 1] : '.';
|
||||||
|
const upperRight = i > 0 && j < row.length - 1 ? usedArray[i - 1][j + 1] : '.';
|
||||||
|
const lowerLeft = i < usedArray.length - 1 && j > 0 ? usedArray[i + 1][j - 1] : '.';
|
||||||
|
const lowerRight = i < usedArray.length - 1 && j < row.length - 1 ? usedArray[i + 1][j + 1] : '.';
|
||||||
|
if (left === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (right === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (up === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (down === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (upperLeft === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (upperRight === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (lowerLeft === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (lowerRight === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (openSides >= 5) {
|
||||||
|
sum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(sum);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
let usedArray2 = [];
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray2 = [].concat(sampleArray);
|
||||||
|
} else {
|
||||||
|
usedArray2 = [].concat(inputArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
let sum2 = 0;
|
||||||
|
let done = false;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
getOpenSides(sum2, usedArray2);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(sum2);
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
function getOpenSides(startingSum, usedArray) {
|
||||||
|
const newSum = JSON.parse(JSON.stringify(startingSum));
|
||||||
|
for (let i = 0; i < usedArray.length; i++) {
|
||||||
|
let row = [...usedArray[i].split('')];
|
||||||
|
for (let j = 0; j < row.length; j++) {
|
||||||
|
let cell = row[j];
|
||||||
|
if (cell === '@') {
|
||||||
|
let openSides = 0;
|
||||||
|
const left = j > 0 ? row[j - 1] : '.';
|
||||||
|
const right = j < row.length - 1 ? row[j + 1] : '.';
|
||||||
|
const up = i > 0 ? usedArray[i - 1][j] : '.';
|
||||||
|
const down = i < usedArray.length - 1 ? usedArray[i + 1][j] : '.';
|
||||||
|
const upperLeft = i > 0 && j > 0 ? usedArray[i - 1][j - 1] : '.';
|
||||||
|
const upperRight = i > 0 && j < row.length - 1 ? usedArray[i - 1][j + 1] : '.';
|
||||||
|
const lowerLeft = i < usedArray.length - 1 && j > 0 ? usedArray[i + 1][j - 1] : '.';
|
||||||
|
const lowerRight = i < usedArray.length - 1 && j < row.length - 1 ? usedArray[i + 1][j + 1] : '.';
|
||||||
|
if (left === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (right === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (up === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (down === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (upperLeft === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (upperRight === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (lowerLeft === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (lowerRight === '.') {
|
||||||
|
openSides++;
|
||||||
|
}
|
||||||
|
if (openSides >= 5) {
|
||||||
|
sum2++;
|
||||||
|
row[j] = '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usedArray[i] = row.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sum2 === newSum) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
97
05/code.js
Normal file
97
05/code.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
const ranges = [];
|
||||||
|
const ingredients = [];
|
||||||
|
const validIngredients = [];
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
if (element.includes("-")) {
|
||||||
|
const contents = element.split("-");
|
||||||
|
const start = Number.parseInt(contents[0]);
|
||||||
|
const end = Number.parseInt(contents[1]);
|
||||||
|
ranges.push({ start, end });
|
||||||
|
} else if (element !== "") {
|
||||||
|
ingredients.push(Number.parseInt(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ingredient of ingredients) {
|
||||||
|
for (const range of ranges) {
|
||||||
|
if (ingredient >= range.start && ingredient <= range.end) {
|
||||||
|
validIngredients.push(ingredient);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(validIngredients.length);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
const ranges2 = [];
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
if (element.includes("-")) {
|
||||||
|
const contents = element.split("-");
|
||||||
|
const start = Number.parseInt(contents[0]);
|
||||||
|
const end = Number.parseInt(contents[1]);
|
||||||
|
ranges2.push({ start, end });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges2.sort((a, b) => a.start - b.start);
|
||||||
|
let processing = true;
|
||||||
|
|
||||||
|
while (processing) {
|
||||||
|
compactRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Five more times for fun, just in case
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
compactRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
let sum = 0;
|
||||||
|
for (const range of ranges2) {
|
||||||
|
sum += range.end - range.start + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(sum);
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
function compactRanges() {
|
||||||
|
let continuing = false;
|
||||||
|
let i = 0;
|
||||||
|
while (i < ranges2.length - 1) {
|
||||||
|
if (ranges2[i + 1].start <= ranges2[i].end + 1) {
|
||||||
|
ranges2[i].end = Math.max(ranges2[i].end, ranges2[i + 1].end);
|
||||||
|
ranges2.splice(i + 1, 1);
|
||||||
|
continuing = true;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processing = continuing;
|
||||||
|
}
|
||||||
135
06/code.js
Normal file
135
06/code.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
|
||||||
|
const problems = [];
|
||||||
|
for (const row of usedArray) {
|
||||||
|
const contents = row.trim().split(/\s+/);
|
||||||
|
|
||||||
|
for (let i = 0; i < contents.length; i++) {
|
||||||
|
const element = contents[i];
|
||||||
|
if (!problems[i]) {
|
||||||
|
problems[i] = [];
|
||||||
|
}
|
||||||
|
problems[i].push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let problem of problems) {
|
||||||
|
problem = problem.reverse();
|
||||||
|
const operator = problem.shift();
|
||||||
|
let answer = operator === '+' ? 0 : 1;
|
||||||
|
for (const number of problem) {
|
||||||
|
if (operator === '+') {
|
||||||
|
answer += Number.parseInt(number);
|
||||||
|
} else {
|
||||||
|
answer *= Number.parseInt(number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total += answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(total);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
let total2 = 0;
|
||||||
|
|
||||||
|
let newInput = JSON.parse(JSON.stringify(usedArray));
|
||||||
|
for (let rowIndex = 0; rowIndex < newInput.length; rowIndex++) {
|
||||||
|
const row = newInput[rowIndex];
|
||||||
|
newInput[rowIndex] = row.split("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const problemStarts = [];
|
||||||
|
const operatorRowArray = newInput[usedArray.length - 1];
|
||||||
|
for (let i = 0; i < operatorRowArray.length; i++) {
|
||||||
|
if (["*", "+"].includes(operatorRowArray[i])) {
|
||||||
|
problemStarts.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let rowIndex = 0; rowIndex < usedArray.length; rowIndex++) {
|
||||||
|
const row = usedArray[rowIndex];
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
const char = row[i];
|
||||||
|
if (char === " " && !problemStarts.includes(i + 1)) {
|
||||||
|
newInput[rowIndex][i] = '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let rowIndex = 0; rowIndex < newInput.length; rowIndex++) {
|
||||||
|
const row = newInput[rowIndex];
|
||||||
|
newInput[rowIndex] = row.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const problems2 = [];
|
||||||
|
for (const row of newInput) {
|
||||||
|
const contents = row.trim().split(/\s+/);
|
||||||
|
for (let i = 0; i < contents.length; i++) {
|
||||||
|
const element = contents[i];
|
||||||
|
if (!problems2[i]) {
|
||||||
|
problems2[i] = [];
|
||||||
|
}
|
||||||
|
problems2[i].push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let problemsVertical = [];
|
||||||
|
for (let i = 0; i < problems2.length; i++) {
|
||||||
|
const problem = problems2[i];
|
||||||
|
let vertProblem = [];
|
||||||
|
for (let j = 0; j < problem.length - 1; j++) {
|
||||||
|
const newElement =
|
||||||
|
(problem[0] && problem[0][j] && problem[0][j] !== '#' ? problem[0][j] : '') +
|
||||||
|
(problem[1] && problem[1][j] && problem[1][j] !== '#' ? problem[1][j] : '') +
|
||||||
|
(problem[2] && problem[2][j] && problem[2][j] !== '#' ? problem[2][j] : '') +
|
||||||
|
(problem[3] && problem[3][j] && problem[3][j] !== '#' ? problem[3][j] : '') +
|
||||||
|
(problem[4] && problem[4][j] && problem[4][j] !== '#' ? problem[4][j] : '');
|
||||||
|
if (Number.parseInt(newElement)) {
|
||||||
|
vertProblem.push(Number.parseInt(newElement));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertProblem.push(problem[problem.length - 1]);
|
||||||
|
problemsVertical.push(vertProblem);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let problem of problemsVertical) {
|
||||||
|
problem = problem.reverse();
|
||||||
|
const operator = problem.shift();
|
||||||
|
let answer = operator.includes('+') ? 0 : 1;
|
||||||
|
for (const number of problem) {
|
||||||
|
if (operator.includes('+')) {
|
||||||
|
answer += Number.parseInt(number);
|
||||||
|
} else {
|
||||||
|
answer *= Number.parseInt(number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total2 += answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(total2);
|
||||||
|
|
||||||
|
// functions
|
||||||
103
07/code.js
Normal file
103
07/code.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
let newInput = JSON.parse(JSON.stringify(usedArray));
|
||||||
|
for (let i = 0; i < newInput.length; i++) {
|
||||||
|
newInput[i] = newInput[i].split("");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i < newInput.length; i++) {
|
||||||
|
for (let j = 0; j < newInput[i].length; j++) {
|
||||||
|
if ((newInput[i - 1][j] === '|' || newInput[i - 1][j] === 'S') && newInput[i][j] === '.') {
|
||||||
|
newInput[i][j] = '|';
|
||||||
|
}
|
||||||
|
if (newInput[i - 1][j] === '|' && newInput[i][j] === '^') {
|
||||||
|
if (j > 0 && newInput[i][j - 1] === '.') {
|
||||||
|
newInput[i][j - 1] = '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newInput[i - 1][j] === '|' && newInput[i][j] === '^') {
|
||||||
|
if (j < newInput[i].length - 1 && newInput[i][j + 1] === '.') {
|
||||||
|
newInput[i][j + 1] = '|';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let splits = 0;
|
||||||
|
|
||||||
|
for (let i = 1; i < newInput.length; i++) {
|
||||||
|
for (let j = 0; j < newInput[i].length; j++) {
|
||||||
|
if (newInput[i][j] === '^' && newInput[i - 1][j] === '|') {
|
||||||
|
splits++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(splits);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
newInput = JSON.parse(JSON.stringify(usedArray));
|
||||||
|
for (let i = 0; i < newInput.length; i++) {
|
||||||
|
newInput[i] = newInput[i].split("");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i < newInput.length; i++) {
|
||||||
|
for (let j = 0; j < newInput[i].length; j++) {
|
||||||
|
if (newInput[i - 1][j] === 'S') {
|
||||||
|
newInput[i][j] = 1;
|
||||||
|
}
|
||||||
|
if (Number.parseInt(newInput[i - 1][j]) > 0 && Number.parseInt(newInput[i][j]) > 0) {
|
||||||
|
newInput[i][j] = newInput[i][j] + newInput[i - 1][j];
|
||||||
|
}
|
||||||
|
if ((Number.parseInt(newInput[i - 1][j]) > 0) && newInput[i][j] === '.') {
|
||||||
|
newInput[i][j] = newInput[i - 1][j];
|
||||||
|
}
|
||||||
|
if (Number.parseInt(newInput[i - 1][j]) > 0 && newInput[i][j] === '^') {
|
||||||
|
if (j > 0 && newInput[i][j - 1] === '.') {
|
||||||
|
newInput[i][j - 1] = newInput[i - 1][j];
|
||||||
|
} else if (j > 0 && Number.parseInt(newInput[i][j - 1]) > 0) {
|
||||||
|
newInput[i][j - 1] = Number.parseInt(newInput[i][j - 1]) + newInput[i - 1][j];
|
||||||
|
}
|
||||||
|
if (j < newInput[i].length - 1 && newInput[i][j + 1] === '.') {
|
||||||
|
newInput[i][j + 1] = newInput[i - 1][j];
|
||||||
|
} else if (j < newInput[i].length - 1 && Number.parseInt(newInput[i][j + 1]) > 0) {
|
||||||
|
newInput[i][j + 1] = Number.parseInt(newInput[i][j + 1]) + newInput[i - 1][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let timelines = 0;
|
||||||
|
|
||||||
|
const bottomRow = newInput[newInput.length - 1];
|
||||||
|
for (let i = 0; i < bottomRow.length; i++) {
|
||||||
|
if (Number.parseInt(bottomRow[i]) > 0) {
|
||||||
|
timelines += Number.parseInt(bottomRow[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(timelines);
|
||||||
|
|
||||||
|
// functions
|
||||||
120
08/code.js
Normal file
120
08/code.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
let coordinatesArray = [];
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
const coordinates = element.split(",");
|
||||||
|
coordinatesArray.push({ x: Number.parseInt(coordinates[0]), y: Number.parseInt(coordinates[1]), z: Number.parseInt(coordinates[2]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
let segments = [];
|
||||||
|
let parent = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < coordinatesArray.length; i++) {
|
||||||
|
for (let j = i + 1; j < coordinatesArray.length; j++) {
|
||||||
|
const x2 = Math.pow(coordinatesArray[i].x - coordinatesArray[j].x, 2);
|
||||||
|
const y2 = Math.pow(coordinatesArray[i].y - coordinatesArray[j].y, 2);
|
||||||
|
const z2 = Math.pow(coordinatesArray[i].z - coordinatesArray[j].z, 2);
|
||||||
|
const distance = Math.sqrt(x2 + y2 + z2);
|
||||||
|
segments.push({ from: i, to: j, distance });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.sort((a, b) => a.distance - b.distance);
|
||||||
|
const shortest = segments.slice(0, sampleMode ? 10 : 1000);
|
||||||
|
|
||||||
|
for (const seg of shortest) {
|
||||||
|
union(seg.from, seg.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
const circuits = {};
|
||||||
|
for (const seg of shortest) {
|
||||||
|
const root = find(seg.from);
|
||||||
|
circuits[root] = circuits[root] || new Set();
|
||||||
|
circuits[root].add(seg.from);
|
||||||
|
circuits[root].add(seg.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
const circuitList = Object.values(circuits);
|
||||||
|
const sizes = circuitList.map(nodes => nodes.size).sort((a, b) => b - a);
|
||||||
|
const result = sizes[0] * sizes[1] * sizes[2];
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
coordinatesArray = [];
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
const coordinates = element.split(",");
|
||||||
|
coordinatesArray.push({ x: Number.parseInt(coordinates[0]), y: Number.parseInt(coordinates[1]), z: Number.parseInt(coordinates[2]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
segments = [];
|
||||||
|
parent = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < coordinatesArray.length; i++) {
|
||||||
|
for (let j = i + 1; j < coordinatesArray.length; j++) {
|
||||||
|
const x2 = Math.pow(coordinatesArray[i].x - coordinatesArray[j].x, 2);
|
||||||
|
const y2 = Math.pow(coordinatesArray[i].y - coordinatesArray[j].y, 2);
|
||||||
|
const z2 = Math.pow(coordinatesArray[i].z - coordinatesArray[j].z, 2);
|
||||||
|
const distance = Math.sqrt(x2 + y2 + z2);
|
||||||
|
segments.push({ from: i, to: j, distance });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.sort((a, b) => a.distance - b.distance);
|
||||||
|
let circuitCount = coordinatesArray.length;
|
||||||
|
let lastSegment;
|
||||||
|
for (const seg of segments) {
|
||||||
|
const rootFrom = find(seg.from);
|
||||||
|
const rootTo = find(seg.to);
|
||||||
|
|
||||||
|
if (rootFrom !== rootTo) {
|
||||||
|
union(seg.from, seg.to);
|
||||||
|
circuitCount--;
|
||||||
|
lastSegment = seg;
|
||||||
|
|
||||||
|
if (circuitCount === 1) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result2 = coordinatesArray[lastSegment.from].x * coordinatesArray[lastSegment.to].x;
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(result2);
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
function find(x) {
|
||||||
|
if (parent[x] === undefined) parent[x] = x;
|
||||||
|
if (parent[x] !== x) parent[x] = find(parent[x]);
|
||||||
|
return parent[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
function union(a, b) {
|
||||||
|
const rootA = find(a);
|
||||||
|
const rootB = find(b);
|
||||||
|
if (rootA !== rootB) {
|
||||||
|
parent[rootA] = rootB;
|
||||||
|
}
|
||||||
|
}
|
||||||
151
09/code.js
Normal file
151
09/code.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
let coordinatesArray = [];
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
const coordinates = element.split(",");
|
||||||
|
coordinatesArray.push({ x: Number.parseInt(coordinates[0]), y: Number.parseInt(coordinates[1]) });
|
||||||
|
}
|
||||||
|
|
||||||
|
let segments = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < coordinatesArray.length; i++) {
|
||||||
|
for (let j = i + 1; j < coordinatesArray.length; j++) {
|
||||||
|
const dx = Math.abs(coordinatesArray[i].x - coordinatesArray[j].x) + 1;
|
||||||
|
const dy = Math.abs(coordinatesArray[i].y - coordinatesArray[j].y) + 1;
|
||||||
|
const area = dx * dy;
|
||||||
|
segments.push({ dx: dx, dy: dy, area: area });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.sort((a, b) => a.area > b.area ? -1 : 1);
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(segments[0].area);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
// Only collect the RED tile coordinates (endpoints)
|
||||||
|
const xSet = new Set();
|
||||||
|
const ySet = new Set();
|
||||||
|
|
||||||
|
for (const tile of coordinatesArray) {
|
||||||
|
xSet.add(tile.x);
|
||||||
|
ySet.add(tile.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xCoords = [...xSet].sort((a, b) => a - b);
|
||||||
|
const yCoords = [...ySet].sort((a, b) => a - b);
|
||||||
|
|
||||||
|
// Store horizontal and vertical line segments for boundary checking
|
||||||
|
const hLines = []; // {y, x1, x2}
|
||||||
|
const vLines = []; // {x, y1, y2}
|
||||||
|
|
||||||
|
for (let i = 0; i < coordinatesArray.length; i++) {
|
||||||
|
const curr = coordinatesArray[i];
|
||||||
|
const next = coordinatesArray[(i + 1) % coordinatesArray.length];
|
||||||
|
|
||||||
|
if (curr.x === next.x) {
|
||||||
|
vLines.push({ x: curr.x, y1: Math.min(curr.y, next.y), y2: Math.max(curr.y, next.y) });
|
||||||
|
} else {
|
||||||
|
hLines.push({ y: curr.y, x1: Math.min(curr.x, next.x), x2: Math.max(curr.x, next.x) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a point is on the boundary
|
||||||
|
function isOnBoundary(x, y) {
|
||||||
|
for (const line of hLines) {
|
||||||
|
if (y === line.y && x >= line.x1 && x <= line.x2) return true;
|
||||||
|
}
|
||||||
|
for (const line of vLines) {
|
||||||
|
if (x === line.x && y >= line.y1 && y <= line.y2) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a point is inside using ray casting (count vertical line crossings to the left)
|
||||||
|
function isInside(x, y) {
|
||||||
|
let crossings = 0;
|
||||||
|
for (const line of vLines) {
|
||||||
|
if (line.x < x && y >= line.y1 && y < line.y2) {
|
||||||
|
crossings++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crossings % 2 === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a point is valid (on boundary or inside)
|
||||||
|
function isValid(x, y) {
|
||||||
|
return isOnBoundary(x, y) || isInside(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if entire rectangle is valid
|
||||||
|
function isRectValid(x1, y1, x2, y2) {
|
||||||
|
// Check all corner and edge points at compressed coordinates within the rectangle
|
||||||
|
for (const y of yCoords) {
|
||||||
|
if (y < y1 || y > y2) continue;
|
||||||
|
for (const x of xCoords) {
|
||||||
|
if (x < x1 || x > x2) continue;
|
||||||
|
if (!isValid(x, y)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check if any boundary line crosses through the rectangle's interior
|
||||||
|
// creating an "outside" region
|
||||||
|
for (const line of vLines) {
|
||||||
|
if (line.x > x1 && line.x < x2) {
|
||||||
|
// Vertical line inside rectangle - check if it creates a gap
|
||||||
|
if (line.y1 > y1 || line.y2 < y2) {
|
||||||
|
// Line doesn't span full height - might create invalid region
|
||||||
|
if (line.y1 > y1 && !isValid(line.x, y1)) return false;
|
||||||
|
if (line.y2 < y2 && !isValid(line.x, y2)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find largest valid rectangle with red corners
|
||||||
|
let maxArea = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < coordinatesArray.length; i++) {
|
||||||
|
for (let j = i + 1; j < coordinatesArray.length; j++) {
|
||||||
|
const r1 = coordinatesArray[i];
|
||||||
|
const r2 = coordinatesArray[j];
|
||||||
|
|
||||||
|
const x1 = Math.min(r1.x, r2.x);
|
||||||
|
const x2 = Math.max(r1.x, r2.x);
|
||||||
|
const y1 = Math.min(r1.y, r2.y);
|
||||||
|
const y2 = Math.max(r1.y, r2.y);
|
||||||
|
|
||||||
|
if (isRectValid(x1, y1, x2, y2)) {
|
||||||
|
const area = (x2 - x1 + 1) * (y2 - y1 + 1);
|
||||||
|
if (area > maxArea) maxArea = area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(maxArea);
|
||||||
|
|
||||||
|
// functions
|
||||||
208
10/code.js
Normal file
208
10/code.js
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
let totalPresses = 0;
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
const lights = element.match(/\[([.#]+)\]/)[1];
|
||||||
|
const buttonMatches = [...element.matchAll(/\(([0-9,]+)\)/g)];
|
||||||
|
const numLights = lights.length;
|
||||||
|
|
||||||
|
// Target state
|
||||||
|
const target = [...lights].map(c => c === '#' ? 1 : 0);
|
||||||
|
|
||||||
|
// Parse buttons
|
||||||
|
const buttons = buttonMatches.map(match => {
|
||||||
|
const indices = match[1].split(',').map(Number);
|
||||||
|
const effect = new Array(numLights).fill(0);
|
||||||
|
for (const idx of indices) effect[idx] = 1;
|
||||||
|
return effect;
|
||||||
|
});
|
||||||
|
|
||||||
|
const numButtons = buttons.length;
|
||||||
|
let minPresses = Infinity;
|
||||||
|
|
||||||
|
for (let mask = 0; mask < (1 << numButtons); mask++) {
|
||||||
|
const state = new Array(numLights).fill(0);
|
||||||
|
let presses = 0;
|
||||||
|
|
||||||
|
for (let b = 0; b < numButtons; b++) {
|
||||||
|
if (mask & (1 << b)) {
|
||||||
|
presses++;
|
||||||
|
for (let i = 0; i < numLights; i++) state[i] ^= buttons[b][i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.every((v, i) => v === target[i]) && presses < minPresses) {
|
||||||
|
minPresses = presses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPresses += minPresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(totalPresses);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
let totalPresses2 = 0;
|
||||||
|
|
||||||
|
for (const element of usedArray) {
|
||||||
|
const buttonMatches = [...element.matchAll(/\(([0-9,]+)\)/g)];
|
||||||
|
const joltageMatch = element.match(/\{([0-9,]+)\}/);
|
||||||
|
|
||||||
|
const targets = joltageMatch[1].split(',').map(Number);
|
||||||
|
const numCounters = targets.length;
|
||||||
|
|
||||||
|
const buttons = buttonMatches.map(match => {
|
||||||
|
const indices = match[1].split(',').map(Number);
|
||||||
|
const effect = new Array(numCounters).fill(0);
|
||||||
|
for (const idx of indices) {
|
||||||
|
if (idx < numCounters) effect[idx] = 1;
|
||||||
|
}
|
||||||
|
return effect;
|
||||||
|
});
|
||||||
|
|
||||||
|
const numButtons = buttons.length;
|
||||||
|
|
||||||
|
// Build augmented matrix [A | b] where A is transpose of buttons matrix
|
||||||
|
// Rows = counters, Cols = buttons + 1 (for target)
|
||||||
|
const matrix = [];
|
||||||
|
for (let c = 0; c < numCounters; c++) {
|
||||||
|
const row = [];
|
||||||
|
for (let b = 0; b < numButtons; b++) {
|
||||||
|
row.push(buttons[b][c]);
|
||||||
|
}
|
||||||
|
row.push(targets[c]);
|
||||||
|
matrix.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gaussian elimination (over rationals, tracking which columns are pivots)
|
||||||
|
const pivotCols = [];
|
||||||
|
let pivotRow = 0;
|
||||||
|
|
||||||
|
for (let col = 0; col < numButtons && pivotRow < numCounters; col++) {
|
||||||
|
// Find pivot
|
||||||
|
let maxRow = pivotRow;
|
||||||
|
for (let row = pivotRow + 1; row < numCounters; row++) {
|
||||||
|
if (Math.abs(matrix[row][col]) > Math.abs(matrix[maxRow][col])) {
|
||||||
|
maxRow = row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matrix[maxRow][col] === 0) continue;
|
||||||
|
|
||||||
|
// Swap rows
|
||||||
|
[matrix[pivotRow], matrix[maxRow]] = [matrix[maxRow], matrix[pivotRow]];
|
||||||
|
|
||||||
|
// Eliminate
|
||||||
|
for (let row = 0; row < numCounters; row++) {
|
||||||
|
if (row !== pivotRow && matrix[row][col] !== 0) {
|
||||||
|
const factor = matrix[row][col] / matrix[pivotRow][col];
|
||||||
|
for (let c = col; c <= numButtons; c++) {
|
||||||
|
matrix[row][c] -= factor * matrix[pivotRow][c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pivotCols.push(col);
|
||||||
|
pivotRow++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free variables are buttons not in pivotCols
|
||||||
|
const freeVars = [];
|
||||||
|
for (let b = 0; b < numButtons; b++) {
|
||||||
|
if (!pivotCols.includes(b)) freeVars.push(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search over free variables with bounded range
|
||||||
|
const maxFreeVal = Math.max(...targets);
|
||||||
|
|
||||||
|
let bestPresses = Infinity;
|
||||||
|
|
||||||
|
function searchFree(freeIdx, freeVals) {
|
||||||
|
if (freeIdx === freeVars.length) {
|
||||||
|
// Compute pivot variables from free variables
|
||||||
|
const x = new Array(numButtons).fill(0);
|
||||||
|
for (let i = 0; i < freeVars.length; i++) {
|
||||||
|
x[freeVars[i]] = freeVals[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back-substitute to find pivot variables
|
||||||
|
for (let i = pivotCols.length - 1; i >= 0; i--) {
|
||||||
|
const col = pivotCols[i];
|
||||||
|
let val = matrix[i][numButtons]; // RHS
|
||||||
|
for (let c = col + 1; c < numButtons; c++) {
|
||||||
|
val -= matrix[i][c] * x[c];
|
||||||
|
}
|
||||||
|
val /= matrix[i][col];
|
||||||
|
x[col] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all non-negative integers
|
||||||
|
let valid = true;
|
||||||
|
let totalPresses = 0;
|
||||||
|
for (let b = 0; b < numButtons; b++) {
|
||||||
|
if (x[b] < -0.0001 || Math.abs(x[b] - Math.round(x[b])) > 0.0001) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
x[b] = Math.round(x[b]);
|
||||||
|
totalPresses += x[b];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid && totalPresses < bestPresses) {
|
||||||
|
// Verify solution
|
||||||
|
let correct = true;
|
||||||
|
for (let c = 0; c < numCounters; c++) {
|
||||||
|
let sum = 0;
|
||||||
|
for (let b = 0; b < numButtons; b++) {
|
||||||
|
sum += buttons[b][c] * x[b];
|
||||||
|
}
|
||||||
|
if (sum !== targets[c]) {
|
||||||
|
correct = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (correct) bestPresses = totalPresses;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune based on current sum
|
||||||
|
const currentSum = freeVals.reduce((a, b) => a + b, 0);
|
||||||
|
if (currentSum >= bestPresses) return;
|
||||||
|
|
||||||
|
const maxVal = Math.min(maxFreeVal, bestPresses - currentSum);
|
||||||
|
for (let v = 0; v <= maxVal; v++) {
|
||||||
|
searchFree(freeIdx + 1, [...freeVals, v]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchFree(0, []);
|
||||||
|
totalPresses2 += bestPresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(totalPresses2);
|
||||||
|
|
||||||
|
// functions
|
||||||
89
11/code.js
Normal file
89
11/code.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = true;
|
||||||
|
|
||||||
|
const sampleArray1 = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const sampleArray2 = readFileSync('sample2.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
const usedArray1 = sampleMode ? sampleArray1 : inputArray;
|
||||||
|
const usedArray2 = sampleMode ? sampleArray2 : inputArray;
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
// Parse the graph
|
||||||
|
const graph = new Map();
|
||||||
|
|
||||||
|
for (const element of usedArray1) {
|
||||||
|
const [node, targets] = element.split(': ');
|
||||||
|
graph.set(node, targets.split(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count paths from 'you' to 'out' using memoization
|
||||||
|
const memo = new Map();
|
||||||
|
|
||||||
|
function countPaths(node) {
|
||||||
|
if (node === 'out') return 1n;
|
||||||
|
if (memo.has(node)) return memo.get(node);
|
||||||
|
if (!graph.has(node)) return 0n;
|
||||||
|
|
||||||
|
let total = 0n;
|
||||||
|
for (const next of graph.get(node)) {
|
||||||
|
total += countPaths(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
memo.set(node, total);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
const part1 = countPaths('you');
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(part1.toString());
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
// Re-parse graph for part 2 (may use different sample file)
|
||||||
|
const graph2 = new Map();
|
||||||
|
for (const element of usedArray2) {
|
||||||
|
const [node, targets] = element.split(': ');
|
||||||
|
graph2.set(node, targets.split(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count paths between any two nodes using memoization
|
||||||
|
const memo2 = new Map();
|
||||||
|
|
||||||
|
function countPathsBetween(from, to) {
|
||||||
|
const key = `${from}->${to}`;
|
||||||
|
if (memo2.has(key)) return memo2.get(key);
|
||||||
|
|
||||||
|
if (from === to) return 1n;
|
||||||
|
if (!graph2.has(from)) return 0n;
|
||||||
|
|
||||||
|
let total = 0n;
|
||||||
|
for (const next of graph2.get(from)) {
|
||||||
|
total += countPathsBetween(next, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
memo2.set(key, total);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paths from svr to out visiting both dac and fft:
|
||||||
|
// Either: svr -> dac -> fft -> out
|
||||||
|
// Or: svr -> fft -> dac -> out
|
||||||
|
|
||||||
|
const path1 = countPathsBetween('svr', 'dac') * countPathsBetween('dac', 'fft') * countPathsBetween('fft', 'out');
|
||||||
|
const path2 = countPathsBetween('svr', 'fft') * countPathsBetween('fft', 'dac') * countPathsBetween('dac', 'out');
|
||||||
|
|
||||||
|
const part2 = path1 + path2;
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log(part2.toString());
|
||||||
|
|
||||||
|
// functions
|
||||||
197
12/code.js
Normal file
197
12/code.js
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
let sampleMode = false;
|
||||||
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part One
|
||||||
|
|
||||||
|
console.time("part1");
|
||||||
|
|
||||||
|
// Parse input
|
||||||
|
const shapes = [];
|
||||||
|
const regions = [];
|
||||||
|
|
||||||
|
let idx = 0;
|
||||||
|
// Parse shapes - continue until we hit a region line (NxN: ...)
|
||||||
|
while (idx < usedArray.length) {
|
||||||
|
const regionMatch = usedArray[idx].match(/^(\d+)x(\d+): (.+)$/);
|
||||||
|
if (regionMatch) break; // Start of regions section
|
||||||
|
|
||||||
|
const shapeMatch = usedArray[idx].match(/^(\d+):$/);
|
||||||
|
if (shapeMatch) {
|
||||||
|
const shapeLines = [];
|
||||||
|
idx++;
|
||||||
|
while (idx < usedArray.length && usedArray[idx] !== '' && !usedArray[idx].match(/^\d+:/) && !usedArray[idx].match(/^\d+x\d+:/)) {
|
||||||
|
shapeLines.push(usedArray[idx]);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
// Convert shape to list of coordinates
|
||||||
|
const coords = [];
|
||||||
|
for (let y = 0; y < shapeLines.length; y++) {
|
||||||
|
for (let x = 0; x < shapeLines[y].length; x++) {
|
||||||
|
if (shapeLines[y][x] === '#') {
|
||||||
|
coords.push([x, y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shapes.push(coords);
|
||||||
|
} else {
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse regions
|
||||||
|
while (idx < usedArray.length) {
|
||||||
|
if (usedArray[idx] === '') { idx++; continue; }
|
||||||
|
const match = usedArray[idx].match(/^(\d+)x(\d+): (.+)$/);
|
||||||
|
if (match) {
|
||||||
|
const width = Number(match[1]);
|
||||||
|
const height = Number(match[2]);
|
||||||
|
const counts = match[3].split(' ').map(Number);
|
||||||
|
regions.push({ width, height, counts });
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate all rotations and flips of a shape
|
||||||
|
function getAllOrientations(coords) {
|
||||||
|
const orientations = new Set();
|
||||||
|
let current = coords;
|
||||||
|
|
||||||
|
for (let flip = 0; flip < 2; flip++) {
|
||||||
|
for (let rot = 0; rot < 4; rot++) {
|
||||||
|
// Normalize: translate to origin (min x = 0, min y = 0)
|
||||||
|
const minX = Math.min(...current.map(c => c[0]));
|
||||||
|
const minY = Math.min(...current.map(c => c[1]));
|
||||||
|
const normalized = current.map(c => [c[0] - minX, c[1] - minY]);
|
||||||
|
normalized.sort((a, b) => a[1] - b[1] || a[0] - b[0]);
|
||||||
|
orientations.add(JSON.stringify(normalized));
|
||||||
|
|
||||||
|
// Rotate 90 degrees: (x, y) -> (-y, x)
|
||||||
|
current = current.map(c => [-c[1], c[0]]);
|
||||||
|
}
|
||||||
|
// Flip horizontally: (x, y) -> (-x, y)
|
||||||
|
current = coords.map(c => [-c[0], c[1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...orientations].map(s => JSON.parse(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precompute all orientations for all shapes
|
||||||
|
const shapeOrientations = shapes.map(getAllOrientations);
|
||||||
|
|
||||||
|
// Check if a region can fit all required presents
|
||||||
|
function canFit(width, height, counts) {
|
||||||
|
const grid = Array(height).fill(null).map(() => Array(width).fill(false));
|
||||||
|
|
||||||
|
// Build list of pieces to place
|
||||||
|
const pieces = [];
|
||||||
|
for (let shapeIdx = 0; shapeIdx < counts.length; shapeIdx++) {
|
||||||
|
for (let j = 0; j < counts[shapeIdx]; j++) {
|
||||||
|
pieces.push(shapeIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total cells needed - must not exceed region size
|
||||||
|
const totalCells = pieces.reduce((sum, shapeIdx) => sum + shapes[shapeIdx].length, 0);
|
||||||
|
if (totalCells > width * height) return false; // Can't exceed region size
|
||||||
|
|
||||||
|
if (pieces.length === 0) return true;
|
||||||
|
|
||||||
|
function canPlace(orientedShape, startX, startY) {
|
||||||
|
for (const [dx, dy] of orientedShape) {
|
||||||
|
const x = startX + dx;
|
||||||
|
const y = startY + dy;
|
||||||
|
if (x < 0 || x >= width || y < 0 || y >= height || grid[y][x]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function place(orientedShape, startX, startY) {
|
||||||
|
for (const [dx, dy] of orientedShape) {
|
||||||
|
grid[startY + dy][startX + dx] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unplace(orientedShape, startX, startY) {
|
||||||
|
for (const [dx, dy] of orientedShape) {
|
||||||
|
grid[startY + dy][startX + dx] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track placement positions for each shape type to avoid duplicates
|
||||||
|
const lastPlacement = new Map(); // shapeIdx -> [x, y] of last placement
|
||||||
|
|
||||||
|
function solve(pieceIdx) {
|
||||||
|
if (pieceIdx >= pieces.length) {
|
||||||
|
return true; // All pieces placed successfully
|
||||||
|
}
|
||||||
|
|
||||||
|
const shapeIdx = pieces[pieceIdx];
|
||||||
|
const orientations = shapeOrientations[shapeIdx];
|
||||||
|
|
||||||
|
// Get minimum position for this shape type (to avoid duplicate placements of same shape)
|
||||||
|
const minPos = lastPlacement.get(shapeIdx);
|
||||||
|
|
||||||
|
for (const oriented of orientations) {
|
||||||
|
// Try all valid placements in the grid
|
||||||
|
for (let startY = 0; startY < height; startY++) {
|
||||||
|
for (let startX = 0; startX < width; startX++) {
|
||||||
|
// Skip if we've already placed this shape type at a later position
|
||||||
|
// (enforces canonical ordering for identical pieces)
|
||||||
|
if (minPos && (startY < minPos[1] || (startY === minPos[1] && startX <= minPos[0]))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canPlace(oriented, startX, startY)) {
|
||||||
|
place(oriented, startX, startY);
|
||||||
|
const prevPos = lastPlacement.get(shapeIdx);
|
||||||
|
lastPlacement.set(shapeIdx, [startX, startY]);
|
||||||
|
if (solve(pieceIdx + 1)) return true;
|
||||||
|
if (prevPos) {
|
||||||
|
lastPlacement.set(shapeIdx, prevPos);
|
||||||
|
} else {
|
||||||
|
lastPlacement.delete(shapeIdx);
|
||||||
|
}
|
||||||
|
unplace(oriented, startX, startY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return solve(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fitCount = 0;
|
||||||
|
for (const region of regions) {
|
||||||
|
if (canFit(region.width, region.height, region.counts)) {
|
||||||
|
fitCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
|
console.log(fitCount);
|
||||||
|
|
||||||
|
|
||||||
|
// Part Two
|
||||||
|
|
||||||
|
console.time("part2");
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
|
console.log("Free star!");
|
||||||
|
|
||||||
|
// functions
|
||||||
@@ -1,25 +1,38 @@
|
|||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
const inputArray = readFileSync('sample.txt').toString().split("\n");
|
let sampleMode = true;
|
||||||
// const inputArray = readFileSync('input.txt').toString().split("\n");
|
let usedArray = [];
|
||||||
|
|
||||||
|
const sampleArray = readFileSync('sample.txt').toString().split("\n");
|
||||||
|
const inputArray = readFileSync('input.txt').toString().split("\n");
|
||||||
|
|
||||||
|
if (sampleMode) {
|
||||||
|
usedArray = sampleArray;
|
||||||
|
} else {
|
||||||
|
usedArray = inputArray;
|
||||||
|
}
|
||||||
|
|
||||||
// Part One
|
// Part One
|
||||||
|
|
||||||
for (const element of inputArray) {
|
console.time("part1");
|
||||||
|
for (const element of usedArray) {
|
||||||
const contents = element.split(" ");
|
const contents = element.split(" ");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part1");
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
|
|
||||||
// Part Two
|
// Part Two
|
||||||
|
|
||||||
for (const element of inputArray) {
|
console.time("part2");
|
||||||
|
for (const element of usedArray) {
|
||||||
const contents = element.split(" ");
|
const contents = element.split(" ");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.timeEnd("part2");
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
|
|||||||
Reference in New Issue
Block a user