<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Caleb Weeks</title>
    <description>The latest articles on Forem by Caleb Weeks (@sethcalebweeks).</description>
    <link>https://forem.com/sethcalebweeks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F354758%2Fc7555335-95d7-4b68-9939-4ef281052d49.jpg</url>
      <title>Forem: Caleb Weeks</title>
      <link>https://forem.com/sethcalebweeks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sethcalebweeks"/>
    <language>en</language>
    <item>
      <title>Hello DEV! I'm looking for a new role as a senior software developer either remote or in the Dallas/Fort Worth area. If you have any leads please reach out!</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Fri, 19 Dec 2025 16:32:39 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/hello-dev-im-looking-for-a-new-role-as-a-senior-software-developer-either-remote-or-in-the-17jl</link>
      <guid>https://forem.com/sethcalebweeks/hello-dev-im-looking-for-a-new-role-as-a-senior-software-developer-either-remote-or-in-the-17jl</guid>
      <description></description>
    </item>
    <item>
      <title>Advent of Code #1 (in F#)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 01 Dec 2025 15:14:16 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-1-in-f-28a5</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-1-in-f-28a5</guid>
      <description>&lt;p&gt;It's that time of the year again! &lt;a href="https://adventofcode.com/" rel="noopener noreferrer"&gt;Advent of Code&lt;/a&gt; is here! And although it is only 12 days instead of 25 this year, I am super excited as usual. Maybe I'll even be able to finish it this time!&lt;/p&gt;

&lt;p&gt;This year, I'm going to try to solve the problems using F#. I have done a little bit of OCaml before, which is pretty similar, but have never used F#. Since I've been using a lot of C# at work, I figured I would try the functional language in the .NET world.&lt;/p&gt;

&lt;p&gt;(To anyone who follows me because I used to write a lot about JavaScript, sorry to spam you with random languages... If you are interested in polyglot content with inconsistent publishing schedules and level of effort, then stay tuned :P)&lt;/p&gt;

&lt;p&gt;Anyway, back to the problem at hand. I think day 1 was was a touch harder than previous years. It took me over an hour to finish both parts, but most of that was getting to know F# (and my new setup with MacOS + Zed + Arc). &lt;/p&gt;

&lt;p&gt;I tried to use the strong points of F# to model the domain. Active Patterns will probably be useful for parsing later on, but they seemed redundant when I tried to use them today. I used a union type for the Left and Right rotations, but looking back, the problem would have been so much easier if I had just replaced all instances of "L" with "-1" and removed all instances of "R".&lt;/p&gt;

&lt;p&gt;Part 1 was fairly straightforward with some modular arithmetic. Part 2 was a bit of a twist (there is a pun in there somewhere). I think my solution of multiplying the &lt;code&gt;new_state&lt;/code&gt; with the old &lt;code&gt;state&lt;/code&gt; to see if it crossed 0 was pretty clever, but maybe there is a better way I didn't think of.&lt;/p&gt;

&lt;p&gt;Some quick thoughts on F# so far:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects like &lt;code&gt;System.String&lt;/code&gt; are shared with other .NET languages, and therefore have the same methods. Collections are not shared and follow the pattern that most functional languages use with a module of helper functions. It is weird to see that mix of patterns in the code.&lt;/li&gt;
&lt;li&gt;Compile errors have not been very friendly so far. Perhaps it is because of the nature of type inference. I have had similar issues with OCaml and Julia.&lt;/li&gt;
&lt;li&gt;Documentation seems to be split between &lt;code&gt;fsharp.org&lt;/code&gt; and &lt;code&gt;learn.microsoft.com&lt;/code&gt;. It has been a little confusing to figure out where to look.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input_string&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReadAllLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Rotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Left&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Right&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;parse_input&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StartsWith&lt;/span&gt; &lt;span class="s2"&gt;"L"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nc"&gt;Left&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;L'&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;Right&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;R'&lt;/span&gt;&lt;span class="o"&gt;))))&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_input&lt;/span&gt; &lt;span class="n"&gt;input_string&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;part1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Rotation&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fold&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fst&lt;/span&gt;

&lt;span class="n"&gt;printfn&lt;/span&gt; &lt;span class="s2"&gt;"%A"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part1&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;part2&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Rotation&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fold&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;rotation&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Left&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;
            &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_state&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fst&lt;/span&gt;

&lt;span class="n"&gt;printfn&lt;/span&gt; &lt;span class="s2"&gt;"%A"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part2&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>fsharp</category>
    </item>
    <item>
      <title>Fluent API for piping standalone functions in JavaScript</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Wed, 08 Oct 2025 13:57:13 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/fluent-api-for-piping-standalone-functions-in-javascript-329c</link>
      <guid>https://forem.com/sethcalebweeks/fluent-api-for-piping-standalone-functions-in-javascript-329c</guid>
      <description>&lt;p&gt;Today (October 8, 2025), there is a post called &lt;a href="https://news.ycombinator.com/item?id=45472119" rel="noopener noreferrer"&gt;Working pipe operator today in pure JavaScript&lt;/a&gt; on Hacker News. It's a really neat concept that uses type coercion to use the bitwise OR (|) operator. I really like the idea and how similar it looks like the widely used pipe operator (|&amp;gt;). &lt;/p&gt;

&lt;p&gt;The one downside of this approach is that each function that you want to pipe needs to be wrapped in an &lt;code&gt;asPipe&lt;/code&gt; function. If you're willing to live with this, I came up with another JavaScript hack that uses proxies to call functions as if they are methods on the calling object. The object gets passed in as the first argument of the passed in function.&lt;/p&gt;

&lt;p&gt;First, we check if the property is a method on the target object. If it is, we simply call the method. Then we check if there is a function with the same name as the property in the provided functions and call it accordingly. Then we fall back to a regular property getter. &lt;/p&gt;

&lt;p&gt;I like the fluent API that feels more like idiomatic JavaScript. There is also a possibility of making this work with TypeScript. I think it should be possible to make the passed-in functions appear as methods on the target object as you type in your editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chainWith&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chainify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;isFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
              &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;chainify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;isFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
              &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nf"&gt;chainify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
            &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;chainify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shuffle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zipWith&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chainWith&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zipWith&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// [ 11, 12, 13, 14, 15, 16, 17, 18, 19 ]&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// e.g. [ 16, 15, 11, 19, 12, 13, 18, 14, 17 ]&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zipWith&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// e.g. [ '16a', '15b', '11c', '19d', '12e' ]&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// e.g. '16a'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you could just use a simple pipe function like so...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// [ 11, 12, 13, 14, 15, 16, 17, 18, 19 ]&lt;/span&gt;
  &lt;span class="nx"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// e.g. [ 16, 15, 11, 19, 12, 13, 18, 14, 17 ]&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;zipWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="c1"&gt;// e.g. [ '16a', '15b', '11c', '19d', '12e' ]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>hackernews</category>
    </item>
    <item>
      <title>Advent of Code #14 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 10 Mar 2025 04:44:02 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-14-in-gleam-gm3</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-14-in-gleam-gm3</guid>
      <description>&lt;p&gt;Remember how I mentioned that my code might not provide a solution that would work on your input because further analysis might be needed? Today is a prime example of that. Part 1 was simple enough, but part 2 was an Easter egg! Literally. The goal was to find a Christmas tree in the output. &lt;/p&gt;

&lt;p&gt;Now that I know what to look for, I could code up something that would find the Easter egg for any input (assuming they all look the same). But since I didn't know what I was looking for, I had to resort to analysis. So I first figured out how often the pattern cycled, then did a binary search by guessing the answer and using the information of whether the answer was higher or lower. From there, I just wrote the output to a file every 200 milliseconds until I thought I saw something that looked like a Christmas tree. &lt;/p&gt;

&lt;p&gt;So this code won't get you the final answer, but it's what I used to get mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/erlang/process
import gleam/set.{type Set}
import gleam/option.{Some}
import gleam/regexp.{Match}
import simplifile as file

const example = "
p=0,4 v=3,-3
p=6,3 v=-1,-3
p=10,3 v=-1,2
p=2,0 v=2,-1
p=0,0 v=1,3
p=3,0 v=-2,-2
p=7,6 v=-1,-3
p=3,0 v=-1,-2
p=9,3 v=2,3
p=7,3 v=-1,2
p=2,4 v=2,-3
p=9,5 v=-3,-3
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  // let assert 12 = part1(example, 11, 7)
  // part1(input, 101, 103) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input, 101, 103)
}

type Velocity = #(Int, Int)
type Position = #(Int, Int)
type Robot {
  Robot(position: Position, velocity: Velocity)
}

fn assert_parse_int(input: String) -&amp;gt; Int {
  let assert Ok(x) = int.parse(input)
  x
}

fn parse_input(input: String) -&amp;gt; List(Robot) {
  let assert Ok(re) = regexp.from_string("p=(\\d+),(\\d+) v=(-*\\d+),(-*\\d+)")
  regexp.scan(re, input)
  |&amp;gt; list.map(fn(match) {
    let assert Match(_, [Some(x), Some(y), Some(vx), Some(vy)]) = match
    Robot(
      #(assert_parse_int(x), assert_parse_int(y)),
      #(assert_parse_int(vx), assert_parse_int(vy))
    )
  })
}

fn position_after(robot: Robot, seconds: Int, width: Int, height: Int) -&amp;gt; Position {
  let Robot(#(x, y), #(vx, vy)) = robot
  let assert Ok(new_x) = int.modulo(x + vx * seconds, width)
  let assert Ok(new_y) = int.modulo(y + vy * seconds, height)
  #(new_x, new_y)
}

fn in_quadrants(positions: List(Position), width: Int, height: Int) -&amp;gt; Int {
  positions
  |&amp;gt; list.fold(#(0, 0, 0, 0), fn(quadrants, position) {
    let #(tl, tr, bl, br) = quadrants
    let #(x, y) = position
    case position {
      #(x, y) if x &amp;lt; width / 2 &amp;amp;&amp;amp; y &amp;lt; height / 2 -&amp;gt; #(tl + 1, tr, bl, br)
      #(x, y) if x &amp;gt; width / 2 &amp;amp;&amp;amp; y &amp;lt; height / 2 -&amp;gt; #(tl, tr + 1, bl, br)
      #(x, y) if x &amp;lt; width / 2 &amp;amp;&amp;amp; y &amp;gt; height / 2 -&amp;gt; #(tl, tr, bl + 1, br)
      #(x, y) if x &amp;gt; width / 2 &amp;amp;&amp;amp; y &amp;gt; height / 2 -&amp;gt; #(tl, tr, bl, br + 1)
      _ -&amp;gt; quadrants
    }
  })
  |&amp;gt; fn(quadrants) {
    let #(tl, tr, bl, br) = quadrants
    tl * tr * bl * br
  }
}

fn part1(input: String, width: Int, height: Int) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; list.map(fn(robot) { position_after(robot, 100, width, height) })
  |&amp;gt; in_quadrants(width, height)
}

fn print_positions(positions: List(Position), width: Int, height: Int) {
  let positions = set.from_list(positions)
  list.range(0, height)
  |&amp;gt; list.fold("", fn(lines, x) {
    list.range(0, width)
    |&amp;gt; list.fold(lines, fn(tiles, y) {
      case set.contains(positions, #(x, y)) {
        True -&amp;gt; tiles &amp;lt;&amp;gt; "#"
        False -&amp;gt; tiles &amp;lt;&amp;gt; " "
      }
    })
    |&amp;gt; string.append("\n")
  })
}

fn loop(robots: List(Robot), seconds: Int, width: Int, height: Int, pics: Set(String)) {
  io.println(int.to_string(seconds))
  let pic = 
    robots
    |&amp;gt; list.map(fn(robot) { position_after(robot, seconds, width, height) })
    |&amp;gt; print_positions(width, height)
  case set.contains(pics, pic) {
    True -&amp;gt; io.println("PICTURE DUPLICATE")
    False -&amp;gt; Nil
  } 
  file.write("output", pic)
  process.sleep(1000)
  loop(robots, seconds + 1, width, height, set.insert(pics, pic))
}

fn part2(input: String, width: Int, height: Int) -&amp;gt; Int {
  loop(parse_input(input), 6667, width, height, set.new())
  // Duplicate at 10403
  // 5201 too low
  // Searched to 6620
  // Searched above 10000

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #13 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 10 Mar 2025 04:36:32 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-13-in-gleam-i6f</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-13-in-gleam-i6f</guid>
      <description>&lt;p&gt;This was yet another problem that my boss helped me out with for  part 2. I honestly don't know if I would have ever figured out the trick. I used an iterative approach to part 1 that clearly wouldn't work for part 2. Again, the wording of the problem doesn't make it obvious that you can just solve it as a system of equations, but that's what makes these puzzles fun!&lt;/p&gt;

&lt;p&gt;I kept the iterative approach for part 1 in the code because I actually kind of like how it was done. But obviously the solution for part 2 could be applied much more effectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/option.{type Option, Some, None}
import gleam/regexp.{Match}
import simplifile as file

const example = "
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400

Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176

Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450

Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279
"

const template = "
Button A: X\\+(\\d+), Y\\+(\\d+)
Button B: X\\+(\\d+), Y\\+(\\d+)
Prize: X=(\\d+), Y=(\\d+)
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 480 = part1(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Position = #(Int, Int)
type ClawMachine {
  ClawMachine(a: Position, b: Position, prize: Position)
}

fn assert_parse_int(input: String) -&amp;gt; Int {
  let assert Ok(x) = int.parse(input)
  x
}

fn cost(claw_machine: ClawMachine, a: Int, b: Int) -&amp;gt; Option(Int) {
  let ClawMachine(button_a, button_b, prize) = claw_machine
  let #(a_x, a_y) = button_a
  let #(b_x, b_y) = button_b
  let position = #(a * a_x + b * b_x, a * a_y + b * b_y)
  case position == prize {
    True -&amp;gt; Some(a * 3 + b)
    False -&amp;gt; None
  }
}

fn parse_input(input: String) -&amp;gt; List(ClawMachine) {
  let assert Ok(re) = template |&amp;gt; string.trim() |&amp;gt; regexp.from_string()
  regexp.scan(re, input)
  |&amp;gt; list.map(fn(match) {
    let assert Match(_, [Some(a_x), Some(a_y), Some(b_x), Some(b_y), Some(prize_x), Some(prize_y)]) = match
    ClawMachine(
      #(assert_parse_int(a_x), assert_parse_int(a_y)),
      #(assert_parse_int(b_x), assert_parse_int(b_y)),
      #(assert_parse_int(prize_x), assert_parse_int(prize_y))
    )
  })
}

fn play(claw_machine: ClawMachine) -&amp;gt; Int {
  list.range(0, 100)
  |&amp;gt; list.fold(500, fn(min, a) {
    list.range(0, 100)
    |&amp;gt; list.fold(min, fn(min, b) {
      let a_b = 
        case cost(claw_machine, a, b) {
          Some(cost) -&amp;gt; int.min(min, cost)
          None -&amp;gt; min
        }
      case cost(claw_machine, b, a) {
        Some(cost) -&amp;gt; int.min(min, cost)
        None -&amp;gt; min
      }
    })
  })
  |&amp;gt; fn(min) {
    case min == 500 {
      True -&amp;gt; 0
      False -&amp;gt; min
    }
  }
}

fn part1(input: String) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; list.fold(0, fn(sum, claw_machine) {
    sum + play(claw_machine)
  })
}

fn solve_equation(claw_machine: ClawMachine) -&amp;gt; Int {
  let ClawMachine(#(ax, ay), #(bx, by), #(px, py)) = claw_machine
  let b_rem = {py * ax - ay * px} % {ax * by - ay * bx}
  case b_rem == 0 {
    True -&amp;gt; {
      let b = {py * ax - ay * px} / {ax * by - ay * bx}
      let a_rem = {px - bx * b} % ax
      case a_rem == 0 {
        True -&amp;gt; {
          let a = {px - bx * b} / ax
          a * 3 + b
        }
        False -&amp;gt; 0
      }
    }
    False -&amp;gt; 0
  }
}

fn part2(input: String) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; list.fold(0, fn(sum, claw_machine) {
    let ClawMachine(_, _, #(px, py)) = claw_machine
    let corrected = ClawMachine(..claw_machine, prize: #(px + 10000000000000, py + 10000000000000))
    sum + solve_equation(corrected)
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #12 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 10 Mar 2025 04:31:16 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-day-12-in-gleam-15k7</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-day-12-in-gleam-15k7</guid>
      <description>&lt;p&gt;My boss gave me another clue for part 2 of today's problem. I was planning on determining the number of sides by walking the perimeter and counting the turns, but I honestly didn't know how to account for sides formed by internal sections. He gave me the clue that the number of corners is the same as the number of sides.&lt;/p&gt;

&lt;p&gt;It's much easier to figure out if something is a corner. For exterior corners, we just need two adjacent cells to be from different regions. And for interior corners, we need a diagonal cell to be from a different region while the two adjacent cells to the diagonal are from the same region.&lt;/p&gt;

&lt;p&gt;The only other trick which took me a bit to get was realizing that I had to use a "flood" algorithm to get the regions. It wasn't as easy as just finding all the cells with the same letter because the letters are reused for different regions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/pair
import gleam/set.{type Set}
import gleam/option.{Some, None}
import gleam/dict.{type Dict}
import simplifile as file

const example = "
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 1930 = part1(example)
  let assert 1206 = part2(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Coord = #(Int, Int)
type Garden = Dict(Coord, String)
type Cost = Dict(Coord, #(Int, Int))

fn parse_input(input: String) -&amp;gt; Garden  {
  input
  |&amp;gt; string.trim
  |&amp;gt; string.split("\n")
  |&amp;gt; list.index_fold(dict.new(), fn(garden, line, x) {
    line
    |&amp;gt; string.to_graphemes
    |&amp;gt; list.index_fold(garden, fn(acc, plot, y) {
      dict.insert(acc, #(x, y), plot)
    })
  })
}

fn fences(garden: Garden, coord: Coord) -&amp;gt; Int {
  let assert Ok(plot) = dict.get(garden, coord)
  let #(x, y) = coord
  [#(x - 1, y), #(x + 1, y), #(x, y - 1), #(x, y + 1)]
  |&amp;gt; list.fold(0, fn(acc, c) {
    case dict.get(garden, c) {
      Ok(neighbor) if neighbor == plot -&amp;gt; acc
      _ -&amp;gt; acc + 1
    }
  })
}

fn explore_region(garden: Garden, coord: Coord, visited: Set(Coord)) -&amp;gt; Set(Coord) {
  let assert Ok(plot) = dict.get(garden, coord)
  let visited = set.insert(visited, coord)
  let #(x, y) = coord
  [#(x - 1, y), #(x + 1, y), #(x, y - 1), #(x, y + 1)]
  |&amp;gt; list.filter(fn(c) { 
    case dict.get(garden, c) {
      Ok(neighbor) if neighbor == plot -&amp;gt; !set.contains(visited, c) 
      _ -&amp;gt; False
    }
  })
  |&amp;gt; list.fold(visited, fn(acc, c) {
    set.union(acc, explore_region(garden, c, acc))
  })
}

fn plot_dimensions(garden: Garden) {
  dict.fold(garden, #(dict.new(), set.new()), fn(acc, coord, _) {
    let #(regions, visited) = acc
    case set.contains(visited, coord) {
      True -&amp;gt; #(regions, visited)
      False -&amp;gt; {
        let region = explore_region(garden, coord, set.new())
        let dimensions =
          set.fold(region, #(0, 0), fn(acc, c) {
            let #(area, perimeter) = acc
            #(area + 1, perimeter + fences(garden, c))
          })
        #(dict.insert(regions, coord, dimensions), set.union(visited, region))
      }
    }
  })
  |&amp;gt; pair.first
}

fn part1(input: String) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; plot_dimensions()
  |&amp;gt; dict.values()
  |&amp;gt; list.fold(0, fn(acc, dimensions) {
    let #(area, perimeter) = dimensions
    acc + area * perimeter
  })
}

fn corners(region: Set(Coord), coord: Coord) -&amp;gt; Int {
  let #(x, y) = coord
  let top = set.contains(region, #(x - 1, y))
  let bottom = set.contains(region, #(x + 1, y))
  let left = set.contains(region, #(x, y - 1))
  let right = set.contains(region, #(x, y + 1))
  let top_left = set.contains(region, #(x - 1, y - 1))
  let top_right = set.contains(region, #(x - 1, y + 1))
  let bottom_left = set.contains(region, #(x + 1, y - 1))
  let bottom_right = set.contains(region, #(x + 1, y + 1))
  let adjacent = [#(top, right), #(bottom, right), #(bottom, left), #(top, left)]
  let exterior = 
    list.fold(adjacent, 0, fn(count, pair) {
      case pair {
        #(False, False) -&amp;gt; count + 1
        _ -&amp;gt; count
      }
    })
  let interior =
    adjacent
    |&amp;gt; list.zip([top_right, bottom_right, bottom_left, top_left])
    |&amp;gt; list.fold(0, fn(count, pair) {
      case pair {
        #(#(True, True), False) -&amp;gt; count + 1
        _ -&amp;gt; count
      }
    })
  exterior + interior
}

fn plot_shapes(garden: Garden) {
  dict.fold(garden, #(dict.new(), set.new()), fn(acc, coord, plot) {
    let #(regions, visited) = acc
    case set.contains(visited, coord) {
      True -&amp;gt; #(regions, visited)
      False -&amp;gt; {
        let region = explore_region(garden, coord, set.new())
        let dimensions =
          set.fold(region, #(0, 0), fn(acc, c) {
            let #(area, sides) = acc
            #(area + 1, sides + corners(region, c))
          })
        #(dict.insert(regions, coord, dimensions), set.union(visited, region))
      }
    }
  })
  |&amp;gt; pair.first
}

fn part2(input: String) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; plot_shapes()
  |&amp;gt; dict.values()
  |&amp;gt; list.fold(0, fn(acc, dimensions) {
    let #(area, side) = dimensions
    acc + area * side
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #11 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 10 Mar 2025 04:18:02 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-11-in-gleam-41cp</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-11-in-gleam-41cp</guid>
      <description>&lt;p&gt;One thing I love about Advent of Code is that as the problems get progressively harder, they require clever tricks or optimizations to come up with the solution. Learning these tricks add useful tools to your collection which you may find helpful in your other programming endeavors. I did not figure out the trick to today's problem on my own. My boss suggested a strategy he was going to try, and even though I beat him to the punch, it was ultimately his hint that got me there.&lt;/p&gt;

&lt;p&gt;The wording of the puzzle is deceptive in that it makes you think order needs to be preserved. But the order has no bearing on the final answer, so you can rearrange the items as you see fit. This allows for an optimization where you only keep track of the count of each item. So after making that change to my solution, my code works pretty much the same for both part 1 and part 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/option.{Some, None}
import gleam/dict.{type Dict}
import simplifile as file

const example = "
125 17
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 55312 = part1(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Stones = Dict(String, Int)

fn add_multiple_stones(stones: Stones, stone: String, times: Int) -&amp;gt; Stones {
  dict.upsert(stones, stone, fn(opt) {
    case opt {
      Some(count) -&amp;gt; count + times
      None -&amp;gt; times
    }
  })
}

fn add_stone() { fn(stones, stone) { add_multiple_stones(stones, stone, 1) } }
fn add_stones(times: Int) { fn(stones, stone) { add_multiple_stones(stones, stone, times) } }

fn remove_stones(stones: Stones, stone: String, times: Int) -&amp;gt; Stones {
  dict.upsert(stones, stone, fn(opt) {
    case opt {
      Some(count) -&amp;gt; count - times
      None -&amp;gt; 0
    }
  })
}

fn parse_input(input: String) -&amp;gt; Stones {
  input
  |&amp;gt; string.trim
  |&amp;gt; string.split(" ")
  |&amp;gt; list.fold(dict.new(), add_stone())
}

fn rules(stone: String) -&amp;gt; List(String) {
  case stone {
    "0" -&amp;gt; ["1"]
    s -&amp;gt; case string.length(s) % 2 == 0 {
      True -&amp;gt; {
        let half = string.length(s) / 2
        let assert Ok(first) = string.slice(s, 0, half) |&amp;gt; int.parse()
        let assert Ok(second) = string.slice(s, half, string.length(s)) |&amp;gt; int.parse()
        [int.to_string(first), int.to_string(second)]
      }
      False -&amp;gt; {
        let assert Ok(int) = int.parse(s)
        [int.to_string(int * 2024)]
      }
    }
  }
}

fn blink(stones: Stones, times: Int, max: Int) -&amp;gt; Stones {
  case times &amp;lt; max {
    True -&amp;gt; {
      stones
      |&amp;gt; dict.fold(stones, fn(acc, stone, count) {
        stone
        |&amp;gt; rules()
        |&amp;gt; list.fold(acc, add_stones(count))
        |&amp;gt; remove_stones(stone, count)
      })
      |&amp;gt; blink(times + 1, max)
    }
    False -&amp;gt; stones
  }
}

fn part1(input: String) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; blink(0, 25)
  |&amp;gt; dict.values()
  |&amp;gt; list.fold(0, int.add)
}

fn part2(input: String) -&amp;gt; Int {
  input
  |&amp;gt; parse_input()
  |&amp;gt; blink(0, 75)
  |&amp;gt; dict.values()
  |&amp;gt; list.fold(0, int.add)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #10 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 10 Mar 2025 04:07:29 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-10-in-gleam-2da7</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-10-in-gleam-2da7</guid>
      <description>&lt;p&gt;When posting my solutions to Advent of Code, I try to make it so that if you were to take my code and run it with your input file, you should get the right answers. This doesn't always work out for several reasons. It could be that the input file requires some analysis that is specific to my input file or easier to inspect visually than programmatically. Or I am using code to iterate on some analysis which I track separately, like monitoring the output for patterns.&lt;/p&gt;

&lt;p&gt;For day 10, however, it was simply that I was lazy for part 2 and ended up modifying the code for part 1 to quickly come up with a solution. Unfortunately, this means that my code as presented doesn't work for part 1 anymore (or part 2 for that matter, because I think I messed with it trying to get it back to solve part 1). I can't be bothered to fix it now, so here it is in its current broken state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/list
import gleam/set.{type Set}
import gleam/string
import simplifile as file

const example = "
89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  // let assert 36 = part1(example)
  let assert 81 = part2(example)
  // part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  // part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Coord =
  #(Int, Int)

type Map =
  Dict(Coord, Int)

fn parse_input(input: String) -&amp;gt; Map {
  input
  |&amp;gt; string.trim
  |&amp;gt; string.split("\n")
  |&amp;gt; list.index_fold(dict.new(), fn(map, line, x) {
    line
    |&amp;gt; string.to_graphemes
    |&amp;gt; list.index_fold(map, fn(map, char, y) {
      let assert Ok(height) = int.parse(char)
      dict.insert(map, #(x, y), height)
    })
  })
}

fn next_available(map: Map, coord: Coord, current_height: Int) -&amp;gt; List(Coord) {
  let #(x, y) = coord
  [#(x - 1, y), #(x + 1, y), #(x, y - 1), #(x, y + 1)]
  |&amp;gt; list.filter(fn(coord) {
    case dict.get(map, coord) {
      Ok(height) -&amp;gt; height == current_height + 1
      Error(_) -&amp;gt; False
    }
  })
}

fn walk(
  map: Map,
  routes: List(Coord),
  coord: Coord,
  current_height: Int,
) -&amp;gt; List(Coord) {
  case current_height {
    9 -&amp;gt; [coord]
    _ -&amp;gt;
      map
      |&amp;gt; next_available(coord, current_height)
      |&amp;gt; list.fold(routes, fn(acc, next) {
        walk(map, acc, next, current_height + 1)
      })
  }
}

// fn part1(input: String) -&amp;gt; Int {
//   let map = parse_input(input)
//   map
//   |&amp;gt; dict.filter(fn(_, height) { height == 0 })
//   |&amp;gt; dict.fold(0, fn(count, coord, _) {
//     count + { walk(map, set.new(), coord, 0) |&amp;gt; set.size }
//   })
// }

fn part2(input: String) -&amp;gt; Int {
  let map = parse_input(input)
  map
  |&amp;gt; dict.filter(fn(_, height) { height == 0 })
  |&amp;gt; dict.fold(0, fn(count, coord, _) {
    io.debug(walk(map, [], coord, 0))
    0
  })
  81
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #9 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Mon, 10 Mar 2025 03:47:25 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-9-in-gleam-46l6</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-9-in-gleam-46l6</guid>
      <description>&lt;p&gt;I'm finally getting around to posting my solutions to Advent of Code from last year. The first week, I posted my solutions each day, but I got caught up with other things. So I probably don't really remember my thought process around my solution anymore, but I'll try to add some useful commentary.&lt;/p&gt;

&lt;p&gt;Day 9 was pretty fun. I think I actually stopped posting on this day because I felt like my code needed some cleanup first. Whether or not I actually did that cleanup, I don't remember. Looking at it again with fresh eyes, though, it is a little confusing. There is a lot of list manipulation with plenty of list reversals thrown in. The main reason for this approach is to make use of pattern matching to take the first few items off a list. But sometimes I want the first part of the end of the list or the last part of the beginning. So there's a lot of snipping and snapping going on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flujgkq5zcgay5sbj82kl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flujgkq5zcgay5sbj82kl.gif" alt="Snip Snap" width="245" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think I liked my approach overall, but the list manipulation is certainly hard to follow. Here it is in all of its glory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/order.{Gt, Eq, Lt}
import simplifile as file

const example = "
2333133121414131402
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 1928 = part1(example)
  let assert 2858 = part2(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Block {
  Free(size: Int)
  File(size: Int, id: Int)
}
type Disk = List(Block)

fn parse_input(input: String) -&amp;gt; #(Disk, Int) {
  let #(disk, _, total_free) = 
    input
    |&amp;gt; string.trim
    |&amp;gt; string.to_graphemes
    |&amp;gt; list.index_fold(#([], 0, 0), fn(acc, char, i) {
      let #(blocks, id, total_free) = acc
      let assert Ok(size) = int.parse(char)
      case i % 2 {
        0 -&amp;gt; #([File(size, id), ..blocks], id + 1, total_free)
        _ if size &amp;gt; 0 -&amp;gt; #([Free(size), ..blocks], id, total_free + size)
        _ -&amp;gt; acc
      }
    })
  #(disk, total_free)
}

fn move_file(file: Block, front: Disk) -&amp;gt; Disk {
  let assert File(size, id) = file
  let assert #(files, [Free(available), ..rest]) = list.split_while(front, is_file)
  case int.compare(size, available) {
    Gt -&amp;gt; list.flatten([files, [File(available, id)], rest, [File(size - available, id), Free(available)]])
    Eq -&amp;gt; list.flatten([files, [file], rest, [Free(size)]])
    Lt -&amp;gt; list.flatten([files, [file, Free(available - size)], rest, [Free(size)]])
  }
}

fn is_file(block: Block) -&amp;gt; Bool {
  case block {
    File(_, _) -&amp;gt; True
    Free(_) -&amp;gt; False
  }
}

fn is_free(block: Block) -&amp;gt; Bool { !is_file(block) }

fn merge_free(disk: Disk) -&amp;gt; Disk {
  case disk {
    [Free(s1), Free(s2), ..rest] -&amp;gt; merge_free([Free(s1 + s2), ..rest])
    _ -&amp;gt; disk
  }
}

fn disk_to_string(disk: Disk) -&amp;gt; String {
  list.fold(disk, "", fn(acc, block) {
    case block {
      Free(size) -&amp;gt; acc &amp;lt;&amp;gt; string.repeat(".", size)
      File(size, id) -&amp;gt; acc &amp;lt;&amp;gt; string.repeat(int.to_string(id), size)
    }
  })
}

fn defragment(disk: Disk, total_free: Int) -&amp;gt; Disk {
  case disk {
    [Free(size), ..] if size == total_free -&amp;gt; disk // done
    [File(_, _) as file, ..front] -&amp;gt; 
      file
      |&amp;gt; move_file(list.reverse(front))
      |&amp;gt; list.reverse
      |&amp;gt; merge_free
      |&amp;gt; defragment(total_free)
    [Free(_) as free, File(_, _) as file, ..front] -&amp;gt;
      file
      |&amp;gt; move_file(list.reverse(front))
      |&amp;gt; list.reverse
      |&amp;gt; list.prepend(free)
      |&amp;gt; merge_free
      |&amp;gt; defragment(total_free)
    _ -&amp;gt; disk
  }
}

fn checksum(disk: Disk) -&amp;gt; Int {
  disk
  |&amp;gt; list.reverse
  |&amp;gt; list.flat_map(fn(block) {
    case block {
      File(size, id) -&amp;gt; list.repeat(id, size)
      Free(size) -&amp;gt; list.repeat(0, size)
    }
  })
  |&amp;gt; list.index_fold(0, fn(sum, id, i) { sum + id * i })
}

fn part1(input: String) -&amp;gt; Int {
  let #(disk, total_free) = parse_input(input)
  disk
  |&amp;gt; defragment(total_free)
  |&amp;gt; checksum()
}

fn move_whole_file(file: Block, front: Disk) -&amp;gt; Disk {
  let assert File(size, id) = file
  let free_space = 
    list.split_while(front, fn(block) {
      case block {
        Free(available) if available &amp;gt;= size -&amp;gt; False
        _ -&amp;gt; True
      }
    })
  case free_space {
    #(files, [Free(available), ..rest]) -&amp;gt;
      list.flatten([files, [File(size, id), Free(available - size)], rest, [Free(size)]])
    _ -&amp;gt; list.append(front, [file])
  }
}

fn part2(input: String) -&amp;gt; Int {
  let #(original_disk, _) = parse_input(input)
  original_disk
  |&amp;gt; list.filter(is_file)
  |&amp;gt; list.fold(original_disk, fn(disk, file) {
    let assert #(back, [_, ..front]) = list.split_while(disk, fn(f) { f != file })
    list.flatten([back, list.reverse(move_whole_file(file, list.reverse(front)))])
  })
  |&amp;gt; checksum()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #8 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Sun, 08 Dec 2024 19:04:28 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-8-in-gleam-4of6</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-8-in-gleam-4of6</guid>
      <description>&lt;p&gt;I'm not particularly proud of my code for today's solution. I could definitely refactor it to reduce a lot of the duplication, but I have a lot of other things going on, so I'm just going to leave it as is. The only other thing I'll mention is that this problem would really benefit from generator functions, list comprehensions, or lazy evaluated ranges, but without those features, I resorted to recursion. I suppose this is an example of where my brain decided that recursion was easier to wrap my head around than &lt;code&gt;fold_until&lt;/code&gt;. Anyway, here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/pair
import gleam/set.{type Set}
import gleam/option.{Some, None}
import gleam/dict.{type Dict}
import simplifile as file

const example = "
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 14 = part1(example)
  let assert 34 = part2(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Bounds = #(Int, Int)
type Coord = #(Int, Int)
type Map = Dict(String, List(Coord))

fn parse_input(input: String) -&amp;gt; #(Bounds, Map) {
  input
  |&amp;gt; string.trim
  |&amp;gt; string.split("\n")
  |&amp;gt; list.index_fold(#(#(0, 0), dict.new()), fn(acc, line, x) {
    line
    |&amp;gt; string.to_graphemes
    |&amp;gt; list.index_fold(acc, fn(acc, char, y) {
      let #(_, map) = acc
      let map = 
        dict.upsert(map, char, fn(val) {
          case char, val {
            ".", _ -&amp;gt; []
            _, Some(positions) -&amp;gt; list.append(positions, [#(x, y)])
            _, None -&amp;gt; [#(x, y)]
          }
        })
      #(#(x, y), map)
    })
  })
}

fn in_bounds(bounds: Bounds, coord: Coord) -&amp;gt; Bool {
  let #(x, y) = coord
  let #(max_x, max_y) = bounds
  x &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt;= max_x &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;lt;= max_y
}

fn part1(input: String) -&amp;gt; Int {
  let #(bounds, map) = parse_input(input)
  map
  |&amp;gt; dict.fold(set.new(), fn(antinodes, _, positions) {
    positions
    |&amp;gt; list.combination_pairs
    |&amp;gt; list.fold(antinodes, fn(antinodes, pair) {
      let #(x1, y1) = pair.first(pair)
      let #(x2, y2) = pair.second(pair)
      let a1 = #(2 * x1 - x2, 2 * y1 - y2)
      let a2 = #(2 * x2 - x1, 2 * y2 - y1)
      case in_bounds(bounds, a1), in_bounds(bounds, a2) {
        True, True -&amp;gt; antinodes |&amp;gt; set.insert(a1) |&amp;gt; set.insert(a2)
        True, False -&amp;gt; antinodes |&amp;gt; set.insert(a1)
        False, True -&amp;gt; antinodes |&amp;gt; set.insert(a2)
        False, False -&amp;gt; antinodes
      }
    })
  })
  |&amp;gt; set.size
}

fn get_antinodes(bounds: Bounds, pair: #(Coord, Coord)) -&amp;gt; Set(Coord) {
  set.new()
  |&amp;gt; set.insert(pair.first(pair))
  |&amp;gt; set.insert(pair.second(pair))
  |&amp;gt; direction_1(bounds, pair)
  |&amp;gt; direction_2(bounds, pair)
}

fn direction_1(set: Set(Coord), bounds: Bounds, pair: #(Coord, Coord)) -&amp;gt; Set(Coord) {
  let #(x1, y1) = pair.first(pair)
  let #(x2, y2) = pair.second(pair)
  let d1 = #(2 * x1 - x2, 2 * y1 - y2)
  case in_bounds(bounds, d1) {
    True -&amp;gt; set |&amp;gt; set.insert(d1) |&amp;gt; direction_1(bounds, #(d1, #(x1, y1)))
    False -&amp;gt; set
  }
}

fn direction_2(set: Set(Coord), bounds: Bounds, pair: #(Coord, Coord)) -&amp;gt; Set(Coord) {
  let #(x1, y1) = pair.first(pair)
  let #(x2, y2) = pair.second(pair)
  let d2 = #(2 * x2 - x1, 2 * y2 - y1)
  case in_bounds(bounds, d2) {
    True -&amp;gt; set |&amp;gt; set.insert(d2) |&amp;gt; direction_2(bounds, #(#(x2, y2), d2))
    False -&amp;gt; set
  }
}

fn part2(input: String) -&amp;gt; Int {
  let #(bounds, map) = parse_input(input)
  map
  |&amp;gt; dict.fold(set.new(), fn(antinodes, _, positions) {
    positions
    |&amp;gt; list.combination_pairs
    |&amp;gt; list.fold(antinodes, fn(antinodes, pair) {
      set.union(antinodes, get_antinodes(bounds, pair))
    })
  })
  |&amp;gt; set.size
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #7 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Sat, 07 Dec 2024 16:16:34 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-7-in-gleam-3013</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-7-in-gleam-3013</guid>
      <description>&lt;p&gt;I really enjoyed today's puzzle. At first glance, it seems pretty complicated, but if you know how to code up a backtracking algorithm, there isn't too much else to it. I refactored my solution to part 1 to make solving part 2 straightforward. It took a while to run, but I didn't have to implement caching to get it to finish in a reasonable time. Well, that's all I have for today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list.{Continue, Stop}
import gleam/string
import gleam/result
import simplifile as file

const example = "
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 3749 = part1(example)
  let assert 11387 = part2(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

fn parse_input(input: String) -&amp;gt; List(#(Int, List(Int))) {
  input
  |&amp;gt; string.trim
  |&amp;gt; string.split("\n")
  |&amp;gt; list.map(fn(line) {
    let assert [result, params] = string.split(line, ":")
    let assert Ok(result) = int.parse(result)
    let params =
      params
      |&amp;gt; string.trim
      |&amp;gt; string.split(" ")
      |&amp;gt; list.map(fn(param) {
        param
        |&amp;gt; int.parse
        |&amp;gt; result.unwrap(0)
      })
    #(result, params)
  })
}

type EquationResult {
  Solvable
  Unsolvable
}

fn solve_equation(result: Int, params: List(Int), operators: List(fn(Int, Int) -&amp;gt; Int)) -&amp;gt; EquationResult {
  case params {
    [a] if a == result -&amp;gt; Solvable
    [a] -&amp;gt; Unsolvable
    [a, b, ..rest] -&amp;gt; {
      list.fold_until(operators, Unsolvable, fn(solvable, op) {
        case solve_equation(result, [op(a, b), ..rest], operators) {
          Unsolvable -&amp;gt; Continue(solvable)
          Solvable -&amp;gt; Stop(Solvable)
        }
      })
    }
    _ -&amp;gt; Unsolvable
  }
}

fn calibration_result(input: String, operators: List(fn(Int, Int) -&amp;gt; Int)) -&amp;gt; Int {
  input
  |&amp;gt; parse_input
  |&amp;gt; list.fold(0, fn(count, equation) {
    let #(result, params) = equation
    case solve_equation(result, params, operators) {
      Solvable -&amp;gt; count + result 
      Unsolvable -&amp;gt; count
    }
  })
}

fn part1(input: String) -&amp;gt; Int {
  calibration_result(input, [int.multiply, int.add])
}

fn concat(a: Int, b: Int) -&amp;gt; Int {
  int.to_string(a)
  |&amp;gt; string.append(int.to_string(b))
  |&amp;gt; int.parse
  |&amp;gt; result.unwrap(0)
}

fn part2(input: String) -&amp;gt; Int {
  calibration_result(input, [int.multiply, int.add, concat])
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
    <item>
      <title>Advent of Code #6 (in Gleam)</title>
      <dc:creator>Caleb Weeks</dc:creator>
      <pubDate>Fri, 06 Dec 2024 19:23:11 +0000</pubDate>
      <link>https://forem.com/sethcalebweeks/advent-of-code-6-in-gleam-3j2a</link>
      <guid>https://forem.com/sethcalebweeks/advent-of-code-6-in-gleam-3j2a</guid>
      <description>&lt;p&gt;Today was the first day I decided not to stay up to solve the puzzles right as they come out. Where I live, the puzzles come out at midnight, and for the first few days, I'm usually pretty confident that I can knock out a solution in about half an hour to an hour.&lt;/p&gt;

&lt;p&gt;This puzzle was definitely a step up, particularly for part 2. It was the first time this year where I submitted a wrong answer and the first time I had to wait more than a second for the code to run. But I haven't had to reach for any advanced techniques yet. My solution is still pretty much the most straightforward approach.&lt;/p&gt;

&lt;p&gt;Typically, these sorts of puzzles where you have to check a lot of things will start to benefit from divide and conquer techniques, caching strategies, or analytical approaches. The one 'optimization' I applied is I only tried placing new barriers along the standard route, but this is only a reduction of about 75% of the search space, which isn't really the defining dimension of the problem.&lt;/p&gt;

&lt;p&gt;The other thing I did differently in this solution compared to previous solutions is using types and records. This helped me keep track in my head of the different pieces of data, and provides a nicer interface for pattern matching and accessing record fields. Here's my solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gleam/int
import gleam/io
import gleam/list
import gleam/string
import gleam/result
import gleam/option
import gleam/set.{type Set}
import gleam/dict.{type Dict, insert}
import simplifile as file

const example = "
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
"

pub fn main() {
  let assert Ok(input) = file.read("input")
  let assert 41 = part1(example)
  let assert 6 = part2(example)
  part1(input) |&amp;gt; int.to_string |&amp;gt; io.println
  part2(input) |&amp;gt; int.to_string |&amp;gt; io.println
}

type Coord = #(Int, Int)
type Direction {
  Up
  Down
  Left
  Right
}
type Guard {
  Guard(position: Coord, direction: Direction)
}
type Space {
  Blank
  Barrier
}
type Map = Dict(Coord, Space)

fn parse_input(input: String) -&amp;gt; #(Map, Guard) {
  input
  |&amp;gt; string.trim
  |&amp;gt; string.split("\n")
  |&amp;gt; list.index_fold(#(dict.new(), Guard(#(0, 0), Up)), fn(init, line, x) {
    line
    |&amp;gt; string.to_graphemes
    |&amp;gt; list.index_fold(init, fn(init, char, y) {
      let #(map, start) = init
      case char {
        "." -&amp;gt; #(insert(map, #(x, y), Blank), start)
        "#" -&amp;gt; #(insert(map, #(x, y), Barrier), start)
        "^" -&amp;gt; #(insert(map, #(x, y), Blank), Guard(#(x, y), Up))
        "v" -&amp;gt; #(insert(map, #(x, y), Blank), Guard(#(x, y), Down))
        "&amp;gt;" -&amp;gt; #(insert(map, #(x, y), Blank), Guard(#(x, y), Right))
        "&amp;lt;" -&amp;gt; #(insert(map, #(x, y), Blank), Guard(#(x, y), Left))
        _ -&amp;gt; init
      }
    })
  })
}

fn rotate(guard: Guard) -&amp;gt; Guard {
  case guard {
    Guard(position, Up) -&amp;gt; Guard(position, Right)
    Guard(position, Right) -&amp;gt; Guard(position, Down)
    Guard(position, Down) -&amp;gt; Guard(position, Left)
    Guard(position, Left) -&amp;gt; Guard(position, Up)
  }
}

fn move(map: Map, guard: Guard) -&amp;gt; Result(Guard, Nil) {
  let Guard(#(x, y), direction) = guard
  let next_position = case direction {
    Up -&amp;gt; #(x - 1, y)
    Down -&amp;gt; #(x + 1, y)
    Left -&amp;gt; #(x, y - 1)
    Right -&amp;gt; #(x, y + 1)
  }

  map
  |&amp;gt; dict.get(next_position)
  |&amp;gt; result.map(fn(space) {
    case space {
      Blank -&amp;gt; Guard(next_position, direction)
      Barrier -&amp;gt; rotate(guard)
    }
  })
}

fn standard_route(visited: Set(Coord), map: Map, guard: Guard) -&amp;gt; Set(Coord) {
  case move(map, guard) {
    Ok(guard) -&amp;gt; standard_route(set.insert(visited, guard.position), map, guard)
    _ -&amp;gt; visited
  }
}

fn part1(input: String) -&amp;gt; Int {
  let #(map, start) = parse_input(input)
  set.new()
  |&amp;gt; set.insert(start.position)
  |&amp;gt; standard_route(map, start)
  |&amp;gt; set.size
}

type Route {
  Exit
  Loop
}

fn route(visited: Set(Guard), map: Map, guard: Guard) -&amp;gt; Route {
  case move(map, guard) {
    Ok(guard) -&amp;gt;
      case set.contains(visited, guard) {
        True -&amp;gt; Loop
        False -&amp;gt; route(set.insert(visited, guard), map, guard)
      }
    _ -&amp;gt; Exit
  }
}

fn part2(input: String) -&amp;gt; Int {
  let #(map, start) = parse_input(input)

  set.new()
  |&amp;gt; set.delete(start.position)
  |&amp;gt; standard_route(map, start)
  |&amp;gt; set.fold(0, fn(count, coord) {
    let new_map = 
      map
      |&amp;gt; dict.upsert(coord, fn(space) {
        space
        |&amp;gt; option.map(fn(_) { Barrier })
        |&amp;gt; option.unwrap(Blank)
      })
    case route(set.new(), new_map, start) {
      Loop -&amp;gt; count + 1
      Exit -&amp;gt; count
    }
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>adventofcode</category>
      <category>gleam</category>
    </item>
  </channel>
</rss>
