-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #82 from kotlinski/2023/day-17
feature(2023-day-17): solve part 1 and 2
- Loading branch information
Showing
17 changed files
with
526 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
52
src/advent-of-code-solver/2023/day-17/__tests__/solver.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); | ||
}); |
93 changes: 93 additions & 0 deletions
93
src/advent-of-code-solver/2023/day-17/path-finder/__tests__/crucible-steps-provider.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
65 changes: 65 additions & 0 deletions
65
src/advent-of-code-solver/2023/day-17/path-finder/__tests__/dijkstra.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); | ||
}); |
33 changes: 33 additions & 0 deletions
33
src/advent-of-code-solver/2023/day-17/path-finder/__tests__/path-store.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); |
46 changes: 46 additions & 0 deletions
46
src/advent-of-code-solver/2023/day-17/path-finder/crucible-steps-provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
}; | ||
} | ||
} |
Oops, something went wrong.