11defmodule Day08 do
22 def input ( infile ) do
3- File . read! ( infile )
4- |> String . trim ( )
5- |> String . split ( "\n " )
6- |> Enum . map ( & String . trim / 1 )
7- |> Enum . map ( & String . split ( & 1 , "," ) )
8- |> Enum . map ( fn str ->
9- Enum . map ( str , & String . to_integer / 1 )
3+ infile
4+ |> File . read! ( )
5+ |> String . split ( "\n " , trim: true )
6+ |> Enum . map ( fn line ->
7+ line
8+ |> String . split ( "," , trim: true )
9+ |> Enum . map ( & String . to_integer / 1 )
10+ |> List . to_tuple ( )
1011 end )
11- |> Enum . map ( & List . to_tuple / 1 )
1212 end
1313
1414 # We skip the sqrt here to save CPU cycles, we still compare apples to apples
15- def distance ( a , b ) do
16- { a_x , a_y , a_z } = a
17- { b_x , b_y , b_z } = b
18- x_d = a_x - b_x
19- y_d = a_y - b_y
20- z_d = a_z - b_z
21- Integer . pow ( x_d , 2 ) + Integer . pow ( y_d , 2 ) + Integer . pow ( z_d , 2 )
15+ def distance ( { ax , ay , az } , { bx , by , bz } ) do
16+ dx = ax - bx
17+ dy = ay - by
18+ dz = az - bz
19+ dx * dx + dy * dy + dz * dz
2220 end
2321
2422 # Hash keyed on edge distance, mapped to a set of two points
@@ -33,12 +31,12 @@ defmodule Day08 do
3331 end
3432
3533 defp points_to_circuits ( points ) do
36- MapSet . new ( Enum . map ( points , fn p -> MapSet . new ( [ p ] ) end ) )
34+ points
35+ |> Enum . map ( & MapSet . new ( [ & 1 ] ) )
36+ |> MapSet . new ( )
3737 end
3838
39- def connect_points ( points , edges , connections ) do
40- circuits = points_to_circuits ( points )
41-
39+ def connect_points ( circuits , edges , connections ) do
4240 Map . keys ( edges )
4341 |> Enum . sort ( )
4442 |> Enum . take ( connections )
@@ -47,22 +45,22 @@ defmodule Day08 do
4745 end )
4846 end
4947
50- def connect_points_edges ( circuits , edge ) do
51- { a , b } = edge
52-
53- { edge_groups , remaining_circuits } =
54- MapSet . split_with ( circuits , fn circuit ->
55- MapSet . member? ( circuit , a ) or MapSet . member? ( circuit , b )
48+ # Connect to circuits and return the resulting circuit set
49+ # TODO This would be dramatically faster using a disjoint-set union
50+ def connect_points_edges ( circuits , { a , b } ) do
51+ { edge_groups , remaining } =
52+ MapSet . split_with ( circuits , fn group ->
53+ MapSet . member? ( group , a ) or MapSet . member? ( group , b )
5654 end )
5755
58- new_group = Enum . reduce ( edge_groups , & MapSet . union ( & 2 , & 1 ) )
59- MapSet . put ( remaining_circuits , new_group )
56+ merged = Enum . reduce ( edge_groups , & MapSet . union ( & 2 , & 1 ) )
57+
58+ # Dialyzer complains about remaining but I assure you it's a MapSet and is fine
59+ MapSet . put ( remaining , merged )
6060 end
6161
6262 # the last connection which causes all of the junction boxes to form a single circuit
63- def last_connection ( points , edges ) do
64- circuits = points_to_circuits ( points )
65-
63+ def last_connection ( edges , circuits ) do
6664 Map . keys ( edges )
6765 |> Enum . sort ( )
6866 |> Enum . reduce_while ( circuits , fn edge_d , circ_acc ->
@@ -77,33 +75,37 @@ defmodule Day08 do
7775 end )
7876 end
7977
80- def part1 ( infile , connections ) do
81- points = input ( infile )
82- edges = edges ( points )
83- circuits = connect_points ( points , edges , connections )
78+ def part1 ( edges , circuits , connections ) do
79+ connected_circuits = connect_points ( circuits , edges , connections )
8480
85- Enum . map ( circuits , & MapSet . size / 1 )
86- |> Enum . sort ( )
87- |> Enum . reverse ( )
81+ Enum . map ( connected_circuits , & MapSet . size / 1 )
82+ |> Enum . sort ( :desc )
8883 |> Enum . take ( 3 )
8984 |> Enum . product ( )
9085 end
9186
92- def part2 ( infile ) do
87+ def part2 ( edges , circuits ) do
88+ { { a_x , _ , _ } , { b_x , _ , _ } } = last_connection ( edges , circuits )
89+
90+ a_x * b_x
91+ end
92+
93+ def loader ( infile ) do
9394 points = input ( infile )
9495 edges = edges ( points )
96+ circuits = points_to_circuits ( points )
9597
96- { { a_x , _ , _ } , { b_x , _ , _ } } = last_connection ( points , edges )
97-
98- a_x * b_x
98+ { edges , circuits }
9999 end
100100
101101 def main do
102102 input_path = "lib/day08/input.txt"
103103
104- answer = part1 ( input_path , 1000 )
104+ { edges , circuits } = loader ( input_path )
105+
106+ answer = part1 ( edges , circuits , 1000 )
105107 IO . puts ( "Part 1: #{ answer } " )
106- answer = part2 ( input_path )
108+ answer = part2 ( edges , circuits )
107109 IO . puts ( "Part 2: #{ answer } " )
108110 end
109111end
0 commit comments