move to fixed-size arrays instead of sequences
This commit is contained in:
parent
e0f83cdca1
commit
e5e2f30045
139
colors.nim
Normal file
139
colors.nim
Normal file
@ -0,0 +1,139 @@
|
||||
import strutils
|
||||
|
||||
|
||||
type
|
||||
Color* = enum
|
||||
cRed, cGreen, cBlue, cYellow, cPurple
|
||||
|
||||
ColorStack* = object
|
||||
pieces: array[5, int8]
|
||||
last: int8
|
||||
|
||||
|
||||
proc init*(s: var ColorStack) =
|
||||
for i in 0..4:
|
||||
s.pieces[i] = -1
|
||||
s.last = -1
|
||||
|
||||
|
||||
proc `[]`*(s: ColorStack, i: Natural): Color =
|
||||
if i > s.last:
|
||||
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
|
||||
result = Color(s.pieces[i])
|
||||
|
||||
|
||||
proc `[]`*(s: ColorStack, i: BackwardsIndex): Color =
|
||||
if s.last == -1:
|
||||
raise newException(IndexDefect, "index out of bounds, the container is empty.") # matching stdlib again
|
||||
result = Color(s.pieces[s.last - int8(i) + 1])
|
||||
|
||||
|
||||
proc `[]=`*(s: var ColorStack, i: Natural, v: Color) =
|
||||
if i > s.last:
|
||||
raise newException(IndexDefect, "index " & $i & " is out of bounds.")
|
||||
s.pieces[i] = int8(v)
|
||||
|
||||
|
||||
proc `$`*(s: ColorStack): string =
|
||||
result.add("St@[")
|
||||
for i in 0 .. s.last:
|
||||
let c = Color(s.pieces[i])
|
||||
result.add($c)
|
||||
if i < s.last:
|
||||
result.add(", ")
|
||||
result.add("]")
|
||||
|
||||
|
||||
proc high*(s: ColorStack): int8 =
|
||||
result = s.last
|
||||
|
||||
|
||||
proc low*(s: ColorStack): int8 =
|
||||
result = case s.last
|
||||
of -1: 0 # a bit weird but it's how the stdlib seq works
|
||||
else: s.last
|
||||
|
||||
|
||||
proc len*(s: ColorStack): int8 =
|
||||
result = s.last + 1
|
||||
|
||||
|
||||
iterator items*(s: ColorStack): Color =
|
||||
for i in 0 .. s.last:
|
||||
yield Color(s[i])
|
||||
|
||||
|
||||
iterator asInt*(s: ColorStack): int8 =
|
||||
for i in 0 .. s.last:
|
||||
yield s.pieces[i] # no conversion, should speed up hashing? maybe
|
||||
|
||||
|
||||
iterator pairs*(s: ColorStack): auto =
|
||||
var count = 0
|
||||
for c in s:
|
||||
yield (count, c)
|
||||
inc count
|
||||
|
||||
|
||||
proc add*(s: var ColorStack, c: Color) =
|
||||
let i = s.last + 1
|
||||
s.pieces[i] = int8(c) # will raise exception if out of bounds
|
||||
s.last = i
|
||||
|
||||
|
||||
proc insert*(s: var ColorStack, c: Color, idx: Natural = 0) =
|
||||
for i in countdown(s.last, int8(idx)):
|
||||
swap(s.pieces[i], s.pieces[i + 1]) # will also raise exception if out of bounds
|
||||
s.pieces[idx] = int8(c)
|
||||
inc s.last
|
||||
|
||||
|
||||
proc delete*(s: var ColorStack, idx: Natural) =
|
||||
if idx > s.last:
|
||||
raise newException(IndexDefect, "index " & $idx & " is out of bounds.")
|
||||
s.pieces[idx] = -1
|
||||
dec s.last
|
||||
for i in int8(idx) .. s.last:
|
||||
swap(s.pieces[i], s.pieces[i + 1])
|
||||
|
||||
|
||||
proc find(s: ColorStack, c: Color): int8 =
|
||||
let needle = int(c)
|
||||
for i, v in s.pieces:
|
||||
if v == needle:
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
proc moveSubstack*(src, dst: var ColorStack; start: Natural) =
|
||||
var count = 0 # have to track this separately apparently
|
||||
for idx in (int8(start) .. src.last):
|
||||
swap(src.pieces[idx], dst.pieces[dst.last + 1 + count])
|
||||
inc count
|
||||
dst.last += int8(count)
|
||||
src.last -= int8(count)
|
||||
|
||||
|
||||
proc moveSubstackPre*(src, dst: var ColorStack; start: Natural) =
|
||||
let ssLen = src.last - start + 1 # length of substack
|
||||
for i in countdown(dst.last, 0):
|
||||
swap(dst.pieces[i], dst.pieces[i + ssLen])
|
||||
|
||||
var count = 0
|
||||
for i in start .. src.last:
|
||||
swap(src.pieces[i], dst.pieces[count])
|
||||
inc count
|
||||
|
||||
dst.last += int8(ssLen)
|
||||
src.last -= int8(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"
|
@ -12,23 +12,24 @@ iterator allPermutations*[T](x: seq[T]): seq[T] =
|
||||
yield workingCopy
|
||||
|
||||
|
||||
iterator allDigits*(lo, hi, size: int): seq[int] =
|
||||
# we use uninitialized since we will initialize it below, but not necessarily with 0s
|
||||
var digits = newSeqUninitialized[int](size)
|
||||
for i in 0 .. digits.high:
|
||||
digits[i] = lo
|
||||
iterator allDigits*(lo, hi, size: Natural): seq[int] =
|
||||
if size > 0: # otherwise we get an infinite loop
|
||||
# we use uninitialized since we will initialize it below, but not necessarily with 0s
|
||||
var digits = newSeqUninitialized[int](size)
|
||||
for i in 0 .. digits.high:
|
||||
digits[i] = lo
|
||||
|
||||
var complete = false
|
||||
while not complete:
|
||||
yield digits
|
||||
for i in countdown(digits.high, 0):
|
||||
if digits[i] < hi:
|
||||
inc digits[i]
|
||||
break
|
||||
elif i == 0: # since this is the last digit to be incremented, we must be done
|
||||
complete = true
|
||||
else:
|
||||
digits[i] = lo
|
||||
var complete = false
|
||||
while not complete:
|
||||
yield digits
|
||||
for i in countdown(digits.high, 0):
|
||||
if digits[i] < hi:
|
||||
inc digits[i]
|
||||
break
|
||||
elif i == 0: # since this is the last digit to be incremented, we must be done
|
||||
complete = true
|
||||
else:
|
||||
digits[i] = lo
|
||||
|
||||
|
||||
iterator possibleFutures*[C](dice: seq[C]): seq[tuple[color: C, value: int]] =
|
||||
|
116
main.nim
116
main.nim
@ -1,46 +1,44 @@
|
||||
import math, hashes, options, tables, sequtils, sets, sugar
|
||||
import combinators
|
||||
|
||||
|
||||
type
|
||||
Color = enum
|
||||
cRed, cGreen, cBlue, cYellow, cPurple
|
||||
|
||||
Tile = enum
|
||||
tBackward = -1,
|
||||
tForward = 1
|
||||
|
||||
Square = object
|
||||
camels: seq[Color]
|
||||
tile: Option[Tile]
|
||||
|
||||
Die = tuple[color: Color, value: int]
|
||||
|
||||
ScoreSet = array[Color, int]
|
||||
|
||||
LegResults = tuple[scores: ScoreSet, endStates: HashSet[Board]]
|
||||
|
||||
Board = object
|
||||
squares: array[1..16, Square]
|
||||
camels: array[Color, range[1..16]]
|
||||
diceRolled: array[Color, bool]
|
||||
leader: Option[Color]
|
||||
gameOver: bool
|
||||
import combinators, colors
|
||||
|
||||
|
||||
const allDice = @[cRed, cGreen, cBlue, cYellow, cPurple]
|
||||
|
||||
|
||||
proc update(scores: var ScoreSet, toAdd: ScoreSet) =
|
||||
type
|
||||
Die* = tuple[color: Color, value: int]
|
||||
|
||||
Tile* = enum
|
||||
tBackward = -1,
|
||||
tForward = 1
|
||||
|
||||
Square* = object
|
||||
camels: ColorStack
|
||||
tile: Option[Tile]
|
||||
|
||||
Board* = object
|
||||
squares: array[1..16, Square]
|
||||
camels: array[Color, range[1..16]]
|
||||
diceRolled: array[Color, bool]
|
||||
leader: Option[Color]
|
||||
gameOver: bool
|
||||
initialized: bool
|
||||
|
||||
ScoreSet* = array[Color, int]
|
||||
|
||||
LegResults* = tuple[scores: ScoreSet, endStates: HashSet[Board]]
|
||||
|
||||
|
||||
proc update*(scores: var ScoreSet, toAdd: ScoreSet) =
|
||||
for i, s in toAdd:
|
||||
scores[i] += s
|
||||
|
||||
|
||||
proc `[]`[T](b: var Board, idx: T): var Square =
|
||||
proc `[]`*[T](b: var Board, idx: T): var Square =
|
||||
b.squares[idx]
|
||||
|
||||
|
||||
proc hash(b: Board): Hash =
|
||||
proc hash*(b: Board): Hash =
|
||||
var h: Hash = 0
|
||||
# there could be a tile anywhere so we have to check all squares
|
||||
for i, sq in b.squares:
|
||||
@ -49,11 +47,17 @@ proc hash(b: Board): Hash =
|
||||
if sq.tile.isSome:
|
||||
h = h !& int(sq.tile.get) * 10 # so it isn't confused with a camel
|
||||
else:
|
||||
for c in sq.camels:
|
||||
h = h !& int(c)
|
||||
for c in sq.camels.asInt:
|
||||
h = h !& c
|
||||
result = !$h
|
||||
|
||||
|
||||
proc init*(b: var Board) =
|
||||
for sq in b.squares.mitems:
|
||||
sq.camels.init
|
||||
b.initialized = true
|
||||
|
||||
|
||||
proc display(b: Board, start, stop: int) =
|
||||
for i in start..stop:
|
||||
let sq = b.squares[i]
|
||||
@ -79,6 +83,11 @@ proc setState(b: var Board;
|
||||
b.leader = some(leadCamel)
|
||||
|
||||
|
||||
proc resetDice(b: var Board) =
|
||||
var d: array[Color, bool]
|
||||
b.diceRolled = d
|
||||
|
||||
|
||||
proc advance(b: var Board, die: Die) =
|
||||
let
|
||||
(color, roll) = die
|
||||
@ -96,21 +105,24 @@ proc advance(b: var Board, die: Die) =
|
||||
endPos += int(t)
|
||||
if t == tBackward: prepend = true
|
||||
|
||||
for i, c in b[startPos].camels:
|
||||
if c == color:
|
||||
let subStack = b[startPos].camels[i .. ^1]
|
||||
if prepend:
|
||||
b[endPos].camels.insert(subStack)
|
||||
else:
|
||||
b[endPos].camels.add(subStack)
|
||||
|
||||
b[startPos].camels[i .. ^1] = @[]
|
||||
for moved in subStack:
|
||||
b.camels[moved] = endPos
|
||||
# if we are stacking on or moving past the previous leader
|
||||
if endPos >= b.camels[b.leader.get]:
|
||||
b.leader = some(b[endPos].camels[^1])
|
||||
break # breaking the outer loop here, not the inner - but only conditionally! gah!
|
||||
let stackStart = b[startPos].camels.find(color)
|
||||
if prepend:
|
||||
b[startPos].camels.moveSubstackPre(b[endPos].camels, stackStart)
|
||||
let stackLen = b[startPos].camels.len - stackStart
|
||||
for i in 0 ..< stackLen:
|
||||
# we know how many camels we added to the bottom, so set the position for each of those
|
||||
b.camels[b[endPos].camels[i]] = endPos
|
||||
else:
|
||||
let dstPrevHigh = b[endPos].camels.high
|
||||
b[startPos].camels.moveSubstack(b[endPos].camels, stackStart)
|
||||
# the camels that have moved start immediately after the previous high camel
|
||||
for i in (dstPrevHigh + 1) .. b[endPos].camels.high:
|
||||
b.camels[b[endPos].camels[i]] = endPos
|
||||
|
||||
# if we are stacking on or moving past the previous leader
|
||||
if endPos >= b.camels[b.leader.get]:
|
||||
b.leader = some(b[endPos].camels[^1])
|
||||
|
||||
b.diceRolled[color] = true
|
||||
|
||||
|
||||
@ -129,14 +141,20 @@ proc projectLeg(b: Board): LegResults =
|
||||
inc scores[prediction.leader.get]
|
||||
# deduplicate results
|
||||
endStates.incl(prediction)
|
||||
|
||||
result = (scores, endStates)
|
||||
|
||||
|
||||
proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
|
||||
var outcomeStack = @[ [b].toHashSet ]
|
||||
for depth in 1..maxDepth:
|
||||
echo "simulating ", outcomeStack[^1].len, " possible legs."
|
||||
var endStates: HashSet[Board]
|
||||
|
||||
for o in outcomeStack[^1]:
|
||||
var o = o # make it mutable
|
||||
o.resetDice # o was describina an end-of-leg state, so dice were exhausted
|
||||
|
||||
let projection = o.projectLeg
|
||||
result.update(projection[0])
|
||||
endStates.incl(projection[1])
|
||||
@ -147,11 +165,15 @@ proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
|
||||
|
||||
|
||||
var b: Board
|
||||
b.init
|
||||
b.display(1, 5)
|
||||
|
||||
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 4}, @[])
|
||||
b.display(1, 5)
|
||||
|
||||
# b.advance((cRed, 1))
|
||||
# b.display(1, 5)
|
||||
|
||||
let r = b.projectOutcomes(2)
|
||||
let total = r.sum
|
||||
for i, c in r:
|
||||
|
Loading…
x
Reference in New Issue
Block a user