Support playing tabula games

This commit is contained in:
Trevor Slocum 2024-01-07 21:25:49 -08:00
parent 7064b0b085
commit ce0122a594
7 changed files with 278 additions and 108 deletions

View File

@ -39,8 +39,8 @@ func NewBoard(variant int8) []int8 {
}
// HomeRange returns the start and end space of the provided player's home board.
func HomeRange(player int8) (from int8, to int8) {
if player == 2 {
func HomeRange(player int8, variant int8) (from int8, to int8) {
if player == 2 || variant == VariantTabula {
return 24, 19
}
return 1, 6
@ -74,7 +74,7 @@ func RollForMove(from int8, to int8, player int8, variant int8) int8 {
// Handle moves with special 'from' space.
if from == SpaceBarPlayer {
if player == 2 {
if player == 2 && variant != VariantTabula {
return 25 - to
} else {
return to
@ -83,25 +83,6 @@ func RollForMove(from int8, to int8, player int8, variant int8) int8 {
return 0
}
// CanBearOff returns whether the provided player can bear checkers off of the board.
func CanBearOff(board []int8, player int8, local bool) bool {
if PlayerCheckers(board[SpaceBarPlayer], player) > 0 || PlayerCheckers(board[SpaceBarOpponent], player) > 0 {
return false
}
homeStart, homeEnd := int8(1), int8(6)
if !local {
homeStart, homeEnd = HomeRange(player)
homeStart, homeEnd = minInt(homeStart, homeEnd), maxInt(homeStart, homeEnd)
}
for i := int8(1); i <= 24; i++ {
if (i < homeStart || i > homeEnd) && PlayerCheckers(board[i], player) > 0 {
return false
}
}
return true
}
func ParseSpace(space string) int8 {
i, err := strconv.Atoi(space)
if err != nil {

View File

@ -80,6 +80,7 @@ type EventRolled struct {
Event
Roll1 int8
Roll2 int8
Roll3 int8 // Used in tabula games.
Selected bool // Whether the roll is selected by the player (used in acey-deucey games).
}

256
game.go
View File

@ -30,10 +30,13 @@ type Game struct {
Variant int8 // 0 - Backgammon, 1 - Acey-deucey, 2 - Tabula.
Board []int8
Turn int8
Roll1 int8
Roll2 int8
Moves [][]int8 // Pending moves.
Winner int8
Roll1 int8
Roll2 int8
Roll3 int8 // Used in tabula games.
Moves [][]int8 // Pending moves.
Winner int8
Points int8 // Points required to win the match.
DoubleValue int8 // Doubling cube value.
@ -42,7 +45,8 @@ type Game struct {
Reroll bool // Used in acey-deucey.
boardStates [][]int8 // One board state for each move to allow undoing a move.
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.
@ -80,6 +84,7 @@ func (g *Game) Copy() *Game {
Turn: g.Turn,
Roll1: g.Roll1,
Roll2: g.Roll2,
Roll3: g.Roll3,
Moves: make([][]int8, len(g.Moves)),
Winner: g.Winner,
@ -90,11 +95,13 @@ func (g *Game) Copy() *Game {
Reroll: g.Reroll,
boardStates: make([][]int8, len(g.boardStates)),
boardStates: make([][]int8, len(g.boardStates)),
enteredStates: make([][2]bool, len(g.enteredStates)),
}
copy(newGame.Board, g.Board)
copy(newGame.Moves, g.Moves)
copy(newGame.boardStates, g.boardStates)
copy(newGame.enteredStates, g.enteredStates)
return newGame
}
@ -103,16 +110,6 @@ func (g *Game) NextTurn(replay bool) {
return
}
// Check whether the players have finished entering the board.
if g.Variant != VariantBackgammon {
if !g.Player1.Entered && PlayerCheckers(g.Board[SpaceHomePlayer], 1) == 0 {
g.Player1.Entered = true
}
if !g.Player2.Entered && PlayerCheckers(g.Board[SpaceHomeOpponent], 2) == 0 {
g.Player2.Entered = true
}
}
if !replay {
var nextTurn int8 = 1
if g.Turn == 1 {
@ -121,9 +118,10 @@ func (g *Game) NextTurn(replay bool) {
g.Turn = nextTurn
}
g.Roll1, g.Roll2 = 0, 0
g.Roll1, g.Roll2, g.Roll3 = 0, 0, 0
g.Moves = g.Moves[:0]
g.boardStates = g.boardStates[:0]
g.enteredStates = g.enteredStates[:0]
}
func (g *Game) Reset() {
@ -135,12 +133,14 @@ func (g *Game) Reset() {
g.Turn = 0
g.Roll1 = 0
g.Roll2 = 0
g.Roll3 = 0
g.Moves = nil
g.DoubleValue = 1
g.DoublePlayer = 0
g.DoubleOffered = false
g.Reroll = false
g.boardStates = nil
g.enteredStates = nil
}
func (g *Game) turnPlayer() Player {
@ -161,6 +161,50 @@ func (g *Game) opponentPlayer() Player {
}
}
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
}
}
func (g *Game) addMove(move []int8) bool {
opponentCheckers := OpponentCheckers(g.Board[move[1]], g.Turn)
if opponentCheckers > 1 {
@ -175,6 +219,7 @@ func (g *Game) addMove(move []int8) bool {
boardState := make([]int8, len(g.Board))
copy(boardState, g.Board)
g.boardStates = append(g.boardStates, boardState)
g.enteredStates = append(g.enteredStates, [2]bool{g.Player1.Entered, g.Player2.Entered})
g.Board[move[0]] -= delta
if opponentCheckers == 1 { // Hit checker.
@ -191,6 +236,7 @@ func (g *Game) addMove(move []int8) bool {
}
g.Moves = append(g.Moves, []int8{move[0], move[1]})
g.setEntered()
return true
}
@ -317,8 +363,10 @@ ADDMOVES:
gameMove := gameCopy.Moves[i]
if move[0] == gameMove[1] && move[1] == gameMove[0] {
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]
gameCopy.Moves = gameCopy.Moves[:i]
continue
}
@ -328,7 +376,9 @@ ADDMOVES:
g.Board = append(g.Board[:0], gameCopy.Board...)
g.Moves = gameCopy.Moves
g.Player1.Entered, g.Player2.Entered = gameCopy.Player1.Entered, gameCopy.Player2.Entered
g.boardStates = gameCopy.boardStates
g.enteredStates = gameCopy.enteredStates
if checkWin {
entered := g.Player1.Entered
@ -369,12 +419,22 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
g.Roll1,
g.Roll2,
}
if g.Roll1 == g.Roll2 { // Rolled doubles.
if g.Variant == VariantTabula {
rolls = append(rolls, g.Roll3)
} else if g.Roll1 == g.Roll2 {
rolls = append(rolls, g.Roll1, g.Roll2)
}
haveDiceRoll := func(from, 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 rolls {
if roll == diff {
@ -385,6 +445,9 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
}
haveBearOffDiceRoll := func(diff int8) int8 {
if diff == 0 {
return 0
}
var c int8
for _, roll := range rolls {
if roll == diff || (roll > diff && g.Variant == VariantBackgammon) {
@ -397,7 +460,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
useDiceRoll := func(from, to int8) bool {
if to == SpaceHomePlayer || to == SpaceHomeOpponent {
needRoll := from
if to == SpaceHomeOpponent {
if to == SpaceHomeOpponent || g.Variant == VariantTabula {
needRoll = 25 - from
}
for i, roll := range rolls {
@ -444,9 +507,12 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
barSpace = SpaceBarOpponent
}
if mustEnter { // Must enter from bar.
from, to := HomeRange(g.opponentPlayer().Number)
from, to := HomeRange(g.opponentPlayer().Number, g.Variant)
if g.Variant == VariantTabula {
from, to = 1, 6
}
IterateSpaces(from, to, g.Variant, func(homeSpace int8, spaceCount int8) {
if movesFound[barSpace*100+homeSpace] {
if false && movesFound[barSpace*100+homeSpace] {
return
}
available := haveDiceRoll(barSpace, homeSpace)
@ -460,7 +526,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
}
})
} else {
canBearOff := CanBearOff(g.Board, g.Turn, false)
mayBearOff := g.MayBearOff(g.Turn, false)
for sp := range g.Board {
space := int8(sp)
if space == SpaceBarPlayer || space == SpaceBarOpponent { // Handled above.
@ -483,20 +549,20 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
continue
}
if canBearOff {
if mayBearOff {
homeSpace := SpaceHomePlayer
if g.Turn == 2 {
homeSpace = SpaceHomeOpponent
}
if movesFound[space*100+homeSpace] {
if false && movesFound[space*100+homeSpace] {
continue
}
available := haveBearOffDiceRoll(SpaceDiff(space, homeSpace, g.Variant))
if available > 0 {
ok := true
if haveDiceRoll(space, homeSpace) == 0 {
_, homeEnd := HomeRange(g.Turn)
if g.Turn == 2 {
if g.Variant == VariantBackgammon && haveDiceRoll(space, homeSpace) == 0 {
_, homeEnd := HomeRange(g.Turn, g.Variant)
if g.Turn == 2 && g.Variant != VariantTabula {
for homeSpace := space - 1; homeSpace >= homeEnd; homeSpace-- {
if PlayerCheckers(g.Board[homeSpace], g.Turn) != 0 {
ok = false
@ -520,13 +586,13 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
}
// Move normally.
var lastSpace int8 = 1
if g.Turn == 2 {
lastSpace = 24
var lastSpace int8 = 0
if g.Turn == 2 || g.Variant == VariantTabula {
lastSpace = 25
}
f := func(to int8, spaceCount int8) {
if movesFound[space*100+to] {
if false && movesFound[space*100+to] {
return
}
available := haveDiceRoll(space, to)
@ -541,7 +607,11 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
}
}
if space == SpaceHomePlayer {
IterateSpaces(25, lastSpace, g.Variant, f)
iterateSpace := int8(25)
if g.Variant == VariantTabula {
iterateSpace = 1
}
IterateSpaces(iterateSpace, lastSpace, g.Variant, f)
} else if space == SpaceHomeOpponent {
IterateSpaces(1, lastSpace, g.Variant, f)
} else {
@ -587,9 +657,50 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
moves = newMoves
}
replaceSpace := func(i int8) int8 {
if g.Turn == 1 && i == SpaceHomeOpponent {
return SpaceHomePlayer
} else if g.Turn == 1 && i == SpaceBarOpponent {
return SpaceBarPlayer
} else if g.Turn == 2 && i == SpaceHomePlayer {
return SpaceHomeOpponent
} else if g.Turn == 2 && i == SpaceBarPlayer {
return SpaceBarOpponent
}
return i
}
for i := range moves {
for j := range moves[i] {
moves[i][j] = replaceSpace(moves[i][j])
}
}
return moves
}
// 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
}
func (g *Game) RenderSpace(player int8, space int8, spaceValue int8, legalMoves [][]int8) []byte {
var playerColor = "x"
var opponentColor = "o"
@ -778,6 +889,9 @@ func (g *Game) BoardState(player int8, local bool) []byte {
} else if g.Turn != player {
if g.Roll1 > 0 {
t.Write([]byte(fmt.Sprintf(" %d %d ", g.Roll1, g.Roll2)))
if g.Roll3 != 0 {
t.Write([]byte(fmt.Sprintf("%d ", g.Roll3)))
}
} else if opponentName != "" {
t.Write([]byte(" - - "))
}
@ -794,6 +908,9 @@ func (g *Game) BoardState(player int8, local bool) []byte {
} else if g.Turn == player {
if g.Roll1 > 0 {
t.Write([]byte(fmt.Sprintf(" %d %d ", g.Roll1, g.Roll2)))
if g.Roll3 != 0 {
t.Write([]byte(fmt.Sprintf("%d ", g.Roll3)))
}
} else if playerName != "" {
t.Write([]byte(" - - "))
}
@ -824,49 +941,57 @@ func (g *Game) BoardState(player int8, local bool) []byte {
}
func SpaceDiff(from int8, to int8, variant int8) int8 {
if from < 0 || from > 27 || to < 0 || to > 27 {
switch {
case from < 0 || from > 27 || to < 0 || to > 27:
return 0
} else if to == SpaceBarPlayer || to == SpaceBarOpponent {
case to == SpaceBarPlayer || to == SpaceBarOpponent:
return 0
} else if from == SpaceHomePlayer || from == SpaceHomeOpponent {
if variant != VariantBackgammon {
case (from == SpaceBarPlayer || from == SpaceBarOpponent) && (to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomePlayer || to == SpaceHomeOpponent):
return 0
case to == SpaceHomePlayer:
if variant == VariantTabula {
return 25 - from
}
return from
case to == SpaceHomeOpponent:
return 25 - from
case from == SpaceHomePlayer || from == SpaceHomeOpponent:
switch variant {
case VariantAceyDeucey:
if from == SpaceHomePlayer {
return 25 - to
} else {
return to
}
case VariantTabula:
return to
}
return 0
}
if (from == SpaceBarPlayer || from == SpaceBarOpponent) && (to == SpaceBarPlayer || to == SpaceBarOpponent || to == SpaceHomePlayer || to == SpaceHomeOpponent) {
return 0
}
if from == SpaceBarPlayer {
case from == SpaceBarPlayer:
if variant == VariantTabula {
return to
}
return 25 - to
} else if from == SpaceBarOpponent {
case from == SpaceBarOpponent:
return to
default:
diff := to - from
if diff < 0 {
return diff * -1
}
return diff
}
if to == SpaceHomePlayer {
return from
} else if to == SpaceHomeOpponent {
return 25 - from
}
diff := to - from
if diff < 0 {
return diff * -1
}
return diff
}
func IterateSpaces(from int8, to int8, variant int8, f func(space int8, spaceCount int8)) {
if from == to || from < 0 || from > 25 || to < 0 || to > 25 {
return
} else if variant == VariantBackgammon && (from == 0 || from == 25 || to == 0 || to == 25) {
return
} else if variant == VariantBackgammon {
if from == 0 {
from = 1
} else if from == 25 {
from = 24
}
}
var i int8 = 1
if to > from {
@ -910,7 +1035,7 @@ func OpponentCheckers(checkers int8, player int8) int8 {
}
}
func FlipSpace(space int8, player int8) int8 {
func FlipSpace(space int8, player int8, variant int8) int8 {
if player == 1 {
return space
}
@ -928,13 +1053,16 @@ func FlipSpace(space int8, player int8) int8 {
return -1
}
}
if variant == VariantTabula {
return space
}
return 24 - space + 1
}
func FlipMoves(moves [][]int8, player int8) [][]int8 {
func FlipMoves(moves [][]int8, player int8, variant int8) [][]int8 {
m := make([][]int8, len(moves))
for i := range moves {
m[i] = []int8{FlipSpace(moves[i][0], player), FlipSpace(moves[i][1], player)}
m[i] = []int8{FlipSpace(moves[i][0], player, variant), FlipSpace(moves[i][1], player, variant)}
}
return m
}
@ -965,8 +1093,8 @@ func FormatMoves(moves [][]int8) []byte {
return out.Bytes()
}
func FormatAndFlipMoves(moves [][]int8, player int8) []byte {
return FormatMoves(FlipMoves(moves, player))
func FormatAndFlipMoves(moves [][]int8, player int8, variant int8) []byte {
return FormatMoves(FlipMoves(moves, player, variant))
}
func ValidSpace(space int8) bool {

View File

@ -116,11 +116,15 @@ func (c *serverClient) sendEvent(e interface{}) {
case *bgammon.EventLeft:
c.Write([]byte(fmt.Sprintf("left %s", ev.Player)))
case *bgammon.EventRolled:
c.Write([]byte(fmt.Sprintf("rolled %s %d %d", ev.Player, ev.Roll1, ev.Roll2)))
msg := []byte(fmt.Sprintf("rolled %s %d %d", ev.Player, ev.Roll1, ev.Roll2))
if ev.Roll3 != 0 {
msg = append(msg, []byte(fmt.Sprintf(" %d", ev.Roll3))...)
}
c.Write(msg)
case *bgammon.EventFailedRoll:
c.Write([]byte(fmt.Sprintf("failedroll %s", ev.Reason)))
case *bgammon.EventMoved:
c.Write([]byte(fmt.Sprintf("moved %s %s", ev.Player, bgammon.FormatAndFlipMoves(ev.Moves, c.playerNumber))))
c.Write([]byte(fmt.Sprintf("moved %s %s", ev.Player, bgammon.FormatAndFlipMoves(ev.Moves, c.playerNumber, bgammon.VariantBackgammon))))
case *bgammon.EventFailedMove:
c.Write([]byte(fmt.Sprintf("failedmove %d/%d %s", ev.From, ev.To, ev.Reason)))
case *bgammon.EventFailedOk:

View File

@ -74,6 +74,9 @@ func (g *serverGame) roll(player int8) bool {
g.Roll1 = int8(RandInt(6) + 1)
g.Roll2 = int8(RandInt(6) + 1)
if g.Variant == bgammon.VariantTabula {
g.Roll3 = int8(RandInt(6) + 1)
}
return true
}
@ -126,17 +129,21 @@ func (g *serverGame) sendBoard(client *serverClient) {
}
// Flip board.
for space := int8(1); space <= 24; space++ {
ev.Board[space] = g.Game.Board[bgammon.FlipSpace(space, client.playerNumber)] * -1
if g.Variant == bgammon.VariantTabula {
for space := int8(1); space <= 24; space++ {
ev.Board[space] = g.Game.Board[space] * -1
}
} else {
for space := int8(1); space <= 24; space++ {
ev.Board[space] = g.Game.Board[bgammon.FlipSpace(space, client.playerNumber, g.Variant)] * -1
}
}
ev.Board[bgammon.SpaceHomePlayer], ev.Board[bgammon.SpaceHomeOpponent] = ev.Board[bgammon.SpaceHomeOpponent]*-1, ev.Board[bgammon.SpaceHomePlayer]*-1
ev.Board[bgammon.SpaceBarPlayer], ev.Board[bgammon.SpaceBarOpponent] = ev.Board[bgammon.SpaceBarOpponent]*-1, ev.Board[bgammon.SpaceBarPlayer]*-1
ev.Moves = bgammon.FlipMoves(g.Game.Moves, client.playerNumber)
legalMoves := g.LegalMoves(false)
ev.Moves = bgammon.FlipMoves(g.Game.Moves, client.playerNumber, g.Variant)
ev.GameState.Available = g.LegalMoves(false)
for i := range ev.GameState.Available {
ev.GameState.Available[i][0], ev.GameState.Available[i][1] = bgammon.FlipSpace(legalMoves[i][0], client.playerNumber), bgammon.FlipSpace(legalMoves[i][1], client.playerNumber)
ev.GameState.Available[i][0], ev.GameState.Available[i][1] = bgammon.FlipSpace(ev.GameState.Available[i][0], client.playerNumber, g.Variant), bgammon.FlipSpace(ev.GameState.Available[i][1], client.playerNumber, g.Variant)
}
}

View File

@ -112,6 +112,25 @@ func NewServer(tz string, dataSource string, mailServer string, passwordSalt str
allowDebugCommands = allowDebug
/*gm := bgammon.NewGame(bgammon.VariantBackgammon)
gm.Turn = 1
gm.Roll1 = 2
gm.Roll2 = 3
log.Println(gm.MayBearOff(1, false))
gm.Player1.Entered = true
gm.Player2.Entered = true
log.Println(gm.Board)
//ok, expanded := gm.AddMoves([][]int8{{3, 1}}, false)
//log.Println(ok, expanded, "!")
log.Println(gm.MayBearOff(1, false))
gs := &bgammon.GameState{
Game: gm,
PlayerNumber: 1,
Available: gm.LegalMoves(false),
}
log.Printf("%+v", gs)
os.Exit(0)*/
go s.handleNewGameIDs()
go s.handleNewClientIDs()
go s.handleCommands()

View File

@ -637,6 +637,7 @@ COMMANDS:
ev := &bgammon.EventRolled{
Roll1: clientGame.Roll1,
Roll2: clientGame.Roll2,
Roll3: clientGame.Roll3,
}
ev.Player = string(cmd.client.name)
if clientGame.Turn == 0 && client.playerNumber == 2 {
@ -651,12 +652,13 @@ COMMANDS:
clientGame.Roll1 = 0
clientGame.Roll2 = 0
if !clientGame.roll(clientGame.Turn) {
log.Fatal("failed to re-roll while starting acey-deucey game")
log.Fatal("failed to re-roll while starting game")
}
ev := &bgammon.EventRolled{
Roll1: clientGame.Roll1,
Roll2: clientGame.Roll2,
Roll3: clientGame.Roll3,
}
ev.Player = string(clientGame.Player1.Name)
if clientGame.Turn == 2 {
@ -789,7 +791,6 @@ COMMANDS:
sendUsage()
continue COMMANDS
}
if !bgammon.ValidSpace(from) || !bgammon.ValidSpace(to) {
cmd.client.sendEvent(&bgammon.EventFailedMove{
From: from,
@ -799,7 +800,7 @@ COMMANDS:
continue COMMANDS
}
from, to = bgammon.FlipSpace(from, cmd.client.playerNumber), bgammon.FlipSpace(to, cmd.client.playerNumber)
from, to = bgammon.FlipSpace(from, cmd.client.playerNumber, clientGame.Variant), bgammon.FlipSpace(to, cmd.client.playerNumber, clientGame.Variant)
moves = append(moves, []int8{from, to})
}
@ -828,7 +829,7 @@ COMMANDS:
backgammon := bgammon.PlayerCheckers(clientGame.Board[playerBar], opponent) != 0
if !backgammon {
homeStart, homeEnd := bgammon.HomeRange(clientGame.Winner)
homeStart, homeEnd := bgammon.HomeRange(clientGame.Winner, clientGame.Variant)
bgammon.IterateSpaces(homeStart, homeEnd, clientGame.Variant, func(space int8, spaceCount int8) {
if bgammon.PlayerCheckers(clientGame.Board[space], opponent) != 0 {
backgammon = true
@ -859,15 +860,26 @@ COMMANDS:
clientGame.replay = append([][]byte{[]byte(fmt.Sprintf("i %d %s %s %d %d %d %d %d %d", clientGame.Started.Unix(), clientGame.Player1.Name, clientGame.Player2.Name, clientGame.Points, clientGame.Player1.Points, clientGame.Player2.Points, clientGame.Winner, winPoints, clientGame.Variant))}, clientGame.replay...)
r1, r2 := clientGame.Roll1, clientGame.Roll2
r1, r2, r3 := clientGame.Roll1, clientGame.Roll2, clientGame.Roll3
if r2 > r1 {
r1, r2 = r2, r1
}
if r3 > r1 {
r1, r3 = r3, r1
}
if r3 > r2 {
r2, r3 = r3, r2
}
var movesFormatted []byte
if len(clientGame.Moves) != 0 {
movesFormatted = append([]byte(" "), bgammon.FormatMoves(clientGame.Moves)...)
}
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d r %d-%d%s", clientGame.Turn, r1, r2, movesFormatted)))
line := []byte(fmt.Sprintf("%d r %d-%d", clientGame.Turn, r1, r2))
if r3 > 0 {
line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
}
line = append(line, movesFormatted...)
clientGame.replay = append(clientGame.replay, line)
winEvent = &bgammon.EventWin{
Points: winPoints * clientGame.DoubleValue,
@ -913,7 +925,7 @@ COMMANDS:
clientGame.eachClient(func(client *serverClient) {
ev := &bgammon.EventMoved{
Moves: bgammon.FlipMoves(expandedMoves, client.playerNumber),
Moves: bgammon.FlipMoves(expandedMoves, client.playerNumber, clientGame.Variant),
}
ev.Player = string(cmd.client.name)
client.sendEvent(ev)
@ -952,7 +964,7 @@ COMMANDS:
} else {
clientGame.eachClient(func(client *serverClient) {
ev := &bgammon.EventMoved{
Moves: bgammon.FlipMoves(undoMoves, client.playerNumber),
Moves: bgammon.FlipMoves(undoMoves, client.playerNumber, clientGame.Variant),
}
ev.Player = string(cmd.client.name)
@ -1009,7 +1021,7 @@ COMMANDS:
legalMoves := clientGame.LegalMoves(false)
if len(legalMoves) != 0 {
available := bgammon.FlipMoves(legalMoves, cmd.client.playerNumber)
available := bgammon.FlipMoves(legalMoves, cmd.client.playerNumber, clientGame.Variant)
bgammon.SortMoves(available)
cmd.client.sendEvent(&bgammon.EventFailedOk{
Reason: fmt.Sprintf("The following legal moves are available: %s", bgammon.FormatMoves(available)),
@ -1018,15 +1030,26 @@ COMMANDS:
}
recordEvent := func() {
r1, r2 := clientGame.Roll1, clientGame.Roll2
r1, r2, r3 := clientGame.Roll1, clientGame.Roll2, clientGame.Roll3
if r2 > r1 {
r1, r2 = r2, r1
}
if r3 > r1 {
r1, r3 = r3, r1
}
if r3 > r2 {
r2, r3 = r3, r2
}
var movesFormatted []byte
if len(clientGame.Moves) != 0 {
movesFormatted = append([]byte(" "), bgammon.FormatMoves(clientGame.Moves)...)
}
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d r %d-%d%s", clientGame.Turn, r1, r2, movesFormatted)))
line := []byte(fmt.Sprintf("%d r %d-%d", clientGame.Turn, r1, r2))
if r3 > 0 {
line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
}
line = append(line, movesFormatted...)
clientGame.replay = append(clientGame.replay, line)
}
if clientGame.Variant == bgammon.VariantAceyDeucey && ((clientGame.Roll1 == 1 && clientGame.Roll2 == 2) || (clientGame.Roll1 == 2 && clientGame.Roll2 == 1)) && len(clientGame.Moves) == 2 {
@ -1094,6 +1117,7 @@ COMMANDS:
ev := &bgammon.EventRolled{
Roll1: clientGame.Roll1,
Roll2: clientGame.Roll2,
Roll3: clientGame.Roll3,
}
if clientGame.Turn == 1 {
ev.Player = gameState.Player1.Name
@ -1314,8 +1338,14 @@ COMMANDS:
clientGame.Turn = 1
clientGame.Roll1 = 6
clientGame.Roll2 = 6
clientGame.Board = []int8{7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -7, -7, 0, 0}
clientGame.Roll2 = 1
clientGame.Roll3 = 1
clientGame.Variant = 2
clientGame.Player1.Entered = true
clientGame.Player2.Entered = true
clientGame.Board = []int8{0, 0, 0, 0, 0, -3, 0, 0, -3, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 9, 3, 1, -5, 1, 1, 0}
log.Println(clientGame.Board[0:28])
clientGame.eachClient(func(client *serverClient) {
clientGame.sendBoard(client)