-
Notifications
You must be signed in to change notification settings - Fork 0
/
day13.lean
73 lines (60 loc) · 2.04 KB
/
day13.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import Init.Data.String.Basic
import Init.Data.Int.Basic
import Init.Data.Ord
import Init.Data.Option.BasicAux
open Ordering
open Prod
open List
-- Helpers for points
def Point : Type := Nat × Nat
instance : BEq Point where
beq p q := p.fst == q.fst && p.snd == q.snd
def parsePoint (s : String) : Point :=
match s.splitOn "," with
| sx :: sy :: _ =>
match (sx.toNat?, sy.toNat?) with
| (some x, some y) => (x, y)
| (_, _) => (0, 0)
| _ => (0, 0)
-- Helpers for fold instructions
abbrev Fold := Char × Nat
def parseFold (s : String) : Fold :=
match s.splitOn "=" with
| pre :: sc :: _ =>
match sc.toNat? with
| some c => (pre.back, c)
| _ => ('?', 0)
| _ => ('?', 0)
def ApplyFold (f : Fold) (p : Point) : Point :=
match p, f with
| (x, y), ('x', x') => (if x <= x' then x else 2 * x' - x, y)
| (x, y), ('y', y') => (x, if y <= y' then y else 2 * y' - y)
| _, _ => p
-- compute answers
def compute (ls : List String) : (Nat × List String) := do
let (points, folds) := ls.span (not ∘ String.isEmpty)
let points := points.map parsePoint
let folds := (folds.drop 1).map parseFold
let applyAllFolds fs := fs.foldl (flip $ List.map ∘ ApplyFold) points
let points' := applyAllFolds (folds.take 1)
let finalPoints := applyAllFolds folds
let mx := ((finalPoints.map fst).maximum?).get!
let my := ((finalPoints.map snd).maximum?).get!
let getLoc p := if finalPoints.elem p then '∎' else ' '
let xs := 0 :: reverse (iota mx)
let ys := 0 :: reverse (iota my)
let grid := ys.map (fun y => String.mk $ xs.map (fun x => getLoc (x, y)))
return (points'.eraseDups.length, grid)
-- input/output
partial def getLines (h : IO.FS.Stream) : IO (List String) := do
let rec loop (ls : List String) := do
let line ← h.getLine
if line.isEmpty then return ls
else loop (line.trim :: ls)
List.reverse <$> loop []
def main : IO Unit := do
let cin ← IO.getStdin
let input ← getLines cin
let (part1, part2) := compute input
IO.println part1
IO.println $ "\n".intercalate part2