This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-12-01
Channels
- # adventofcode (170)
- # announcements (3)
- # babashka (1)
- # beginners (25)
- # cherry (1)
- # cider (3)
- # clj-kondo (5)
- # cljsrn (9)
- # clojure (27)
- # clojure-art (2)
- # clojure-europe (11)
- # clojure-nl (1)
- # clojure-norway (26)
- # clojure-sweden (2)
- # clojure-uk (5)
- # code-reviews (12)
- # component (8)
- # conjure (1)
- # data-science (2)
- # hyperfiddle (6)
- # malli (5)
- # off-topic (65)
- # overtone (34)
- # polylith (3)
- # re-frame (2)
- # reagent (2)
- # releases (3)
- # rum (1)
- # shadow-cljs (2)
- # slack-help (8)
- # sql (8)
- # squint (100)
- # thejaloniki (3)
- # tools-build (16)
- # vim (7)
- # yamlscript (1)
https://github.com/nbardiuk/adventofcode/blob/master/2023/src/day01.clj Had to google how to make overlapping regex
https://github.com/samcf/advent-of-code/blob/main/2023-01-trebuchet.clj
Got frustrated with regex and resorted to index-of
and last-index-of
re-seq
+ (juxt first last)
to the rescue 😉
https://github.com/vollcheck/aoc/blob/master/src/y23/day01.clj
first day first problems with understanding of the task... anyway, the solution: https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2023/day01.clj
Here's what happens when you are dumb and don't know about overlapping regexes https://gist.github.com/alexander-yakushev/06485fe945e30691a1f4d0c87be1f4d9
lol I gave up on regex as well: https://github.com/potetm/advent-of-code/blob/b77255eb3e3111274c5438189ba0a2ccbbe6eee4/src/advent_2023/day_1.clj
It never occurred to me until now to put an entire regex into a zero-width lookahead/lookbehind.
I like the pragmatic idiot-solution driven approach instead of trying to be elegant today ;)
in total anger, my first pass was munging the number-words to duplicate the last character so I could destructively replace. “eight” -> “eightt”
second pass looks almost identical to others here. https://github.com/tschady/advent-of-code/blob/main/src/aoc/2023/d01.clj
@U1Z392WMQ Same sentiment, really. I got irrationally angry over AoC today. I rallied up a bunch of friends for it this year and promised that it's gonna start easy, and the very first day is such a newbie-destroyer.
do you think it's the Eric way of defending against the continuously rising AI tooling?
Well, to be honest, this is what you can expect from the rest of AoC, there's always an angry throw-keyboard-out-of-window thing in there ;)
Yeah, it's usually like that, and it's all fine and dandy. Just pulling that out day 1 will shoot a lot of unfamiliar folks down.
Today task is easy for an approach when one iterates over indexes and check if there is a word at that position. I've noticed that Jonathan Paulson was lucky and didn't even notice edge cases https://www.youtube.com/watch?v=rnidYOt9m2o
yup, I guess we (me for sure) got kinda tricked by the Clojure that encourages to treat everything as a collection to apply functions over in compare to Python when you have more imperative way of thinking
https://github.com/jantorestolsvik/advent-of-code-2023/blob/main/src/day01.clj
Oh I didn't even think about this edge case:
blah7foo is 77
but I guess it just worked because I did first and last on a vector (with a single element)ended up getting frustrated with the tricky eightwo
case and ended up replacing all the words with the digit wrapped in words 😅 eight8eight
https://github.com/muthuishere/advent-of-code-clojure-2023. Did a livestream as well https://www.youtube.com/watch?v=15DdYethJHg. Part2 is definitely not a day 1 problem !!! https://www.youtube.com/watch?v=q60gepcBaZs My code No regex , a plain stuff
(def text-digits ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"])
(defn is-string-a-number [inp]
(not= nil (parse-long inp)))
(defn text-to-number [input]
(str (inc (.indexOf text-digits input))))
(defn get-position-info-at-index [index input text-digit]
(if (str/index-of input text-digit index)
{:text-digit text-digit :index (str/index-of input text-digit index) :digit-to-be-replaced (text-to-number text-digit) }
nil))
(defn get-position-info [input text-digit]
(let [first-index (str/index-of input text-digit)
last-index (str/last-index-of input text-digit)]
(if (not= first-index last-index)
[(get-position-info-at-index 0 input text-digit) (get-position-info-at-index (dec last-index) input text-digit)]
[(get-position-info-at-index 0 input text-digit)])))
(defn get-all-position-data [currentline]
(->> text-digits
(map #(get-position-info currentline %1))
(flatten)
(filter not-empty)
(sort-by :index #(compare %2 %1))))
(defn replace-input-with[input position-info]
(let [index (get position-info :index)
digit-to-be-replaced (get position-info :digit-to-be-replaced)]
(str (.substring input 0 index) digit-to-be-replaced (.substring input (inc index) ))))
(defn convert-text-digit-to-normal-digit [input]
(reduce #(replace-input-with %1 %2)
input (get-all-position-data input)))
(defn first-and-last [inp]
(let [first-one (first inp)
last-one (last inp)]
[first-one last-one]))
(defn calibrate [input]
(->> input
(convert-text-digit-to-normal-digit)
(map str)
(filter is-string-a-number)
(first-and-last)
(reduce str)
(parse-long)))
(defn read-as-array-from-classpath [filename]
(->> filename
(io/resource)
(slurp)
(str/split-lines)))
(defn find-total-calibration-value-in [filename]
(->> filename
(read-as-array-from-classpath)
(map calibrate)
(filter #(some? %1))
(reduce +)))
(comment
(find-total-calibration-value-in "original.txt")
(find-total-calibration-value-in "sample.txt"))
I'm more annoyed by having to hunt edge cases in a thousand-line file. Including it in the examples in a less subtle way would've been nice 🙂
kinda curious, did any of you figure out the overlapping regexes from the explanation or from trying it out ?
for me it was running the code on the second sample and then double checking why the answer was out
whenever I hit these problems I scrutinize the examples, caught eightwothree
eventually.
yeah, I was thrown off by the sixteen
but then searched my input for teen
but didn't see anything. Then re-read the instructions finally 😄
I need to brush up on my regex. Got lazy and did some silly string reverse regex to get the answer to #2, so I could go to bed.
I'm relieved to see other people finding the lookaround regex solution a pain 😅
@j4m3s I got a pretty good hint because I originally had a used map to string replace the strings with digits. Because the map was in random order, I got lucky and saw the eigh2. If the map had had eight first, it wouldn’t have been so intuitive that the same problem was happening at the end on my full dataset. I had it passing the example data, but not my full data.
This time I imagined the overlap edge case as soon as I read part 2, before encountering any evidence of its occurrence. Experience, I guess. I was lucky to quickly find an example of expressing the regex for overlapping matches, which I definitely did not know by heart. TIL (?=...)
. Only later did I think of the sensible reverse regex approach.
I just used clojure.string/replace
with a list of potential overlaps coming first in the |
list in the regex, because I don't get overlapping regexes.
I only did that once I realised that the overlapping edge-case was being enforced.
I thought it was a pretty crappy thing to not explain explicitly
Like this:
(defn numbers-as-text-to-digits
"Convert text expressions of numbers 1-9 into string digits"
[input-string]
(str/replace
input-string
#"oneight|threeight|fiveight|nineight|twone|eightwo|eighthree|sevenine|one|two|three|four|five|six|seven|eight|nine"
{"oneight" "18"
"threeeight" "38"
"fiveight" "58"
"nineight" "98"
"twone" "21"
"eightwo" "82"
"eighthree" "83"
"sevenine" "79"
"one" "1"
"two" "2"
"three" "3"
"four" "4"
"five" "5"
"six" "6"
"seven" "7"
"eight" "8"
"nine" "9"}))
I accept that it only "works" because there are a very limited number of overlaps that are possible because of how the words for numbers are spelled,
Also, I've just spotted a typo that means that threeight is not one of the overlaps in the input data as I got the correct answer despite matching on threeeight (incorrectly)
what about triplets like eightwone
? Or even fiveightwone
? Maybe they don’t show in the input data?
What @U04V15CAJ said 🙂
FWIW @U04V15CAJ, I ❤️ your part1 digit sieve, using keep
and parse-long
Mine was similar but far less beautiful
what?!! didn't know overlapping regex was possible... all I could think of was regex with a reversed string (re-seq #"\d|eno|owt|eerht|ruof|evif|xis|neves|thgie|enin")
:rolling_on_the_floor_laughing:
@U0355S1RV60 - Please go into detail, I wish to learn and what you said there ^^ makes no sense to me 🙂
like eightwothree
you only need first and last, so you could match first string from the beginning with exact string, and same from the end of the string ?
One of the most interesting approaches I've seen today was replacing one
with o1e
, two
with t2o
and so on. Then just extract digits.
The problem with overlaps between just two words for numbers is that there are a handful that share a letter and because the match on the first wipes out the match on the second you have to match twone and eighthree and so on as separate options in the regex
the numbers aren't exactly at the end or beginning, so you have to compensate for that somehow
@U1EP3BZ3Q I saw that on a Python-based solutions thread after I'd finished (was looking around to see how people had done it out of curiosity) and I couldn't figure out how that would handle two word overlaps.
the first puzzle drives you to regex and then the second puzzle punishes you for choosing that direction, very AOC
@U0355S1RV60 - i see what you mean, but an OR regex in the right order with a list of replacements inside clojure.string/replace
is so convenient 🙂
I am also trying to record a video for each day. I have to say, I really struggled with the overlapping numbers 😬 https://youtu.be/7zD4OYcqaWI
@U04V15CAJ - I agree, but having Clojure to hand i didn't go near regex for part1
@U08ABGP70, spotted your string/replace solution and had my mind blown 😄
@U08ABGP70 when you replace, both ends are still in the string and the next substitution works. oneightwo
becomes o1ei8ght2o
also, means you can do it in place and not allocate memory 😮 (although, not very clojurey)
I like that that approach works, but I think @U08ABGP70 way of replacing is easier to understand when reading the code. But again something I would not have thought of
I'm thinking also about recording a videos of that, but from the post-solution analysis perspective - do you think that might be interesting?
@U03N5HN4K1N - I would say try it and find out..? I think that there is enough interest overall in AoC and it's a good way to get Clojure coolness out there
Agreed! Go for it! I'd like to see a video that collected different approaches. I have learned about two different approaches by just reading this thread.
It would be interesting to see which approach is most performant, but also which one is easiest to look at, parse, and understand, and what that trade-off costs.
thank you people for the motivation and ideas! I will try to compile some stuff on the evening
Here is my full solution in case you would like to share it: https://gist.github.com/maleghast/9bc7cd4851d0563de9686394b1debe37
Mine using a regex look-ahead can be found here: https://github.com/JDurstberger/aoc-2023-clj/blob/main/src/day_1/core_2.clj
I honestly still don't understand them fully. I got it to work in that situation, but I am not certain I could correctly use it in other cases.
Scratch that. It actually just clicked.
To be fair, it does feel quite hacky now that I understand how it works 😕
I made this (brute-force) overlapping re-seq because googling for this look-ahead syntax took too long ;)
(defn match-overlapping
"re-seq with overlap"
[re s]
(loop [matches []
s s]
(if-let [match (re-find re s)]
(recur
(conj matches match)
(subs s (inc (.indexOf s match) 2)))
(vec matches))))
Here's my reversed regex solution: https://github.com/rmrt1n/advent-of-code-2023-clj/blob/main/src/aoc/day01.clj
@U05PEDP7J85 Beautiful, I guess one could unduplicate that regex pattern with str/reverse and use re-pattern as well
@U05PEDP7J85 that is brilliant! I wonder if you could extract the regex-pattern string before making it a regex and reverse it too so you don't need the eno|owt|...
borkdude beat me to it
Yeah, didn't think about that earlier. Was just focused on getting a correct answer
I think you can use clojure.pprint/cl-format to get the human names for numbers too, and then compose the regex from that ;)
cl-format
is such a great addition to the language, I'm doing this year's AOC in CL and I'm learning the ropes of format
there and wow, what a tool
(solution in basilisp - a clojure compatible dialect for python) https://github.com/ikappaki/aoc23-basilisp/blob/master/day1.clj
@U012BL6D9GE nice, never heard of basilisp!
After reading this thread, I did a version 2 with reversed string and reversed regex which I like better than my original version with “fancy” regex. https://github.com/wevre/advent-of-code/blob/master/src/advent_of_code/2023/day_01_trebuchet_v2.clj
(@U04V15CAJ I've recently ported the https://github.com/basilisp-lang/basilisp/pull/723 to it, I was waiting for the basilisp author to review it before contacting you with regards to copyright and credits. It's amazing how much effort he has put to it. I've been using it for a while and thought to put it on the test with AoC)
@U012BL6D9GE awesome :)
https://github.com/chase-lambert/aoc-clojure/blob/main/src/aoc/2023/day_01.clj holy schnikeys, was not expecting Part 2 to hit that hard on Day 1. I finally looked up how to do overlapping regexes when I figured out why the example data was passing but not the real stuff. My solution is ugly and probably horrifically inefficient as of now. I may have talked a couple of new programmers into trying AOC with promises the first few days are easy. lol oops
Part 2 of this was so hard for me being a clj beginner who doesn't like regex, I just cheated by looking at solutions here. What a day 1. I feel the overlap thing ought to be possible to do with seq and stuffs.
Interestingly, the imperative folks don’t seem to be having as much trouble. Their first instinct is to index everything, while our first instinct to first/last leads us astray, I suspect?
Yea there's lots of talk (her and HN) about it being hard but I feel like it wasn't that difficult if you didn't try to use regex for part 2.
Of course saying it's easy or not easy (overall ) is a hard judgement to make, as one is clouded by their experience
some implementations accidentally picked the right arbitrary overlap rule, some did not
@U1EP3BZ3Q’s two
-> t2o
is my kind of solution, your yearly reminder to punch AOC input until it quacks.
@U70QFSCG2 I think the hard part is realizing that overlaps can happen. even when I did notice it I missed that you can't just replace the first occurrence, reverse the string and do the same to find the last one, because if you have simply twone
it becomes 2ne
Dodging regex until I die. eightwothree
becomes eight8eighttwo2twothree3three
.
(def literal->digit
[["one" "1"]
["two" "2"]
["three" "3"]
["four" "4"]
["five" "5"]
["six" "6"]
["seven" "7"]
["eight" "8"]
["nine" "9"]])
(def star2 "54591"
(problem1 (reduce (fn [s [literal digit]]
(string/join
(str literal digit literal)
(string/split s (re-pattern literal))))
input
literal->digit)))
my solutions using squint https://gist.github.com/corasaurus-hex/c5e4a9e31b36a5b1bedabc4778309d4c
some of it is probably suboptimal
@U02N27RK69K yay! wanna try out the squint playground? https://clojurians.slack.com/archives/C0GLTDB2T/p1701426406868059?thread_ts=1701414970.950789&cid=C0GLTDB2T
(def name->num {"one" 1
"two" 2
"three" 3
"four" 4
"five" 5
"six" 6
"seven" 7
"eight" 8
"nine" 9
"1" 1
"2" 2
"3" 3
"4" 4
"5" 5
"6" 6
"7" 7
"8" 8
"9" 9})
(defn starts-with-num [s]
(loop [ss s]
(or (some (fn [[k v]]
(when (clojure.string/starts-with? ss k)
v))
name->num)
(recur (subs ss 1)))))
(defn ends-with-num [s]
(loop [ss s]
(or (some (fn [[k v]]
(when (clojure.string/ends-with? ss k)
v))
name->num)
(recur (subs ss 0 (dec (count ss)))))))
(defn cal-val [input]
(->> input
((juxt starts-with-num ends-with-num))
(apply str)
read-string))
(-> "aoc/day1input.txt"
slurp
(clojure.string/split #"\n")
(->> (map cal-val)
(reduce +)))
my solution after figuring out the annoying 'regex' issue:
(ns advent-of-code.day-one
(:require [clojure.string :as s]))
(defn part-one
[]
(apply + (map (fn [line]
(let [numbers (re-seq #"\d" line)
num (str (first numbers)
(last numbers))]
(try
(Integer/parseInt num)
(catch Exception e
(println "that's what you get for impurity")
0))))
(line-seq ( "./input/day1.txt")))))
(defn part-two
[]
(apply + (map (fn [line]
(let [lookup {"one" "1" "two" "2" "three" "3" "four" "4"
"five" "5" "six" "6" "seven" "7" "eight" "8" "nine" "9"}
numbers (->> line
(re-seq #"(?=(\d|one|two|three|four|five|six|seven|eight|nine))")
(map second)
(map #(get lookup % %)))
num (str (first numbers)
(last numbers))]
(try
(Integer/parseInt num)
(catch Exception e
(println "that's what you get for impurity")))))
(line-seq ( "./input/day1.txt")))))
BTW, Clojure 1.11 added parse-long
that can be used instead of Integer/parseInt
.
@U06PNK4HG I couldn't remember what it was called. lol
I know. haha
(def digit-strings
["one" "two" "three" "four"
"five" "six" "seven" "eight" "nine"
"1" "2" "3" "4" "5"
"6" "7" "8" "9"])
(->> digit-strings
(map #(vector (str/index-of "xtwone3four" %) %))
(filter first)
(into (sorted-map))); => {1 "two", 3 "one", 6 "3", 7 "four"}
pseudo-edit: now if only index-of returned the indexes of all occurences :face_with_rolling_eyes:Newb solution to part2 (part1 fell victim to my edit history): https://gist.github.com/mrnhrd/a544b725b865015ec88c9f1c10cad718 (thanks @UTFAPNRPT)
That was a bit harder for me than day 1 usually is.
(defn part-1 [input]
(->> input
(map (fn [line]
[(re-find #"(?<=^\D*)\d" line)
(re-find #"\d(?=\D*$)" line)]))
(map #(apply str %))
(map edn/read-string)
(apply +)))
(def spelled ["one" "two" "three" "four" "five" "six" "seven" "eight" "nine"])
(def spelled->digit (into {}
(concat (map (fn [i s]
[s (str i)])
(range 1 10)
spelled)
(map (fn [d] [(str d) (str d)])
(range 1 10)))))
(def pattern (re-pattern (str "(?=(" (string/join "|" (sort (keys spelled->digit))) "))")))
(defn part-2 [input]
(->> input
(map (fn [line]
[(spelled->digit (second (first (re-seq pattern line))))
(spelled->digit (second (last (re-seq pattern line))))]))
(map #(apply str %))
(map edn/read-string)
(apply +)))
(defn parse-line
[f g line]
(let [digits (f line)]
(assert (seq digits) (format "no digits found in `%s`" line))
(parse-long (str (g (first digits)) (g (peek digits))))))
(defn solve-b
([]
(solve-b (slurp "src/day01.txt")))
([input]
(let [g (merge (zipmap (map str (range 10)) ;; string keys
(map str (range 10)))
(zipmap ["one" "two" "three" "four"
"five" "six" "seven" "eight" "nine"]
(map str (next (range 10)))))
pat (re-pattern (str "(?=(" (str/join "|" (keys g)) "))"))
f (fn [line] (into [] (map second) (re-seq pat line)))
extract (partial parse-line f g)]
(apply + (map extract (str/split-lines input))))))
https://github.com/Aziiz1989/AOC2023/blob/main/src/aziz/day01.clj my first time participating in AOC
Refactored with that awesome replacement hack @U1EP3BZ3Q mentioned: https://github.com/tschady/advent-of-code/blob/main/src/aoc/2023/d01.clj
Coming late to the party, but I decided to abuse strings and regexes
(defn reverse-charseq
^CharSequence [^CharSequence cs]
(let [len (.length cs)
n (unchecked-dec-int len)]
(reify CharSequence
(length [_] (.length cs))
(subSequence [_ from to]
(reverse-charseq
(.subSequence cs (unchecked-subtract-int len to) (unchecked-subtract-int len from))))
(charAt [_ i] (.charAt cs (unchecked-subtract-int n i)))
(toString [this]
(.toString (StringBuilder. ^CharSequence this))))))
This solves the problem of the overlapping regexes by allowing me to search for the reversed pattern on the string without allocating a new string, terrible idea 😄I was up at 5am by accident and by the time I solved it I still only got 7777 😄 Leaderboard is too timezone favoured for me to care about it
Yeah I'm only for the fun now 😄 And a good excuse to show off some cool #C035GRLJEP8 viewers
I guess you could also try it out the live in-browser editor feature in clerk ;) https://snapshots.nextjournal.com/clerk/build/ad8e5a2c19aa55921ea357fa9005d563f80c53be/editor/
I could, but then I'd have to leave Emacs 😉 I get the same workflow in Emacs with xwidgets
My off by one error was in time zone conversion - when I opened AoC the task was available for an hour already
This is not AoC but it is very Advent, Code and Clojure...
I'm publishing a Programming Advent blog about my new programming language, YAMLScript. I've announced it in other channels as:
> YAMLScript is live today! https://yamlscript.org/posts/advent-2023/dec-01/
> I've been working on YAMLScript nonstop since my https://www.youtube.com/watch?v=9OcFh-HaCyI about it when @pez noticed and invited me here.
> YAMLScript is billed as a new programming language that can be use for general purpose programming or embedding in YAML data files for dynamic transformation of the content.
> In reality YAMLScript compiles/transpiles to Clojure code and is evaluated with SCI.
> The ys
runtime binary CLI is GraalVM compiled and libyamlscript.so
can be bound to almost any programming language.
> YAMLScript plans to ship as a dynamic YAML config loading module/binding to dozens of programming languages.
> The language is written in Clojure of course.
> Please join #C05HQFMTURF if you to discuss it further.
https://old.reddit.com/r/adventofcode/comments/188bpfg/2023_day_1_playdate_cranked_solution/ I love these aspects of AOC
I won't make a habit of sharing all the subreddit stuff but Rockstar
is one wild language!
https://old.reddit.com/r/adventofcode/comments/1883ibu/2023_day_1_solutions/kbifxzl/
these elves are very incompetent!