day_5
To solve part 1, we create ordering rule from list of strings “a|b” where a needs to come before b
Since numbers don’t have to be in an ordering rule, we use the negation, so “a|b” results in a rule #(b, a), which, if violated, fails the check. To check for a list [a,b,c,d] we check the ordering for #(a, [b,c,d]), #(b, [c,d]) and #(c, [d])
Part 2 is tricky. In general, the rules form a cyclic graph, which means we can’t just flatten them to a pre-set order. Since this is a naive approach we run the risk of entering loops where the recursive solve function keeps swapping forever.
We could build some sort of per-element pre-solver to check which order we need to do the swaps in to get a valid page (which is NP-hard, and, more importantly, effort) orrrrrrr we just randomize the rules every time, lol
Functions
pub fn check_page(
page: List(Int),
rules: List(#(Int, Int)),
) -> Bool
Empty and 1 element lists are always valid For the others, check if the first element satisfies all rules with the rest of the list (pairwise) and then check the rest of the list
pub fn get_valid_pages(data: List(String)) -> List(List(Int))
Given a list of pages and rules, return the pages that satisfy all rules
pub fn parse_data(
data: List(String),
) -> #(List(List(Int)), List(#(Int, Int)))
Given the initial data (already as lines) parse the data to a tuple of a list of pages and a list of rules.
pub fn parse_pages(page: String) -> List(Int)
Given a page, parse the page to a list of integers.
pub fn parse_rule(rule: String) -> #(Int, Int)
Given a rule line, parse a rule in format of “a|b” to a tuple of #(b, a). This is because we use the rule to check for validations, meaning we need to reverse the order.
pub fn rest_satisfies_all_rules(
a: Int,
rest: List(Int),
rules: List(#(Int, Int)),
) -> Bool
Does the provided list of integers satisfy all rules pairwise?
pub fn sort_until_valid(
page: List(Int),
rules: List(#(Int, Int)),
) -> List(Int)
pub fn sum_middle_values(pages: List(List(Int))) -> Int