advent-of-code

Entries to advent of code, multiple years
git clone git://git.finwo.net/misc/advent-of-code
Log | Files | Refs

commit 5f6a3409d8bb742b4a42f79f7ba1885372ddb96e
parent e8d9887147cddfb6d491eb900fb9b5fa6a4ba5a4
Author: finwo <finwo@pm.me>
Date:   Sun, 25 Dec 2022 00:57:04 +0100

2022/19 solution

Diffstat:
A2022/day-19/index.js | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/day-19/sample | 2++
2 files changed, 234 insertions(+), 0 deletions(-)

diff --git a/2022/day-19/index.js b/2022/day-19/index.js @@ -0,0 +1,232 @@ +const fs = require('fs'); + +String.prototype.startsWith = function(subject) { + return this.substr(0, subject.length) === subject; +}; + +const blueprints = fs.readFileSync('input', 'utf-8') +// const blueprints = fs.readFileSync('sample', 'utf-8') + .split('\n') + .filter(l => l) + .map(l => l.match(/\d+/g).map(d => parseInt(d))) + .map(l => ({ + id : l[0], + ore : { ore: l[1] }, + clay : { ore: l[2] }, + obsidian: { ore: l[3], clay : l[4] }, + geode : { ore: l[5], obsidian: l[6] }, + })) +; + +function canBuild(blueprint, resources, name) { + for(const resourceType in blueprint[name]) { + if (blueprint[name][resourceType] > resources[resourceType]) return false; + } + return true; +} + +function step(resources, robots, amount = 1) { + for(const resourceType in robots) { + resources[resourceType] += robots[resourceType] * amount; + } +} + +function when(blueprint, resources, robots, name) { + let timeCost = 0; + resources = {...resources}; + while(!canBuild(blueprint, resources, name)) { + timeCost++; + step(resources, robots); + } + return timeCost; +} + +function simulate(blueprint, time) { + let maxGeodes = 0; + let counter = 0; + const max = { + clay: blueprint.obsidian.clay, + ore : Math.max( + blueprint.geode.ore, + blueprint.obsidian.ore, + ), + }; + const openset = [{ + time: 0, + resources: { + ore : 0, + clay : 0, + obsidian : 0, + geode : 0, + }, + robots: { + ore : 1, + clay : 0, + obsidian : 0, + geode : 0, + }, + }]; + let minlen = Infinity; + let maxlen = -Infinity; + let mintime = Infinity; + let logint = 1e4; + let logtime = Date.now() + logint; + while(openset.length) { + // Calculate END state + const state = openset.pop(); + const total = { + ore : state.resources.ore + (state.robots.ore * (time - state.time)), + clay : state.resources.clay + (state.robots.clay * (time - state.time)), + obsidian: state.resources.obsidian + (state.robots.obsidian * (time - state.time)), + geode : state.resources.geode + (state.robots.geode * (time - state.time)), + }; + + if (openset.length < minlen ) minlen = openset.length; + if (openset.length > maxlen ) maxlen = openset.length; + if (state.time < mintime) mintime = state.time; + counter++; + if (Date.now() >= logtime) { + logtime += logint; + console.log(JSON.stringify({ + time: mintime, + l: minlen, + h: maxlen, counter, + maxGeodes, + })); + minlen = Infinity; + maxlen = -Infinity; + mintime = Infinity; + counter = 0; + } + + // Keep track of max geodes + if (total.geode > maxGeodes) { + maxGeodes = total.geode; + } + + // Build geode robot + if (canBuild(blueprint, total, 'geode')) { + const skip = 1 + when(blueprint, state.resources, state.robots, 'geode'); + // console.log(`Can build obsidian after after ${skip} minute(s) at ${state.time + skip}`); + if ((state.time + skip) < time) { + // console.log(` Building`); + openset.push({ + time: state.time + skip, + resources: { + ore : state.resources.ore + (state.robots.ore * skip) - blueprint.geode.ore, + clay : state.resources.clay + (state.robots.clay * skip), + obsidian : state.resources.obsidian + (state.robots.obsidian * skip) - blueprint.geode.obsidian, + geode : state.resources.geode + (state.robots.geode * skip), + }, + robots: { + ...state.robots, + geode : state.robots.geode + 1, + }, + }); + } else { + // console.log(` Takes too much time, ditching state: ${state.time + skip} >= ${time}`); + } + } + + // Build obsidian robot + if (canBuild(blueprint, total, 'obsidian')) { + const skip = 1 + when(blueprint, state.resources, state.robots, 'obsidian'); + // console.log(`Can build obsidian after after ${skip} minute(s) at ${state.time + skip}`); + if ((state.time + skip + 1) < time) { + // console.log(` Building`); + openset.push({ + time: state.time + skip, + resources: { + ore : state.resources.ore + (state.robots.ore * skip) - blueprint.obsidian.ore, + clay : state.resources.clay + (state.robots.clay * skip) - blueprint.obsidian.clay, + obsidian : state.resources.obsidian + (state.robots.obsidian * skip), + geode : state.resources.geode + (state.robots.geode * skip), + }, + robots: { + ...state.robots, + obsidian : state.robots.obsidian + 1, + }, + }); + } else { + // console.log(` Takes too much time, ditching state: ${state.time + skip} >= ${time}`); + } + } + + // Build clay robot + if (canBuild(blueprint, total, 'clay') && state.robots.clay < max.clay) { + const skip = 1 + when(blueprint, state.resources, state.robots, 'clay'); + // console.log(`Can build clay after after ${skip} minute(s) at ${state.time + skip}`); + if ((state.time + skip + 2) < time) { + // console.log(` Building`); + openset.push({ + time: state.time + skip, + resources: { + ore : state.resources.ore + (state.robots.ore * skip) - blueprint.clay.ore, + clay : state.resources.clay + (state.robots.clay * skip), + obsidian : state.resources.obsidian + (state.robots.obsidian * skip), + geode : state.resources.geode + (state.robots.geode * skip), + }, + robots: { + ...state.robots, + clay : state.robots.clay + 1, + }, + }); + } else { + // console.log(` Takes too much time, ditching state: ${state.time + skip} >= ${time}`); + } + + // state.time += skip; + // step(state.resources, state.robots, skip); + // state.resources.ore -= blueprint.clay.ore; + + // console.log(`Can build clay after after ${skip} minute(s)`); + } + + // Build ore robot + if (canBuild(blueprint, total, 'ore') && state.robots.ore < max.ore) { + const skip = 1 + when(blueprint, state.resources, state.robots, 'ore'); + // console.log(`Can build ore after after ${skip} minute(s) at ${state.time + skip}`); + if ((state.time + skip + 1) < time) { + // console.log(` Building`); + openset.push({ + time: state.time + skip, + resources: { + ore : state.resources.ore + (state.robots.ore * skip) - blueprint.ore.ore, + clay : state.resources.clay + (state.robots.clay * skip), + obsidian : state.resources.obsidian + (state.robots.obsidian * skip), + geode : state.resources.geode + (state.robots.geode * skip), + }, + robots: { + ...state.robots, + ore : state.robots.ore + 1, + }, + }); + } else { + // console.log(` Takes too much time, ditching state: ${state.time + skip} >= ${time}`); + } + } + + // Sort by most important resources + openset.sort((a, b) => { + return 0 || + (a.resources.geode - b.resources.geode ) || + (a.resources.obsidian - b.resources.obsidian) || + (a.resources.clay - b.resources.clay ) || + (a.resources.ore - b.resources.ore ); + }); + + } + + console.log({ id: blueprint.id, time, maxGeodes }); + return maxGeodes; +} + +let step1 = 0; +let step2 = 1; +for(let i=0; i<blueprints.length; i++) + step1 += simulate(blueprints[i], 24) * blueprints[i].id; + +for(let i=0; i<Math.min(blueprints.length, 3); i++) + step2 *= simulate(blueprints[i], 32); + +console.log({ step1, step2 }); diff --git a/2022/day-19/sample b/2022/day-19/sample @@ -0,0 +1,2 @@ +Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian. +Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.