2025: day09: part 1 and 2

This commit is contained in:
Antoine Martin 2025-12-12 19:53:08 +01:00
parent 22350a3c68
commit 8c9d675964
6 changed files with 673 additions and 0 deletions

View file

@ -8,6 +8,7 @@ use aoc2025::day05;
use aoc2025::day06;
use aoc2025::day07;
use aoc2025::day08;
use aoc2025::day09;
fn aoc2025_all(c: &mut Criterion) {
c.bench_function("day01", |b| b.iter(|| day01::run().unwrap()));
@ -18,6 +19,7 @@ fn aoc2025_all(c: &mut Criterion) {
c.bench_function("day06", |b| b.iter(|| day06::run().unwrap()));
c.bench_function("day07", |b| b.iter(|| day07::run().unwrap()));
c.bench_function("day08", |b| b.iter(|| day08::run().unwrap()));
c.bench_function("day09", |b| b.iter(|| day09::run().unwrap()));
}
criterion_group! {

496
aoc2025/input/day09.txt Normal file
View file

@ -0,0 +1,496 @@
97997,50117
97997,51346
98453,51346
98453,52549
97943,52549
97943,53788
98190,53788
98190,54948
97502,54948
97502,56154
97395,56154
97395,57433
97739,57433
97739,58596
97292,58596
97292,59837
97281,59837
97281,61017
96953,61017
96953,62054
96089,62054
96089,63409
96430,63409
96430,64384
95454,64384
95454,65751
95714,65751
95714,66804
95033,66804
95033,67976
94684,67976
94684,69011
94003,69011
94003,70198
93676,70198
93676,71390
93338,71390
93338,72348
92529,72348
92529,73408
91931,73408
91931,74257
90977,74257
90977,75371
90482,75371
90482,76290
89678,76290
89678,77659
89532,77659
89532,78647
88813,78647
88813,79559
87993,79559
87993,80678
87436,80678
87436,81480
86488,81480
86488,82509
85805,82509
85805,83363
84926,83363
84926,84016
83849,84016
83849,84837
82952,84837
82952,85751
82141,85751
82141,86148
80878,86148
80878,86955
79982,86955
79982,87912
79198,87912
79198,88620
78213,88620
78213,89781
77544,89781
77544,90134
76309,90134
76309,90879
75341,90879
75341,91649
74380,91649
74380,92207
73291,92207
73291,92361
71995,92361
71995,93245
71081,93245
71081,93967
70071,93967
70071,94524
68977,94524
68977,94943
67824,94943
67824,95146
66594,95146
66594,95498
65427,95498
65427,95782
64240,95782
64240,95918
63017,95918
63017,96932
62028,96932
62028,96751
60727,96751
60727,96832
59504,96832
59504,97447
58383,97447
58383,97339
57133,97339
57133,97862
55976,97862
55976,97522
54714,97522
54714,98341
53561,98341
53561,98159
52323,98159
52323,97865
51095,97865
51095,98319
49881,98319
49881,97497
48679,97497
48679,97646
47466,97646
47466,97937
46230,97937
46230,97396
45062,97396
45062,97403
43844,97403
43844,97029
42677,97029
42677,97668
41335,97668
41335,96999
40221,96999
40221,96413
39109,96413
39109,96133
37934,96133
37934,95950
36729,95950
36729,95917
35469,95917
35469,94917
34523,94917
34523,95102
33169,95102
33169,94246
32199,94246
32199,94503
30772,94503
30772,93436
29912,93436
29912,92610
28968,92610
28968,92121
27865,92121
27865,92036
26532,92036
26532,91010
25722,91010
25722,90374
24696,90374
24696,90327
23279,90327
23279,88941
22753,88941
22753,88221
21788,88221
21788,87580
20762,87580
20762,87110
19588,87110
19588,86306
18676,86306
18676,85743
17547,85743
17547,84602
16945,84602
16945,83641
16193,83641
16193,83283
14813,83283
14813,81918
14496,81918
14496,80918
13804,80918
13804,80292
12662,80292
12662,79115
12195,79115
12195,78440
11068,78440
11068,77149
10787,77149
10787,76199
10033,76199
10033,75155
9420,75155
9420,74315
8460,74315
8460,73226
7910,73226
7910,72194
7255,72194
7255,71018
6882,71018
6882,69749
6736,69749
6736,68882
5698,68882
5698,67727
5301,67727
5301,66585
4877,66585
4877,65281
4930,65281
4930,64395
3722,64395
3722,63224
3347,63224
3347,61827
3853,61827
3853,60651
3581,60651
3581,59463
3371,59463
3371,58378
2585,58378
2585,57101
2870,57101
2870,55946
2377,55946
2377,54727
2352,54727
2352,53560
1681,53560
1681,52313
2044,52313
2044,51094
2172,51094
2172,50110
94865,50110
94865,48656
1652,48656
1652,47426
1613,47426
1613,46276
2643,46276
2643,44990
1915,44990
1915,43812
2348,43812
2348,42565
2253,42565
2253,41451
2971,41451
2971,40208
2941,40208
2941,39037
3278,39037
3278,37906
3762,37906
3762,36793
4270,36793
4270,35611
4530,35611
4530,34328
4516,34328
4516,33171
4901,33171
4901,32169
5679,32169
5679,31086
6224,31086
6224,29900
6537,29900
6537,28819
7087,28819
7087,27837
7824,27837
7824,26571
8032,26571
8032,25560
8716,25560
8716,24305
9002,24305
9002,23228
9595,23228
9595,22755
11060,22755
11060,21499
11387,21499
11387,20615
12230,20615
12230,19629
12940,19629
12940,18363
13330,18363
13330,17822
14559,17822
14559,16593
15028,16593
15028,16138
16304,16138
16304,15078
16967,15078
16967,14567
18145,14567
18145,13691
18985,13691
18985,12725
19758,12725
19758,11618
20440,11618
20440,11285
21717,11285
21717,10537
22677,10537
22677,9996
23776,9996
23776,9085
24636,9085
24636,8807
25887,8807
25887,7954
26797,7954
26797,7299
27828,7299
27828,6560
28824,6560
28824,6675
30222,6675
30222,6188
31326,6188
31326,5665
32416,5665
32416,5269
33558,5269
33558,4558
34592,4558
34592,4297
35783,4297
35783,4096
36987,4096
36987,3676
38127,3676
38127,3093
39236,3093
39236,2965
40454,2965
40454,2678
41638,2678
41638,2319
42815,2319
42815,2781
44104,2781
44104,2245
45262,2245
45262,2218
46479,2218
46479,1829
47675,1829
47675,1732
48895,1732
48895,1948
50118,1948
50118,2255
51327,2255
51327,1730
52566,1730
52566,1670
53799,1670
53799,2521
54945,2521
54945,1990
56234,1990
56234,2955
57325,2955
57325,2820
58575,2820
58575,3321
59712,3321
59712,3024
61022,3024
61022,3906
62055,3906
62055,3723
63364,3723
63364,4203
64492,4203
64492,4440
65697,4440
65697,4855
66845,4855
66845,5402
67941,5402
67941,5922
69043,5922
69043,6713
70017,6713
70017,7102
71172,7102
71172,7317
72429,7317
72429,7869
73519,7869
73519,8752
74417,8752
74417,9149
75601,9149
75601,10303
76301,10303
76301,10274
77794,10274
77794,11384
78502,11384
78502,12252
79367,12252
79367,12863
80432,12863
80432,14015
81045,14015
81045,14153
82546,14153
82546,15277
83169,15277
83169,16371
83794,16371
83794,17100
84780,17100
84780,18019
85572,18019
85572,18602
86755,18602
86755,19867
87140,19867
87140,20414
88416,20414
88416,21566
88920,21566
88920,22438
89807,22438
89807,23692
90131,23692
90131,24638
90912,24638
90912,25875
91212,25875
91212,26672
92272,26672
92272,28023
92324,28023
92324,29101
92870,29101
92870,30144
93494,30144
93494,31085
94375,31085
94375,32167
94962,32167
94962,33582
94664,33582
94664,34550
95563,34550
95563,35684
96022,35684
96022,36879
96285,36879
96285,38051
96621,38051
96621,39161
97232,39161
97232,40476
96926,40476
96926,41698
96982,41698
96982,42794
97820,42794
97820,44053
97627,44053
97627,45302
97346,45302
97346,46459
98049,46459
98049,47710
97453,47710
97453,48890
98462,48890
98462,50117

View file

@ -0,0 +1,8 @@
7,1
11,1
11,7
9,7
9,5
2,5
2,3
7,3

164
aoc2025/src/day09.rs Normal file
View file

@ -0,0 +1,164 @@
use anyhow::{Context, Result, anyhow};
use std::{fmt::Write, str::FromStr};
const INPUT: &str = include_str!("../input/day09.txt");
pub fn run() -> Result<String> {
let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?;
writeln!(res, "part 2: {}", part2(INPUT)?)?;
Ok(res)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Point {
x: usize,
y: usize,
}
impl FromStr for Point {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
let (x, y) = s
.split_once(',')
.with_context(|| format!("couldn't parse point `{}'", s))?;
let (x, y) = (x.parse()?, y.parse()?);
Ok(Self { x, y })
}
}
fn rectangle_area(p1: &Point, p2: &Point) -> usize {
(p1.x.max(p2.x) - p1.x.min(p2.x) + 1) * (p1.y.max(p2.y) - p1.y.min(p2.y) + 1)
}
fn part1(input: &str) -> Result<usize> {
let points = input
.lines()
.map(|line| line.parse::<Point>())
.collect::<Result<Vec<_>>>()?;
let mut max = None;
for (idx, p1) in points.iter().enumerate() {
for p2 in points.iter().skip(idx + 1) {
let area = rectangle_area(p1, p2);
match max {
Some(m) => max = Some(area.max(m)),
None => max = Some(area),
}
}
}
max.ok_or_else(|| anyhow!("input was empty"))
}
// Computes whether a segment crosses through our rectangle.
//
// Since edges are always parallel to axes, this is easier than in the general case
fn edge_intersects(&(e1, e2): &(Point, Point), (r1, r2): (&Point, &Point)) -> bool {
let (rect_xmin, rect_xmax) = (r1.x.min(r2.x), r1.x.max(r2.x));
let (rect_ymin, rect_ymax) = (r1.y.min(r2.y), r1.y.max(r2.y));
#[allow(clippy::nonminimal_bool)]
if e1.x == e2.x {
// segment is vertical
let edge_ymin = e1.y.min(e2.y);
let edge_ymax = e1.y.max(e2.y);
(rect_xmin < e1.x && e1.x < rect_xmax ) // we're in the right X range to cross
&& ((edge_ymin <= rect_ymin && edge_ymax >= rect_ymax) // segment crosses through both sides
|| (edge_ymin <= rect_ymin && rect_ymin < edge_ymax) // segment crosses through bottom edge
|| (edge_ymin < rect_ymax && edge_ymax >= rect_ymax) // segment crosses through top edge
)
} else {
// segment is horizontal
let edge_xmin = e1.x.min(e2.x);
let edge_xmax = e1.x.max(e2.x);
(rect_ymin < e1.y && e1.y < rect_ymax ) // we're in the right X range to cross
&& ((edge_xmin <= rect_xmin && edge_xmax >= rect_xmax) // segment crosses through both sides
|| (edge_xmin <= rect_xmin && rect_xmin < edge_xmax) // segment crosses through bottom edge
|| (edge_xmin < rect_xmax && edge_xmax >= rect_xmax) // segment crosses through top edge
)
}
}
fn part2(input: &str) -> Result<usize> {
let points = input
.lines()
.map(|line| line.parse::<Point>())
.collect::<Result<Vec<_>>>()?;
let mut edges = points.windows(2).map(|w| (w[0], w[1])).collect::<Vec<_>>();
edges.push((
*points.last().context("input was empty")?,
*points.first().context("input was empty")?,
));
let mut max = None;
for (idx, p1) in points.iter().enumerate() {
for p2 in points.iter().skip(idx + 1) {
if edges.iter().any(|e| edge_intersects(e, (p1, p2))) {
// If an edge crosses through our rectangle then one side of the edge is obviously
// outside our polygon.
//
// Generally, this isn't sufficient to guarantee that a rectangle is "valid".
// Consider the following polygon:
//
// ..........
// .#X1..#X#.
// .XXX..XXX.
// .XX#XX2XX.
// .#XXXXXX#.
// ..........
//
// The rectangle between 1 and 2 isn't crossed by any edge, yet it is outside the
// polygon. However looking at the shape of my input (which more or less looks like
// a rasterized circle), it's obvious that this kind of edge case will never happen
// for rectangles large enough to be of maximal area.
continue;
}
let area = rectangle_area(p1, p2);
match max {
Some(m) => max = Some(area.max(m)),
None => max = Some(area),
}
}
}
max.ok_or_else(|| anyhow!("input was empty"))
}
#[cfg(test)]
mod tests {
use super::*;
const PROVIDED: &str = include_str!("../input/day09_provided.txt");
#[test]
fn part1_provided() {
assert_eq!(part1(PROVIDED).unwrap(), 50);
}
#[test]
fn part1_real() {
assert_eq!(part1(INPUT).unwrap(), 4758121828);
}
#[test]
fn part2_provided() {
assert!(edge_intersects(
&(Point { x: 7, y: 3 }, Point { x: 7, y: 1 }),
(&Point { x: 2, y: 5 }, &Point { x: 11, y: 1 })
));
assert!(edge_intersects(
&(Point { x: 9, y: 5 }, Point { x: 2, y: 5 }),
(&Point { x: 9, y: 7 }, &Point { x: 2, y: 3 })
));
assert_eq!(part2(PROVIDED).unwrap(), 24);
}
#[test]
fn part2_real() {
assert_eq!(part2(INPUT).unwrap(), 1577956170);
}
}

View file

@ -6,3 +6,4 @@ pub mod day05;
pub mod day06;
pub mod day07;
pub mod day08;
pub mod day09;

View file

@ -10,6 +10,7 @@ use aoc2025::day05;
use aoc2025::day06;
use aoc2025::day07;
use aoc2025::day08;
use aoc2025::day09;
fn main() -> Result<()> {
let days: &[DayFunc] = &[
@ -21,6 +22,7 @@ fn main() -> Result<()> {
day06::run,
day07::run,
day08::run,
day09::run,
];
aoc::run(days)