open Core
let read_lines path =
In_channel.read_lines path
let opening = function
| '(' | '[' | '<' | '{' -> true
| _ -> false
let matching = function
| '(' -> ')'
| '[' -> ']'
| '{' -> '}'
| '<' -> '>'
| _ -> assert false
let illegal_character_score = function
| ')' -> 3
| ']' -> 57
| '}' -> 1197
| '>' -> 25137
| _ -> assert false
type line_state =
| Valid
| Incomplete of char list
| Invalid of int
let line_score (line : string) =
String.fold_until ~init:[]
~f: (fun stack character ->
match stack, character with
| x, c when opening c -> Continue (matching c :: x)
| x :: xs, c when Stdlib.(x = c) -> Continue xs
| _, c -> Stop (Invalid (illegal_character_score c))
)
~finish: (fun stack ->
if List.is_empty stack
then Valid
else Incomplete stack
)
line
let part1 lines =
lines
|> List.map ~f:(function Invalid x -> x | _ -> 0)
|> List.fold_left ~f:(+) ~init:0
let part2_character_score = function
| ')' -> 1
| ']' -> 2
| '}' -> 3
| '>' -> 4
| _ -> assert false
let score_incomplete stack =
List.fold_left ~init:0
~f:(fun acc current -> acc * 5 + part2_character_score current)
stack
let part2 lines =
let filtered = List.filter lines ~f:(function Incomplete _ -> true | _ -> false) in
let sorted_scores = filtered
|> List.map ~f:(function Incomplete x -> x | _ -> assert false)
|> List.map ~f:score_incomplete
|> List.sort ~compare:compare
in List.nth_exn sorted_scores ((List.length filtered) / 2)
let input = read_lines (Sys.get_argv ()).(1)
let preprocessed_lines = List.map ~f:line_score input
let () = Printf.printf "Part 1: %d\n" (part1 preprocessed_lines)
let () = Printf.printf "Part 2: %d\n" (part2 preprocessed_lines)