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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more