113 lines
3.0 KiB
OCaml
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 |}]
|
|
;;
|