bgammon/game.go

1076 lines
23 KiB
Go
Raw Normal View History

2023-07-31 23:46:28 +00:00
package bgammon
2023-08-23 00:05:35 +00:00
import (
"bytes"
"fmt"
2023-08-25 07:26:56 +00:00
"log"
"math"
2023-08-23 00:05:35 +00:00
"strconv"
"time"
2024-01-11 05:51:49 +00:00
"code.rocket9labs.com/tslocum/tabula"
2023-08-23 00:05:35 +00:00
)
var boardTopBlack = []byte("+13-14-15-16-17-18-+---+19-20-21-22-23-24-+")
var boardBottomBlack = []byte("+12-11-10--9--8--7-+---+-6--5--4--3--2--1-+")
var boardTopWhite = []byte("+24-23-22-21-20-19-+---+18-17-16-15-14-13-+")
var boardBottomWhite = []byte("+-1--2--3--4--5--6-+---+-7--8--9-10-11-12-+")
2023-07-31 23:46:28 +00:00
const (
VariantBackgammon int8 = 0
VariantAceyDeucey int8 = 1
VariantTabula int8 = 2
)
2023-07-31 23:46:28 +00:00
type Game struct {
Started time.Time
Ended time.Time
2023-11-25 03:15:50 +00:00
Player1 Player
Player2 Player
Variant int8 // 0 - Backgammon, 1 - Acey-deucey, 2 - Tabula.
Board []int8
Turn int8
2024-01-08 05:25:49 +00:00
Roll1 int8
Roll2 int8
Roll3 int8 // Used in tabula games.
Moves [][]int8 // Pending moves.
Winner int8
2024-01-06 02:18:54 +00:00
Points int8 // Points required to win the match.
DoubleValue int8 // Doubling cube value.
DoublePlayer int8 // Player that currently posesses the doubling cube.
2023-10-20 20:51:32 +00:00
DoubleOffered bool // Whether the current player is offering a double.
2023-09-03 06:40:31 +00:00
2023-11-25 03:15:50 +00:00
Reroll bool // Used in acey-deucey.
partialTurn int8
partialTime time.Time
partialHandled bool
2024-01-08 05:25:49 +00:00
boardStates [][]int8 // One board state for each move to allow undoing a move.
enteredStates [][2]bool // Player 1 entered state and Player 2 entered state for each move.
// Fields after this point are provided for backwards-compatibility only and will eventually be removed.
Acey bool // For Boxcars v1.2.1 and earlier.
2023-07-31 23:46:28 +00:00
}
func NewGame(variant int8) *Game {
2023-11-25 03:15:50 +00:00
g := &Game{
Variant: variant,
Board: NewBoard(variant),
2023-10-20 20:51:32 +00:00
Player1: NewPlayer(1),
Player2: NewPlayer(2),
Points: 1,
DoubleValue: 1,
2023-07-31 23:46:28 +00:00
}
if variant == VariantBackgammon {
2023-11-25 03:15:50 +00:00
g.Player1.Entered = true
g.Player2.Entered = true
} else {
// Set backwards-compatible field.
g.Acey = true
2023-11-25 03:15:50 +00:00
}
return g
2023-07-31 23:46:28 +00:00
}
2024-01-11 05:51:49 +00:00
func (g *Game) Copy(shallow bool) *Game {
2023-08-27 03:33:16 +00:00
newGame := &Game{
2023-11-25 03:15:50 +00:00
Started: g.Started,
Ended: g.Ended,
Player1: g.Player1,
Player2: g.Player2,
Variant: g.Variant,
Board: make([]int8, len(g.Board)),
Turn: g.Turn,
Roll1: g.Roll1,
Roll2: g.Roll2,
2024-01-08 05:25:49 +00:00
Roll3: g.Roll3,
Moves: make([][]int8, len(g.Moves)),
Winner: g.Winner,
2023-11-25 03:15:50 +00:00
2023-10-20 21:24:18 +00:00
Points: g.Points,
DoubleValue: g.DoubleValue,
DoublePlayer: g.DoublePlayer,
DoubleOffered: g.DoubleOffered,
2023-11-25 03:15:50 +00:00
Reroll: g.Reroll,
partialTurn: g.partialTurn,
partialTime: g.partialTime,
partialHandled: g.partialHandled,
2023-08-27 03:33:16 +00:00
}
copy(newGame.Board, g.Board)
copy(newGame.Moves, g.Moves)
2024-01-11 05:51:49 +00:00
if !shallow {
newGame.boardStates = make([][]int8, len(g.boardStates))
newGame.enteredStates = make([][2]bool, len(g.enteredStates))
copy(newGame.boardStates, g.boardStates)
copy(newGame.enteredStates, g.enteredStates)
}
2023-08-27 03:33:16 +00:00
return newGame
}
func (g *Game) PartialTurn() int8 {
return g.partialTurn
}
func (g *Game) PartialTime() int {
var delta time.Duration
if g.partialTime.IsZero() {
delta = time.Since(g.Started)
} else {
delta = time.Since(g.partialTime)
}
if delta <= 30*time.Second {
return 0
}
return int(math.Floor(delta.Seconds()))
}
func (g *Game) PartialHandled() bool {
return g.partialHandled
}
func (g *Game) SetPartialHandled(handled bool) {
g.partialHandled = handled
}
func (g *Game) NextPartialTurn(player int8) {
if g.Started.IsZero() || g.Winner != 0 {
return
}
delta := g.PartialTime()
if delta > 0 {
switch g.partialTurn {
case 1:
g.Player1.Inactive += delta
case 2:
g.Player2.Inactive += delta
}
}
g.partialTurn = player
g.partialTime = time.Now()
}
func (g *Game) NextTurn(reroll bool) {
2023-09-08 06:35:27 +00:00
if g.Winner != 0 {
return
}
if !reroll {
2024-01-06 02:18:54 +00:00
var nextTurn int8 = 1
2023-11-25 03:15:50 +00:00
if g.Turn == 1 {
nextTurn = 2
}
g.Turn = nextTurn
}
2023-11-25 03:15:50 +00:00
g.NextPartialTurn(g.Turn)
2024-01-08 05:25:49 +00:00
g.Roll1, g.Roll2, g.Roll3 = 0, 0, 0
g.Moves = g.Moves[:0]
g.boardStates = g.boardStates[:0]
2024-01-08 05:25:49 +00:00
g.enteredStates = g.enteredStates[:0]
}
2023-10-20 20:51:32 +00:00
func (g *Game) Reset() {
g.Player1.Inactive = 0
g.Player2.Inactive = 0
if g.Variant != VariantBackgammon {
2023-11-25 03:15:50 +00:00
g.Player1.Entered = false
g.Player2.Entered = false
}
g.Board = NewBoard(g.Variant)
2023-10-20 20:51:32 +00:00
g.Turn = 0
g.Roll1 = 0
g.Roll2 = 0
2024-01-08 05:25:49 +00:00
g.Roll3 = 0
2023-10-20 20:51:32 +00:00
g.Moves = nil
g.DoubleValue = 1
g.DoublePlayer = 0
g.DoubleOffered = false
2023-11-25 03:15:50 +00:00
g.Reroll = false
g.Winner = 0
2023-10-20 20:51:32 +00:00
g.boardStates = nil
2024-01-08 05:25:49 +00:00
g.enteredStates = nil
g.partialTurn = 0
g.partialTime = time.Time{}
2023-10-20 20:51:32 +00:00
}
2023-08-23 00:05:35 +00:00
func (g *Game) turnPlayer() Player {
switch g.Turn {
case 2:
return g.Player2
default:
return g.Player1
}
}
func (g *Game) opponentPlayer() Player {
switch g.Turn {
case 2:
return g.Player1
default:
return g.Player2
}
}
2024-01-08 05:25:49 +00:00
func (g *Game) SecondHalf(player int8, local bool) bool {
if g.Variant != VariantTabula {
return false
}
b := g.Board
switch player {
case 1:
if b[SpaceBarPlayer] != 0 {
return false
} else if !g.Player1.Entered && b[SpaceHomePlayer] != 0 {
return false
}
case 2:
if b[SpaceBarOpponent] != 0 {
return false
} else if !g.Player2.Entered && b[SpaceHomeOpponent] != 0 {
return false
}
default:
log.Panicf("unknown player: %d", player)
}
for space := 1; space < 13; space++ {
v := b[space]
if (player == 1 && v > 0) || (player == 2 && v < 0) {
return false
}
}
return true
}
func (g *Game) setEntered() {
if g.Variant == VariantBackgammon {
return
}
if !g.Player1.Entered && g.Board[SpaceHomePlayer] == 0 {
g.Player1.Entered = true
} else if !g.Player2.Entered && g.Board[SpaceHomeOpponent] == 0 {
g.Player2.Entered = true
}
}
2024-01-06 02:18:54 +00:00
func (g *Game) addMove(move []int8) bool {
opponentCheckers := OpponentCheckers(g.Board[move[1]], g.Turn)
if opponentCheckers > 1 {
2023-09-08 06:35:27 +00:00
return false
}
2024-01-06 02:18:54 +00:00
var delta int8 = 1
2023-09-03 06:40:31 +00:00
if g.Turn == 2 {
delta = -1
}
2024-01-06 02:18:54 +00:00
boardState := make([]int8, len(g.Board))
copy(boardState, g.Board)
g.boardStates = append(g.boardStates, boardState)
2024-01-08 05:25:49 +00:00
g.enteredStates = append(g.enteredStates, [2]bool{g.Player1.Entered, g.Player2.Entered})
g.Board[move[0]] -= delta
if opponentCheckers == 1 { // Hit checker.
g.Board[move[1]] = delta
// Move opponent checker to bar.
barSpace := SpaceBarOpponent
if g.Turn == 2 {
barSpace = SpaceBarPlayer
}
g.Board[barSpace] += delta * -1
} else {
g.Board[move[1]] += delta
}
2024-01-06 02:18:54 +00:00
g.Moves = append(g.Moves, []int8{move[0], move[1]})
2024-01-08 05:25:49 +00:00
g.setEntered()
return true
}
2023-09-30 20:50:31 +00:00
// AddLocalMove adds a move without performing any validation. This is useful when
// adding a move locally while waiting for an EventBoard response from the server.
2024-01-06 02:18:54 +00:00
func (g *Game) AddLocalMove(move []int8) bool {
2023-09-30 20:50:31 +00:00
return g.addMove(move)
}
2024-01-06 02:18:54 +00:00
func (g *Game) ExpandMove(move []int8, currentSpace int8, moves [][]int8, local bool) ([][]int8, bool) {
l := g.LegalMoves(local)
2024-01-06 02:18:54 +00:00
var hitMoves [][]int8
for _, m := range l {
if OpponentCheckers(g.Board[m[1]], g.Turn) == 1 {
hitMoves = append(hitMoves, m)
}
}
for i := 0; i < 2; i++ {
2024-01-06 02:18:54 +00:00
var checkMoves [][]int8
if i == 0 { // Try moves that will hit an opponent's checker first.
checkMoves = hitMoves
} else {
checkMoves = l
}
for _, lm := range checkMoves {
if lm[0] != currentSpace {
continue
}
2024-01-06 02:18:54 +00:00
newMoves := make([][]int8, len(moves))
copy(newMoves, moves)
2024-01-06 02:18:54 +00:00
newMoves = append(newMoves, []int8{lm[0], lm[1]})
if lm[1] == move[1] {
return newMoves, true
}
currentSpace = lm[1]
2024-01-11 05:51:49 +00:00
gc := g.Copy(true)
gc.addMove(lm)
m, ok := gc.ExpandMove(move, currentSpace, newMoves, local)
if ok {
return m, ok
}
}
}
return nil, false
}
// AddMoves adds moves to the game state. Adding a backwards move will remove the equivalent existing move.
2024-01-06 02:18:54 +00:00
func (g *Game) AddMoves(moves [][]int8, local bool) (bool, [][]int8) {
if g.Player1.Name == "" || g.Player2.Name == "" || g.Winner != 0 {
return false, nil
}
2024-01-06 02:18:54 +00:00
var addMoves [][]int8
var undoMoves [][]int8
2023-09-03 06:40:31 +00:00
2024-01-11 05:51:49 +00:00
gameCopy := g.Copy(false)
2023-09-07 07:21:58 +00:00
2023-09-03 06:40:31 +00:00
validateOffset := 0
VALIDATEMOVES:
2023-08-27 21:10:18 +00:00
for _, move := range moves {
l := gameCopy.LegalMoves(local)
2023-09-03 06:40:31 +00:00
for _, lm := range l {
if lm[0] == move[0] && lm[1] == move[1] {
2024-01-06 02:18:54 +00:00
addMoves = append(addMoves, []int8{move[0], move[1]})
2023-09-03 06:40:31 +00:00
continue VALIDATEMOVES
}
}
2023-09-07 07:21:58 +00:00
if len(gameCopy.Moves) > 0 {
i := len(gameCopy.Moves) - 1 - validateOffset
2023-09-03 06:40:31 +00:00
if i < 0 {
return false, nil
2023-09-03 06:40:31 +00:00
}
2023-09-07 07:21:58 +00:00
gameMove := gameCopy.Moves[i]
2023-09-03 06:40:31 +00:00
if move[0] == gameMove[1] && move[1] == gameMove[0] {
2024-01-06 02:18:54 +00:00
undoMoves = append(undoMoves, []int8{gameMove[1], gameMove[0]})
2023-09-03 06:40:31 +00:00
validateOffset++
continue VALIDATEMOVES
}
}
expandedMoves, ok := g.ExpandMove(move, move[0], nil, local)
if ok {
for _, expanded := range expandedMoves {
2024-01-06 02:18:54 +00:00
addMoves = append(addMoves, []int8{expanded[0], expanded[1]})
}
continue VALIDATEMOVES
}
return false, nil
2023-09-03 06:40:31 +00:00
}
if len(addMoves) != 0 && len(undoMoves) != 0 {
return false, nil
2023-09-03 06:40:31 +00:00
}
2023-09-08 06:35:27 +00:00
var checkWin bool
2023-09-03 06:40:31 +00:00
ADDMOVES:
for _, move := range addMoves {
l := gameCopy.LegalMoves(local)
2023-08-27 21:10:18 +00:00
for _, lm := range l {
if lm[0] == move[0] && lm[1] == move[1] {
if !gameCopy.addMove(move) {
return false, nil
2023-08-27 21:10:18 +00:00
}
2023-09-07 07:21:58 +00:00
2023-09-08 06:35:27 +00:00
if move[1] == SpaceHomePlayer || move[1] == SpaceHomeOpponent {
checkWin = true
}
2023-08-27 21:10:18 +00:00
continue ADDMOVES
}
}
2023-09-03 06:40:31 +00:00
}
for _, move := range undoMoves {
2023-09-07 07:21:58 +00:00
if len(gameCopy.Moves) > 0 {
i := len(gameCopy.Moves) - 1
2023-09-03 06:40:31 +00:00
if i < 0 {
return false, nil
2023-09-03 06:40:31 +00:00
}
2023-09-07 07:21:58 +00:00
gameMove := gameCopy.Moves[i]
2023-09-03 06:40:31 +00:00
if move[0] == gameMove[1] && move[1] == gameMove[0] {
2023-09-07 07:21:58 +00:00
gameCopy.Moves = gameCopy.Moves[:i]
if !local {
copy(gameCopy.Board, gameCopy.boardStates[i])
gameCopy.Player1.Entered = gameCopy.enteredStates[i][0]
gameCopy.Player2.Entered = gameCopy.enteredStates[i][1]
gameCopy.boardStates = gameCopy.boardStates[:i]
gameCopy.enteredStates = gameCopy.enteredStates[:i]
}
2023-09-03 06:40:31 +00:00
continue
}
}
return false, nil
2023-08-27 21:10:18 +00:00
}
2023-09-03 06:40:31 +00:00
g.Board = append(g.Board[:0], gameCopy.Board...)
2023-09-07 07:21:58 +00:00
g.Moves = gameCopy.Moves
2024-01-08 05:25:49 +00:00
g.Player1.Entered, g.Player2.Entered = gameCopy.Player1.Entered, gameCopy.Player2.Entered
2023-09-07 07:21:58 +00:00
g.boardStates = gameCopy.boardStates
2024-01-08 05:25:49 +00:00
g.enteredStates = gameCopy.enteredStates
2023-09-08 06:35:27 +00:00
if checkWin {
2023-11-25 03:15:50 +00:00
entered := g.Player1.Entered
if !local && g.Turn == 2 {
entered = g.Player2.Entered
}
2023-09-08 06:35:27 +00:00
var foundChecker bool
if g.Variant != VariantBackgammon && !entered {
2023-11-25 03:15:50 +00:00
foundChecker = true
} else {
for space := 1; space <= 24; space++ {
if PlayerCheckers(g.Board[space], g.Turn) != 0 {
foundChecker = true
break
}
2023-09-08 06:35:27 +00:00
}
}
2023-11-25 03:15:50 +00:00
2023-09-08 06:35:27 +00:00
if !foundChecker {
g.Winner = g.Turn
}
}
if len(addMoves) > 0 {
return true, addMoves
} else {
return true, undoMoves
}
2023-08-27 21:10:18 +00:00
}
func (g *Game) DiceRolls() []int8 {
2024-01-06 02:18:54 +00:00
rolls := []int8{
2023-08-25 07:26:56 +00:00
g.Roll1,
g.Roll2,
}
2024-01-08 05:25:49 +00:00
if g.Variant == VariantTabula {
rolls = append(rolls, g.Roll3)
} else if g.Roll1 == g.Roll2 {
2023-08-25 07:26:56 +00:00
rolls = append(rolls, g.Roll1, g.Roll2)
}
2024-01-06 02:18:54 +00:00
useDiceRoll := func(from, to int8) bool {
2023-09-07 07:21:58 +00:00
if to == SpaceHomePlayer || to == SpaceHomeOpponent {
needRoll := from
2024-01-08 05:25:49 +00:00
if to == SpaceHomeOpponent || g.Variant == VariantTabula {
2023-09-07 07:21:58 +00:00
needRoll = 25 - from
}
for i, roll := range rolls {
2023-11-02 05:47:59 +00:00
if roll == needRoll {
rolls = append(rolls[:i], rolls[i+1:]...)
return true
2023-11-02 05:47:59 +00:00
}
}
for i, roll := range rolls {
if roll > needRoll {
2023-09-07 07:21:58 +00:00
rolls = append(rolls[:i], rolls[i+1:]...)
return true
2023-09-07 07:21:58 +00:00
}
}
return false
2023-08-25 07:26:56 +00:00
}
2023-09-07 07:21:58 +00:00
diff := SpaceDiff(from, to, g.Variant)
2023-08-25 07:26:56 +00:00
for i, roll := range rolls {
if roll == diff {
rolls = append(rolls[:i], rolls[i+1:]...)
return true
2023-08-25 07:26:56 +00:00
}
}
return false
2023-08-25 07:26:56 +00:00
}
for _, move := range g.Moves {
if !useDiceRoll(move[0], move[1]) {
return nil
}
2023-08-25 07:26:56 +00:00
}
return rolls
}
func (g *Game) HaveDiceRoll(from int8, to int8) int8 {
if g.Variant == VariantTabula && to > 12 && to < 25 && ((g.Turn == 1 && !g.Player1.Entered) || (g.Turn == 2 && !g.Player2.Entered)) {
return 0
} else if (to == SpaceHomePlayer || to == SpaceHomeOpponent) && !g.MayBearOff(g.Turn, false) {
return 0
}
diff := SpaceDiff(from, to, g.Variant)
if diff == 0 {
return 0
}
var c int8
for _, roll := range g.DiceRolls() {
if roll == diff {
c++
}
}
return c
}
func (g *Game) HaveBearOffDiceRoll(diff int8) int8 {
if diff == 0 {
return 0
}
var c int8
for _, roll := range g.DiceRolls() {
if roll == diff || (roll > diff && g.Variant == VariantBackgammon) {
c++
}
}
return c
}
func (g *Game) LegalMoves(local bool) [][]int8 {
2024-01-11 05:51:49 +00:00
if g.Turn == 0 {
return nil
}
2024-01-11 05:51:49 +00:00
b, ok := g.TabulaBoard()
if !ok {
return nil
}
barSpace := SpaceBarPlayer
if g.Turn == 2 {
barSpace = SpaceBarOpponent
}
onBar := g.Board[barSpace] != 0
2024-01-11 05:51:49 +00:00
available, _ := b.Available(g.Turn)
mayBearOff := b.MayBearOff(g.Turn)
2024-01-06 02:18:54 +00:00
var moves [][]int8
2024-01-11 05:51:49 +00:00
for i := range available {
for j := range available[i] {
if available[i][j][0] == 0 && available[i][j][1] == 0 {
break
}
if (!onBar || (onBar && available[i][j][0] == barSpace)) && ((available[i][j][1] != tabula.SpaceHomePlayer && available[i][j][1] != tabula.SpaceHomeOpponent) || mayBearOff) && PlayerCheckers(g.Board[available[i][j][0]], g.Turn) != 0 {
2024-01-11 05:51:49 +00:00
var found bool
for _, m := range moves {
if m[0] == available[i][j][0] && m[1] == available[i][j][1] {
found = true
break
2023-09-07 07:21:58 +00:00
}
}
2024-01-11 05:51:49 +00:00
if !found {
moves = append(moves, []int8{available[i][j][0], available[i][j][1]})
2023-08-23 00:05:35 +00:00
}
2023-11-25 03:15:50 +00:00
}
2024-01-08 05:25:49 +00:00
}
}
2023-07-31 23:46:28 +00:00
return moves
}
2023-08-23 00:05:35 +00:00
2024-01-08 05:25:49 +00:00
// MayBearOff returns whether the provided player may bear checkers off of the board.
func (g *Game) MayBearOff(player int8, local bool) bool {
if PlayerCheckers(g.Board[SpaceBarPlayer], player) > 0 || PlayerCheckers(g.Board[SpaceBarOpponent], player) > 0 {
return false
} else if (player == 1 && !g.Player1.Entered) || (player == 2 && !g.Player2.Entered) {
return false
} else if g.Variant == VariantTabula {
return g.SecondHalf(player, local)
}
homeStart, homeEnd := int8(1), int8(6)
if !local {
homeStart, homeEnd = HomeRange(player, g.Variant)
homeStart, homeEnd = minInt(homeStart, homeEnd), maxInt(homeStart, homeEnd)
}
for i := int8(1); i <= 24; i++ {
if (i < homeStart || i > homeEnd) && PlayerCheckers(g.Board[i], player) > 0 {
return false
}
}
return true
}
2024-01-06 02:18:54 +00:00
func (g *Game) RenderSpace(player int8, space int8, spaceValue int8, legalMoves [][]int8) []byte {
2023-08-23 00:05:35 +00:00
var playerColor = "x"
var opponentColor = "o"
if player == 2 {
playerColor = "o"
opponentColor = "x"
}
var pieceColor string
value := g.Board[space]
if space == SpaceBarPlayer {
pieceColor = playerColor
} else if space == SpaceBarOpponent {
pieceColor = opponentColor
} else {
if value < 0 {
pieceColor = "o"
2023-09-02 21:48:49 +00:00
} else if value > 0 {
pieceColor = "x"
2023-08-23 00:05:35 +00:00
} else {
pieceColor = playerColor
}
}
abs := value
if value < 0 {
abs = value * -1
}
top := space > 12
if player == 2 {
top = !top
}
2024-01-06 02:18:54 +00:00
var firstDigit int8 = 4
var secondDigit int8 = 5
2023-08-23 00:05:35 +00:00
if !top {
firstDigit = 5
secondDigit = 4
}
var firstNumeral string
var secondNumeral string
if abs > 5 {
if abs > 9 {
firstNumeral = "1"
} else {
2024-01-06 02:18:54 +00:00
firstNumeral = strconv.Itoa(int(abs))
2023-08-23 00:05:35 +00:00
}
if abs > 9 {
2024-01-06 02:18:54 +00:00
secondNumeral = strconv.Itoa(int(abs) - 10)
2023-08-23 00:05:35 +00:00
}
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
if abs > 0 && spaceValue <= abs {
r = []byte(pieceColor)
} else {
r = []byte(" ")
}
return append(append([]byte(" "), r...), ' ')
}
2024-01-06 02:18:54 +00:00
func (g *Game) BoardState(player int8, local bool) []byte {
2023-08-23 00:05:35 +00:00
var t bytes.Buffer
playerRating := "0"
opponentRating := "0"
var white bool
if player == 2 {
white = true
}
var opponentName = g.Player2.Name
var playerName = g.Player1.Name
if playerName == "" {
playerName = "Waiting..."
}
if opponentName == "" {
opponentName = "Waiting..."
}
if white {
playerName, opponentName = opponentName, playerName
}
var playerColor = "x"
var opponentColor = "o"
2023-08-25 08:39:43 +00:00
playerRoll := g.Roll1
opponentRoll := g.Roll2
2023-08-23 00:05:35 +00:00
if white {
playerColor = "o"
opponentColor = "x"
2023-08-25 08:39:43 +00:00
playerRoll = g.Roll2
opponentRoll = g.Roll1
2023-08-23 00:05:35 +00:00
}
if white {
t.Write(boardTopWhite)
} else {
t.Write(boardTopBlack)
}
t.WriteString(" ")
t.WriteByte('\n')
legalMoves := g.LegalMoves(local)
2024-01-06 02:18:54 +00:00
space := func(row int8, col int8) []byte {
var spaceValue int8 = row + 1
2023-08-23 00:05:35 +00:00
if row > 5 {
spaceValue = 5 - (row - 6)
}
if col == -1 {
if row <= 4 {
return g.RenderSpace(player, SpaceBarOpponent, spaceValue, legalMoves)
}
return g.RenderSpace(player, SpaceBarPlayer, spaceValue, legalMoves)
}
2024-01-06 02:18:54 +00:00
var space int8
2023-09-02 21:48:49 +00:00
if white {
space = 24 - col
2023-08-23 00:05:35 +00:00
if row > 5 {
2023-09-02 21:48:49 +00:00
space = 1 + col
2023-08-23 00:05:35 +00:00
}
} else {
2023-09-02 21:48:49 +00:00
space = 13 + col
2023-08-23 00:05:35 +00:00
if row > 5 {
2023-09-02 21:48:49 +00:00
space = 12 - col
2023-08-23 00:05:35 +00:00
}
}
if row == 5 {
return []byte(" ")
}
2023-09-02 21:48:49 +00:00
return g.RenderSpace(player, space, spaceValue, legalMoves)
2023-08-23 00:05:35 +00:00
}
2024-01-06 02:41:13 +00:00
const verticalBar rune = '│'
2024-01-06 02:18:54 +00:00