fix game state representation

This commit is contained in:
Joseph Montanaro 2021-11-01 15:44:21 -07:00
parent 0ea49d3534
commit 4995a9388b
7 changed files with 72 additions and 23 deletions

View File

@ -1,5 +1,4 @@
--threads: on
--d: release
--d: lto
--opt: speed
--passC: -flto
--passL: -flto

View File

@ -5,8 +5,7 @@ when isMainModule:
let config = parseArgs()
var b: Board
b.init
b.setState(config.state, [])
b.diceRolled = config.diceRolled
b.setState(config.state)
let legScores = b.getLegScores
let gameScores = b.randomGames(1_000_000)

View File

@ -101,12 +101,16 @@ proc delete*(s: var FixedSeq, idx: Natural) =
when not defined(danger):
if idx > s.last:
raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
s.data[idx] = -1
s.data[idx] = -1 # do we even need this?
dec s.last
for i in typeof(s.last)(idx) .. s.last:
swap(s.data[i], s.data[i + 1])
proc clear*(s: var FixedSeq) =
s.last = -1
proc find*(s: FixedSeq, needle: FixedSeq.Contents): FixedSeq.Pointer =
for i, v in s.data:
if v == needle:
@ -127,6 +131,9 @@ proc reverse*(s: var FixedSeq; first, last: Natural) =
proc shuffle*(s: var FixedSeq, r: var Rand) =
r.shuffle(s.data)
proc shuffle*(s: var FixedSeq) =
shuffle(s.data)
proc moveSubstack*(src, dst: var FixedSeq; start: Natural) =
var count: typeof(src.last) = 0 # have to track this separately apparently

View File

@ -53,6 +53,11 @@ type
camels*: ColorStack
tile*: Option[Tile]
GameState* = object
dice*: array[Color, bool]
camels*: FixedSeq[5, tuple[c: Color, p: range[1..16]], int8]
tiles*: FixedSeq[8, tuple[t: Tile, p: range[1..16]], int8] # max 8 players, so max 8 tiles
Board* = object
squares*: array[1..16, Square]
camels*: array[Color, range[1..16]]
@ -62,6 +67,14 @@ type
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
template `[]`*[T](b: var Board, idx: T): var Square =
b.squares[idx]
@ -107,16 +120,38 @@ proc display*(b: Board, start, stop: int) =
echo ""
proc setState*(b: var Board;
camels: openArray[tuple[c: Color, p: int]];
tiles: openArray[tuple[t: Tile, p: int]]) =
for (color, dest) in camels: # note that `camels` is ordered, as this determines stacking
proc setState*(b: var Board; state: GameState) =
for color, pos in b.camels:
if pos > 0:
b[pos].camels.clear()
for (color, dest) in state.camels: # note that `camels` is ordered, as this determines stacking
b[dest].camels.add(color)
b.camels[color] = dest
for (tile, dest) in tiles:
for (tile, dest) in state.tiles:
b[dest].tile = some(tile)
b.diceRolled = state.dice
proc getState*(b: Board): GameState =
result.init
var camelCount = 0
let start = min(b.camels)
for pos in start .. b.squares.high:
let sq = b[pos]
for color in sq.camels:
result.camels.add((c: color, p: pos))
camelCount += 1
if sq.tile.isSome:
result.tiles.add((t: sq.tile.get, p: pos))
if camelCount >= 5:
break
result.dice = b.diceRolled
proc diceRemaining*(b: Board): ColorStack =
result.initFixedSeq

View File

@ -43,11 +43,14 @@ iterator legEndStates(b: Board): Board =
for i, c in b.diceRolled:
if not c: diceRemaining.add(i)
let origState = b.getState
var prediction = b
for future in possibleFutures(diceRemaining):
var prediction = b # make a copy so we can mutate
# var prediction = b # make a copy so we can mutate
for dieRoll in future:
prediction.advance(dieRoll)
yield prediction
prediction.setState(origState)
proc getLegScores*(b: Board): ScoreSet =

View File

@ -39,12 +39,15 @@ proc summarize(tr: TestResults, opname = "operations") =
proc newRandomGame(): Board =
randomize()
var dice: array[5, tuple[c: Color, p: int]]
for i in 0 .. 4:
dice[i] = (Color(i), rand(1..3))
result.init
result.setState(dice, [])
var state = newGameState()
for i in 0 .. 4:
let pos = rand(1..3)
state.camels.add((c: Color(i), p: pos))
state.camels.shuffle()
result.setState(state)
template executionTime(body: untyped): Duration =

17
ui.nim
View File

@ -16,7 +16,7 @@ const help = block:
type
CmdConfig* = object
state*: seq[tuple[c: Color, p: int]]
state*: GameState
interactive*: bool
diceRolled*: array[Color, bool]
@ -34,17 +34,18 @@ proc parseColor(c: char): Color =
of 'P', 'p':
return cPurple
else:
raise newException(ValueError, "Invalid camel color specified.")
raise newException(ValueError, "Invalid camel color specified: " & c)
proc parseArgs*(): CmdConfig =
result.state.init()
for p in os.commandLineParams():
if p == "-h":
echo help
quit 0
elif p == "-i":
result.interactive = true
elif result.state.len < 5:
elif result.state.camels.len < 5:
let splat = p.split(':')
let sq = splat[0]
@ -53,11 +54,14 @@ proc parseArgs*(): CmdConfig =
let colors = splat[1]
for c in colors:
let color = parseColor(c)
result.state.add((color, square))
result.state.camels.add((c: color, p: square))
else:
for c in p:
let color = parseColor(c)
result.diceRolled[color] = true
result.state.dice[color] = true
if result.state.camels.len != 5:
raise newException(ValueError, "Please specify positions for all camels.")
# ==========================
@ -98,7 +102,6 @@ proc showSpaces*(b: Board; start, stop: Natural): string =
proc showPercents*(scores: ScoreSet): string =
var lines: array[5, string]
for color, pct in scores.percents:
let label = align($color, 7) # e.g. " Green"
var bar = repeat(" ", 20)
let percentage = round(pct * 100, 2)
# populate the progress bar
@ -106,5 +109,5 @@ proc showPercents*(scores: ScoreSet): string =
for i in 0 ..< barFill:
bar[i] = '='
lines[int(color)] = fmt"{label}: [{bar}] {percentage}%"
lines[int(color)] = fmt"{color:>7}: [{bar}] {percentage:5.2f}%"
result = lines.join("\n")