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 import fixedseq, game
@ -15,7 +15,7 @@ iterator allPermutations*(x: FixedSeq): FixedSeq =
iterator allDigits*(lo, hi, size: Natural): auto = iterator allDigits*(lo, hi, size: Natural): auto =
if size > 0: # otherwise we get an infinite loop if size > 0: # otherwise we get an infinite loop
var digits: FixedSeq[5, int8, int8] var digits: FixedSeq[5, int, int8]
digits.initFixedSeq digits.initFixedSeq
for i in 0 ..< size: for i in 0 ..< size:
digits.add(lo) digits.add(lo)
@ -41,4 +41,11 @@ iterator possibleFutures*(dice: FixedSeq): auto =
var f = initFixedSeq(5, Die, int8) var f = initFixedSeq(5, Die, int8)
for i in 0 .. dice.high: for i in 0 .. dice.high:
f.add((perm[i], digits[i])) 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 type
@ -16,6 +16,11 @@ proc initFixedSeq*(s: var FixedSeq) =
s.last = -1 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 = proc `[]`*(s: var FixedSeq, i: Natural): var FixedSeq.Contents =
if i > s.last: if i > s.last:
raise newException(IndexDefect, "index " & $i & " is out of bounds.") 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 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) = proc moveSubstack*(src, dst: var FixedSeq; start: Natural) =

View File

@ -9,6 +9,18 @@ type
ColorStack* = FixedSeq[5, Color, int8] 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 = proc `$`*(s: ColorStack): string =
result.add("St@[") result.add("St@[")
for i, color in s: for i, color in s:
@ -38,9 +50,6 @@ type
initialized: bool initialized: bool
const allDice = @[cRed, cGreen, cBlue, cYellow, cPurple]
proc `[]`*[T](b: var Board, idx: T): var Square = proc `[]`*[T](b: var Board, idx: T): var Square =
b.squares[idx] b.squares[idx]
@ -90,6 +99,12 @@ proc setState*(b: var Board;
b.leader = some(leadCamel) 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) = proc resetDice*(b: var Board) =
var d: array[Color, bool] var d: array[Color, bool]
b.diceRolled = d 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 import combinators, game, fixedseq
@ -41,7 +41,8 @@ proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
for o in outcomeStack[^1]: for o in outcomeStack[^1]:
var o = o # make it mutable 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 let projection = o.projectLeg
result.update(projection[0]) result.update(projection[0])
@ -52,11 +53,32 @@ proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
echo "\nDistinct end states: ", outcomeStack.mapIt(it.len).sum 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 var b: Board
b.init b.init
b.display(1, 5) 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.display(1, 5)
# b.advance((cRed, 1)) # b.advance((cRed, 1))
@ -70,7 +92,7 @@ b.display(1, 5)
# echo s # echo s
# echo s[2] # echo s[2]
let r = b.projectOutcomes(2) let r = b.randomGames(10_000_000)
let total = r.sum let total = r.sum
for i, c in r: for i, c in r:
echo Color(i), ": ", (100 * c / total).round(2), "% (", c, " / ", total, ")" echo Color(i), ": ", (100 * c / total).round(2), "% (", c, " / ", total, ")"