diff --git a/faststack.nim b/faststack.nim index 12e2f04..ee253a9 100644 --- a/faststack.nim +++ b/faststack.nim @@ -29,7 +29,7 @@ const template offset(s: ColorStack, idx: Natural): uint8 = # Compute the bit offset for a given index. # Dependent on the stack's length. - (s.len - 1 - cast[uint8](idx)) * 3 + (s.len - 1 - idx.uint8) * 3 # items are stored from left to right but right-aligned, so we # need to shift right to access anything other than the last @@ -37,7 +37,7 @@ template offset(s: ColorStack, idx: Natural): uint8 = template offset(s: ColorStack, idx: BackwardsIndex): uint8 = # for backwards index, we are still shifting right but # the lower the index the less we have to shift - (cast[uint8](idx) - 1) * 3 + (idx.uint8 - 1) * 3 proc add*(s: var ColorStack, c: Color) = @@ -57,21 +57,21 @@ proc low*(s: ColorStack): uint8 = 0 # just... always 0, I guess proc `[]`*(s: ColorStack, i: uint8 | BackwardsIndex): Color = # shift, then mask everything but the three rightmost bits - result = cast[Color]( + result = Color( (s.data shr s.offset(i)) and masks[1] ) proc `[]=`*(s: var ColorStack, i: uint8 | BackwardsIndex, c: Color) = let offset = s.offset(i) - s.data = (s.data and bitnot(masks[1] shl offset)) or (cast[uint16](c) shl offset) + s.data = (s.data and bitnot(masks[1] shl offset)) or (c.uint16 shl offset) iterator items*(s: ColorStack): Color = # s.len is unsigned so it will wrap around if we do s.len - 1 in that case if s.len != 0: for i in countdown(s.len - 1, 0'u8): - yield cast[Color]((s.data shr (i * 3)) and masks[1]) + yield Color((s.data shr (i * 3)) and masks[1]) iterator pairs*(s: ColorStack): (uint8, Color) = @@ -81,13 +81,16 @@ iterator pairs*(s: ColorStack): (uint8, Color) = inc count -proc find*(s: ColorStack, needle: Color): uint8 = +proc find*(s: ColorStack, needle: Color): int8 = for i in 0'u8 .. s.high: if s[i] == needle: - return i + return i.int8 + return -1 proc moveSubstack*(src, dst: var ColorStack, startIdx: uint8) = + if startIdx >= src.len: + raise newException(IndexDefect, "index " & $startIdx & " is out of bounds.") # Moves a sub-stack from the top of src to the top of dst # shift the dst stack by the length of the substack to make room let nToMove = src.len - startIdx @@ -95,9 +98,7 @@ proc moveSubstack*(src, dst: var ColorStack, startIdx: uint8) = dst.data = dst.data shl shift # then we mask the source data to present only the items # being moved, and OR that with the shifted dst data - if src.len > 5: - echo src.len - dst.data = dst.data or (src.data and masks[src.len]) + dst.data = dst.data or (src.data and masks[nToMove]) dst.len += nToMove # then we shift the source to get rid of the moved items src.data = src.data shr shift @@ -105,13 +106,16 @@ proc moveSubstack*(src, dst: var ColorStack, startIdx: uint8) = proc moveSubstackPre*(src, dst: var ColorStack, startIdx: uint8) = + if startIdx >= src.len: + raise newException(IndexDefect, "index " & $startIdx & " is out of bounds.") # Moves a sub-stack from the top of src to the bottom of dst let nToMove = src.len - startIdx - # shift src stack to position substack above its destination, + # shift src to position the substack above its destination, # get rid of everything to the left of the substack, # and OR that with the existing dst data - dst.data = dst.data or ( (src.data shl (dst.len * 3)) and masks[src.len]) - dst.len += nToMove + let newLen = dst.len + nToMove + dst.data = dst.data or ( (src.data shl (dst.len * 3)) and masks[newLen] ) + dst.len = newLen # get rid of the substack we just moved src.data = src.data shr (nToMove * 3) src.len -= nToMove @@ -142,7 +146,7 @@ proc swap*(s: var ColorStack, i1, i2: uint8) = proc shuffle*(r: var Rand, s: var ColorStack) = # Fisher-Yates shuffle for i in countdown(s.high, 1'u8): - let j = cast[uint8](r.rand(i)) + let j = r.rand(i).uint8 if j != i: s.swap(i, j) @@ -170,17 +174,31 @@ proc `$`*(s: ColorStack): string = result.add("]") -var one: ColorStack -one.add(cRed) -one.add(cGreen) -one.add(cBlue) -one.add(cYellow) -one.add(cPurple) +proc check(s: ColorStack) = + # ensure length is accurate + var d = s.data + for i in 0'u8 .. 4'u8: + if (d and masks[1]) > 4: + raise newException(RangeDefect, "Value out of range.") + if d > 0 and i >= s.len: + raise newException(RangeDefect, "Invalid length.") + else: + d = d shr 3 -var two: ColorStack -one.moveSubstack(two, 2) -two.moveSubstackPre(one, 1) +when isMainModule: + var one: ColorStack + one.add(cRed) + one.add(cGreen) + one.add(cBlue) + one.add(cYellow) + one.add(cPurple) -echo one, " ", one.len -echo two, " ", two.len + var two: ColorStack + + one.moveSubstack(two, 2) + + + echo one, " ", one.len + echo two, " ", two.len + echo two.find(cRed) diff --git a/game.nim b/game.nim index dcb51ad..cd56a4f 100644 --- a/game.nim +++ b/game.nim @@ -141,7 +141,7 @@ proc advance*(b: var Board, die: Die) = endPos += int(t) if t == tBackward: prepend = true - let stackStart = b[startPos].camels.find(color) + let stackStart = b[startPos].camels.find(color).uint8 if prepend: b[startPos].camels.moveSubstackPre(b[endPos].camels, stackStart) let stackLen = b[startPos].camels.len - stackStart @@ -159,4 +159,4 @@ proc advance*(b: var Board, die: Die) = if endPos >= b.camels[b.leader.get]: b.leader = some(b[endPos].camels[^1]) - b.diceRolled[color] = true \ No newline at end of file + b.diceRolled[color] = true diff --git a/simulation.nim b/simulation.nim index 51b78e8..a7bb49b 100644 --- a/simulation.nim +++ b/simulation.nim @@ -1,5 +1,5 @@ import cpuinfo, math, options, random, tables -import combinators, game, fixedseq +import combinators, game, faststack, fixedseq type @@ -59,6 +59,39 @@ proc getLegScores*(b: Board): ScoreSet = # Full-game simulations # ===================== +# get rid of this later +import strutils +proc showSpaces*(b: Board; start, stop: Natural): string = + let numSpaces = stop - start + 1 + let width = 4 * numSpaces - 1 + var lines: array[7, string] + # start by building up an empty board + for i in 0 .. 6: # gotta initialize the strings + lines[i] = newString(width) + for c in lines[i].mitems: + c = ' ' + # fill in the dividers + lines[5] = repeat("=== ", numSpaces - 1) + lines[5].add("===") + + # now populate the board + for sp in 0 ..< numSpaces: + # fill in the square numbers + let squareNum = sp + start + let cellMid = 4 * sp + 1 + for i, chr in $squareNum: + lines[6][cellMid + i] = chr + + # fill in the camel stacks + for i, color in b.squares[squareNum].camels: + let lineNum = 4 - i # lines go to 6, but bottom 2 are reserved + let repr = '|' & color.abbrev & '|' + for j, chr in repr: + lines[lineNum][cellMid - 1 + j] = chr + + result = lines.join("\n") +# get rid of this later + proc randomGame*(b: Board, r: var Rand): Color = var projection = b while true: diff --git a/test.nim b/test.nim index cceb761..9d47963 100644 --- a/test.nim +++ b/test.nim @@ -43,13 +43,13 @@ proc newRandomGame(r: var Rand): Board = result.setState(dice, []) -proc games(nTests, nSamples: SomeInteger): TestResults = +proc games(nTests, nSamples: SomeInteger, parallel = true): TestResults = var r = getRand() var scores: ScoreSet for i in 1 .. nTests: let b = newRandomGame(r) let dur = executionTime: - let s = b.randomGames(nSamples) + let s = b.randomGames(nSamples, parallel = parallel) result.ops += s.sum() result.time += dur @@ -94,17 +94,4 @@ proc testSpread(nTests, nSamples: Natural) = when isMainModule: - games(10, 1_000_000).summarize() - # let start_states = 2_000 - # let executionTime = testLegs(start_states) - # echo "Execution time: ", executionTime - # echo "Leg simulations per second: ", float(start_states * 29_160) / executionTime - - # for i in 1 .. 1: - # let num_games = 100_000_005 - # let executionTime = testGames(num_games) - # echo "Execution time: ", executionTime - # echo "Full-game simulations per second: ", float(num_games) / executionTime - # echo "" - - # testSpread(100, 1_000_000) + games(10, 10_000_000).summarize()