stochastic modeling for full games

This commit is contained in:
Joseph Montanaro 2021-03-24 10:54:50 -07:00
parent 2256b6d9bf
commit f05ab2cfa2
4 changed files with 62 additions and 12 deletions

View File

@ -1,4 +1,4 @@
import sequtils
import random, sugar
import fixedseq, game
@ -15,7 +15,7 @@ iterator allPermutations*(x: FixedSeq): FixedSeq =
iterator allDigits*(lo, hi, size: Natural): auto =
if size > 0: # otherwise we get an infinite loop
var digits: FixedSeq[5, int8, int8]
var digits: FixedSeq[5, int, int8]
digits.initFixedSeq
for i in 0 ..< size:
digits.add(lo)
@ -41,4 +41,11 @@ iterator possibleFutures*(dice: FixedSeq): auto =
var f = initFixedSeq(5, Die, int8)
for i in 0 .. dice.high:
f.add((perm[i], digits[i]))
yield f
yield f
proc randomFuture*(dice: FixedSeq, r: var Rand): FixedSeq[5, Die, int8] =
result.initFixedSeq
let order = dice.dup(shuffle(r))
for i, color in order:
result.add((color, r.rand(1..3)))

View File

@ -1,4 +1,4 @@
import algorithm
import algorithm, random
type
@ -16,6 +16,11 @@ proc initFixedSeq*(s: var FixedSeq) =
s.last = -1
proc `[]`*(s: FixedSeq, i: Natural): FixedSeq.Contents =
if i > s.last:
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
s.data[i]
proc `[]`*(s: var FixedSeq, i: Natural): var FixedSeq.Contents =
if i > s.last:
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
@ -98,7 +103,8 @@ proc nextPermutation*(s: var FixedSeq): bool = s.data.nextPermutation
proc prevPermutation*(s: var FixedSeq): bool = s.data.prevPermutation
proc maxSize*(s: FixedSeq): FixedSeq.Pointer = s.data.len
proc shuffle*(s: var FixedSeq, r: var Rand) =
r.shuffle(s.data)
proc moveSubstack*(src, dst: var FixedSeq; start: Natural) =

View File

@ -9,6 +9,18 @@ type
ColorStack* = FixedSeq[5, Color, int8]
proc initColorStack: ColorStack =
result.initFixedSeq
proc getAllColors: ColorStack =
var i = 0
for c in Color.low .. Color.high:
result[i] = c
const allColors = getAllColors() # compile-time evaluation
proc `$`*(s: ColorStack): string =
result.add("St@[")
for i, color in s:
@ -38,9 +50,6 @@ type
initialized: bool
const allDice = @[cRed, cGreen, cBlue, cYellow, cPurple]
proc `[]`*[T](b: var Board, idx: T): var Square =
b.squares[idx]
@ -90,6 +99,12 @@ proc setState*(b: var Board;
b.leader = some(leadCamel)
proc diceRemaining*(b: Board): ColorStack =
result.initFixedSeq
for color, isRolled in b.diceRolled:
if not isRolled: result.add(color)
proc resetDice*(b: var Board) =
var d: array[Color, bool]
b.diceRolled = d

View File

@ -1,4 +1,4 @@
import math, options, sequtils, sets, sugar
import math, options, sequtils, random, sets
import combinators, game, fixedseq
@ -41,7 +41,8 @@ proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
for o in outcomeStack[^1]:
var o = o # make it mutable
o.resetDice # o was describina an end-of-leg state, so dice were exhausted
if outcomeStack.len > 1:
o.resetDice # o was describina an end-of-leg state, so dice were exhausted
let projection = o.projectLeg
result.update(projection[0])
@ -52,11 +53,32 @@ proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
echo "\nDistinct end states: ", outcomeStack.mapIt(it.len).sum
proc randomGame(b: Board, r: var Rand): Color =
var projection = b
while true:
for roll in randomFuture(projection.diceRemaining, r):
projection.advance(roll)
if projection.gameOver:
return projection.leader.get
projection.resetDice
proc randomGames(b: Board, count: SomeInteger): ScoreSet =
randomize()
var r = initRand(rand(int64))
for i in 0 ..< count:
let winner = b.randomGame(r)
inc result[winner]
if i mod 100_000 == 0:
stdout.write("simulated: " & $i & "\r")
echo ""
var b: Board
b.init
b.display(1, 5)
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 4}, @[])
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 5}, @[])
b.display(1, 5)
# b.advance((cRed, 1))
@ -70,7 +92,7 @@ b.display(1, 5)
# echo s
# echo s[2]
let r = b.projectOutcomes(2)
let r = b.randomGames(10_000_000)
let total = r.sum
for i, c in r:
echo Color(i), ": ", (100 * c / total).round(2), "% (", c, " / ", total, ")"