d24
This commit is contained in:
222
24/main.ml
Normal file
222
24/main.ml
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
open Printf;;
|
||||||
|
|
||||||
|
let rec list_of_lines in_file =
|
||||||
|
try
|
||||||
|
let line = input_line in_file in
|
||||||
|
line :: list_of_lines(in_file)
|
||||||
|
with End_of_file ->
|
||||||
|
close_in in_file;
|
||||||
|
[]
|
||||||
|
|
||||||
|
let rec split_on_empty_line lines =
|
||||||
|
match lines with
|
||||||
|
| head :: tail ->
|
||||||
|
if String.length head = 0 then
|
||||||
|
([], tail)
|
||||||
|
else
|
||||||
|
let rest_of_list, remaining_lines = split_on_empty_line tail in
|
||||||
|
(head :: rest_of_list, remaining_lines)
|
||||||
|
| _ -> ([], lines)
|
||||||
|
|
||||||
|
let encode_wire_id str =
|
||||||
|
(Char.code str.[0])
|
||||||
|
|> Int.logor (Int.shift_left (Char.code str.[1]) 8)
|
||||||
|
|> Int.logor (Int.shift_left (Char.code str.[2]) 16)
|
||||||
|
|
||||||
|
let decode_wire_id id =
|
||||||
|
[Char.chr (Int.logand id 0xff);
|
||||||
|
Char.chr (Int.logand (Int.shift_right id 8) 0xff);
|
||||||
|
Char.chr (Int.shift_right id 16)]
|
||||||
|
|> List.to_seq |> String.of_seq
|
||||||
|
|
||||||
|
let parse_init str =
|
||||||
|
(encode_wire_id (String.sub str 0 3), (String.sub str 5 1) |> int_of_string |> (fun x -> x = 1))
|
||||||
|
|
||||||
|
type gate_type =
|
||||||
|
GATE_OR
|
||||||
|
| GATE_AND
|
||||||
|
| GATE_XOR
|
||||||
|
|
||||||
|
let gate_str gate =
|
||||||
|
match gate with
|
||||||
|
| GATE_OR -> "or"
|
||||||
|
| GATE_AND -> "and"
|
||||||
|
| GATE_XOR -> "xor"
|
||||||
|
|
||||||
|
let parse_gate str =
|
||||||
|
let in1 = encode_wire_id (String.sub str 0 3) in
|
||||||
|
let gate_str = (String.sub str 4 3) in
|
||||||
|
let gate = if gate_str = "XOR" then GATE_XOR else if gate_str = "AND" then GATE_AND else GATE_OR in
|
||||||
|
let in2_pos = if gate = GATE_OR then 7 else 8 in
|
||||||
|
let in2 = encode_wire_id (String.sub str in2_pos 3) in
|
||||||
|
let out_pos = in2_pos + 7 in
|
||||||
|
let out = encode_wire_id (String.sub str out_pos 3) in
|
||||||
|
(gate, in1, in2, out)
|
||||||
|
|
||||||
|
module IntPair =
|
||||||
|
struct
|
||||||
|
type t = int * int
|
||||||
|
let compare x0 x1 =
|
||||||
|
Stdlib.compare x0 x1
|
||||||
|
end
|
||||||
|
module IntPairMap = Map.Make(IntPair)
|
||||||
|
module IntMap = Map.Make(Int)
|
||||||
|
|
||||||
|
let make_wire_state_map init_list =
|
||||||
|
List.fold_left (fun acc (id, state) -> IntMap.add id state acc) IntMap.empty init_list
|
||||||
|
|
||||||
|
let make_wire_gate_mapping_fold acc_map gate_data =
|
||||||
|
let gate, in1, in2, out = gate_data in
|
||||||
|
let update_fun = (fun other_in prev_opt ->
|
||||||
|
let new_entry = (gate, other_in, out) in
|
||||||
|
match prev_opt with
|
||||||
|
| Some prev -> Some (new_entry :: prev)
|
||||||
|
| None -> Some [new_entry]
|
||||||
|
) in
|
||||||
|
acc_map
|
||||||
|
|> IntMap.update in1 (update_fun in2)
|
||||||
|
|> IntMap.update in2 (update_fun in1)
|
||||||
|
|
||||||
|
let make_wire_gate_mapping gate_data_list =
|
||||||
|
List.fold_left make_wire_gate_mapping_fold IntMap.empty gate_data_list
|
||||||
|
|
||||||
|
let rec simulate_circuit_inner wire_state_mapping wire_states wire_state_queue =
|
||||||
|
match wire_state_queue with
|
||||||
|
| (id, state) :: queue_tail -> begin
|
||||||
|
let new_states = IntMap.add id state wire_states in
|
||||||
|
match IntMap.find_opt id wire_state_mapping with
|
||||||
|
| Some connection_list ->
|
||||||
|
let new_queue = connection_list
|
||||||
|
|> List.fold_left (fun queue (gate, other_in, out) ->
|
||||||
|
match IntMap.find_opt other_in new_states with
|
||||||
|
| Some other_state -> begin
|
||||||
|
match gate with
|
||||||
|
| GATE_OR -> (out, state || other_state) :: queue
|
||||||
|
| GATE_AND -> (out, state && other_state) :: queue
|
||||||
|
| GATE_XOR -> (out, state <> other_state) :: queue
|
||||||
|
end
|
||||||
|
| None -> queue
|
||||||
|
) queue_tail in
|
||||||
|
simulate_circuit_inner wire_state_mapping new_states new_queue
|
||||||
|
| None -> simulate_circuit_inner wire_state_mapping new_states queue_tail
|
||||||
|
end
|
||||||
|
| _ -> wire_states
|
||||||
|
|
||||||
|
let simulate_circuit wire_gate_mapping wire_states =
|
||||||
|
simulate_circuit_inner wire_gate_mapping IntMap.empty (wire_states |> IntMap.to_seq |> List.of_seq)
|
||||||
|
|
||||||
|
let generate_prefixed_id prefix_c n =
|
||||||
|
let n_str = string_of_int n in
|
||||||
|
let prefix_str = String.make 1 prefix_c in
|
||||||
|
if String.length n_str = 1 then
|
||||||
|
String.cat (String.cat prefix_str "0") n_str
|
||||||
|
else
|
||||||
|
String.cat prefix_str n_str
|
||||||
|
|
||||||
|
let rec combine_z_output_inner output_states n out_num =
|
||||||
|
match IntMap.find_opt (encode_wire_id @@ generate_prefixed_id 'z' n) output_states with
|
||||||
|
| Some bit_bool ->
|
||||||
|
let bit_num = if bit_bool then 1 else 0 in
|
||||||
|
combine_z_output_inner output_states (n + 1) (Int.logor out_num (Int.shift_left bit_num n))
|
||||||
|
| None -> out_num
|
||||||
|
|
||||||
|
let combine_z_output output_states =
|
||||||
|
combine_z_output_inner output_states 0 0
|
||||||
|
|
||||||
|
let find_gate_out wire_gate_mapping in1 in2 gate_type =
|
||||||
|
wire_gate_mapping
|
||||||
|
|> IntMap.find in1
|
||||||
|
|> List.find_opt (fun (other_gate, other_in, _) -> other_gate = gate_type && other_in = in2)
|
||||||
|
|> Option.map (fun (_, _, out) -> out)
|
||||||
|
|
||||||
|
let search_gate_out_incomplete_in wire_gate_mapping in0 gate_type =
|
||||||
|
wire_gate_mapping
|
||||||
|
|> IntMap.find in0
|
||||||
|
|> List.find_opt (fun (other_gate, _, _) -> other_gate = gate_type)
|
||||||
|
|> Option.map (fun (_, other_in, out) -> printf "Found missing second input %s for input %s\n" (decode_wire_id other_in) (decode_wire_id in0); out)
|
||||||
|
|
||||||
|
let find_gate_out_opt wire_gate_mapping in1_opt in2_opt gate_type =
|
||||||
|
match (in1_opt, in2_opt) with
|
||||||
|
| Some in1, Some in2 ->
|
||||||
|
let result1 = find_gate_out wire_gate_mapping in1 in2 gate_type in
|
||||||
|
if Option.is_none result1 then begin
|
||||||
|
printf "Did not find expected gate with inputs %s and %s\n" (decode_wire_id in1) (decode_wire_id in2);
|
||||||
|
let new_result1 = search_gate_out_incomplete_in wire_gate_mapping in1 gate_type in
|
||||||
|
let new_result2 = search_gate_out_incomplete_in wire_gate_mapping in2 gate_type in
|
||||||
|
if Option.is_some new_result1 then
|
||||||
|
new_result1
|
||||||
|
else
|
||||||
|
new_result2
|
||||||
|
end else
|
||||||
|
result1
|
||||||
|
| Some in1, None -> search_gate_out_incomplete_in wire_gate_mapping in1 gate_type
|
||||||
|
| None, Some in2 -> search_gate_out_incomplete_in wire_gate_mapping in2 gate_type
|
||||||
|
| None, None -> printf "Trying to find a gate with no given inputs\n"; None
|
||||||
|
|
||||||
|
let expect_wires_equal level expected_opt actual_opt =
|
||||||
|
match expected_opt with
|
||||||
|
| None -> begin
|
||||||
|
match actual_opt with
|
||||||
|
| None -> printf "No idea what was expected and got uncertain at level %d\n" level
|
||||||
|
| Some actual -> printf "No idea what was expected but got %s at level %d\n" (decode_wire_id actual) level
|
||||||
|
end
|
||||||
|
| Some expected ->
|
||||||
|
match actual_opt with
|
||||||
|
| None -> printf "Expected wire %s, got uncertain at level %d\n" (decode_wire_id expected) level
|
||||||
|
| Some actual ->
|
||||||
|
if expected <> actual then
|
||||||
|
printf "Expected wire %s, got %s at level %d\n" (decode_wire_id expected) (decode_wire_id actual) level
|
||||||
|
|
||||||
|
let is_z_wire id =
|
||||||
|
let str = decode_wire_id id in
|
||||||
|
str.[0] = 'z' && int_of_string_opt (String.sub str 1 2) |> Option.is_some
|
||||||
|
let expect_wire_not_z id_opt =
|
||||||
|
match id_opt with
|
||||||
|
| Some id ->
|
||||||
|
if is_z_wire id then begin
|
||||||
|
printf "Expected non-z wire, got %s\n" (decode_wire_id id); None end
|
||||||
|
else Some id
|
||||||
|
| None -> None
|
||||||
|
|
||||||
|
let rec fix_adder_structure_inner wire_gate_mapping prev_carry_id level =
|
||||||
|
let x_id = encode_wire_id @@ generate_prefixed_id 'x' level in
|
||||||
|
let y_id = encode_wire_id @@ generate_prefixed_id 'y' level in
|
||||||
|
let z_id = encode_wire_id @@ generate_prefixed_id 'z' level in
|
||||||
|
if IntMap.find_opt x_id wire_gate_mapping |> Option.is_none then begin
|
||||||
|
expect_wires_equal level (Some z_id) prev_carry_id;
|
||||||
|
end else begin
|
||||||
|
let partial_sum_id = find_gate_out wire_gate_mapping x_id y_id GATE_XOR |> expect_wire_not_z in
|
||||||
|
let partial_carry_id = find_gate_out wire_gate_mapping x_id y_id GATE_AND |> expect_wire_not_z in
|
||||||
|
let sum_id = find_gate_out_opt wire_gate_mapping partial_sum_id prev_carry_id GATE_XOR in
|
||||||
|
let second_partial_carry_id = find_gate_out_opt wire_gate_mapping partial_sum_id prev_carry_id GATE_AND |> expect_wire_not_z in
|
||||||
|
let is_last_level = IntMap.find_opt (encode_wire_id @@ generate_prefixed_id 'x' (level + 1)) wire_gate_mapping |> Option.is_none in
|
||||||
|
let carry_id = find_gate_out_opt wire_gate_mapping partial_carry_id second_partial_carry_id GATE_OR in
|
||||||
|
if is_last_level then
|
||||||
|
fix_adder_structure_inner wire_gate_mapping carry_id (level + 1)
|
||||||
|
else
|
||||||
|
let checked_carry_id = expect_wire_not_z carry_id in
|
||||||
|
expect_wires_equal level (Some z_id) sum_id;
|
||||||
|
fix_adder_structure_inner wire_gate_mapping checked_carry_id (level + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
let fix_adder_structure wire_gate_mapping =
|
||||||
|
let x0_id = encode_wire_id @@ generate_prefixed_id 'x' 0 in
|
||||||
|
let y0_id = encode_wire_id @@ generate_prefixed_id 'y' 0 in
|
||||||
|
let z0_id = encode_wire_id @@ generate_prefixed_id 'z' 0 in
|
||||||
|
let sum_id = find_gate_out wire_gate_mapping x0_id y0_id GATE_XOR in
|
||||||
|
let carry_id = find_gate_out wire_gate_mapping x0_id y0_id GATE_AND in
|
||||||
|
expect_wires_equal 0 (Some z0_id) sum_id;
|
||||||
|
fix_adder_structure_inner wire_gate_mapping carry_id 1
|
||||||
|
|
||||||
|
let () =
|
||||||
|
Printexc.record_backtrace true;
|
||||||
|
let f = open_in "input.txt" in
|
||||||
|
let init_strs, connection_strs = list_of_lines f |> split_on_empty_line in
|
||||||
|
let init_list = List.map parse_init init_strs in
|
||||||
|
let connection_list = List.map parse_gate connection_strs in
|
||||||
|
let wire_states_init = make_wire_state_map init_list in
|
||||||
|
let wire_gate_mapping = make_wire_gate_mapping connection_list in
|
||||||
|
let end_states = simulate_circuit wire_gate_mapping wire_states_init in
|
||||||
|
let result = combine_z_output end_states in
|
||||||
|
fix_adder_structure wire_gate_mapping;
|
||||||
|
printf "Part 1: %d\n" result
|
Reference in New Issue
Block a user