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
|
yield workingCopy
|
||||||
|
|
||||||
|
|
||||||
iterator allDigits*(lo, hi, size: int): seq[int] =
|
iterator allDigits*(lo, hi, size: Natural): seq[int] =
|
||||||
# we use uninitialized since we will initialize it below, but not necessarily with 0s
|
if size > 0: # otherwise we get an infinite loop
|
||||||
var digits = newSeqUninitialized[int](size)
|
# we use uninitialized since we will initialize it below, but not necessarily with 0s
|
||||||
for i in 0 .. digits.high:
|
var digits = newSeqUninitialized[int](size)
|
||||||
digits[i] = lo
|
for i in 0 .. digits.high:
|
||||||
|
digits[i] = lo
|
||||||
|
|
||||||
var complete = false
|
var complete = false
|
||||||
while not complete:
|
while not complete:
|
||||||
yield digits
|
yield digits
|
||||||
for i in countdown(digits.high, 0):
|
for i in countdown(digits.high, 0):
|
||||||
if digits[i] < hi:
|
if digits[i] < hi:
|
||||||
inc digits[i]
|
inc digits[i]
|
||||||
break
|
break
|
||||||
elif i == 0: # since this is the last digit to be incremented, we must be done
|
elif i == 0: # since this is the last digit to be incremented, we must be done
|
||||||
complete = true
|
complete = true
|
||||||
else:
|
else:
|
||||||
digits[i] = lo
|
digits[i] = lo
|
||||||
|
|
||||||
|
|
||||||
iterator possibleFutures*[C](dice: seq[C]): seq[tuple[color: C, value: int]] =
|
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 math, hashes, options, tables, sequtils, sets, sugar
|
||||||
import combinators
|
import combinators, colors
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
const allDice = @[cRed, cGreen, cBlue, cYellow, cPurple]
|
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:
|
for i, s in toAdd:
|
||||||
scores[i] += s
|
scores[i] += s
|
||||||
|
|
||||||
|
|
||||||
proc `[]`[T](b: var Board, idx: T): var Square =
|
proc `[]`*[T](b: var Board, idx: T): var Square =
|
||||||
b.squares[idx]
|
b.squares[idx]
|
||||||
|
|
||||||
|
|
||||||
proc hash(b: Board): Hash =
|
proc hash*(b: Board): Hash =
|
||||||
var h: Hash = 0
|
var h: Hash = 0
|
||||||
# there could be a tile anywhere so we have to check all squares
|
# there could be a tile anywhere so we have to check all squares
|
||||||
for i, sq in b.squares:
|
for i, sq in b.squares:
|
||||||
@ -49,11 +47,17 @@ proc hash(b: Board): Hash =
|
|||||||
if sq.tile.isSome:
|
if sq.tile.isSome:
|
||||||
h = h !& int(sq.tile.get) * 10 # so it isn't confused with a camel
|
h = h !& int(sq.tile.get) * 10 # so it isn't confused with a camel
|
||||||
else:
|
else:
|
||||||
for c in sq.camels:
|
for c in sq.camels.asInt:
|
||||||
h = h !& int(c)
|
h = h !& c
|
||||||
result = !$h
|
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) =
|
proc display(b: Board, start, stop: int) =
|
||||||
for i in start..stop:
|
for i in start..stop:
|
||||||
let sq = b.squares[i]
|
let sq = b.squares[i]
|
||||||
@ -79,6 +83,11 @@ proc setState(b: var Board;
|
|||||||
b.leader = some(leadCamel)
|
b.leader = some(leadCamel)
|
||||||
|
|
||||||
|
|
||||||
|
proc resetDice(b: var Board) =
|
||||||
|
var d: array[Color, bool]
|
||||||
|
b.diceRolled = d
|
||||||
|
|
||||||
|
|
||||||
proc advance(b: var Board, die: Die) =
|
proc advance(b: var Board, die: Die) =
|
||||||
let
|
let
|
||||||
(color, roll) = die
|
(color, roll) = die
|
||||||
@ -96,21 +105,24 @@ proc advance(b: var Board, die: Die) =
|
|||||||
endPos += int(t)
|
endPos += int(t)
|
||||||
if t == tBackward: prepend = true
|
if t == tBackward: prepend = true
|
||||||
|
|
||||||
for i, c in b[startPos].camels:
|
let stackStart = b[startPos].camels.find(color)
|
||||||
if c == color:
|
if prepend:
|
||||||
let subStack = b[startPos].camels[i .. ^1]
|
b[startPos].camels.moveSubstackPre(b[endPos].camels, stackStart)
|
||||||
if prepend:
|
let stackLen = b[startPos].camels.len - stackStart
|
||||||
b[endPos].camels.insert(subStack)
|
for i in 0 ..< stackLen:
|
||||||
else:
|
# we know how many camels we added to the bottom, so set the position for each of those
|
||||||
b[endPos].camels.add(subStack)
|
b.camels[b[endPos].camels[i]] = endPos
|
||||||
|
else:
|
||||||
b[startPos].camels[i .. ^1] = @[]
|
let dstPrevHigh = b[endPos].camels.high
|
||||||
for moved in subStack:
|
b[startPos].camels.moveSubstack(b[endPos].camels, stackStart)
|
||||||
b.camels[moved] = endPos
|
# the camels that have moved start immediately after the previous high camel
|
||||||
# if we are stacking on or moving past the previous leader
|
for i in (dstPrevHigh + 1) .. b[endPos].camels.high:
|
||||||
if endPos >= b.camels[b.leader.get]:
|
b.camels[b[endPos].camels[i]] = endPos
|
||||||
b.leader = some(b[endPos].camels[^1])
|
|
||||||
break # breaking the outer loop here, not the inner - but only conditionally! gah!
|
# 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
|
b.diceRolled[color] = true
|
||||||
|
|
||||||
|
|
||||||
@ -129,14 +141,20 @@ proc projectLeg(b: Board): LegResults =
|
|||||||
inc scores[prediction.leader.get]
|
inc scores[prediction.leader.get]
|
||||||
# deduplicate results
|
# deduplicate results
|
||||||
endStates.incl(prediction)
|
endStates.incl(prediction)
|
||||||
|
|
||||||
result = (scores, endStates)
|
result = (scores, endStates)
|
||||||
|
|
||||||
|
|
||||||
proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
|
proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
|
||||||
var outcomeStack = @[ [b].toHashSet ]
|
var outcomeStack = @[ [b].toHashSet ]
|
||||||
for depth in 1..maxDepth:
|
for depth in 1..maxDepth:
|
||||||
|
echo "simulating ", outcomeStack[^1].len, " possible legs."
|
||||||
var endStates: HashSet[Board]
|
var endStates: HashSet[Board]
|
||||||
|
|
||||||
for o in outcomeStack[^1]:
|
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
|
let projection = o.projectLeg
|
||||||
result.update(projection[0])
|
result.update(projection[0])
|
||||||
endStates.incl(projection[1])
|
endStates.incl(projection[1])
|
||||||
@ -147,11 +165,15 @@ proc projectOutcomes(b: Board, maxDepth = 1): ScoreSet =
|
|||||||
|
|
||||||
|
|
||||||
var b: Board
|
var b: Board
|
||||||
|
b.init
|
||||||
b.display(1, 5)
|
b.display(1, 5)
|
||||||
|
|
||||||
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 4}, @[])
|
b.setState({cGreen: 4, cYellow: 3, cPurple: 4, cBlue: 3, cRed: 4}, @[])
|
||||||
b.display(1, 5)
|
b.display(1, 5)
|
||||||
|
|
||||||
|
# b.advance((cRed, 1))
|
||||||
|
# b.display(1, 5)
|
||||||
|
|
||||||
let r = b.projectOutcomes(2)
|
let r = b.projectOutcomes(2)
|
||||||
let total = r.sum
|
let total = r.sum
|
||||||
for i, c in r:
|
for i, c in r:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user