Skip to content

Instantly share code, notes, and snippets.

@zindel
Created December 5, 2019 19:19
Show Gist options
  • Save zindel/217ee026044bd7d7fc0089773234a99d to your computer and use it in GitHub Desktop.
Save zindel/217ee026044bd7d7fc0089773234a99d to your computer and use it in GitHub Desktop.
let solve lines =
let program =
lines
|> List.hd
|> CCString.split_on_char ','
|> List.map int_of_string
in
let access () =
let program = Array.of_list program in
let get = Array.get program in
let set ~pos = Array.set program pos in
(get, set)
in
let exec inputs =
let inputs = ref inputs in
let read () =
match !inputs with
| hd :: tl ->
inputs := tl;
hd
| _ -> failwith "no input :("
in
let outputs = ref [] in
let write output =
outputs := output :: !outputs
in
let (get, set) = access () in
let rec exec pos =
let op = get pos |> string_of_int |> CCString.to_list |> List.rev in
let with_modes modes =
let modes =
modes
|> CCString.of_list
|> CCString.pad ~side:`Right ~c:'0' 4
|> CCString.to_list
in
let mode param =
match CCList.drop param modes with
| [] | '0' :: _ -> `Pos
| '1' :: _ -> `Value
| _ -> assert false
in
fun param ->
let value = get (pos + param) in
match mode param with
| `Value -> value
| `Pos -> get value
in
match op with
(* halt *)
| ['9'; '9'] -> ()
(* read *)
| ['3'] ->
let input_pos = get (pos + 1) in
let value = read () in
set ~pos:input_pos value;
exec (pos + 2)
(* write *)
| '4' :: modes ->
let get_param = with_modes modes in
write (get_param 1);
exec (pos + 2)
(* jump-if-true / jump-if-false *)
| op :: modes when op = '5' || op = '6' ->
let get_param = with_modes modes in
let f = if op = '5' then ( <> ) 0 else ( = ) 0 in
let next = match get_param 1 |> f with
| true -> get_param 2
| false -> pos + 3
in
exec next
(* less-than / equals *)
| op :: modes when op = '7' || op = '8' ->
let get_param = with_modes modes in
let f = if op = '7' then ( < ) else ( = ) in
let value = match f (get_param 1) (get_param 2) with
| true -> 1
| false -> 0
in
set ~pos:(get (pos + 3)) value;
exec (pos + 4)
(* add / mul *)
| op :: modes when op = '1' || op = '2' ->
let get_param = with_modes modes in
let f = if op = '1' then ( + ) else ( * ) in
let res = f (get_param 1) (get_param 2) in
let res_pos = get (pos + 3) in
set ~pos:res_pos res;
exec (pos + 4)
| op ->
CCString.of_list op |> print_endline;
assert false
in
exec 0;
!outputs
in
Printf.printf "Part1 = %d\n" (exec [1] |> List.hd);
Printf.printf "Part2 = %d\n" (exec [5] |> List.hd);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment