diff --git a/board.go b/board.go index 21051bd..711d7b0 100644 --- a/board.go +++ b/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 diff --git a/game.go b/game.go index 57e8e8e..d88bb9d 100644 --- a/game.go +++ b/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 diff --git a/gamestate.go b/gamestate.go index 835c398..b9d3e65 100644 --- a/gamestate.go +++ b/gamestate.go @@ -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. diff --git a/pkg/server/database.go b/pkg/server/database.go index b988510..0f52344 100644 --- a/pkg/server/database.go +++ b/pkg/server/database.go @@ -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()) } diff --git a/pkg/server/database_disabled.go b/pkg/server/database_disabled.go index 1735085..fcc6167 100644 --- a/pkg/server/database_disabled.go +++ b/pkg/server/database_disabled.go @@ -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 } diff --git a/pkg/server/game.go b/pkg/server/game.go index a22ceda..ccf1dd8 100644 --- a/pkg/server/game.go +++ b/pkg/server/game.go @@ -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{ diff --git a/pkg/server/server.go b/pkg/server/server.go index 651ca26..0d21a2e 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -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 diff --git a/pkg/server/server_command.go b/pkg/server/server_command.go index 5fa9550..b0c7c16 100644 --- a/pkg/server/server_command.go +++ b/pkg/server/server_command.go @@ -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) diff --git a/pkg/server/server_web.go b/pkg/server/server_web.go index 4d9cf7f..2d8e997 100644 --- a/pkg/server/server_web.go +++ b/pkg/server/server_web.go @@ -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) {