switch to iterative style rather than recursive
This commit is contained in:
parent
c11e4e4696
commit
6e1a1c8c96
39
combinators.nim
Normal file
39
combinators.nim
Normal 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)
|
73
main.nim
73
main.nim
@ -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, ")"
|
||||
|
Loading…
x
Reference in New Issue
Block a user