@@ -3,78 +3,100 @@ defmodule AdventOfCode.Y2023.Day12 do
33 --- Day 12: Hot Springs ---
44 Problem Link: https://adventofcode.com/2023/day/12
55 Difficulty: xl
6- Tags: memoization vector
6+ Tags: memoization
77 """
88 alias AdventOfCode.Helpers . { InputReader , Transformers }
9- alias Aja.Vector
109
1110 def input , do: InputReader . read_from_file ( 2023 , 12 )
1211
1312 def run ( input \\ input ( ) ) do
14- input = parse ( input )
15-
16- { total_arrangements ( input , fn { spring , groups } ->
17- arrangements ( { as_vector ( spring ) , groups } )
18- end ) ,
19- total_arrangements ( input , fn { spring , groups } ->
20- arrangements (
21- { [ spring ] |> List . duplicate ( 5 ) |> Enum . join ( "?" ) |> as_vector ( ) ,
22- groups |> Vector . duplicate ( 5 ) |> Vector . flat_map ( & & 1 ) }
23- )
24- end ) }
13+ parsed = parse ( input )
14+
15+ res1 = compute ( parsed , 1 )
16+ res2 = compute ( parsed , 5 )
17+
18+ { res1 , res2 }
2519 end
2620
27- defp total_arrangements ( input , fun ) do
28- for { :ok , count } <- Task . async_stream ( input , fun ) , reduce: 0 do
29- acc -> acc + count
30- end
21+ defp compute ( parsed , multiplier ) do
22+ parsed
23+ |> Task . async_stream (
24+ fn { springs , groups } ->
25+ unfolded_springs = List . duplicate ( springs , multiplier ) |> Enum . join ( "?" )
26+ unfolded_groups = List . duplicate ( groups , multiplier ) |> List . flatten ( ) |> List . to_tuple ( )
27+
28+ Process . get_keys ( ) |> Enum . each ( & Process . delete / 1 )
29+ do_count ( unfolded_springs , unfolded_groups , 0 , 0 , 0 )
30+ end ,
31+ timeout: :infinity
32+ )
33+ |> Enum . reduce ( 0 , fn { :ok , count } , acc -> acc + count end )
3134 end
3235
33- def parse ( data \\ input ( ) ) do
36+ def parse ( data ) do
3437 for line <- Transformers . lines ( data ) do
35- [ springs , data ] = String . split ( line , " " )
36- { springs , Vector . new ( Transformers . int_words ( data , "," ) ) }
38+ [ springs , groups ] = String . split ( line , " " )
39+ { springs , Transformers . int_words ( groups , "," ) }
3740 end
3841 end
3942
40- defp memoize ( input , counts ) , do: Process . put ( input , counts )
41- defp memoized ( { _ , _ , _ , _ , _ } = input ) , do: memoized ( input , Process . get ( input ) )
42- defp memoized ( input , nil ) , do: tap ( arrangements ( input ) , & memoize ( input , & 1 ) )
43- defp memoized ( _ , counts ) , do: counts
44- defp as_vector ( springs ) , do: Vector . new ( String . graphemes ( springs <> "." ) )
45- defp arrangements ( { springs , groups } ) , do: arrangements ( { springs , groups , 0 , 0 , 0 } )
46-
47- defp arrangements ( { springs , groups , current , current_count , processed } = input ) do
48- case { Vector . at ( springs , current ) , Vector . size ( springs ) , Vector . size ( groups ) } do
49- { _ , ^ current , ^ processed } -> tap ( 1 , & memoize ( input , & 1 ) )
50- { _ , ^ current , _ } -> tap ( 0 , & memoize ( input , & 1 ) )
51- { "#" , _ , _ } -> memoized ( { springs , groups , current + 1 , current_count + 1 , processed } )
52- { "." , _ , _ } -> arrangements ( input , :operational )
53- { _ , _ , ^ processed } -> arrangements ( input , :operational )
54- _ -> arrangements ( input , :unknown )
43+ defp do_count ( springs , groups , s_idx , g_idx , current_count ) do
44+ key = { s_idx , g_idx , current_count }
45+
46+ case Process . get ( key ) do
47+ nil ->
48+ res = calc ( springs , groups , s_idx , g_idx , current_count )
49+ Process . put ( key , res )
50+ res
51+
52+ val ->
53+ val
5554 end
5655 end
5756
58- defp arrangements ( { springs , groups , current , current_count , processed } = input , :operational ) do
59- groups_processed = Vector . at ( groups , processed )
57+ defp calc ( springs , groups , s_idx , g_idx , current_count ) do
58+ if s_idx == byte_size ( springs ) do
59+ cond do
60+ g_idx == tuple_size ( groups ) and current_count == 0 -> 1
61+ g_idx == tuple_size ( groups ) - 1 and current_count == elem ( groups , g_idx ) -> 1
62+ true -> 0
63+ end
64+ else
65+ char = :binary . at ( springs , s_idx )
6066
61- case { processed < Vector . size ( groups ) , current_count } do
62- { true , ^ groups_processed } -> memoized ( { springs , groups , current + 1 , 0 , processed + 1 } )
63- { _ , 0 } -> memoized ( { springs , groups , current + 1 , 0 , processed } )
64- _ -> tap ( 0 , fn zero -> memoize ( input , zero ) end )
67+ case char do
68+ ?# ->
69+ handle_hash ( springs , groups , s_idx , g_idx , current_count )
70+
71+ ?. ->
72+ handle_dot ( springs , groups , s_idx , g_idx , current_count )
73+
74+ ?? ->
75+ handle_dot ( springs , groups , s_idx , g_idx , current_count ) +
76+ handle_hash ( springs , groups , s_idx , g_idx , current_count )
77+ end
6578 end
6679 end
6780
68- defp arrangements ( { springs , groups , current , current_count , processed } = input , :unknown ) do
69- groups_processed = Vector . at ( groups , processed )
81+ defp handle_hash ( springs , groups , s_idx , g_idx , current_count ) do
82+ if g_idx < tuple_size ( groups ) and current_count < elem ( groups , g_idx ) do
83+ do_count ( springs , groups , s_idx + 1 , g_idx , current_count + 1 )
84+ else
85+ 0
86+ end
87+ end
7088
71- tap (
72- case current_count do
73- ^ groups_processed -> memoized ( { springs , groups , current + 1 , 0 , processed + 1 } )
74- 0 -> memoized ( { springs , groups , current + 1 , 0 , processed } )
75- _ -> 0
76- end + memoized ( { springs , groups , current + 1 , current_count + 1 , processed } ) ,
77- fn counts -> memoize ( input , counts ) end
78- )
89+ defp handle_dot ( springs , groups , s_idx , g_idx , current_count ) do
90+ case current_count do
91+ 0 ->
92+ do_count ( springs , groups , s_idx + 1 , g_idx , 0 )
93+
94+ n ->
95+ if g_idx < tuple_size ( groups ) and n == elem ( groups , g_idx ) do
96+ do_count ( springs , groups , s_idx + 1 , g_idx + 1 , 0 )
97+ else
98+ 0
99+ end
100+ end
79101 end
80102end
0 commit comments