From 6e1a1c8c96887e6d2f8b39d2a19d2c52ae0e2cf4 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Sat, 20 Mar 2021 00:45:38 -0700 Subject: [PATCH] switch to iterative style rather than recursive --- combinators.nim | 39 ++++++++++++++++++++++++++ main.nim | 73 ++++++++++++++++--------------------------------- 2 files changed, 62 insertions(+), 50 deletions(-) create mode 100644 combinators.nim diff --git a/combinators.nim b/combinators.nim new file mode 100644 index 0000000..effbbf7 --- /dev/null +++ b/combinators.nim @@ -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) \ No newline at end of file diff --git a/main.nim b/main.nim index 31fb878..326c536 100644 --- a/main.nim +++ b/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, ")"