Skip to content

Commit

Permalink
Merge pull request #82 from kotlinski/2023/day-17
Browse files Browse the repository at this point in the history
feature(2023-day-17): solve part 1 and 2
  • Loading branch information
kotlinski authored Jan 11, 2024
2 parents d6d1485 + 456ea83 commit 4f09f75
Show file tree
Hide file tree
Showing 17 changed files with 526 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Every problem has example data, but the problem should be solved with a personal
| [2020](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2020) | 4 |
| [2021](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2021) | 20 |
| [2022](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2022) | 16 |
| [2023](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2023) | 32 |
| [2023](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2023) | 34 |

## Prerequisite

Expand Down
1 change: 1 addition & 0 deletions src/advent-of-code-solver/2023/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ https://adventofcode.com/2023
| [Day 14](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2023/day-14) | 🌟 | 🌟 |
| [Day 15](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2023/day-15) | 🌟 | 🌟 |
| [Day 16](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2023/day-16) | 🌟 | 🌟 |
| [Day 17](https://github.com/kotlinski/advent-of-code/tree/main/src/advent-of-code-solver/2023/day-17) | 🌟 | 🌟 |
4 changes: 2 additions & 2 deletions src/advent-of-code-solver/2023/day-08/ghost-map.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type Direction = 'L' | 'R';
export type LeftRightDirection = 'L' | 'R';
export type KeyState = { direction_offset: number; key: string };

export class GhostCoordinate {
Expand All @@ -14,7 +14,7 @@ export class GhostCoordinate {
export class GhostMap {
private readonly connected_coordinates = new Map<string, GhostCoordinate>();
constructor(
private readonly instructions: Direction[],
private readonly instructions: LeftRightDirection[],
coordinates: GhostCoordinate[],
) {
for (const coordinate of coordinates) {
Expand Down
4 changes: 2 additions & 2 deletions src/advent-of-code-solver/2023/day-08/solver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Direction, GhostCoordinate, GhostMap, KeyState } from './ghost-map';
import { LeftRightDirection, GhostCoordinate, GhostMap, KeyState } from './ghost-map';
import Solver from '../../../advent-of-code-solver/solver';
import { removeEmptyLinesPredicate } from '../../common/array-operations/filter';
import { findLeastCommonMultiplier } from '../../common/math/least-common-multiplier';
Expand All @@ -11,7 +11,7 @@ export default class HauntedWastelandSolver extends Solver<GhostMap> {
parse(raw_input: string): GhostMap {
const lines = raw_input.split('\n').filter(removeEmptyLinesPredicate);
const first_line = lines.splice(0, 1)[0];
const instructions: Direction[] = first_line.split('') as Direction[];
const instructions: LeftRightDirection[] = first_line.split('') as LeftRightDirection[];
const coordinates: GhostCoordinate[] = lines.map((line) => new GhostCoordinate(line));
return new GhostMap(instructions, coordinates);
}
Expand Down
4 changes: 1 addition & 3 deletions src/advent-of-code-solver/2023/day-16/solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class TheFloorWillBeLavaSolver extends Solver<MirrorTileValue[][]

const traveler = new LightBeamTraveler(contraption, 'right', { x: 0, y: 0 });
while (traveler.makeStep()) {
console.log(`${traveler.toString()}`);
// console.log(`${traveler.toString()}`);
// make steps until fixed state
}
return traveler.getNumberOfCoveredTiles();
Expand All @@ -43,9 +43,7 @@ export default class TheFloorWillBeLavaSolver extends Solver<MirrorTileValue[][]

const covered_tiles: number[] = start_positions.map((start) => {
const traveler = new LightBeamTraveler(contraption, start.direction, start.coordinate);
// console.log(`${traveler.toString()}`);
while (traveler.makeStep()) {
// console.log(`${traveler.toString()}`);
// make steps until fixed state
}
return traveler.getNumberOfCoveredTiles();
Expand Down
3 changes: 3 additions & 0 deletions src/advent-of-code-solver/2023/day-17/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h2>--- Day 17: Clumsy Crucible ---</h2>

https://adventofcode.com/2023/day/17
52 changes: 52 additions & 0 deletions src/advent-of-code-solver/2023/day-17/__tests__/solver.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Solver from '../../../../advent-of-code-solver/solver';
import ClumsyCrucibleSolver from '../solver';

describe('day 17', () => {
let solver: Solver<number[][]>;
const city_map =
'2413432311323\n' +
'3215453535623\n' +
'3255245654254\n' +
'3446585845452\n' +
'4546657867536\n' +
'1438598798454\n' +
'4457876987766\n' +
'3637877979653\n' +
'4654967986887\n' +
'4564679986453\n' +
'1224686865563\n' +
'2546548887735\n' +
'4322674655533\n';
describe('part one', () => {
type TestCase = { input: string; output: number };
const cases: TestCase[] = [
{
input: city_map,
output: 102,
},
];
describe.each(cases)('with input $input', ({ input, output }: TestCase) => {
it(`should equal to ${output}`, () => {
solver = new ClumsyCrucibleSolver(input);
const result = solver.solvePartOne();
expect(result).toEqual(output);
});
});
});
describe('part two', () => {
type TestCase = { input: string; output: number };
const cases: TestCase[] = [
{
input: city_map,
output: 94,
},
];
describe.each(cases)('with input $input', ({ input, output }: TestCase) => {
it(`should equal to ${output}`, () => {
solver = new ClumsyCrucibleSolver(input);
const result = solver.solvePartTwo();
expect(result).toEqual(output);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Grid } from '../../../../common/matrix/grid/grid';
import { all_directions } from '../../../../common/matrix/grid/interface';
import { CrucibleStepsProvider } from '../crucible-steps-provider';

describe('CrucibleStepsProvider', () => {
let crucible_steps_provider: CrucibleStepsProvider;
const city_map: Grid<number> = new Grid<number>([
// 1 2 3 4 5
[1, 9, 1, 1, 1, 5], // 0
[1, 8, 1, 9, 1, 6], // 1
[1, 1, 1, 9, 2, 2], // 2
]);
beforeAll(() => {
crucible_steps_provider = new CrucibleStepsProvider(1, 3, city_map);
});
describe('createChildPaths', () => {
describe('initial path with no steps', () => {
it(`should return new paths down and right`, () => {
const paths = crucible_steps_provider.createChildPaths({ coordinate: { x: 0, y: 0 }, sum: 0 }, all_directions);
expect(paths.length).toEqual(5);
// down 1 step
expect(paths[0]).toEqual({
coordinate: {
x: 0,
y: 1,
},
direction: 'down',
sum: 1,
});
// down 2 steps
expect(paths[1]).toEqual({
coordinate: {
x: 0,
y: 2,
},
direction: 'down',
sum: 2,
});
// right 1 step
expect(paths[2]).toEqual({
coordinate: {
x: 1,
y: 0,
},
direction: 'right',
sum: 9,
});
// right 2 steps
expect(paths[3]).toEqual({
coordinate: {
x: 2,
y: 0,
},
direction: 'right',
sum: 10,
});
// right 3 steps
expect(paths[4]).toEqual({
coordinate: {
x: 3,
y: 0,
},
direction: 'right',
sum: 11,
});
});
});
describe('from a path with one step right', () => {
it(`should return two paths heading "down"`, () => {
const paths = crucible_steps_provider.createChildPaths({ coordinate: { x: 1, y: 0 }, sum: 9 }, ['down']);
expect(paths.length).toEqual(2);
// down 1 step
expect(paths[0]).toEqual({
coordinate: {
x: 1,
y: 1,
},
direction: 'down',
sum: 17,
});
// down 2 steps
expect(paths[1]).toEqual({
coordinate: {
x: 1,
y: 2,
},
direction: 'down',
sum: 18,
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Grid, parseStringToMatrix } from '../../../../common/matrix/grid/grid';
import { CrucibleStepsProvider } from '../crucible-steps-provider';
import { Dijkstra } from '../dijkstra';
import { PathStore, PathSum } from '../path-store';
import { VisitorTracker } from '../visitor-tracker';

describe('Dijkstra', () => {
let dijkstra: Dijkstra;
let city_map: Grid<number>;
describe('with a small city', () => {
beforeAll(() => {
city_map = new Grid<number>([
// 1 2 3 4 5
[1, 9, 1, 1, 1, 5], // 0
[1, 8, 1, 9, 1, 6], // 1
[1, 1, 1, 9, 2, 2], // 2
]);
const path_store = new PathStore(new VisitorTracker());
const steps_provider = new CrucibleStepsProvider(1, 3, city_map);
dijkstra = new Dijkstra(steps_provider, path_store, { x: 0, y: 0 }, { x: 5, y: 2 });
});
describe('step', () => {
it('should pick the most promising stored path', () => {
let result: PathSum | undefined;
while (result === undefined) {
result = dijkstra.step();
}
expect(result.sum).toEqual(13);
});
});
});
describe('with a test city', () => {
beforeAll(() => {
const input = parseStringToMatrix(
'2413432311323\n' +
'3215453535623\n' +
'3255245654254\n' +
'3446585845452\n' +
'4546657867536\n' +
'1438598798454\n' +
'4457876987766\n' +
'3637877979653\n' +
'4654967986887\n' +
'4564679986453\n' +
'1224686865563\n' +
'2546548887735\n' +
'4322674655533\n',
(str) => Number(str),
);
city_map = new Grid<number>(input);
const path_store = new PathStore(new VisitorTracker());
const steps_provider = new CrucibleStepsProvider(1, 3, city_map);
dijkstra = new Dijkstra(steps_provider, path_store, { x: 0, y: 0 }, { x: 12, y: 12 });
});
describe('step', () => {
it('should pick the most promising stored path', () => {
let result: PathSum | undefined;
while (result === undefined) {
result = dijkstra.step();
}
expect(result.sum).toEqual(102);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { PathStore, PathSum } from '../path-store';
import { VisitorTracker } from '../visitor-tracker';

describe('PathStore', () => {
let path_store: PathStore;
beforeAll(() => {
const visitor_tracker = new VisitorTracker();
path_store = new PathStore(visitor_tracker);
});
describe('consumeNextCandidate', () => {
it('should pick the most promising stored path', () => {
const good_path: PathSum = {
coordinate: {
x: 0,
y: 1,
},
direction: 'down',
sum: 1,
};
const bad_path: PathSum = {
coordinate: {
x: 1,
y: 0,
},
direction: 'right',
sum: 9,
};
path_store.storeCandidates([good_path, bad_path]);
const path = path_store.consumeNextCandidate();
expect(path).toEqual(good_path);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { PathSum } from './path-store';
import { Grid } from '../../../common/matrix/grid/grid';
import { Direction } from '../../../common/matrix/grid/interface';

export class CrucibleStepsProvider {
constructor(
private readonly min_steps: number,
private readonly max_steps: number,
private readonly city_map: Grid<number>,
) {}
/**
* the argument will contain the base coordinate + efficiency sum, together with the new direction.
*/
createChildPathsInDirection(base: PathSum): PathSum[] {
const paths: PathSum[] = [];
let path_sum: PathSum | undefined = base;
for (let step_count = 1; step_count <= this.max_steps && path_sum; step_count++) {
path_sum = this.step(path_sum);
if (path_sum && step_count >= this.min_steps) {
paths.push(path_sum);
}
}
return paths;
}

/**
* Will create a new child path per step
*/
createChildPaths(path_sum: Omit<PathSum, 'direction'>, directions: Direction[]): PathSum[] {
const paths: PathSum[] = [];
for (const direction of directions) {
const child_paths = this.createChildPathsInDirection({ ...path_sum, direction });
paths.push(...child_paths);
}
return paths;
}
private step({ coordinate, sum, direction }: PathSum): PathSum | undefined {
const tile = this.city_map.getNextTileInDirection(coordinate, direction);
if (!tile) return undefined;
return {
sum: sum + tile.value,
direction,
coordinate: tile.getCoordinate(),
};
}
}
Loading

0 comments on commit 4f09f75

Please sign in to comment.