this repo has no description
1<!-- vim:ft=markdown -->
2
3<!-- livebook:{"persist_outputs":true} -->
4
5# Day 23
6
7## Section
8
9```elixir
10input =
11 File.stream!("day23.txt")
12 |> Enum.map(fn line ->
13 for <<c <- line>>, c in 'ABCD', do: String.to_atom(<<c + 32>>)
14 end)
15 |> Enum.reject(&(&1 == []))
16 |> Enum.zip()
17 |> Enum.map(&Tuple.to_list/1)
18 |> Enum.zip_with(~w[a b c d]a, &{&2, &1})
19 |> Map.new()
20```
21
22```output
23%{a: [:d, :b], b: [:d, :a], c: [:c, :b], d: [:c, :a]}
24```
25
26Graph of the possible movements. `--` and `|` have multiply 1 and `---`, `\`, and `/` have
27multiply 2.
28
29```
30.--.---.---.---.---.--.
31 \ / \ / \ / \ /
32 . . . .
33 | | | |
34 . . . .
35```
36
37```elixir
38defmodule Day23 do
39 @costs %{a: 1, b: 10, c: 100, d: 1000}
40 @rooms %{a: -3, b: -1, c: 1, d: 3}
41
42 # - - - - - + + + + +
43 # 5 4 3 2 1 0 1 2 3 4 5
44 # A B C D
45 # A B C D
46
47 defp memoize(tid, args, func) do
48 case :ets.lookup(tid, args) do
49 [{^args, value}] ->
50 value
51
52 [] ->
53 value = func.()
54 :ets.insert(tid, {args, value})
55 value
56 end
57 end
58
59 def process(stacks) do
60 tid = :ets.new(:memoize, [:public, :set])
61 depth = length(stacks[:a])
62
63 try do
64 do_process(stacks, %{}, target(depth), depth, tid)
65 after
66 :ets.delete(tid)
67 end
68 end
69
70 defp do_process(target, _, target, _depth, _tid), do: 0
71
72 defp do_process(stacks, hall, target, depth, tid) do
73 memoize(tid, [stacks, hall], fn ->
74 possible_moves(stacks, hall, depth)
75 |> Enum.map(fn {new_stack, new_hall, cost} ->
76 cost + do_process(new_stack, new_hall, target, depth, tid)
77 end)
78 |> Enum.min(&<=/2, fn -> :inf end)
79 end)
80 end
81
82 defp possible_moves(stacks, hall, depth) do
83 hall_to_room(stacks, hall, depth) ++ room_to_hall(stacks, hall, depth)
84 end
85
86 defp hall_to_room(stacks, hall, depth) do
87 # IO.inspect({stacks, hall, depth}, label: elem(__ENV__.function, 0))
88 for {pos, t} <- hall,
89 clear_path_to_room?(t, pos, hall),
90 accepts?(t, stacks),
91 {size, new_stacks} = into_room(t, stacks),
92 do: {new_stacks, Map.delete(hall, pos), cost(t, pos, @rooms[t], depth - size)}
93 end
94
95 defp room_to_hall(stacks, hall, depth) do
96 []
97 end
98
99 defp clear_path_to_room?(t, pos, hall) do
100 hall
101 |> Map.keys()
102 |> Enum.find(&(&1 in min(pos, @rooms[t])..max(pos, @rooms[t])))
103 |> case do
104 nil -> true
105 _ -> false
106 end
107 end
108
109 defp accepts?(t, stacks), do: Enum.all?(stacks[t], &(&1 == t))
110
111 defp into_room(t, stacks) do
112 Map.get_and_update!(stacks, t, &{length(&1), [t | &1]})
113 end
114
115 defp target(depth) do
116 for v <- ~w[a b c d]a,
117 into: %{},
118 do: {v, List.duplicate(v, depth)}
119 end
120
121 def cost(elem, from, to, depth) do
122 @costs[elem] * (depth + abs(to - from))
123 end
124end
125```
126
127```output
128warning: variable "depth" is unused (if the variable is not meant to be used, prefix it with an underscore)
129 day23.livemd#cell:58: Day23.room_to_hall/3
130
131warning: variable "hall" is unused (if the variable is not meant to be used, prefix it with an underscore)
132 day23.livemd#cell:58: Day23.room_to_hall/3
133
134warning: variable "stacks" is unused (if the variable is not meant to be used, prefix it with an underscore)
135 day23.livemd#cell:58: Day23.room_to_hall/3
136
137```
138
139```output
140{:module, Day23, <<70, 79, 82, 49, 0, 0, 23, ...>>, {:cost, 4}}
141```
142
143```elixir
144Day23.process(input)
145```
146
147```output
148:inf
149```