You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
846 lines
18 KiB
846 lines
18 KiB
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 |
|
lf("CHECK %d %d", checkSpace, from+(checkSpace*b.v[StateDirection])) |
|
if !b.spaceAvailable(from + (checkSpace * b.v[StateDirection])) { |
|
continue CHECKPREMOVES |
|
} |
|
} |
|
lf("SECOND PHASE %+v %d", moves[i], num) |
|
if (from+(checkSpace*b.v[StateDirection]) == to) && len(moves[i]) > 1 { |
|
lf("SECOND.5 PHASE %+v %d", moves[i], num) |
|
for j := 0; j < num; j++ { |
|
checkSpace = 0 |
|
lastSpace := 0 |
|
for _, space := range moves[i] { |
|
checkSpace += space |
|
lf("THIRD PHASE %d %d", from+(lastSpace*b.v[StateDirection]), from+(checkSpace*b.v[StateDirection])) |
|
|
|
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]++ |
|
} |
|
lf("ADD %+v", b.premove) |
|
return true |
|
} |
|
|
|
func (b *Board) AddPreMove(from int, to int) bool { |
|
if !b.addPreMove(from, to, b.selectedNum) { |
|
return false |
|
} |
|
lf("FINAL %+v", b.premove) |
|
|
|
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() |
|
}
|
|
|