add (very) basic CLI
This commit is contained in:
parent
b4c31a7a68
commit
6f694f99ed
@ -1,7 +1,52 @@
|
|||||||
import random, sugar
|
import algorithm, random, sugar
|
||||||
import fixedseq, game
|
import fixedseq, game
|
||||||
|
|
||||||
|
|
||||||
|
proc nextPermutation(x: var FixedSeq): bool =
|
||||||
|
# copied shamelessly from std/algorithm.nim
|
||||||
|
if x.len < 2:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var i = x.high
|
||||||
|
while i > 0 and x[i - 1] >= x[i]:
|
||||||
|
dec i
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var j = x.high
|
||||||
|
while j >= i and x[j] <= x[i - 1]:
|
||||||
|
dec j
|
||||||
|
|
||||||
|
swap x[j], x[i - 1]
|
||||||
|
x.reverse(i, x.high)
|
||||||
|
|
||||||
|
result = true
|
||||||
|
|
||||||
|
|
||||||
|
proc prevPermutation(x: var FixedSeq): bool =
|
||||||
|
# copied shamelessly from std/algorithm.nim
|
||||||
|
if x.len < 2:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var i = x.high
|
||||||
|
while i > 0 and x[i - 1] <= x[i]:
|
||||||
|
dec i
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
return false
|
||||||
|
|
||||||
|
x.reverse(i, x.high)
|
||||||
|
|
||||||
|
var j = x.high
|
||||||
|
while j >= i and x[j - 1] < x[i - 1]:
|
||||||
|
dec j
|
||||||
|
|
||||||
|
swap x[i - 1], x[j]
|
||||||
|
|
||||||
|
result = true
|
||||||
|
|
||||||
|
|
||||||
iterator allPermutations*(x: FixedSeq): FixedSeq =
|
iterator allPermutations*(x: FixedSeq): FixedSeq =
|
||||||
# returns all permutations of a given seq. Order is wonky but we don't care.
|
# returns all permutations of a given seq. Order is wonky but we don't care.
|
||||||
var workingCopy = x
|
var workingCopy = x
|
||||||
|
33
fixedseq.nim
33
fixedseq.nim
@ -1,4 +1,4 @@
|
|||||||
import algorithm, random
|
import random
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -16,11 +16,21 @@ proc initFixedSeq*(s: var FixedSeq) =
|
|||||||
s.last = -1
|
s.last = -1
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(s: FixedSeq): string =
|
||||||
|
result.add("FixedSeq[")
|
||||||
|
for i, item in s:
|
||||||
|
if i != 0:
|
||||||
|
result.add(", ")
|
||||||
|
result.add($item)
|
||||||
|
result.add("]")
|
||||||
|
|
||||||
|
|
||||||
proc `[]`*(s: FixedSeq, i: Natural): FixedSeq.Contents =
|
proc `[]`*(s: FixedSeq, i: Natural): FixedSeq.Contents =
|
||||||
if i > s.last:
|
if i > s.last:
|
||||||
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
|
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
|
||||||
s.data[i]
|
s.data[i]
|
||||||
|
|
||||||
|
|
||||||
proc `[]`*(s: var FixedSeq, i: Natural): var FixedSeq.Contents =
|
proc `[]`*(s: var FixedSeq, i: Natural): var FixedSeq.Contents =
|
||||||
if i > s.last:
|
if i > s.last:
|
||||||
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
|
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
|
||||||
@ -99,9 +109,15 @@ proc find*(s: FixedSeq, needle: FixedSeq.Contents): FixedSeq.Pointer =
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
proc nextPermutation*(s: var FixedSeq): bool = s.data.nextPermutation
|
proc reverse*(s: var FixedSeq; first, last: Natural) =
|
||||||
|
# copied shamelessly from std/algorithm.nim
|
||||||
|
var x = first
|
||||||
|
var y = last
|
||||||
|
while x < y:
|
||||||
|
swap(s[x], s[y])
|
||||||
|
inc x
|
||||||
|
dec y
|
||||||
|
|
||||||
proc prevPermutation*(s: var FixedSeq): bool = s.data.prevPermutation
|
|
||||||
|
|
||||||
proc shuffle*(s: var FixedSeq, r: var Rand) =
|
proc shuffle*(s: var FixedSeq, r: var Rand) =
|
||||||
r.shuffle(s.data)
|
r.shuffle(s.data)
|
||||||
@ -128,14 +144,3 @@ proc moveSubstackPre*(src, dst: var FixedSeq; start: Natural) =
|
|||||||
|
|
||||||
dst.last += ssLen
|
dst.last += ssLen
|
||||||
src.last -= ssLen
|
src.last -= ssLen
|
||||||
|
|
||||||
|
|
||||||
# proc display(s: ColorStack) =
|
|
||||||
# var p: seq[string]
|
|
||||||
# for i in s.pieces:
|
|
||||||
# if i == -1:
|
|
||||||
# p.add("none")
|
|
||||||
# else:
|
|
||||||
# p.add($Color(i))
|
|
||||||
# echo "pieces: @[", p.join(", "), "], last: ", s.last
|
|
||||||
# echo "len: ", s.len, "\n"
|
|
||||||
|
16
game.nim
16
game.nim
@ -18,7 +18,13 @@ proc getAllColors: ColorStack =
|
|||||||
for c in Color.low .. Color.high:
|
for c in Color.low .. Color.high:
|
||||||
result[i] = c
|
result[i] = c
|
||||||
|
|
||||||
const allColors* = getAllColors() # compile-time evaluation
|
const allColors* = getAllColors()
|
||||||
|
const colorNames: array[Color, string] =
|
||||||
|
["Red", "Green", "Blue", "Yellow", "Purple"]
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(c: Color): string =
|
||||||
|
result = colorNames[c]
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(s: ColorStack): string =
|
proc `$`*(s: ColorStack): string =
|
||||||
@ -87,8 +93,8 @@ proc display*(b: Board, start, stop: int) =
|
|||||||
|
|
||||||
|
|
||||||
proc setState*(b: var Board;
|
proc setState*(b: var Board;
|
||||||
camels: openArray[tuple[c: Color, p: int]];
|
camels: openArray[tuple[c: Color, p: int]];
|
||||||
tiles: openArray[tuple[t: Tile, p: int]]) =
|
tiles: openArray[tuple[t: Tile, p: int]]) =
|
||||||
for (color, dest) in camels: # note that `camels` is ordered, as this determines stacking
|
for (color, dest) in 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
|
||||||
@ -107,8 +113,8 @@ proc diceRemaining*(b: Board): ColorStack =
|
|||||||
|
|
||||||
|
|
||||||
proc resetDice*(b: var Board) =
|
proc resetDice*(b: var Board) =
|
||||||
var d: array[Color, bool]
|
for c, rolled in b.diceRolled:
|
||||||
b.diceRolled = d
|
b.diceRolled[c] = false
|
||||||
|
|
||||||
|
|
||||||
proc advance*(b: var Board, die: Die) =
|
proc advance*(b: var Board, die: Die) =
|
||||||
|
58
main.nim
58
main.nim
@ -1,5 +1,5 @@
|
|||||||
import math, options, sequtils, random, sets
|
import math, options, sequtils, random, sets
|
||||||
import combinators, game, fixedseq
|
import combinators, game, fixedseq, ui
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -17,7 +17,13 @@ proc update*(scores: var ScoreSet, toAdd: ScoreSet) =
|
|||||||
scores[i] += s
|
scores[i] += s
|
||||||
|
|
||||||
|
|
||||||
proc projectLeg(b: Board): LegResults =
|
proc display*(scores: ScoreSet) =
|
||||||
|
let total = scores.sum
|
||||||
|
for color, score in scores:
|
||||||
|
echo color, ": ", round(100 * scores[color] / total, 2), '%'
|
||||||
|
|
||||||
|
|
||||||
|
proc projectLeg*(b: Board): LegResults =
|
||||||
var scores: ScoreSet
|
var scores: ScoreSet
|
||||||
var endStates: HashSet[Board]
|
var endStates: HashSet[Board]
|
||||||
|
|
||||||
@ -93,42 +99,12 @@ proc randomSpread(b: Board, nTests: SomeInteger, nSamples: SomeInteger): ScoreSp
|
|||||||
result.hi[color] = pct
|
result.hi[color] = pct
|
||||||
|
|
||||||
|
|
||||||
var b: Board
|
when isMainModule:
|
||||||
b.init
|
let config = parseArgs()
|
||||||
|
var b: Board
|
||||||
randomize()
|
b.init
|
||||||
var r = initRand(rand(int64))
|
b.setState(config.state, [])
|
||||||
|
b.diceRolled = config.diceRolled
|
||||||
var rolls: array[5, tuple[c: Color, p: int]]
|
b.display(1, 5)
|
||||||
for i, roll in randomFuture(b.diceRemaining, r):
|
let scores = b.projectLeg()[0]
|
||||||
rolls[i] = (roll[0], roll[1] + 1)
|
scores.display
|
||||||
|
|
||||||
b.setState(rolls, @[])
|
|
||||||
echo "Starting state:"
|
|
||||||
b.display(1, 5)
|
|
||||||
|
|
||||||
# block outer:
|
|
||||||
# while true:
|
|
||||||
# for roll in randomFuture(b.diceRemaining, r):
|
|
||||||
# b.advance(roll)
|
|
||||||
# if b.gameOver:
|
|
||||||
# echo "last roll: ", roll
|
|
||||||
# break outer
|
|
||||||
# b.resetDice
|
|
||||||
|
|
||||||
# echo "winner: ", b.leader.get
|
|
||||||
# b.display(min(b.camels) - 1, 16)
|
|
||||||
|
|
||||||
# let scores = b.randomGames(10_000_000)
|
|
||||||
# let total = scores.sum
|
|
||||||
# for i, c in scores:
|
|
||||||
# echo Color(i), ": ", (100 * c / total).round(2), "% (", c, " / ", total, ")"
|
|
||||||
|
|
||||||
for i in 0..3:
|
|
||||||
let samples = 10 ^ (i + 3)
|
|
||||||
echo "Simulating ", samples, " games 100 times"
|
|
||||||
let spread = b.randomSpread(100, samples)
|
|
||||||
for color, lo in spread.lo:
|
|
||||||
stdout.write($color & ": " & $lo.round(4) & "-" & $spread.hi[color].round(4) & ", ")
|
|
||||||
stdout.flushFile
|
|
||||||
echo "\n"
|
|
||||||
|
65
ui.nim
Normal file
65
ui.nim
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import os, strutils
|
||||||
|
import game
|
||||||
|
|
||||||
|
|
||||||
|
const help =
|
||||||
|
"""cup - Probability calculator for the board game CamelUp
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
cup [-i] SPACE:STACK [...SPACE:STACK] [DICE]
|
||||||
|
|
||||||
|
SPACE refers to a numbered board space (1-16).
|
||||||
|
STACK refers to a stack of camel colors from bottom to top, e.g.
|
||||||
|
YBR (Yellow, Blue, Red, with Red on top).
|
||||||
|
DICE refers to the set of dice that have already been rolled,
|
||||||
|
e.g. GPR (Green, Purple, Red)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-i Interactive mode (currently unimplemented)
|
||||||
|
-h Show this message and exit
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
CmdConfig* = object
|
||||||
|
state*: seq[tuple[c: Color, p: int]]
|
||||||
|
interactive*: bool
|
||||||
|
diceRolled*: array[Color, bool]
|
||||||
|
|
||||||
|
|
||||||
|
proc parseColor(c: char): Color =
|
||||||
|
case c:
|
||||||
|
of 'R', 'r':
|
||||||
|
return cRed
|
||||||
|
of 'G', 'g':
|
||||||
|
return cGreen
|
||||||
|
of 'B', 'b':
|
||||||
|
return cBlue
|
||||||
|
of 'Y', 'y':
|
||||||
|
return cYellow
|
||||||
|
of 'P', 'p':
|
||||||
|
return cPurple
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "Invalid camel color specified.")
|
||||||
|
|
||||||
|
|
||||||
|
proc parseArgs*(): CmdConfig =
|
||||||
|
for p in os.commandLineParams():
|
||||||
|
if p == "-h":
|
||||||
|
echo help
|
||||||
|
quit 0
|
||||||
|
elif p == "-i":
|
||||||
|
result.interactive = true
|
||||||
|
elif result.state.len < 5:
|
||||||
|
let splat = p.split(':')
|
||||||
|
|
||||||
|
let sq = splat[0]
|
||||||
|
let square = sq.parseInt
|
||||||
|
|
||||||
|
let colors = splat[1]
|
||||||
|
for c in colors:
|
||||||
|
let color = parseColor(c)
|
||||||
|
result.state.add((color, square))
|
||||||
|
else:
|
||||||
|
for c in p:
|
||||||
|
let color = parseColor(c)
|
||||||
|
result.diceRolled[color] = true
|
Loading…
x
Reference in New Issue
Block a user