trackler 2.0.0.1 → 2.0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/bump-content +3 -1
- data/bin/verify-metadata +2 -2
- data/common/exercises/alphametics/canonical-data.json +19 -9
- data/common/exercises/food-chain/canonical-data.json +7 -1
- data/common/exercises/grains/canonical-data.json +66 -0
- data/common/exercises/raindrops/description.md +4 -5
- data/common/exercises/triangle/canonical-data.json +81 -54
- data/lib/trackler/version.rb +1 -1
- data/tracks/c/config.json +26 -19
- data/tracks/c/exercises/allergies/makefile +15 -0
- data/tracks/c/exercises/allergies/src/allergies.h +21 -0
- data/tracks/c/exercises/allergies/src/example.c +32 -0
- data/tracks/c/exercises/allergies/src/example.h +26 -0
- data/tracks/c/exercises/allergies/test/test_allergies.c +203 -0
- data/tracks/c/exercises/allergies/test/vendor/unity.c +1300 -0
- data/tracks/c/exercises/allergies/test/vendor/unity.h +274 -0
- data/tracks/c/exercises/allergies/test/vendor/unity_internals.h +701 -0
- data/tracks/c/exercises/atbash-cipher/makefile +16 -0
- data/tracks/c/exercises/atbash-cipher/src/example.c +71 -0
- data/tracks/c/exercises/atbash-cipher/src/example.h +7 -0
- data/tracks/c/exercises/atbash-cipher/test/test_atbash_cipher.c +113 -0
- data/tracks/c/exercises/atbash-cipher/test/vendor/unity.c +1300 -0
- data/tracks/c/exercises/atbash-cipher/test/vendor/unity.h +274 -0
- data/tracks/c/exercises/atbash-cipher/test/vendor/unity_internals.h +701 -0
- data/tracks/c/exercises/phone-number/makefile +16 -0
- data/tracks/c/exercises/phone-number/src/example.c +66 -0
- data/tracks/c/exercises/phone-number/src/example.h +8 -0
- data/tracks/c/exercises/phone-number/test/test_phone_number.c +108 -0
- data/tracks/c/exercises/phone-number/test/vendor/unity.c +1300 -0
- data/tracks/c/exercises/phone-number/test/vendor/unity.h +274 -0
- data/tracks/c/exercises/phone-number/test/vendor/unity_internals.h +701 -0
- data/tracks/csharp/config.json +8 -0
- data/tracks/csharp/exercises/bowling/BowlingTest.cs +188 -33
- data/tracks/csharp/exercises/bowling/Example.cs +38 -9
- data/tracks/csharp/exercises/rectangles/Example.cs +91 -0
- data/tracks/csharp/exercises/rectangles/RectanglesTest.cs +133 -0
- data/tracks/elisp/docs/INSTALLATION.org +1 -1
- data/tracks/elixir/config.json +8 -66
- data/tracks/elixir/docs/LEARNING.md +1 -1
- data/tracks/elixir/exercises/diamond/diamond_test.exs +12 -12
- data/tracks/elixir/exercises/grep/example.exs +92 -0
- data/tracks/elixir/exercises/grep/grep.exs +6 -0
- data/tracks/elixir/exercises/grep/grep_test.exs +259 -0
- data/tracks/elixir/exercises/markdown/markdown.exs +59 -2
- data/tracks/elixir/exercises/nucleotide-count/nucleotide_count_test.exs +4 -4
- data/tracks/elixir/exercises/phone-number/phone_number_test.exs +5 -0
- data/tracks/fsharp/exercises/bowling/BowlingTest.fs +158 -43
- data/tracks/fsharp/exercises/bowling/Example.fs +53 -24
- data/tracks/go/config.json +5 -0
- data/tracks/go/exercises/diamond/diamond_test.go +227 -0
- data/tracks/go/exercises/diamond/example.go +47 -0
- data/tracks/go/exercises/food-chain/example.go +1 -1
- data/tracks/go/exercises/food-chain/food_chain_test.go +30 -6
- data/tracks/go/exercises/nucleotide-count/example.go +21 -8
- data/tracks/go/exercises/nucleotide-count/nucleotide_count_test.go +45 -37
- data/tracks/haskell/exercises/anagram/test/Tests.hs +1 -11
- data/tracks/java/bin/journey-test.sh +165 -128
- data/tracks/java/docs/ABOUT.md +5 -8
- data/tracks/java/exercises/hello-world/build.gradle +0 -6
- data/tracks/java/exercises/hello-world/src/test/java/HelloWorldTest.java +0 -1
- data/tracks/java/exercises/meetup/build.gradle +0 -1
- data/tracks/java/exercises/meetup/src/example/java/Meetup.java +9 -7
- data/tracks/java/exercises/meetup/src/test/java/MeetupTest.java +185 -188
- data/tracks/lua/config.json +0 -73
- data/tracks/lua/exercises/bowling/bowling_spec.lua +92 -48
- data/tracks/lua/exercises/bowling/example.lua +4 -1
- data/tracks/objective-c/config.json +9 -30
- data/tracks/objective-c/exercises/pangram/PangramExample.h +7 -0
- data/tracks/objective-c/exercises/pangram/PangramExample.m +21 -0
- data/tracks/objective-c/exercises/pangram/PangramTest.m +51 -0
- data/tracks/objective-c/xcodeProject/ObjectiveC.xcodeproj/project.pbxproj +18 -0
- data/tracks/ocaml/README.md +46 -1
- data/tracks/ocaml/SETUP.md +21 -2
- data/tracks/ocaml/config.json +48 -78
- data/tracks/perl5/README.md +15 -8
- data/tracks/perl5/testall.pl +5 -5
- data/tracks/php/config.json +26 -34
- data/tracks/php/docs/ABOUT.md +15 -0
- data/tracks/php/exercises/allergies/allergies_test.php +121 -0
- data/tracks/php/exercises/allergies/example.php +64 -0
- data/tracks/php/exercises/etl/etl_test.php +52 -0
- data/tracks/php/exercises/etl/example.php +12 -0
- data/tracks/php/exercises/nucleotide-count/example.php +25 -0
- data/tracks/php/exercises/nucleotide-count/nucleotide-count_test.php +36 -0
- data/tracks/php/exercises/pig-latin/example.php +25 -29
- data/tracks/php/exercises/pig-latin/pig-latin_test.php +26 -19
- data/tracks/php/exercises/space-age/example.php +65 -0
- data/tracks/php/exercises/space-age/space-age_test.php +70 -0
- data/tracks/php/exercises/triangle/example.php +43 -0
- data/tracks/php/exercises/triangle/triangle_test.php +140 -0
- data/tracks/pony/exercises/anagram/example.pony +6 -6
- data/tracks/pony/exercises/anagram/test.pony +3 -4
- data/tracks/pony/exercises/bob/test.pony +23 -23
- data/tracks/pony/exercises/difference-of-squares/test.pony +11 -11
- data/tracks/pony/exercises/hamming/example.pony +3 -3
- data/tracks/pony/exercises/hamming/test.pony +16 -16
- data/tracks/pony/exercises/hello-world/test.pony +5 -4
- data/tracks/pony/exercises/leap/test.pony +8 -8
- data/tracks/python/config.json +18 -0
- data/tracks/python/exercises/diamond/diamond_test.py +33 -0
- data/tracks/python/exercises/diamond/example.py +15 -0
- data/tracks/python/exercises/linked-list/example.py +47 -0
- data/tracks/python/exercises/linked-list/linked_list.py +13 -0
- data/tracks/python/exercises/linked-list/linked_list_test.py +49 -0
- data/tracks/python/exercises/list-ops/example.py +55 -0
- data/tracks/python/exercises/list-ops/list_ops.py +38 -0
- data/tracks/python/exercises/list-ops/list_ops_test.py +136 -0
- data/tracks/ruby/bin/generate +22 -2
- data/tracks/ruby/config.json +12 -83
- data/tracks/ruby/exercises/allergies/allergies_test.rb +0 -1
- data/tracks/ruby/exercises/atbash-cipher/atbash_cipher_test.rb +0 -1
- data/tracks/ruby/exercises/beer-song/beer_song_test.rb +1 -2
- data/tracks/ruby/exercises/binary/example.rb +0 -1
- data/tracks/ruby/exercises/binary-search-tree/binary_search_tree_test.rb +1 -1
- data/tracks/ruby/exercises/bowling/.version +1 -0
- data/tracks/ruby/exercises/bowling/bowling_test.rb +109 -133
- data/tracks/ruby/exercises/bowling/example.rb +7 -7
- data/tracks/ruby/exercises/bowling/example.tt +27 -0
- data/tracks/ruby/exercises/circular-buffer/circular_buffer_test.rb +0 -2
- data/tracks/ruby/exercises/clock/example.rb +0 -2
- data/tracks/ruby/exercises/connect/connect_test.rb +0 -1
- data/tracks/ruby/exercises/custom-set/custom_set_test.rb +0 -1
- data/tracks/ruby/exercises/diamond/diamond_test.rb +0 -1
- data/tracks/ruby/exercises/etl/etl_test.rb +1 -1
- data/tracks/ruby/exercises/house/house_test.rb +0 -1
- data/tracks/ruby/exercises/isogram/isogram_test.rb +2 -2
- data/tracks/ruby/exercises/largest-series-product/example.tt +0 -3
- data/tracks/ruby/exercises/largest-series-product/largest_series_product_test.rb +0 -4
- data/tracks/ruby/exercises/linked-list/linked_list_test.rb +1 -1
- data/tracks/ruby/exercises/meetup/meetup_test.rb +0 -1
- data/tracks/ruby/exercises/nth-prime/example.tt +0 -2
- data/tracks/ruby/exercises/nth-prime/nth_prime_test.rb +0 -2
- data/tracks/ruby/exercises/ocr-numbers/ocr_numbers_test.rb +1 -2
- data/tracks/ruby/exercises/protein-translation/protein_translation_test.rb +0 -1
- data/tracks/ruby/exercises/proverb/proverb_test.rb +1 -3
- data/tracks/ruby/exercises/queen-attack/example.rb +3 -1
- data/tracks/ruby/exercises/queen-attack/queen_attack_test.rb +34 -8
- data/tracks/ruby/exercises/robot-simulator/robot_simulator_test.rb +1 -1
- data/tracks/ruby/exercises/strain/strain_test.rb +2 -2
- data/tracks/ruby/exercises/tournament/.version +1 -0
- data/tracks/ruby/exercises/tournament/example.rb +54 -0
- data/tracks/ruby/exercises/tournament/example.tt +23 -0
- data/tracks/ruby/exercises/tournament/tournament_test.rb +92 -0
- data/tracks/ruby/exercises/transpose/.version +1 -0
- data/tracks/ruby/exercises/transpose/example.rb +14 -0
- data/tracks/ruby/exercises/transpose/example.tt +22 -0
- data/tracks/ruby/exercises/transpose/transpose_test.rb +303 -0
- data/tracks/ruby/lib/bowling_cases.rb +46 -0
- data/tracks/ruby/lib/isogram_cases.rb +1 -1
- data/tracks/ruby/lib/tournament_cases.rb +45 -0
- data/tracks/ruby/lib/transpose_cases.rb +45 -0
- data/tracks/rust/config.json +2 -0
- data/tracks/rust/exercises/bowling/.gitignore +7 -0
- data/tracks/rust/exercises/bowling/Cargo.toml +3 -0
- data/tracks/rust/exercises/bowling/example.rs +134 -0
- data/tracks/rust/exercises/bowling/tests/bowling.rs +373 -0
- data/tracks/rust/exercises/grains/.gitignore +7 -0
- data/tracks/rust/exercises/grains/Cargo.toml +3 -0
- data/tracks/rust/exercises/grains/example.rs +11 -0
- data/tracks/rust/exercises/grains/src/lib.rs +7 -0
- data/tracks/rust/exercises/grains/tests/grains.rs +63 -0
- data/tracks/rust/problems.md +2 -0
- data/tracks/scala/README.md +38 -0
- data/tracks/scala/config.json +20 -74
- data/tracks/scala/exercises/accumulate/src/test/scala/accumulate_test.scala +4 -0
- data/tracks/scala/exercises/allergies/src/test/scala/allergies_test.scala +11 -0
- data/tracks/scala/exercises/alphametics/build.sbt +6 -0
- data/tracks/scala/exercises/alphametics/example.scala +125 -0
- data/tracks/scala/exercises/alphametics/src/main/scala/.keep +0 -0
- data/tracks/scala/exercises/alphametics/src/test/scala/AlphameticsTest.scala +62 -0
- data/tracks/scala/exercises/atbash-cipher/src/test/scala/atbash_test.scala +8 -0
- data/tracks/scala/exercises/bank-account/src/test/scala/BankAccountTest.scala +4 -0
- data/tracks/scala/exercises/binary/src/test/scala/binary_test.scala +13 -0
- data/tracks/scala/exercises/binary-search-tree/src/test/scala/BstTest.scala +11 -0
- data/tracks/scala/exercises/bowling/Example.scala +116 -0
- data/tracks/scala/exercises/bowling/build.sbt +3 -0
- data/tracks/scala/exercises/bowling/src/main/scala/Bowling.scala +11 -0
- data/tracks/scala/exercises/bowling/src/test/scala/BowlingSuite.scala +237 -0
- data/tracks/scala/exercises/clock/src/test/scala/ClockTest.scala +50 -0
- data/tracks/scala/exercises/connect/README.md +17 -0
- data/tracks/scala/exercises/connect/src/test/scala/ConnectTest.scala +7 -0
- data/tracks/scala/exercises/crypto-square/src/test/scala/{CrytpoSquareTest.scala → CryptoSquareTest.scala} +9 -0
- data/tracks/scala/exercises/custom-set/src/test/scala/CustomSetTest.scala +36 -0
- data/tracks/scala/exercises/difference-of-squares/src/test/scala/squares_test.scala +8 -0
- data/tracks/scala/exercises/forth/src/test/scala/ForthTest.scala +22 -0
- data/tracks/scala/exercises/hexadecimal/src/test/scala/HexadecimalTest.scala +8 -0
- data/tracks/scala/exercises/kindergarten-garden/src/test/scala/GardenTest.scala +5 -0
- data/tracks/scala/exercises/largest-series-product/src/test/scala/SeriesTest.scala +2 -0
- data/tracks/scala/exercises/linked-list/src/test/scala/DequeTest.scala +4 -0
- data/tracks/scala/exercises/luhn/src/test/scala/LuhnTest.scala +4 -0
- data/tracks/scala/exercises/matrix/src/test/scala/MatrixTest.scala +2 -0
- data/tracks/scala/exercises/minesweeper/src/test/scala/MinesweeperTest.scala +6 -0
- data/tracks/scala/exercises/nth-prime/src/test/scala/PrimeTest.scala +5 -0
- data/tracks/scala/exercises/ocr-numbers/src/test/scala/OcrTest.scala +15 -0
- data/tracks/scala/exercises/octal/src/test/scala/OctalTest.scala +7 -0
- data/tracks/scala/exercises/parallel-letter-frequency/src/test/scala/FrequencyTest.scala +8 -0
- data/tracks/scala/exercises/pascals-triangle/src/test/scala/PascalsTriangleTest.scala +5 -0
- data/tracks/scala/exercises/pig-latin/src/test/scala/PigLatinTest.scala +4 -0
- data/tracks/scala/exercises/pythagorean-triplet/src/test/scala/PythagoreanTripletTest.scala +3 -0
- data/tracks/scala/exercises/queen-attack/src/test/scala/QueensTest.scala +5 -0
- data/tracks/scala/exercises/robot-simulator/src/test/scala/RobotTest.scala +16 -8
- data/tracks/scala/exercises/say/src/test/scala/SayTest.scala +34 -17
- data/tracks/scala/exercises/scrabble-score/src/test/scala/scrabble_score_test.scala +6 -0
- data/tracks/scala/exercises/secret-handshake/src/test/scala/SecretHandshakeTest.scala +18 -9
- data/tracks/scala/exercises/series/src/test/scala/SeriesTest.scala +4 -2
- data/tracks/scala/exercises/sieve/src/test/scala/SieveTest.scala +8 -4
- data/tracks/scala/exercises/simple-cipher/src/test/scala/CipherTest.scala +17 -9
- data/tracks/scala/exercises/sublist/src/test/scala/sublist_test.scala +17 -0
- data/tracks/scala/exercises/trinary/src/test/scala/TrinaryTest.scala +14 -7
- data/tracks/scala/exercises/wordy/src/test/scala/WordProblemTest.scala +28 -14
- data/tracks/scala/project/Build.scala +3 -3
- data/tracks/scala/testgen/src/main/scala/BowlingTestGenerator.scala +57 -0
- data/tracks/sml/config.json +41 -6
- data/tracks/sml/exercises/flatten-array/example.sml +6 -0
- data/tracks/sml/exercises/flatten-array/test_flatten_array.sml +10 -0
- data/tracks/sml/exercises/nth-prime/example.sml +46 -0
- data/tracks/sml/exercises/nth-prime/test_nth_prime.sml +14 -0
- data/tracks/sml/exercises/raindrops/example.sml +9 -0
- data/tracks/sml/exercises/raindrops/raindrops.sml +2 -0
- data/tracks/sml/exercises/raindrops/test_raindrops.sml +95 -0
- data/tracks/swift/config.json +269 -328
- data/tracks/swift/exercises/pangram/PangramExample.swift +17 -0
- data/tracks/swift/exercises/pangram/PangramTest.swift +43 -0
- data/tracks/swift/xcodeProject/xSwift.xcodeproj/project.pbxproj +16 -0
- metadata +95 -3
|
@@ -4,56 +4,171 @@ open NUnit.Framework
|
|
|
4
4
|
|
|
5
5
|
open Bowling
|
|
6
6
|
|
|
7
|
-
let rollMany pins
|
|
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 ``
|
|
20
|
-
let
|
|
21
|
-
|
|
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 ``
|
|
26
|
-
let
|
|
27
|
-
|
|
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 ``
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 ``
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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 ``
|
|
55
|
-
let
|
|
56
|
-
|
|
57
|
-
Assert.That(score
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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)
|
data/tracks/go/config.json
CHANGED
|
@@ -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,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 =
|
|
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,
|
|
80
|
-
t.Fatalf("Verses(1,
|
|
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
|
|