Skip to content

Commit

Permalink
feat: 2023 day 17 first try
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexAegis committed Dec 17, 2023
1 parent 735fd00 commit aa79d4e
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 24 deletions.
3 changes: 3 additions & 0 deletions resources/2023/17/example.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
24889
32129
95111
97 changes: 94 additions & 3 deletions solutions/typescript/2023/17/src/p1.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,99 @@
import { task } from '@alexaegis/advent-of-code-lib';
import { Direction, task } from '@alexaegis/advent-of-code-lib';
import packageJson from '../package.json';

export const p1 = (_input: string): number => {
return 0;
export const p1 = (input: string): number => {
const graph = input.toGridGraph<number>({
valueConverter: (value) => Number.parseInt(value, 10),
weighter: (_from, to, _dir) => to.value,
}); // heat loss

const aabb = graph.boundingBox();
const start = graph.getNode(aabb.topLeft);
const end = graph.getNode(aabb.bottomRight);

if (!start || !end) {
throw new Error('No start or end node!');
}

const path = graph.aStar(start, end, {
heuristic: (node) => node.coordinate.manhattan(end.coordinate),
edgeGenerator: (nodeMap, from, tentativePath) => {
const maxConsecutivePathInTheSameDirection = 4;
const lastThree = tentativePath.slice(-maxConsecutivePathInTheSameDirection);
const whenAllTheSame = lastThree
.slideWindow(2)
.map(([a, b]) => a.directionTo(b))
.reduceIfAllTheSame(); // todo: add a minlength filter

const last = tentativePath.at(-2);
//&& last?.directionTo(from)?.equals(d.reverse())
return Direction.cardinalDirections
.filter((d) =>
whenAllTheSame && lastThree.length === maxConsecutivePathInTheSameDirection
? !d.equals(whenAllTheSame)
: true,
)
.filterMap((direction) => {
const to = nodeMap.get(from.coordinate.add(direction).toString());
return to
? {
direction,
from,
to,
weight: to.value,
}
: undefined;
});
},
/*currentPathWeighter: (from, to, direction, tentativePath) => {
//console.log(
// 'tentativePath',
// tentativePath.map((a) => a.coordinate.toString() + ' ' + a.value),
//);
//console.log('a', from.value, from.coordinate.toString());
//console.log('b', to.value, to.coordinate.toString());
const maxConsecutivePathInTheSameDirection = 4;
const lastThree = tentativePath.slice(-maxConsecutivePathInTheSameDirection);
//console.log(
// 'lastThree',
// lastThree.map((a) => a.coordinate.toString() + ' ' + a.value),
//);
const whenAllTheSame = lastThree
.slideWindow(2)
.map(([a, b]) => a.directionTo(b))
.reduceIfAllTheSame();
if (
lastThree.length === maxConsecutivePathInTheSameDirection &&
whenAllTheSame &&
direction.equals(whenAllTheSame)
) {
return 100;
}
const last = tentativePath.at(-2);
if (last === from) {
console.log('WHATTEFEFEFEFEF');
}
if (last?.directionTo(from)?.equals(direction.reverse())) {
return 100;
}
console.log('VAL');
return to.value;
},*/
});

graph.printPath(path.path);

return path.path
.slice(1)
.map((node) => node.value)
.sum();
};

await task(p1, packageJson.aoc); // 0 ~0ms
// 1014 too high
10 changes: 10 additions & 0 deletions solutions/typescript/libs/lib/src/array/array.polyfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { minOf } from './min-of.function.js';
import { pairsWith } from './pairs-with.function.js';
import { partition } from './partition.function.js';
import { peek } from './peek.function.js';
import { reduceIfAllTheSame } from './reduce-if-all-the-same.function.js';
import { rotateMatrix } from './rotate-matrix.function.js';
import { zip } from './zip.function.js';

Expand Down Expand Up @@ -50,6 +51,7 @@ declare global {
}): (number | undefined)[];
intoIter(): IterableIterator<T>;
repeat(until?: (element: T, iteration: number) => boolean): IterableIterator<T>;

sum(): number;
product(): number;
min(): number;
Expand Down Expand Up @@ -98,6 +100,10 @@ declare global {
* @param isDelimiter by default it checks if a value is falsy or not
*/
groupByDelimiter(isDelimiter?: (t: T) => boolean): T[][];
/**
* If every element is the same, return that.
*/
reduceIfAllTheSame(): T | undefined;
}
}

Expand Down Expand Up @@ -346,3 +352,7 @@ Array.prototype.removeItem = function <T>(item: T): boolean {
}
return index >= 0;
};

Array.prototype.reduceIfAllTheSame = function <T>(): T | undefined {
return reduceIfAllTheSame(this);
};
1 change: 1 addition & 0 deletions solutions/typescript/libs/lib/src/array/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export * from './pairs-with.function.js';
export * from './partition.function.js';
export * from './peek.function.js';
export * from './present-in-all.function.js';
export * from './reduce-if-all-the-same.function.js';
export * from './rotate-matrix.function.js';
export * from './zip.function.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, it } from 'vitest';
import { reduceIfAllTheSame } from './reduce-if-all-the-same.function.js';

describe('reduceIfAllTheSame', () => {
it('should return undefined for empty arrays', () => {
const result = reduceIfAllTheSame<unknown>([]);
expect(result).toBeUndefined();
});

it('should return undefined for arrays where items are different', () => {
const result = reduceIfAllTheSame([1, 1, 2]);
expect(result).toBeUndefined();
});

it('should return 1 for arrays where items are 1', () => {
const result = reduceIfAllTheSame([1, 1, 1]);
expect(result).toEqual(1);
});

it('should return 1 for arrays where there is only one item', () => {
const result = reduceIfAllTheSame([1]);
expect(result).toEqual(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const reduceIfAllTheSame = <T>(array: T[]): T | undefined => {
const first = array.first();
return array.every((i) => i === first) ? first : undefined;
};
4 changes: 2 additions & 2 deletions solutions/typescript/libs/lib/src/model/graph/edge.type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Direction } from '../index.js';
import type { CurrentPathWeighter, Direction } from '../index.js';

/**
* Represents an edge between two vertices
Expand All @@ -9,5 +9,5 @@ export interface Edge<N, Dir = Direction, EdgeData = unknown> {
direction: Dir;
data?: EdgeData;
weight?: number;
weighter?: () => number;
currentPathWeighter?: CurrentPathWeighter<N, Dir>;
}
41 changes: 30 additions & 11 deletions solutions/typescript/libs/lib/src/model/graph/graph.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Direction } from '../direction/direction.class.js';
import type { ToString } from '../to-string.interface.js';

import type { Edge } from './edge.type.js';
import type { Heuristic, Weighter } from './heuristic.type.js';
import type { CurrentPathWeighter, Heuristic, Weighter } from './heuristic.type.js';
import { GraphNode } from './node.class.js';

export interface GraphTraversalOptions<N, Dir = Direction> {
Expand All @@ -13,10 +13,9 @@ export interface GraphTraversalOptions<N, Dir = Direction> {
* complete
*/
// generateNode?: (graph: Graph<N>, path: Map<N, N>) => N | undefined;
edgeGenerator?: (nodeMap: Map<string, N>, from: N, path: N[]) => Generator<Edge<N, Dir>>;
edgeGenerator?: (nodeMap: Map<string, N>, from: N, path: N[]) => Edge<N, Dir>[];
heuristic?: Heuristic<N>;
weighter?: Weighter<N, Dir>;
recalc?: boolean; // evaluate need
currentPathWeighter?: CurrentPathWeighter<N, Dir>;
}

// TODO take out DIR, it doesnt make sense here
Expand Down Expand Up @@ -169,6 +168,8 @@ export class Graph<
}
}

static defaultWeighter: Weighter<unknown, unknown> = (_a, _b, _direction) => 1;

public dijkstra(start: N | undefined, target: N | undefined): N[] {
if (!start || !target) {
return [];
Expand Down Expand Up @@ -304,8 +305,13 @@ export class Graph<
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)
(options?.currentPathWeighter
? options.currentPathWeighter(
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)) {
Expand Down Expand Up @@ -344,8 +350,7 @@ export class Graph<
const gScore = new Map<N, number>(); // dist! Infinity

const h = options?.heuristic ?? (() => 1);
// const weighter = options?.weigther ?? (() => 1);
const recalc = options?.recalc ?? false;

const isFinished = typeof end === 'function' ? end : (n: N, _path: N[]) => n === end;
// const generateNode = options?.generateNode ?? (() => undefined);

Expand All @@ -371,7 +376,7 @@ export class Graph<
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const current = umin.node!;

const currentPath = Graph.generatePath(cameFrom, start, current);
const currentPath = Graph.generatePath<T, Dir, N>(cameFrom, start, current);

if (isFinished(current, currentPath)) {
goal = current;
Expand All @@ -383,7 +388,21 @@ export class Graph<
current) {
const tentativegScore =
(gScore.get(current) ?? Number.POSITIVE_INFINITY) +
(recalc && neighbour.weighter ? neighbour.weighter() : neighbour.weight ?? 1);
(options?.currentPathWeighter
? options.currentPathWeighter(
current,
neighbour.to,
neighbour.direction,
currentPath,
)
: neighbour.currentPathWeighter
? neighbour.currentPathWeighter(
current,
neighbour.to,
neighbour.direction,
currentPath,
)
: neighbour.weight ?? 1);
if (tentativegScore < (gScore.get(neighbour.to) ?? Number.POSITIVE_INFINITY)) {
cameFrom.set(neighbour.to, current);
gScore.set(neighbour.to, tentativegScore);
Expand All @@ -395,6 +414,6 @@ export class Graph<
}
}

return { path: Graph.generatePath(cameFrom, start, goal), gScore };
return { path: Graph.generatePath<T, Dir, N>(cameFrom, start, goal), gScore };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ graph.print();
const start = graph.getNode(new Vec2(1, 1))!;
const goal = graph.getNode(new Vec2(11, 12))!;
const { path } = graph.aStar(start, goal, {
weighter: (a, b) => a.coordinate.dist(b.coordinate),
currentPathWeighter: (a, b) => a.coordinate.dist(b.coordinate),
});
console.log(
graph.toString((node) =>
Expand All @@ -41,12 +41,12 @@ await defaultBench(
add('Dijkstra', () => graph.dijkstra(start, goal)),
add('Astar', () =>
graph.aStar(start, goal, {
weighter: (a, g) => a.coordinate.dist(g.coordinate),
currentPathWeighter: (a, g) => a.coordinate.dist(g.coordinate),
}),
),
add('Astar h2', () =>
graph.aStar(start, goal, {
weighter: (a, g) => 140 - a.coordinate.dist(g.coordinate),
currentPathWeighter: (a, g) => 140 - a.coordinate.dist(g.coordinate),
}),
),
);
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export class GridGraphNode<T extends ToString = string> extends GraphNode<T> {
public calculateWeights(weighter: Weighter<this>) {
this.neighbours.forEach((edge, direction) => {
edge.weight = weighter(this, edge.to, direction);
edge.weighter = () => weighter(this, edge.to, direction);
edge.currentPathWeighter = () => weighter(this, edge.to, direction);
});
}

Expand Down Expand Up @@ -143,8 +143,8 @@ export class GridGraphNode<T extends ToString = string> extends GraphNode<T> {
this.calculateWeights(weighter);
forwardEdge.weight = weighter(this, node, dir);
backEdge.weight = weighter(node, this, reverse);
forwardEdge.weighter = () => weighter(this, node, dir);
backEdge.weighter = () => weighter(node, this, reverse);
forwardEdge.currentPathWeighter = () => weighter(this, node, dir);
backEdge.currentPathWeighter = () => weighter(node, this, reverse);
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions solutions/typescript/libs/lib/src/model/graph/heuristic.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ export type Heuristic<N> = (n: N, tentativePath: N[]) => number;
*/
export type Weighter<N, Dir = Direction> = (a: N, b: N, aToBDirection: Dir) => number;

/**
* Determines a value to an edge, based on the current, tentative path
*/
export type CurrentPathWeighter<N, Dir = Direction> = (
a: N,
b: N,
aToBDirection: Dir,
tentativePath: N[],
) => number;

/**
* Determines if an edge should be even formed
*/
Expand Down
6 changes: 6 additions & 0 deletions solutions/typescript/libs/lib/src/model/graph/node.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export class GraphNode<T extends ToString, Dir extends ToString = Direction> imp
return this;
}

public directionTo(target: this): Dir | undefined {
return [...this.neighbours.entries()].find(
([, neightbour]) => neightbour.to === target,
)?.[0];
}

*[Symbol.iterator](): IterableIterator<Edge<this, Dir>> {
yield* this.neighbours.values();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export class PortalGridNode<T extends ToString = string> extends GridGraphNode<T
if (weighter) {
forwardEdge.weight = weighter(this, node, direction);
backEdge.weight = weighter(node, this, direction);
forwardEdge.weighter = () => weighter(this, node, direction);
backEdge.weighter = () => weighter(node, this, direction);
forwardEdge.currentPathWeighter = () => weighter(this, node, direction);
backEdge.currentPathWeighter = () => weighter(node, this, direction);
}
}
}
Expand Down

0 comments on commit aa79d4e

Please sign in to comment.