Tuesday, January 19, 2016

Simulating the "Can't Stop" Dice Game Using Scala

The board game "Can't Stop" has captivated my family since I was a kid. Over the past year, we've been playing a lot. But I often find myself on the losing end to my fiancee, who is a very tough board gamer.

At its core, "Can't Stop" is a probability game. As such, I'm sure a precise probabilistic analysis of the best playing approach is possible. But coming up with a basic strategy is a problem best solved with simulation.

So I wrote some Scala code to do some Monte-Carlo-style simulations to answer the following question: For each triple of numbers on the "Can't Stop" board, what is the average number of times I can roll the dice without losing my turn?

The answer for the ever-popular 6-7-8 combination over 10,000 trials comes out to around 11.5 rolls. On the other hand, the most difficult possible combinations of 2-3-12 and 2-11-12 only allow you to roll about 0.75 times. (In other words, if you find yourself on those numbers, you should most likely quit rolling immediately.)

Although I think this could be a step in the direction of writing a decent AI for the game, as it stands, it's not very usable. When I tried playing some real human games rolling the "recommended" number of times, I lost consistently and badly. That's because in a real game, going out on half your rolls is far too frequent. Perhaps if I re-ran the simulation looking for, say, an 80% success rate instead of 50% (at least for the "easier" combinations), I could come up with a playable table of roll counts.

The implementation is quite straightforward, largely thanks to Scala and List's combinations() method. Iterating over all of the triples from 2 to 12 can be set up in a for loop like this:

val numbers = for (i <- 2 to 12) yield i
for (markers <- numbers.combinations(3)) {
  // ...
}

Clean and simple.

No comments: