3

Simple novice problem, which I strangely haven't been able to conjure a solution for.

I'm making a simple dice roll simulator so I can compare nontransitive dice, as well as normal ones, however the issue is that if I make two dice with the same number of faces and values on said faces, both dice will roll the same value every time. (that is, each roll produces a different number, but both dice have the same value)

Here's my code:

class Die(values: Int*) {
  private val rand: util.Random = new util.Random(compat.Platform.currentTime)
  private val high = values.size + 1
  private val low  = values(0)
  def roll(): Int  = rand.nextInt(high - low) + low
  def this(vals: Range) = this(vals: _*)

  def rollAndCompareTo(that: Die): Symbol = {
    val a = this.roll()
    val b = that.roll()
    if(a > b) 'GT
    else if (a < b) 'LT
    else 'EQ
  }
}

object Program extends App {
  val d61 = new Die(1 to 6)
  val d62 = new Die(1 to 6)

  for(_ <- 1 to 100)
    println(d61 rollAndCompareTo d62)
}

100% of the time, the program will print nothing but 'EQ, because the two dice, despite being different instances created at different times always roll the same value.

I've also tried to add a delay, so that the seed difference is greater, but that doesn't help either.

What would I do to mend this?

Electric Coffee
  • 11,733
  • 9
  • 70
  • 131

4 Answers4

3

Try leaving out the seed for your instance of Random, or use

new util.Random(System.currentTimeMillis)

The specificity of your seed is important if you're making many calls in a short amount of time.

1

As said in other comments you can use a single rand on in the companion object then you don't have to worry about the resolution of the seed. Consider using a SecureRandom else using that to seed your single regular util.Random to avoid the overhead or possibly blocking behaviour of SecureRandom

object Die {
  // consider using java.security.SecureRandom or using that to seed a util.Random
  private[Die] val rand: util.Random = new util.Random(compat.Platform.currentTime)
}

class Die(values: Int*) {
  private val high = values.size + 1
  private val low = values(0)
  def roll(): Int = Die.rand.nextInt(high - low) + low
  def this(vals: Range) = this(vals: _*)

  def rollAndCompareTo(die: Die): Symbol = {
    val a = this.roll()
    val b = die.roll()
    if (a > b) 'GT
    else if (a < b) 'LT
    else 'EQ
  }
}

object Program extends App {
  val d61 = new Die(1 to 6)
  val d62 = new Die(1 to 6)

  for (_ <- 1 to 100)
    println(d61 rollAndCompareTo d62)
}
simbo1905
  • 6,321
  • 5
  • 58
  • 86
0

You may wish to simply pick fixed seeds that are different. Also, compat.Platform.currentTime() and System.currentTimeMillis() seem to be working fine (2014 Macbook Pro). You could try System.nanoTime() in case the two objects get instantiated within the same millisecond. But really, fixed seeds are better, e.g. for testing.

Also, this looks totally like Java - with mutable state, side effects, etc. If you're considering learning Scala the way it's encouraged to be used by its creators (as functional as possible), check out the book by Paul Chiusano and Rúnar Bjarnason called "Functional Programming in Scala" (Manning Press, early access, http://manning.com/bjarnason/). They have a whole chapter about purely functional state using an example of a random number generator. Or, check out NICTA's example: https://github.com/NICTA/rng

marekinfo
  • 1,434
  • 1
  • 12
  • 12
0

Consider java.security.SecureRandom which proves highly more unpredictable than java.util.Random based in linear congruential generators.

For a more detailed discussion note for instance https://stackoverflow.com/a/11052736/3189923 and http://docs.oracle.com/javase/8/docs/api/java/security/SecureRandom.html.

Community
  • 1
  • 1
elm
  • 20,117
  • 14
  • 67
  • 113