Zulip Chat Archive
Stream: general
Topic: Advent of Code 2024 - Day 8
Ayhon (Dec 09 2024 at 10:03):
Posting my solution of Advent of code day 8, trying to encourage others to do the same. Didn't feel too functional today, so I let Id.run
do most of the heavy lifting.
namespace Day8
open Std.Internal.Parsec.String
open Std.Internal.Parsec (skip many many1 attempt)
def andThen(a: α)(b: Prop)[Decidable b]: Option α := if b then some a else none
def List.allUnorderedPairs: List α -> List (α × α)
| x :: xs => xs.map ((x,·)) ++ List.allUnorderedPairs xs
| [] => []
abbrev Position := Nat × Nat
structure Input where
antenas: Std.HashMap Char (Std.HashSet Position)
bounds: Nat × Nat
def parse_input(ls: String): Input := Id.run do
let mut antenas: Std.HashMap Char (Std.HashSet Position) := ∅
let lines := ls.split (· == '\n') |>.filter (¬ ·.isEmpty)
let positions := lines
|>.enum
|>.flatMap (fun (i,row) =>
row.toList
|>.enum
|>.filterMap (fun (j, c) =>
(c != '.') |> andThen (c,(i,j))
)
)
for (c, (i,j)) in positions do
antenas := antenas.getD c ∅
|>.insert (i,j)
|> Std.HashMap.insert antenas c
return {antenas := antenas, bounds := (lines.length, lines[0]!.length)}
namespace Part1
def solve(inp: Input): Int := Id.run do -- brrr
let mut antinodes: Std.HashSet Position := ∅
let getAntinodes (p1 p2: Position): List Position :=
let (x1, y1) := (Int.ofNat p1.1, Int.ofNat p1.2)
let (x2, y2) := (Int.ofNat p2.1, Int.ofNat p2.2)
let (dx, dy) := (x2 - x1, y2 - y1)
[(x2 + dx, y2 + dy), (x1 - dx, y1 - dy)]
|>.filterMap (fun (x,y) =>
(0 <= x && x < inp.bounds.1 && 0 <= y && y < inp.bounds.2)
|> andThen ((x.toNat,y.toNat))
)
for (_, positions) in inp.antenas do
for (p1,p2) in List.allUnorderedPairs positions.toList do
antinodes := antinodes.insertMany $ getAntinodes p1 p2
return antinodes.size
end Part1
namespace Part2
def solve(inp: Input): Int := Id.run do -- brrr
let mut antinodes: Std.HashSet Position := ∅
let getAntinodes (p1 p2: Position): List Position :=
let (x1, y1) := (Int.ofNat p1.1, Int.ofNat p1.2)
let (x2, y2) := (Int.ofNat p2.1, Int.ofNat p2.2)
let (dx, dy) := (x2 - x1, y2 - y1)
let (w, h) := inp.bounds
let rec getAntinodesInDirection (d: Int) (start: Int × Int)
| 0 => panic "Too much"
| lim+1 =>
let (x,y) := (start.1 + d*dx, start.2 + d*dy)
if 0 <= x && x < w && 0 <= y && y < h then
(x.toNat,y.toNat) :: getAntinodesInDirection d (x,y) lim
else []
let limit := (w.max h)
getAntinodesInDirection 1 (x1,y1) limit ++ getAntinodesInDirection (-1) (x2,y2) limit
for (_, positions) in inp.antenas do
for (p1,p2) in List.allUnorderedPairs positions.toList do
antinodes := antinodes.insertMany $ getAntinodes p1 p2
return antinodes.size
end Part2
Matt Russell (Dec 09 2024 at 23:00):
https://github.com/mdr/aoc-2024/blob/main/Aoc2024/Day08/Solve.lean
Paul Nelson (Dec 29 2024 at 20:29):
import Lean.Data.HashSet
abbrev Grid := Array (Array Char)
def parse (input : String) : Grid :=
input.splitOn "\n" |>.filter (!·.isEmpty)
|>.map String.toList |>.map List.toArray |> List.toArray
abbrev Vec := Int × Int
instance : Add Vec where add p q := (p.1 + q.1, p.2 + q.2)
instance : Sub Vec where sub p q := (p.1 - q.1, p.2 - q.2)
instance : HMul Nat Vec Vec where hMul n p := (n * p.1, n * p.2)
def inBounds (height width : Nat) (p : Vec) : Bool :=
let ⟨r, c⟩ := p; 0 ≤ r ∧ r < height ∧ 0 ≤ c ∧ c < width
def antinodes (height width : Nat) (a b : Vec) : List Vec :=
let v := a - b; [a + v, b - v].filter (inBounds height width)
def Vec.primitiveMultiple (v : Vec) : Vec :=
let d := Int.gcd v.1 v.2; (v.1 / d, v.2 / d)
def antinodes2 (height width : Nat) (a b : Vec) : List Vec :=
let v := (a - b).primitiveMultiple
let max_steps := max height width
let forward := List.range max_steps
|>.map (fun n => a + n * v)
|>.takeWhile (inBounds height width)
let backward := List.range' 1 max_steps
|>.map (fun n => a - n * v)
|>.takeWhile (inBounds height width)
forward ++ backward
def Array.enum (ar : Array α) : Array (Nat × α) :=
Array.mapIdx (fun i x => (i, x)) ar
def Grid.groupByChar (grid : Grid) : List (List Vec) :=
let entries := grid.enum.flatMap fun (r, row) =>
row.enum.filterMap fun (c, ch) =>
if ch = '.' then none else some (ch, (r, c))
entries.qsort (lt := (fun a b => a.1 < b.1))
|>.toList.splitBy (·.1 == ·.1)
|>.map (·.map (fun (_, (r,c)) => (Int.ofNat r, Int.ofNat c)))
def List.orderedPairs {α : Type} (xs : List α) : List (α × α) :=
xs.enum.foldr (fun (i, x) acc =>
xs.drop (i + 1) |>.map (fun y => (x, y)) |>.append acc
) []
def count (grid : Grid) (f : Nat → Nat → Vec → Vec → List Vec) : Nat :=
grid.groupByChar
|>.flatMap (·.orderedPairs)
|>.flatMap (fun (a, b) => f grid.size grid[0]!.size a b)
|>.foldl Std.HashSet.insert Std.HashSet.empty
|>.size
def part1 (grid : Grid) : Nat := count grid antinodes
def part2 (grid : Grid) : Nat := count grid antinodes2
Last updated: May 02 2025 at 03:31 UTC