trackler 2.0.0.1 → 2.0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (226) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bump-content +3 -1
  3. data/bin/verify-metadata +2 -2
  4. data/common/exercises/alphametics/canonical-data.json +19 -9
  5. data/common/exercises/food-chain/canonical-data.json +7 -1
  6. data/common/exercises/grains/canonical-data.json +66 -0
  7. data/common/exercises/raindrops/description.md +4 -5
  8. data/common/exercises/triangle/canonical-data.json +81 -54
  9. data/lib/trackler/version.rb +1 -1
  10. data/tracks/c/config.json +26 -19
  11. data/tracks/c/exercises/allergies/makefile +15 -0
  12. data/tracks/c/exercises/allergies/src/allergies.h +21 -0
  13. data/tracks/c/exercises/allergies/src/example.c +32 -0
  14. data/tracks/c/exercises/allergies/src/example.h +26 -0
  15. data/tracks/c/exercises/allergies/test/test_allergies.c +203 -0
  16. data/tracks/c/exercises/allergies/test/vendor/unity.c +1300 -0
  17. data/tracks/c/exercises/allergies/test/vendor/unity.h +274 -0
  18. data/tracks/c/exercises/allergies/test/vendor/unity_internals.h +701 -0
  19. data/tracks/c/exercises/atbash-cipher/makefile +16 -0
  20. data/tracks/c/exercises/atbash-cipher/src/example.c +71 -0
  21. data/tracks/c/exercises/atbash-cipher/src/example.h +7 -0
  22. data/tracks/c/exercises/atbash-cipher/test/test_atbash_cipher.c +113 -0
  23. data/tracks/c/exercises/atbash-cipher/test/vendor/unity.c +1300 -0
  24. data/tracks/c/exercises/atbash-cipher/test/vendor/unity.h +274 -0
  25. data/tracks/c/exercises/atbash-cipher/test/vendor/unity_internals.h +701 -0
  26. data/tracks/c/exercises/phone-number/makefile +16 -0
  27. data/tracks/c/exercises/phone-number/src/example.c +66 -0
  28. data/tracks/c/exercises/phone-number/src/example.h +8 -0
  29. data/tracks/c/exercises/phone-number/test/test_phone_number.c +108 -0
  30. data/tracks/c/exercises/phone-number/test/vendor/unity.c +1300 -0
  31. data/tracks/c/exercises/phone-number/test/vendor/unity.h +274 -0
  32. data/tracks/c/exercises/phone-number/test/vendor/unity_internals.h +701 -0
  33. data/tracks/csharp/config.json +8 -0
  34. data/tracks/csharp/exercises/bowling/BowlingTest.cs +188 -33
  35. data/tracks/csharp/exercises/bowling/Example.cs +38 -9
  36. data/tracks/csharp/exercises/rectangles/Example.cs +91 -0
  37. data/tracks/csharp/exercises/rectangles/RectanglesTest.cs +133 -0
  38. data/tracks/elisp/docs/INSTALLATION.org +1 -1
  39. data/tracks/elixir/config.json +8 -66
  40. data/tracks/elixir/docs/LEARNING.md +1 -1
  41. data/tracks/elixir/exercises/diamond/diamond_test.exs +12 -12
  42. data/tracks/elixir/exercises/grep/example.exs +92 -0
  43. data/tracks/elixir/exercises/grep/grep.exs +6 -0
  44. data/tracks/elixir/exercises/grep/grep_test.exs +259 -0
  45. data/tracks/elixir/exercises/markdown/markdown.exs +59 -2
  46. data/tracks/elixir/exercises/nucleotide-count/nucleotide_count_test.exs +4 -4
  47. data/tracks/elixir/exercises/phone-number/phone_number_test.exs +5 -0
  48. data/tracks/fsharp/exercises/bowling/BowlingTest.fs +158 -43
  49. data/tracks/fsharp/exercises/bowling/Example.fs +53 -24
  50. data/tracks/go/config.json +5 -0
  51. data/tracks/go/exercises/diamond/diamond_test.go +227 -0
  52. data/tracks/go/exercises/diamond/example.go +47 -0
  53. data/tracks/go/exercises/food-chain/example.go +1 -1
  54. data/tracks/go/exercises/food-chain/food_chain_test.go +30 -6
  55. data/tracks/go/exercises/nucleotide-count/example.go +21 -8
  56. data/tracks/go/exercises/nucleotide-count/nucleotide_count_test.go +45 -37
  57. data/tracks/haskell/exercises/anagram/test/Tests.hs +1 -11
  58. data/tracks/java/bin/journey-test.sh +165 -128
  59. data/tracks/java/docs/ABOUT.md +5 -8
  60. data/tracks/java/exercises/hello-world/build.gradle +0 -6
  61. data/tracks/java/exercises/hello-world/src/test/java/HelloWorldTest.java +0 -1
  62. data/tracks/java/exercises/meetup/build.gradle +0 -1
  63. data/tracks/java/exercises/meetup/src/example/java/Meetup.java +9 -7
  64. data/tracks/java/exercises/meetup/src/test/java/MeetupTest.java +185 -188
  65. data/tracks/lua/config.json +0 -73
  66. data/tracks/lua/exercises/bowling/bowling_spec.lua +92 -48
  67. data/tracks/lua/exercises/bowling/example.lua +4 -1
  68. data/tracks/objective-c/config.json +9 -30
  69. data/tracks/objective-c/exercises/pangram/PangramExample.h +7 -0
  70. data/tracks/objective-c/exercises/pangram/PangramExample.m +21 -0
  71. data/tracks/objective-c/exercises/pangram/PangramTest.m +51 -0
  72. data/tracks/objective-c/xcodeProject/ObjectiveC.xcodeproj/project.pbxproj +18 -0
  73. data/tracks/ocaml/README.md +46 -1
  74. data/tracks/ocaml/SETUP.md +21 -2
  75. data/tracks/ocaml/config.json +48 -78
  76. data/tracks/perl5/README.md +15 -8
  77. data/tracks/perl5/testall.pl +5 -5
  78. data/tracks/php/config.json +26 -34
  79. data/tracks/php/docs/ABOUT.md +15 -0
  80. data/tracks/php/exercises/allergies/allergies_test.php +121 -0
  81. data/tracks/php/exercises/allergies/example.php +64 -0
  82. data/tracks/php/exercises/etl/etl_test.php +52 -0
  83. data/tracks/php/exercises/etl/example.php +12 -0
  84. data/tracks/php/exercises/nucleotide-count/example.php +25 -0
  85. data/tracks/php/exercises/nucleotide-count/nucleotide-count_test.php +36 -0
  86. data/tracks/php/exercises/pig-latin/example.php +25 -29
  87. data/tracks/php/exercises/pig-latin/pig-latin_test.php +26 -19
  88. data/tracks/php/exercises/space-age/example.php +65 -0
  89. data/tracks/php/exercises/space-age/space-age_test.php +70 -0
  90. data/tracks/php/exercises/triangle/example.php +43 -0
  91. data/tracks/php/exercises/triangle/triangle_test.php +140 -0
  92. data/tracks/pony/exercises/anagram/example.pony +6 -6
  93. data/tracks/pony/exercises/anagram/test.pony +3 -4
  94. data/tracks/pony/exercises/bob/test.pony +23 -23
  95. data/tracks/pony/exercises/difference-of-squares/test.pony +11 -11
  96. data/tracks/pony/exercises/hamming/example.pony +3 -3
  97. data/tracks/pony/exercises/hamming/test.pony +16 -16
  98. data/tracks/pony/exercises/hello-world/test.pony +5 -4
  99. data/tracks/pony/exercises/leap/test.pony +8 -8
  100. data/tracks/python/config.json +18 -0
  101. data/tracks/python/exercises/diamond/diamond_test.py +33 -0
  102. data/tracks/python/exercises/diamond/example.py +15 -0
  103. data/tracks/python/exercises/linked-list/example.py +47 -0
  104. data/tracks/python/exercises/linked-list/linked_list.py +13 -0
  105. data/tracks/python/exercises/linked-list/linked_list_test.py +49 -0
  106. data/tracks/python/exercises/list-ops/example.py +55 -0
  107. data/tracks/python/exercises/list-ops/list_ops.py +38 -0
  108. data/tracks/python/exercises/list-ops/list_ops_test.py +136 -0
  109. data/tracks/ruby/bin/generate +22 -2
  110. data/tracks/ruby/config.json +12 -83
  111. data/tracks/ruby/exercises/allergies/allergies_test.rb +0 -1
  112. data/tracks/ruby/exercises/atbash-cipher/atbash_cipher_test.rb +0 -1
  113. data/tracks/ruby/exercises/beer-song/beer_song_test.rb +1 -2
  114. data/tracks/ruby/exercises/binary/example.rb +0 -1
  115. data/tracks/ruby/exercises/binary-search-tree/binary_search_tree_test.rb +1 -1
  116. data/tracks/ruby/exercises/bowling/.version +1 -0
  117. data/tracks/ruby/exercises/bowling/bowling_test.rb +109 -133
  118. data/tracks/ruby/exercises/bowling/example.rb +7 -7
  119. data/tracks/ruby/exercises/bowling/example.tt +27 -0
  120. data/tracks/ruby/exercises/circular-buffer/circular_buffer_test.rb +0 -2
  121. data/tracks/ruby/exercises/clock/example.rb +0 -2
  122. data/tracks/ruby/exercises/connect/connect_test.rb +0 -1
  123. data/tracks/ruby/exercises/custom-set/custom_set_test.rb +0 -1
  124. data/tracks/ruby/exercises/diamond/diamond_test.rb +0 -1
  125. data/tracks/ruby/exercises/etl/etl_test.rb +1 -1
  126. data/tracks/ruby/exercises/house/house_test.rb +0 -1
  127. data/tracks/ruby/exercises/isogram/isogram_test.rb +2 -2
  128. data/tracks/ruby/exercises/largest-series-product/example.tt +0 -3
  129. data/tracks/ruby/exercises/largest-series-product/largest_series_product_test.rb +0 -4
  130. data/tracks/ruby/exercises/linked-list/linked_list_test.rb +1 -1
  131. data/tracks/ruby/exercises/meetup/meetup_test.rb +0 -1
  132. data/tracks/ruby/exercises/nth-prime/example.tt +0 -2
  133. data/tracks/ruby/exercises/nth-prime/nth_prime_test.rb +0 -2
  134. data/tracks/ruby/exercises/ocr-numbers/ocr_numbers_test.rb +1 -2
  135. data/tracks/ruby/exercises/protein-translation/protein_translation_test.rb +0 -1
  136. data/tracks/ruby/exercises/proverb/proverb_test.rb +1 -3
  137. data/tracks/ruby/exercises/queen-attack/example.rb +3 -1
  138. data/tracks/ruby/exercises/queen-attack/queen_attack_test.rb +34 -8
  139. data/tracks/ruby/exercises/robot-simulator/robot_simulator_test.rb +1 -1
  140. data/tracks/ruby/exercises/strain/strain_test.rb +2 -2
  141. data/tracks/ruby/exercises/tournament/.version +1 -0
  142. data/tracks/ruby/exercises/tournament/example.rb +54 -0
  143. data/tracks/ruby/exercises/tournament/example.tt +23 -0
  144. data/tracks/ruby/exercises/tournament/tournament_test.rb +92 -0
  145. data/tracks/ruby/exercises/transpose/.version +1 -0
  146. data/tracks/ruby/exercises/transpose/example.rb +14 -0
  147. data/tracks/ruby/exercises/transpose/example.tt +22 -0
  148. data/tracks/ruby/exercises/transpose/transpose_test.rb +303 -0
  149. data/tracks/ruby/lib/bowling_cases.rb +46 -0
  150. data/tracks/ruby/lib/isogram_cases.rb +1 -1
  151. data/tracks/ruby/lib/tournament_cases.rb +45 -0
  152. data/tracks/ruby/lib/transpose_cases.rb +45 -0
  153. data/tracks/rust/config.json +2 -0
  154. data/tracks/rust/exercises/bowling/.gitignore +7 -0
  155. data/tracks/rust/exercises/bowling/Cargo.toml +3 -0
  156. data/tracks/rust/exercises/bowling/example.rs +134 -0
  157. data/tracks/rust/exercises/bowling/tests/bowling.rs +373 -0
  158. data/tracks/rust/exercises/grains/.gitignore +7 -0
  159. data/tracks/rust/exercises/grains/Cargo.toml +3 -0
  160. data/tracks/rust/exercises/grains/example.rs +11 -0
  161. data/tracks/rust/exercises/grains/src/lib.rs +7 -0
  162. data/tracks/rust/exercises/grains/tests/grains.rs +63 -0
  163. data/tracks/rust/problems.md +2 -0
  164. data/tracks/scala/README.md +38 -0
  165. data/tracks/scala/config.json +20 -74
  166. data/tracks/scala/exercises/accumulate/src/test/scala/accumulate_test.scala +4 -0
  167. data/tracks/scala/exercises/allergies/src/test/scala/allergies_test.scala +11 -0
  168. data/tracks/scala/exercises/alphametics/build.sbt +6 -0
  169. data/tracks/scala/exercises/alphametics/example.scala +125 -0
  170. data/tracks/scala/exercises/alphametics/src/main/scala/.keep +0 -0
  171. data/tracks/scala/exercises/alphametics/src/test/scala/AlphameticsTest.scala +62 -0
  172. data/tracks/scala/exercises/atbash-cipher/src/test/scala/atbash_test.scala +8 -0
  173. data/tracks/scala/exercises/bank-account/src/test/scala/BankAccountTest.scala +4 -0
  174. data/tracks/scala/exercises/binary/src/test/scala/binary_test.scala +13 -0
  175. data/tracks/scala/exercises/binary-search-tree/src/test/scala/BstTest.scala +11 -0
  176. data/tracks/scala/exercises/bowling/Example.scala +116 -0
  177. data/tracks/scala/exercises/bowling/build.sbt +3 -0
  178. data/tracks/scala/exercises/bowling/src/main/scala/Bowling.scala +11 -0
  179. data/tracks/scala/exercises/bowling/src/test/scala/BowlingSuite.scala +237 -0
  180. data/tracks/scala/exercises/clock/src/test/scala/ClockTest.scala +50 -0
  181. data/tracks/scala/exercises/connect/README.md +17 -0
  182. data/tracks/scala/exercises/connect/src/test/scala/ConnectTest.scala +7 -0
  183. data/tracks/scala/exercises/crypto-square/src/test/scala/{CrytpoSquareTest.scala → CryptoSquareTest.scala} +9 -0
  184. data/tracks/scala/exercises/custom-set/src/test/scala/CustomSetTest.scala +36 -0
  185. data/tracks/scala/exercises/difference-of-squares/src/test/scala/squares_test.scala +8 -0
  186. data/tracks/scala/exercises/forth/src/test/scala/ForthTest.scala +22 -0
  187. data/tracks/scala/exercises/hexadecimal/src/test/scala/HexadecimalTest.scala +8 -0
  188. data/tracks/scala/exercises/kindergarten-garden/src/test/scala/GardenTest.scala +5 -0
  189. data/tracks/scala/exercises/largest-series-product/src/test/scala/SeriesTest.scala +2 -0
  190. data/tracks/scala/exercises/linked-list/src/test/scala/DequeTest.scala +4 -0
  191. data/tracks/scala/exercises/luhn/src/test/scala/LuhnTest.scala +4 -0
  192. data/tracks/scala/exercises/matrix/src/test/scala/MatrixTest.scala +2 -0
  193. data/tracks/scala/exercises/minesweeper/src/test/scala/MinesweeperTest.scala +6 -0
  194. data/tracks/scala/exercises/nth-prime/src/test/scala/PrimeTest.scala +5 -0
  195. data/tracks/scala/exercises/ocr-numbers/src/test/scala/OcrTest.scala +15 -0
  196. data/tracks/scala/exercises/octal/src/test/scala/OctalTest.scala +7 -0
  197. data/tracks/scala/exercises/parallel-letter-frequency/src/test/scala/FrequencyTest.scala +8 -0
  198. data/tracks/scala/exercises/pascals-triangle/src/test/scala/PascalsTriangleTest.scala +5 -0
  199. data/tracks/scala/exercises/pig-latin/src/test/scala/PigLatinTest.scala +4 -0
  200. data/tracks/scala/exercises/pythagorean-triplet/src/test/scala/PythagoreanTripletTest.scala +3 -0
  201. data/tracks/scala/exercises/queen-attack/src/test/scala/QueensTest.scala +5 -0
  202. data/tracks/scala/exercises/robot-simulator/src/test/scala/RobotTest.scala +16 -8
  203. data/tracks/scala/exercises/say/src/test/scala/SayTest.scala +34 -17
  204. data/tracks/scala/exercises/scrabble-score/src/test/scala/scrabble_score_test.scala +6 -0
  205. data/tracks/scala/exercises/secret-handshake/src/test/scala/SecretHandshakeTest.scala +18 -9
  206. data/tracks/scala/exercises/series/src/test/scala/SeriesTest.scala +4 -2
  207. data/tracks/scala/exercises/sieve/src/test/scala/SieveTest.scala +8 -4
  208. data/tracks/scala/exercises/simple-cipher/src/test/scala/CipherTest.scala +17 -9
  209. data/tracks/scala/exercises/sublist/src/test/scala/sublist_test.scala +17 -0
  210. data/tracks/scala/exercises/trinary/src/test/scala/TrinaryTest.scala +14 -7
  211. data/tracks/scala/exercises/wordy/src/test/scala/WordProblemTest.scala +28 -14
  212. data/tracks/scala/project/Build.scala +3 -3
  213. data/tracks/scala/testgen/src/main/scala/BowlingTestGenerator.scala +57 -0
  214. data/tracks/sml/config.json +41 -6
  215. data/tracks/sml/exercises/flatten-array/example.sml +6 -0
  216. data/tracks/sml/exercises/flatten-array/test_flatten_array.sml +10 -0
  217. data/tracks/sml/exercises/nth-prime/example.sml +46 -0
  218. data/tracks/sml/exercises/nth-prime/test_nth_prime.sml +14 -0
  219. data/tracks/sml/exercises/raindrops/example.sml +9 -0
  220. data/tracks/sml/exercises/raindrops/raindrops.sml +2 -0
  221. data/tracks/sml/exercises/raindrops/test_raindrops.sml +95 -0
  222. data/tracks/swift/config.json +269 -328
  223. data/tracks/swift/exercises/pangram/PangramExample.swift +17 -0
  224. data/tracks/swift/exercises/pangram/PangramTest.swift +43 -0
  225. data/tracks/swift/xcodeProject/xSwift.xcodeproj/project.pbxproj +16 -0
  226. metadata +95 -3
@@ -4,56 +4,171 @@ open NUnit.Framework
4
4
 
5
5
  open Bowling
6
6
 
7
- let rollMany pins count game =
8
- List.replicate count pins
9
- |> List.fold (fun acc item -> roll item acc) game
10
-
11
- let rollSpare game =
12
- game
13
- |> roll 5
14
- |> roll 5
15
-
16
- let rollStrike game = game |> roll 10
7
+ let rollMany rolls game = List.fold (fun game pins -> roll pins game) game rolls
17
8
 
18
9
  [<Test>]
19
- let ``Gutter game`` () =
20
- let result = newGame |> rollMany 0 20
21
- Assert.That(score result, Is.EqualTo(0))
22
-
10
+ let ``Should be able to score a game with all zeros`` () =
11
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
12
+ let game = rollMany rolls newGame
13
+ Assert.That(score game, Is.EqualTo(Some 0))
14
+
23
15
  [<Test>]
24
16
  [<Ignore("Remove to run test")>]
25
- let ``All ones game`` () =
26
- let result = newGame |> rollMany 1 20
27
- Assert.That(score result, Is.EqualTo(20))
28
-
17
+ let ``Should be able to score a game with no strikes or spares`` () =
18
+ let rolls = [3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6; 3; 6]
19
+ let game = rollMany rolls newGame
20
+ Assert.That(score game, Is.EqualTo(Some 90))
21
+
29
22
  [<Test>]
30
23
  [<Ignore("Remove to run test")>]
31
- let ``One spare game`` () =
32
- let result =
33
- newGame
34
- |> rollSpare
35
- |> roll 3
36
- |> rollMany 0 17
37
-
38
- Assert.That(score result, Is.EqualTo(16))
39
-
24
+ let ``A spare followed by zeros is worth ten points`` () =
25
+ let rolls = [6; 4; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
26
+ let game = rollMany rolls newGame
27
+ Assert.That(score game, Is.EqualTo(Some 10))
28
+
40
29
  [<Test>]
41
30
  [<Ignore("Remove to run test")>]
42
- let ``One strike game`` () =
43
- let result =
44
- newGame
45
- |> rollStrike
46
- |> roll 3
47
- |> roll 4
48
- |> rollMany 0 16
49
-
50
- Assert.That(score result, Is.EqualTo(24))
51
-
31
+ let ``Points scored in the roll after a spare are counted twice`` () =
32
+ let rolls = [6; 4; 3; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
33
+ let game = rollMany rolls newGame
34
+ Assert.That(score game, Is.EqualTo(Some 16))
35
+
52
36
  [<Test>]
53
37
  [<Ignore("Remove to run test")>]
54
- let ``Perfect game`` () =
55
- let result = newGame |> rollMany 10 12
56
-
57
- Assert.That(score result, Is.EqualTo(300))
58
-
59
-
38
+ let ``Consecutive spares each get a one roll bonus`` () =
39
+ let rolls = [5; 5; 3; 7; 4; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
40
+ let game = rollMany rolls newGame
41
+ Assert.That(score game, Is.EqualTo(Some 31))
42
+
43
+ [<Test>]
44
+ [<Ignore("Remove to run test")>]
45
+ let ``A spare in the last frame gets a one roll bonus that is counted once`` () =
46
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 7; 3; 7]
47
+ let game = rollMany rolls newGame
48
+ Assert.That(score game, Is.EqualTo(Some 17))
49
+
50
+ [<Test>]
51
+ [<Ignore("Remove to run test")>]
52
+ let ``A strike earns ten points in frame with a single roll`` () =
53
+ let rolls = [10; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
54
+ let game = rollMany rolls newGame
55
+ Assert.That(score game, Is.EqualTo(Some 10))
56
+
57
+ [<Test>]
58
+ [<Ignore("Remove to run test")>]
59
+ let ``Points scored in the two rolls after a strike are counted twice as a bonus`` () =
60
+ let rolls = [10; 5; 3; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
61
+ let game = rollMany rolls newGame
62
+ Assert.That(score game, Is.EqualTo(Some 26))
63
+
64
+ [<Test>]
65
+ [<Ignore("Remove to run test")>]
66
+ let ``Consecutive strikes each get the two roll bonus`` () =
67
+ let rolls = [10; 10; 10; 5; 3; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
68
+ let game = rollMany rolls newGame
69
+ Assert.That(score game, Is.EqualTo(Some 81))
70
+
71
+ [<Test>]
72
+ [<Ignore("Remove to run test")>]
73
+ let ``A strike in the last frame gets a two roll bonus that is counted once`` () =
74
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 7; 1]
75
+ let game = rollMany rolls newGame
76
+ Assert.That(score game, Is.EqualTo(Some 18))
77
+
78
+ [<Test>]
79
+ [<Ignore("Remove to run test")>]
80
+ let ``Rolling a spare with the two roll bonus does not get a bonus roll`` () =
81
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 7; 3]
82
+ let game = rollMany rolls newGame
83
+ Assert.That(score game, Is.EqualTo(Some 20))
84
+
85
+ [<Test>]
86
+ [<Ignore("Remove to run test")>]
87
+ let ``Strikes with the two roll bonus do not get bonus rolls`` () =
88
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 10; 10]
89
+ let game = rollMany rolls newGame
90
+ Assert.That(score game, Is.EqualTo(Some 30))
91
+
92
+ [<Test>]
93
+ [<Ignore("Remove to run test")>]
94
+ let ``A strike with the one roll bonus after a spare in the last frame does not get a bonus`` () =
95
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 7; 3; 10]
96
+ let game = rollMany rolls newGame
97
+ Assert.That(score game, Is.EqualTo(Some 20))
98
+
99
+ [<Test>]
100
+ [<Ignore("Remove to run test")>]
101
+ let ``All strikes is a perfect game`` () =
102
+ let rolls = [10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 10; 10]
103
+ let game = rollMany rolls newGame
104
+ Assert.That(score game, Is.EqualTo(Some 300))
105
+
106
+ [<Test>]
107
+ [<Ignore("Remove to run test")>]
108
+ let ``Rolls can not score negative points`` () =
109
+ let rolls = [-1; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
110
+ let game = rollMany rolls newGame
111
+ Assert.That(score game, Is.EqualTo(None))
112
+
113
+ [<Test>]
114
+ [<Ignore("Remove to run test")>]
115
+ let ``A roll can not score more than 10 points`` () =
116
+ let rolls = [11; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
117
+ let game = rollMany rolls newGame
118
+ Assert.That(score game, Is.EqualTo(None))
119
+
120
+ [<Test>]
121
+ [<Ignore("Remove to run test")>]
122
+ let ``Two rolls in a frame can not score more than 10 points`` () =
123
+ let rolls = [5; 6; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
124
+ let game = rollMany rolls newGame
125
+ Assert.That(score game, Is.EqualTo(None))
126
+
127
+ [<Test>]
128
+ [<Ignore("Remove to run test")>]
129
+ let ``Two bonus rolls after a strike in the last frame can not score more than 10 points`` () =
130
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 5; 6]
131
+ let game = rollMany rolls newGame
132
+ Assert.That(score game, Is.EqualTo(None))
133
+
134
+ [<Test>]
135
+ [<Ignore("Remove to run test")>]
136
+ let ``An unstarted game can not be scored`` () =
137
+ let rolls = []
138
+ let game = rollMany rolls newGame
139
+ Assert.That(score game, Is.EqualTo(None))
140
+
141
+ [<Test>]
142
+ [<Ignore("Remove to run test")>]
143
+ let ``An incomplete game can not be scored`` () =
144
+ let rolls = [0; 0]
145
+ let game = rollMany rolls newGame
146
+ Assert.That(score game, Is.EqualTo(None))
147
+
148
+ [<Test>]
149
+ [<Ignore("Remove to run test")>]
150
+ let ``A game with more than ten frames can not be scored`` () =
151
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0]
152
+ let game = rollMany rolls newGame
153
+ Assert.That(score game, Is.EqualTo(None))
154
+
155
+ [<Test>]
156
+ [<Ignore("Remove to run test")>]
157
+ let ``Bonus rolls for a strike in the last frame must be rolled before score can be calculated`` () =
158
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10]
159
+ let game = rollMany rolls newGame
160
+ Assert.That(score game, Is.EqualTo(None))
161
+
162
+ [<Test>]
163
+ [<Ignore("Remove to run test")>]
164
+ let ``Both bonus rolls for a strike in the last frame must be rolled before score can be calculated`` () =
165
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 10; 10]
166
+ let game = rollMany rolls newGame
167
+ Assert.That(score game, Is.EqualTo(None))
168
+
169
+ [<Test>]
170
+ [<Ignore("Remove to run test")>]
171
+ let ``Bonus roll for a spare in the last frame must be rolled before score can be calculated`` () =
172
+ let rolls = [0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 7; 3]
173
+ let game = rollMany rolls newGame
174
+ Assert.That(score game, Is.EqualTo(None))
@@ -1,29 +1,58 @@
1
1
  module Bowling
2
2
 
3
+ let map2 f opt1 opt2 =
4
+ match opt1, opt2 with
5
+ | Some x, Some y -> Some (f x y)
6
+ | _ -> None
7
+
3
8
  let numberOfFrames = 10
4
9
  let maximumFrameScore = 10
5
-
6
- let roll pins game = game @ [pins]
7
-
8
- let score game =
9
-
10
- let roll index = List.item index game
11
-
12
- let isStrike frameIndex = roll frameIndex = maximumFrameScore
13
- let isSpare frameIndex = roll frameIndex + roll (frameIndex + 1) = maximumFrameScore
14
-
15
- let strikeBonus frameIndex = roll (frameIndex + 1) + roll (frameIndex + 2)
16
- let spareBonus frameIndex = roll (frameIndex + 2)
17
-
18
- let sumOfBallsInFrame frameIndex = roll frameIndex + roll (frameIndex + 1)
10
+ let minimumFrameScore = 0
11
+
12
+ let newGame = Some []
13
+
14
+ let validatePins pins =
15
+ if pins < minimumFrameScore || pins > maximumFrameScore then
16
+ None
17
+ else
18
+ Some pins
19
+
20
+ let isStrike pins = pins = maximumFrameScore
21
+ let isSpare pins1 pins2 = pins1 + pins2 = maximumFrameScore
22
+
23
+ let roll pins rolls = map2 (fun rolls pins -> rolls @ [pins]) rolls (validatePins pins)
24
+
25
+ let rec scoreRolls totalScore frame rolls =
26
+ let isLastFrame = frame = numberOfFrames
27
+ let gameFinished = frame = numberOfFrames + 1
28
+
29
+ let scoreStrike remainder =
30
+ match remainder with
31
+ | x::y::zs when isLastFrame ->
32
+ if x + y > 10 && x <> 10 then None
33
+ else scoreRolls (totalScore + 10 + x + y) (frame + 1) zs
34
+ | x::y::zs ->
35
+ scoreRolls (totalScore + 10 + x + y) (frame + 1) (x::y::zs)
36
+ | _ ->
37
+ None
38
+
39
+ let scoreSpare x y remainder =
40
+ match remainder with
41
+ | z::zs->
42
+ scoreRolls (totalScore + x + y + z) (frame + 1) (if isLastFrame then zs else z::zs)
43
+ | _ ->
44
+ None
45
+
46
+ let scoreNormal x y remainder =
47
+ match validatePins (x + y) with
48
+ | Some z -> scoreRolls (totalScore + z) (frame + 1) remainder
49
+ | None -> None
50
+
51
+ match rolls with
52
+ | [] -> if gameFinished then Some totalScore else None
53
+ | x::xs when isStrike x -> scoreStrike xs
54
+ | x::y::ys when isSpare x y -> scoreSpare x y ys
55
+ | x::y::zs -> scoreNormal x y zs
56
+ | _ -> None
19
57
 
20
- let folder (score, frameIndex) _ =
21
- if isStrike frameIndex then (score + 10 + strikeBonus frameIndex, frameIndex + 1)
22
- elif isSpare frameIndex then (score + 10 + spareBonus frameIndex, frameIndex + 2)
23
- else (score + sumOfBallsInFrame frameIndex, frameIndex + 2)
24
-
25
- [1..numberOfFrames]
26
- |> List.fold folder (0, 0)
27
- |> fst
28
-
29
- let newGame = []
58
+ let score = Option.bind (scoreRolls 0 1)
@@ -211,6 +211,11 @@
211
211
  "slug": "robot-name",
212
212
  "topics": []
213
213
  },
214
+ {
215
+ "difficulty": 1,
216
+ "slug": "diamond",
217
+ "topics": []
218
+ },
214
219
  {
215
220
  "difficulty": 1,
216
221
  "slug": "react",
@@ -0,0 +1,227 @@
1
+ package diamond
2
+
3
+ import (
4
+ "math/rand"
5
+ "reflect"
6
+ "strings"
7
+ "testing"
8
+ "testing/quick"
9
+ "time"
10
+ )
11
+
12
+ var config = &quick.Config{Rand: rand.New(rand.NewSource(time.Now().UnixNano()))}
13
+
14
+ type correctChar byte
15
+
16
+ func (c correctChar) Generate(rand *rand.Rand, _ int) reflect.Value {
17
+ return reflect.ValueOf(correctChar('A' + rand.Intn('Z'-'A'+1)))
18
+ }
19
+
20
+ func checkCorrect(requirement func(byte, []string) bool, keepSeparator bool, t *testing.T) {
21
+ assertion := func(char correctChar) bool {
22
+ d, err := Gen(byte(char))
23
+ if err != nil {
24
+ return false
25
+ }
26
+ separator := strings.Split
27
+ if keepSeparator {
28
+ separator = strings.SplitAfter
29
+ }
30
+ rows := separator(d, "\n")
31
+ if len(rows) < 2 {
32
+ return false
33
+ }
34
+ return requirement(byte(char), rows[:len(rows)-1])
35
+ }
36
+ if err := quick.Check(assertion, config); err != nil {
37
+ t.Error(err)
38
+ }
39
+ }
40
+
41
+ func TestFirstRowContainsOneA(t *testing.T) {
42
+ requirement := func(char byte, rows []string) bool {
43
+ return len(rows) > 0 && strings.Count(rows[0], "A") == 1
44
+ }
45
+ checkCorrect(requirement, false, t)
46
+ }
47
+
48
+ func TestLastRowContainsOneA(t *testing.T) {
49
+ requirement := func(char byte, rows []string) bool {
50
+ return len(rows) > 0 && strings.Count(rows[len(rows)-1], "A") == 1
51
+ }
52
+ checkCorrect(requirement, false, t)
53
+ }
54
+
55
+ func TestAllRowsIdenticalLettersExceptFirstAndLast(t *testing.T) {
56
+ requirement := func(char byte, rows []string) bool {
57
+ for i, row := range rows {
58
+ r := strings.TrimSpace(row)
59
+ if r[0] != r[len(r)-1] {
60
+ return false
61
+ }
62
+ if len(r) < 2 && i != 0 && i != len(rows)-1 {
63
+ return false
64
+ }
65
+ }
66
+ return true
67
+ }
68
+ checkCorrect(requirement, false, t)
69
+ }
70
+
71
+ func TestAllRowsHaveSameTrailingSpaces(t *testing.T) {
72
+ requirement := func(char byte, rows []string) bool {
73
+ for _, row := range rows {
74
+ if len(row) == 0 {
75
+ return false
76
+ }
77
+ for i, j := 0, len(row)-1; i < j && row[i] == ' '; i, j = i+1, j-1 {
78
+ if row[j] != ' ' {
79
+ return false
80
+ }
81
+ }
82
+ }
83
+ return true
84
+ }
85
+ checkCorrect(requirement, false, t)
86
+ }
87
+
88
+ func TestDiamondIsHorizontallySymmetric(t *testing.T) {
89
+ requirement := func(char byte, rows []string) bool {
90
+ for _, row := range rows {
91
+ l := len(row)
92
+ for i := l/2 - 1; i >= 0; i-- {
93
+ if row[i] != row[l-1-i] {
94
+ return false
95
+ }
96
+ }
97
+ }
98
+ return true
99
+ }
100
+ checkCorrect(requirement, false, t)
101
+ }
102
+
103
+ func TestDiamondIsVerticallySymmetric(t *testing.T) {
104
+ requirement := func(char byte, rows []string) bool {
105
+ for i, j := 0, len(rows)-1; i < j; i, j = i+1, j-1 {
106
+ if rows[i] != rows[j] {
107
+ return false
108
+ }
109
+ }
110
+ return true
111
+ }
112
+ checkCorrect(requirement, true, t)
113
+ }
114
+
115
+ func TestDiamondIsSquare(t *testing.T) {
116
+ requirement := func(char byte, rows []string) bool {
117
+ if int(char-'A')*2+1 != len(rows) {
118
+ return false
119
+ }
120
+ for _, row := range rows {
121
+ if len(row) != len(rows) {
122
+ return false
123
+ }
124
+ }
125
+ return true
126
+ }
127
+ checkCorrect(requirement, false, t)
128
+ }
129
+
130
+ func TestDiamondHasItsShape(t *testing.T) {
131
+ requirement := func(char byte, rows []string) bool {
132
+ var n int
133
+ for i, row := range rows {
134
+ s := len(strings.TrimSpace(row))
135
+ if i > len(rows)/2 && n <= s {
136
+ return false
137
+ } else if i <= len(rows)/2 && n >= s {
138
+ return false
139
+ }
140
+ n = s
141
+ }
142
+ return true
143
+ }
144
+ checkCorrect(requirement, false, t)
145
+ }
146
+
147
+ func TestTopHalfHasAscendingLetters(t *testing.T) {
148
+ requirement := func(char byte, rows []string) bool {
149
+ var start byte = 'A' - 1
150
+ for i := 0; i <= len(rows)/2; i++ {
151
+ s := strings.TrimLeft(rows[i], " ")
152
+ if s == "" || s[0] <= start {
153
+ return false
154
+ }
155
+ start = s[0]
156
+ }
157
+ return true
158
+ }
159
+ checkCorrect(requirement, false, t)
160
+ }
161
+
162
+ func TestBottomHalfHasDescendingLetters(t *testing.T) {
163
+ requirement := func(char byte, rows []string) bool {
164
+ var start byte = 'A' - 1
165
+ for i := len(rows) - 1; i > len(rows)/2; i-- {
166
+ s := strings.TrimLeft(rows[i], " ")
167
+ if s == "" || s[0] <= start {
168
+ return false
169
+ }
170
+ start = s[0]
171
+ }
172
+ return true
173
+ }
174
+ checkCorrect(requirement, false, t)
175
+ }
176
+
177
+ func TestDiamondFourCornersAreTriangle(t *testing.T) {
178
+ requirement := func(char byte, rows []string) bool {
179
+ notSpace := func(r rune) bool { return r <= 'Z' && r >= 'A' }
180
+ var n int
181
+ for i, row := range rows {
182
+ s := strings.IndexFunc(row, notSpace)
183
+ e := len(row) - strings.LastIndexFunc(row, notSpace) - 1
184
+ if s != e {
185
+ return false
186
+ } else if i == 0 {
187
+ n = s
188
+ } else {
189
+ if i > len(rows)/2 && n >= s {
190
+ return false
191
+ } else if i <= len(rows)/2 && n <= s {
192
+ return false
193
+ }
194
+ n = s
195
+ }
196
+ }
197
+ return true
198
+ }
199
+ checkCorrect(requirement, false, t)
200
+ }
201
+
202
+ type wrongChar byte
203
+
204
+ func (c wrongChar) Generate(rand *rand.Rand, _ int) reflect.Value {
205
+ b := rand.Intn(256)
206
+ for ; b >= 'A' && b <= 'Z'; b = rand.Intn(256) {
207
+ }
208
+ return reflect.ValueOf(wrongChar(b))
209
+ }
210
+
211
+ func TestCharOutOfRangeShouldGiveError(t *testing.T) {
212
+ assertion := func(char wrongChar) bool {
213
+ _, err := Gen(byte(char))
214
+ return err != nil
215
+ }
216
+ if err := quick.Check(assertion, config); err != nil {
217
+ t.Error(err)
218
+ }
219
+ }
220
+
221
+ const targetTestVersion = 1
222
+
223
+ func TestTestVersion(t *testing.T) {
224
+ if testVersion != targetTestVersion {
225
+ t.Errorf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
226
+ }
227
+ }
@@ -0,0 +1,47 @@
1
+ package diamond
2
+
3
+ import (
4
+ "errors"
5
+ "strings"
6
+ )
7
+
8
+ const testVersion = 1
9
+
10
+ const startIndex = 'A'
11
+
12
+ // Gen builds a diamond
13
+ func Gen(char byte) (string, error) {
14
+ if char > 'Z' || char < 'A' {
15
+ return "", errors.New(string(char) + " isn't supported, only between (A, Z)")
16
+ }
17
+ return gen(char), nil
18
+ }
19
+
20
+ func gen(char byte) string {
21
+ var output []string
22
+ currentIndex := int(char - startIndex)
23
+ for i := 0; i <= currentIndex; i++ {
24
+ output = append(output, getLine(currentIndex, i))
25
+ }
26
+ for i := currentIndex - 1; i > -1; i-- {
27
+ output = append(output, getLine(currentIndex, i))
28
+ }
29
+ return strings.Join(output, "\n") + "\n"
30
+ }
31
+
32
+ func getLine(currentStart, current int) string {
33
+ diff := currentStart - current
34
+ return spaces(diff) + alphabets(current) + spaces(diff)
35
+ }
36
+
37
+ func alphabets(current int) string {
38
+ if current == 0 {
39
+ return "A"
40
+ }
41
+ c := current + startIndex
42
+ return string(c) + spaces(current*2-1) + string(c)
43
+ }
44
+
45
+ func spaces(n int) string {
46
+ return strings.Repeat(" ", n)
47
+ }
@@ -1,6 +1,6 @@
1
1
  package foodchain
2
2
 
3
- const testVersion = 2
3
+ const testVersion = 3
4
4
 
5
5
  var verse = []struct{ eaten, comment string }{
6
6
  {"", ""},
@@ -1,11 +1,12 @@
1
1
  package foodchain
2
2
 
3
3
  import (
4
+ "fmt"
4
5
  "strings"
5
6
  "testing"
6
7
  )
7
8
 
8
- const targetTestVersion = 2
9
+ const targetTestVersion = 3
9
10
 
10
11
  func TestTestVersion(t *testing.T) {
11
12
  if testVersion != targetTestVersion {
@@ -67,24 +68,47 @@ I don't know why she swallowed the fly. Perhaps she'll die.`,
67
68
  She's dead, of course!`,
68
69
  }
69
70
 
71
+ // diff compares two multi-line strings and returns a helpful comment
72
+ func diff(got, want string) string {
73
+ g := strings.Split(got, "\n")
74
+ w := strings.Split(want, "\n")
75
+ for i := 0; ; i++ {
76
+ switch {
77
+ case i < len(g) && i < len(w):
78
+ if g[i] == w[i] {
79
+ continue
80
+ }
81
+ return fmt.Sprintf("-- first difference in line %d:\n"+
82
+ "-- got : %q\n-- want: %q\n", i+1, g[i], w[i])
83
+ case i < len(g):
84
+ return fmt.Sprintf("-- got %d extra lines after line %d:\n"+
85
+ "-- first extra line: %q\n", len(g)-len(w), i, g[i])
86
+ case i < len(w):
87
+ return fmt.Sprintf("-- got %d correct lines, want %d more lines:\n"+
88
+ "-- want next: %q\n", i, len(w)-i, w[i])
89
+ default:
90
+ return "no differences found"
91
+ }
92
+ }
93
+ }
94
+
70
95
  func TestVerse(t *testing.T) {
71
96
  for v := 1; v <= 8; v++ {
72
97
  if ret := Verse(v); ret != ref[v] {
73
- t.Fatalf("Verse(%d) =\n%s\n want:\n%s", v, ret, ref[v])
98
+ t.Fatalf("Verse(%d) =\n%s\n want:\n%s\n%s", v, ret, ref[v], diff(ret, ref[v]))
74
99
  }
75
100
  }
76
101
  }
77
102
 
78
103
  func TestVerses(t *testing.T) {
79
- if ret, want := Verses(1, 2), ref[1]+"\n\n"+ref[2]; ret != want {
80
- t.Fatalf("Verses(1, 2) =\n%s\n want:\n%s", ret, want)
104
+ if ret, want := Verses(1, 3), strings.Join(ref[1:4], "\n\n"); ret != want {
105
+ t.Fatalf("Verses(1, 3) =\n%s\n want:\n%s\n%s", ret, want, diff(ret, want))
81
106
  }
82
-
83
107
  }
84
108
 
85
109
  func TestSong(t *testing.T) {
86
110
  if ret, want := Song(), strings.Join(ref[1:], "\n\n"); ret != want {
87
- t.Fatalf("Song() =\n%s\n want:\n%s", ret, want)
111
+ t.Fatalf("Song() =\n%s\n want:\n%s\n%s", ret, want, diff(ret, want))
88
112
  }
89
113
  }
90
114