fix game state representation
This commit is contained in:
parent
0ea49d3534
commit
4995a9388b
@ -1,5 +1,4 @@
|
|||||||
--threads: on
|
--threads: on
|
||||||
--d: release
|
--d: release
|
||||||
|
--d: lto
|
||||||
--opt: speed
|
--opt: speed
|
||||||
--passC: -flto
|
|
||||||
--passL: -flto
|
|
||||||
|
3
cup.nim
3
cup.nim
@ -5,8 +5,7 @@ when isMainModule:
|
|||||||
let config = parseArgs()
|
let config = parseArgs()
|
||||||
var b: Board
|
var b: Board
|
||||||
b.init
|
b.init
|
||||||
b.setState(config.state, [])
|
b.setState(config.state)
|
||||||
b.diceRolled = config.diceRolled
|
|
||||||
|
|
||||||
let legScores = b.getLegScores
|
let legScores = b.getLegScores
|
||||||
let gameScores = b.randomGames(1_000_000)
|
let gameScores = b.randomGames(1_000_000)
|
||||||
|
@ -101,12 +101,16 @@ proc delete*(s: var FixedSeq, idx: Natural) =
|
|||||||
when not defined(danger):
|
when not defined(danger):
|
||||||
if idx > s.last:
|
if idx > s.last:
|
||||||
raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
|
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
|
dec s.last
|
||||||
for i in typeof(s.last)(idx) .. s.last:
|
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) =
|
||||||
|
s.last = -1
|
||||||
|
|
||||||
|
|
||||||
proc find*(s: FixedSeq, needle: FixedSeq.Contents): FixedSeq.Pointer =
|
proc find*(s: FixedSeq, needle: FixedSeq.Contents): FixedSeq.Pointer =
|
||||||
for i, v in s.data:
|
for i, v in s.data:
|
||||||
if v == needle:
|
if v == needle:
|
||||||
@ -127,6 +131,9 @@ proc reverse*(s: var FixedSeq; first, last: Natural) =
|
|||||||
proc shuffle*(s: var FixedSeq, r: var Rand) =
|
proc shuffle*(s: var FixedSeq, r: var Rand) =
|
||||||
r.shuffle(s.data)
|
r.shuffle(s.data)
|
||||||
|
|
||||||
|
proc shuffle*(s: var FixedSeq) =
|
||||||
|
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: typeof(src.last) = 0 # have to track this separately apparently
|
||||||
|
45
game.nim
45
game.nim
@ -53,6 +53,11 @@ type
|
|||||||
camels*: ColorStack
|
camels*: ColorStack
|
||||||
tile*: Option[Tile]
|
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
|
Board* = object
|
||||||
squares*: array[1..16, Square]
|
squares*: array[1..16, Square]
|
||||||
camels*: array[Color, range[1..16]]
|
camels*: array[Color, range[1..16]]
|
||||||
@ -62,6 +67,14 @@ type
|
|||||||
initialized: 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
|
||||||
template `[]`*[T](b: var Board, idx: T): var Square =
|
template `[]`*[T](b: var Board, idx: T): var Square =
|
||||||
b.squares[idx]
|
b.squares[idx]
|
||||||
@ -107,16 +120,38 @@ proc display*(b: Board, start, stop: int) =
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
|
||||||
proc setState*(b: var Board;
|
proc setState*(b: var Board; state: GameState) =
|
||||||
camels: openArray[tuple[c: Color, p: int]];
|
for color, pos in b.camels:
|
||||||
tiles: openArray[tuple[t: Tile, p: int]]) =
|
if pos > 0:
|
||||||
for (color, dest) in camels: # note that `camels` is ordered, as this determines stacking
|
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[dest].camels.add(color)
|
||||||
b.camels[color] = dest
|
b.camels[color] = dest
|
||||||
|
|
||||||
for (tile, dest) in tiles:
|
for (tile, dest) in state.tiles:
|
||||||
b[dest].tile = some(tile)
|
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 =
|
proc diceRemaining*(b: Board): ColorStack =
|
||||||
result.initFixedSeq
|
result.initFixedSeq
|
||||||
|
@ -43,11 +43,14 @@ iterator legEndStates(b: Board): Board =
|
|||||||
for i, c in b.diceRolled:
|
for i, c in b.diceRolled:
|
||||||
if not c: diceRemaining.add(i)
|
if not c: diceRemaining.add(i)
|
||||||
|
|
||||||
|
let origState = b.getState
|
||||||
|
var prediction = b
|
||||||
for future in possibleFutures(diceRemaining):
|
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:
|
for dieRoll in future:
|
||||||
prediction.advance(dieRoll)
|
prediction.advance(dieRoll)
|
||||||
yield prediction
|
yield prediction
|
||||||
|
prediction.setState(origState)
|
||||||
|
|
||||||
|
|
||||||
proc getLegScores*(b: Board): ScoreSet =
|
proc getLegScores*(b: Board): ScoreSet =
|
||||||
|
13
test.nim
13
test.nim
@ -39,12 +39,15 @@ proc summarize(tr: TestResults, opname = "operations") =
|
|||||||
|
|
||||||
proc newRandomGame(): Board =
|
proc newRandomGame(): Board =
|
||||||
randomize()
|
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.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 =
|
template executionTime(body: untyped): Duration =
|
||||||
|
17
ui.nim
17
ui.nim
@ -16,7 +16,7 @@ const help = block:
|
|||||||
|
|
||||||
type
|
type
|
||||||
CmdConfig* = object
|
CmdConfig* = object
|
||||||
state*: seq[tuple[c: Color, p: int]]
|
state*: GameState
|
||||||
interactive*: bool
|
interactive*: bool
|
||||||
diceRolled*: array[Color, bool]
|
diceRolled*: array[Color, bool]
|
||||||
|
|
||||||
@ -34,17 +34,18 @@ proc parseColor(c: char): Color =
|
|||||||
of 'P', 'p':
|
of 'P', 'p':
|
||||||
return cPurple
|
return cPurple
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Invalid camel color specified.")
|
raise newException(ValueError, "Invalid camel color specified: " & c)
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
quit 0
|
quit 0
|
||||||
elif p == "-i":
|
elif p == "-i":
|
||||||
result.interactive = true
|
result.interactive = true
|
||||||
elif result.state.len < 5:
|
elif result.state.camels.len < 5:
|
||||||
let splat = p.split(':')
|
let splat = p.split(':')
|
||||||
|
|
||||||
let sq = splat[0]
|
let sq = splat[0]
|
||||||
@ -53,11 +54,14 @@ proc parseArgs*(): CmdConfig =
|
|||||||
let colors = splat[1]
|
let colors = splat[1]
|
||||||
for c in colors:
|
for c in colors:
|
||||||
let color = parseColor(c)
|
let color = parseColor(c)
|
||||||
result.state.add((color, square))
|
result.state.camels.add((c: color, p: square))
|
||||||
else:
|
else:
|
||||||
for c in p:
|
for c in p:
|
||||||
let color = parseColor(c)
|
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 =
|
proc showPercents*(scores: ScoreSet): string =
|
||||||
var lines: array[5, string]
|
var lines: array[5, string]
|
||||||
for color, pct in scores.percents:
|
for color, pct in scores.percents:
|
||||||
let label = align($color, 7) # e.g. " Green"
|
|
||||||
var bar = repeat(" ", 20)
|
var bar = repeat(" ", 20)
|
||||||
let percentage = round(pct * 100, 2)
|
let percentage = round(pct * 100, 2)
|
||||||
# populate the progress bar
|
# populate the progress bar
|
||||||
@ -106,5 +109,5 @@ proc showPercents*(scores: ScoreSet): string =
|
|||||||
for i in 0 ..< barFill:
|
for i in 0 ..< barFill:
|
||||||
bar[i] = '='
|
bar[i] = '='
|
||||||
|
|
||||||
lines[int(color)] = fmt"{label}: [{bar}] {percentage}%"
|
lines[int(color)] = fmt"{color:>7}: [{bar}] {percentage:5.2f}%"
|
||||||
result = lines.join("\n")
|
result = lines.join("\n")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user