diff --git a/solutions/typescript/2024/01/src/p1.ts b/solutions/typescript/2024/01/src/p1.ts index efe9f2485..74bf31642 100644 --- a/solutions/typescript/2024/01/src/p1.ts +++ b/solutions/typescript/2024/01/src/p1.ts @@ -12,4 +12,5 @@ export const p1 = (input: string): number => { .map(([l, r]) => Math.abs(l - r)) .sum(); }; + await task(p1, packageJson.aoc); // 1722302 ~16.04ms diff --git a/solutions/typescript/2024/06/src/p1.ts b/solutions/typescript/2024/06/src/p1.ts index 79e48a9fe..c2b9835e6 100644 --- a/solutions/typescript/2024/06/src/p1.ts +++ b/solutions/typescript/2024/06/src/p1.ts @@ -27,7 +27,7 @@ export const p1 = (input: string): number => { guardPosition.addMut(guardDirection); guardPath.add(guardPosition.toString()); } - //g.print((n) => (guardPath.has(n.coordinate.toString()) ? 'X' : n.toString())); + g.print((n) => (guardPath.has(n.coordinate.toString()) ? 'X' : n.toString())); return guardPath.size; }; diff --git a/solutions/typescript/2024/06/src/p2.ts b/solutions/typescript/2024/06/src/p2.ts index 259a7da68..95ee70d7f 100644 --- a/solutions/typescript/2024/06/src/p2.ts +++ b/solutions/typescript/2024/06/src/p2.ts @@ -1,64 +1,186 @@ -import { Direction, task } from '@alexaegis/advent-of-code-lib'; +import { BoundingBox, Direction, task, Vec2, type Vec2String } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json' assert { type: 'json' }; -export const p2 = (input: string): number => { - const g = input.toGridGraph({}); - const possibleObstructionCoordinates = g.nodeValues - .filter((node) => node.value === '.') - .map((node) => node.coordinate); - return possibleObstructionCoordinates.filter((possibleObstructionCoordinate) => { - const g = input.toGridGraph({}); - - const guardNode = g.findNode((node) => node.value === '^'); - if (!guardNode) { - throw new Error('Guard not found'); +export interface PatrolResult { + path: Set; + encounters: Set; + isLoop: boolean; + movement: Move[]; +} + +interface Move { + position: Vec2; + direction: Direction; +} + +export const patrol = (obscructions: Set, map: BoundingBox, from: Vec2): PatrolResult => { + let guardPosition = from.clone(); + let guardDirection = Direction.NORTH.clone(); + const path = new Set(); + const encounters = new Set(); + const guardMovement = new Set(); + const movementPath: Move[] = []; + + path.add(guardPosition.toString()); + guardMovement.add(guardPosition.toString() + '+' + guardDirection.toString()); + movementPath.push({ position: guardPosition, direction: guardDirection }); + + let isLoop = false; + + while (true) { + let forwardPosition = guardPosition.add(guardDirection); + if (!forwardPosition.isWithin(map)) { + break; } - const obsructionNode = g.getNode(possibleObstructionCoordinate); - if (!obsructionNode) { - throw new Error('Obstruction not found'); + if (obscructions.has(forwardPosition.toString())) { + encounters.add(forwardPosition.toString()); + guardDirection = guardDirection.rotateLeft(); } - obsructionNode.setValue('#'); - guardNode.setValue('.'); - let guardPosition = guardNode.coordinate; - let guardDirection = Direction.NORTH.clone(); - const guardPath = new Set(); - const guardMovement = new Set(); - - guardPath.add(guardPosition.toString()); - guardMovement.add(guardPosition.toString() + '+' + guardDirection.toString()); - - let isLoop = false; - let i = 0; - while (true) { - i++; - let forwardPosition = guardPosition.add(guardDirection); - let forwardNode = g.getNode(forwardPosition); - if (!forwardNode) { - // Not a loop - break; - } - if (forwardNode.value === '#') { - guardDirection = guardDirection.rotateLeft(); - } - - const pathLengthBeforeStep = guardMovement.size; - guardPosition.addMut(guardDirection); - guardPath.add(guardPosition.toString()); - guardMovement.add(guardPosition.toString() + '+' + guardDirection.toString()); - - const pathLengthAfterStep = guardMovement.size; - if (pathLengthBeforeStep === pathLengthAfterStep) { - isLoop = true; - break; - } + guardPosition.addMut(guardDirection); + path.add(guardPosition.toString()); + const move = guardPosition.toString() + '+' + guardDirection.toString(); + movementPath.push({ position: guardPosition, direction: guardDirection }); + + if (guardMovement.has(move)) { + isLoop = true; + break; + } else { + guardMovement.add(move); } - //g.print((n) => (guardPath.has(n.coordinate.toString()) ? 'X' : n.toString())); - //console.log('--------', daysWithoutUpdate, isLoop, i); - // possibleObstructionNode.setValue('.'); - return isLoop; + } + return { + path, + encounters, + isLoop, + }; +}; + +/** + * Since the guard can only rotate right, and 90 degrees, a loop always + * will look like a rectangle + */ +export const matchEnclosure = ( + position: Vec2, + direction: Direction, + pivotPlacement: Direction, + boundingBox: BoundingBox, + obscructions: Set, +): boolean => { + const possibleObstruction = position.add(direction); + if (obscructions.has(possibleObstruction.toString())) { + return false; // Already obsctructed + } + + const firstCorner = position.add(direction.rotateLeft(1), { + times: (v) => !obscructions.has(v.toString()) && boundingBox.contains(v), + }); + console.log('fc', firstCorner); + + return false; +}; + +export const createParametricEnclosure = + (pivot: Vec2, pivotPlacement: Direction) => (size: Vec2) => {}; + +export const createEnclosure = (pivot: Vec2, pivotPlacement: Direction, size: Vec2): Vec2[] => { + const corners = [pivot]; + + const vertical = pivotPlacement.y > 0 ? new Vec2(1, -size.y) : new Vec2(-1, size.y); + const horizontal = pivotPlacement.x > 0 ? new Vec2(-size.x, -1) : new Vec2(size.x, 1); + // Horizontal + + corners.push(pivot.add(vertical)); + corners.push(pivot.add(horizontal)); + + corners.push(pivot.add(vertical.add(horizontal))); + + return corners; +}; + +const enclosurePivotMap = { + [Direction.NORTH.toString()]: Direction.NORTHWEST, + [Direction.EAST.toString()]: Direction.NORTHEAST, + [Direction.SOUTH.toString()]: Direction.SOUTHEAST, + [Direction.WEST.toString()]: Direction.SOUTHWEST, +} as const; + +export const p2 = (input: string): number => { + const g = input.toGridGraph(); + const boundingBox = g.boundingBox(); + + const obstructions = new Set( + g.nodeValues.filter((node) => node.value === '#').map((node) => node.coordinate.toString()), + ); + + const freeNodes = new Set( + g.nodeValues.filter((node) => node.value === '.').map((node) => node.coordinate.toString()), + ); + + const guardNode = g.findNode((node) => node.value === '^'); + if (!guardNode) { + throw new Error('Guard not found'); + } + + g.print(); + console.log('23332323----------------------'); + + const originalPatrolResult = patrol(obstructions, boundingBox, guardNode.coordinate); + + const enclosure = createEnclosure(new Vec2(3, 3), Direction.SOUTHWEST, new Vec2(3, 3)); + + g.print((n) => + enclosure.map((e) => e.toString()).includes(n.coordinate.toString()) + ? enclosure + .map((e) => e.toString()) + .indexOf(n.coordinate.toString()) + .toString() + : originalPatrolResult.path.has(n.coordinate.toString()) + ? 'X' + : originalPatrolResult.encounters.has(n.coordinate.toString()) + ? 'E' + : n.toString(), + ); + + const result = originalPatrolResult.movement.filter((move) => { + const pivotPlacement = enclosurePivotMap[move.direction.toString()]; + if (!pivotPlacement) { + throw new Error('invalid direction'); + } + return matchEnclosure( + move.position, + move.direction, + pivotPlacement, + boundingBox, + obstructions, + ); }).length; + + // const possibleObstructionCoordinates = [...originalPatrolResult.path].filter( + // (p) => p !== guardNode.coordinate.toString(), + // ); + + //const possibleObstructionCoordinates = [...freeNodes]; + // + //return possibleObstructionCoordinates.filter((possibleObstructionCoordinate) => { + // const newObstructions = new Set(obstructions); + // newObstructions.add(possibleObstructionCoordinate); + // + // const patrolResult = patrol(newObstructions, map, guardNode.coordinate); + // + // //g.print((n) => + // // patrolResult.path.has(n.coordinate.toString()) + // // ? 'X' + // // : n.coordinate.toString() === possibleObstructionCoordinate + // // ? 'O' + // // : n.toString(), + // //); + // // console.log('--------', patrolResult); + // return patrolResult.isLoop; + //}).length; + return result; }; -await task(p2, packageJson.aoc); // 4602 ~0.09ms +await task(p2, packageJson.aoc); // 1563 ~0.09ms +// 1563 too low diff --git a/solutions/typescript/libs/lib/src/model/vector/vec2.class.spec.ts b/solutions/typescript/libs/lib/src/model/vector/vec2.class.spec.ts index 072ac1e76..8f490545c 100644 --- a/solutions/typescript/libs/lib/src/model/vector/vec2.class.spec.ts +++ b/solutions/typescript/libs/lib/src/model/vector/vec2.class.spec.ts @@ -31,4 +31,52 @@ describe('Vec2', () => { }); }); }); + + describe('addMut', () => { + it('should add a vector to another', () => { + const target = new Vec2(1, 1); + const delta = new Vec2(2, 3); + const expectedResult = new Vec2(3, 4); + target.addMut(delta); + expect(target).toEqual(expectedResult); + }); + + it('should add a vector to another many times', () => { + const target = new Vec2(1, 1); + const delta = new Vec2(2, 3); + const expectedResult = new Vec2(9, 13); + target.addMut(delta, { times: 4 }); + expect(target).toEqual(expectedResult); + }); + + it('should add a vector to another multiple times while it should', () => { + const target = new Vec2(1, 1); + const delta = new Vec2(2, 3); + const expectedResult = new Vec2(9, 13); + target.addMut(delta, { + times: (_v, i) => i < 4, + }); + expect(target).toEqual(expectedResult); + }); + + it('should add a vector to another multiple times while it can', () => { + const target = new Vec2(1, 1); + const delta = new Vec2(2, 3); + const expectedResult = new Vec2(5, 7); + target.addMut(delta, { + times: (v, _i) => v.x < 7, + }); + expect(target).toEqual(expectedResult); + }); + + it('should not add a vector to another multiple times if it cant', () => { + const target = new Vec2(1, 1); + const delta = new Vec2(2, 3); + const expectedResult = new Vec2(1, 1); + target.addMut(delta, { + times: (v, _i) => v.x < 1, + }); + expect(target).toEqual(expectedResult); + }); + }); }); diff --git a/solutions/typescript/libs/lib/src/model/vector/vec2.class.ts b/solutions/typescript/libs/lib/src/model/vector/vec2.class.ts index e21652aa9..4b234481e 100644 --- a/solutions/typescript/libs/lib/src/model/vector/vec2.class.ts +++ b/solutions/typescript/libs/lib/src/model/vector/vec2.class.ts @@ -156,7 +156,7 @@ export class Vec2 implements Vec2Like { public add( coord: Vec2Like, options?: { - times?: number; + times?: number | ((v: Vec2, i: number) => boolean); limit?: BoundingBox | ((v: Vec2Like) => boolean); }, ): Vec2 { @@ -190,7 +190,7 @@ export class Vec2 implements Vec2Like { public addMut( v: Vec2Like, options?: { - times?: number; + times?: number | ((v: Vec2, i: number) => boolean); limit?: BoundingBox | ((v: Vec2Like) => boolean); flipX?: boolean; flipY?: boolean; @@ -198,8 +198,22 @@ export class Vec2 implements Vec2Like { ): this { const originalX = this.x; const originalY = this.y; - const diffX = v.x * (options?.times ?? 1); - const diffY = v.y * (options?.times ?? 1); + + let diffX = 0; + let diffY = 0; + + if (typeof options?.times === 'function') { + let times = 0; + while (options.times(this.add(v, { times: times + 1 }), times)) { + times++; + } + diffX = v.x * times; + diffY = v.y * times; + } else { + const times = options?.times ?? 1; + diffX = v.x * times; + diffY = v.y * times; + } this.x += options?.flipX ? -diffX : diffX; this.y += options?.flipY ? -diffY : diffY; diff --git a/solutions/typescript/libs/lib/src/platform/bench-task.function.ts b/solutions/typescript/libs/lib/src/platform/bench-task.function.ts index 30c00e4f0..8c36cb1cd 100644 --- a/solutions/typescript/libs/lib/src/platform/bench-task.function.ts +++ b/solutions/typescript/libs/lib/src/platform/bench-task.function.ts @@ -15,15 +15,12 @@ export const benchTask = async ( resources: TaskResources, logger?: Logger, ): Promise => { - // TODO: Remove the if once PerformanceObserver is implemented in bun - if (process.versions['bun'] === undefined) { - const obs = new PerformanceObserver((list) => { - list.getEntries().forEach((entry) => { - logger?.(`${entry.name}: ${roundToDecimal(entry.duration, 2)} ms`); - }); + const obs = new PerformanceObserver((list) => { + list.getEntries().forEach((entry) => { + logger?.(`${entry.name}: ${roundToDecimal(entry.duration, 2)} ms`); }); - obs.observe({ entryTypes: ['measure'], buffered: true }); - } + }); + obs.observe({ entryTypes: ['measure'], buffered: true }); performance.mark('runstart'); const result = await runner(resources.input, resources.args);