aboutsummaryrefslogtreecommitdiff
path: root/aoc2023/src/day12/solve.gleam
blob: 893b83c8d697966ced63b898fa06a6a2abfdad75 (plain)
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import adglent.{First, Second}
import gleam/io
import gleam/string
import gleam/list
import gleam/int
import gleam/result
import utilities/memo.{type Cache}

type ParserState =
  #(String, List(Int), Int, Bool)

fn parse_folds(input: String, folds: Int) {
  let records = string.split(input, "\n")
  use record <- list.map(records)
  let assert Ok(#(template, sets_str)) = string.split_once(record, " ")

  let template =
    template
    |> list.repeat(folds)
    |> list.intersperse("?")
    |> string.concat
  let sets =
    sets_str
    |> string.split(",")
    |> list.map(int.parse)
    |> result.values
    |> list.repeat(folds)
    |> list.flatten()

  #(template, sets)
}

fn do_count(
  template: String,
  groups: List(Int),
  left: Int,
  gap: Bool,
  cache: Cache(ParserState, Int),
) -> Int {
  use <- memo.memoize(cache, #(template, groups, left, gap))
  case template, groups, left, gap {
    "", [], 0, _ -> 1
    "?" <> t_rest, [g, ..g_rest], 0, False ->
      do_count(t_rest, g_rest, g - 1, g == 1, cache)
      + do_count(t_rest, groups, 0, False, cache)
    "?" <> t_rest, [], 0, False
    | "?" <> t_rest, _, 0, True
    | "." <> t_rest, _, 0, _ -> do_count(t_rest, groups, 0, False, cache)
    "#" <> t_rest, [g, ..g_rest], 0, False ->
      do_count(t_rest, g_rest, g - 1, g == 1, cache)
    "?" <> t_rest, gs, l, False | "#" <> t_rest, gs, l, False ->
      do_count(t_rest, gs, l - 1, l == 1, cache)
    _, _, _, _ -> 0
  }
}

fn count_solutions(acc: Int, condition: #(String, List(Int))) -> Int {
  use cache: Cache(ParserState, Int) <- memo.create()
  let #(template, groups) = condition
  acc + do_count(template, groups, 0, False, cache)
}

pub fn part1(input: String) {
  input
  |> parse_folds(1)
  |> list.fold(0, count_solutions)
  |> string.inspect
}

pub fn part2(input: String) {
  input
  |> parse_folds(5)
  |> list.fold(0, count_solutions)
  |> string.inspect
}

pub fn main() {
  let assert Ok(part) = adglent.get_part()
  let assert Ok(input) = adglent.get_input("12")
  case part {
    First ->
      part1(input)
      |> adglent.inspect
      |> io.println
    Second ->
      part2(input)
      |> adglent.inspect
      |> io.println
  }
}