From 20d6022828d75cdafe4fb63907710231f5cfebb4 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Wed, 14 Jul 2021 21:49:10 -0700 Subject: [PATCH] prettier displays for game board and win probabilities --- game.nim | 16 +++++++++----- simulation.nim | 7 ++++++ test.nim | 20 +++++++++++++++-- ui.nim | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 93 insertions(+), 9 deletions(-) diff --git a/game.nim b/game.nim index 94f86aa..f6e8d8a 100644 --- a/game.nim +++ b/game.nim @@ -18,15 +18,21 @@ proc getAllColors: ColorStack = for c in Color.low .. Color.high: result[i] = c -const allColors* = getAllColors() -const colorNames: array[Color, string] = - ["Red", "Green", "Blue", "Yellow", "Purple"] +const + allColors* = getAllColors() + colorNames: array[Color, string] = + ["Red", "Green", "Blue", "Yellow", "Purple"] + colorAbbrevs: array[Color, char] = ['R', 'G', 'B', 'Y', 'P'] proc `$`*(c: Color): string = result = colorNames[c] +proc abbrev*(c: Color): char = + result = colorAbbrevs[c] + + proc `$`*(s: ColorStack): string = result.add("St@[") for i, color in s: @@ -44,8 +50,8 @@ type tForward = 1 Square* = object - camels: ColorStack - tile: Option[Tile] + camels*: ColorStack + tile*: Option[Tile] Board* = object squares*: array[1..16, Square] diff --git a/simulation.nim b/simulation.nim index 67d64bb..96d6fc1 100644 --- a/simulation.nim +++ b/simulation.nim @@ -4,6 +4,7 @@ import combinators, game, fixedseq type ScoreSet* = array[Color, int] + WinPercents* = array[Color, float] ScoreSpread = object lo*: array[Color, float] @@ -26,6 +27,12 @@ proc display*(scores: ScoreSet) = # 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 # ====================== diff --git a/test.nim b/test.nim index 17cc1e2..09d342e 100644 --- a/test.nim +++ b/test.nim @@ -1,5 +1,5 @@ import math, random, strformat, times -import fixedseq, game, simulation +import fixedseq, game, simulation, ui proc randomDice(r: var Rand): seq[tuple[c: Color, p: int]] = @@ -9,6 +9,15 @@ proc randomDice(r: var Rand): seq[tuple[c: Color, p: int]] = 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 = var r = initRand(rand(int64)) let dice = randomDice(r) @@ -64,6 +73,13 @@ proc testSpread(nTests, nSamples: Natural) = when isMainModule: 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 executionTime = testLegs(start_states) # echo "Execution time: ", executionTime @@ -76,4 +92,4 @@ when isMainModule: # echo "Full-game simulations per second: ", float(num_games) / executionTime # echo "" - testSpread(100, 1_000_000) + # testSpread(100, 1_000_000) diff --git a/ui.nim b/ui.nim index 3ca9636..5575d59 100644 --- a/ui.nim +++ b/ui.nim @@ -1,5 +1,5 @@ -import os, strutils -import game +import os, math, strutils, strformat +import fixedseq, game, simulation const help = @@ -19,6 +19,11 @@ Options: -h Show this message and exit """ + +# ============================= +# User input parsing/validation +# ============================= + type CmdConfig* = object state*: seq[tuple[c: Color, p: int]] @@ -63,3 +68,53 @@ proc parseArgs*(): CmdConfig = for c in p: let color = parseColor(c) 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")