/ 2025 / 12 / p1.tri
p1.tri
 1  import "trilogy:io" use readall
 2  import "trilogy:debug" use dbg
 3  import "trilogy:bits" as bits use pop_count, concat
 4  import "trilogy:parsec" as p use parse, run_parser, apply, followed_by, string, integer, char_of, per_line, many_1, sep_by, char
 5  import "trilogy:array" use length, filter, flatten, reduce, zip, map, rotate_cw, collect
 6  import "trilogy:string" use split, chomp
 7  import "trilogy:iterator" as it
 8  
 9  proc point!() {
10    return '#' == apply <| char_of "#."
11  }
12  
13  proc present!() {
14    apply <| followed_by (string ":\n") integer
15    return apply <| per_line <| many_1 point
16  }
17  
18  proc tree!() {
19    let w = apply integer
20    apply <| char 'x'
21    let h = apply integer
22    apply <| string ": "
23    let req = apply <| sep_by (char ' ') integer
24    return w:h:req
25  }
26  
27  func id x = x
28  
29  func area present = flatten present |> filter id |> length
30  
31  func spread _ [bb] = bb
32  func spread width [bb, ..rest] = bb | (spread width rest ~~> width)
33  func expand size bb = bb | bits::init_zero size
34  
35  func possible areas w:h:requirements =
36    let total = areas
37    |> zip requirements
38    |> map (fn a:r. a * r)
39    |> reduce (+),
40    total <= w * h
41  
42  func trivial w:h:requirements =
43    let total = requirements
44    |> map ((*) 9)
45    |> reduce (+),
46    total <= w * h 
47  
48  func rotations arr =
49    let brr = rotate_cw arr,
50    let crr = rotate_cw brr,
51    let drr = rotate_cw crr,
52    [arr, brr, crr, drr]
53  
54  func place_all _ _ _ [] = true
55  func place_all w h state [present, ..rest] =
56    it::range 0 (h - 3)
57    |> it::map ((*) w)
58    |> it::flat_map (fn y. it::range 0 (w - 3) |> it::map ((+) y))
59    |> it::flat_map (fn offset. it::from present |> it::map (fn p. p ~> offset))
60    |> it::filter (fn p. pop_count (state & p) == 0)
61    |> it::map ((|) state)
62    |> it::any (fn st. place_all w h st rest)
63  
64  func solve presents w:h:requirements =
65    presents
66    |> map (rotations >> map (map (bits::from_array) >> spread w >> expand (w * h)))
67    |> zip requirements
68    |> it::from
69    |> it::flat_map (fn n:arr. it::repeat n arr)
70    |> collect
71    |> place_all w h (bits::init_zero <| w * h)
72  
73  proc main!() {
74    let presents:rest = run_parser (per_line present) <| chomp readall!()
75    rest
76    |> split "\n"
77    |> it::from
78    |> it::map (parse tree)
79    |> it::filter (possible <| map area presents)
80    |> it::filter (fn tree. trivial tree || solve presents tree)
81    |> it::count
82    |> dbg
83  }