Support playing tabula games
This commit is contained in:
parent
7064b0b085
commit
ce0122a594
7 changed files with 278 additions and 108 deletions
25
board.go
25
board.go
|
@ -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 {
|
||||
|
|
1
event.go
1
event.go
|
@ -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
256
game.go
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue