diff options
Diffstat (limited to 'aoc2023-gleam/src/day19/solve.gleam')
-rw-r--r-- | aoc2023-gleam/src/day19/solve.gleam | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/aoc2023-gleam/src/day19/solve.gleam b/aoc2023-gleam/src/day19/solve.gleam new file mode 100644 index 0000000..186e783 --- /dev/null +++ b/aoc2023-gleam/src/day19/solve.gleam @@ -0,0 +1,255 @@ +import adglent.{First, Second} +import gleam/io +import gleam/string +import gleam/dict.{type Dict} +import gleam/order.{type Order, Gt, Lt} +import gleam/regex.{type Match, Match} +import gleam/list +import gleam/option.{Some} +import gleam/int + +type Rating { + XtremelyCool + Musical + Aerodynamic + Shiny +} + +type Part { + Part(x: Int, m: Int, a: Int, s: Int) +} + +type Action { + Accept + Reject + SendTo(String) +} + +type Rule { + If(rating: Rating, comparison: Order, threshold: Int, do: Action) + Just(do: Action) +} + +type Workflow = + Dict(String, List(Rule)) + +type Interval { + Interval(min: Int, max: Int) +} + +type PartRange { + PartRange(x: Interval, m: Interval, a: Interval, s: Interval) +} + +fn parse_workflow(input: String) -> Workflow { + let assert Ok(re) = regex.from_string("(.*){(.*)}") + + use acc, line <- list.fold(string.split(input, "\n"), dict.new()) + let assert [Match(submatches: [Some(name), Some(all_rules)], ..)] = + regex.scan(re, line) + let rules = + string.split(all_rules, ",") + |> parse_rules + dict.insert(acc, name, rules) +} + +fn parse_rules(rules: List(String)) -> List(Rule) { + let assert Ok(re_rule) = regex.from_string("(.*)(>|<)(.*):(.*)") + use rule <- list.map(rules) + case regex.scan(re_rule, rule) { + [Match(submatches: [Some(r), Some(c), Some(t), Some(i)], ..)] -> + If(to_rating(r), to_comp(c), to_val(t), to_instruction(i)) + _nomatch -> Just(to_instruction(rule)) + } +} + +fn to_instruction(rule: String) { + case rule { + "A" -> Accept + "R" -> Reject + name -> SendTo(name) + } +} + +fn to_rating(rating: String) { + case rating { + "x" -> XtremelyCool + "m" -> Musical + "a" -> Aerodynamic + _s -> Shiny + } +} + +fn get_rating(part: Part, rating: Rating) -> Int { + case rating { + XtremelyCool -> part.x + Musical -> part.m + Aerodynamic -> part.a + Shiny -> part.s + } +} + +fn to_comp(comp: String) { + case comp { + "<" -> Lt + _gt -> Gt + } +} + +fn to_val(val: String) { + let assert Ok(n) = int.parse(val) + n +} + +fn parse_parts(input: String) -> List(Part) { + let assert Ok(re) = regex.from_string("{x=(.*),m=(.*),a=(.*),s=(.*)}") + + use part <- list.map(string.split(input, "\n")) + let assert [Match(submatches: [Some(x), Some(m), Some(a), Some(s)], ..)] = + regex.scan(re, part) + Part(to_val(x), to_val(m), to_val(a), to_val(s)) +} + +fn start_evaluating_workflow(part: Part, workflow: Workflow) -> Int { + evaluate_workflow(part, "in", workflow) +} + +fn evaluate_workflow(part: Part, name: String, workflow: Workflow) -> Int { + let assert Ok(rules) = dict.get(workflow, name) + case evaluate_rules(part, rules) { + Accept -> part.x + part.m + part.a + part.s + Reject -> 0 + SendTo(name) -> evaluate_workflow(part, name, workflow) + } +} + +fn evaluate_rules(part: Part, rules: List(Rule)) -> Action { + case rules { + [] -> panic + [Just(do), ..] -> do + [If(rating, comparison, threshold, do), ..rest] -> + case int.compare(get_rating(part, rating), threshold) == comparison { + True -> do + False -> evaluate_rules(part, rest) + } + } +} + +pub fn part1(input: String) { + let assert Ok(#(workflows_str, parts_str)) = string.split_once(input, "\n\n") + + let workflows = parse_workflow(workflows_str) + let parts = parse_parts(parts_str) + + list.map(parts, start_evaluating_workflow(_, workflows)) + |> int.sum + |> string.inspect +} + +fn size(interval: Interval) { + interval.max - interval.min + 1 +} + +fn all_in_range(pr: PartRange) { + size(pr.x) * size(pr.m) * size(pr.a) * size(pr.s) +} + +fn get_partrange(pr: PartRange, rating: Rating) -> Interval { + case rating { + XtremelyCool -> pr.x + Musical -> pr.m + Aerodynamic -> pr.a + Shiny -> pr.s + } +} + +fn update_partrange(pr: PartRange, rating: Rating, i: Interval) -> PartRange { + case rating { + XtremelyCool -> PartRange(..pr, x: i) + Musical -> PartRange(..pr, m: i) + Aerodynamic -> PartRange(..pr, a: i) + Shiny -> PartRange(..pr, s: i) + } +} + +pub fn part2(input: String) { + let assert Ok(#(workflows_str, _)) = string.split_once(input, "\n\n") + + let workflow = parse_workflow(workflows_str) + let start = Interval(1, 4000) + + PartRange(start, start, start, start) + |> evaluate_workflow_on_range("in", workflow) + |> string.inspect +} + +fn evaluate_workflow_on_range( + pr: PartRange, + name: String, + workflow: Workflow, +) -> Int { + let assert Ok(rules) = dict.get(workflow, name) + evaluate_rules_on_range(pr, rules, workflow) +} + +fn evaluate_rules_on_range( + pr: PartRange, + rules: List(Rule), + workflow: Workflow, +) -> Int { + case rules { + [Just(Accept), ..] -> all_in_range(pr) + [Just(Reject), ..] -> 0 + [Just(SendTo(name)), ..] -> evaluate_workflow_on_range(pr, name, workflow) + [If(rating, comparison, t, action), ..rest] -> { + let mod_i = get_partrange(pr, rating) + case comparison { + Lt -> + split_range( + keep: update_partrange(pr, rating, Interval(mod_i.min, t - 1)), + and_do: action, + pass: update_partrange(pr, rating, Interval(t, mod_i.max)), + and_eval: rest, + with: workflow, + ) + _gt -> + split_range( + keep: update_partrange(pr, rating, Interval(t + 1, mod_i.max)), + and_do: action, + pass: update_partrange(pr, rating, Interval(mod_i.min, t)), + and_eval: rest, + with: workflow, + ) + } + } + [] -> panic + } +} + +fn split_range( + keep keep: PartRange, + and_do action: Action, + pass pass: PartRange, + and_eval rest: List(Rule), + with workflow: Workflow, +) -> Int { + int.add( + evaluate_rules_on_range(keep, [Just(action)], workflow), + evaluate_rules_on_range(pass, rest, workflow), + ) +} + +pub fn main() { + let assert Ok(part) = adglent.get_part() + let assert Ok(input) = adglent.get_input("19") + case part { + First -> + part1(input) + |> adglent.inspect + |> io.println + Second -> + part2(input) + |> adglent.inspect + |> io.println + } +} |