Skip to content

Commit ac59eed

Browse files
committed
Solve the day 21 puzzle
1 parent e509a83 commit ac59eed

File tree

2 files changed

+122
-0
lines changed

2 files changed

+122
-0
lines changed

day21/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Execute this code via `clj -M -m day21 <filename> <num_of_bidirectional_pads>`.
2+
3+
### Observations
4+

day21/src/day21.clj

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
(ns day21
2+
(:require [clojure.set :as set]
3+
[clojure.string :as str]
4+
[clojure.java.io :as io]))
5+
6+
(defn myprint [x] (println x) x)
7+
8+
(def numpad-neighbors
9+
{\7 [[:right \8] [:down \4]]
10+
\8 [[:left \7] [:right \9] [:down \5]]
11+
\9 [[:left \8] [:down \6]]
12+
\4 [[:up \7] [:right \5] [:down \1]]
13+
\5 [[:up \8] [:right \6] [:down \2] [:left \4]]
14+
\6 [[:up \9] [:left \5] [:down \3]]
15+
\1 [[:up \4] [:right \2]]
16+
\2 [[:up \5] [:right \3] [:down \0] [:left \1]]
17+
\3 [[:left \2] [:up \6] [:down \A]]
18+
\0 [[:up \2] [:right \A]]
19+
\A [[:left \0] [:up \3]]})
20+
21+
(def arrowpad-neighbors
22+
{\A [[:left \^] [:down \>]]
23+
\^ [[:right \A] [:down \v]]
24+
\< [[:right \v]]
25+
\v [[:left \<] [:up \^] [:right \>]]
26+
\> [[:left \v] [:up \A]]})
27+
28+
(defn read-codes [filename]
29+
(with-open [file (io/reader filename)]
30+
(into [] (line-seq file))))
31+
32+
(defn translate [code]
33+
(let [mapper {:up \^, :down \v, :left \<, :right \>, \A \A}]
34+
(str/join (map mapper code))))
35+
36+
; Ordering of the directions is critical to identifying the shortest path
37+
(def directions [:left :down :up :right])
38+
39+
(defn gen-consecutive-pairs [lst] (map vector (drop-last 1 lst) (drop 1 lst)))
40+
41+
(defn get-movements [neighbors]
42+
(defn get-acyclic-paths [[path & remaining] paths]
43+
(if (nil? path)
44+
paths
45+
(let [last-node (last path)
46+
next-nodes (filter #(not (.contains path (second %)))
47+
(neighbors last-node))]
48+
(get-acyclic-paths (concat remaining (map #(concat path %) next-nodes))
49+
(conj paths path)))))
50+
51+
(defn get-min-path-between [[src-trg paths]]
52+
(defn key-helper [path]
53+
(reduce (fn [acc v] (+ (* acc 10) (.indexOf directions v))) 0 path))
54+
(defn is-one-turn-path? [path]
55+
(let [consecutive-dirs (gen-consecutive-pairs path)
56+
turns (filter #(not= (first %) (second %)) consecutive-dirs)]
57+
(<= (count turns) 1)))
58+
(let [dir-only-paths (map (fn [p] (filter #(.contains directions %) p))
59+
paths)
60+
one-turn-paths (filter #(is-one-turn-path? %) dir-only-paths)]
61+
[src-trg (apply min-key key-helper one-turn-paths)]))
62+
63+
(defn get-min-paths [acc src]
64+
(->>
65+
(get-acyclic-paths [[src]] #{})
66+
(group-by (fn [x] [src (last x)]))
67+
(map get-min-path-between)
68+
(into acc)))
69+
(reduce get-min-paths {} (keys neighbors)))
70+
71+
(defn translate-code-to-moves [src-trg-pairs from-to-moves moves]
72+
(translate (flatten (map #(concat (from-to-moves %) '(\A)) src-trg-pairs))))
73+
74+
(defn get-length-of-expanded-code [code reps arrowpad-from-to-moves]
75+
(defn helper [code-segment depth memory]
76+
(cond
77+
(contains? memory [code-segment depth]) memory
78+
(== depth reps) (assoc memory [code-segment depth]
79+
(- (count code-segment) 1))
80+
:else (let [new-code-segments (->>
81+
code-segment
82+
(gen-consecutive-pairs)
83+
(map arrowpad-from-to-moves)
84+
(map #(str "A" (translate %) "A")))
85+
next-depth (+ 1 depth)
86+
new-memory (reduce #(helper %2 next-depth %) memory
87+
new-code-segments)
88+
length (reduce + (map #(new-memory [% next-depth])
89+
new-code-segments))]
90+
(assoc new-memory [code-segment depth] length))))
91+
92+
(let [code-segments (map #(str "A" % "A") (str/split code #"A"))
93+
memory (reduce #(helper %2 0 %1) {} code-segments)]
94+
(reduce + (map #(memory [% 0]) code-segments))))
95+
96+
(defn solve [codes numpad-from-to-moves arrowpad-from-to-moves num-of-arrowpads]
97+
(defn type-code-on-numpad [code]
98+
(->>
99+
(gen-consecutive-pairs (cons \A (seq code)))
100+
(map #(concat (numpad-from-to-moves %) '(\A)))
101+
(flatten)
102+
(translate)))
103+
104+
(defn process-code [code]
105+
(let [numpad-moves (type-code-on-numpad code)
106+
moves-length (get-length-of-expanded-code numpad-moves
107+
num-of-arrowpads
108+
arrowpad-from-to-moves)
109+
code-value (Integer. (str/replace code #"A" ""))]
110+
(* code-value moves-length)))
111+
(reduce + (map process-code codes)))
112+
113+
(defn -main [filename num-of-robots]
114+
(let [codes (read-codes filename)
115+
numpad-from-to-moves (get-movements numpad-neighbors)
116+
arrowpad-from-to-moves (get-movements arrowpad-neighbors)]
117+
(println (solve codes numpad-from-to-moves arrowpad-from-to-moves
118+
(Integer. num-of-robots)))))

0 commit comments

Comments
 (0)