From 2d99878eff6d1f093f2113479bed4981b0db824c Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Thu, 5 Dec 2024 07:49:07 +0100 Subject: [PATCH] feat(aoc2024): day5 (#53) * feat(aoc2024): day5 part1 * feat(aoc2024): day5 part2 --- aoc2024.ps1 | 4 +- visp/examples/aoc2024/common.visp | 15 +- visp/examples/aoc2024/day5.visp | 161 ++++++++++++++++++ visp/examples/aoc2024/inputs/day5_example.txt | 28 +++ 4 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 visp/examples/aoc2024/day5.visp create mode 100644 visp/examples/aoc2024/inputs/day5_example.txt diff --git a/aoc2024.ps1 b/aoc2024.ps1 index 5910628..a595b85 100644 --- a/aoc2024.ps1 +++ b/aoc2024.ps1 @@ -6,8 +6,8 @@ param ( [switch] $All, [switch] $NoBuild, [ValidateSet( - "day1", "day2", "day3", "day4" - # , "day5", "day6", "day7", "day8", "day9" + "day1", "day2", "day3", "day4", "day5" + #, "day6", "day7", "day8", "day9" # , "day10", "day11", "day12", "day13", "day14", "day15", "day16" # , "day17", "day18", "day19", "day20", "day21", "day22" # , "day23", "day24", "day25" diff --git a/visp/examples/aoc2024/common.visp b/visp/examples/aoc2024/common.visp index e41919a..70d4ed9 100644 --- a/visp/examples/aoc2024/common.visp +++ b/visp/examples/aoc2024/common.visp @@ -21,9 +21,15 @@ (fn EnumerateSpanSplitLines ([text: string]) (.EnumerateSplitSubstrings text [| #\lf #\cr |] commonSplitOptions)) -(fn EnumareteSpanSplitChars ([ch: array] [text: ReadOnlySpan]) +(fn EnumerateSpanSplitLinesWithoutEmpty ([text: string]) + (.EnumerateSplitSubstrings text [| #\lf #\cr |] (bor StringSplitOptions.TrimEntries StringSplitOptions.RemoveEmptyEntries))) + +(fn EnumerateSpanSplitChars ([ch: array] [text: ReadOnlySpan]) (.EnumerateSplitSubstrings text ch commonSplitOptions)) +(fn EnumerateSpanSplitCharsWithoutEmpty ([ch: array] [text: ReadOnlySpan]) + (.EnumerateSplitSubstrings text ch (bor StringSplitOptions.TrimEntries StringSplitOptions.RemoveEmptyEntries))) + (fn EnumerateSpaceSeparated ([text: ReadOnlySpan]) (.EnumerateSplitSubstrings text [| #\space |] (bor StringSplitOptions.TrimEntries StringSplitOptions.RemoveEmptyEntries))) @@ -32,6 +38,13 @@ (printfn "file: %s" filepath) (System.IO.File.ReadAllText filepath)) +(syntax-macro Macro_ReadWhile + ([_ (id enu) body ...] + (while (.MoveNext enu) + (let id (+Current enu)) + (begin body ...) + ))) + (syntax-macro Macro_ReadWhileNotEmpty ([_ (id enu) body ...] (while (.MoveNext enu) diff --git a/visp/examples/aoc2024/day5.visp b/visp/examples/aoc2024/day5.visp new file mode 100644 index 0000000..588d0ab --- /dev/null +++ b/visp/examples/aoc2024/day5.visp @@ -0,0 +1,161 @@ + +;; Copyright 2024 Ville Penttinen +;; Distributed under the MIT License. +;; https://github.com/vipentti/visp-fs/blob/main/LICENSE.md +;; +;; for basic syntax highlighting +;; vim: set syntax=clojure: + +;; +;; day5 +;; +;; Include common utlities +(include "./common.visp") + +;; Functions & types +(record Rule + [before: int32] + [after: int32]) + +(typedef PageUpdate list) +(typedef Rules Map>) + +(record PageOrder + [rules : Rules] + [updates: list] +) + +(fn ParseFile ([text: string]) + (mut lines (EnumerateSpanSplitLinesWithoutEmpty text)) + + (mut reading_first_section true) + + (let rules (new ResizeArray<_>)) + (let updates (new ResizeArray<_>)) + + (mut new_rules Map.empty) + + (Macro_ReadWhile [line lines] + (let index (.IndexOf line #\|)) + + (if (> index -1) + (begin + (mut parts (EnumerateSpanSplitCharsWithoutEmpty [|#\||] line)) + (let f (span->int32 (Macro_ReadNext parts))) + (let s (span->int32 (Macro_ReadNext parts))) + + (up! new_rules + (Map.change f #( + match %1 + [(Some its) (Some (Set.add s its))] + [_ (Some (Set.singleton s))]) + )) + + ;; (printfn "%A %A" f s) + ;; (.Add rules {| [before f] [after s] |}) + ) + (begin + (mut parts (EnumerateSpanSplitCharsWithoutEmpty [|#\,|] line)) + (let arr (new ResizeArray<_>)) + (Macro_ReadWhile [part parts] + (.Add arr (span->int32 part))) + + ;; (printfn "%A" arr) + + (.Add updates (->> arr Seq.toList))) + ) + ;; Read contents here + ()) + + {| + [rules new_rules] + [updates (->> updates Seq.toList)] + |} + ) + +(fn rec Rules_IsBeforeAllOf [(before: int32) (rest: PageUpdate) (rules: Rules)] + (match (Map.tryFind before rules) + [(Some afters) + ;; + (->> rest + (List.forall #(begin + (or (Set.contains %1 afters) + (not (Rules_IsBeforeAllOf %1 (| before |) rules))) + ))) + ] + [None + ;; (printfn "here? %A %A" before rest) + (->> rest + (List.forall #( + not (Rules_IsBeforeAllOf %1 (| before |) rules) + ))) + ])) + +(fn UpdateInCorrectOrder [(upd: PageUpdate) (rules: Rules)] + (fn rec loop ([up: PageUpdate] [rules: Rules] (correct: bool)) + (cond_ + [(not correct) correct] + [_ + (match up + [(cur :: rest) (loop rest rules (Rules_IsBeforeAllOf cur rest rules)) ] + [[] correct] + )])) + (loop upd rules true)) + +(fn Update_PickMiddle [(upd: PageUpdate)] + (let len (/ (List.length upd) 2)) + ;; (printfn "valid %A %A" upd len) + (List.item len upd)) + +(fn Update_Fix [(rules: Rules) (upd: PageUpdate)] + ;; (printfn "valid %A %A" upd len) + (fn rec loop ([up: PageUpdate] [rules: Rules] [fixed: PageUpdate]) + (match up + [[] (List.rev fixed)] + [(cur :: []) + (-> (cons cur fixed) (List.rev)) + ] + [(cur :: next :: rest) + (cond_ + [(Rules_IsBeforeAllOf cur (cons next rest) rules) + (loop (cons next rest) rules (cons cur fixed)) + ] + [(Rules_IsBeforeAllOf next (cons cur rest) rules) + (loop (cons cur rest) rules (cons next fixed)) + ] + [_ (loop (List.concat (| rest (| cur next |)|)) rules fixed)] + ) + ] + ) + ) + + (loop upd rules (||))) + +(fn Part1 (parsedInput) + ;; (printfn "%A" parsedInput) + ;; Implement part1 + (->> (+updates parsedInput) + (List.filter #(UpdateInCorrectOrder %1 (+rules parsedInput))) + (List.map Update_PickMiddle) + (List.sum))) + +(fn Part2 (parsedInput) + ;; Implement part2 + (->> (+updates parsedInput) + (List.filter #(not (UpdateInCorrectOrder %1 (+rules parsedInput)))) + (List.map (Update_Fix (+rules parsedInput))) + (List.map Update_PickMiddle) + (List.sum))) + +;; Implementation + +(let parsed (-> (ReadInput "day5") ParseFile)) + +;; Expected results +(let PART1_EXPECTED_RESULT (if IS_EXAMPLE 143 4637)) +(let PART2_EXPECTED_RESULT (if IS_EXAMPLE 123 6370)) + +(WriteResult "part1" (-> parsed Part1) PART1_EXPECTED_RESULT) +(WriteResult "part2" (-> parsed Part2) PART2_EXPECTED_RESULT) + +() diff --git a/visp/examples/aoc2024/inputs/day5_example.txt b/visp/examples/aoc2024/inputs/day5_example.txt new file mode 100644 index 0000000..9d146d6 --- /dev/null +++ b/visp/examples/aoc2024/inputs/day5_example.txt @@ -0,0 +1,28 @@ +47|53 +97|13 +97|61 +97|47 +75|29 +61|13 +75|53 +29|13 +97|29 +53|29 +61|53 +97|53 +61|29 +47|13 +75|47 +97|75 +47|61 +75|61 +47|29 +75|13 +53|13 + +75,47,61,53,29 +97,61,53,29,13 +75,29,13 +75,97,47,61,53 +61,13,29 +97,13,75,29,47