track length instead of last index for fixedseq

This commit is contained in:
Joseph Montanaro 2022-01-11 15:26:35 -08:00
parent 63df46eee6
commit 8715e5e354
8 changed files with 64 additions and 112 deletions

View File

@ -60,8 +60,7 @@ iterator allPermutations*(x: FixedSeq): FixedSeq =
iterator allDigits*(lo, hi, size: Natural): auto = iterator allDigits*(lo, hi, size: Natural): auto =
if size > 0: # otherwise we get an infinite loop if size > 0: # otherwise we get an infinite loop
var digits: FixedSeq[5, int, int8] var digits: FixedSeq[5, int]
digits.initFixedSeq
for i in 0 ..< size: for i in 0 ..< size:
digits.add(lo) digits.add(lo)
@ -78,12 +77,12 @@ iterator allDigits*(lo, hi, size: Natural): auto =
digits[i] = lo digits[i] = lo
iterator possibleFutures*(dice: FixedSeq): auto = iterator possibleFutures*(dice: ColorStack): auto =
# iterate over all possible sequences of die rolls. Each outcome # iterate over all possible sequences of die rolls. Each outcome
# is returned as a 5-sequence of (color, number) tuples. # is returned as a 5-sequence of (color, number) tuples.
for perm in dice.allPermutations: for perm in dice.allPermutations:
for digits in allDigits(1, 3, dice.len): for digits in allDigits(1, 3, dice.len):
var f = initFixedSeq(5, Die, int8) var f: FixedSeq[5, Die]
for i in 0 .. dice.high: for i in 0'u8 .. dice.high:
f.add((perm[i], digits[i])) f.add((color: perm[i], value: digits[i]))
yield f yield f

View File

@ -4,7 +4,6 @@ import game, simulation, ui
when isMainModule: when isMainModule:
let config = parseArgs() let config = parseArgs()
var b: Board var b: Board
b.init
b.setState(config.state) b.setState(config.state)
let legScores = b.getLegScores let legScores = b.getLegScores

View File

@ -2,18 +2,9 @@ import random
type type
FixedSeq*[Idx: static int; Contents; Pointer: SomeSignedInt] = object FixedSeq*[Size: static range[0..255], Content] = object
data: array[Idx, Contents] data: array[Size, Content]
last: Pointer len*: uint8
proc initFixedSeq*(size: static Natural; cType: typedesc; pType: typedesc[SomeSignedInt]): auto =
var s: FixedSeq[size, cType, pType]
s.last = -1
result = s
proc initFixedSeq*(s: var FixedSeq) =
s.last = -1
proc `$`*(s: FixedSeq): string = proc `$`*(s: FixedSeq): string =
@ -25,55 +16,51 @@ proc `$`*(s: FixedSeq): string =
result.add("]") result.add("]")
proc `[]`*(s: FixedSeq, i: Natural): FixedSeq.Contents = proc `[]`*(s: FixedSeq, idx: Natural): FixedSeq.Content =
when not defined(danger): when not defined(danger):
if i > s.last: if idx.uint8 >= s.len:
raise newException(IndexDefect, "index " & $i & " is out of bounds.") raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
s.data[i] s.data[idx]
proc `[]`*(s: var FixedSeq, i: Natural): var FixedSeq.Contents = proc `[]`*(s: var FixedSeq, idx: Natural): var FixedSeq.Content =
when not defined(danger): when not defined(danger):
if i > s.last: if idx.uint8 >= s.len:
raise newException(IndexDefect, "index " & $i & " is out of bounds.") raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
s.data[i] s.data[idx]
proc `[]`*(s: FixedSeq, i: BackwardsIndex): auto = proc `[]`*(s: FixedSeq, idx: BackwardsIndex): auto =
when not defined(danger): when not defined(danger):
if s.last == -1: if s.len == 0:
raise newException(IndexDefect, "index out of bounds, the container is empty.") # matching stdlib again raise newException(IndexDefect, "index out of bounds, the container is empty.") # matching stdlib again
s.data[s.last - typeof(s.last)(i) + 1] s.data[s.len - idx.uint8]
proc `[]=`*(s: var FixedSeq, i: Natural, v: FixedSeq.Contents) = proc `[]=`*(s: var FixedSeq, idx: Natural, v: FixedSeq.Content) =
when not defined(danger): when not defined(danger):
if i > s.last: if idx.uint8 >= s.len:
raise newException(IndexDefect, "index " & $i & " is out of bounds.") raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
s.data[i] = v s.data[idx] = v
proc high*(s: FixedSeq): auto = proc high*(s: FixedSeq): auto =
result = s.last result = s.len - 1
proc low*(s: FixedSeq): auto = proc low*(s: FixedSeq): auto =
result = case s.last result = case s.len
of -1: 0 # a bit weird but it's how the stdlib seq works of 0: 0 # a bit weird but it's how the stdlib seq works
else: s.last else: s.len - 1
proc len*(s: FixedSeq): auto =
result = s.last + 1
iterator items*(s: FixedSeq): auto = iterator items*(s: FixedSeq): auto =
for i in 0 .. s.last: for i in 0'u8 ..< s.len:
yield s.data[i] yield s.data[i]
iterator asInt*(s: FixedSeq): int8 = iterator asInt*(s: FixedSeq): int8 =
for i in 0 .. s.last: for i in 0'u8 ..< s.len:
yield int8(s.data[i]) # now we do have to convert yield int8(s.data[i]) # now we do have to convert
@ -84,34 +71,32 @@ iterator pairs*(s: FixedSeq): auto =
inc count inc count
proc add*(s: var FixedSeq, v: FixedSeq.Contents) = proc add*(s: var FixedSeq, v: FixedSeq.Content) =
let i = s.last + 1 s.data[s.len] = v # will raise exception if out of bounds
s.data[i] = v # will raise exception if out of bounds inc s.len
s.last = i
proc insert*(s: var FixedSeq, v: FixedSeq.Contents, idx: Natural = 0) = proc insert*(s: var FixedSeq, v: FixedSeq.Content, idx: Natural = 0) =
for i in countdown(s.last, typeof(s.last)(idx)): for i in countdown(s.len - 1, idx.uint8):
swap(s.data[i], s.data[i + 1]) # will also raise exception if out of bounds swap(s.data[i], s.data[i + 1]) # will also raise exception if out of bounds
s.data[idx] = v s.data[idx] = v
inc s.last inc s.len
proc delete*(s: var FixedSeq, idx: Natural) = proc delete*(s: var FixedSeq, idx: Natural) =
when not defined(danger): when not defined(danger):
if idx > s.last: if idx.uint8 >= s.len:
raise newException(IndexDefect, "index " & $idx & " is out of bounds.") raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
s.data[idx] = -1 # do we even need this? dec s.len
dec s.last for i in idx.uint8 ..< s.len:
for i in typeof(s.last)(idx) .. s.last:
swap(s.data[i], s.data[i + 1]) swap(s.data[i], s.data[i + 1])
proc clear*(s: var FixedSeq) = proc clear*(s: var FixedSeq) =
s.last = -1 s.len = 0
proc find*(s: FixedSeq, needle: FixedSeq.Contents): FixedSeq.Pointer = proc find*(s: FixedSeq, needle: FixedSeq.Content): int =
for i, v in s.data: for i, v in s.data:
if v == needle: if v == needle:
return i return i
@ -129,30 +114,36 @@ proc reverse*(s: var FixedSeq; first, last: Natural) =
proc shuffle*(s: var FixedSeq, r: var Rand) = proc shuffle*(s: var FixedSeq, r: var Rand) =
when not defined(danger):
if s.len < s.data.len.uint8:
raise newException(IndexDefect, "Cannot shuffle a partially-full FixedSeq")
r.shuffle(s.data) r.shuffle(s.data)
proc shuffle*(s: var FixedSeq) = proc shuffle*(s: var FixedSeq) =
when not defined(danger):
if s.len < s.data.len.uint8:
raise newException(IndexDefect, "Cannot shuffle a partially-full FixedSeq")
shuffle(s.data) shuffle(s.data)
proc moveSubstack*(src, dst: var FixedSeq; start: Natural) = proc moveSubstack*(src, dst: var FixedSeq; start: Natural) =
var count: typeof(src.last) = 0 # have to track this separately apparently var count = 0'u8 # have to track this separately apparently
for idx in start .. src.last: for idx in start ..< src.len:
swap(src.data[idx], dst.data[dst.last + 1 + count]) swap(src.data[idx], dst.data[dst.len + count])
inc count inc count
dst.last += count dst.len += count
src.last -= count src.len -= count
proc moveSubstackPre*(src, dst: var FixedSeq; start: Natural) = proc moveSubstackPre*(src, dst: var FixedSeq; start: Natural) =
let ssLen = typeof(src.last)(src.last - start + 1) # length of substack let ssLen = src.len - start.uint8 # length of substack
for i in countdown(dst.last, 0): for i in countdown(dst.len - 1, 0):
swap(dst.data[i], dst.data[i + ssLen]) swap(dst.data[i], dst.data[i + ssLen])
var count = 0 var count = 0
for i in start .. src.last: for i in start ..< src.len:
swap(src.data[i], dst.data[count]) swap(src.data[i], dst.data[count])
inc count inc count
dst.last += ssLen dst.len += ssLen
src.last -= ssLen src.len -= ssLen

View File

@ -6,20 +6,9 @@ type
Color* = enum Color* = enum
cRed, cGreen, cBlue, cYellow, cPurple cRed, cGreen, cBlue, cYellow, cPurple
ColorStack* = FixedSeq[5, Color, int8] ColorStack* = FixedSeq[5, Color]
proc initColorStack*: ColorStack =
result.initFixedSeq
proc getAllColors: ColorStack =
var i = 0
for c in Color.low .. Color.high:
result[i] = c
const const
allColors* = getAllColors()
colorNames: array[Color, string] = colorNames: array[Color, string] =
["Red", "Green", "Blue", "Yellow", "Purple"] ["Red", "Green", "Blue", "Yellow", "Purple"]
colorAbbrevs: array[Color, char] = ['R', 'G', 'B', 'Y', 'P'] colorAbbrevs: array[Color, char] = ['R', 'G', 'B', 'Y', 'P']
@ -37,7 +26,7 @@ proc `$`*(s: ColorStack): string =
result.add("St@[") result.add("St@[")
for i, color in s: for i, color in s:
result.add($color) result.add($color)
if i < s.high: if i.uint8 < s.high:
result.add(", ") result.add(", ")
result.add("]") result.add("]")
@ -55,8 +44,8 @@ type
GameState* = object GameState* = object
dice*: array[Color, bool] dice*: array[Color, bool]
camels*: FixedSeq[5, tuple[c: Color, p: range[1..16]], int8] camels*: FixedSeq[5, tuple[c: Color, p: range[1..16]]]
tiles*: FixedSeq[8, tuple[t: Tile, p: range[1..16]], int8] # max 8 players, so max 8 tiles tiles*: FixedSeq[8, tuple[t: Tile, p: range[1..16]]] # max 8 players, so max 8 tiles
Board* = object Board* = object
squares*: array[1..16, Square] squares*: array[1..16, Square]
@ -64,15 +53,6 @@ type
diceRolled*: array[Color, bool] diceRolled*: array[Color, bool]
winner*: Option[Color] winner*: Option[Color]
gameOver*: bool gameOver*: bool
initialized: bool
proc init*(state: var GameState) =
state.camels.initFixedSeq
state.tiles.initFixedSeq
proc newGameState*(): GameState =
result.init
# use a template here for better inlining # use a template here for better inlining
@ -98,12 +78,6 @@ proc hash*(b: Board): Hash =
result = !$h result = !$h
proc init*(b: var Board) =
for sq in b.squares.mitems:
sq.camels.initFixedSeq
b.initialized = true
proc leader*(b: Board): Color = proc leader*(b: Board): Color =
let leadSquare = max(b.camels) let leadSquare = max(b.camels)
result = b[leadSquare].camels[^1] result = b[leadSquare].camels[^1]
@ -138,7 +112,6 @@ proc setState*(b: var Board; state: GameState) =
proc getState*(b: Board): GameState = proc getState*(b: Board): GameState =
result.init
var camelCount = 0 var camelCount = 0
let start = min(b.camels) let start = min(b.camels)
for pos in start .. b.squares.high: for pos in start .. b.squares.high:
@ -156,7 +129,6 @@ proc getState*(b: Board): GameState =
proc diceRemaining*(b: Board): ColorStack = proc diceRemaining*(b: Board): ColorStack =
result.initFixedSeq
for color, isRolled in b.diceRolled: for color, isRolled in b.diceRolled:
if not isRolled: result.add(color) if not isRolled: result.add(color)
@ -183,11 +155,11 @@ proc advance*(b: var Board, die: Die) =
endPos += int(t) endPos += int(t)
if t == tBackward: prepend = true if t == tBackward: prepend = true
let stackStart = b[startPos].camels.find(color) let stackStart = cast[uint8](b[startPos].camels.find(color)) # cast is safe here, as long as b.camels is valid
if prepend: if prepend:
b[startPos].camels.moveSubstackPre(b[endPos].camels, stackStart) b[startPos].camels.moveSubstackPre(b[endPos].camels, stackStart)
let stackLen = b[startPos].camels.len - stackStart let stackLen = b[startPos].camels.len - stackStart
for i in 0 ..< stackLen: for i in 0'u8 ..< stackLen:
# we know how many camels we added to the bottom, so set the position for each of those # we know how many camels we added to the bottom, so set the position for each of those
b.camels[b[endPos].camels[i]] = endPos b.camels[b[endPos].camels[i]] = endPos
else: else:

View File

@ -39,7 +39,6 @@ proc percents*(scores: ScoreSet): WinPercents =
iterator legEndStates(b: Board): Board = iterator legEndStates(b: Board): Board =
var diceRemaining: ColorStack var diceRemaining: ColorStack
diceRemaining.initFixedSeq
for i, c in b.diceRolled: for i, c in b.diceRolled:
if not c: diceRemaining.add(i) if not c: diceRemaining.add(i)

View File

@ -32,16 +32,10 @@ proc summarize(tr: TestResults, opname = "operations") =
stdout.flushFile() stdout.flushFile()
# proc getRand(): Rand =
# randomize()
# result = initRand(rand(int64))
proc newRandomGame(): Board = proc newRandomGame(): Board =
randomize() randomize()
result.init
var state = newGameState() var state: GameState
for i in 0 .. 4: for i in 0 .. 4:
let pos = rand(1..3) let pos = rand(1..3)
state.camels.add((c: Color(i), p: pos)) state.camels.add((c: Color(i), p: pos))

View File

@ -1,4 +1,3 @@
--threads: on --threads: on
--d: danger --d: danger
--d: lto --d: lto
--opt: speed

1
ui.nim
View File

@ -38,7 +38,6 @@ proc parseColor(c: char): Color =
proc parseArgs*(): CmdConfig = proc parseArgs*(): CmdConfig =
result.state.init()
for p in os.commandLineParams(): for p in os.commandLineParams():
if p == "-h": if p == "-h":
echo help echo help