diff --git a/colors.nim b/colors.nim new file mode 100644 index 0000000..ed7bbb9 --- /dev/null +++ b/colors.nim @@ -0,0 +1,17 @@ +type + Color* = enum + cRed, cGreen, cBlue, cYellow, cPurple + + +const + colorNames: array[Color, string] = + ["Red", "Green", "Blue", "Yellow", "Purple"] + colorAbbrevs: array[Color, char] = ['R', 'G', 'B', 'Y', 'P'] + + +proc `$`*(c: Color): string = + result = colorNames[c] + + +proc abbrev*(c: Color): char = + result = colorAbbrevs[c] diff --git a/config.nims b/config.nims index 824ce34..c3b9fc0 100644 --- a/config.nims +++ b/config.nims @@ -1,5 +1,5 @@ -# --threads: on -# --d: release -# --opt: speed -# --passC: -flto -# --passL: -flto \ No newline at end of file +--threads: on +--d: release +--opt: speed +--passC: -flto +--passL: -flto \ No newline at end of file diff --git a/fixedseq.nim b/fixedseq.nim index 9961f2b..33d77b9 100644 --- a/fixedseq.nim +++ b/fixedseq.nim @@ -25,6 +25,16 @@ proc `$`*(s: FixedSeq): string = result.add("]") +proc `==`*[T1: FixedSeq, T2: FixedSeq](a: T1, b: T2): bool = + # generics are so that we can compare ShiftStack vs regular FixedSeq + if a.len != b.len: + return false + for i in 0 ..< a.len: + if a.data[i] != b.data[i]: + return false + return true + + proc `[]`*(s: FixedSeq, i: Natural): FixedSeq.Contents = if i > s.last: raise newException(IndexDefect, "index " & $i & " is out of bounds.") diff --git a/game.nim b/game.nim index 598f665..edd3f86 100644 --- a/game.nim +++ b/game.nim @@ -1,11 +1,9 @@ import hashes, options -import fixedseq +import fixedseq, colors +export colors type - Color* = enum - cRed, cGreen, cBlue, cYellow, cPurple - ColorStack* = FixedSeq[5, Color, int8] @@ -13,26 +11,6 @@ proc initColorStack*: ColorStack = result.initFixedSeq -proc getAllColors: ColorStack = - var i = 0 - for c in Color.low .. Color.high: - result[i] = c - -const - allColors* = getAllColors() - colorNames: array[Color, string] = - ["Red", "Green", "Blue", "Yellow", "Purple"] - colorAbbrevs: array[Color, char] = ['R', 'G', 'B', 'Y', 'P'] - - -proc `$`*(c: Color): string = - result = colorNames[c] - - -proc abbrev*(c: Color): char = - result = colorAbbrevs[c] - - proc `$`*[T](s: FixedSeq[T, Color, int8]): string = result.add("St@[") for i, color in s: @@ -50,7 +28,7 @@ type tForward = 1 Square* = object - camels*: ColorStack + camels*: ShiftStack tile*: Option[Tile] Board* = object diff --git a/shiftstack.nim b/shiftstack.nim index 7be8b57..8f66d34 100644 --- a/shiftstack.nim +++ b/shiftstack.nim @@ -1,6 +1,6 @@ # optimized bit-shifting versions of the FixedSequence substack operations import bitops, macros -import game +import colors macro show(expr: untyped) = @@ -10,9 +10,8 @@ macro show(expr: untyped) = proc getMasks(): (array[9, uint64], array[9, uint64]) = - # on little-endian architectures, casting an array[8, Color] - # to uint64 effectively reverses it. So we switch these masks - # so that we can refer to them consistently. + # on little-endian architectures, casting an array[8, Color] to uint64 effectively + # reverses it. So we switch these masks so that we can refer to them consistently. let left = [ 0'u64, @@ -42,7 +41,7 @@ proc getMasks(): (array[9, uint64], array[9, uint64]) = result = (right, left) -type ShiftStack = FixedSeq[8, Color, int8] +type ShiftStack* = FixedSeq[8, Color, int8] const (masksLeft, masksRight) = getMasks() @@ -78,11 +77,12 @@ proc moveSubstack*(src, dst: var ShiftStack; start: Natural) = # offset is the length of the destination stack, minus the number of items NOT being moved # number of items not being moved is the same as the start index var substack: array[8, Color] - # if dst.len == start, no shift necessary, otherwise: - if dst.len > start: + if dst.len == start: # no shift necessary in this case + substack = src.data + elif dst.len > start: substack = src.data shr (dst.len - start) elif dst.len < start: - substack = src.data shl (start) + substack = src.data shl (start - dst.len) # next, mask the source data to present only the items being moved # dst.len of 0 corresponds to last mask in masksRight, aka masksRight[^1] substack = substack and masksRight[^(dst.len + 1)] @@ -111,25 +111,16 @@ proc moveSubstackPre*(src, dst: var ShiftStack; start: Natural) = dst.last += ssLen -proc `==`[T1: FixedSeq, T2: FixedSeq](a: T1, b: T2): bool = - if a.len != b.len: - return false - for i in 0 ..< a.len: - if a.data[i] != b.data[i]: - return false - return true - - proc testMove[T1, T2: FixedSeq](a1, a2: var T1; b1, b2: var T2; i: Natural): bool = let (orig_a1, orig_a2) = (a1, a2) let (orig_b1, orig_b2) = (b1, b2) - a1.moveSubstackPre(a2, i) - b1.moveSubstackPre(b2, i) + a1.moveSubstack(a2, i) + b1.moveSubstack(b2, i) if a1 != b1 or a2 != b2: echo "Failed!" show orig_b1 show orig_b2 - echo "<< move ", i, ">>" + echo "<>" show b1 show b2 return false @@ -145,16 +136,22 @@ when isMainModule: var s2: ShiftStack s2.initFixedSeq - # s1.add(cPurple) - # s1.add(cRed) - # s1.add(cYellow) - # s1.add(cBlue) - # s1.add(cGreen) + c1.add(cPurple) + c1.add(cRed) + c1.add(cYellow) + c1.add(cBlue) + c1.add(cGreen) + + s1.add(cPurple) + s1.add(cRed) + s1.add(cYellow) + s1.add(cBlue) + s1.add(cGreen) # show s1 # show s2 - # echo "<>" - # s1.moveSubstack(s2, 4) + # echo "<>" + # s1.moveSubstack(s2, 2) # show s1 # show s2 @@ -163,20 +160,32 @@ when isMainModule: var r = initRand(rand(int64)) var success = true - for n in 1 .. 10000: + for n in 1 .. 1_000_000: + var ranFirst, ranSecond: bool + if c1.len > 0: let i = r.rand(c1.high) + ranFirst = true if not testMove(c1, c2, s1, s2, i): success = false echo "Failed after ", n, " iterations." break + else: + ranFirst = false if c2.len > 0: let j = r.rand(c2.high) + ranSecond = true if not testMove(c2, c1, s2, s1, j): success = false echo "Failed after ", n, " iterations." break + else: + ranSecond = false + + if (not ranFirst) and (not ranSecond): + echo "Ran neither first nor second move." + break if success: echo "Success." diff --git a/test.nim b/test.nim index 09d342e..96c5903 100644 --- a/test.nim +++ b/test.nim @@ -27,7 +27,7 @@ proc testGames(n: SomeInteger = 100): auto = b.display(1, 5) let startTime = cpuTime() - let scores = b.randomGames(n) + let scores = b.randomGames(n, parallel = true) result = cpuTime() - startTime scores.display() @@ -73,23 +73,23 @@ proc testSpread(nTests, nSamples: Natural) = when isMainModule: randomize() - var r = initRand(rand(int64)) - let b = newRandomGame(r) - b.display(1, 5) - echo b.showSpaces(1, 16) + # var r = initRand(rand(int64)) + # let b = newRandomGame(r) + # b.display(1, 5) + # echo b.showSpaces(1, 16) - let scores = b.getLegScores - echo scores.showPercents + # let scores = b.getLegScores + # echo scores.showPercents # 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 "" + 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)