840 lines
18 KiB
Go
840 lines
18 KiB
Go
package fibs
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
const (
|
|
BoxDrawingsLightVertical = '|'
|
|
)
|
|
|
|
const (
|
|
StateLength = iota
|
|
StatePlayerScore
|
|
StateOpponentScore
|
|
StateBoardSpace0
|
|
)
|
|
|
|
const (
|
|
StatePlayerName = iota
|
|
StateOpponentName
|
|
)
|
|
|
|
const (
|
|
StateTurn = 29 + iota
|
|
StatePlayerDice1
|
|
StatePlayerDice2
|
|
StateOpponentDice1
|
|
StateOpponentDice2
|
|
StateDoublingValue
|
|
StatePlayerMayDouble
|
|
StateOpponentMayDouble
|
|
StateWasDoubled
|
|
StatePlayerColor
|
|
StateDirection
|
|
StateObsoleteHome
|
|
StateObsoleteBar
|
|
StatePlayerHome
|
|
StateOpponentHome
|
|
StatePlayerBar
|
|
StateOpponentBar
|
|
StateMovablePieces
|
|
StateObsoletePlayerForced
|
|
StateObsoleteOpponentForced
|
|
StateRedoubles
|
|
)
|
|
|
|
const (
|
|
SpaceUnknown = -1
|
|
)
|
|
|
|
const initialState = "FIBS:Welcome:5:0:2:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:-1:0:0:0:0:1:1:1:0:1:-1:0:25:0:0:0:0:4:0:0:0"
|
|
|
|
var boardTopWhite = []byte("+13-14-15-16-17-18-+---+19-20-21-22-23-24-+")
|
|
var boardBottomWhite = []byte("+12-11-10--9--8--7-+---+-6--5--4--3--2--1-+")
|
|
|
|
var boardTopBlack = []byte("+-1--2--3--4--5--6-+---+-7--8--9-10-11-12-+")
|
|
var boardBottomBlack = []byte("+24-23-22-21-20-19-+---+18-17-16-15-14-13-+")
|
|
|
|
type Board struct {
|
|
client *Client
|
|
|
|
state string
|
|
|
|
s []string
|
|
v []int
|
|
|
|
moves [][2]int
|
|
movesColor int
|
|
|
|
validMoves map[int][][]int
|
|
|
|
from map[int]int
|
|
to map[int]int
|
|
|
|
selectedNum int
|
|
selectedSpace int
|
|
|
|
premove [][2]int
|
|
Premovefrom map[int]int
|
|
Premoveto map[int]int
|
|
|
|
dragFromX int
|
|
dragFromY int
|
|
|
|
sync.Mutex
|
|
}
|
|
|
|
func NewBoard(client *Client) *Board {
|
|
b := &Board{
|
|
client: client,
|
|
s: make([]string, 52),
|
|
v: make([]int, 50),
|
|
}
|
|
|
|
b.ResetMoves()
|
|
b.ResetPreMoves()
|
|
|
|
b.SetState(initialState)
|
|
|
|
// TODO
|
|
/*
|
|
b.v[StatePlayerColor] = -1
|
|
b.v[StateBoardSpace0+11] = 12
|
|
b.v[StateBoardSpace0+9] = 7
|
|
b.v[StateBoardSpace0+13] = -13
|
|
b.v[StateBoardSpace0+24] = -3
|
|
b.v[StatePlayerBar] = 3
|
|
b.Update()
|
|
*/
|
|
|
|
return b
|
|
}
|
|
|
|
// TODO refactor
|
|
func (b *Board) GetStringState() []string {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
return b.s
|
|
}
|
|
|
|
func (b *Board) GetIntState() []int {
|
|
b.Lock()
|
|
defer b.Unlock()
|
|
return b.v
|
|
}
|
|
|
|
func (b *Board) resetSelection() {
|
|
b.selectedSpace = 0
|
|
b.selectedNum = 0
|
|
}
|
|
|
|
func (b *Board) autoSendMoves() {
|
|
movable := 2
|
|
if b.v[StatePlayerDice1] > 0 && b.v[StatePlayerDice1] == b.v[StatePlayerDice2] {
|
|
movable = 4
|
|
}
|
|
if b.v[StateMovablePieces] > 0 {
|
|
movable = b.v[StateMovablePieces]
|
|
}
|
|
if len(b.premove) < movable {
|
|
return
|
|
}
|
|
|
|
moveCommand := []byte("move")
|
|
for j := 0; j < 2; j++ {
|
|
for i := range b.premove {
|
|
var from string
|
|
if b.premove[i][0] == 0 || b.premove[i][0] == 25 {
|
|
from = "bar"
|
|
} else {
|
|
from = strconv.Itoa(b.premove[i][0])
|
|
}
|
|
|
|
if (j == 0) != (from == "bar") {
|
|
continue // Always send bar moves first
|
|
}
|
|
|
|
var to string
|
|
if b.premove[i][1] == b.PlayerBearOffSpace() {
|
|
to = "off"
|
|
} else {
|
|
to = strconv.Itoa(b.premove[i][1])
|
|
}
|
|
|
|
moveCommand = append(moveCommand, []byte(" "+from+"-"+to)...)
|
|
}
|
|
|
|
}
|
|
|
|
b.client.Out <- moveCommand
|
|
}
|
|
|
|
func (b *Board) GetState() string {
|
|
var s = strings.Join(b.s, ":")
|
|
for i := range b.v {
|
|
s += ":" + strconv.Itoa(b.v[i])
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (b *Board) SetState(state string) {
|
|
b.Lock()
|
|
|
|
s := strings.Split(state, ":")
|
|
newPlayers := s[StatePlayerName] != b.s[StatePlayerName] || s[StateOpponentName] != b.s[StateOpponentName]
|
|
copy(b.s, s)
|
|
|
|
v := make([]int, 50)
|
|
var err error
|
|
for i := 0; i < 50; i++ {
|
|
v[i], err = strconv.Atoi(b.s[i+2])
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
newTurn := v[StateTurn] != b.v[StateTurn]
|
|
|
|
// Retain dice rolls
|
|
if !newPlayers && !newTurn {
|
|
copyDice := []int{
|
|
StatePlayerDice1,
|
|
StatePlayerDice2,
|
|
StateOpponentDice1,
|
|
StateOpponentDice2,
|
|
}
|
|
for _, vi := range copyDice {
|
|
if v[vi] == 0 {
|
|
v[vi] = b.v[vi]
|
|
}
|
|
}
|
|
}
|
|
|
|
copy(b.v, v)
|
|
|
|
b.ResetPreMoves()
|
|
|
|
b.Unlock()
|
|
b.Draw()
|
|
}
|
|
|
|
func (b *Board) Draw() {
|
|
b.client.Event <- &EventDraw{}
|
|
}
|
|
|
|
func (b *Board) renderSpace(index int, spaceValue int) []byte {
|
|
var playerColor = "x"
|
|
var opponentColor = "o"
|
|
if b.v[StatePlayerColor] == 1 {
|
|
playerColor = "o"
|
|
opponentColor = "x"
|
|
}
|
|
|
|
var pieceColor string
|
|
value := b.v[StateBoardSpace0+index]
|
|
if index == b.PlayerBarSpace() {
|
|
value = b.v[StatePlayerBar]
|
|
pieceColor = playerColor
|
|
} else if index == 25-b.PlayerBarSpace() {
|
|
value = b.v[StateOpponentBar]
|
|
pieceColor = opponentColor
|
|
} else {
|
|
if value < 0 {
|
|
pieceColor = "x"
|
|
} else if value > 0 {
|
|
pieceColor = "o"
|
|
} else {
|
|
pieceColor = playerColor
|
|
}
|
|
}
|
|
|
|
abs := value
|
|
if value < 0 {
|
|
abs = value * -1
|
|
}
|
|
|
|
top := index <= 12
|
|
if b.v[StatePlayerColor] == 1 {
|
|
top = !top
|
|
}
|
|
|
|
firstDigit := 4
|
|
secondDigit := 5
|
|
if !top {
|
|
firstDigit = 5
|
|
secondDigit = 4
|
|
}
|
|
|
|
var firstNumeral string
|
|
var secondNumeral string
|
|
if abs > 5 {
|
|
if abs > 9 {
|
|
firstNumeral = "1"
|
|
} else {
|
|
firstNumeral = strconv.Itoa(abs)
|
|
}
|
|
if abs > 9 {
|
|
secondNumeral = strconv.Itoa(abs - 10)
|
|
}
|
|
|
|
if spaceValue == firstDigit && (!top || abs > 9) {
|
|
pieceColor = firstNumeral
|
|
} else if spaceValue == secondDigit && abs > 9 {
|
|
pieceColor = secondNumeral
|
|
} else if top && spaceValue == secondDigit {
|
|
pieceColor = firstNumeral
|
|
}
|
|
}
|
|
|
|
if abs > 5 {
|
|
abs = 5
|
|
}
|
|
|
|
var r []byte
|
|
foregroundColor := "#FFFFFF"
|
|
backgroundColor := "#000000"
|
|
if index != 0 && index != 25 {
|
|
if true { // default theme
|
|
if index%2 == 0 {
|
|
backgroundColor = "#303030"
|
|
} else {
|
|
backgroundColor = "#101010"
|
|
}
|
|
} else { // rainbow
|
|
foregroundColor = "#000000"
|
|
switch index % 6 {
|
|
case 1:
|
|
backgroundColor = "#FF0000"
|
|
case 2:
|
|
backgroundColor = "#FFA500"
|
|
case 3:
|
|
backgroundColor = "#FFFF00"
|
|
case 4:
|
|
backgroundColor = "#008000"
|
|
case 5:
|
|
backgroundColor = "#0000FF"
|
|
case 0:
|
|
backgroundColor = "#4B0082"
|
|
}
|
|
}
|
|
}
|
|
// Highlight legal moves
|
|
highlightSpace := b.ValidMove(b.selectedSpace, index)
|
|
highlightSpace = false // TODO Make configurable, disable by default
|
|
if b.selectedNum > 0 && highlightSpace && index != 25 && index != 0 {
|
|
foregroundColor = "black"
|
|
backgroundColor = "yellow"
|
|
}
|
|
if abs > 0 && spaceValue <= abs {
|
|
r = []byte(pieceColor)
|
|
} else {
|
|
r = []byte(" ")
|
|
}
|
|
|
|
rightArrowFrom := (b.v[StateDirection] == b.movesColor) == (index > 12)
|
|
if b.selectedSpace == index && b.selectedNum > 0 && spaceValue <= abs && spaceValue > abs-b.selectedNum {
|
|
r = []byte("*")
|
|
} else if b.Premovefrom[index] > 0 && spaceValue > (abs+b.Premoveto[index])-b.Premovefrom[index] && spaceValue <= abs+b.Premoveto[index] {
|
|
if index == 25-b.PlayerBarSpace() {
|
|
r = []byte("▾")
|
|
} else if index == b.PlayerBarSpace() {
|
|
r = []byte("▴")
|
|
} else if rightArrowFrom {
|
|
r = []byte("▸")
|
|
} else {
|
|
r = []byte("◂")
|
|
}
|
|
foregroundColor = "yellow"
|
|
} else if b.Premoveto[index] > 0 && spaceValue > abs && spaceValue <= abs+(b.Premoveto[index]) {
|
|
r = []byte(playerColor)
|
|
foregroundColor = "yellow"
|
|
} else if b.from[index] > 0 && spaceValue > abs && spaceValue <= abs+b.from[index] {
|
|
if rightArrowFrom {
|
|
r = []byte("▸")
|
|
} else {
|
|
r = []byte("◂")
|
|
}
|
|
if b.movesColor == b.v[StatePlayerColor] {
|
|
foregroundColor = "yellow"
|
|
} else {
|
|
foregroundColor = "green"
|
|
}
|
|
} else if b.to[index] > 0 && spaceValue > abs-(b.to[index]+b.from[index]) {
|
|
if b.movesColor == b.v[StatePlayerColor] {
|
|
foregroundColor = "yellow"
|
|
} else {
|
|
foregroundColor = "green"
|
|
}
|
|
}
|
|
|
|
return append(append([]byte(fmt.Sprintf("[\"space-%d\"][%s:%s:b] ", index, foregroundColor, backgroundColor)), r...), []byte(" [-:-:-][\"\"]")...)
|
|
}
|
|
|
|
func (b *Board) ResetMoves() {
|
|
b.moves = nil
|
|
b.movesColor = 0
|
|
b.validMoves = make(map[int][][]int)
|
|
b.from = make(map[int]int)
|
|
b.to = make(map[int]int)
|
|
}
|
|
|
|
func (b *Board) ResetPreMoves() {
|
|
b.premove = nil
|
|
b.Premovefrom = make(map[int]int)
|
|
b.Premoveto = make(map[int]int)
|
|
}
|
|
|
|
func (b *Board) PlayerHomeSpaces() (int, int) {
|
|
homeBoardStart := 1
|
|
homeBoardEnd := 6
|
|
if (b.v[StateDirection] == -1) == (b.v[StatePlayerColor] == -1) {
|
|
homeBoardStart = 19
|
|
homeBoardEnd = 24
|
|
}
|
|
return homeBoardStart, homeBoardEnd
|
|
}
|
|
|
|
func (b *Board) PlayerPieceAreHome() bool {
|
|
homeBoardStart, homeBoardEnd := b.PlayerHomeSpaces()
|
|
hasPlayerPiece := func(index int) bool {
|
|
if index < 0 || index > 25 {
|
|
return false
|
|
}
|
|
value := b.v[StateBoardSpace0+index]
|
|
|
|
// Include pre-moves
|
|
mod := b.v[StatePlayerColor]
|
|
value -= b.client.Board.Premovefrom[index] * mod
|
|
|
|
if b.v[StatePlayerColor] == -1 {
|
|
return value < 0
|
|
}
|
|
return value > 0
|
|
}
|
|
for i := 1; i < 24; i++ {
|
|
if i >= homeBoardStart && i <= homeBoardEnd {
|
|
continue
|
|
}
|
|
if hasPlayerPiece(i) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (b *Board) spaceAvailable(index int) bool {
|
|
if index < 0 || index > 25 {
|
|
return false
|
|
}
|
|
if index == 0 || index == 25 {
|
|
return b.PlayerPieceAreHome()
|
|
}
|
|
return (b.v[StatePlayerColor] == 1 && b.v[StateBoardSpace0+index] >= -1) ||
|
|
(b.v[StatePlayerColor] == -1 && b.v[StateBoardSpace0+index] <= 1)
|
|
}
|
|
|
|
func (b *Board) GetValidMoves(from int) [][]int {
|
|
if validMoves, ok := b.validMoves[from]; ok {
|
|
return validMoves
|
|
}
|
|
|
|
var validMoves [][]int
|
|
defer func() {
|
|
b.validMoves[from] = validMoves
|
|
}()
|
|
|
|
if b.v[StateTurn] != b.v[StatePlayerColor] || b.v[StatePlayerDice1] == 0 || b.v[StatePlayerDice2] == 0 {
|
|
return validMoves
|
|
}
|
|
|
|
trySpaces := [][]int{
|
|
{b.v[StatePlayerDice1]},
|
|
{b.v[StatePlayerDice2]},
|
|
{b.v[StatePlayerDice1], b.v[StatePlayerDice2]},
|
|
{b.v[StatePlayerDice2], b.v[StatePlayerDice1]},
|
|
}
|
|
if b.v[StatePlayerDice1] == b.v[StatePlayerDice2] {
|
|
trySpaces = append(trySpaces,
|
|
[]int{b.v[StatePlayerDice1], b.v[StatePlayerDice1], b.v[StatePlayerDice1]},
|
|
[]int{b.v[StatePlayerDice1], b.v[StatePlayerDice1], b.v[StatePlayerDice1], b.v[StatePlayerDice1]})
|
|
}
|
|
|
|
if b.PlayerPieceAreHome() {
|
|
homeSpace := b.PlayerBearOffSpace()
|
|
spacesHome := from - homeSpace
|
|
if spacesHome < 0 {
|
|
spacesHome *= -1
|
|
}
|
|
if spacesHome <= b.v[StatePlayerDice1] || spacesHome <= b.v[StatePlayerDice2] {
|
|
trySpaces = append(trySpaces, []int{spacesHome})
|
|
}
|
|
}
|
|
foundMoves := make(map[int]bool)
|
|
CHECKSPACES:
|
|
for i := range trySpaces {
|
|
checkSpace := 0
|
|
for _, space := range trySpaces[i] {
|
|
checkSpace += space
|
|
if !b.spaceAvailable(from + (checkSpace * b.v[StateDirection])) {
|
|
continue CHECKSPACES
|
|
}
|
|
}
|
|
space := from + (checkSpace * b.v[StateDirection])
|
|
if _, value := foundMoves[space]; !value {
|
|
foundMoves[space] = true
|
|
validMoves = append(validMoves, trySpaces[i])
|
|
}
|
|
}
|
|
|
|
return validMoves
|
|
}
|
|
|
|
func (b *Board) PlayerBarSpace() int {
|
|
return 25 - b.PlayerBearOffSpace()
|
|
}
|
|
|
|
func (b *Board) PlayerBearOffSpace() int {
|
|
if b.v[StateDirection] == -1 {
|
|
return 0
|
|
}
|
|
return 25
|
|
}
|
|
|
|
func (b *Board) ValidMove(f int, t int) bool {
|
|
if b.v[StateTurn] != b.v[StatePlayerColor] || b.v[StatePlayerDice1] == 0 || b.v[StatePlayerDice2] == 0 {
|
|
return false
|
|
}
|
|
|
|
if t == b.PlayerBearOffSpace() {
|
|
// TODO bear off logic, only allow high roll
|
|
return b.PlayerPieceAreHome()
|
|
}
|
|
|
|
validMoves := b.GetValidMoves(f)
|
|
CHECKVALID:
|
|
for i := range validMoves {
|
|
checkSpace := 0
|
|
for _, space := range validMoves[i] {
|
|
checkSpace += space
|
|
if !b.spaceAvailable(f + (checkSpace * b.v[StateDirection])) {
|
|
continue CHECKVALID
|
|
}
|
|
}
|
|
if f+(checkSpace*b.v[StateDirection]) == t {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (b *Board) parseMoveString(player int, s string) int {
|
|
space, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
space = SpaceUnknown
|
|
if s == "bar" {
|
|
barSpace := b.PlayerBarSpace()
|
|
if b.v[StatePlayerColor] == player {
|
|
space = barSpace
|
|
} else {
|
|
space = 25 - barSpace
|
|
}
|
|
} else if s == "off" {
|
|
space = b.PlayerBearOffSpace()
|
|
}
|
|
}
|
|
return space
|
|
}
|
|
|
|
func (b *Board) Move(player int, f string, t string) {
|
|
from := b.parseMoveString(player, f)
|
|
to := b.parseMoveString(player, t)
|
|
|
|
if from == SpaceUnknown || to == SpaceUnknown {
|
|
lf("WARNING: Unknown move %s-%s", f, t)
|
|
return
|
|
}
|
|
|
|
b.moves = append(b.moves, [2]int{from, to})
|
|
b.movesColor = player
|
|
|
|
b.from[from]++
|
|
b.to[to]++
|
|
|
|
spaceValue := b.v[StateBoardSpace0+to]
|
|
|
|
// Hit.
|
|
if (spaceValue == -1 && player == 1) || (spaceValue == 1 && player == -1) {
|
|
bar := 25 - b.PlayerBarSpace()
|
|
if player == b.v[StatePlayerColor] {
|
|
bar = b.PlayerBarSpace()
|
|
}
|
|
|
|
b.v[StateBoardSpace0+bar] -= player
|
|
}
|
|
|
|
b.v[StateBoardSpace0+from] -= player
|
|
b.v[StateBoardSpace0+to] += player
|
|
|
|
b.v[StateTurn] = player * -1
|
|
|
|
b.validMoves = make(map[int][][]int)
|
|
b.ResetPreMoves()
|
|
}
|
|
|
|
func (b *Board) SimplifyMoves() {
|
|
for i := range b.moves {
|
|
for j := range b.moves {
|
|
if b.moves[i][1] == b.moves[j][0] {
|
|
// Same to space as from space
|
|
b.moves[j][0] = b.moves[i][0] // Set from space to earlier from space
|
|
b.moves = append(b.moves[:i], b.moves[i+1:]...)
|
|
b.SimplifyMoves()
|
|
return
|
|
} else if b.moves[i][0] == b.moves[j][1] {
|
|
// Same to space as from space
|
|
b.moves[j][1] = b.moves[i][1] // Set to space to earlier to space
|
|
b.moves = append(b.moves[:i], b.moves[i+1:]...)
|
|
b.SimplifyMoves()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *Board) GetSelection() (num int, space int) {
|
|
return b.selectedNum, b.selectedSpace
|
|
}
|
|
|
|
func (b *Board) SetSelection(num int, space int) {
|
|
b.selectedNum, b.selectedSpace = num, space
|
|
}
|
|
|
|
func (b *Board) ResetSelection() {
|
|
b.selectedNum, b.selectedSpace = 0, 0
|
|
}
|
|
|
|
func (b *Board) addPreMove(from int, to int, num int) bool {
|
|
// Allow bearing off when the player moves their own pieces on to the bar
|
|
if to == 0 || to == 25 {
|
|
to = b.PlayerBearOffSpace()
|
|
}
|
|
|
|
// Expand combined move
|
|
moves := b.client.Board.GetValidMoves(from)
|
|
|
|
CHECKPREMOVES:
|
|
for i := range moves {
|
|
checkSpace := 0
|
|
for _, space := range moves[i] {
|
|
checkSpace += space
|
|
if !b.spaceAvailable(from + (checkSpace * b.v[StateDirection])) {
|
|
continue CHECKPREMOVES
|
|
}
|
|
}
|
|
if (from+(checkSpace*b.v[StateDirection]) == to) && len(moves[i]) > 1 {
|
|
for j := 0; j < num; j++ {
|
|
checkSpace = 0
|
|
lastSpace := 0
|
|
for _, space := range moves[i] {
|
|
checkSpace += space
|
|
if !b.addPreMove(from+(lastSpace*b.v[StateDirection]), from+(checkSpace*b.v[StateDirection]), 1) {
|
|
return false
|
|
}
|
|
lastSpace = checkSpace
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
if !b.ValidMove(from, to) {
|
|
return false
|
|
}
|
|
|
|
for i := 0; i < num; i++ {
|
|
b.premove = append(b.premove, [2]int{from, to})
|
|
b.Premovefrom[from]++
|
|
b.Premoveto[to]++
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (b *Board) AddPreMove(from int, to int) bool {
|
|
if !b.addPreMove(from, to, b.selectedNum) {
|
|
return false
|
|
}
|
|
|
|
b.resetSelection()
|
|
b.autoSendMoves()
|
|
return true
|
|
}
|
|
|
|
func (b *Board) GetPreMoves() [][2]int {
|
|
return b.premove
|
|
}
|
|
|
|
func (b *Board) Render() []byte {
|
|
b.Lock()
|
|
|
|
var white bool
|
|
if b.v[StatePlayerColor] == 1 {
|
|
white = true
|
|
}
|
|
|
|
var opponentName = b.s[1]
|
|
var playerName = b.s[0]
|
|
|
|
var playerColor = "x"
|
|
var opponentColor = "o"
|
|
if white {
|
|
playerColor = "o"
|
|
opponentColor = "x"
|
|
}
|
|
|
|
var t bytes.Buffer
|
|
t.WriteString("[\"space-off\"] [\"\"]\n")
|
|
t.WriteString("[\"space-off\"] [\"\"]\n")
|
|
t.WriteString("[\"space-off\"] ")
|
|
if white {
|
|
t.Write(boardTopWhite)
|
|
} else {
|
|
t.Write(boardTopBlack)
|
|
}
|
|
t.WriteString("[\"\"] ")
|
|
t.WriteByte('\n')
|
|
|
|
space := func(i int, j int) []byte {
|
|
spaceValue := i + 1
|
|
if i > 5 {
|
|
spaceValue = 5 - (i - 6)
|
|
}
|
|
|
|
if j == -1 {
|
|
if i <= 4 {
|
|
return b.renderSpace(25-b.PlayerBarSpace(), spaceValue)
|
|
}
|
|
return b.renderSpace(b.PlayerBarSpace(), spaceValue)
|
|
}
|
|
|
|
var index int
|
|
if !white {
|
|
if i < 6 {
|
|
j = 12 - j
|
|
} else {
|
|
j = 11 - j
|
|
}
|
|
|
|
index = 12 + j
|
|
if i > 5 {
|
|
index = 12 - j
|
|
}
|
|
} else {
|
|
index = 12 + j
|
|
if i > 5 {
|
|
index = 11 - j
|
|
}
|
|
}
|
|
if !white {
|
|
index = 24 - index
|
|
}
|
|
index++ // increment to get actual space number (0 is bar)
|
|
|
|
if i == 5 {
|
|
return []byte("[-:#000000] [-:-]")
|
|
}
|
|
|
|
return b.renderSpace(index, spaceValue)
|
|
}
|
|
|
|
for i := 0; i < 11; i++ {
|
|
t.Write([]byte("[\"space-off\"]"))
|
|
|
|
if i == 5 && b.v[StateDoublingValue] > 1 {
|
|
t.WriteString(fmt.Sprintf("%2d ", b.v[StateDoublingValue]))
|
|
if b.v[StatePlayerMayDouble] == 1 {
|
|
t.WriteByte('v')
|
|
} else {
|
|
t.WriteByte('^')
|
|
}
|
|
} else {
|
|
t.WriteByte(' ')
|
|
t.WriteByte(' ')
|
|
t.WriteByte(' ')
|
|
t.WriteByte(' ')
|
|
}
|
|
|
|
t.WriteRune(BoxDrawingsLightVertical)
|
|
t.Write([]byte("[\"\"]"))
|
|
for j := 0; j < 12; j++ {
|
|
t.Write(space(i, j))
|
|
|
|
if j == 5 {
|
|
t.WriteRune(BoxDrawingsLightVertical)
|
|
t.Write(space(i, -1))
|
|
t.WriteRune(BoxDrawingsLightVertical)
|
|
}
|
|
}
|
|
|
|
t.Write([]byte("[\"space-off\"]" + string(BoxDrawingsLightVertical) + " "))
|
|
|
|
playerRollColor := "yellow"
|
|
playerBold := "b"
|
|
opponentRollColor := "white"
|
|
opponentBold := ""
|
|
if b.v[StateTurn] != b.v[StatePlayerColor] {
|
|
playerRollColor = "white"
|
|
opponentRollColor = "green"
|
|
playerBold = ""
|
|
opponentBold = "b"
|
|
}
|
|
|
|
if i == 0 {
|
|
t.Write([]byte("[" + opponentRollColor + "::" + opponentBold + "]" + opponentColor + " " + opponentName + " (" + b.s[4] + ")"))
|
|
if b.v[StateOpponentHome] > 0 {
|
|
t.Write([]byte(fmt.Sprintf(" %d off", b.v[StateOpponentHome])))
|
|
}
|
|
t.Write([]byte("[-::-]"))
|
|
} else if i == 2 {
|
|
if b.v[StateOpponentDice1] > 0 {
|
|
t.Write([]byte(fmt.Sprintf(" [%s::%s]%d %d[-::-] ", opponentRollColor, opponentBold, b.v[StateOpponentDice1], b.v[StateOpponentDice2])))
|
|
} else {
|
|
t.Write([]byte(fmt.Sprintf(" [%s]- -[-] ", opponentRollColor)))
|
|
}
|
|
} else if i == 8 {
|
|
if b.v[StatePlayerDice1] > 0 {
|
|
t.Write([]byte(fmt.Sprintf(" [%s::%s]%d %d[-::-] ", playerRollColor, playerBold, b.v[StatePlayerDice1], b.v[StatePlayerDice2])))
|
|
} else {
|
|
t.Write([]byte(fmt.Sprintf(" [%s]- -[-] ", playerRollColor)))
|
|
}
|
|
} else if i == 10 {
|
|
t.Write([]byte("[" + playerRollColor + "::" + playerBold + "]" + playerColor + " " + playerName + " (" + b.s[3] + ")"))
|
|
if b.v[StatePlayerHome] > 0 {
|
|
t.Write([]byte(fmt.Sprintf(" %d off", b.v[StatePlayerHome])))
|
|
}
|
|
t.Write([]byte("[-::-]"))
|
|
}
|
|
|
|
t.Write([]byte("[\"\"] "))
|
|
t.WriteByte('\n')
|
|
}
|
|
|
|
t.WriteString("[\"space-off\"] ")
|
|
if white {
|
|
t.Write(boardBottomWhite)
|
|
} else {
|
|
t.Write(boardBottomBlack)
|
|
}
|
|
t.WriteString(" [\"\"]\n")
|
|
t.WriteString("[\"space-off\"] [\"\"]")
|
|
|
|
b.Unlock()
|
|
|
|
return t.Bytes()
|
|
}
|