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
|
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, ")"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user