One of my peers at work, and a great source of Scala tips, Dave Briccetti asks potential new hires to solve a fairly simple dice throwing problem. He's written a blog post describing the problem. I figured it was time to give it a try.
My first attempt was fairly complicated, but worked:

My first attempt was fairly complicated, but worked:
package scalaplayground.diceSince it was too complex, I went back to the drawing board and eventually came up with a very much simplified and single purpose solution that I'm pretty proud of. Using "yield" (which I tend to forget about) removed the need for that ugly MutableList stuff, the subtype for six sided die was overkill, and a little filtering and mapping simplified the output quite a bit. Here's what I came up with:
import scala.collection.mutable.MutableList
import scala.util.Random
object DiceRoller {
def main(args: Array[String]): Unit = {
if (args.length != 1) exit(255)
println("Rolling 2 dice " + args(0) + " times.")
val resultList = new MutableList[Int]()
for (i <- 0 to args(0).toInt) {
resultList += SixSidedDie.roll(2)
}
println(new DiceHistogram[SixSidedDie](SixSidedDie,2, resultList).toString)
}
abstract class Dice {
val sides = 0
def roll: Int = {
val r = new Random
r.nextInt(sides) + 1
}
def roll(numTimes:Int):Int = {
if (numTimes == 0) {
return 0
} else {
return roll + roll(numTimes-1)
}
}
}
class SixSidedDie extends Dice{}
object SixSidedDie extends SixSidedDie {
override val sides = 6
}
class DiceHistogram[D <: Dice](dice:D, numDice:Int, rollResults:MutableList[Int]) {
val min = numDice
val max = dice.sides * numDice
override def toString():String = {
var str = ""
for (i <- min to max) {
str += i + ": "
rollResults.filter(r => r == i).foreach(x => str += "x")
str += "\n"
}
str
}
}
}
package scalaplayground.diceI also wanted to tackle a the more general purpose problem for all of the Dungeons and Dragons players out there. This version allows you to specify how many dice to roll and how many sides the dice have.
import scala.util.Random
object TwoSixSidedDiceRoller {
def main(args: Array[String]): Unit = {
if (args.length != 1) {
System.err.println("Usage: DiceRoller numRolls")
} else {
val r = new Random
val rolls = for (i <- 1 to args(0).toInt) yield r.nextInt(6) + r.nextInt(6) + 2
for (i <- 2 to 12) {println(i + ": " + rolls.filter(r => r == i).map(x => "x").mkString)}
}
}
}
package scalaplayground.diceThough this problem seems simple, I learned a lot about Scala working on the solution. I continue to be amazed by how elegant Scala can be. Thanks Dave!
import scala.util.Random
object DiceRoller {
def main(args: Array[String]): Unit = {
if (args.length != 3) {
System.err.println("Usage: DiceRoller2 numDice numRolls numSidesOnDice")
} else {
val numDice = args(0).toInt
val numRolls = args(1).toInt
val numSidesOnDice = args(2).toInt
println("Rolling " + numDice + " " + numSidesOnDice + "-sided dice " + numRolls + " times.")
val d = new Die(numSidesOnDice)
val rolls = for (i <- 0 to numRolls) yield d.roll(numDice)
for (i <- numDice to numSidesOnDice*numDice) {
println(i + ": " + rolls.filter(r => r == i).map(x => "x").mkString)
}
}
}
class Die(sides:Int = 6) {
val r = new Random
def roll:Int = {r.nextInt(sides) + 1}
def roll(numTimes:Int):Int = {
numTimes match {
case 0 => 0
case _ => roll + roll(numTimes-1)
}
}
}
}

No comments:
Post a Comment
Note: Only a member of this blog may post a comment.