diff --git a/2022/16/part1.js b/2022/16/part1.js index 8a06d49..859c0be 100644 --- a/2022/16/part1.js +++ b/2022/16/part1.js @@ -2,77 +2,91 @@ import * as R from "remeda"; import u from "updeep"; import Graph from 'graphology'; -import {bidirectional} from 'graphology-shortest-path'; +import { bidirectional } from 'graphology-shortest-path'; -import { combinations } from "combinatorial-generators"; +import { combinations, permutations } from "combinatorial-generators"; import { readFile } from "../05/part1.js"; const parseLine = (line) => { - const {groups} = line.match( /Valve (?..).*flow rate=(?\d+).*valves? (?.*)/ ); - return [ groups.valve, { - exits: groups.exits.split(',').map( x => x.trim() ), - flow: groups.flow - } ] + const { groups } = line.match(/Valve (?..).*flow rate=(?\d+).*valves? (?.*)/); + return [groups.valve, { + exits: groups.exits.split(',').map(x => x.trim()), + flow: parseInt(groups.flow) + }] } const readInput = (...args) => - R.pipe( - readFile(...args), - (lines) => lines.split("\n"), - R.compact, // remove last line - R.map(parseLine), - Object.fromEntries, - ); + R.pipe( + readFile(...args), + (lines) => lines.split("\n"), + R.compact, // remove last line + R.map(parseLine), + Object.fromEntries, + ); export const puzzleInput = readInput(import.meta.url, "input"); export const sample = readInput(import.meta.url, "sample"); -function bestMoveFor(tunnels,minutesLeft,opened,location='AA',totalSteam=0,activeSteam=0) { - console.log(minutesLeft, totalSteam); +function finalSteam(tunnels, graph, minutesLeft, possibilities, location = 'AA', activeSteam = 0) { + //console.log(minutesLeft, totalSteam, itinary); - if(minutesLeft<=0) return totalSteam; + if (minutesLeft <= 0) return 0; - totalSteam += activeSteam; + if (possibilities.length === 0) return minutesLeft * activeSteam; - const possibilities = []; + let scores = []; - if(!opened.includes(location)) { - possibilities.push( - bestMoveFor( - tunnels, - minutesLeft-1, - [...opened, location], - location, - totalSteam, - activeSteam + tunnels[location].flow - ) + for ( const next of possibilities ) { + const path = bidirectional(graph, location, next); + + //console.log(path); + + let time = path.length; + if (time >= minutesLeft) { + scores.push( minutesLeft * activeSteam ); + } + else { + //console.log({totalSteam, time, activeSteam}); + let ts = time * activeSteam; + let as = activeSteam + tunnels[next].flow; + + scores.push( + ts + finalSteam( + tunnels, graph, minutesLeft - time, + possibilities.filter( x => x !== next ), + next, + as + ) ) } - for( const exit of tunnels[location].exits ) { - possibilities.push( - bestMoveFor( - tunnels, - minutesLeft-1, - opened, - exit, - totalSteam, - activeSteam, - ) - ) } - return Math.max( ...possibilities ); + return Math.max( ...scores ); } +export const buildGraph = tunnels => { + const graph = new Graph(); + Object.keys(tunnels).forEach(x => graph.addNode(x)); + + Object.entries(tunnels).forEach(([location, { exits }]) => { + exits.forEach(exit => graph.addEdge(location, exit)); + }); + + + return graph; + +}; export default (tunnels) => { - const graph = new Graph(); - Object.entries(tunnels).forEach( ([location, { exits }]) => { - exits.forEach( exit => graph.addEdge(location, exit) ); - }); + const possibilities = + Object.keys(tunnels).filter( + k => tunnels[k].flow + ); - console.log(graph); + const graph = buildGraph(tunnels); + + return finalSteam(tunnels, graph, 30, possibilities, 'AA', 0, 0) }; diff --git a/2022/16/part2.js b/2022/16/part2.js index 10fc179..12b2097 100644 --- a/2022/16/part2.js +++ b/2022/16/part2.js @@ -1,4 +1,72 @@ +import { bidirectional } from 'graphology-shortest-path'; import * as R from "remeda"; +import { buildGraph } from './part1.js'; -export default () => {}; +function finalSteam(tunnels, graph, minutesLeft, possibilities, locations, activeSteam = 0) { + //console.log(minutesLeft, activeSteam, locations, possibilities); + + if (minutesLeft <= 0) return 0; + + if (possibilities.length === 0) return minutesLeft * activeSteam; + + const nextToGo = locations.find(([time]) => !time); + + for( const l of locations) { + if( l[0] === 0 ) { + activeSteam += l[2]; + l[2] = 0; + } + } + + + let scores = []; + + for ( const next of possibilities ) { + const path = bidirectional(graph, nextToGo[1], next); + + //console.log(path); + + const locs = locations.map( x => x === nextToGo ? [ + path.length, next, tunnels[next].flow + ] : x ); + + let time = Math.min( ...locs.map( ([time]) => time ) ); + + if (time >= minutesLeft) { + scores.push( minutesLeft * activeSteam ); + } + else { + //console.log({totalSteam, time, activeSteam}); + let ts = time * activeSteam; + + scores.push( + ts + finalSteam( + tunnels, graph, minutesLeft - time, + possibilities.filter( x => x !== next ), + locs.map( ([t,dest] ) => [ t -time, dest ]), + activeSteam + ) + ) + } + + } + + return Math.max( ...scores ); +} + +export default (tunnels) => { + + const possibilities = + Object.keys(tunnels).filter( + k => tunnels[k].flow + ); + + const graph = buildGraph(tunnels); + + return finalSteam(tunnels, graph, 26, possibilities, [ + [ 0, 'AA' ], + [ 0, 'AA' ] + ], 0, 0) + +}; diff --git a/2022/16/test.js b/2022/16/test.js index 8521f1c..14f8d9d 100644 --- a/2022/16/test.js +++ b/2022/16/test.js @@ -14,15 +14,20 @@ describe("part 1", () => { }}) } ) - test.only("sample", () => { - expectSolution(part1(sample)).toEqual(1651); + test("sample", () => { + expect(part1(sample)).toEqual(1651); }); - test.todo("solution", () => { - expectSolution(part1(puzzleInput)).toEqual("TODO"); + test.only("solution", () => { + const r= part1(puzzleInput); + expect(r).toBeGreaterThan(707); + expectSolution(part1(puzzleInput)).toEqual(1871); }); }); -describe("part 2", () => { +describe.only("part 2", () => { + test("sample", () => { + expect(part2(sample)).toEqual(1707); + }); test.todo("solution", () => { expectSolution(part2(puzzleInput)).toEqual("TODO"); });