From 93371e742d7b98972a43eb37cfce081e63cb70c6 Mon Sep 17 00:00:00 2001 From: AlexAegis Date: Sun, 10 Dec 2023 10:41:44 +0100 Subject: [PATCH] chore: cleaned up around 2023 day 10 --- resources/2023/10/example.3.txt | 19 +++--- resources/2023/10/example.4.txt | 10 ---- solutions/typescript/2023/10/package.json | 5 ++ solutions/typescript/2023/10/src/p1.spec.ts | 25 +++++++- solutions/typescript/2023/10/src/p1.ts | 33 +--------- solutions/typescript/2023/10/src/p2.spec.ts | 25 +++++++- solutions/typescript/2023/10/src/p2.ts | 60 ++++++------------- solutions/typescript/2023/10/src/parse.ts | 35 +++++++++++ .../libs/lib/src/model/graph/graph.class.ts | 18 +++--- 9 files changed, 127 insertions(+), 103 deletions(-) delete mode 100644 resources/2023/10/example.4.txt create mode 100644 solutions/typescript/2023/10/src/parse.ts diff --git a/resources/2023/10/example.3.txt b/resources/2023/10/example.3.txt index d6c0f21cc..8f950ae86 100644 --- a/resources/2023/10/example.3.txt +++ b/resources/2023/10/example.3.txt @@ -1,9 +1,10 @@ -.......... -.S------7. -.|F----7|. -.||....||. -.||....||. -.|L-7F-J|. -.|..||..|. -.L--JL--J. -.......... +FF7FSF7F7F7F7F7F---7 +L|LJ||||||||||||F--J +FL-7LJLJ||||||LJL-77 +F--JF--7||LJLJ7F7FJ- +L---JF-JLJ.||-FJLJJ7 +|F|F-JF---7F7-L7L|7| +|FFJF7L7F-JF7|JL---7 +7-L-JL7||F7|L7F-7F7| +L.L7LFJ|||||FJL7||LJ +L7JLJL-JLJLJL--JLJ.L diff --git a/resources/2023/10/example.4.txt b/resources/2023/10/example.4.txt deleted file mode 100644 index 8f950ae86..000000000 --- a/resources/2023/10/example.4.txt +++ /dev/null @@ -1,10 +0,0 @@ -FF7FSF7F7F7F7F7F---7 -L|LJ||||||||||||F--J -FL-7LJLJ||||||LJL-77 -F--JF--7||LJLJ7F7FJ- -L---JF-JLJ.||-FJLJJ7 -|F|F-JF---7F7-L7L|7| -|FFJF7L7F-JF7|JL---7 -7-L-JL7||F7|L7F-7F7| -L.L7LFJ|||||FJL7||LJ -L7JLJL-JLJLJL--JLJ.L diff --git a/solutions/typescript/2023/10/package.json b/solutions/typescript/2023/10/package.json index b853af009..8e5470062 100644 --- a/solutions/typescript/2023/10/package.json +++ b/solutions/typescript/2023/10/package.json @@ -61,6 +61,11 @@ "import": "./dist/p2.js", "default": "./dist/p2.js" }, + "./parse": { + "types": "./src/parse.ts", + "import": "./dist/parse.js", + "default": "./dist/parse.js" + }, "./readme": "./readme.md" }, "dependencies": { diff --git a/solutions/typescript/2023/10/src/p1.spec.ts b/solutions/typescript/2023/10/src/p1.spec.ts index 597ecedde..5b508a479 100644 --- a/solutions/typescript/2023/10/src/p1.spec.ts +++ b/solutions/typescript/2023/10/src/p1.spec.ts @@ -7,14 +7,35 @@ describe('2023 10 p1', () => { describe('the input', () => { it('should solve the input', async () => { const resources = await loadTaskResources(packageJson.aoc); - expect(p1(resources.input)).toEqual(0); + expect(p1(resources.input)).toEqual(6733); }); }); describe('example 1', () => { it('should be solved', async () => { const resources = await loadTaskResources(packageJson.aoc, 'example.1.txt'); - expect(p1(resources.input)).toEqual(0); + expect(p1(resources.input)).toEqual(8); + }); + }); + + describe('example 2', () => { + it('should be solved', async () => { + const resources = await loadTaskResources(packageJson.aoc, 'example.2.txt'); + expect(p1(resources.input)).toEqual(23); + }); + }); + + describe('example 2b', () => { + it('should be solved', async () => { + const resources = await loadTaskResources(packageJson.aoc, 'example.2b.txt'); + expect(p1(resources.input)).toEqual(22); + }); + }); + + describe('example 3', () => { + it('should be solved', async () => { + const resources = await loadTaskResources(packageJson.aoc, 'example.3.txt'); + expect(p1(resources.input)).toEqual(80); }); }); }); diff --git a/solutions/typescript/2023/10/src/p1.ts b/solutions/typescript/2023/10/src/p1.ts index 0d9234e98..e66440c73 100644 --- a/solutions/typescript/2023/10/src/p1.ts +++ b/solutions/typescript/2023/10/src/p1.ts @@ -1,36 +1,9 @@ -import { Direction, descending, task } from '@alexaegis/advent-of-code-lib'; +import { descending, task } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; - -const pipeConnectorMap: Record = { - '|': [Direction.NORTH, Direction.SOUTH], - '-': [Direction.WEST, Direction.EAST], - L: [Direction.NORTH, Direction.EAST], - J: [Direction.NORTH, Direction.WEST], - '7': [Direction.SOUTH, Direction.WEST], - F: [Direction.SOUTH, Direction.EAST], - '.': [], - S: [Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST], -}; +import { parse } from './parse.js'; export const p1 = (input: string): number => { - const graph = input.toGridGraph({ - weighter: (a, b, dir) => { - console.log(a.value, b.value, dir.toString()); - const aConnectors = pipeConnectorMap[a.value.toString()]; - const bConnectors = pipeConnectorMap[b.value.toString()]; - const connection = - (aConnectors?.includes(dir) && bConnectors?.includes(dir.reverse())) ?? false; - return connection ? 1 : 0; - }, - connectionFilter: (a, b, dir) => { - console.log(a.value, b.value, dir.toString()); - const aConnectors = pipeConnectorMap[a.value.toString()]; - const bConnectors = pipeConnectorMap[b.value.toString()]; - - return (aConnectors?.includes(dir) && bConnectors?.includes(dir.reverse())) ?? false; - }, - }); - graph.print(); + const graph = parse(input); const animalStart = graph.findNode((node) => node.value === 'S'); if (!animalStart) { diff --git a/solutions/typescript/2023/10/src/p2.spec.ts b/solutions/typescript/2023/10/src/p2.spec.ts index 994c4a72b..01be9bec7 100644 --- a/solutions/typescript/2023/10/src/p2.spec.ts +++ b/solutions/typescript/2023/10/src/p2.spec.ts @@ -7,14 +7,35 @@ describe('2023 10 p2', () => { describe('the input', () => { it('should solve the input', async () => { const { input } = await loadTaskResources(packageJson.aoc); - expect(p2(input)).toEqual(0); + expect(p2(input)).toEqual(435); }); }); describe('example 1', () => { it('should be solved', async () => { const { input } = await loadTaskResources(packageJson.aoc, 'example.1.txt'); - expect(p2(input)).toEqual(0); + expect(p2(input)).toEqual(1); + }); + }); + + describe('example 2', () => { + it('should be solved', async () => { + const { input } = await loadTaskResources(packageJson.aoc, 'example.2.txt'); + expect(p2(input)).toEqual(4); + }); + }); + + describe('example 2b', () => { + it('should be solved', async () => { + const { input } = await loadTaskResources(packageJson.aoc, 'example.2b.txt'); + expect(p2(input)).toEqual(4); + }); + }); + + describe('example 3', () => { + it('should be solved', async () => { + const { input } = await loadTaskResources(packageJson.aoc, 'example.3.txt'); + expect(p2(input)).toEqual(10); }); }); }); diff --git a/solutions/typescript/2023/10/src/p2.ts b/solutions/typescript/2023/10/src/p2.ts index 60ce6d3da..8647d4d32 100644 --- a/solutions/typescript/2023/10/src/p2.ts +++ b/solutions/typescript/2023/10/src/p2.ts @@ -4,20 +4,9 @@ import { GridGraphNode, task, type ToString, - type Weighter, } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; - -const pipeConnectorMap: Record = { - '|': [Direction.NORTH, Direction.SOUTH], - '-': [Direction.WEST, Direction.EAST], - L: [Direction.NORTH, Direction.EAST], - J: [Direction.NORTH, Direction.WEST], - '7': [Direction.SOUTH, Direction.WEST], - F: [Direction.SOUTH, Direction.EAST], - '.': [], - S: [Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST], -}; +import { parse, weighter } from './parse.js'; export const findPartition = < T extends ToString = string, @@ -132,31 +121,16 @@ export const partitionIntoTwoFromLoop = < }; }; -const weighter: Weighter = (a, b, dir) => { - if (a.value === '.' && b.value === '.') { - return 1; - } - - const aConnectors = pipeConnectorMap[a.value.toString()]; - const bConnectors = pipeConnectorMap[b.value.toString()]; - - const connection = - (aConnectors?.includes(dir) && bConnectors?.includes(dir.reverse())) ?? false; - return connection ? 1 : Number.POSITIVE_INFINITY; -}; - export const p2 = (input: string): number => { - const og = input.toGridGraph({ - weighter, - }); - + const originalGraph = parse(input); const liminalNodes = new Set(); - const gg = new GridGraph(); - for (const ogNode of og.nodeValues) { + // Expanding graph to address liminal space + const graph = new GridGraph(); + for (const ogNode of originalGraph.nodeValues) { for (const { from, to, weight, direction } of Direction.allDirections.map((direction) => ({ ...ogNode.neighbours.get(direction), from: ogNode, - to: og.getNode(ogNode.coordinate.add(direction)), + to: originalGraph.getNode(ogNode.coordinate.add(direction)), direction, }))) { if (to === undefined) { @@ -175,37 +149,39 @@ export const p2 = (input: string): number => { const middle = fromDoubled.middle(toDoubled); - const newFrom = gg.nodes.getOrAdd( + const newFrom = graph.nodes.getOrAdd( fromDoubled.toString(), (_n) => new GridGraphNode(fromDoubled, from.value), ); - const newMiddle = gg.nodes.getOrAdd( + const newMiddle = graph.nodes.getOrAdd( middle.toString(), (_n) => new GridGraphNode(middle, newNodeSymbol), ); liminalNodes.add(newMiddle); - const newTo = gg.nodes.getOrAdd( + const newTo = graph.nodes.getOrAdd( toDoubled.toString(), (_n) => new GridGraphNode(toDoubled, to.value), ); - newFrom.attachNeightbours(gg, Direction.cardinalDirections, weighter); - newMiddle.attachNeightbours(gg, Direction.cardinalDirections, weighter); - newTo.attachNeightbours(gg, Direction.cardinalDirections, weighter); + newFrom.attachNeightbours(graph, Direction.cardinalDirections, weighter); + newMiddle.attachNeightbours(graph, Direction.cardinalDirections, weighter); + newTo.attachNeightbours(graph, Direction.cardinalDirections, weighter); } } - const animalStart = gg.findNode((node) => node.value === 'S'); + const animalStart = graph.findNode((node) => node.value === 'S'); if (!animalStart) { throw new Error('no starting position for the animal!'); } - const pathLoop = gg.flood(animalStart); + const pathLoop = graph.flood(animalStart); - const { inside, outside } = partitionIntoTwoFromLoop(gg, pathLoop); + const { inside, outside } = partitionIntoTwoFromLoop(graph, pathLoop); - gg.print((n) => (inside.has(n) ? 'I' : outside.has(n) ? 'O' : n.toString())); + if (process.env['RUN']) { + graph.print((n) => (inside.has(n) ? 'I' : outside.has(n) ? 'O' : n.toString())); + } return inside.valueArray().filter((node) => !liminalNodes.has(node)).length ?? -1; }; diff --git a/solutions/typescript/2023/10/src/parse.ts b/solutions/typescript/2023/10/src/parse.ts new file mode 100644 index 000000000..5cf024598 --- /dev/null +++ b/solutions/typescript/2023/10/src/parse.ts @@ -0,0 +1,35 @@ +import { + Direction, + GridGraphNode, + type GridGraph, + type Weighter, +} from '@alexaegis/advent-of-code-lib'; + +const pipeConnectorMap: Record = { + '|': [Direction.NORTH, Direction.SOUTH], + '-': [Direction.WEST, Direction.EAST], + L: [Direction.NORTH, Direction.EAST], + J: [Direction.NORTH, Direction.WEST], + '7': [Direction.SOUTH, Direction.WEST], + F: [Direction.SOUTH, Direction.EAST], + '.': [], + S: [Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST], +}; + +export const weighter: Weighter = (a, b, dir) => { + if (a.value === '.' && b.value === '.') { + return 1; + } + + const aConnectors = pipeConnectorMap[a.value.toString()]; + const bConnectors = pipeConnectorMap[b.value.toString()]; + + const connection = + (aConnectors?.includes(dir) && bConnectors?.includes(dir.reverse())) ?? false; + return connection ? 1 : Number.POSITIVE_INFINITY; +}; + +export const parse = (input: string): GridGraph => + input.toGridGraph({ + weighter, + }); diff --git a/solutions/typescript/libs/lib/src/model/graph/graph.class.ts b/solutions/typescript/libs/lib/src/model/graph/graph.class.ts index 25d4550f2..1e106474c 100644 --- a/solutions/typescript/libs/lib/src/model/graph/graph.class.ts +++ b/solutions/typescript/libs/lib/src/model/graph/graph.class.ts @@ -220,7 +220,8 @@ export class Graph< } /** - * + * A gutted out aStar, not trying to find a path, but calculating a distanceMap + * to all reachable node. */ public flood(start: N | undefined, options?: GraphTraversalOptions): Map { if (!start) { @@ -228,11 +229,13 @@ export class Graph< } const openSet = new Set([start]); // q? const cameFrom = new Map(); // prev! - const gScore = new Map(); // dist! Infinity + const gScore = new Map(); // weightMap! Infinity + const dMap = new Map(); // distanceMap Infinity const h = options?.heuristic ?? (() => 1); gScore.set(start, 0); + dMap.set(start, 0); const fScore = new Map(); // Infinity fScore.set(start, h(start, [])); @@ -252,21 +255,20 @@ export class Graph< // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const current = umin.node!; - const currentPath = Graph.generatePath(cameFrom, start, current); - openSet.delete(current); - for (const neighbour of options?.edgeGenerator?.(this.nodes, current, currentPath) ?? - current) { + for (const neighbour of options?.edgeGenerator?.(this.nodes, current, []) ?? current) { const tentativegScore = (gScore.get(current) ?? Number.POSITIVE_INFINITY) + (options?.weighter ? options.weighter(neighbour.from, neighbour.to, neighbour.direction) : neighbour.weight ?? 1); + const tentativeDistance = (dMap.get(current) ?? 0) + 1; if (tentativegScore < (gScore.get(neighbour.to) ?? Number.POSITIVE_INFINITY)) { cameFrom.set(neighbour.to, current); gScore.set(neighbour.to, tentativegScore); - fScore.set(neighbour.to, tentativegScore + h(neighbour.to, currentPath)); + fScore.set(neighbour.to, tentativegScore); + dMap.set(neighbour.to, tentativeDistance); if (!openSet.has(neighbour.to)) { openSet.add(neighbour.to); } @@ -274,7 +276,7 @@ export class Graph< } } - return gScore; + return dMap; } /**