aoc-2022-ocaml/src/day7.ml
2023-10-09 14:14:13 -07:00

113 lines
3.0 KiB
OCaml

open Containers
(* could have simplified this a lot by getting rid of File *)
type node =
| File of
{ name : string
; size : int
}
| Dir of
{ name : string
; parent : node option
; mutable children : node list
}
let get_dir_tree () =
let lines = List.drop 1 @@ Utils.lines_of_input 7 in
let line_num = ref 0 in
let tree = Dir { name = "/"; children = []; parent = None } in
let _ =
lines
|> List.fold_left
(fun node line ->
incr line_num;
match node with
| File _ -> failwith "current directory cannot be a file"
| Dir dir ->
(match String.split ~by:" " line with
| [ "$"; "ls" ] -> node
| [ "dir"; name ] ->
let new_dir = Dir { name; parent = Some (Dir dir); children = [] } in
dir.children <- new_dir :: dir.children;
node
| [ "$"; "cd"; ".." ] ->
(match dir.parent with
| Some parent -> parent
| _ ->
failwith
@@ Printf.sprintf
"ERROR:%d: cant cd .. because node '%s' has no parent"
!line_num
dir.name)
| [ "$"; "cd"; name ] ->
dir.children
|> List.find (fun child ->
match child with
| Dir child_dir -> String.Infix.(name = child_dir.name)
| _ -> false)
| [ size; name ] ->
let new_file = File { name; size = int_of_string size } in
dir.children <- new_file :: dir.children;
node
| _ -> failwith @@ Printf.sprintf "failed to parse line '%s'" line))
tree
in
tree
;;
let rec get_node_size fn node =
let get_node_size' = get_node_size fn in
match node with
| File file -> file.size
| Dir dir ->
let size =
match List.reduce ( + ) @@ List.map get_node_size' dir.children with
| Some size -> size
| None -> 0
in
fn size;
size
;;
let part1 () =
let tree = get_dir_tree () in
let answer = ref 0 in
let build_answer dir_size =
if dir_size <= 100000 then answer := !answer + dir_size;
()
in
let _ = get_node_size build_answer tree in
!answer
;;
let part2 () =
let tree = get_dir_tree () in
let dir_sizes = ref [] in
let collect_dir_sizes dir_size =
dir_sizes := dir_size :: !dir_sizes;
()
in
let used_space = get_node_size collect_dir_sizes tree in
let total_space = 70_000_000 in
let required_space = 30_000_000 in
let free_space = total_space - used_space in
match
!dir_sizes
|> List.sort ( - )
|> List.drop_while (fun size -> size < required_space - free_space)
|> List.head_opt
with
| Some size -> size
| None -> failwith "not possible!!"
;;
let%expect_test "Day 7.1" =
Printf.printf "%d" @@ part1 ();
[%expect {| 1453349 |}]
;;
let%expect_test "Day 7.2" =
Printf.printf "%d" @@ part2 ();
[%expect {| 2948823 |}]
;;