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 type
@ -13,7 +14,7 @@ type
camels: seq[Color] camels: seq[Color]
tile: Option[Tile] tile: Option[Tile]
Die = tuple[color: Color, value: range[1..3]] Die = tuple[color: Color, value: int]
ScoreSet = array[Color, 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! break # breaking the outer loop here, not the inner - but only conditionally! gah!
iterator allPermutations[T](x: seq[T]): seq[T] = type CamelPositions = seq[tuple[square: int, camels: seq[Color]]]
# 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
proc simulateLeg(b: Board, diceRemaining: seq[Color]): ScoreSet =
proc update(scores: var ScoreSet, toAdd: ScoreSet) = var endStates: HashSet[Board]
for i, s in toAdd: for future in possibleFutures(diceRemaining):
scores[i] += s var prediction = b # make a copy
for dieRoll in future:
prediction.advance(dieRoll)
proc simOrdering(b: Board, ordering: seq[Color]): ScoreSet = inc result[prediction.leader]
for color in ordering: # deduplicate results
for roll in 1..3: endStates.incl(prediction) # need to add hash implementation for Board
let d = (color, range[1..3](roll)) #[
let nextBoardState = b.dup(advance(d)) # make a copy instead of mutating var positions: CamelPositions
# only continue recursing if this is not the last die AND the game is not over for i in prediction.camels.min .. prediction.camels.max:
if ordering.len > 1 and not nextBoardState.gameOver: positions.add((i, prediction.squares[i].camels))
let nextResult = simOrdering(nextBoardState, ordering[1 .. ^1]) endStates.incl(positions)
result.update(nextResult) ]#
else: echo "Distinct end states: ", endStates.len
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
var b: Board var b: Board
@ -134,10 +107,10 @@ b.display(1, 5)
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 2}) b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 2})
b.display(1, 5) b.display(1, 5)
b.advance((cRed, range[1..3](2))) b.advance((cRed, 2))
b.display(1, 5) 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 let total = r.sum
for i, c in r: 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, ")"