It's also more distinct. CP has many potential definitions. CSAM only has the one I'm aware of.
If you like idle games, Magic Research 2
Javascript
I generally like trying the brute force approach first (though in the latter days it just wastes time). After exhausting 32GB of RAM I changed approaches.
Admittedly I don't really understand these algorithms as well as I would like, and I also will admit I inadvertently was LLM assisted :(. When I was trying to figure out what algo I should use and the AI summary in Google's search spoiled me. But I did learn alot.
One interesting observation, unrelated to the solution itself, is the solutions runs about 30% faster in Firefox than it does via Node (25.2.1 and 24.11.1) on my machine.
Code
const input = require('fs').readFileSync('input-day9.txt', 'utf-8');
/** @type {Array<[number, number]} */
const points = input.split("\n").map(r => r.split(',').map(v => parseInt(v, 10)));
let largest = 0;
let largestInside = 0;
for (const [startX, startY] of points) {
for (const [nextX, nextY] of points) {
if (startX === nextX && startY === nextY) continue;
const minX = Math.min(startX, nextX);
const maxX = Math.max(startX, nextX);
const minY = Math.min(startY, nextY);
const maxY = Math.max(startY, nextY);
const area = (maxX - minX + 1) * (maxY - minY + 1);
if (area > largest) {
largest = area;
}
if (area <= largestInside) continue;
// center point check, ala https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule
const centerX = (minX + maxX) / 2;
const centerY = (minY + maxY) / 2;
let inside = false;
for (const [i, [aX, aY]] of points.entries()) {
const [bX, bY] = points[i - 1] ?? points[points.length - 1];
if (centerX === aX && centerY === aY) {
inside = true;
break;
}
if ((aY > centerY) !== (bY > centerY)) {
const slope = (centerX - aX) * (bY - aY) - (bX - aX) * (centerY - aY);
if (slope === 0) {
inside = true;
break;
}
if ((slope < 0) !== (bY < aY)) {
inside = !inside;
}
}
}
if (!inside) continue;
// check for edge intersections, ala https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
let intersects = false;
for (const [i, [aX, aY]] of points.entries()) {
const [bX, bY] = points[i - 1] ?? points[points.length - 1];
if (aX === bX) {
if (aX > minX && aX < maxX) {
const wallMinY = Math.min(aY, bY);
const wallMaxY = Math.max(aY, bY);
if (Math.max(minY, wallMinY) < Math.min(maxY, wallMaxY)) {
intersects = true;
break;
}
}
} else if (aY === bY) {
if (aY > minY && aY < maxY) {
const wallMinX = Math.min(aX, bX);
const wallMaxX = Math.max(aX, bX);
if (Math.max(minX, wallMinX) < Math.min(maxX, wallMaxX)) {
intersects = true;
break;
}
}
}
}
if (intersects) continue;
if (area > largestInside) {
largestInside = area;
}
}
}
console.log(`Part 1 Answer: ${largest}`);
console.log(`Part 2 Answer: ${largestInside}`);
I know if I were to setup the V2L feature on my car to power my home it would look like this (the manual way). But there would be a physical interlock that prevents mains power from being on while this is plugged in.
That file is for debugging. Seems like something is setting SSLKEYLOGFILE. You could try unsetting it (unset SSLKEYLOGFILE). Something is probably setting it in your shell, so you probably want to look at your specific shell configs for a more permanent solution.
Cookie consent dialogs can fuck off and die in general. Always using dark patterns to try and trick you into choosing the worst option.
Javascript
Short and sweet. Basically just handle all the range overlaps for part 2 and then do basic subtraction to get the answer.
spoiler
const input = require('fs').readFileSync('input-day5.txt', 'utf-8');
/** @typedef {[number, number]} Range */
/** @type {[Range[], number[]]} */
const [freshRanges, availableIngredients] = input.split("\n\n").map(l => l.split("\n")).map((l, i) => (i === 1) ? l.map(v => parseInt(v, 10)) : l.map(v => v.split('-').map(vv => parseInt(vv, 10))));
let freshOnHand = 0;
for (const ingredient of availableIngredients) {
for (const [start, stop] of freshRanges) {
if (ingredient >= start && ingredient <= stop) {
freshOnHand += 1;
break;
}
}
}
console.log(`Part 1 Answer: ${freshOnHand}`);
const sortedRanges = [...freshRanges].sort((a, b) => a[0] - b[0]);
const mergedRanges = [];
let current = sortedRanges[0];
for (let i = 1; i < sortedRanges.length; i++) {
const [nextStart, nextEnd] = sortedRanges[i];
if (nextStart <= current[1] + 1) {
current = [current[0], Math.max(current[1], nextEnd)];
} else {
mergedRanges.push(current);
current = [nextStart, nextEnd];
}
}
mergedRanges.push(current);
const freshIngredientCount = mergedRanges.reduce((acc, [start, stop]) => acc + ((stop + 1) - start), 0)
console.log(`Part 2 Answer: ${freshIngredientCount}`);
Saved this. My doggo is nearly 15 and whole he's still healthy I am much too afraid of losing him. I suspect I'll need a reminder like this in the future.
Javascript
After smashing out a functional version in 20 minutes, I converted it into a OOP approach for a more appealing solution.
Solution
const input = require('fs').readFileSync('input-day4.txt', 'utf-8');
class PrintingDepartmentMap {
/** @param {string} input */
constructor(input) {
/** @type {number[][]} */
this.map = input.split("\n").map(r => r.split('').map(v => v === '@' ? 1 : 0));
}
/**
* @param {number} x
* @param {number} y
* @returns {number} the value of that tile, or -1 if invalid
*/
getCoord(x, y) {
if (x < 0 || y < 0 || x >= this.map[0].length || y >= this.map.length) {
return -1;
}
return this.map[y][x];
}
/**
* @param {number} x
* @param {number} y
* @returns {number} the number of adjacent tiles that are >= 1
*/
countAdjacent(x, y) {
return [
// top-left
this.getCoord(x - 1, y - 1) >= 1,
// top
this.getCoord(x, y - 1) >= 1,
// top-right
this.getCoord(x + 1, y - 1) >= 1,
// right
this.getCoord(x + 1, y) >= 1,
// bottom-right
this.getCoord(x + 1, y + 1) >= 1,
// bottom
this.getCoord(x, y + 1) >= 1,
// bottom-left
this.getCoord(x - 1, y + 1) >= 1,
// left
this.getCoord(x - 1, y) >= 1
].reduce((acc, v) => !!v ? acc + 1 : acc, 0);
}
/**
* transform in place the map replacing any rolls (1) with (2) if they are accessible
* @returns {PrintingDepartmentMap}
*/
markAccessable() {
for (let y = 0; y < this.map.length; y += 1) {
for (let x = 0; x < this.map[0].length; x += 1) {
if (this.map[y][x] < 1) continue;
if (this.countAdjacent(x, y) < 4) {
this.map[y][x] = 2;
}
}
}
return this;
}
/** @returns {number} the number of removed rolls */
removeAccessable() {
let removed = 0;
for (let y = 0; y < this.map.length; y += 1) {
for (let x = 0; x < this.map[0].length; x += 1) {
if (this.map[y][x] === 2) {
removed += 1;
this.map[y][x] = 0;
}
}
}
return removed;
}
}
const map = new PrintingDepartmentMap(input);
let removed = 0;
while (true) {
const justRemoved = map.markAccessable().removeAccessable();
if (removed === 0) {
console.log(`Part 1 Answer: ${justRemoved}`);
}
if (justRemoved === 0) break;
removed += justRemoved;
}
console.log(`Part 2 Answer: ${removed}`);
Sure, which makes the transition suck even lol.
:(
Time to switch to yarn or something.
Javascript
More bruteforcing! There are probably better ways to do this but I'm happy enough with this lol.
Solution
You can replace the require('fs') on the first line with the input and run it in your browser console as well.
const input = require('fs').readFileSync('input-day2.txt', 'utf-8');
let idsPart1 = [];
let idsPart2 = [];
input.split(',').forEach(range => {
const [start, end] = range.split('-').map(v => parseInt(v, 10));
let cursor = start;
while (cursor <= end) {
const cursorString = cursor.toString(10);
// part 1 check
let halfLength = Math.floor(cursorString.length / 2);
let left = cursorString.slice(0, halfLength);
let right = cursorString.slice(halfLength, cursorString.length);
if (left === right) {
idsPart1.push(cursor);
}
// part 2 check
let sequenceLength = 1;
while (sequenceLength <= halfLength) {
const sequence = cursorString.slice(0, sequenceLength);
let builtString = sequence;
while (builtString.length < cursorString.length) {
builtString = `${builtString}${sequence}`;
}
if (builtString === cursorString) {
idsPart2.push(cursor);
break;
}
sequenceLength += 1;
}
cursor += 1;
}
})
const answer1 = idsPart1.flat().reduce((acc, cur) => acc += cur, 0);
const answer2 = idsPart2.flat().reduce((acc, cur) => acc += cur, 0);
console.log(`Part 1 Answer: ${answer1}`);
console.log(`Part 2 Answer: ${answer2}`);