Fork me on GitHub
#adventofcode
<
2022-12-05
>
norman05:12:11

Day 5 - Solutions

mchampine05:12:20

;; Part 1
(def istacks  ["VCDRZGBW" "GWFCBSTV" "CBSNW"
               "QGMNJVCP" "TSLFDHB" "JVTWMN"
               "PFLCSTG" "BDZ" "MNZW"])

(defn move [rfn stks [n from to]]
  (let [from (dec from) to (dec to)
        lnff (take-last n (get stks from))]
    (-> (assoc stks to (apply str (get stks to) (rfn lnff)))
        (assoc from (apply str (drop-last n (get stks from)))))))

(defn reducer [rf stk inp]
  (->> (reduce (partial move rf) stk inp)
       (map last)
       (apply str)))

((partial reducer reverse) istacks input) ;; "TBVFVDZPN"

;; Part2
((partial reducer identity) istacks input) ;; "VLCWHTDSZ"

Apple06:12:30

(ns aoc.y2022
  (:require [clojure.string :as cstr]))

;; 202205
(let [[d1 d2] (-> (slurp "resources/202205") (cstr/split #"\n\n"))
      procedures (->> d2 (re-seq #"\d+") (map parse-long) (partition 3))
      n-stack (count (re-seq #"\d+" d1))
      stacks (->> (re-seq #"\w|    " d1)
                  (partition n-stack)
                  (apply mapv #(cstr/replace (cstr/join %&) " " "")))
      f #(->> (reduce (fn [stacks [quantity from to]]
                        (let [froms  (get stacks (dec from))
                              s      (subs froms 0 quantity)
                              froms' (subs froms quantity)
                              tos'   (str (% s) (get stacks (dec to)))]
                          (-> stacks
                              (assoc (dec from) froms')
                              (assoc (dec to) tos'))))
                      stacks
                      procedures)
              (map first)
              cstr/join)]
  (map f [cstr/reverse identity]))
;; ("JDTMRWCQJ" "VHJDDCWRD")

1
tylerw06:12:57

(Could use some cleanup 😄)

Casey10:12:36

https://github.com/Ramblurr/advent-of-code-2022/blob/main/src/aoc/day05.clj#L10-L77 Parsing the input on this one was pretty fun! I used read-string like some others have been talking about the past few days.

Mikko Koski13:12:37

https://github.com/rap1ds/advent-of-code-2022 TIL: (list* xs) is NOT the same as (apply list xs). I tried to use list* to create my stacks with poor results:

;; works
(apply list "ABCD") ;; #=> (\A \B \C \D)
(pop (apply list "ABCD")) ;; #=> (\B \C \D)
(type (apply list "ABCD")) ;; #=> clojure.lang.PersistentList

;; doesn't work
(list* "ABCD") ;; #=> (\A \B \C \D)
(pop (list* "ABCD")) ;; #=> ERROR!
(type (list* "ABCD")) ;; #=> clojure.lang.StringSeq
list*'s https://clojuredocs.org/clojure.core/list* explains it pretty clearly why it's like this

👍 2
potetm13:12:06

Never would have occurred to me to reverse the inputs and re-run the same engine.

erwinrooijakkers14:12:21

@U076FM90B isn’t (update from #(drop move %)) the same as (update from drop move) ?

potetm14:12:17

@U2PGHFU5U the latter will do (update from #(drop % move))

erwinrooijakkers14:12:29

ah thanks you’re right 🙂

nbardiuk14:12:01

yes, unfortunately update , like thread first macro, is optimized for single value, not a collection

nooga14:12:21

https://github.com/nooga/aoc2022/blob/master/day5.lg

(ns day5)

(def data (->> "input/day5.txt" slurp lines (split-with (complement empty?))))

(def stacks (->> data first butlast
                 (map #(str-replace % #"( |\\[)([A-Z ])(\\]| ) ?" "$2"))
                 (apply mapv list)
                 (mapv #(drop-while #{\space} %))))

(def instructions (->> data second rest
                       (map #(str-replace % #"\\w+ (\\d+)" "$1"))
                       (map (comp #(mapv parse-long %) #(split % " ")))))

(defn solve [pickup]
  (->> instructions
       (reduce (fn [a [n f t]]
                 (let [f (dec f) t (dec t)]
                   (-> a (update t into (pickup (take n (a f))))
                       (update f #(drop n %)))))
               stacks)
       (map first)
       (apply str)))

(println "1:" (solve identity))
(println "2:" (solve reverse))
I had a chance to add Regex to let-go core thanks to this ;)

1
👏 1
Stuart15:12:08

Oh boy, I am NOT proud of my solution today

Stuart15:12:09

I struggled with the parsing. So I filled in the empty spaces with -, then removed them later after I had made a map of location and boxes. I thought this would make it easy since I could then just split on the boxes... (apply map vector) to rotate the rows into columns, then zipmap with (range) to get a map of the stack. https://github.com/stuartstein777/clj-advent-of-code/blob/master/src/stuartstein777/2022/day5.clj At least part 2 turned out easy, I just had add in a reverse call.

robertfw19:12:04

I am not proud of this code 😅 make it work, make it good, make it fast, right? well, this works...

nooga19:12:55

That's the spirit! I always code mine with utter disregard for performance and style by putting down first though that I had and sticking to it. Then I go here to appreciate all the thought out solutions. 😆

bananadance 1
zamansky20:12:43

Here's my day 5: https://github.com/zamansky/advent2022/blob/main/src/day05.clj Was stupid with my regex for a while and also for not paying attention to vectors vs lists 🙂

Luis Santos21:12:29

Took me a while to solve this:

(defn transpose [xs]
  (apply map list xs))


(defn build-stacks [i]
  (->> (string/split-lines i)
       (map #(re-seq #"\[\w\]|    |[0-9]" %))
       (take-while (complement nil?))
       (transpose)
       (map reverse)
       (map #(remove string/blank? %))
       (reduce #(assoc %1 (first %2) (rest %2)) (sorted-map))))

(defn  parse-steps [i]
  (->> (string/split-lines i)
       (map #(re-seq #"move (\d*) from (\d) to (\d)" %))
       (filter (complement nil?))
       (map (comp rest first))
       (map #(cons (parse-long (first %)) (rest %)))))


(defn apply-step [stacks [n a b]]
  (let [sa  (stacks a)
        sb  (stacks b)]
    (-> stacks
        (assoc a (drop-last n sa))
        (assoc b (concat sb (take-last n sa))))))

(let [data (slurp "input/day-5-input.txt")
      stacks (build-stacks data)]
  (->> (parse-steps data)
       (reduce apply-step stacks)
       (vals)
       (map (comp second last))
       (reduce str)))

CarnunMP21:12:37

little late to the game today, but it https://github.com/CarnunMP/Advent-of-Code/blob/master/src/y2022/d5.clj :)) one thing I like about the stacks parsing here is this line:

(defn parse [input]
  (let [{stacks false, steps true} (group-by #(str/starts-with? % "move") input)]
    {:stacks (->> (take (- (count stacks) 2) stacks) ; ignore numbers and blank line
                  (map #(partition 4 4 (repeat \space) %))
                  (apply (partial map list)) ; <-- HERE
                  (mapv #(keep (partial some uppercase-letter?) %)))
     :steps (map #(let [[move from to] (re-seq #"\d+" %)]
                    [(parse-long move)
                     ;; 0-based indexing of the stacks vector
                     (dec (parse-long from))
                     (dec (parse-long to))]) steps)}))
Effectively turns the (already somewhat parsed) lines sideways! Then just the non-crates need to be removed. :))

Piotr Kaznowski22:12:40

(defn get-stacks [s]
  (let [chars (re-seq #"\w|    " s)
        len (parse-long (last chars))]
    (->> chars
         (map #(re-matches #"\w" %))
         (partition len)
         butlast
         (apply mapv vector)
         (mapv #(remove nil? %)))))

(defn get-movements [m]
  (->> m
       (re-seq #"\d+")
       (map parse-long)
       (partition 3)))

(defn parse [s]
  (-> s
      (split #"\n\n")
      ((juxt (comp get-stacks first)
             (comp get-movements second)))))

(defn reducer [[coll f] [amount from to]]
  [(-> coll
       (update (dec from) #(drop amount %))
       (update (dec to) #(into % (f (take amount (coll (dec from)))))))
   f])

(defn -main [day]
  (let [[stacks movements] (parse (file->str day))]
    (zipmap [:part1 :part2]
            (->> [identity reverse]
                 (map #(reduce reducer [stacks %] movements))
                 (map #(->> % first (map first) join))))))

gabo23:12:45

Really struggled with the parsing 😓 and after playing at repl during part 2 I was happy that I noticed reversing or not the stacks when moving them was the only difference in between part 1 and 2

(ns me.galuque.aoc-2022.day-05
  (:require [ :as io]
            [clojure.string :as str]))

(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)

(def raw-input (slurp (io/resource "input/day_05.txt")))

(defn transpose
  [m]
  (apply mapv vector m))

(defn parse-stacks [raw-input]
  (-> raw-input
      (str/split #"\n\n")
      (nth 0)
      (str/split #"\n")
      (->>
       (butlast)
       (map #(re-seq #"    |[\w+]" %))
       (transpose)
       (mapv #(remove str/blank? %)))))


(defn parse-instructions [raw-input]
  (-> raw-input
      (str/split #"\n\n")
      (nth 1)
      (str/split-lines)
      (->>
       (map #(re-seq #"\d+" %))
       (mapv (fn [[amount from to]]
               {:amount (parse-long amount)
                :from (dec ^long (parse-long from))
                :to (dec ^long (parse-long to))})))))


(defn move [reverse? stacks {:keys [amount from to]}]
  (let [reverse-fn (if reverse? reverse identity)
        value    (->> (nth stacks from)
                      (take amount)
                      (reverse-fn))
        stack'  (-> stacks
                    (update from (partial drop amount))
                    (update to (partial apply conj) value))]
    stack'))


(defn top-rearrange [reverse? init-stack instructions]
  (let [mv (partial move reverse?)]
    (->> (reduce mv init-stack instructions)
         (map first)
         (str/join))))

(defn part-1 [raw-input]
  (top-rearrange false
                 (parse-stacks raw-input)
                 (parse-instructions raw-input)))

(comment
  (part-1 raw-input)
  ;; => "FRDSQRRCD"
  )

(defn part-2 [raw-input]
  (top-rearrange true
                 (parse-stacks raw-input)
                 (parse-instructions raw-input)))

(comment
  (part-2 raw-input)
  ;; => "HRFTQVWNN"
  )

Anssi Kittilä06:12:35

struggled with parsing the original structure, but after that it was simple

jaihindhreddy11:12:39

Mine turned out pretty ugly. The parsing is really annoying on this one 😩:

(defn day5 [fs]
  (let [[stk steps]
        ; parsing
        (let [[s1 s2] (str/split fs #"\R\R")]
          [(->> (pop (str/split-lines s1))
             (mapv #(->> (partition-all 4 %)
                      (mapv (fn [[_ c _]]
                              (when-not (= c \space) c)))))
             (apply mapv vector)
             (mapv (comp vec #(take-while some? %) reverse)))
           (->> (str/split-lines s2)
             (mapv #(let [[n from to] (mapv parse-long (re-seq #"\d+" %))]
                      [n (dec from) (dec to)])))])
        msg (fn [mover]
              (->> (reduce mover stk steps)
                (mapv peek)
                (apply str)))]
    [(msg (fn [stk [n from to]]
            (-> (iterate #(let [x (peek (nth % from))]
                            (-> (update % from pop)
                              (update to conj x))) stk)
              (nth n))))
     (msg (fn [stk [n from to]]
            (let [fr (nth stk from)
                  [fr' xs] (split-at (- (count fr) n) fr)]
              (-> (update stk to into xs)
                (assoc from (vec fr'))))))]))

pwojnowski12:12:54

I wonder how the people (sic!) from the top of the leaderboard got that parsed that fast, but probably something really straigthforward...

pwojnowski12:12:32

Thats's why I wrote "people" 😉

pwojnowski12:12:20

With gpt3 the advent became so depressing... 😭

Stuart12:12:00

Yeah, I think a lot of us are going to have to change careers in next 10 years

pwojnowski12:12:33

This is sad, but it appears so.

Thierry10:12:45

Finally had the time to do day 5, I probably used a ridiculous way to solve it and there are probably quicker ways to solve it, but the end result is what matters 😉 https://github.com/LouDnl/advent-of-code-2022/blob/master/src/clj/advent_of_code_2022/days/day_five.clj

mbjarland14:12:05

(ns aoc.day-05
  (:require [clojure.string :refer [split join split-lines]]))

(defn initial-state [cs]
  (->> (for [line (map vec (butlast (split-lines cs)))]
         (map line (range 1 (count line) 4)))
       (apply mapv vector)
       (mapv #(remove #{\space} %))))

(defn operations [os]
  (partition 3 (map parse-long (re-seq #"\d+" os))))

(defn solve [cfn]
  (let [[is os] (split (slurp "../day_05.data") #"\n\n")]
    (reduce (fn [a [n f t]]
              (let [[s r] (split-at n (a (dec f)))]
                (-> (assoc a (dec f) r)
                    (update (dec t) #(concat (cfn s) %)))))
            (initial-state is)
            (operations os))))

(defn solution-1 []
  (join (map first (solve reverse))))

(defn solution-2 []
  (join (map first (solve identity))))

metal 1
👍 2
wow 1
leinfink00:12:36

@U4VDXB2TU That's so pretty! Learned a lot reading that.

mbjarland09:12:28

@U04BA34MCDN thank you! Yes I’ve learnt a lot from other’s solutions here as well (@UP82LQR9N looking at you among others)

dumrat19:12:35

(ns aoc2022.day5
  (:require [ :refer [resource]]
            [clojure.string :as str]))

(defn data []
  (->> (resource "inputs/day5.txt")
       (slurp)
       (str/split-lines)))

(defn read-crate-line [line]
  (->> (partition 4 4 [\space] line)
       (map (comp flatten (partial partition-by #{\[ \]})))
       (map (partial remove #{\[ \] \space}))))

(defn read-move-line [line]
  (->> (re-find (re-matcher #"move (\d+) from (\d+) to (\d+)" line))
       (drop 1)
       (map parse-long)
       (interleave [:amount :from :to])
       (partition 2)
       (map vec)
       (vec)
       (into {})))

(defn starting-state []
  (let [raw (->> (data)
                 (partition-by str/blank?)
                 (remove #(= (count %) 1)))
        crate-line-count (dec (count (first raw)))
        crates (->> (first raw)
                    (take crate-line-count)
                    (map read-crate-line)
                    (map (partial map (fn [e] (if (empty? e) nil (first e)))))
                    ;Transpose trick
                    (apply map vector)
                    (map reverse)
                    (mapv (partial keep identity))
                    (mapv vec))
        moves (->> (second raw)
                   (mapv read-move-line))]
    {:crates crates
     :moves moves}))

(def rearrange-1 reverse)
(def rearrange-2 identity)

(defn move [{:keys [crates moves] :as state} rearrange-strat]
  (tap> state)
  (if (empty? moves)
    state
    (let [{:keys [amount to from]} (first moves)
          to (dec to)
          from (dec from)]
      (recur {:crates (-> crates
                          (update to #(apply conj % (rearrange-strat (take-last amount (get crates from)))))
                          (update from #(vec (drop-last amount %))))
              :moves (drop 1 moves)}
             rearrange-strat))))

(defn solution [rearrange-strat]
  (let [{crates :crates} (move (starting-state) rearrange-strat)]
    (apply str (map last crates))))

(def part1 (partial solution rearrange-1))
(def part2 (partial solution rearrange-2))

dumrat19:12:44

Not cute 😄

potetm13:12:46

I just hopped back in after having been out of the Clojurians slack for a while. Gotta say, it's awesome to see what some of ya'll do in here.

metal 6
potetm13:12:22

So many brilliant solutions.

genRaiy16:12:37

I added an explainer for day 3 (based on a random input of reasonable length) ... not sure if it's going to get too much going forward but wdyt?

borkdude16:12:39

It's awesome but you gotta get this up on github pages or else it doesn't count @raymcdermott

genRaiy16:12:03

haha - I know

mkvlr16:12:28

maybe try clerk.garden to publish your scittle stuff 🙃

genRaiy20:12:52

it's a great idea but I might be past that point this time around

mkvlr20:12:45

maybe next year

1
bhauman16:12:48

OK let’s talk parsing again

bhauman16:12:34

I put the columns into a separate file and then used emacs rectangle commands and a macro to reformat the file. https://github.com/bhauman/advent-of-code-2022/blob/main/src/adv2022/day5/input-columns.txt

bhauman17:12:38

Then I did this:

(def columns
  ;; file prepared with emacs rectangle commands and a macro
  (->> (str "[" (slurp "src/adv2022/day5/input-columns.txt") "]")
       read-string
       (partition-by integer?)
       (remove (comp integer? first))
       (mapv #(map first %))))

bhauman17:12:15

That left the commands in another file and I just used read-string on the whole file again like above and filtered out the integers and partitioned by 3.

(def commands
  (->> (str "[" (slurp "src/adv2022/day5/input.txt") "]")
       read-string
       (filter integer?)
       (partition 3)))

bhauman17:12:56

just trying to drive home the super-power that is read-string

norman17:12:45

read-string is such a dangerous function, that I don't let my brain even consider it 🙂

bhauman17:12:52

yeah but if you can’t use it when you’re fun hacking, when can you??😉

bhauman17:12:01

I love me some dangerous read-string 👹

🔥 1
borkdude19:12:16

I know y'all know this, but pretty good what it comes up with :-)

1
bhauman19:12:24

Yeah you caused me to get a #chatgpt account the other day. It’s pretty insane.

bhauman19:12:32

AND just quell the controversy I’m going to use clojure.end/read-string even when I’m hacking. 🥲

norman19:12:32

another soul saved!

bhauman19:12:04

(def READ-STRING cloure.edn/read-string)

pez19:12:33

Pity. I was waiting for when the file input would be massaged to be code so that read-string proper would spit out the answer.

borkdude19:12:51

load-string to the rescue :P

bhauman17:12:56

Although thinking about it I could have used some code to accomplish the same thing as the emacs rectangle macros.

nooga17:12:40

I just did this to parse the first section:

(def data (->> "input/day5.txt" slurp lines (split-with (complement empty?))))

(def stacks (->> data first butlast
                 (map #(str-replace % #"( |\\[)([A-Z ])(\\]| ) ?" "$2"))
                 (apply mapv list)
                 (mapv #(drop-while #{\space} %))))
It gives me a vector of lists representing each stack. The caveat here is that I'm solving and developing the language at the same time so I had to add regex support to pull this off :d

bhauman17:12:30

I’ll go look at the orig code to check it out

bhauman17:12:45

Yeah it’s the transpose that seems important here 🙂

bhauman17:12:23

Yeah definitely like your solution here

nooga17:12:32

yeah, this is basically: 1. clean up the cruft to get something like

C 
 DE
ABC
2. split into chars and transpose
[(\space \E \C) (\C \D \B) (\space \space \A)]
3. clean up leading spaces
[(\E \C) (\C \D \B) (\A)]

bhauman17:12:07

And I offer one more way of parsing the columns without formatting the file:

(def columns-alt
  (->> (slurp "src/adv2022/day5/columns-orig.txt")
       string/split-lines
       ;; make rows lists of 4 characters
       (mapv #(partition-all 4 (seq %)))
       ;; transpose - this gets the columns together
       (apply map vector)
       ;; to make the column a string
       (map #(string/join (flatten %)))
        ;; make into clojure data like [[A] [B] 2]
       (map #(read-string (str "[" % "]")))
       ;; remove the last number
       (map butlast)
       ;; to join [[A] [B]] -> [A B]
       (map flatten)))

Ben Lieberman19:12:31

hey, I've been stuck on day five for quite a while, can someone give my code a once over and see if they can find anything stupid and obvious? See in 🧵

Ben Lieberman19:12:03

(ns slothrop.day-five.crates
  (:require [ :as io]
            [clojure.string :as string]))

(def input (-> "public/puzzle_inputs/day_five.txt"
               io/resource
               slurp
               string/split-lines))

(def crates (take 8 input))

(def instructions (map (comp #(map parse-long %)
                             #(re-seq #"\d+" %))
                       (drop 10 input)))

(def full-stacks (atom (->> crates
                            reverse
                            (apply map vector)
                            (filter (fn [[a b c]] (not= a b c)))
                            (map #(remove #{\space} %))
                            (map vec)
                            (into []))))

(defn move-crates [coll params]
  (loop [coll coll
         [qty from to] params]
    (if (<= 0 qty)
      (let [from-val (get-in coll [from])
            to-val (update-in coll [to] conj (peek from-val))
            safe-pop (fn [v] (if (empty? v) v (pop v)))]
        (recur (update-in to-val [from] safe-pop) [(dec qty) from to]))
      coll)))

(comment
  (doseq [ins instructions]
    (swap! full-stacks move-crates ins))
  @full-stacks
  (apply str (map (comp last #(filter some? %)) @full-stacks)))

jumar19:12:20

I don’t have time to debug this but I suggest getting rid of the atom and trying it on the sample input first

👌 1
nbardiuk19:12:25

Oh, I had this bug also! The commands to move crates use 1 based indexes but Clojure collections are 0 based. I don't see any place in your code to decrement from and to

Ben Lieberman19:12:44

Oh good point @U076FM90B! I accounted for this earlier and then changed my code and forgot to update that part 😅

👍 1
pithyless20:12:18

I had a bug in today's code (in my defense, AOC comes out at 6am in my timezone... before ). Since I was using reduce to go through all the moves one-by-one and update the current stacks, it was really easy to replace reduce with reductions and tap> all of the individual steps to portal (where I could just view them in a table and see where the unexpected behavior happened). Doesn't answer your question directly, but perhaps it may help give you some ideas for the future (it is related to what @jumar wrote about avoiding doseq and swap! if you want an easier time debugging later).

metal 2
Ben Lieberman20:12:45

That's a good tip, thanks @U05476190! I didn't even know about reductions until now.

wevrem21:12:06

Another potential gotcha with the crates: the input lines are not all equal length, so the transpose might cut off some rows/columns.

Ben Lieberman21:12:30

Yeah, I was thinking about that initially but using (apply map vector) in my processing pipeline helped. The problem with my code was the off-by-one issue.

Joe Dumont00:12:01

@U05476190 Whoa! I like the sound of using tap> to send the steps to portal. I just caught an intro to portal from the ds4clj group but haven't played with it yet. Would you mind sharing how you have it setup? EDIT: I see now from the portal README, (add-tap #'p/submit) ; Add portal as a tap> target then (tap> x)

pithyless05:12:03

@U03KMPFU4TH 👍 Some use nrepl middleware to pipe all evals to portal, but I prefer using portal just via tap. It's also helpful to hook up editor keybindings to run the taps for you, eg:

(tap> *1) ;; last result

(tap> *e) ;; last error

pithyless05:12:17

I've even got this hack in my emacs config for piping tests to portal (maybe nowadays there is a better way, but it still works for me):

(defun my/cider-portal-test-ns ()
   (interactive)
   (when-let ((ns (clojure-find-ns)))
     (cider-nrepl-sync-request:eval
      (concat
       "(let [report (atom [])]"
       "  (tap> ['" ns " report])"
       "  (with-redefs [clojure.test/report #(swap! report conj %)]"
       "  (clojure.test/run-tests '" ns " )))"))))

🙌 1
pithyless20:12:18

I had a bug in today's code (in my defense, AOC comes out at 6am in my timezone... before ). Since I was using reduce to go through all the moves one-by-one and update the current stacks, it was really easy to replace reduce with reductions and tap> all of the individual steps to portal (where I could just view them in a table and see where the unexpected behavior happened). Doesn't answer your question directly, but perhaps it may help give you some ideas for the future (it is related to what @jumar wrote about avoiding doseq and swap! if you want an easier time debugging later).

metal 2
nooga20:12:00

hey @borkdude, after your suggestion, I've just added reader conditionals to let-go - will try to update my solutions to run both on lg an bb

metal 1
borkdude20:12:49

exciting :)

nooga20:12:18

I'm having a blast 🙂 lg is still extremely barebones but it's surprisingly workable for what it is at the moment

borkdude20:12:26

I have the same state with #C03QZH5PG6M and #C03U8L2NXNC: it's doable but some things are obviously missing

borkdude20:12:46

does lg support interop on go stuff already?

nooga20:12:38

it does, as in it can move values from go to lg and back, convert them, call methods on things etc. it even supports moving entire functions and channels between two worlds

nooga20:12:13

however, the API for this is not entirely fleshed out, I'm still experimenting on how to make it super comfy

nooga20:12:37

here's an example of calling methods on a Go http response https://github.com/nooga/let-go/blob/main/examples/server.lg here are some lousy mini tests for calling lg from Go https://github.com/nooga/let-go/blob/main/pkg/api/interop_test.go

👍 1
nooga20:12:36

in the first example that fn is actually passed to some Go function as a callback

nooga21:12:58

btw, I've made a channel: #C04DCFZKPJN 😉

zamansky20:12:10

Since people are sharing how they parsed the input, here's what I did to go from the graphical tower to my map:

(defn make-towers
  "Turn the text of the towers into a dictionary
  keys are tower names, list of vectors at each entry"
  [raw-stacks]
  (->> (str/split-lines raw-stacks)
       (apply mapv vector) ;; transpose the "matrix"
       (mapv reverse) ;; reverse them so that the tower names are  left
       (filterv (fn [x] (not= (first x) \space))) ;; remove non tower rows
       (mapv (fn [x] (filterv #(not= % \space) x))) ;; trim trailing space

       ;; turn list of lists into a dictionary
       (reduce (fn [acc next]
                 (assoc acc (first next) (into [] (rest next)))) {})))
Solution shared in the solutions thread.

clojure-spin 3
metal 3
😁 2
Luis Santos21:12:15

Similar to mine:

(defn build-stacks [i]
  (->> (string/split-lines i)
       (map #(re-seq #"\[\w\]|    |[0-9]" %))
       (take-while (complement nil?))
       (transpose)
       (map reverse)
       (map #(remove string/blank? %))
       (reduce #(assoc %1 (first %2) (rest %2)) (sorted-map))))

👍 1
CarnunMP21:12:50

I packed things straight into a vector, which is effectively a map from 0-based index to value... and generally simplifies everything downstream. :)) https://clojurians.slack.com/archives/C0GLTDB2T/p1670276617404109?thread_ts=1670219051.953159&amp;cid=C0GLTDB2T

👍 1
CarnunMP21:12:58

what's transpose @UK00KCK6D?

genmeblog22:12:06

Here is mine. stacks contains lines of the first part.

(defn parse-stacks-line
  [line]
  (->> line rest (take-nth 4)))

(defn parse-instructions
  [line]
  (->> (re-seq #"\d+" line)
       (map (comp dec read-string))))

(defn parse
  [[stacks instructions]]
  {:stacks (->> (butlast stacks) ;; drop stack numbers, bottom line
                (map parse-stacks-line) ;; get crates 
                (apply map list) ;; transpose and keep in the list (stack)
                (mapv #(remove #{\space} %))) ;; remove empty spaces
   :instructions (map parse-instructions instructions)})

👍 1
peterh22:12:16

I somehow always forget that you can map through multiple seqs for transposition, so I used a different approach where I attached the stack index to each letter row by row as a key so that I could merge each row-map to a single map of concatenated items.

(defn parse-stacks [input]
  (let [uppercase   (set (map char (range (int \A) (inc (int \Z)))))
        parse-crate (fn [i xs]
                      (let [crate-id (filter uppercase xs)]
                        (when-not (empty? crate-id)
                          [(inc i) crate-id])))]
    (->> input
         (map (comp (partial into {}) ;; each row becomes a map: {1 \N, 2 \C}
                    (partial map-indexed parse-crate) ;; parses the letter along with its position
                    (partial partition-all 4))) ;; two crates are always 4 chars apart
         (apply merge-with concat))))

Luis Santos12:12:38

@U018D6NKRA4

(defn transpose [xs]
  (apply map list xs))
Converts rows into columns.

👍 1
wevrem21:12:37

This approach doesn’t work on my input, @zamansky, because the input lines are not all the same length (the last stack is shorter than some of those in the middle), and the transpose step truncates to the shortest.

R.A. Porter21:12:55

You can pad your short stacks first. That should get you over that hump.

wevrem21:12:03

Ah, but should that padding happen as a manual edit on the input file? I think it should be accounted for in the code. That was my point.

R.A. Porter21:12:40

Yeah. In code. You’d need to update that sample code between the first line of the threading macro and the reduce fn.

bhauman22:12:06

yeah I don’t think my solution above would work either.

wevrem23:12:37

I might need to be more careful saving my input files. The original file does have spaces padding out the stack drawing. But saving that in my editor got rid of trailing spaces. Meanwhile, the whole thing prompted me to create a handy transpose method that will watch for and pad shorter lines. https://github.com/wevre/advent-of-code/blob/e27c48292592a31619cc70e355e6e10784000be5/src/advent_of_code/common.clj#L24

bhauman23:12:08

that makes sense

bhauman22:12:30

hmmm this makes me want to try a different solution, working it

jurjanpaul22:12:07

Anybody else solving the puzzles in Clojure(Script) entirely on their mobile phone? I guess not. And I wouldn’t recommend it. 🙂 But I find it’s the only way for me to keep up on busy days, particularly celebrating the great tradition of Sinterklaas in The Netherlands today! (Laptop time is reserved for work and other important stuff, like writing Sinterklaas rhymes, which is great fun actually.) So I’m pleased that despite some quirks and annoyances (that I really need to dive into someday, mainly with respect to the cursor position jumping out of focus for no good reason), the Scittle based ”‘Away from Preferred Editor’ ClojureScript Playground” (a static HTML page gluing together Scittle, Reagent, CodeMirror, Parinfer and localStorage) at https://jurjanpaul.github.io/ape-cljs-playground allows me to finish this year’s puzzles on the same day they come out (so far, but not much longer I expect, which is fine). Just paste the input into a fresh buffer and go… 🙂 In case anybody knows of a more convenient mobile Clojure experience, I’d be eager to try it though (but suspicious of anything that doesn’t support Parinfer to be honest).

🤯 1
🚀 1
jurjanpaul09:12:57

Ultimately I was able to finish the first 15 puzzles on the day they came out, entirely edited and run on my phone with Scittle. Got stuck on number 16, part 1, with a typical case of not being able to figure out why my obviously correct solution laughcry does not get accepted by the AoC software. Unfortunately, I have not yet been able to figure out how to fix the editor’s focus issue either; CodeMirror 5 provides a scrollIntoView method that still manages to scroll the cursor under the iPhone’s keyboard. 🙂 Including a margin helps, but I am still looking for a recipe for figuring out an appropriate value (that would work both for portrait and for landscape phone holding… for any zoom level, on any device anywhere… :thinking_face:).

borkdude23:12:29

@jurjanpaul502 Don't know if it's better since this doesn't have parinfer yet, but it uses nextjournal/clojure-mode directly from npm, in the scittle-spirit of having no build step: https://babashka.org/scittle/codemirror.html

🎉 1
borkdude23:12:46

This editor does support the barfing/slurping stuff: https://nextjournal.github.io/clojure-mode

jurjanpaul08:12:58

Thank you @borkdude! That’s a great showcase for Scittle and nextjournal/clojure-mode. I gave it a try in my phone’s browser, but I don’t find it to be mobile-friendly (or meant to be 🙂). The slurping and barfing key chords are simply unavailable on a mobile phone’s browser keyboard (or: what am I missing?). The lack of on-page stdout is fixable of course, as is the lack of explicit storage (the browser remembers the page’s state for a while anyway, until … I make it hang due to an inefficient algorithm. 🙂) CodeMirror 6 itself probably provides the improvements (over 5) that I am looking for though. Perhaps it is worth trying to use that (despite the lack of a working Parinfer extension; if only I had enough time to spare to try my hand…) and balance my braces myself after all. 🙂

borkdude09:12:32

The one from npm doesn't support barfing and slurping, but perhaps there can be a pre-made scittle plugin to facilitate this. Then it would be more like this here: https://nextjournal.github.io/clojure-mode/ cc @U5H74UNSF

jurjanpaul09:12:02

'Unavailable' as in: you simply can't enter those key chords in a standard mobile browser keyboard, surely?

borkdude09:12:26

oh right, also that :)

borkdude09:12:41

yeah, I can see why parinfer is nice for that

jurjanpaul09:12:29

🙂 Well, I would love for someone or some team to do it better.

borkdude09:12:52

Having a code editor like ape is certainly one of the things that people would like to do with scittle so perhaps a nextjournal/clojure-code + parinfer thing for mobile kind of plugin could work some day