adventofcode

2022-12-05T05:44:11.953159Z

Day 5 - Solutions

dumrat 2022-12-22T19:04:35.287969Z

(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))

dumrat 2022-12-22T19:04:44.959159Z

Not cute 😄

pwojnowski 2022-12-06T08:15:25.800269Z

Parsing the input became so involved: https://github.com/pwojnowski/advent-of-code/blob/master/src/aoc/aoc2022.clj#L102

jaihindhreddy 2022-12-06T11:20:39.841699Z

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'))))))]))

pwojnowski 2022-12-06T12:40:54.123279Z

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

2022-12-06T12:41:08.152239Z

Gpt3

pwojnowski 2022-12-06T12:41:32.407769Z

Thats's why I wrote "people" 😉

pwojnowski 2022-12-06T12:42:20.314669Z

With gpt3 the advent became so depressing... 😭

2022-12-06T12:43:00.891899Z

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

pwojnowski 2022-12-06T12:43:33.216459Z

This is sad, but it appears so.

Thierry 2022-12-07T10:06:45.902729Z

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

mchampine 2022-12-05T05:56:20.929939Z

;; 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"

Apple 2022-12-05T06:18:30.284809Z

(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
2022-12-05T06:35:57.009689Z

(Could use some cleanup 😄)

motform 2022-12-05T08:27:59.583169Z

Let's see the input get even more strange and specific tomorrow https://github.com/motform/advent-of-clojure/blob/master/src/advent_of_clojure/2022/05.clj

genmeblog 2022-12-05T09:34:18.473199Z

TIL: re-seq

Casey 2022-12-05T10:18:36.252469Z

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.

2022-12-05T13:32:37.511699Z

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
2022-12-05T13:52:06.648769Z

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

erwinrooijakkers 2022-12-05T14:12:21.854649Z

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

2022-12-05T14:13:17.124289Z

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

erwinrooijakkers 2022-12-05T14:13:29.681649Z

ah thanks you’re right 🙂

nbardiuk 2022-12-05T14:15:01.333779Z

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

nooga 2022-12-05T14:21:21.132299Z

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
Benjamin 2022-12-05T14:22:11.154229Z

https://github.com/benjamin-asdf/advent-of-code/blob/master/src/Y2022/day5.clj barely got my stars

2022-12-05T15:00:08.286269Z

Oh boy, I am NOT proud of my solution today

2022-12-05T15:05:09.321529Z

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.

R.A. Porter 2022-12-05T15:40:01.608509Z

I could/should consider rewriting my parsing code. I won't. https://coyotesqrl.github.io/advent-of-code/2022/src/coyotesqrl/2022/day05.html

robertfw 2022-12-05T19:54:04.304969Z

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

nooga 2022-12-05T19:56:55.236359Z

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. 😆

1
2022-12-05T20:45:43.304769Z

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 Santos 2022-12-05T21:11:29.998729Z

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)))

peterh 2022-12-05T21:29:22.645569Z

Not the most elegant solution, but I learned things today! https://github.clerk.garden/formsandlines/aoc2022-clojure/commit/d32c6ea4a4c8b1d7daf8803e2ce5e91e3154f6b3/src/advent_of_clerk/day_05.html

carnundotcom 2022-12-05T21:43:37.404109Z

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. :))

2022-12-05T22:02:40.113209Z

(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))))))

gabo 2022-12-05T23:23:45.096879Z

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ä 2022-12-06T06:58:35.949089Z

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

mbjarland 2022-12-09T09:41:28.577529Z

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

mbjarland 2022-12-08T14:45:05.666969Z

(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))))

👍 2
1
1
leinfink 2022-12-09T00:55:36.270699Z

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

2022-12-05T13:50:46.976929Z

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.

6
2022-12-05T13:51:22.001119Z

So many brilliant solutions.

ray 2022-12-05T16:48:37.820809Z

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?

borkdude 2022-12-05T16:55:39.133009Z

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

ray 2022-12-05T17:09:49.498699Z

https://aoc-rmc.github.io/aoc22/

borkdude 2022-12-05T17:11:19.361869Z

awesome!

ray 2022-12-05T16:57:03.481019Z

haha - I know

mkvlr 2022-12-05T16:58:28.611569Z

maybe try clerk.garden to publish your scittle stuff 🙃

ray 2022-12-05T20:17:52.174659Z

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

mkvlr 2022-12-05T20:46:45.892199Z

maybe next year

👍🏼 1
bhauman 2022-12-05T16:57:48.872829Z

OK let’s talk parsing again

bhauman 2022-12-05T16:59:34.032459Z

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

bhauman 2022-12-05T17:00:38.629619Z

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 %))))

bhauman 2022-12-05T17:02:15.515869Z

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)))

bhauman 2022-12-05T17:02:56.784209Z

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

2022-12-05T17:15:45.941459Z

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

bhauman 2022-12-05T17:16:52.129879Z

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

bhauman 2022-12-05T17:47:01.340629Z

I love me some dangerous read-string 👹

🔥 1
borkdude 2022-12-05T19:04:49.034609Z

😂

😂 1
borkdude 2022-12-05T19:06:16.214409Z

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

➕ 1
bhauman 2022-12-05T19:18:24.548339Z

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

bhauman 2022-12-05T19:19:32.837569Z

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

2022-12-05T19:20:32.474619Z

another soul saved!

bhauman 2022-12-05T19:21:04.049869Z

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

borkdude 2022-12-05T19:23:44.886689Z

hehe

pez 2022-12-05T19:45:33.323119Z

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.

borkdude 2022-12-05T19:46:51.483229Z

load-string to the rescue :P

bhauman 2022-12-05T17:07:56.221779Z

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

nooga 2022-12-05T17:14:40.145159Z

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

bhauman 2022-12-05T17:38:30.680379Z

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

bhauman 2022-12-05T17:42:45.200429Z

Yeah it’s the transpose that seems important here 🙂

bhauman 2022-12-05T17:44:23.869939Z

Yeah definitely like your solution here

nooga 2022-12-05T17:44:32.937909Z

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)]

bhauman 2022-12-05T17:37:07.245909Z

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)))

liebs 2022-12-05T19:31:31.154859Z

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 🧵

liebs 2022-12-05T19:32:03.767719Z

(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)))

jumar 2022-12-05T19:34:20.839829Z

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
nbardiuk 2022-12-05T19:44:25.371659Z

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

liebs 2022-12-05T19:45:44.402419Z

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

👍 1
pithyless 2022-12-05T20:05:18.242139Z

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).

3
liebs 2022-12-05T20:10:45.503449Z

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

wevrem 2022-12-05T21:35:06.273229Z

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

liebs 2022-12-05T21:37:30.698919Z

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 Dumont 2022-12-06T00:27:01.497469Z

@pithyless 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)

pithyless 2022-12-06T05:33:03.651839Z

@dumont 👍 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

pithyless 2022-12-06T05:35:17.532549Z

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
nooga 2022-12-05T20:32:00.941089Z

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

1
borkdude 2022-12-05T20:43:49.154669Z

exciting :)

nooga 2022-12-05T20:45:18.936479Z

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

borkdude 2022-12-05T20:47:26.899289Z

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

borkdude 2022-12-05T20:47:46.615329Z

does lg support interop on go stuff already?

nooga 2022-12-05T20:50:38.817289Z

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

nooga 2022-12-05T20:51:13.470089Z

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

nooga 2022-12-05T20:53:37.406419Z

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
nooga 2022-12-05T20:57:36.917959Z

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

nooga 2022-12-05T21:00:58.961079Z

btw, I've made a channel: #let-go 😉

2022-12-05T20:45:10.326639Z

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.

3
😁 2
3
Luis Santos 2022-12-12T12:18:38.825609Z

@carnunmp

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

👍 1
Luis Santos 2022-12-05T21:15:15.161839Z

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
carnundotcom 2022-12-05T21:46:50.396999Z

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
carnundotcom 2022-12-05T21:49:58.539079Z

what's transpose @luis559?

genmeblog 2022-12-05T22:15:06.160479Z

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
peterh 2022-12-05T22:19:16.634199Z

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))))

wevrem 2022-12-05T21:41:37.834829Z

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. Porter 2022-12-05T21:42:55.889599Z

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

wevrem 2022-12-05T21:50:03.122419Z

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. Porter 2022-12-05T21:51:40.067929Z

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

bhauman 2022-12-05T22:33:06.285439Z

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

wevrem 2022-12-05T23:02:37.785159Z

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

bhauman 2022-12-05T23:07:08.978059Z

that makes sense

bhauman 2022-12-05T22:34:30.194289Z

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

jurjanpaul 2022-12-05T22:58:07.457259Z

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
jurjanpaul 2022-12-17T09:56:57.160919Z

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… 🤔).

borkdude 2022-12-05T23:03:29.562799Z

@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
jurjanpaul 2022-12-06T08:30:58.360309Z

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. 🙂

borkdude 2022-12-06T09:33:32.036229Z

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 @mkvlr

jurjanpaul 2022-12-06T09:37:02.910699Z

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

borkdude 2022-12-06T09:37:26.268959Z

oh right, also that :)

borkdude 2022-12-06T09:37:41.947709Z

yeah, I can see why parinfer is nice for that

borkdude 2022-12-06T09:38:01.090309Z

ape ftw!

jurjanpaul 2022-12-06T09:41:29.463549Z

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

borkdude 2022-12-06T09:43:52.531739Z

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

borkdude 2022-12-05T23:17:46.585189Z

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