2021-03-24 17:54:50 +00:00
|
|
|
import math, options, sequtils, random, sets
|
2021-03-24 07:18:53 +00:00
|
|
|
import combinators, game, fixedseq
|
2021-03-18 22:40:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
type
|
2021-03-22 03:59:45 +00:00
|
|
|
ScoreSet* = array[Color, int]
|
2021-03-18 22:40:09 +00:00
|
|
|
|
2021-03-22 03:59:45 +00:00
|
|
|
LegResults* = tuple[scores: ScoreSet, endStates: HashSet[Board]]
|
2021-03-21 01:04:27 +00:00
|
|
|
|
|
|
|
|
2021-03-22 03:59:45 +00:00
|
|
|
proc update*(scores: var ScoreSet, toAdd: ScoreSet) =
|
2021-03-21 01:04:27 +00:00
|
|
|
for i, s in toAdd:
|
|
|
|
scores[i] += s
|
|
|
|
|
|
|
|
|
2021-03-21 06:56:07 +00:00
|
|
|
proc projectLeg(b: Board): LegResults =
|
2021-03-21 01:04:27 +00:00
|
|
|
var scores: ScoreSet
|
2021-03-20 07:45:38 +00:00
|
|
|
var endStates: HashSet[Board]
|
2021-03-21 06:56:07 +00:00
|
|
|
|
2021-03-24 07:18:53 +00:00
|
|
|
var diceRemaining: ColorStack
|
|
|
|
diceRemaining.initFixedSeq
|
|
|
|
for i, c in b.diceRolled:
|
|
|
|
if not c: diceRemaining.add(i)
|
2021-03-21 06:56:07 +00:00
|
|
|
|
2021-03-20 07:45:38 +00:00
|
|
|
for future in possibleFutures(diceRemaining):
|
|
|
|
var prediction = b # make a copy
|
|
|
|
for dieRoll in future:
|
|
|
|
prediction.advance(dieRoll)
|
2021-03-21 01:04:27 +00:00
|
|
|
inc scores[prediction.leader.get]
|
2021-03-20 07:45:38 +00:00
|
|
|
# deduplicate results
|
2021-03-21 01:04:27 +00:00
|
|
|
endStates.incl(prediction)
|
2021-03-22 03:59:45 +00:00
|
|
|
|
2021-03-21 01:04:27 +00:00
|
|
|
result = (scores, endStates)
|
|
|
|
|
|
|
|
|
2021-03-21 06:56:07 +00:00
|
|
|
proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
|
|
|
|
var outcomeStack = @[ [b].toHashSet ]
|
|
|
|
for depth in 1..maxDepth:
|
2021-03-22 03:59:45 +00:00
|
|
|
echo "simulating ", outcomeStack[^1].len, " possible legs."
|
2021-03-21 06:56:07 +00:00
|
|
|
var endStates: HashSet[Board]
|
2021-03-22 03:59:45 +00:00
|
|
|
|
2021-03-21 06:56:07 +00:00
|
|
|
for o in outcomeStack[^1]:
|
2021-03-22 03:59:45 +00:00
|
|
|
var o = o # make it mutable
|
2021-03-24 17:54:50 +00:00
|
|
|
if outcomeStack.len > 1:
|
|
|
|
o.resetDice # o was describina an end-of-leg state, so dice were exhausted
|
2021-03-22 03:59:45 +00:00
|
|
|
|
2021-03-21 06:56:07 +00:00
|
|
|
let projection = o.projectLeg
|
|
|
|
result.update(projection[0])
|
|
|
|
endStates.incl(projection[1])
|
|
|
|
stdout.write("simulated: " & $result.sum & "\r")
|
2021-03-21 01:04:27 +00:00
|
|
|
|
2021-03-21 06:56:07 +00:00
|
|
|
outcomeStack.add(endStates)
|
|
|
|
echo "\nDistinct end states: ", outcomeStack.mapIt(it.len).sum
|
2021-03-20 00:47:18 +00:00
|
|
|
|
|
|
|
|
2021-03-24 17:54:50 +00:00
|
|
|
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))
|
2021-03-25 01:14:57 +00:00
|
|
|
for i in 1 .. count:
|
2021-03-24 17:54:50 +00:00
|
|
|
let winner = b.randomGame(r)
|
|
|
|
inc result[winner]
|
2021-03-25 01:14:57 +00:00
|
|
|
if i mod 100_000 == 0 or i == count - 1:
|
2021-03-24 17:54:50 +00:00
|
|
|
stdout.write("simulated: " & $i & "\r")
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
2021-03-18 22:40:09 +00:00
|
|
|
var b: Board
|
2021-03-22 03:59:45 +00:00
|
|
|
b.init
|
2021-03-20 00:47:18 +00:00
|
|
|
|
2021-03-25 01:14:57 +00:00
|
|
|
randomize()
|
|
|
|
var r = initRand(rand(int64))
|
2021-03-22 03:59:45 +00:00
|
|
|
|
2021-03-25 01:14:57 +00:00
|
|
|
var rolls: array[5, tuple[c: Color, p: int]]
|
|
|
|
for i, roll in randomFuture(b.diceRemaining, r):
|
|
|
|
rolls[i] = (roll[0], roll[1] + 1)
|
2021-03-24 07:18:53 +00:00
|
|
|
|
2021-03-25 01:14:57 +00:00
|
|
|
b.setState(rolls, @[])
|
|
|
|
b.display(1, 5)
|
2021-03-24 07:18:53 +00:00
|
|
|
|
2021-03-25 01:14:57 +00:00
|
|
|
# block outer:
|
|
|
|
# while true:
|
|
|
|
# for roll in randomFuture(b.diceRemaining, r):
|
|
|
|
# b.advance(roll)
|
|
|
|
# if b.gameOver:
|
|
|
|
# echo "last roll: ", roll
|
|
|
|
# break outer
|
|
|
|
# b.resetDice
|
|
|
|
|
|
|
|
# echo "winner: ", b.leader.get
|
|
|
|
# b.display(min(b.camels) - 1, 16)
|
|
|
|
|
|
|
|
let scores = b.randomGames(10_000_000)
|
|
|
|
let total = scores.sum
|
|
|
|
for i, c in scores:
|
2021-03-20 07:45:38 +00:00
|
|
|
echo Color(i), ": ", (100 * c / total).round(2), "% (", c, " / ", total, ")"
|