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.
- 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
|
|