module IntMap = Map.Make (Int) let parse_robot s = let re = Str.regexp {|p=\(-?[0-9]+\),\(-?[0-9]+\) v=\(-?[0-9]+\),\(-?[0-9]+\)|} in let _ = Str.search_forward re s 0 in ( ( int_of_string (Str.matched_group 1 s), int_of_string (Str.matched_group 2 s) ), ( int_of_string (Str.matched_group 3 s), int_of_string (Str.matched_group 4 s) ) ) let parse_robots = List.map parse_robot let robots_of_file fname = Aoc.strings_of_file fname |> parse_robots let width = 101 let height = 103 let secs = 100 let normalize_velocity (p, (dx, dy)) = (p, ((dx + width) mod width, (dy + height) mod height)) let calc_pos_after secs ((x, y), (dx, dy)) = let x' = (x + (secs * dx)) mod width in let y' = (y + (secs * dy)) mod height in (x', y') let in_a_quadrant (x, y) = x <> width / 2 && y <> height / 2 let update_count = function None -> Some 1 | Some x -> Some (x + 1) let get_quadrant (x, y) = if x < width / 2 && y < height / 2 then 1 else if x > width / 2 && y < height / 2 then 2 else if x < width / 2 && y > height / 2 then 4 else if x > width / 2 && y > height / 2 then 3 else failwith "get_quadrant" let loc_counts map p = let idx = get_quadrant p in IntMap.update idx update_count map let part robots = let counts = robots |> List.map normalize_velocity |> List.map (calc_pos_after secs) |> List.filter in_a_quadrant |> List.fold_left loc_counts IntMap.empty in IntMap.fold (fun _ v acc -> acc * v) counts 1 let _ = Aoc.main robots_of_file [ (string_of_int, part) ]