Rename parameter acey as variant, allowing additional variants to be supported

This commit is contained in:
Trevor Slocum 2024-01-07 12:37:01 -08:00
parent 469865395f
commit 0892030f73
9 changed files with 174 additions and 215 deletions

View file

@ -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
View file

@ -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

View file

@ -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.

View file

@ -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())
}

View file

@ -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
}

View file

@ -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{

View file

@ -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

View file

@ -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)

View file

@ -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) {