11//! [Day 9: Movie Theater](https://adventofcode.com/2025/day/9)
22
3- // Nota: not a good solution 😡 But it solves the puzzle...
4-
5- use geo:: algorithm:: contains:: Contains ;
6- use geo:: { Point , Polygon , Rect } ;
73use itertools:: Itertools ;
84
5+ struct Point {
6+ x : i32 ,
7+ y : i32 ,
8+ }
99struct Puzzle {
1010 points : Vec < Point > ,
1111}
@@ -19,32 +19,25 @@ impl Puzzle {
1919 . map ( |line| {
2020 let ( x, y) = line
2121 . split ( ',' )
22- . map ( |x| x. parse :: < f64 > ( ) . unwrap ( ) )
22+ . map ( |x| x. parse :: < i32 > ( ) . unwrap ( ) )
2323 . collect_tuple ( )
2424 . unwrap ( ) ;
2525
26- Point :: new ( x, y)
26+ Point { x, y }
2727 } )
2828 . collect ( ) ,
2929 }
3030 }
3131
3232 /// Solve part one.
33- fn part1 ( & self ) -> f64 {
34- let mut max_area = 0. ;
35-
36- for ( i, p1) in self . points . iter ( ) . enumerate ( ) {
37- for ( j, p2) in self . points . iter ( ) . enumerate ( ) {
38- if i > j {
39- let xmin = p1. x ( ) . min ( p2. x ( ) ) ;
40- let xmax = p1. x ( ) . max ( p2. x ( ) ) ;
41- let ymin = p1. y ( ) . min ( p2. y ( ) ) ;
42- let ymax = p1. y ( ) . max ( p2. y ( ) ) ;
43-
44- let area = ( xmax + 1. - xmin) * ( ymax + 1. - ymin) ;
45- if area > max_area {
46- max_area = area;
47- }
33+ fn part1 ( & self ) -> u64 {
34+ let mut max_area = 0 ;
35+
36+ for ( i, a) in self . points . iter ( ) . enumerate ( ) {
37+ for b in self . points . iter ( ) . skip ( i + 1 ) {
38+ let area = ( u64:: from ( a. x . abs_diff ( b. x ) ) + 1 ) * ( u64:: from ( a. y . abs_diff ( b. y ) ) + 1 ) ;
39+ if area > max_area {
40+ max_area = area;
4841 }
4942 }
5043 }
@@ -53,27 +46,85 @@ impl Puzzle {
5346 }
5447
5548 /// Solve part two.
56- fn part2 ( & self ) -> f64 {
57- let poly = Polygon :: new ( self . points . clone ( ) . into ( ) , vec ! [ ] ) ;
58-
59- let mut max_area = 0. ;
60-
61- for ( i, p1) in self . points . iter ( ) . enumerate ( ) {
62- for ( j, p2) in self . points . iter ( ) . enumerate ( ) {
63- if i > j {
64- let xmin = p1. x ( ) . min ( p2. x ( ) ) ;
65- let xmax = p1. x ( ) . max ( p2. x ( ) ) ;
66- let ymin = p1. y ( ) . min ( p2. y ( ) ) ;
67- let ymax = p1. y ( ) . max ( p2. y ( ) ) ;
68-
69- let rect = Rect :: new ( ( xmin, ymin) , ( xmax, ymax) ) ;
70-
71- if poly. contains ( & rect) {
72- let area = ( xmax + 1. - xmin) * ( ymax + 1. - ymin) ;
73- if area > max_area {
74- max_area = area;
75- }
76- }
49+ fn part2 ( & self ) -> u64 {
50+ let mut max_area = 0 ;
51+ let n = self . points . len ( ) ;
52+
53+ // If the first edge (p0-p1) is vertical, the next edge (p1-p2) is horizontal.
54+ // So, we have to use points 1,3,5,... for the horizontal edges and 0,2,4... for the v ones.
55+ // Otherwise, it is the opposite.
56+ let start = usize:: from ( self . points [ 0 ] . x == self . points [ 1 ] . x ) ;
57+
58+ let horizontal_edges = self
59+ . points
60+ . iter ( )
61+ . enumerate ( )
62+ . skip ( start)
63+ . step_by ( 2 )
64+ . map ( |( i, p) | {
65+ let x1 = p. x ;
66+ let x2 = self . points [ ( i + 1 ) % n] . x ;
67+ ( x1. min ( x2) , x1. max ( x2) , p. y )
68+ } )
69+ . collect :: < Vec < _ > > ( ) ;
70+
71+ let vertical_edges = self
72+ . points
73+ . iter ( )
74+ . enumerate ( )
75+ . skip ( 1 - start)
76+ . step_by ( 2 )
77+ . map ( |( i, p) | {
78+ let y1 = p. y ;
79+ let y2 = self . points [ ( i + 1 ) % n] . y ;
80+ ( p. x , y1. min ( y2) , y1. max ( y2) )
81+ } )
82+ . collect :: < Vec < _ > > ( ) ;
83+
84+ let is_ok = |a : & Point , b : & Point | -> bool {
85+ let x1 = a. x . min ( b. x ) ;
86+ let x2 = a. x . max ( b. x ) ;
87+ let y1 = a. y . min ( b. y ) ;
88+ let y2 = a. y . max ( b. y ) ;
89+
90+ //
91+ // y1 → Axxxxx If there is an horizontal edge between
92+ // x x the top and bottom of the rectangle,
93+ // x x it should be entirely at the left or at the right
94+ // ey → hhhh x but not overlap the horizontal side of the rectangle.
95+ // x x If this is the case, either the top or bottom of
96+ // y2 → xxxxxB the edge is outside the polygon.
97+ // ↑ ↑
98+ // x1 x2 ❌ The configuration at the left is invalid.
99+ //
100+ // y1 → Axxxxx
101+ // x x hhhhh
102+ // x x
103+ // hhh x x ✅ These both configurations are ok.
104+ // x x
105+ // y2 → xxxxxB
106+ //
107+ for & ( edge_x1, edge_x2, edge_y) in & horizontal_edges {
108+ if y1 < edge_y && edge_y < y2 && x1 < edge_x2 && edge_x1 < x2 {
109+ return false ;
110+ }
111+ }
112+
113+ // Same thing with vertical edges.
114+ for & ( edge_x, edge_y1, edge_y2) in & vertical_edges {
115+ if x1 < edge_x && edge_x < x2 && y1 < edge_y2 && edge_y1 < y2 {
116+ return false ;
117+ }
118+ }
119+
120+ true
121+ } ;
122+
123+ for ( i, a) in self . points . iter ( ) . enumerate ( ) {
124+ for b in self . points . iter ( ) . skip ( i + 1 ) {
125+ let area = ( u64:: from ( a. x . abs_diff ( b. x ) ) + 1 ) * ( u64:: from ( a. y . abs_diff ( b. y ) ) + 1 ) ;
126+ if area > max_area && is_ok ( a, b) {
127+ max_area = area;
77128 }
78129 }
79130 }
@@ -84,7 +135,7 @@ impl Puzzle {
84135
85136/// # Panics
86137#[ must_use]
87- pub fn solve ( data : & str ) -> ( f64 , f64 ) {
138+ pub fn solve ( data : & str ) -> ( u64 , u64 ) {
88139 let puzzle = Puzzle :: new ( data) ;
89140 ( puzzle. part1 ( ) , puzzle. part2 ( ) )
90141}
@@ -103,12 +154,12 @@ mod test {
103154 #[ test]
104155 fn part1 ( ) {
105156 let puzzle = Puzzle :: new ( TEST_INPUT ) ;
106- assert_eq ! ( puzzle. part1( ) , 50. ) ;
157+ assert_eq ! ( puzzle. part1( ) , 50 ) ;
107158 }
108159
109160 #[ test]
110161 fn part2 ( ) {
111162 let puzzle = Puzzle :: new ( TEST_INPUT ) ;
112- assert_eq ! ( puzzle. part2( ) , 24. ) ;
163+ assert_eq ! ( puzzle. part2( ) , 24 ) ;
113164 }
114165}
0 commit comments