From 7cc273a9a820362e815ef8bb785e8891fc92d389 Mon Sep 17 00:00:00 2001 From: Jan-Erik Rediger Date: Fri, 20 Dec 2024 20:03:16 +0100 Subject: [PATCH] new post: A Lil Advent of Code --- _posts/2024-12-20-a-lil-advent-of-code.md | 262 ++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 _posts/2024-12-20-a-lil-advent-of-code.md diff --git a/_posts/2024-12-20-a-lil-advent-of-code.md b/_posts/2024-12-20-a-lil-advent-of-code.md new file mode 100644 index 0000000..123a790 --- /dev/null +++ b/_posts/2024-12-20-a-lil-advent-of-code.md @@ -0,0 +1,262 @@ +--- +permalink: "/{{ year }}/{{ month }}/{{ day }}/a-lil-advent-of-code" +title: "A Lil Advent of Code" +published_date: "2024-12-20 20:00:00 +0100" +layout: post.liquid +data: + route: blog +excerpt: | + I solved the first 3 days worth of puzzles of this year's Advent of Code in Lil, + a tiny programming language, part of the Decker project. + These are my solutions and brief thoughts on Lil. +--- + +_This post won't include too much explanation. I wanted a place to at least put my code and that's what I have a blog for._ + +It's December, so everyone is doing [Advent of Code](https://adventofcode.com/). +I'm not. Or so I told myself. + +This week, while on vacation, I had some downtime and played around with [Decker][][^1] and its accompanying programming language [Lil]. + +Armed with the [tutorial][liltutorial], the [reference card][lilquickref] and the [Lil Terminal][lilt] installed, I set out to learn it and solved three days worth of puzzles from the ongoing Advent of Code 2024[^2]. + +[Decker]: https://beyondloom.com/decker/ +[Lil]: https://beyondloom.com/decker/lil.html +[liltutorial]: https://beyondloom.com/decker/learnlil.html +[lilquickref]: https://beyondloom.com/decker/lilquickref.html +[lilt]: https://beyondloom.com/decker/lilt.html + +Want to jump ahead? [Day 1](#day1) | [Day 2](#day2) | [Day 3](#day3) | [Thoughts](#thoughts) + +## Day 1 + +On Day 1 you get two lists side-by-side, need to order each one individually, then take the absolute differences and add them up. +Part 2 requires you to find the number of times an element shows up in the other list. + +That can be done in Lil like this: + +``` +example:"%i %i" parse "\n" split " +3 4 +4 3 +2 5 +1 3 +3 9 +3 3 +" + +on sort l do + extract value orderby value asc from l +end + +on reverse l do + extract value orderby index desc from l +end + +on part1 input do + sorted:sort @ flip input + result:sum each x in (sorted[0] - sorted[1]) mag x end + print[result] +end + +on dump name x do + print[name] + print["," fuse (list "%J") format x] +end + +on part2 input do + lists:flip input + left:extract value where value > 0 from lists[0] + right:extract value where value > 0 from lists[1] + counts:each elem in left + sum right=elem + end + result:sum left*counts + print[result] +end + +part1[example] +part2[example] +``` + +Example: + +``` +; lilt aoc1.lil +11 +31 +``` + +## Day 2 + +On Day 2 you have to determine which reports are safe. +A report is a line of numbers and if they match some rules it is considered safe. +Count those safe ones. + +And part 2 allows you to make some reports safe by ignoring some results. + +``` +example:"7 6 4 2 1 +1 2 7 8 9 +9 7 6 2 1 +1 3 2 4 5 +8 6 4 4 1 +1 3 6 7 9" + +on spreadcheck elems do + prod each elem in -2 window elems + diff:mag elem[0]-elem[1] + (diff-1) > 0 and (diff-1) < 3 + end +end + +on allincr elems do + prod each elem in -2 window elems elem[0] < elem[1] end +end + +on alldecr elems do + prod each elem in -2 window elems elem[0] > elem[1] end +end + +on reportcheck report do + report:on _ elem do "%i" parse elem end @ report + (max allincr[report],alldecr[report]) * spreadcheck[report] +end + +on part1 input do + reports:on _ line do " " split line end @ "\n" split input + + result:sum reportcheck @ reports + show[result] +end + +on part2 input do + reports:on _ line do " " split line end @ "\n" split input + + result:each report in reports + report:on _ elem do "%i" parse elem end @ report + valid:(max allincr[report],alldecr[report]) & spreadcheck[report] + if valid + 1 + else + max each i in range count report + newreport:extract value where !(index=i) from report + (max allincr[newreport],alldecr[newreport]) & spreadcheck[newreport] + end + end + end + show[sum result] +end + +part1[example] +part2[example] +``` + +Example: + +``` +; lilt aoc2.lil +2 +4 +``` + +## Day 3 + +I couldn't stop, so I also solved day 3. + +Day 3 has a garbled string containing some instructions to multiply some numbers. +Part 2 has additional instructions that turns on or off those instructions. + +I opted to iterate the string input and match for the expected patterns. + +``` +example:"xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" +example2:"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))" + +on part1 input do + f:"mul(%i,%i)" + result:sum each i in range count input + curinp:i drop input + ab:f parse i drop input + offset:(count "mul(")+(count "," fuse ab) + if curinp[offset]=")" + prod ab + end + end + show[result] +end + +on part2 input do + f:"mul(%i,%i)" + do_:"do()" + dont:"don't()" + enabled:1 + result:sum each i in range count input + curinp:i drop input + if ((count do_) limit curinp)=do_ + enabled:1 + end + if ((count dont) limit curinp)=dont + enabled:0 + end + + if (4 limit curinp)="mul(" + ab:f parse curinp + offset:(count "mul(")+(count "," fuse ab) + if enabled & curinp[offset]=")" + prod ab + end + end + end + show[result] +end + +part1[example] +part2[example2] +``` + +Run it: + +``` +; lilt aoc3.lil +159892596 +92626942 +``` + +## Thoughts + +I like Lil. +It's small, it's simple and it's surprisingly fast. +It also gets the job done. +These `name:data` variable assignments threw me off a bit, but I got over that hump. + +[jq](https://jqlang.github.io/jq/) is a powerful tool to work with JSON, but for the sake of it I can't remember any more of its syntax than the basic `.[]`. +Reading and transforming a JSON file using Lil instead felt very intuitive. +I opted to write a script I needed to transform some JSON into some other output it was intuitive. + +This is how you read and parse a JSON file[^3]: + +``` +json:"%j" parse read["droids.json"] +``` + +And then you can access the data: + +``` + extract n:name d:description where name like "Astro.*" from table json.data +{"n":("Astromech Droid"),"d":("Astromech droids are a series of versatile utility robots generally used for the maintenance and repair of starships and related technology. These small droids are often equipped with a variety of tool-tipped appendages that are stowed in recessed compartments. The R2 unit is a popular example of an astromech droid.")} +``` + +Not needing to deal with missing fields or inconsistencies in the format +makes it easy to ignore the error cases, +something that comes in handy for one-off scripts. + +Now I need to take a closer look at Decker and how this powerful language can be used with it. + +--- + +_Footnotes:_ + +[^1]: Decker is a "multimedia platform for creating and sharing interactive documents". I have yet to actually do something cool with Decker itself. +[^2]: I didn't _know_ I would be doing 3. But I finished Day 1 quickly, Day 2 not long after and Day 3 was done an hour later. I looked at Day 4 and how to solve it was not immediately clear, so I stopped. +[^3]: Data from the [`droids` endpoint of the Star Wars Databank](https://starwars-databank-server.vercel.app/api/v1/droids/)