Compare commits

..

No commits in common. "20d6022828d75cdafe4fb63907710231f5cfebb4" and "57c991cf5fd95aaa0a385b49c677e6f466337f3a" have entirely different histories.

6 changed files with 9 additions and 114 deletions

View File

@ -1,21 +0,0 @@
# `cup` - CamelUp probability calculator
This tool calculates probable outcomes for the board game CamelUp.
It can calculate all possible outcomes for a single game leg in about 5ms, so effectively instantaneously.
Full-game calculations take a little bit longer and are not exact (since it isn't practical to simulate all possible full game states.)
However it can easily simulate a million random games in about 80ms in the worst case, which should provide estimates accurate to within about 0.2%.
```
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
```

View File

@ -18,21 +18,15 @@ proc getAllColors: ColorStack =
for c in Color.low .. Color.high: for c in Color.low .. Color.high:
result[i] = c result[i] = c
const const allColors* = getAllColors()
allColors* = getAllColors() const 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']
proc `$`*(c: Color): string = proc `$`*(c: Color): string =
result = colorNames[c] result = colorNames[c]
proc abbrev*(c: Color): char =
result = colorAbbrevs[c]
proc `$`*(s: ColorStack): string = proc `$`*(s: ColorStack): string =
result.add("St@[") result.add("St@[")
for i, color in s: for i, color in s:
@ -50,8 +44,8 @@ type
tForward = 1 tForward = 1
Square* = object Square* = object
camels*: ColorStack camels: ColorStack
tile*: Option[Tile] tile: Option[Tile]
Board* = object Board* = object
squares*: array[1..16, Square] squares*: array[1..16, Square]

View File

View File

@ -4,7 +4,6 @@ import combinators, game, fixedseq
type type
ScoreSet* = array[Color, int] ScoreSet* = array[Color, int]
WinPercents* = array[Color, float]
ScoreSpread = object ScoreSpread = object
lo*: array[Color, float] lo*: array[Color, float]
@ -27,12 +26,6 @@ proc display*(scores: ScoreSet) =
# echo color, ": ", round(100 * scores[color] / total, 2), '%' # echo color, ": ", round(100 * scores[color] / total, 2), '%'
proc percents*(scores: ScoreSet): WinPercents =
let total = scores.sum
for c, score in scores:
result[c] = score / total
# ====================== # ======================
# Single-leg simulations # Single-leg simulations
# ====================== # ======================

View File

@ -1,5 +1,5 @@
import math, random, strformat, times import math, random, strformat, times
import fixedseq, game, simulation, ui import fixedseq, game, simulation
proc randomDice(r: var Rand): seq[tuple[c: Color, p: int]] = proc randomDice(r: var Rand): seq[tuple[c: Color, p: int]] =
@ -9,15 +9,6 @@ proc randomDice(r: var Rand): seq[tuple[c: Color, p: int]] =
result.shuffle result.shuffle
proc newRandomGame(r: var Rand): Board =
var dice: array[5, tuple[c: Color, p: int]]
for i in 0 .. 4:
dice[i] = (Color(i), r.rand(1..3))
result.init
result.setState(dice, [])
proc testGames(n: SomeInteger = 100): auto = proc testGames(n: SomeInteger = 100): auto =
var r = initRand(rand(int64)) var r = initRand(rand(int64))
let dice = randomDice(r) let dice = randomDice(r)
@ -73,13 +64,6 @@ proc testSpread(nTests, nSamples: Natural) =
when isMainModule: when isMainModule:
randomize() randomize()
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 start_states = 2_000 # let start_states = 2_000
# let executionTime = testLegs(start_states) # let executionTime = testLegs(start_states)
# echo "Execution time: ", executionTime # echo "Execution time: ", executionTime
@ -92,4 +76,4 @@ when isMainModule:
# echo "Full-game simulations per second: ", float(num_games) / executionTime # echo "Full-game simulations per second: ", float(num_games) / executionTime
# echo "" # echo ""
# testSpread(100, 1_000_000) testSpread(100, 1_000_000)

59
ui.nim
View File

@ -1,5 +1,5 @@
import os, math, strutils, strformat import os, strutils
import fixedseq, game, simulation import game
const help = const help =
@ -19,11 +19,6 @@ Options:
-h Show this message and exit -h Show this message and exit
""" """
# =============================
# User input parsing/validation
# =============================
type type
CmdConfig* = object CmdConfig* = object
state*: seq[tuple[c: Color, p: int]] state*: seq[tuple[c: Color, p: int]]
@ -68,53 +63,3 @@ proc parseArgs*(): CmdConfig =
for c in p: for c in p:
let color = parseColor(c) let color = parseColor(c)
result.diceRolled[color] = true result.diceRolled[color] = true
# ==========================
# Game state representations
# ==========================
proc showSpaces*(b: Board; start, stop: Natural): string =
let numSpaces = stop - start + 1
let width = 4 * numSpaces - 1
var lines: array[7, string]
# start by building up an empty board
for i in 0 .. 6: # gotta initialize the strings
lines[i] = newString(width)
for c in lines[i].mitems:
c = ' '
# fill in the dividers
lines[5] = repeat("=== ", numSpaces - 1)
lines[5].add("===")
# now populate the board
for sp in 0 ..< numSpaces:
# fill in the square numbers
let squareNum = sp + start
let cellMid = 4 * sp + 1
for i, chr in $squareNum:
lines[6][cellMid + i] = chr
# fill in the camel stacks
for i, color in b.squares[squareNum].camels:
let lineNum = 4 - i # lines go to 6, but bottom 2 are reserved
let repr = '|' & color.abbrev & '|'
for j, chr in repr:
lines[lineNum][cellMid - 1 + j] = chr
result = lines.join("\n")
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
let barFill = int(round(pct * 100 / 20))
for i in 0 ..< barFill:
bar[i] = '='
lines[int(color)] = fmt"{label}: [{bar}] {percentage}%"
result = lines.join("\n")