Public
Edited
Nov 29, 2023
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
function parse(input) {
return input
.split("\n")
.map((s) => s.match(/(\w+ generator|\w+-compatible microchip)|(nothing)/g))
.map((items) => {
return [
items
.filter((item) => item.endsWith("generator"))
.map((item) => item.split(/[ |-]/)[0])
.map((item) => item.slice(0, 2) + "G"),
items
.filter((item) => item.endsWith("microchip"))
.map((item) => item.split(/[ |-]/)[0])
.map((item) => item.slice(0, 2) + "M")
].flat();
});
}
Insert cell
Insert cell
function atGoal(state) {
return state.slice(0, 3).flat().length === 0;
}
Insert cell
Insert cell
function getCombinations(items) {
const combinations = [];
for (let i = 0; i < items.length; i++) {
combinations.push([items[i]]);
for (let j = i + 1; j < items.length; j++) {
combinations.push([items[i], items[j]]);
}
}
return combinations;
}
Insert cell
Insert cell
function move(state, from, to, items) {
const newState = AOC.cloneArray(state);
for (const item of items) {
const index = newState[from].indexOf(item);
newState[from].splice(index, 1);
newState[to].push(item);
}
return newState;
}
Insert cell
Insert cell
function willFry(floor) {
const gens = floor
.filter((item) => item.endsWith("G"))
.map((g) => g.slice(0, -1));
if (gens.length === 0) {
return false; // Can't fry if no generators on this floor.
}
// Will fry if some microchip doesn't have a matching generator.
return floor
.filter((item) => item.endsWith("M"))
.some((chip) => !gens.includes(chip.slice(0, -1)));
}
Insert cell
Insert cell
Insert cell
function heuristic(state) {
let totalDistance = 0;
const k = 100; // A little larger than approximate shortest steps
for (let i = 0; i < 3; i++) {
totalDistance += state[i].length * (k - i);
}
return totalDistance;
}
Insert cell
Insert cell
Insert cell
function genericState(state) {
// Store a string of Gs and Ms representing number of generators and microchips on each floor
return state.map((floor) => floor.map((item) => item[2]).sort());
}
Insert cell
Insert cell
function minTransitions(state) {
const visited = new Set();
const queue = new PriorityQueue();
queue.put([state, 0, 0], 0);

while (!queue.isEmpty()) {
const [state, floor, nTransitions] = queue.get();
const gState = JSON.stringify([floor, genericState(state)]);

if (visited.has(gState)) {
continue; // We've been here before
}
visited.add(gState);

if (atGoal(state)) {
return nTransitions; // We've reached the top floor with all items!
}

const items = state[floor];
const payloads = getCombinations(items);

for (const payload of payloads) {
for (const direction of [-1, 1]) {
const newFloor = floor + direction;
if (newFloor < 0 || newFloor > 3) {
continue; // No floor to move to
}
const newState = move(state, floor, newFloor, payload);
if (willFry(newState[floor]) || willFry(newState[newFloor])) {
continue; // Invalid move
}
const priority = nTransitions + 1 + heuristic(newState);
queue.put([newState, newFloor, nTransitions + 1], priority);
}
}
}

return -1; // No more possible states and we haven't reached the goal.
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function part2(input) {
const initState = parse(input);
initState[0] = initState[0].concat(["elG", "elM", "diG", "diM"]);
return minTransitions(initState);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more