Last active
May 19, 2017 12:23
-
-
Save miquelbeltran/b26ddef6fe86e95942cb75219755751b to your computer and use it in GitHub Desktop.
Bowling Kata Dart vs Kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library bowling; | |
int score([Iterable<int> list = const []]) => | |
toFrames(list).take(10).fold(0, _sum); | |
int _sum(int sum, int pins) => | |
sum + pins; | |
bool isStrike(Iterable<int> list) => | |
list.first == 10; | |
bool isSpare(Iterable<int> list) => | |
!isStrike(list) && list.take(2).reduce(_sum) == 10; | |
Iterable<int> rollsForFrame(Iterable<int> list) => | |
list.take(isStrike(list) || isSpare(list) ? 3 : 2); | |
Iterable<int> toFrames(Iterable<int> rolls, | |
[Iterable<int> frames = const []]) => | |
rolls.isEmpty | |
? frames | |
: toFrames(remainingRolls(rolls), | |
frames.toList() | |
..add(rollsForFrame(rolls).reduce(_sum))); | |
Iterable<int> remainingRolls(Iterable<int> rolls) => | |
rolls.skip(isStrike(rolls) ? 1 : 2); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun List<Int>.score(): Int = toFrames().take(10).sum() | |
fun List<Int>.isSpare(): Boolean = !isStrike() && take(2).sum() == 10 | |
fun List<Int>.isStrike(): Boolean = firstOrNull() == 10 | |
fun List<Int>.rollsForFrame(): List<Int> = if (isStrike() || isSpare()) take(3) else take(2) | |
fun List<Int>.toFrames(frames: List<Int> = emptyList()): List<Int> = | |
if (isEmpty()) frames else remainingRolls().toFrames(frames + rollsForFrame().sum()) | |
fun List<Int>.remainingRolls(): List<Int> = if (isStrike()) drop(1) else drop(2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library blowling_test; | |
import 'package:Lets_Dart/bowling/bowling.dart'; | |
import 'package:test/test.dart'; | |
main() { | |
test("score is 0 when the player did not knock down any pins", () { | |
expect(score(), equals(0)); | |
}); | |
test("score should be 20 if all rolls knocked down a single pin", () { | |
expect(score(new List.filled(20, 1)), equals(20)); | |
}); | |
test("should identify a strike if the first roll in a frame was 10", () { | |
expect(isStrike([10, 5, 1]), equals(true)); | |
expect(isStrike([9, 1]), equals(false)); | |
}); | |
test("should identify a spare from two rolls adding up to 10, if the first roll was not a 10", () { | |
expect(isSpare([10]), equals(false)); | |
expect(isSpare([10, 0]), equals(false)); | |
expect(isSpare([1, 0]), equals(false)); | |
expect(isSpare([1, 9]), equals(true)); | |
expect(isSpare([0, 10]), equals(true)); | |
}); | |
test("should only score two rolls if a frame is less than ten", () { | |
expect(rollsForFrame([4, 5, 6, 7]), equals([4, 5])); | |
}); | |
test("should add the next roll to the score after a spare", () { | |
expect(rollsForFrame([1, 9, 6, 7]), equals([1, 9, 6])); | |
}); | |
test("should add the next two rolls to the score after a strike", () { | |
expect(rollsForFrame([10, 9, 6, 7]), equals([10, 9, 6])); | |
}); | |
test("should correctly produce frame totals for <10 frames", () { | |
expect(toFrames([3]), equals([3])); | |
expect(toFrames([1, 2, 3, 4, 5]), equals([3, 7, 5])); | |
expect(toFrames([1, 2, 3, 4, 5, 3]), equals([3, 7, 8])); | |
}); | |
test("should correctly product frame totals that include spares", () { | |
expect(toFrames([3, 7]), equals([10])); | |
expect(toFrames([3, 7, 6]), equals([16, 6])); | |
expect(toFrames([3, 7, 6, 4, 2]), equals([16, 12, 2])); | |
}); | |
test("should correctly product frame totals that include strike", () { | |
expect(toFrames([10]), equals([10])); | |
expect(toFrames([10, 3]), equals([13, 3])); | |
expect(toFrames([10, 10, 2]), equals([22, 12, 2])); | |
}); | |
test("should add and extra roll if the tenth frame was a spare", () { | |
expect(score(new List.from(new List.filled(18, 1))..addAll([4, 6])), equals(28)); | |
expect(score(new List.from(new List.filled(18, 1))..addAll([4, 6, 1])), equals(29)); | |
}); | |
test("should be 150 if all rolls knocked down 5 pins (including 1 bonus roll for ending on a spare)", () { | |
expect(score(new List.filled(21, 5)), equals(150)); | |
}); | |
test("should be 300 when bowling all strikes (including 2 bonus rolls for ending with a strike)", () { | |
expect(score(new List.filled(22, 10)), equals(300)); | |
}); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.jupiter.api.Assertions.* | |
import org.junit.jupiter.api.Test | |
internal class BowlingKtTest { | |
@Test | |
fun `score is 0 when the player did not knock down any pins`() { | |
assertEquals(0, Array(20, { 0 }).toList().score()) | |
} | |
@Test | |
fun `score is 0 when the player did not play`() { | |
assertEquals(0, emptyList<Int>().score()) | |
} | |
@Test | |
fun `should identify a strike if the first roll in a frame was 10`() { | |
assertEquals(true, listOf(10, 5, 1).isStrike()) | |
assertEquals(false, listOf(9, 1).isStrike()) | |
} | |
@Test | |
fun `should identify a spare from two rolls adding up to 10, if the first roll was not a 10`() { | |
assertEquals(false, listOf(10).isSpare()) | |
assertEquals(false, listOf(10, 0).isSpare()) | |
assertEquals(false, listOf(1, 0).isSpare()) | |
assertEquals(true, listOf(1, 9).isSpare()) | |
assertEquals(true, listOf(0, 10).isSpare()) | |
} | |
@Test | |
fun `should only score two rolls if a frame is less than ten`() { | |
assertEquals(listOf(4, 5), listOf(4, 5, 6, 7).rollsForFrame()) | |
} | |
@Test | |
fun `should add the next roll to the score after a spare`() { | |
assertEquals(listOf(1, 9, 6), listOf(1, 9, 6, 7).rollsForFrame()) | |
} | |
@Test | |
fun `should add the next two rolls to the score after a strike`() { | |
assertEquals(listOf(10, 9, 6), listOf(10, 9, 6, 7).rollsForFrame()) | |
} | |
@Test | |
fun `should correctly produce frame totals for less 10 frames`() { | |
assertEquals(listOf(3), listOf(3).toFrames()) | |
assertEquals(listOf(3, 7, 5), listOf(1, 2, 3, 4, 5).toFrames()) | |
assertEquals(listOf(3, 7, 8), listOf(1, 2, 3, 4, 5, 3).toFrames()) | |
} | |
@Test | |
fun `should correctly product frame totals that include spares`() { | |
assertEquals(listOf(10), listOf(3, 7).toFrames()) | |
assertEquals(listOf(16, 6), listOf(3, 7, 6).toFrames()) | |
assertEquals(listOf(16, 12, 2), listOf(3, 7, 6, 4, 2).toFrames()) | |
} | |
@Test | |
fun `should correctly product frame totals that include strike`() { | |
assertEquals(listOf(10), listOf(10).toFrames()) | |
assertEquals(listOf(13, 3), listOf(10, 3).toFrames()) | |
assertEquals(listOf(22, 12, 2), listOf(10, 10, 2).toFrames()) | |
} | |
@Test | |
fun `should be 150 if all rolls knocked down 5 pins (including 1 bonus roll for ending on spare)`() { | |
assertEquals(150, Array(21, { 5 }).toList().score()) | |
} | |
@Test | |
fun `should be 300 when bowling all strikes (including 2 bonus rolls for ending with a strike`() { | |
assertEquals(300, Array(22, { 10 }).toList().score()) | |
} | |
} |
A few suggestions for the Dart code:
You can delete these:
library bowling;
and:
library blowling_test;
They aren't needed and don't accomplish anything useful.
I'm not sure why you make the parameter optional here:
int score([Iterable<int> list = const []]) =>
toFrames(list).take(10).fold(0, _sum);
The Kotlin version can only be called on an actual list, so the Dart version may as well make the parameter required too. That would give you:
int score(Iterable<int> list) => toFrames(list).take(10).fold(0, _sum);
In general, the Kotlin code here leans heavily on extension methods. Those are really nice. I hope we can get something analogous in Dart eventually.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I've updated the Dart solution with the suggestions I got on Twitter.
I've also improved a bit the Kotlin solution.