@@ -3,7 +3,7 @@ defmodule AdventOfCode.Y2020.Day23 do
33 --- Day 23: Crab Cups ---
44 Problem Link: https://adventofcode.com/2020/day/23
55 Difficulty: l
6- Tags: circular-linked-list slow
6+ Tags: circular-linked-list atomics
77 """
88 def input , do: "467528193"
99
@@ -20,90 +20,83 @@ defmodule AdventOfCode.Y2020.Day23 do
2020 input |> String . graphemes ( ) |> Enum . map ( & String . to_integer / 1 )
2121 end
2222
23- # Part 1 and Part 2 use the same logic, but different constraints
2423 defp solve ( initial_cups , moves , total_cups ) do
25- # 1. Build the circular linked list using :counters (O(1) mutable array)
26- arr = :counters . new ( total_cups + 1 , [ :write_concurrency ] )
27-
28- # Fill initial cups
29- initial_len = length ( initial_cups )
30- max_initial = Enum . max ( initial_cups )
31-
32- initial_cups
33- |> Enum . chunk_every ( 2 , 1 , [ ] )
34- |> Enum . each ( fn
35- [ curr , nxt ] ->
36- :counters . put ( arr , curr , nxt )
37-
38- [ last ] ->
39- if total_cups > initial_len do
40- :counters . put ( arr , last , max_initial + 1 )
41- else
42- :counters . put ( arr , last , hd ( initial_cups ) )
43- end
44- end )
24+ arr = :atomics . new ( total_cups + 1 , [ ] )
25+ setup_pointers ( arr , initial_cups , total_cups )
4526
46- # Fill the rest for Part 2
47- if total_cups > initial_len do
48- Enum . each ( ( max_initial + 1 ) .. total_cups , fn i ->
49- if i == total_cups do
50- :counters . put ( arr , i , hd ( initial_cups ) )
51- else
52- :counters . put ( arr , i , i + 1 )
53- end
54- end )
55- end
56-
57- first_cup = hd ( initial_cups )
58-
59- play ( arr , first_cup , moves , total_cups )
27+ [ first | _ ] = initial_cups
28+ play ( arr , first , moves , total_cups )
6029
6130 if total_cups == 9 do
62- # Part 1: Labels after cup 1
6331 read_after ( arr , 1 , 8 )
6432 else
65- # Part 2: Product of two cups after cup 1
66- c1 = :counters . get ( arr , 1 )
67- c2 = :counters . get ( arr , c1 )
33+ c1 = :atomics . get ( arr , 1 )
34+ c2 = :atomics . get ( arr , c1 )
6835 c1 * c2
6936 end
7037 end
7138
72- defp play ( _arr , _current , 0 , _ ) , do: :ok
39+ defp setup_pointers ( arr , initial , total ) do
40+ max_init = Enum . max ( initial )
41+ [ first | _ ] = initial
42+ last_init = List . last ( initial )
43+
44+ initial
45+ |> Enum . chunk_every ( 2 , 1 , :discard )
46+ |> Enum . each ( fn [ u , v ] -> :atomics . put ( arr , u , v ) end )
47+
48+ if total > length ( initial ) do
49+ :atomics . put ( arr , last_init , max_init + 1 )
50+
51+ if total > max_init + 1 do
52+ Enum . each ( ( max_init + 1 ) .. ( total - 1 ) , fn i -> :atomics . put ( arr , i , i + 1 ) end )
53+ end
54+
55+ :atomics . put ( arr , total , first )
56+ else
57+ :atomics . put ( arr , last_init , first )
58+ end
59+ end
60+
61+ defp play ( _arr , _current , 0 , _max ) , do: :ok
7362
7463 defp play ( arr , current , moves , max ) do
75- # 1. Pick up three cups immediately clockwise of current
76- c1 = :counters . get ( arr , current )
77- c2 = :counters . get ( arr , c1 )
78- c3 = :counters . get ( arr , c2 )
79- after_picked = :counters . get ( arr , c3 )
64+ c1 = :atomics . get ( arr , current )
65+ c2 = :atomics . get ( arr , c1 )
66+ c3 = :atomics . get ( arr , c2 )
67+ after_c3 = :atomics . get ( arr , c3 )
8068
81- # 2. Select destination cup
82- dest = select_destination ( current - 1 , c1 , c2 , c3 , max )
69+ d1 = if current == 1 , do: max , else: current - 1
8370
84- # 3. Update pointers to "insert" cups
85- after_dest = :counters . get ( arr , dest )
71+ dest =
72+ if d1 == c1 or d1 == c2 or d1 == c3 do
73+ d2 = if d1 == 1 , do: max , else: d1 - 1
8674
87- :counters . put ( arr , current , after_picked )
88- :counters . put ( arr , dest , c1 )
89- :counters . put ( arr , c3 , after_dest )
75+ if d2 == c1 or d2 == c2 or d2 == c3 do
76+ d3 = if d2 == 1 , do: max , else: d2 - 1
9077
91- play ( arr , after_picked , moves - 1 , max )
92- end
78+ if d3 == c1 or d3 == c2 or d3 == c3 ,
79+ do: if ( d3 == 1 , do: max , else: d3 - 1 ) ,
80+ else: d3
81+ else
82+ d2
83+ end
84+ else
85+ d1
86+ end
9387
94- defp select_destination ( 0 , c1 , c2 , c3 , max ) , do: select_destination ( max , c1 , c2 , c3 , max )
88+ after_dest = :atomics . get ( arr , dest )
9589
96- defp select_destination ( dest , c1 , c2 , c3 , max ) do
97- if dest == c1 or dest == c2 or dest == c3 do
98- select_destination ( dest - 1 , c1 , c2 , c3 , max )
99- else
100- dest
101- end
90+ :atomics . put ( arr , current , after_c3 )
91+ :atomics . put ( arr , dest , c1 )
92+ :atomics . put ( arr , c3 , after_dest )
93+
94+ play ( arr , after_c3 , moves - 1 , max )
10295 end
10396
104- defp read_after ( arr , start , count ) do
105- Enum . reduce ( 1 .. count , { [ ] , start } , fn _ , { acc , curr } ->
106- nxt = :counters . get ( arr , curr )
97+ defp read_after ( arr , current , count ) do
98+ Enum . reduce ( 1 .. count , { [ ] , current } , fn _ , { acc , curr } ->
99+ nxt = :atomics . get ( arr , curr )
107100 { [ nxt | acc ] , nxt }
108101 end )
109102 |> elem ( 0 )
0 commit comments