You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
2.9 KiB
OCaml
107 lines
2.9 KiB
OCaml
(* Common util. Should probably put this in a module or something.. *)
|
|
let get_one_line file =
|
|
try Some (input_line file) with
|
|
End_of_file -> None
|
|
|
|
let get_lines file =
|
|
let rec input lines =
|
|
match get_one_line file with
|
|
Some line -> input (line :: lines)
|
|
| None -> List.rev lines
|
|
in input []
|
|
|
|
let read_file file =
|
|
let channel = open_in(file) in
|
|
get_lines channel
|
|
|
|
let explode s = (* convert a string to a list of chars *)
|
|
let rec exp i l =
|
|
if i < 0 then l else exp (i - 1) (s.[i] :: l) in
|
|
exp (String.length s - 1) []
|
|
|
|
let implode l = String.of_seq (List.to_seq l)
|
|
|
|
let (<<) f g x = f(g(x))
|
|
let (>>) f g x = g(f(x))
|
|
|
|
let rec take n l = (* take the first n elements *)
|
|
match n with
|
|
| 0 -> []
|
|
| n ->
|
|
match l with
|
|
| [] -> []
|
|
| x :: xs -> x :: (take (n-1) xs)
|
|
|
|
let rec drop n l = (* drop the first n elemnts *)
|
|
match n with
|
|
| 0 -> l
|
|
| n -> drop (n-1) @@ List.tl l
|
|
|
|
let c2i = String.make 1 >> int_of_string
|
|
|
|
(* --- *)
|
|
|
|
type exp = Add of exp * exp | Mul of exp * exp | Num of int
|
|
type op = Plus | Mult
|
|
|
|
let rec string_of_exp = function
|
|
| Num x -> string_of_int x
|
|
| Mul (a, b) -> Printf.sprintf "(%s * %s)" (string_of_exp a) (string_of_exp b)
|
|
| Add (a, b) -> Printf.sprintf "(%s + %s)" (string_of_exp a) (string_of_exp b)
|
|
|
|
let print_exp = print_string << string_of_exp
|
|
let print_exps = List.iter print_exp
|
|
|
|
let string_of_op = function | Plus -> "Plus" | Mult -> "Mult"
|
|
let print_op = print_string << string_of_op
|
|
let print_ops = List.iter print_op
|
|
|
|
let rec find_matching_paren count (x::xs) = (* find matching closing paren *)
|
|
match x with
|
|
| ')' -> if count = 0 then 0 else 1 + find_matching_paren (count-1) xs
|
|
| '(' -> 1 + find_matching_paren (count+1) xs
|
|
| n -> 1 + find_matching_paren count xs
|
|
|
|
let rec run_stack exps ops =
|
|
if List.length exps = 1
|
|
then List.hd exps
|
|
else
|
|
let (l :: r :: exps') = exps in
|
|
let (op :: ops') = ops in
|
|
match op with
|
|
| Plus -> run_stack ((Add (l, r)) :: exps') ops'
|
|
| Mult -> run_stack ((Mul (l, r)) :: exps') ops'
|
|
|
|
let read_exp line =
|
|
let line' = line
|
|
|> explode
|
|
|> List.filter (fun x -> x != ' ')
|
|
in
|
|
|
|
let rec go exps ops = function
|
|
| [] -> run_stack exps ops (* end of our string *)
|
|
| '*' :: xs -> Mul (run_stack exps ops, go [] [] xs) (* "split" on Mul *)
|
|
| '+' :: xs -> go exps (Plus :: ops) xs
|
|
| '(' :: xs ->
|
|
let matching = 1 + find_matching_paren 0 xs in
|
|
let sub = take matching xs in
|
|
let rest = drop matching xs in
|
|
go ((go [] [] sub)::exps) ops rest
|
|
| ')' :: xs -> go exps ops xs
|
|
| num :: xs -> go ((Num (c2i num)) :: exps) ops xs
|
|
in
|
|
|
|
go [] [] line'
|
|
|
|
let rec eval = function
|
|
| Num a -> a
|
|
| Add (a, b) -> (eval a) + (eval b)
|
|
| Mul (a, b) -> (eval a) * (eval b)
|
|
|
|
let () =
|
|
let exps = read_file "day18.txt" |> List.map read_exp in
|
|
|
|
let values = List.map eval exps in
|
|
let sum = List.fold_left (+) 0 values in
|
|
print_int sum;
|