Zulip Chat Archive
Stream: general
Topic: Advent of Code 2024 - Day 11
Chris Henson (Dec 12 2024 at 12:46):
For day 11, I was pretty happy with writing memoization as a state monad, did anyone else take a similar approach? (Here's a link to my repo: https://github.com/chenson2018/advent-of-code/blob/main/2024/lean/AoC/Day11.lean)
Ayhon (Dec 13 2024 at 00:55):
I have been falling behind on my advent of code problems. Currently I'm at day 10, once I've come over to day 11 I'll post my solution and check out yours!
Ayhon (Dec 15 2024 at 00:41):
As promised, today I got to day 11 and here's my solution. I feel like it's very similar to yours, but in my case I make the Cached
effect much less generic. I also defined a sequence
function (which probably already exists somewhere in the standard library, but I con't manage to loogle it) because originally I wanted to do something way more complicated, storing the intermediate generated lists to store results for depths of powers of two. Not viable though :/
Anyways, here's my code
import Std.Internal.Parsec
import Std.Internal.Parsec.String
import Std.Data.HashMap
namespace Day11
open Std.Internal.Parsec.String (pchar pstring skipString skipChar Parser digits digit ws)
open Std.Internal.Parsec (skip many many1 attempt)
abbrev Input := List Nat
def parse_input(content: String): Input :=
content.split Char.isWhitespace
|>.filter (¬ ·.isEmpty)
|>.map (·.toNat!)
def log10 (n : Nat) : Nat :=
if n ≥ 10 then log10 (n / 10) + 1 else 1
namespace Part1
def expand(x: Nat): List Nat :=
let n := log10 x
if x == 0 then
[1]
else if n % 2 == 0 then
let mask := 10^(n/2)
[x / mask, x % mask]
else
[x*2024]
def solve(inp: Input): Int :=
inp
|> Nat.repeat (·.flatMap expand) 25
|>.length
#eval (include_str "../inputs/day11.txt")
|> parse_input
|> solve
end Part1
namespace Part2
abbrev Cache := Std.HashMap (Nat × Nat) Nat
abbrev Cached α := StateM Cache α
def sequence(ls: List (Cached α)): Cached (List α) := do match ls with
| [] => return []
| x :: xs => return (<- x) :: (<- sequence xs)
def withCached(args: Nat × Nat)(body: Nat × Nat -> Cached Nat): Cached Nat := do
match (<- get).get? args with
| some result => return result
| none =>
let result <- body args
modify (·.insert args result)
return result
partial def simulate'(times: Nat)(x: Nat): Cached Nat := do
match times with
| 0 => return 1
| _ => withCached (times, x) fun (times, x) =>
Part1.expand x
|>.map (simulate' (times-1))
|> sequence
|>.map List.sum
partial def simulate(times: Nat)(x: Nat): Nat :=
simulate' times x
|>.run ∅
|>.run
|>.1
partial def solve(inp: Input): Int := inp |>.map (simulate 75) |>.sum
#eval (include_str "../inputs/day11.txt")
|> parse_input
|> solve
end Part2
Paul Nelson (Dec 31 2024 at 12:37):
import Std.Data.HashSet
import Mathlib.Logic.Function.Iterate
def parse (input : String) : List Nat :=
input.splitOn "\n" |>.filter (!·.isEmpty) |>.head!
|>.splitOn " " |>.filter (!·.isEmpty) |>.map String.toNat!
def blink : Nat → List Nat
| 0 => [1]
| n =>
let str := n.repr
match str.length % 2 with
| 1 => [2024 * n]
| _ =>
let mid := String.Pos.mk (str.length / 2)
[str.extract 0 mid, str.extract mid str.endPos].map String.toNat!
abbrev State := Std.HashMap Nat Nat
def State.init (input : List Nat) : State :=
input.foldl (·.insert · 1) Std.HashMap.empty
def State.next (state : State) : State := Id.run do
let mut newState := Std.HashMap.empty
for (k, v) in state do
for n in blink k do
newState := newState.insert n (v + newState.getD n 0)
newState
def numberOfStones (fuel : Nat) (input : List Nat) : Nat :=
let finalState := Nat.iterate State.next fuel (State.init input)
finalState.fold (fun acc _ v => acc + v) 0
def part1 : List Nat → Nat := numberOfStones 25
def part2 : List Nat → Nat := numberOfStones 75
Last updated: May 02 2025 at 03:31 UTC