155 lines
5.1 KiB
OCaml
155 lines
5.1 KiB
OCaml
open Printf;;
|
|
open Buffer;;
|
|
open Str;;
|
|
|
|
let read_whole_file f =
|
|
let s = really_input_string f (in_channel_length f) in
|
|
close_in f;
|
|
s
|
|
|
|
let rec find_all_numbers pos str =
|
|
try
|
|
let re = Str.regexp "-?[0-9]+" in
|
|
let new_pos = Str.search_forward re str pos in
|
|
let n_str = Str.matched_group 0 str in
|
|
let n = int_of_string n_str in
|
|
n :: find_all_numbers (new_pos + String.length n_str) str
|
|
with Not_found -> []
|
|
|
|
let take_3_values lst =
|
|
match lst with
|
|
| n1 :: n2 :: n3 :: tail ->
|
|
((n1, n2, n3), tail)
|
|
| _ -> raise (Invalid_argument "Not enough elements")
|
|
|
|
let get_combo_operand (a, b, c) operand =
|
|
match operand with
|
|
| 4 -> a
|
|
| 5 -> b
|
|
| 6 -> c
|
|
| n ->
|
|
if n >= 0 && n < 4 then
|
|
n
|
|
else
|
|
raise (Invalid_argument "Invalid combo operand")
|
|
|
|
let read_instruction program program_counter =
|
|
try
|
|
let opcode = List.nth program program_counter in
|
|
let operand = List.nth program (program_counter + 1) in
|
|
Some (opcode, operand)
|
|
with Failure _ -> None
|
|
|
|
let rec cpu_tick program (a, b, c, program_counter) =
|
|
match read_instruction program program_counter with
|
|
| Some (opcode, operand) -> begin
|
|
let next_program_counter = program_counter + 2 in
|
|
match opcode with
|
|
| 0 ->
|
|
let result = Int.shift_right a (get_combo_operand (a, b, c) operand) in
|
|
((result, b, c, next_program_counter), None, false)
|
|
| 1 ->
|
|
let result = Int.logxor b operand in
|
|
((a, result, c, next_program_counter), None, false)
|
|
| 2 ->
|
|
let result = (get_combo_operand (a, b, c) operand) mod 8 in
|
|
((a, result, c, next_program_counter), None, false)
|
|
| 3 ->
|
|
if a = 0 then
|
|
((a, b, c, next_program_counter), None, false)
|
|
else
|
|
((a, b, c, operand), None, false)
|
|
| 4 ->
|
|
let result = Int.logxor b c in
|
|
((a, result, c, next_program_counter), None, false)
|
|
| 5 ->
|
|
let out = (get_combo_operand (a, b, c) operand) mod 8 in
|
|
((a, b, c, next_program_counter), Some out, false)
|
|
| 6 ->
|
|
let result = Int.shift_right a (get_combo_operand (a, b, c) operand) in
|
|
((a, result, c, next_program_counter), None, false)
|
|
| 7 ->
|
|
let result = Int.shift_right a (get_combo_operand (a, b, c) operand) in
|
|
((a, b, result, next_program_counter), None, false)
|
|
| _ -> raise (Invalid_argument "Invalid opcode")
|
|
end
|
|
| None -> ((a, b, c, program_counter), None, true)
|
|
|
|
let rec run_program_inner program state =
|
|
match cpu_tick program state with
|
|
| (_, _, true) -> []
|
|
| (new_state, out, false) ->
|
|
match out with
|
|
| Some n ->
|
|
n :: run_program_inner program new_state
|
|
| None ->
|
|
run_program_inner program new_state
|
|
|
|
let run_program (init_a, init_b, init_c) program =
|
|
let result = run_program_inner program (init_a, init_b, init_c, 0) in
|
|
result
|
|
|
|
let rec join_output lst =
|
|
lst
|
|
|> List.map string_of_int
|
|
|> String.concat ","
|
|
|
|
let rec construct_a_consider_all_options reverse_mapping program possibilities prev_mask =
|
|
match possibilities with
|
|
| next :: possibilities_tail -> begin
|
|
match construct_a_try_option reverse_mapping program (Some (Int.shift_right next 3)) prev_mask with
|
|
| Some result -> begin
|
|
Some (Int.logor (Int.logand next 7) (Int.shift_left result 3))
|
|
end
|
|
| None -> construct_a_consider_all_options reverse_mapping program possibilities_tail prev_mask
|
|
end
|
|
| _ -> None
|
|
|
|
and construct_a_try_option reverse_mapping program prev_opt prev_mask =
|
|
match program with
|
|
| n :: tail -> begin
|
|
let this_possibilities = reverse_mapping.(n) in
|
|
let restrict_possibilities_with_prev this_possibilities =
|
|
match prev_opt with
|
|
| Some prev ->
|
|
List.filter (fun x -> (Int.logand x prev_mask) = prev) this_possibilities
|
|
| None -> this_possibilities in
|
|
let this_filtered = restrict_possibilities_with_prev this_possibilities in
|
|
construct_a_consider_all_options reverse_mapping tail this_filtered prev_mask
|
|
end
|
|
| _ ->
|
|
match prev_opt with
|
|
| Some prev -> if prev = 0 then Some 0 else None
|
|
| None -> Some 0
|
|
|
|
let get_initial_possibilities lookahead_bits =
|
|
Int.shift_left 1 lookahead_bits
|
|
|
|
let rec get_prev_mask lookahead_bits =
|
|
if lookahead_bits > 3 then
|
|
Int.logor 1 (Int.shift_left (get_prev_mask (lookahead_bits - 1)) 1 )
|
|
else
|
|
0
|
|
|
|
(* The amount of lookahead bits needs to be at least equal to amount of bits of a that influences a single output digit *)
|
|
let construct_a_based_on_program program lookahead_bits =
|
|
let initial_possibilities = get_initial_possibilities lookahead_bits in
|
|
let prev_mask = get_prev_mask lookahead_bits in
|
|
let reverse_mapping = Array.make 8 [] in
|
|
Seq.ints 0
|
|
|> Seq.take initial_possibilities
|
|
|> Seq.map (fun a -> run_program (a, 0, 0) program |> List.hd)
|
|
|> Seq.iteri (fun i a -> reverse_mapping.(a) <- List.append reverse_mapping.(a) [i]);
|
|
construct_a_try_option reverse_mapping program None prev_mask
|
|
|
|
let () =
|
|
let f = open_in_bin "input.txt" in
|
|
let (registers, program) = f
|
|
|> read_whole_file
|
|
|> find_all_numbers 0
|
|
|> take_3_values in
|
|
let result = join_output @@ run_program registers program in
|
|
printf "%s\n" result;
|
|
let result2 = construct_a_based_on_program program 10 |> Option.get in
|
|
printf "%d\n" result2;
|