switch to iterative style rather than recursive

This commit is contained in:
Joseph Montanaro 2021-03-20 00:45:38 -07:00
parent c11e4e4696
commit 6e1a1c8c96
2 changed files with 62 additions and 50 deletions

39
combinators.nim Normal file
View File

@ -0,0 +1,39 @@
import algorithm, sequtils
iterator allPermutations*[T](x: seq[T]): seq[T] =
# returns all permutations of a given seq. Order is wonky but we don't care.
var workingCopy = x
yield workingCopy
while workingCopy.nextPermutation: # this mutates workingCopy
yield workingCopy
workingCopy = x
while workingCopy.prevPermutation:
yield workingCopy
iterator allDigits*(lo, hi, size: int): seq[int] =
# we use uninitialized since we will initialize it below, but not necessarily with 0s
var digits = newSeqUninitialized[int](size)
for i in 0 .. digits.high:
digits[i] = lo
var complete = false
while not complete:
yield digits
for i in countdown(digits.high, 0):
if digits[i] < hi:
inc digits[i]
break
elif i == 0: # since this is the last digit to be incremented, we must be done
complete = true
else:
digits[i] = lo
iterator possibleFutures*[C](dice: seq[C]): seq[tuple[color: C, value: int]] =
# iterate over all possible sequences of die rolls. Each outcome
# is returned as a 5-sequence of (color, number) tuples.
for perm in dice.allPermutations:
for d in allDigits(1, 3, dice.len):
yield zip(perm, d)

View File

@ -1,4 +1,5 @@
import algorithm, math, options, tables, sequtils, sugar
import math, options, tables, sequtils, sets, sugar
import combinators
type
@ -13,7 +14,7 @@ type
camels: seq[Color]
tile: Option[Tile]
Die = tuple[color: Color, value: range[1..3]]
Die = tuple[color: Color, value: int]
ScoreSet = array[Color, int]
@ -80,52 +81,24 @@ proc advance(b: var Board, die: Die) =
break # breaking the outer loop here, not the inner - but only conditionally! gah!
iterator allPermutations[T](x: seq[T]): seq[T] =
# returns all permutations of a given seq. Order is wonky but we don't care.
var workingCopy = x
yield workingCopy
while workingCopy.nextPermutation: # this mutates workingCopy
yield workingCopy
workingCopy = x
while workingCopy.prevPermutation:
yield workingCopy
type CamelPositions = seq[tuple[square: int, camels: seq[Color]]]
proc update(scores: var ScoreSet, toAdd: ScoreSet) =
for i, s in toAdd:
scores[i] += s
proc simOrdering(b: Board, ordering: seq[Color]): ScoreSet =
for color in ordering:
for roll in 1..3:
let d = (color, range[1..3](roll))
let nextBoardState = b.dup(advance(d)) # make a copy instead of mutating
# only continue recursing if this is not the last die AND the game is not over
if ordering.len > 1 and not nextBoardState.gameOver:
let nextResult = simOrdering(nextBoardState, ordering[1 .. ^1])
result.update(nextResult)
else:
inc result[nextBoardState.leader]
iterator possibleFutures(dice: seq[Color]): seq[Die]
# iterate over all possible sequences of die rolls. Each outcome
# is returned as a 5-sequence of (color, number) tuples.
yield (cRed, range[1..3]3)
proc sumLegResults(b: Board, diceRemaining: seq[Color]): ScoreSet =
var count = 0
for perm in diceRemaining.allPermutations:
let permResult = simOrdering(b, perm)
result.update(permResult)
let total = result.sum
stdout.write("simulated: " & $total & "\r")
stdout.flushFile
count += 1
echo ""
echo count
proc simulateLeg(b: Board, diceRemaining: seq[Color]): ScoreSet =
var endStates: HashSet[Board]
for future in possibleFutures(diceRemaining):
var prediction = b # make a copy
for dieRoll in future:
prediction.advance(dieRoll)
inc result[prediction.leader]
# deduplicate results
endStates.incl(prediction) # need to add hash implementation for Board
#[
var positions: CamelPositions
for i in prediction.camels.min .. prediction.camels.max:
positions.add((i, prediction.squares[i].camels))
endStates.incl(positions)
]#
echo "Distinct end states: ", endStates.len
var b: Board
@ -134,10 +107,10 @@ b.display(1, 5)
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 2})
b.display(1, 5)
b.advance((cRed, range[1..3](2)))
b.advance((cRed, 2))
b.display(1, 5)
let r = b.sumLegResults(@[cRed, cGreen, cBlue, cYellow, cPurple])
let r = b.simulateLeg(@[cRed, cGreen, cBlue, cYellow, cPurple])
let total = r.sum
for i, c in r:
echo Color(i), ": ", (c / total).round(4) * 100, "% (", c, " / ", total, ")"
echo Color(i), ": ", (100 * c / total).round(2), "% (", c, " / ", total, ")"