Rename parameter acey as variant, allowing additional variants to be supported
This commit is contained in:
parent
469865395f
commit
0892030f73
9 changed files with 174 additions and 215 deletions
16
board.go
16
board.go
|
@ -1,6 +1,7 @@
|
|||
package bgammon
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -21,15 +22,18 @@ const BoardSpaces = 28
|
|||
// player 2's checkers. The board's space numbering is always from the
|
||||
// perspective of the current player (i.e. the 1 space will always be in the
|
||||
// current player's home board).
|
||||
func NewBoard(acey bool) []int8 {
|
||||
func NewBoard(variant int8) []int8 {
|
||||
space := make([]int8, BoardSpaces)
|
||||
if acey {
|
||||
space[SpaceHomePlayer], space[SpaceHomeOpponent] = 15, -15
|
||||
} else {
|
||||
switch variant {
|
||||
case VariantBackgammon:
|
||||
space[24], space[1] = 2, -2
|
||||
space[19], space[6] = -5, 5
|
||||
space[17], space[8] = -3, 3
|
||||
space[13], space[12] = 5, -5
|
||||
case VariantAceyDeucey, VariantTabula:
|
||||
space[SpaceHomePlayer], space[SpaceHomeOpponent] = 15, -15
|
||||
default:
|
||||
log.Panicf("failed to initialize board: unknown variant: %d", variant)
|
||||
}
|
||||
return space
|
||||
}
|
||||
|
@ -43,14 +47,14 @@ func HomeRange(player int8) (from int8, to int8) {
|
|||
}
|
||||
|
||||
// RollForMove returns the roll needed to move a checker from the provided spaces.
|
||||
func RollForMove(from int8, to int8, player int8, acey bool) int8 {
|
||||
func RollForMove(from int8, to int8, player int8, variant int8) int8 {
|
||||
if !ValidSpace(from) || !ValidSpace(to) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Handle standard moves.
|
||||
if from >= 1 && from <= 24 && to >= 1 && to <= 24 {
|
||||
return SpaceDiff(from, to, acey)
|
||||
return SpaceDiff(from, to, variant)
|
||||
}
|
||||
|
||||
playerHome := SpaceHomePlayer
|
||||
|
|
83
game.go
83
game.go
|
@ -14,6 +14,12 @@ 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-+")
|
||||
|
||||
const (
|
||||
VariantBackgammon int8 = 0
|
||||
VariantAceyDeucey int8 = 1
|
||||
VariantTabula int8 = 2
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
Started time.Time
|
||||
Ended time.Time
|
||||
|
@ -21,13 +27,13 @@ type Game struct {
|
|||
Player1 Player
|
||||
Player2 Player
|
||||
|
||||
Acey bool // Acey-deucey.
|
||||
Board []int8
|
||||
Turn int8
|
||||
Roll1 int8
|
||||
Roll2 int8
|
||||
Moves [][]int8 // Pending moves.
|
||||
Winner int8
|
||||
Variant int8 // 0 - Backgammon, 1 - Acey-deucey, 2 - Tabula.
|
||||
Board []int8
|
||||
Turn int8
|
||||
Roll1 int8
|
||||
Roll2 int8
|
||||
Moves [][]int8 // Pending moves.
|
||||
Winner int8
|
||||
|
||||
Points int8 // Points required to win the match.
|
||||
DoubleValue int8 // Doubling cube value.
|
||||
|
@ -37,20 +43,26 @@ type Game struct {
|
|||
Reroll bool // Used in acey-deucey.
|
||||
|
||||
boardStates [][]int8 // One board state for each move to allow undoing a 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.
|
||||
}
|
||||
|
||||
func NewGame(acey bool) *Game {
|
||||
func NewGame(variant int8) *Game {
|
||||
g := &Game{
|
||||
Acey: acey,
|
||||
Board: NewBoard(acey),
|
||||
Variant: variant,
|
||||
Board: NewBoard(variant),
|
||||
Player1: NewPlayer(1),
|
||||
Player2: NewPlayer(2),
|
||||
Points: 1,
|
||||
DoubleValue: 1,
|
||||
}
|
||||
if !g.Acey {
|
||||
if variant == VariantBackgammon {
|
||||
g.Player1.Entered = true
|
||||
g.Player2.Entered = true
|
||||
} else {
|
||||
// Set backwards-compatible field.
|
||||
g.Acey = true
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
@ -63,13 +75,13 @@ func (g *Game) Copy() *Game {
|
|||
Player1: g.Player1,
|
||||
Player2: g.Player2,
|
||||
|
||||
Acey: g.Acey,
|
||||
Board: make([]int8, len(g.Board)),
|
||||
Turn: g.Turn,
|
||||
Roll1: g.Roll1,
|
||||
Roll2: g.Roll2,
|
||||
Moves: make([][]int8, len(g.Moves)),
|
||||
Winner: g.Winner,
|
||||
Variant: g.Variant,
|
||||
Board: make([]int8, len(g.Board)),
|
||||
Turn: g.Turn,
|
||||
Roll1: g.Roll1,
|
||||
Roll2: g.Roll2,
|
||||
Moves: make([][]int8, len(g.Moves)),
|
||||
Winner: g.Winner,
|
||||
|
||||
Points: g.Points,
|
||||
DoubleValue: g.DoubleValue,
|
||||
|
@ -91,7 +103,8 @@ func (g *Game) NextTurn(replay bool) {
|
|||
return
|
||||
}
|
||||
|
||||
if g.Acey {
|
||||
// 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
|
||||
}
|
||||
|
@ -114,11 +127,11 @@ func (g *Game) NextTurn(replay bool) {
|
|||
}
|
||||
|
||||
func (g *Game) Reset() {
|
||||
if g.Acey {
|
||||
if g.Variant != VariantBackgammon {
|
||||
g.Player1.Entered = false
|
||||
g.Player2.Entered = false
|
||||
}
|
||||
g.Board = NewBoard(g.Acey)
|
||||
g.Board = NewBoard(g.Variant)
|
||||
g.Turn = 0
|
||||
g.Roll1 = 0
|
||||
g.Roll2 = 0
|
||||
|
@ -324,7 +337,7 @@ ADDMOVES:
|
|||
}
|
||||
|
||||
var foundChecker bool
|
||||
if g.Acey && !entered {
|
||||
if g.Variant != VariantBackgammon && !entered {
|
||||
foundChecker = true
|
||||
} else {
|
||||
for space := 1; space <= 24; space++ {
|
||||
|
@ -361,7 +374,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
}
|
||||
|
||||
haveDiceRoll := func(from, to int8) int8 {
|
||||
diff := SpaceDiff(from, to, g.Acey)
|
||||
diff := SpaceDiff(from, to, g.Variant)
|
||||
var c int8
|
||||
for _, roll := range rolls {
|
||||
if roll == diff {
|
||||
|
@ -374,7 +387,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
haveBearOffDiceRoll := func(diff int8) int8 {
|
||||
var c int8
|
||||
for _, roll := range rolls {
|
||||
if roll == diff || (roll > diff && !g.Acey) {
|
||||
if roll == diff || (roll > diff && g.Variant == VariantBackgammon) {
|
||||
c++
|
||||
}
|
||||
}
|
||||
|
@ -402,7 +415,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
return false
|
||||
}
|
||||
|
||||
diff := SpaceDiff(from, to, g.Acey)
|
||||
diff := SpaceDiff(from, to, g.Variant)
|
||||
for i, roll := range rolls {
|
||||
if roll == diff {
|
||||
rolls = append(rolls[:i], rolls[i+1:]...)
|
||||
|
@ -432,7 +445,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
}
|
||||
if mustEnter { // Must enter from bar.
|
||||
from, to := HomeRange(g.opponentPlayer().Number)
|
||||
IterateSpaces(from, to, g.Acey, func(homeSpace int8, spaceCount int8) {
|
||||
IterateSpaces(from, to, g.Variant, func(homeSpace int8, spaceCount int8) {
|
||||
if movesFound[barSpace*100+homeSpace] {
|
||||
return
|
||||
}
|
||||
|
@ -459,7 +472,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
homeSpace = SpaceHomeOpponent
|
||||
entered = g.Player2.Entered
|
||||
}
|
||||
if !g.Acey || space != homeSpace || entered {
|
||||
if g.Variant == VariantBackgammon || space != homeSpace || entered {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +491,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
if movesFound[space*100+homeSpace] {
|
||||
continue
|
||||
}
|
||||
available := haveBearOffDiceRoll(SpaceDiff(space, homeSpace, g.Acey))
|
||||
available := haveBearOffDiceRoll(SpaceDiff(space, homeSpace, g.Variant))
|
||||
if available > 0 {
|
||||
ok := true
|
||||
if haveDiceRoll(space, homeSpace) == 0 {
|
||||
|
@ -528,11 +541,11 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
}
|
||||
}
|
||||
if space == SpaceHomePlayer {
|
||||
IterateSpaces(25, lastSpace, g.Acey, f)
|
||||
IterateSpaces(25, lastSpace, g.Variant, f)
|
||||
} else if space == SpaceHomeOpponent {
|
||||
IterateSpaces(1, lastSpace, g.Acey, f)
|
||||
IterateSpaces(1, lastSpace, g.Variant, f)
|
||||
} else {
|
||||
IterateSpaces(space, lastSpace, g.Acey, f)
|
||||
IterateSpaces(space, lastSpace, g.Variant, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -810,13 +823,13 @@ func (g *Game) BoardState(player int8, local bool) []byte {
|
|||
return t.Bytes()
|
||||
}
|
||||
|
||||
func SpaceDiff(from int8, to int8, acey bool) int8 {
|
||||
func SpaceDiff(from int8, to int8, variant int8) int8 {
|
||||
if from < 0 || from > 27 || to < 0 || to > 27 {
|
||||
return 0
|
||||
} else if to == SpaceBarPlayer || to == SpaceBarOpponent {
|
||||
return 0
|
||||
} else if from == SpaceHomePlayer || from == SpaceHomeOpponent {
|
||||
if acey {
|
||||
if variant != VariantBackgammon {
|
||||
if from == SpaceHomePlayer {
|
||||
return 25 - to
|
||||
} else {
|
||||
|
@ -849,10 +862,10 @@ func SpaceDiff(from int8, to int8, acey bool) int8 {
|
|||
return diff
|
||||
}
|
||||
|
||||
func IterateSpaces(from int8, to int8, acey bool, f func(space int8, spaceCount int8)) {
|
||||
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 !acey && (from == 0 || from == 25 || to == 0 || to == 25) {
|
||||
} else if variant == VariantBackgammon && (from == 0 || from == 25 || to == 0 || to == 25) {
|
||||
return
|
||||
}
|
||||
var i int8 = 1
|
||||
|
|
|
@ -66,7 +66,7 @@ func (g *GameState) Pips(player int8) int {
|
|||
} else {
|
||||
pips += int(PlayerCheckers(g.Board[SpaceBarOpponent], player)) * 25
|
||||
}
|
||||
if g.Acey {
|
||||
if g.Variant != VariantBackgammon {
|
||||
if player == 1 && !g.Player1.Entered {
|
||||
pips += int(PlayerCheckers(g.Board[SpaceHomePlayer], player)) * 25
|
||||
} else if player == 2 && !g.Player2.Entered {
|
||||
|
@ -86,7 +86,7 @@ func (g *GameState) Pips(player int8) int {
|
|||
|
||||
// MayDouble returns whether the player may send the 'double' command.
|
||||
func (g *GameState) MayDouble() bool {
|
||||
if g.Spectating || g.Winner != 0 || g.Acey {
|
||||
if g.Spectating || g.Winner != 0 || g.Variant != VariantBackgammon {
|
||||
return false
|
||||
}
|
||||
return g.Points != 1 && g.Turn != 0 && g.Turn == g.PlayerNumber && g.Roll1 == 0 && !g.DoubleOffered && (g.DoublePlayer == 0 || g.DoublePlayer == g.PlayerNumber)
|
||||
|
@ -118,7 +118,7 @@ func (g *GameState) MayRoll() bool {
|
|||
// MayChooseRoll returns whether the player may send the 'ok' command, supplying
|
||||
// the chosen roll. This command only applies to acey-deucey games.
|
||||
func (g *GameState) MayChooseRoll() bool {
|
||||
return g.Acey && g.Turn != 0 && g.Turn == g.PlayerNumber && ((g.Roll1 == 1 && g.Roll2 == 2) || (g.Roll1 == 2 && g.Roll2 == 1))
|
||||
return g.Variant == VariantAceyDeucey && g.Turn != 0 && g.Turn == g.PlayerNumber && ((g.Roll1 == 1 && g.Roll2 == 2) || (g.Roll1 == 2 && g.Roll2 == 1))
|
||||
}
|
||||
|
||||
// MayOK returns whether the player may send the 'ok' command.
|
||||
|
|
|
@ -37,12 +37,16 @@ CREATE TABLE account (
|
|||
password text NOT NULL,
|
||||
casual_backgammon_single integer NOT NULL DEFAULT 150000,
|
||||
casual_backgammon_multi integer NOT NULL DEFAULT 150000,
|
||||
casual_acey_single integer NOT NULL DEFAULT 150000,
|
||||
casual_acey_multi integer NOT NULL DEFAULT 150000,
|
||||
casual_acey_single integer NOT NULL DEFAULT 150000,
|
||||
casual_acey_multi integer NOT NULL DEFAULT 150000,
|
||||
casual_tabula_single integer NOT NULL DEFAULT 150000,
|
||||
casual_tabula_multi integer NOT NULL DEFAULT 150000,
|
||||
rated_backgammon_single integer NOT NULL DEFAULT 150000,
|
||||
rated_backgammon_multi integer NOT NULL DEFAULT 150000,
|
||||
rated_acey_single integer NOT NULL DEFAULT 150000,
|
||||
rated_acey_multi integer NOT NULL DEFAULT 150000,
|
||||
rated_tabula_single integer NOT NULL DEFAULT 150000,
|
||||
rated_tabula_multi integer NOT NULL DEFAULT 150000,
|
||||
highlight smallint NOT NULL DEFAULT 1,
|
||||
pips smallint NOT NULL DEFAULT 1,
|
||||
moves smallint NOT NULL DEFAULT 0,
|
||||
|
@ -50,7 +54,7 @@ CREATE TABLE account (
|
|||
);
|
||||
CREATE TABLE game (
|
||||
id serial PRIMARY KEY,
|
||||
acey integer NOT NULL,
|
||||
variant integer NOT NULL,
|
||||
started bigint NOT NULL,
|
||||
ended bigint NOT NULL,
|
||||
player1 text NOT NULL,
|
||||
|
@ -440,11 +444,7 @@ func recordGameResult(g *bgammon.Game, winType int8, account1 int, account2 int,
|
|||
}
|
||||
defer tx.Commit(context.Background())
|
||||
|
||||
acey := 0
|
||||
if g.Acey {
|
||||
acey = 1
|
||||
}
|
||||
_, err = tx.Exec(context.Background(), "INSERT INTO game (acey, started, ended, player1, account1, player2, account2, points, winner, wintype, replay) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", acey, g.Started.Unix(), ended.Unix(), g.Player1.Name, account1, g.Player2.Name, account2, g.Points, g.Winner, winType, bytes.Join(replay, []byte("\n")))
|
||||
_, err = tx.Exec(context.Background(), "INSERT INTO game (variant, started, ended, player1, account1, player2, account2, points, winner, wintype, replay) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", g.Variant, g.Started.Unix(), ended.Unix(), g.Player1.Name, account1, g.Player2.Name, account2, g.Points, g.Winner, winType, bytes.Join(replay, []byte("\n")))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -462,39 +462,7 @@ func recordMatchResult(g *bgammon.Game, matchType int, account1 int, account2 in
|
|||
}
|
||||
defer tx.Commit(context.Background())
|
||||
|
||||
var columnName string
|
||||
switch matchType {
|
||||
case matchTypeCasual:
|
||||
if !g.Acey {
|
||||
if g.Points == 1 {
|
||||
columnName = "casual_backgammon_single"
|
||||
} else {
|
||||
columnName = "casual_backgammon_multi"
|
||||
}
|
||||
} else {
|
||||
if g.Points == 1 {
|
||||
columnName = "casual_acey_single"
|
||||
} else {
|
||||
columnName = "casual_acey_multi"
|
||||
}
|
||||
}
|
||||
case matchTypeRated:
|
||||
if !g.Acey {
|
||||
if g.Points == 1 {
|
||||
columnName = "rated_backgammon_single"
|
||||
} else {
|
||||
columnName = "rated_backgammon_multi"
|
||||
}
|
||||
} else {
|
||||
if g.Points == 1 {
|
||||
columnName = "rated_acey_single"
|
||||
} else {
|
||||
columnName = "rated_acey_multi"
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.Panicf("unknown match type: %d", matchType)
|
||||
}
|
||||
columnName := ratingColumn(matchType, g.Variant, g.Points != 1)
|
||||
|
||||
var rating1i int
|
||||
err = tx.QueryRow(context.Background(), "SELECT "+columnName+" FROM account WHERE id = $1", account1).Scan(&rating1i)
|
||||
|
@ -614,7 +582,7 @@ func matchHistory(username string) ([]*bgammon.HistoryMatch, error) {
|
|||
return matches, nil
|
||||
}
|
||||
|
||||
func getLeaderboard(matchType int, acey bool, multiPoint bool) (*leaderboardResult, error) {
|
||||
func getLeaderboard(matchType int, variant int8, multiPoint bool) (*leaderboardResult, error) {
|
||||
dbLock.Lock()
|
||||
defer dbLock.Unlock()
|
||||
|
||||
|
@ -624,39 +592,7 @@ func getLeaderboard(matchType int, acey bool, multiPoint bool) (*leaderboardResu
|
|||
}
|
||||
defer tx.Commit(context.Background())
|
||||
|
||||
var columnName string
|
||||
switch matchType {
|
||||
case matchTypeCasual:
|
||||
if !acey {
|
||||
if !multiPoint {
|
||||
columnName = "casual_backgammon_single"
|
||||
} else {
|
||||
columnName = "casual_backgammon_multi"
|
||||
}
|
||||
} else {
|
||||
if !multiPoint {
|
||||
columnName = "casual_acey_single"
|
||||
} else {
|
||||
columnName = "casual_acey_multi"
|
||||
}
|
||||
}
|
||||
case matchTypeRated:
|
||||
if !acey {
|
||||
if !multiPoint {
|
||||
columnName = "rated_backgammon_single"
|
||||
} else {
|
||||
columnName = "rated_backgammon_multi"
|
||||
}
|
||||
} else {
|
||||
if !multiPoint {
|
||||
columnName = "rated_acey_single"
|
||||
} else {
|
||||
columnName = "rated_acey_multi"
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.Panicf("unknown match type: %d", matchType)
|
||||
}
|
||||
columnName := ratingColumn(matchType, variant, multiPoint)
|
||||
|
||||
result := &leaderboardResult{}
|
||||
rows, err := tx.Query(context.Background(), "SELECT username, "+columnName+" FROM account ORDER BY "+columnName+" DESC LIMIT 100")
|
||||
|
@ -880,6 +816,32 @@ func botStats(name string, tz *time.Location) (*botStatsResult, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func ratingColumn(matchType int, variant int8, multiPoint bool) string {
|
||||
var columnStart = "casual_"
|
||||
if matchType == matchTypeRated {
|
||||
columnStart = "rated_"
|
||||
}
|
||||
|
||||
var columnMid string
|
||||
switch variant {
|
||||
case bgammon.VariantBackgammon:
|
||||
columnMid = "backgammon_"
|
||||
case bgammon.VariantAceyDeucey:
|
||||
columnMid = "acey_"
|
||||
case bgammon.VariantTabula:
|
||||
columnMid = "tabula_"
|
||||
default:
|
||||
log.Panicf("unknown variant: %d", variant)
|
||||
}
|
||||
|
||||
columnEnd := "single"
|
||||
if multiPoint {
|
||||
columnEnd = "multi"
|
||||
}
|
||||
|
||||
return columnStart + columnMid + columnEnd
|
||||
}
|
||||
|
||||
func midnight(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func matchHistory(username string) ([]*bgammon.HistoryMatch, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func getLeaderboard(matchType int, acey bool, multiPoint bool) (*leaderboardResult, error) {
|
||||
func getLeaderboard(matchType int, variant int8, multiPoint bool) (*leaderboardResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,13 @@ type serverGame struct {
|
|||
*bgammon.Game
|
||||
}
|
||||
|
||||
func newServerGame(id int, acey bool) *serverGame {
|
||||
func newServerGame(id int, variant int8) *serverGame {
|
||||
now := time.Now().Unix()
|
||||
return &serverGame{
|
||||
id: id,
|
||||
created: now,
|
||||
lastActive: now,
|
||||
Game: bgammon.NewGame(acey),
|
||||
Game: bgammon.NewGame(variant),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,8 +369,11 @@ func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
|
|||
}
|
||||
|
||||
name := string(g.name)
|
||||
if g.Acey {
|
||||
switch g.Variant {
|
||||
case bgammon.VariantAceyDeucey:
|
||||
name = "(Acey-deucey) " + name
|
||||
case bgammon.VariantTabula:
|
||||
name = "(Tabula) " + name
|
||||
}
|
||||
|
||||
return &bgammon.GameListing{
|
||||
|
|
|
@ -56,7 +56,7 @@ type server struct {
|
|||
statsCacheTime time.Time
|
||||
statsCacheLock sync.Mutex
|
||||
|
||||
leaderboardCache [8][]byte
|
||||
leaderboardCache [12][]byte
|
||||
leaderboardCacheTime time.Time
|
||||
leaderboardCacheLock sync.Mutex
|
||||
|
||||
|
|
|
@ -328,13 +328,18 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
var acey bool
|
||||
variant := bgammon.VariantBackgammon
|
||||
|
||||
// Backwards-compatible acey-deucey parameter. Added in v1.1.5.
|
||||
noAcey := bytes.HasPrefix(gameName, []byte("0 ")) || bytes.Equal(gameName, []byte("0"))
|
||||
yesAcey := bytes.HasPrefix(gameName, []byte("1 ")) || bytes.Equal(gameName, []byte("1"))
|
||||
if noAcey || yesAcey {
|
||||
acey = yesAcey
|
||||
// Backwards-compatible acey-deucey and tabula parameter. Acey-deucey added in v1.1.5. Tabula added in v1.2.2.
|
||||
variantNone := bytes.HasPrefix(gameName, []byte("0 ")) || bytes.Equal(gameName, []byte("0"))
|
||||
variantAcey := bytes.HasPrefix(gameName, []byte("1 ")) || bytes.Equal(gameName, []byte("1"))
|
||||
variantTabula := bytes.HasPrefix(gameName, []byte("2 ")) || bytes.Equal(gameName, []byte("2"))
|
||||
if variantNone || variantAcey || variantTabula {
|
||||
if variantAcey {
|
||||
variant = bgammon.VariantAceyDeucey
|
||||
} else if variantTabula {
|
||||
variant = bgammon.VariantTabula
|
||||
}
|
||||
if len(gameName) > 1 {
|
||||
gameName = gameName[2:]
|
||||
} else {
|
||||
|
@ -358,7 +363,7 @@ COMMANDS:
|
|||
gameName = []byte(fmt.Sprintf("%s%s match", cmd.client.name, abbr))
|
||||
}
|
||||
|
||||
g := newServerGame(<-s.newGameIDs, acey)
|
||||
g := newServerGame(<-s.newGameIDs, variant)
|
||||
g.name = gameName
|
||||
g.Points = int8(points)
|
||||
g.password = gamePassword
|
||||
|
@ -545,11 +550,7 @@ COMMANDS:
|
|||
cmd.client.sendNotice("Declined double offer")
|
||||
clientGame.opponent(cmd.client).sendNotice(fmt.Sprintf("%s declined double offer.", cmd.client.name))
|
||||
|
||||
acey := 0
|
||||
if clientGame.Acey {
|
||||
acey = 1
|
||||
}
|
||||
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, clientGame.DoubleValue, acey))}, clientGame.replay...)
|
||||
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, clientGame.DoubleValue, clientGame.Variant))}, clientGame.replay...)
|
||||
|
||||
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d d %d 0", clientGame.Turn, clientGame.DoubleValue*2)))
|
||||
|
||||
|
@ -670,12 +671,12 @@ COMMANDS:
|
|||
|
||||
if clientGame.Roll1 > clientGame.Roll2 {
|
||||
clientGame.Turn = 1
|
||||
if clientGame.Acey {
|
||||
if clientGame.Variant != bgammon.VariantBackgammon {
|
||||
reroll()
|
||||
}
|
||||
} else if clientGame.Roll2 > clientGame.Roll1 {
|
||||
clientGame.Turn = 2
|
||||
if clientGame.Acey {
|
||||
if clientGame.Variant != bgammon.VariantBackgammon {
|
||||
reroll()
|
||||
}
|
||||
} else {
|
||||
|
@ -713,13 +714,13 @@ COMMANDS:
|
|||
})
|
||||
if clientGame.Roll1 > clientGame.Roll2 {
|
||||
clientGame.Turn = 1
|
||||
if clientGame.Acey {
|
||||
if clientGame.Variant != bgammon.VariantBackgammon {
|
||||
reroll()
|
||||
}
|
||||
break
|
||||
} else if clientGame.Roll2 > clientGame.Roll1 {
|
||||
clientGame.Turn = 2
|
||||
if clientGame.Acey {
|
||||
if clientGame.Variant != bgammon.VariantBackgammon {
|
||||
reroll()
|
||||
}
|
||||
break
|
||||
|
@ -828,7 +829,7 @@ COMMANDS:
|
|||
backgammon := bgammon.PlayerCheckers(clientGame.Board[playerBar], opponent) != 0
|
||||
if !backgammon {
|
||||
homeStart, homeEnd := bgammon.HomeRange(clientGame.Winner)
|
||||
bgammon.IterateSpaces(homeStart, homeEnd, clientGame.Acey, func(space int8, spaceCount int8) {
|
||||
bgammon.IterateSpaces(homeStart, homeEnd, clientGame.Variant, func(space int8, spaceCount int8) {
|
||||
if bgammon.PlayerCheckers(clientGame.Board[space], opponent) != 0 {
|
||||
backgammon = true
|
||||
}
|
||||
|
@ -836,7 +837,17 @@ COMMANDS:
|
|||
}
|
||||
|
||||
var winPoints int8
|
||||
if !clientGame.Acey {
|
||||
switch clientGame.Variant {
|
||||
case bgammon.VariantAceyDeucey:
|
||||
for space := int8(0); space < bgammon.BoardSpaces; space++ {
|
||||
if (space == bgammon.SpaceHomePlayer || space == bgammon.SpaceHomeOpponent) && opponentEntered {
|
||||
continue
|
||||
}
|
||||
winPoints += bgammon.PlayerCheckers(clientGame.Board[space], opponent)
|
||||
}
|
||||
case bgammon.VariantTabula:
|
||||
winPoints = 1
|
||||
default:
|
||||
if backgammon {
|
||||
winPoints = 3 // Award backgammon.
|
||||
} else if clientGame.Board[opponentHome] == 0 {
|
||||
|
@ -844,20 +855,9 @@ COMMANDS:
|
|||
} else {
|
||||
winPoints = 1
|
||||
}
|
||||
} else {
|
||||
for space := int8(0); space < bgammon.BoardSpaces; space++ {
|
||||
if (space == bgammon.SpaceHomePlayer || space == bgammon.SpaceHomeOpponent) && opponentEntered {
|
||||
continue
|
||||
}
|
||||
winPoints += bgammon.PlayerCheckers(clientGame.Board[space], opponent)
|
||||
}
|
||||
}
|
||||
|
||||
acey := 0
|
||||
if clientGame.Acey {
|
||||
acey = 1
|
||||
}
|
||||
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, acey))}, clientGame.replay...)
|
||||
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
|
||||
if r2 > r1 {
|
||||
|
@ -892,7 +892,7 @@ COMMANDS:
|
|||
}
|
||||
|
||||
winType := winPoints
|
||||
if clientGame.Acey {
|
||||
if clientGame.Variant != bgammon.VariantBackgammon {
|
||||
winType = 1
|
||||
}
|
||||
err := recordGameResult(clientGame.Game, winType, clientGame.client1.account, clientGame.client2.account, clientGame.replay)
|
||||
|
@ -1029,7 +1029,7 @@ COMMANDS:
|
|||
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d r %d-%d%s", clientGame.Turn, r1, r2, movesFormatted)))
|
||||
}
|
||||
|
||||
if clientGame.Acey && ((clientGame.Roll1 == 1 && clientGame.Roll2 == 2) || (clientGame.Roll1 == 2 && clientGame.Roll2 == 1)) && len(clientGame.Moves) == 2 {
|
||||
if clientGame.Variant == bgammon.VariantAceyDeucey && ((clientGame.Roll1 == 1 && clientGame.Roll2 == 2) || (clientGame.Roll1 == 2 && clientGame.Roll2 == 1)) && len(clientGame.Moves) == 2 {
|
||||
var doubles int
|
||||
if len(params) > 0 {
|
||||
doubles, _ = strconv.Atoi(string(params[0]))
|
||||
|
@ -1055,7 +1055,7 @@ COMMANDS:
|
|||
ev.Player = string(cmd.client.name)
|
||||
client.sendEvent(ev)
|
||||
})
|
||||
} else if clientGame.Acey && clientGame.Reroll {
|
||||
} else if clientGame.Variant == bgammon.VariantAceyDeucey && clientGame.Reroll {
|
||||
recordEvent()
|
||||
clientGame.NextTurn(true)
|
||||
clientGame.Roll1, clientGame.Roll2 = 0, 0
|
||||
|
@ -1125,7 +1125,7 @@ COMMANDS:
|
|||
} else if clientGame.rematch != 0 && clientGame.rematch != cmd.client.playerNumber {
|
||||
s.gamesLock.Lock()
|
||||
|
||||
newGame := newServerGame(<-s.newGameIDs, clientGame.Acey)
|
||||
newGame := newServerGame(<-s.newGameIDs, clientGame.Variant)
|
||||
newGame.name = clientGame.name
|
||||
newGame.Points = clientGame.Points
|
||||
newGame.password = clientGame.password
|
||||
|
@ -1315,7 +1315,7 @@ COMMANDS:
|
|||
clientGame.Turn = 1
|
||||
clientGame.Roll1 = 6
|
||||
clientGame.Roll2 = 6
|
||||
clientGame.Board = []int8{1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0}
|
||||
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.eachClient(func(client *serverClient) {
|
||||
clientGame.sendBoard(client)
|
||||
|
|
|
@ -19,14 +19,18 @@ func (s *server) listenWebSocket(address string) {
|
|||
m.HandleFunc("/reset/{id:[0-9]+}/{key:[A-Za-z0-9]+}", s.handleResetPassword)
|
||||
m.HandleFunc("/match/{id:[0-9]+}", s.handleMatch)
|
||||
m.HandleFunc("/matches", s.handleListMatches)
|
||||
m.HandleFunc("/leaderboard-casual-backgammon-single", s.handleLeaderboardCasualBackgammonSingle)
|
||||
m.HandleFunc("/leaderboard-casual-backgammon-multi", s.handleLeaderboardCasualBackgammonMulti)
|
||||
m.HandleFunc("/leaderboard-casual-acey-single", s.handleLeaderboardCasualAceySingle)
|
||||
m.HandleFunc("/leaderboard-casual-acey-multi", s.handleLeaderboardCasualAceyMulti)
|
||||
m.HandleFunc("/leaderboard-rated-backgammon-single", s.handleLeaderboardRatedBackgammonSingle)
|
||||
m.HandleFunc("/leaderboard-rated-backgammon-multi", s.handleLeaderboardRatedBackgammonMulti)
|
||||
m.HandleFunc("/leaderboard-rated-acey-single", s.handleLeaderboardRatedAceySingle)
|
||||
m.HandleFunc("/leaderboard-rated-acey-multi", s.handleLeaderboardRatedAceyMulti)
|
||||
m.HandleFunc("/leaderboard-casual-backgammon-single", s.handleLeaderboardFunc(matchTypeCasual, bgammon.VariantBackgammon, false))
|
||||
m.HandleFunc("/leaderboard-casual-backgammon-multi", s.handleLeaderboardFunc(matchTypeCasual, bgammon.VariantBackgammon, true))
|
||||
m.HandleFunc("/leaderboard-casual-acey-single", s.handleLeaderboardFunc(matchTypeCasual, bgammon.VariantAceyDeucey, false))
|
||||
m.HandleFunc("/leaderboard-casual-acey-multi", s.handleLeaderboardFunc(matchTypeCasual, bgammon.VariantAceyDeucey, true))
|
||||
m.HandleFunc("/leaderboard-casual-tabula-single", s.handleLeaderboardFunc(matchTypeCasual, bgammon.VariantTabula, false))
|
||||
m.HandleFunc("/leaderboard-casual-tabula-multi", s.handleLeaderboardFunc(matchTypeCasual, bgammon.VariantTabula, true))
|
||||
m.HandleFunc("/leaderboard-rated-backgammon-single", s.handleLeaderboardFunc(matchTypeRated, bgammon.VariantBackgammon, false))
|
||||
m.HandleFunc("/leaderboard-rated-backgammon-multi", s.handleLeaderboardFunc(matchTypeRated, bgammon.VariantBackgammon, true))
|
||||
m.HandleFunc("/leaderboard-rated-acey-single", s.handleLeaderboardFunc(matchTypeRated, bgammon.VariantAceyDeucey, false))
|
||||
m.HandleFunc("/leaderboard-rated-acey-multi", s.handleLeaderboardFunc(matchTypeRated, bgammon.VariantAceyDeucey, true))
|
||||
m.HandleFunc("/leaderboard-rated-tabula-single", s.handleLeaderboardFunc(matchTypeRated, bgammon.VariantTabula, false))
|
||||
m.HandleFunc("/leaderboard-rated-tabula-multi", s.handleLeaderboardFunc(matchTypeRated, bgammon.VariantTabula, true))
|
||||
m.HandleFunc("/stats", s.handlePrintDailyStats)
|
||||
m.HandleFunc("/stats-total", s.handlePrintCumulativeStats)
|
||||
m.HandleFunc("/stats-tabula", s.handlePrintTabulaStats)
|
||||
|
@ -70,7 +74,7 @@ func (s *server) cachedMatches() []byte {
|
|||
return s.gamesCache
|
||||
}
|
||||
|
||||
func (s *server) cachedLeaderboard(matchType int, acey bool, multiPoint bool) []byte {
|
||||
func (s *server) cachedLeaderboard(matchType int, variant int8, multiPoint bool) []byte {
|
||||
s.leaderboardCacheLock.Lock()
|
||||
defer s.leaderboardCacheLock.Unlock()
|
||||
|
||||
|
@ -87,8 +91,11 @@ func (s *server) cachedLeaderboard(matchType int, acey bool, multiPoint bool) []
|
|||
i = 3
|
||||
}
|
||||
}
|
||||
if acey {
|
||||
switch variant {
|
||||
case bgammon.VariantAceyDeucey:
|
||||
i += 4
|
||||
case bgammon.VariantTabula:
|
||||
i += 8
|
||||
}
|
||||
|
||||
if time.Since(s.leaderboardCacheTime) < 5*time.Minute {
|
||||
|
@ -96,14 +103,17 @@ func (s *server) cachedLeaderboard(matchType int, acey bool, multiPoint bool) []
|
|||
}
|
||||
s.leaderboardCacheTime = time.Now()
|
||||
|
||||
for j := 0; j < 2; j++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
i := 0
|
||||
var acey bool
|
||||
var v int8
|
||||
if j == 1 {
|
||||
i += 4
|
||||
acey = true
|
||||
i = 4
|
||||
v = bgammon.VariantAceyDeucey
|
||||
} else if j == 2 {
|
||||
i = 8
|
||||
v = bgammon.VariantTabula
|
||||
}
|
||||
result, err := getLeaderboard(matchTypeCasual, acey, false)
|
||||
result, err := getLeaderboard(matchTypeCasual, v, false)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get leaderboard: %s", err)
|
||||
}
|
||||
|
@ -112,7 +122,7 @@ func (s *server) cachedLeaderboard(matchType int, acey bool, multiPoint bool) []
|
|||
log.Fatalf("failed to marshal %+v: %s", result, err)
|
||||
}
|
||||
|
||||
result, err = getLeaderboard(matchTypeCasual, acey, true)
|
||||
result, err = getLeaderboard(matchTypeCasual, v, true)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get leaderboard: %s", err)
|
||||
}
|
||||
|
@ -121,7 +131,7 @@ func (s *server) cachedLeaderboard(matchType int, acey bool, multiPoint bool) []
|
|||
log.Fatalf("failed to marshal %+v: %s", result, err)
|
||||
}
|
||||
|
||||
result, err = getLeaderboard(matchTypeRated, acey, false)
|
||||
result, err = getLeaderboard(matchTypeRated, v, false)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get leaderboard: %s", err)
|
||||
}
|
||||
|
@ -130,7 +140,7 @@ func (s *server) cachedLeaderboard(matchType int, acey bool, multiPoint bool) []
|
|||
log.Fatalf("failed to marshal %+v: %s", result, err)
|
||||
}
|
||||
|
||||
result, err = getLeaderboard(matchTypeRated, acey, true)
|
||||
result, err = getLeaderboard(matchTypeRated, v, true)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get leaderboard: %s", err)
|
||||
}
|
||||
|
@ -239,44 +249,11 @@ func (s *server) handleListMatches(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(s.cachedMatches())
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardCasualBackgammonSingle(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeCasual, false, false))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardCasualBackgammonMulti(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeCasual, false, true))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardCasualAceySingle(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeCasual, true, false))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardCasualAceyMulti(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeCasual, true, true))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardRatedBackgammonSingle(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeRated, false, false))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardRatedBackgammonMulti(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeRated, false, true))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardRatedAceySingle(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeRated, true, false))
|
||||
}
|
||||
|
||||
func (s *server) handleLeaderboardRatedAceyMulti(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchTypeRated, true, true))
|
||||
func (s *server) handleLeaderboardFunc(matchType int, variant int8, multiPoint bool) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(s.cachedLeaderboard(matchType, variant, multiPoint))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) handlePrintDailyStats(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
Loading…
Reference in a new issue