tracks/ocaml/exercises/bowling/test.ml in trackler-2.1.0.11 vs tracks/ocaml/exercises/bowling/test.ml in trackler-2.1.0.12

- old
+ new

@@ -1,36 +1,141 @@ -open Core.Std +open! Core.Std open OUnit2 -module B = Bowling +open Bowling -let (>>) = Fn.flip Fn.compose +type game = Bowling.t -let assert_score e g _test_context = - assert_equal ~printer:Int.to_string e (B.new_game |> g |> B.score) +let to_ok (r: (t, string) Result.t): t = match r with +| Ok g -> g +| Error e -> failwith ("should be OK but got Error " ^ e) -let replicate count n = - let rec go acc count = - if count = 0 then acc - else go (n :: acc) (count - 1) in - go [] count -let roll = B.roll -let roll_many scores game = List.fold ~init:game ~f:(Fn.flip roll) (List.rev scores) -let roll_spare = roll_many [5;5] -let roll_strike = roll 10 -let roll_repeatedly (count: int) (score: int) (game: B.t): B.t = - roll_many (replicate count score) game +let set_previous_frames (frames : int list): game = + List.fold frames ~init:new_game ~f:(fun g f -> roll f g |> to_ok) +let score_printer = function +| Ok n -> Int.to_string n +| Error e -> e +let assert_score exp game = assert_equal ~printer:score_printer exp (score game) + +let roll_printer = function +| Ok _ -> "Ok <some game>" +| Error e -> e +let assert_roll (exp: (t, string) Result.t) (frame: int) (game: game) = + assert_equal ~printer:roll_printer exp (roll frame game) + let tests = [ - "gutter every frame gives a score of 0" >:: - assert_score 0 (roll_repeatedly 20 0); - "1 pin every frame gives a score of 20" >:: - assert_score 20 (roll_repeatedly 20 1); - "frame score for a spare is 10 plus the number of pins knocked down in the next throw" >:: - assert_score 16 (roll_spare >> roll 3 >> roll_repeatedly 17 0); - "frame score for a strike is 10 plus the number of pins knocked down in the next 2 throws" >:: - assert_score 24 (roll_strike >> roll_many [3;4] >> roll_repeatedly 16 0); - "perfect game" >:: - assert_score 300 (roll_repeatedly 12 10); + "should be able to score a game with all zeros" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 0) g + ); + "should be able to score a game with no strikes or spares" >:: (fun _ -> + let g = set_previous_frames [3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6] in + assert_score (Ok 90) g + ); + "a spare followed by zeros is worth ten points" >:: (fun _ -> + let g = set_previous_frames [6; 4; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 10) g + ); + "points scored in the roll after a spare are counted twice" >:: (fun _ -> + let g = set_previous_frames [6; 4; 3; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 16) g + ); + "consecutive spares each get a one roll bonus" >:: (fun _ -> + let g = set_previous_frames [5; 5; 3; 7; 4; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 31) g + ); + "a spare in the last frame gets a one roll bonus that is counted once" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 7; 3; 7] in + assert_score (Ok 17) g + ); + "a strike earns ten points in a frame with a single roll" >:: (fun _ -> + let g = set_previous_frames [10; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 10) g + ); + "points scored in the two rolls after a strike are counted twice as a bonus" >:: (fun _ -> + let g = set_previous_frames [10; 5; 3; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 26) g + ); + "consecutive strikes each get the two roll bonus" >:: (fun _ -> + let g = set_previous_frames [10; 10; 10; 5; 3; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_score (Ok 81) g + ); + "a strike in the last frame gets a two roll bonus that is counted once" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 7; 1] in + assert_score (Ok 18) g + ); + "rolling a spare with the two roll bonus does not get a bonus roll" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 7; 3] in + assert_score (Ok 20) g + ); + "strikes with the two roll bonus do not get bonus rolls" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 10; 10] in + assert_score (Ok 30) g + ); + "a strike with the one roll bonus after a spare in the last frame does not get a bonus" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 7; 3; 10] in + assert_score (Ok 20) g + ); + "all strikes is a perfect game" >:: (fun _ -> + let g = set_previous_frames [10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 10] in + assert_score (Ok 300) g + ); + "rolls can not score negative points" >:: (fun _ -> + let g = set_previous_frames [] in + assert_roll (Error "Negative roll is invalid") (-1) g + ); + "a roll can not score more than 10 points" >:: (fun _ -> + let g = set_previous_frames [] in + assert_roll (Error "Pin count exceeds pins on the lane") 11 g + ); + "two rolls in a frame can not score more than 10 points" >:: (fun _ -> + let g = set_previous_frames [5] in + assert_roll (Error "Pin count exceeds pins on the lane") 6 g + ); + "bonus roll after a strike in the last frame can not score more than 10 points" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10] in + assert_roll (Error "Pin count exceeds pins on the lane") 11 g + ); + "two bonus rolls after a strike in the last frame can not score more than 10 points" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 5] in + assert_roll (Error "Pin count exceeds pins on the lane") 6 g + ); + "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 10; 6] in + assert_score (Ok 26) g + ); + "the second bonus rolls after a strike in the last frame can not be a strike if the first one is not a strike" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 6] in + assert_roll (Error "Pin count exceeds pins on the lane") 10 g + ); + "second bonus roll after a strike in the last frame can not score than 10 points" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 10] in + assert_roll (Error "Pin count exceeds pins on the lane") 11 g + ); + "an unstarted game can not be scored" >:: (fun _ -> + let g = set_previous_frames [] in + assert_score (Error "Score cannot be taken until the end of the game") g + ); + "an incomplete game can not be scored" >:: (fun _ -> + let g = set_previous_frames [0; 0] in + assert_score (Error "Score cannot be taken until the end of the game") g + ); + "cannot roll if game already has ten frames" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0] in + assert_roll (Error "Cannot roll after game is over") 0 g + ); + "bonus rolls for a strike in the last frame must be rolled before score can be calculated" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10] in + assert_score (Error "Score cannot be taken until the end of the game") g + ); + "both bonus rolls for a strike in the last frame must be rolled before score can be calculated" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 10] in + assert_score (Error "Score cannot be taken until the end of the game") g + ); + "bonus roll for a spare in the last frame must be rolled before score can be calculated" >:: (fun _ -> + let g = set_previous_frames [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 7; 3] in + assert_score (Error "Score cannot be taken until the end of the game") g + ); ] let () = - run_test_tt_main ("bowling tests" >::: tests) + run_test_tt_main ("bowling tests" >::: tests) \ No newline at end of file