add expect tests & refactor

This commit is contained in:
Rhiannon Morris 2020-12-02 15:10:29 +01:00
parent 816b2851c4
commit e6b401d986
6 changed files with 115 additions and 52 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
_build
.merlin
aoc2020.opam

View File

@ -1,3 +1,5 @@
open Aoc2020
let days = [|Day1.main; Day2.main|]
let _ =

67
day1.ml
View File

@ -1,36 +1,53 @@
module IntSet = Set.Make(Int)
module IntSet = struct
include Set.Make(Int)
let exists_opt (type a) (f: int -> a option) (set: t): a option =
let exception Found of a in
let f' x = Option.iter (fun x -> raise (Found x)) (f x) in
match iter f' set with
| exception Found x -> Some x
| _ -> None
end
let read_ints =
Bracket.infile_lines ~line:int_of_string ~of_seq:IntSet.of_seq
let find_with target ints =
let open IntSet in
let p x = mem (target - x) ints in
let res = min_elt (filter p ints) in
res, target - res
let find = find_with 2020
let rec find ?(target=2020) ~n set =
if n < 1 then
raise (Invalid_argument "n must be at least 1")
else if n = 1 then
if IntSet.mem target set then Some [target] else None
else
let joined x =
Option.map (List.cons x) (find ~target:(target - x) ~n:(n - 1) set) in
IntSet.exists_opt joined set
let main_part_1 input =
let ints = read_ints input in
let x, y = find ints in
Printf.printf "%d * %d = %d\n" x y (x * y)
let print_result xs =
let res = List.fold_left ( * ) 1 xs in
let rec print_prod fmt = function
| [] -> Format.pp_print_text fmt "{}"
| [x] -> Format.fprintf fmt "%d" x
| x::xs -> Format.fprintf fmt "%d * %a" x print_prod xs in
Format.printf "%a = %d\n" print_prod xs res
let main_part_2 input =
let ints = read_ints input in
let exception Found of int * int * int in
let go x =
match find_with (2020 - x) ints with
| exception Not_found -> ()
| y, z -> raise (Found (x, y, z))
in
match IntSet.iter go ints with
| exception Found (x, y, z) ->
Printf.printf "%d * %d * %d = %d\n" x y z (x * y * z)
| _ -> raise Not_found
let mains = [|main_part_1; main_part_2|]
let main' n input =
match find ~n (read_ints input) with
| Some xs -> print_result xs
| None -> raise Not_found
let mains = [|main' 2; main' 3|]
let main = function
| [part; input] -> mains.(int_of_string part - 1) input
| _ -> Usage.exit "1 <part> <infile>"
| _ -> Usage.exit "1 <part> <infile>"
let%expect_test "part 1" =
mains.(0) "../../data/day1"; (* is this always the right path??? *)
[%expect{| 534 * 1486 = 793524 |}]
let%expect_test "part 2" =
mains.(1) "../../data/day1";
[%expect{| 71 * 686 * 1263 = 61515678 |}]

81
day2.ml
View File

@ -1,41 +1,70 @@
let parse_line' f str = Scanf.sscanf str "%d - %d %c : %s%!" f
module type Part = sig
type t
val parse_line: string -> t
val ok: t -> bool
end
let parse_line' f str = Scanf.sscanf str "%d - %d %c : %s" f
type part1 = {min: int; max: int; char: char; pass: string}
module Part1: Part = struct
type t = {min: int; max: int; char: char; pass: string}
let parse_line1 = parse_line'
(fun min max char pass -> {min; max; char; pass})
let parse_line = parse_line'
(fun min max char pass -> {min; max; char; pass})
let count c str =
let res = ref 0 in
String.iter (fun d -> if c = d then incr res) str;
!res
let count c str =
let res = ref 0 in
String.iter (fun d -> if c = d then incr res) str;
!res
let ok1 {min; max; char; pass} =
let res = count char pass in
res >= min && res <= max
let ok {min; max; char; pass} =
let res = count char pass in
res >= min && res <= max
end
type part2 = {pos1: int; pos2: int; char: char; pass: string}
module Part2: Part = struct
type t = {pos1: int; pos2: int; char: char; pass: string}
let parse_line2 = parse_line'
(fun pos1 pos2 char pass -> {pos1 = pos1 - 1; pos2 = pos2 - 1; char; pass})
let parse_line = parse_line'
(fun pos1 pos2 char pass -> {pos1 = pos1 - 1; pos2 = pos2 - 1; char; pass})
let ok2 {pos1; pos2; char; pass} =
(pass.[pos1] = char) <> (pass.[pos2] = char)
let ok {pos1; pos2; char; pass} =
(pass.[pos1] = char) <> (pass.[pos2] = char)
end
let main' ~line ~ok input =
let count_all = ref 0 in
let count_ok = ref 0 in
let check l = incr count_all; if ok l then incr count_ok in
Bracket.infile_iter_lines input ~line ~iter:check;
Printf.printf "%d out of %d ok\n" !count_ok !count_all
module type Main = sig val run: string -> unit end
let main1 = main' ~line:parse_line1 ~ok:ok1
let main2 = main' ~line:parse_line2 ~ok:ok2
let mains = [|main1; main2|]
module Main(P: Part)(): Main = struct
open P
let count_all = ref 0
let count_ok = ref 0
let check l =
incr count_all;
if ok l then incr count_ok
let run input =
Bracket.infile_iter_lines input ~line:parse_line ~iter:check;
Printf.printf "%d out of %d ok\n" !count_ok !count_all
end
let main' (module P: Part) =
let module M = Main(P)() in M.run
let mains = [|main' (module Part1); main' (module Part2)|]
let main = function
| [part; input] -> mains.(int_of_string part - 1) input
| _ -> Usage.exit "2 <part> <infile>"
| _ -> Usage.exit "2 <part> <infile>"
let%expect_test "part 1" =
mains.(0) "../../data/day2";
[%expect{| 607 out of 1000 ok |}]
let%expect_test "part 2" =
mains.(1) "../../data/day2";
[%expect{| 321 out of 1000 ok |}]

10
dune
View File

@ -1,2 +1,10 @@
(executable
(name aoc2020))
(public_name aoc2020)
(libraries aoc2020)
(modules aoc2020))
(library
(public_name aoc2020)
(inline_tests)
(modules (:standard \ aoc2020))
(preprocess (pps ppx_expect)))

View File

@ -1 +1,7 @@
(lang dune 2.7)
(generate_opam_files true)
(allow_approximate_merlin)
(package
(name aoc2020)
(depends ppx_expect))