1+ #include "day08/day08.h"
2+
3+ #include "lib/debug.h"
4+ #include "lib/deleter.h"
5+ #include "lib/fatal_error.h"
6+ #include "lib/file.h"
7+ #include "lib/string_split.h"
8+ #include "lib/string_view.h"
9+ #include "lib/vector.h"
10+
11+ #include <assert.h>
12+ #include <stdint.h>
13+ #include <stdio.h>
14+ #include <stdlib.h>
15+
16+ struct JunctionBox {
17+ uint32_t x ;
18+ uint32_t y ;
19+ uint32_t z ;
20+ uint32_t circuit_no ; // 0 = no circuit
21+ };
22+
23+ struct JunctionBoxPair {
24+ struct JunctionBox * box_1 ;
25+ struct JunctionBox * box_2 ;
26+ uint64_t distance_sq ;
27+ };
28+
29+ static uint64_t calc_distance_sq (const struct JunctionBox * box_1 , const struct JunctionBox * box_2 ) {
30+ const int64_t dx = (int64_t )box_1 -> x - (int64_t )box_2 -> x ;
31+ const int64_t dy = (int64_t )box_1 -> y - (int64_t )box_2 -> y ;
32+ const int64_t dz = (int64_t )box_1 -> z - (int64_t )box_2 -> z ;
33+
34+ return dx * dx + dy * dy + dz * dz ;
35+ }
36+
37+ static int compare_box_pairs (const void * a , const void * b ) {
38+ const struct JunctionBoxPair * box_a = a ;
39+ const struct JunctionBoxPair * box_b = b ;
40+
41+ if (box_a -> distance_sq < box_b -> distance_sq ) {
42+ return -1 ;
43+ }
44+ if (box_a -> distance_sq > box_b -> distance_sq ) {
45+ return 1 ;
46+ }
47+
48+ return 0 ;
49+ }
50+
51+ static int compare_uint32_t_desc (const void * a , const void * b ) {
52+ const uint32_t uint_a = * (uint32_t * )a ;
53+ const uint32_t uint_b = * (uint32_t * )b ;
54+
55+ if (uint_a < uint_b ) {
56+ return 1 ;
57+ }
58+ if (uint_a > uint_b ) {
59+ return -1 ;
60+ }
61+
62+ return 0 ;
63+ }
64+
65+ uint32_t day08_part1 (FILE * file , bool example ) {
66+ char buffer [256 ];
67+
68+ struct Vector * box_positions = vector_create (sizeof (struct JunctionBox ), DEFAULT_DELETER );
69+
70+ while (read_line (file , buffer , sizeof (buffer ))) {
71+ struct StringSplitIterator iter = string_split_create (buffer , "," , STRING_SPLIT_DEFAULT );
72+ uint32_t coords [3 ];
73+ size_t coords_idx = 0 ;
74+
75+ do {
76+ if (!string_view_try_parse_uint32 (& iter .current_segment , & coords [coords_idx ])) {
77+ FATAL_ERROR ("Failed to parse number" );
78+ }
79+
80+ coords_idx += 1 ;
81+ } while (string_split_move_next (& iter ));
82+
83+ if (coords_idx != 3 ) {
84+ FATAL_ERROR ("Wrong number of coordinates" );
85+ }
86+
87+ struct JunctionBox pos = {.x = coords [0 ], .y = coords [1 ], .z = coords [2 ], .circuit_no = 0 };
88+
89+ vector_append (box_positions , & pos );
90+ }
91+
92+ const size_t max_pair_count = example ? 10 : 1000 ;
93+
94+ struct Vector * box_pairs = vector_create (sizeof (struct JunctionBoxPair ), DEFAULT_DELETER );
95+
96+ for (size_t i = 0 ; i < vector_size (box_positions ); i ++ ) {
97+ struct JunctionBox * box_1 = vector_at (box_positions , i );
98+ for (size_t j = i + 1 ; j < vector_size (box_positions ); j ++ ) {
99+ struct JunctionBox * box_2 = vector_at (box_positions , j );
100+ const uint64_t distance_sq = calc_distance_sq (box_1 , box_2 );
101+
102+ const struct JunctionBoxPair pair = {
103+ .box_1 = box_1 , .box_2 = box_2 , .distance_sq = distance_sq };
104+
105+ // We only need to check the N closest pairs, and if we don't impose some sort of
106+ // limit, as the input is 1000 lines, we will have 1000^2 = 1 million pairs in our
107+ // vector.
108+ if (vector_size (box_pairs ) < max_pair_count ) {
109+ vector_append (box_pairs , & pair );
110+ // TODO: perhaps a tree or some sort would be nice here for not requiring an
111+ // explicit sort step each time
112+ vector_sort (box_pairs , compare_box_pairs );
113+ } else {
114+ const struct JunctionBoxPair * furthest_pair = vector_back (box_pairs );
115+ if (pair .distance_sq < furthest_pair -> distance_sq ) {
116+ vector_pop (box_pairs , nullptr );
117+ vector_append (box_pairs , & pair );
118+ // Sort again: new pair might not belong at the end of the vector.
119+ vector_sort (box_pairs , compare_box_pairs );
120+ }
121+ }
122+ }
123+ }
124+
125+ int32_t next_circuit_no = 1 ;
126+ size_t made_connections_count = 0 ;
127+
128+ // Iterate through the sorted vector
129+ for (size_t i = 0 ; i < vector_size (box_pairs ) && made_connections_count < max_pair_count ; i ++ ) {
130+ const struct JunctionBoxPair * pair = vector_at (box_pairs , i );
131+ if (pair -> box_1 -> circuit_no == 0 && pair -> box_2 -> circuit_no == 0 ) {
132+ // Create a new circuit
133+ DEBUG_PRINT ("Connecting Box(%u,%u,%u) to Box(%u,%u,%u) in new circuit %d" ,
134+ pair -> box_1 -> x , pair -> box_1 -> y , pair -> box_1 -> z , pair -> box_2 -> x ,
135+ pair -> box_2 -> y , pair -> box_2 -> z , next_circuit_no );
136+
137+ pair -> box_1 -> circuit_no = next_circuit_no ;
138+ pair -> box_2 -> circuit_no = next_circuit_no ;
139+ next_circuit_no += 1 ;
140+ made_connections_count += 1 ;
141+ } else if (pair -> box_1 -> circuit_no == 0 ) {
142+ // Join box 1 into box 2's circuit
143+ DEBUG_PRINT ("Connecting Box(%u,%u,%u) to Box(%u,%u,%u) in box 2's circuit %d" ,
144+ pair -> box_1 -> x , pair -> box_1 -> y , pair -> box_1 -> z , pair -> box_2 -> x ,
145+ pair -> box_2 -> y , pair -> box_2 -> z , pair -> box_2 -> circuit_no );
146+
147+ pair -> box_1 -> circuit_no = pair -> box_2 -> circuit_no ;
148+ made_connections_count += 1 ;
149+ } else if (pair -> box_2 -> circuit_no == 0 ) {
150+ // Join box 2 into box 1's circuit
151+ DEBUG_PRINT ("Connecting Box(%u,%u,%u) to Box(%u,%u,%u) in box 1's circuit %d" ,
152+ pair -> box_1 -> x , pair -> box_1 -> y , pair -> box_1 -> z , pair -> box_2 -> x ,
153+ pair -> box_2 -> y , pair -> box_2 -> z , pair -> box_1 -> circuit_no );
154+
155+ pair -> box_2 -> circuit_no = pair -> box_1 -> circuit_no ;
156+ made_connections_count += 1 ;
157+ } else if (pair -> box_2 -> circuit_no != pair -> box_1 -> circuit_no ) { // Merge circuits
158+ DEBUG_PRINT ("Merging Box(%u,%u,%u) circuit %d with Box(%u,%u,%u) circuit %d into new "
159+ "circuit %d" ,
160+ pair -> box_1 -> x , pair -> box_1 -> y , pair -> box_1 -> z , pair -> box_1 -> circuit_no ,
161+ pair -> box_2 -> x , pair -> box_2 -> y , pair -> box_2 -> z , pair -> box_2 -> circuit_no ,
162+ next_circuit_no );
163+
164+ const uint32_t target_circuit_no_1 = pair -> box_1 -> circuit_no ;
165+ const uint32_t target_circuit_no_2 = pair -> box_2 -> circuit_no ;
166+
167+ for (size_t j = 0 ; j < vector_size (box_positions ); j ++ ) {
168+ struct JunctionBox * box = vector_at (box_positions , j );
169+ if (box -> circuit_no == target_circuit_no_1 ||
170+ box -> circuit_no == target_circuit_no_2 ) {
171+ DEBUG_PRINT ("Moving Box(%u,%u,%u) from circuit %d to circuit %d" , box -> x ,
172+ box -> y , box -> z , box -> circuit_no , next_circuit_no );
173+ box -> circuit_no = next_circuit_no ;
174+ }
175+ }
176+ next_circuit_no += 1 ;
177+ made_connections_count += 1 ;
178+ } else {
179+ DEBUG_PRINT ("Cannot connect Box(%u,%u,%u) to Box(%u,%u,%u): both already members of "
180+ "the same circuit %d and %d" ,
181+ pair -> box_1 -> x , pair -> box_1 -> y , pair -> box_1 -> z , pair -> box_2 -> x ,
182+ pair -> box_2 -> y , pair -> box_2 -> z , pair -> box_1 -> circuit_no ,
183+ pair -> box_2 -> circuit_no );
184+ made_connections_count += 1 ; // I don't get this
185+ }
186+ }
187+
188+ uint32_t circuit_sizes [1000 ] = {0 };
189+ for (size_t j = 0 ; j < vector_size (box_positions ); j ++ ) {
190+ const struct JunctionBox * box = vector_at (box_positions , j );
191+ assert (box -> circuit_no < 1000 );
192+ if (box -> circuit_no != 0 ) {
193+ circuit_sizes [box -> circuit_no ] += 1 ;
194+ }
195+ }
196+
197+ for (size_t i = 1 ; i < 10 ; i ++ ) {
198+ DEBUG_PRINT ("Circuit no. %zu has size %d" , i , circuit_sizes [i ]);
199+ }
200+
201+ qsort (circuit_sizes , sizeof (circuit_sizes ) / sizeof (uint32_t ), sizeof (uint32_t ),
202+ compare_uint32_t_desc );
203+
204+ DEBUG_PRINT ("Largest circuits: %u, %u, and %u\n" , circuit_sizes [0 ], circuit_sizes [1 ],
205+ circuit_sizes [2 ]);
206+
207+ const uint32_t result = circuit_sizes [0 ] * circuit_sizes [1 ] * circuit_sizes [2 ];
208+
209+ vector_free (box_positions );
210+ vector_free (box_pairs );
211+
212+ return result ;
213+ }
0 commit comments