cup/main.nim

111 lines
2.8 KiB
Nim
Raw Normal View History

2021-03-24 17:54:50 +00:00
import math, options, sequtils, random, sets
2021-07-12 22:46:06 +00:00
import combinators, game, fixedseq, ui
2021-03-18 22:40:09 +00:00
type
ScoreSet* = array[Color, int]
2021-03-18 22:40:09 +00:00
ScoreSpread = object
lo: array[Color, float]
hi: array[Color, float]
LegResults* = tuple[scores: ScoreSet, endStates: HashSet[Board]]
2021-03-21 01:04:27 +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-07-12 22:46:06 +00:00
proc display*(scores: ScoreSet) =
let total = scores.sum
for color, score in scores:
echo color, ": ", round(100 * scores[color] / total, 2), '%'
proc projectLeg*(b: Board): LegResults =
2021-03-21 01:04:27 +00:00
var scores: ScoreSet
var endStates: HashSet[Board]
var diceRemaining: ColorStack
diceRemaining.initFixedSeq
for i, c in b.diceRolled:
if not c: diceRemaining.add(i)
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]
# deduplicate results
2021-03-21 01:04:27 +00:00
endStates.incl(prediction)
2021-03-21 01:04:27 +00:00
result = (scores, endStates)
proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
var outcomeStack = @[ [b].toHashSet ]
for depth in 1..maxDepth:
echo "simulating ", outcomeStack[^1].len, " possible legs."
var endStates: HashSet[Board]
for o in outcomeStack[^1]:
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
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
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]
# if i mod 100_000 == 0 or i == count - 1:
# stdout.write("simulating " & count & "random games: " & $i & "\r")
# echo ""
proc randomSpread(b: Board, nTests: SomeInteger, nSamples: SomeInteger): ScoreSpread =
for s in result.lo.mitems:
s = 1
for i in 0 ..< nTests:
let scores = b.randomGames(nSamples)
let total = scores.sum
for color, score in scores:
let pct = score / total
if pct < result.lo[color]:
result.lo[color] = pct
if pct > result.hi[color]:
result.hi[color] = pct
2021-03-24 17:54:50 +00:00
2021-07-12 22:46:06 +00:00
when isMainModule:
let config = parseArgs()
var b: Board
b.init
b.setState(config.state, [])
b.diceRolled = config.diceRolled
b.display(1, 5)
let scores = b.projectLeg()[0]
scores.display