Skip to content

Commit 8392678

Browse files
committed
feat: 2025 Day 5
1 parent 4619ba5 commit 8392678

8 files changed

Lines changed: 210 additions & 98 deletions

File tree

2022/day/15/part/1/solve.ts

Lines changed: 3 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,7 @@
1-
import { parseReports } from "../../reports.ts";
21
import calculateManhattanDistance from "@lib/calculateManhattanDistance.ts";
3-
4-
class Interval {
5-
static of(value: number) {
6-
return new Interval(value, value);
7-
}
8-
9-
readonly #lower: number;
10-
readonly #upper: number;
11-
12-
constructor(lower: number, upper: number) {
13-
if (lower > upper) throw new RangeError("upper cannot be less than lower");
14-
this.#lower = lower;
15-
this.#upper = upper;
16-
}
17-
18-
get lower() {
19-
return this.#lower;
20-
}
21-
22-
get upper() {
23-
return this.#upper;
24-
}
25-
26-
get length() {
27-
return this.#upper - this.#lower + 1;
28-
}
29-
30-
gapTo(other: Interval) {
31-
if (this.#upper < other.#lower) return other.#lower - this.#upper - 1;
32-
if (other.#upper < this.#lower) return other.#upper - this.#lower + 1;
33-
return 0;
34-
}
35-
}
36-
37-
class IntervalSet {
38-
#intervals: Interval[] = [];
39-
40-
constructor(entries?: Iterable<Interval>) {
41-
if (entries) { for (const interval of entries) this.add(interval); }
42-
}
43-
44-
get size() {
45-
return this.#intervals.reduce((sum, interval) => sum + interval.length, 0);
46-
}
47-
48-
[Symbol.iterator]() {
49-
return this.#intervals[Symbol.iterator]();
50-
}
51-
52-
#compareIntervalsTo(other: Interval) {
53-
const { [-1]: lower = [], [0]: contiguous = [], [1]: upper = [] } = Object
54-
.groupBy(this.#intervals, (interval) => Math.sign(other.gapTo(interval)));
55-
return { lower, contiguous, upper };
56-
}
57-
58-
add(interval: Interval) {
59-
const { lower, contiguous, upper } = this.#compareIntervalsTo(interval);
60-
if (contiguous.length === 0) {
61-
this.#intervals = [...lower, interval, ...upper];
62-
return;
63-
}
64-
const mergedInterval = new Interval(
65-
Math.min(interval.lower, contiguous[0].lower),
66-
Math.max(interval.upper, contiguous[contiguous.length - 1].upper),
67-
);
68-
this.#intervals = [...lower, mergedInterval, ...upper];
69-
}
70-
71-
delete(interval: Interval) {
72-
const { lower, contiguous, upper } = this.#compareIntervalsTo(interval);
73-
if (contiguous.length === 0) return;
74-
const newIntervals: Interval[] = [];
75-
const first = contiguous[0];
76-
if (first.lower < interval.lower) {
77-
newIntervals.push(new Interval(first.lower, interval.lower - 1));
78-
}
79-
const last = contiguous[contiguous.length - 1];
80-
if (last.upper > interval.upper) {
81-
newIntervals.push(new Interval(interval.upper + 1, last.upper));
82-
}
83-
this.#intervals = [...lower, ...newIntervals, ...upper];
84-
}
85-
86-
difference(other: IntervalSet) {
87-
const result = new IntervalSet();
88-
result.#intervals = [...this.#intervals];
89-
for (const interval of other) result.delete(interval);
90-
return result;
91-
}
92-
93-
union(other: IntervalSet) {
94-
const result = new IntervalSet();
95-
result.#intervals = [...this.#intervals];
96-
for (const interval of other) result.add(interval);
97-
return result;
98-
}
99-
}
2+
import Interval from "@lib/Interval.ts";
3+
import IntervalSet from "@lib/IntervalSet.ts";
4+
import { parseReports } from "../../reports.ts";
1005

1016
export default function solve(input: string, { y = 2000000 } = {}) {
1027
let intervalSet = new IntervalSet();

2025/day/5/ingredients.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function parseIngredientsDatabase(text: string) {
2+
const [top, bottom] = text.split("\n\n");
3+
const freshIdRanges = top.split("\n")
4+
.map((text: string) => text.split("-").map(Number) as [number, number]);
5+
const availableIds = bottom.split("\n").map(Number);
6+
return { freshIdRanges, availableIds };
7+
}

2025/day/5/part/1/solve.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import solve from "./solve.ts";
2+
3+
import { assertEquals } from "@std/assert";
4+
5+
Deno.test("example", () => {
6+
const input = `\
7+
3-5
8+
10-14
9+
16-20
10+
12-18
11+
12+
1
13+
5
14+
8
15+
11
16+
17
17+
32`;
18+
19+
assertEquals(solve(input), 3);
20+
});

2025/day/5/part/1/solve.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Interval from "@lib/Interval.ts";
2+
import IntervalSet from "@lib/IntervalSet.ts";
3+
import { parseIngredientsDatabase } from "../../ingredients.ts";
4+
5+
export default function solve(input: string) {
6+
const { freshIdRanges, availableIds } = parseIngredientsDatabase(input);
7+
const intervalSet = new IntervalSet(freshIdRanges.map(Interval.from));
8+
let freshCount = 0;
9+
for (const availableId of availableIds) {
10+
if (intervalSet.has(Interval.of(availableId))) freshCount++;
11+
}
12+
return freshCount;
13+
}

2025/day/5/part/2/solve.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import solve from "./solve.ts";
2+
3+
import { assertEquals } from "@std/assert";
4+
5+
Deno.test("example", () => {
6+
const input = `\
7+
3-5
8+
10-14
9+
16-20
10+
12-18
11+
12+
1
13+
5
14+
8
15+
11
16+
17
17+
32`;
18+
19+
assertEquals(solve(input), 14);
20+
});

2025/day/5/part/2/solve.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Interval from "@lib/Interval.ts";
2+
import IntervalSet from "@lib/IntervalSet.ts";
3+
import { parseIngredientsDatabase } from "../../ingredients.ts";
4+
5+
export default function solve(input: string) {
6+
const { freshIdRanges } = parseIngredientsDatabase(input);
7+
return new IntervalSet(freshIdRanges.map(Interval.from)).size;
8+
}

lib/Interval.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
export default class Interval {
2+
static of(value: number) {
3+
return new Interval(value, value);
4+
}
5+
6+
static from([lower, upper]: [number, number]) {
7+
return new Interval(lower, upper);
8+
}
9+
10+
readonly #lower: number;
11+
readonly #upper: number;
12+
13+
constructor(lower: number, upper: number) {
14+
if (lower > upper) throw new RangeError("upper cannot be less than lower");
15+
this.#lower = lower;
16+
this.#upper = upper;
17+
}
18+
19+
get lower() {
20+
return this.#lower;
21+
}
22+
23+
get upper() {
24+
return this.#upper;
25+
}
26+
27+
get length() {
28+
return this.#upper - this.#lower + 1;
29+
}
30+
31+
includes(value: number) {
32+
return value >= this.#lower && value <= this.#upper;
33+
}
34+
35+
contains(other: Interval) {
36+
return this.#lower <= other.#lower && this.#upper >= other.#upper;
37+
}
38+
39+
intersects(other: Interval) {
40+
return this.#lower <= other.#upper && other.#lower <= this.#upper;
41+
}
42+
43+
gapTo(other: Interval) {
44+
if (this.#upper < other.#lower) return other.#lower - this.#upper - 1;
45+
if (other.#upper < this.#lower) return other.#upper - this.#lower + 1;
46+
return 0;
47+
}
48+
}

lib/IntervalSet.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import Interval from "./Interval.ts";
2+
3+
export default class IntervalSet {
4+
#intervals: Interval[] = [];
5+
6+
constructor(entries?: Iterable<Interval>) {
7+
if (entries) { for (const interval of entries) this.add(interval); }
8+
}
9+
10+
get size() {
11+
return this.#intervals.reduce((sum, interval) => sum + interval.length, 0);
12+
}
13+
14+
[Symbol.iterator]() {
15+
return this.#intervals[Symbol.iterator]();
16+
}
17+
18+
has(interval: Interval) {
19+
return this.#intervals.some((existing) => existing.contains(interval));
20+
}
21+
22+
#compareIntervalsTo(other: Interval) {
23+
const { [-1]: lower = [], [0]: contiguous = [], [1]: upper = [] } = Object
24+
.groupBy(this.#intervals, (interval) => Math.sign(other.gapTo(interval)));
25+
return { lower, contiguous, upper };
26+
}
27+
28+
add(interval: Interval) {
29+
const { lower, contiguous, upper } = this.#compareIntervalsTo(interval);
30+
if (contiguous.length === 0) {
31+
this.#intervals = [...lower, interval, ...upper];
32+
return;
33+
}
34+
const mergedInterval = new Interval(
35+
Math.min(interval.lower, contiguous[0].lower),
36+
Math.max(interval.upper, contiguous[contiguous.length - 1].upper),
37+
);
38+
this.#intervals = [...lower, mergedInterval, ...upper];
39+
}
40+
41+
delete(interval: Interval) {
42+
const { lower, contiguous, upper } = this.#compareIntervalsTo(interval);
43+
if (contiguous.length === 0) return;
44+
const newIntervals: Interval[] = [];
45+
const first = contiguous[0];
46+
if (first.lower < interval.lower) {
47+
newIntervals.push(new Interval(first.lower, interval.lower - 1));
48+
}
49+
const last = contiguous[contiguous.length - 1];
50+
if (last.upper > interval.upper) {
51+
newIntervals.push(new Interval(interval.upper + 1, last.upper));
52+
}
53+
this.#intervals = [...lower, ...newIntervals, ...upper];
54+
}
55+
56+
difference(other: IntervalSet) {
57+
const result = new IntervalSet();
58+
result.#intervals = [...this.#intervals];
59+
for (const interval of other) result.delete(interval);
60+
return result;
61+
}
62+
63+
union(other: IntervalSet) {
64+
const result = new IntervalSet();
65+
result.#intervals = [...this.#intervals];
66+
for (const interval of other) result.add(interval);
67+
return result;
68+
}
69+
70+
intersection(other: IntervalSet) {
71+
const result = new IntervalSet();
72+
for (
73+
let i = 0, j = 0;
74+
i < this.#intervals.length && j < other.#intervals.length;
75+
this.#intervals[i].upper < other.#intervals[j].upper
76+
? i++
77+
: other.#intervals[j].upper < this.#intervals[i].upper
78+
? j++
79+
: (i++, j++)
80+
) {
81+
if (!this.#intervals[i].intersects(other.#intervals[j])) continue;
82+
result.#intervals.push(
83+
new Interval(
84+
Math.max(this.#intervals[i].lower, other.#intervals[j].lower),
85+
Math.min(this.#intervals[i].upper, other.#intervals[j].upper),
86+
),
87+
);
88+
}
89+
return result;
90+
}
91+
}

0 commit comments

Comments
 (0)