Created
June 30, 2011 08:37
-
-
Save p3t0r/1055868 to your computer and use it in GitHub Desktop.
Scala TDD workshop - Implement a Yahtzee scorer
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
/** | |
* Results of a 1 hour 'bucketline' exercise @ Marktplaats to test-drive the development | |
* of a Yahtzee scorer in Scala. | |
*/ | |
// --- the test --- // | |
import org.scalatest.FlatSpec | |
import org.scalatest.matchers.ShouldMatchers | |
import Scorer._ | |
class ScorerTest extends FlatSpec with ShouldMatchers { | |
"Scorer" should "score small street (sequence of 4) at 30 points" in { | |
smallStreet(List(1,2,3,4,4)) should be(30) | |
smallStreet(List(1,1,1,1,1)) should be(0) | |
smallStreet(List(1,3,4,5,5)) should be(0) | |
smallStreet(List(1,2,4,5,6)) should be(0) | |
// big street | |
smallStreet(List(1,2,3,4,5)) should be(30) | |
List.fill() | |
} | |
it should "score big street at 40 points" in { | |
bigStreet(List(1,2,3,4,5)) should be(40) | |
bigStreet(List(2,3,4,5,6)) should be(40) | |
bigStreet(List(6,3,4,2,5)) should be(40) | |
bigStreet(List(1,3,2,4,6)) should be(0) | |
} | |
it should "correctly score above-the-line values (n-of-a-kind)" in { | |
// three ones above the line | |
aboveTheLine(List(1,1,1,4,5), 1) should be (3) | |
aboveTheLine(List(2,2,2,4,5), 2) should be (6) | |
aboveTheLine(List(2,2,1,4,5), 2) should be (4) | |
aboveTheLine(List(2,5,1,4,5), 2) should be (2) | |
} | |
it should "score three of a kind " in { | |
threeOfAKind(List(1,1,1,2,3)) should be (8) | |
threeOfAKind(List(1,1,1,1,3)) should be (7) | |
threeOfAKind(List(1,1,1,1,1)) should be (5) | |
threeOfAKind(List(1,1,2,2,3)) should be (0) | |
} | |
it should "give highest score for a given roll" in { | |
pending | |
} | |
it should "score chance (garbage) with the sum of all dices" in { | |
chance(List(1,1,1,1,1)) should be(5) | |
chance(List(1,2,3,4,5)) should be(15) | |
} | |
it should "score full house" in { | |
fullHouse(List(2,2,4,4,4)) should be(25) | |
fullHouse(List(1,2,4,4,4)) should be(0) | |
fullHouse(List(2,2,2,2,4)) should be(0) | |
} | |
} | |
// --- the implementation --- // | |
/** | |
* A collection of functions for calculating Yathzee scores. | |
*/ | |
object Scorer { | |
def chance(dices:Seq[Int]) = dices.reduceLeft(_+_) | |
def fullHouse(dices:Seq[Int]) = if (groupWithCounts(dices).filter(e => e._2 == 2 || e._2 == 3).size == 2) 25 else 0 | |
def groupWithCounts(dices:Seq[Int]) = dices.groupBy(v => v).map(e => (e._1, e._2.size)) | |
def street(dices:Seq[Int], size:Int, points:Int) = { | |
val sortedDices = dices.sorted | |
val containsSlice = sortedDices.zip(sortedDices.tail) | |
.map(t => t._1 - t._2) | |
.containsSlice(List.fill(size)(-1)) | |
if (containsSlice) points else 0 | |
} | |
def threeOfAKind(dices:Seq[Int]) = { | |
if (groupWithCounts(dices).exists(_._2 >= 3)) dices.sum else 0 | |
} | |
def smallStreet(dices:Seq[Int]) = street(dices, 3, 30) | |
def bigStreet(dices:Seq[Int]) = street(dices, 4, 40) | |
def aboveTheLine(dices:Seq[Int], v:Int) = dices.filter(_ == v).sum | |
} |
Similarly, you can write
map(t => t._1 - t._2)
as
map(case (d1, d2) => d1 - d2)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also notice that one of the 'uglier' expressions is a perfect fit for inline pattern matching
def fullHouse(dices:Seq[Int]) = if (groupWithCounts(dices).filter(e => e._2 == 2 || e._2 == 3).size == 2) 25 else 0
Could be written as:
def fullHouse(dices:Seq[Int]) = if (groupWithCounts(dices).filter{case (eyes,size) => size == 2 || size == 3).size == 2} 25 else 0