parent
118a7f78eb
commit
b77a42abd7
11 changed files with 577 additions and 278 deletions
1
event.go
1
event.go
|
@ -113,6 +113,7 @@ type EventWin struct {
|
|||
|
||||
type EventSettings struct {
|
||||
Event
|
||||
AutoPlay bool
|
||||
Highlight bool
|
||||
Pips bool
|
||||
Moves bool
|
||||
|
|
297
game.go
297
game.go
|
@ -105,12 +105,12 @@ func (g *Game) Copy() *Game {
|
|||
return newGame
|
||||
}
|
||||
|
||||
func (g *Game) NextTurn(replay bool) {
|
||||
func (g *Game) NextTurn(reroll bool) {
|
||||
if g.Winner != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !replay {
|
||||
if !reroll {
|
||||
var nextTurn int8 = 1
|
||||
if g.Turn == 1 {
|
||||
nextTurn = 2
|
||||
|
@ -410,11 +410,7 @@ ADDMOVES:
|
|||
}
|
||||
}
|
||||
|
||||
func (g *Game) LegalMoves(local bool) [][]int8 {
|
||||
if g.Winner != 0 || g.Roll1 == 0 || g.Roll2 == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) DiceRolls() []int8 {
|
||||
rolls := []int8{
|
||||
g.Roll1,
|
||||
g.Roll2,
|
||||
|
@ -425,38 +421,6 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
rolls = append(rolls, g.Roll1, g.Roll2)
|
||||
}
|
||||
|
||||
haveDiceRoll := func(from, to int8) int8 {
|
||||
if g.Variant == VariantTabula && to > 12 && to < 25 && ((g.Turn == 1 && !g.Player1.Entered) || (g.Turn == 2 && !g.Player2.Entered)) {
|
||||
return 0
|
||||
} else if (to == SpaceHomePlayer || to == SpaceHomeOpponent) && !g.MayBearOff(g.Turn, false) {
|
||||
return 0
|
||||
}
|
||||
diff := SpaceDiff(from, to, g.Variant)
|
||||
if diff == 0 {
|
||||
return 0
|
||||
}
|
||||
var c int8
|
||||
for _, roll := range rolls {
|
||||
if roll == diff {
|
||||
c++
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
haveBearOffDiceRoll := func(diff int8) int8 {
|
||||
if diff == 0 {
|
||||
return 0
|
||||
}
|
||||
var c int8
|
||||
for _, roll := range rolls {
|
||||
if roll == diff || (roll > diff && g.Variant == VariantBackgammon) {
|
||||
c++
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
useDiceRoll := func(from, to int8) bool {
|
||||
if to == SpaceHomePlayer || to == SpaceHomeOpponent {
|
||||
needRoll := from
|
||||
|
@ -494,6 +458,107 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
}
|
||||
}
|
||||
|
||||
return rolls
|
||||
}
|
||||
|
||||
func (g *Game) HaveDiceRoll(from int8, to int8) int8 {
|
||||
if g.Variant == VariantTabula && to > 12 && to < 25 && ((g.Turn == 1 && !g.Player1.Entered) || (g.Turn == 2 && !g.Player2.Entered)) {
|
||||
return 0
|
||||
} else if (to == SpaceHomePlayer || to == SpaceHomeOpponent) && !g.MayBearOff(g.Turn, false) {
|
||||
return 0
|
||||
}
|
||||
diff := SpaceDiff(from, to, g.Variant)
|
||||
if diff == 0 {
|
||||
return 0
|
||||
}
|
||||
var c int8
|
||||
for _, roll := range g.DiceRolls() {
|
||||
if roll == diff {
|
||||
c++
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (g *Game) HaveBearOffDiceRoll(diff int8) int8 {
|
||||
if diff == 0 {
|
||||
return 0
|
||||
}
|
||||
var c int8
|
||||
for _, roll := range g.DiceRolls() {
|
||||
if roll == diff || (roll > diff && g.Variant == VariantBackgammon) {
|
||||
c++
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// totalMoves tries all legal moves in a game and returns all of the possible combinations of moves that a player may make.
|
||||
func (g *Game) TotalMoves(local bool) [][][]int8 {
|
||||
var maxMoves int
|
||||
var allMoves [][][]int8
|
||||
for _, move := range g.LegalMoves(local) {
|
||||
for _, newMoves := range g._totalMoves(g.Moves, move, local) {
|
||||
if len(newMoves) > maxMoves {
|
||||
maxMoves = len(newMoves)
|
||||
} else if len(newMoves) < maxMoves {
|
||||
continue
|
||||
}
|
||||
allMoves = append(allMoves, newMoves)
|
||||
}
|
||||
}
|
||||
var newMoves [][][]int8
|
||||
for _, moves := range allMoves {
|
||||
if len(moves) == maxMoves {
|
||||
newMoves = append(newMoves, moves)
|
||||
}
|
||||
}
|
||||
return newMoves
|
||||
}
|
||||
|
||||
// totalMoves tries all legal moves in a game and returns all of the possible combinations of moves that a player may make.
|
||||
func (g *Game) _totalMoves(moves [][]int8, move []int8, local bool) [][][]int8 {
|
||||
gc := g.Copy()
|
||||
if !gc.addMove(move) {
|
||||
log.Panicf("failed to add move %+v to game %+v", move, g)
|
||||
}
|
||||
|
||||
var allMoves [][][]int8
|
||||
{
|
||||
newMoves := append([][]int8{}, moves...)
|
||||
newMoves = append(newMoves, move)
|
||||
allMoves = append(allMoves, newMoves)
|
||||
maxMoves := len(newMoves)
|
||||
for _, m := range gc.LegalMoves(local) {
|
||||
for _, newMoves := range gc._totalMoves(newMoves, m, local) {
|
||||
if len(newMoves) > maxMoves {
|
||||
maxMoves = len(newMoves)
|
||||
} else if len(newMoves) < maxMoves {
|
||||
continue
|
||||
}
|
||||
allMoves = append(allMoves, newMoves)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var newMoves [][][]int8
|
||||
TOTALMOVES:
|
||||
for _, m1 := range allMoves {
|
||||
for _, m2 := range newMoves {
|
||||
if movesEqual(m1, m2) {
|
||||
continue TOTALMOVES
|
||||
}
|
||||
}
|
||||
newMoves = append(newMoves, m1)
|
||||
}
|
||||
return allMoves
|
||||
}
|
||||
|
||||
func (g *Game) LegalMoves(local bool) [][]int8 {
|
||||
if g.Winner != 0 || g.Roll1 == 0 || g.Roll2 == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var moves [][]int8
|
||||
var movesFound = make(map[int8]bool)
|
||||
|
||||
|
@ -515,7 +580,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
if false && movesFound[barSpace*100+homeSpace] {
|
||||
return
|
||||
}
|
||||
available := haveDiceRoll(barSpace, homeSpace)
|
||||
available := g.HaveDiceRoll(barSpace, homeSpace)
|
||||
if available == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -557,10 +622,10 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
if false && movesFound[space*100+homeSpace] {
|
||||
continue
|
||||
}
|
||||
available := haveBearOffDiceRoll(SpaceDiff(space, homeSpace, g.Variant))
|
||||
available := g.HaveBearOffDiceRoll(SpaceDiff(space, homeSpace, g.Variant))
|
||||
if available > 0 {
|
||||
ok := true
|
||||
if g.Variant == VariantBackgammon && haveDiceRoll(space, homeSpace) == 0 {
|
||||
if g.Variant == VariantBackgammon && g.HaveDiceRoll(space, homeSpace) == 0 {
|
||||
_, homeEnd := HomeRange(g.Turn, g.Variant)
|
||||
if g.Turn == 2 && g.Variant != VariantTabula {
|
||||
for homeSpace := space - 1; homeSpace >= homeEnd; homeSpace-- {
|
||||
|
@ -595,7 +660,7 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
if false && movesFound[space*100+to] {
|
||||
return
|
||||
}
|
||||
available := haveDiceRoll(space, to)
|
||||
available := g.HaveDiceRoll(space, to)
|
||||
if available == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -620,29 +685,16 @@ func (g *Game) LegalMoves(local bool) [][]int8 {
|
|||
}
|
||||
}
|
||||
|
||||
// totalMoves tries all legal moves in a game and returns the maximum total number of moves that a player may consecutively make.
|
||||
var totalMoves func(in *Game, move []int8) int8
|
||||
totalMoves = func(in *Game, move []int8) int8 {
|
||||
gc := in.Copy()
|
||||
if !gc.addMove(move) {
|
||||
log.Panicf("failed to add move %+v to game %+v", move, in)
|
||||
}
|
||||
|
||||
var maxTotal int8 = 1
|
||||
for _, m := range gc.LegalMoves(local) {
|
||||
total := totalMoves(gc, m)
|
||||
if total+1 > maxTotal {
|
||||
maxTotal = total + 1
|
||||
}
|
||||
}
|
||||
return maxTotal
|
||||
}
|
||||
|
||||
// Simulate all possible moves to their final value and only allow moves that will achieve the maximum total moves.
|
||||
var maxMoves int8
|
||||
moveCounts := make([]int8, len(moves))
|
||||
for i, move := range moves {
|
||||
moveCounts[i] = totalMoves(g, move)
|
||||
var moveCount int
|
||||
allMoves := g._totalMoves(g.Moves, move, local)
|
||||
if len(allMoves) > 0 {
|
||||
moveCount = len(allMoves[0])
|
||||
}
|
||||
moveCounts[i] = int8(moveCount)
|
||||
if moveCounts[i] > maxMoves {
|
||||
maxMoves = moveCounts[i]
|
||||
}
|
||||
|
@ -1100,3 +1152,124 @@ func FormatAndFlipMoves(moves [][]int8, player int8, variant int8) []byte {
|
|||
func ValidSpace(space int8) bool {
|
||||
return space >= 0 && space <= 27
|
||||
}
|
||||
|
||||
func movesEqual(a [][]int8, b [][]int8) bool {
|
||||
l := len(a)
|
||||
if len(b) != l {
|
||||
return false
|
||||
}
|
||||
switch l {
|
||||
case 0:
|
||||
return true
|
||||
case 1:
|
||||
return a[0][0] == b[0][0] && a[0][1] == b[0][1]
|
||||
case 2:
|
||||
return (a[0][0] == b[0][0] && a[0][1] == b[0][1] && a[1][0] == b[1][0] && a[1][1] == b[1][1]) || // 1, 2
|
||||
(a[0][0] == b[1][0] && a[0][1] == b[1][1] && a[1][0] == b[0][0] && a[1][1] == b[0][1]) // 2, 1
|
||||
case 3:
|
||||
if a[0][0] == b[0][0] && a[0][1] == b[0][1] { // 1
|
||||
if (a[1][0] == b[1][0] && a[1][1] == b[1][1] && a[2][0] == b[2][0] && a[2][1] == b[2][1]) || // 2, 3
|
||||
(a[1][0] == b[2][0] && a[1][1] == b[2][1] && a[2][0] == b[1][0] && a[2][1] == b[1][1]) { // 3, 2
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[0][0] == b[1][0] && a[0][1] == b[1][1] { // 2
|
||||
if (a[1][0] == b[0][0] && a[1][1] == b[0][1] && a[2][0] == b[2][0] && a[2][1] == b[2][1]) ||
|
||||
(a[1][0] == b[2][0] && a[1][1] == b[2][1] && a[2][0] == b[0][0] && a[2][1] == b[0][1]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[0][0] == b[2][0] && a[0][1] == b[2][1] { // 3
|
||||
if (a[1][0] == b[0][0] && a[1][1] == b[0][1] && a[2][0] == b[1][0] && a[2][1] == b[1][1]) || // 1, 2
|
||||
(a[1][0] == b[1][0] && a[1][1] == b[1][1] && a[2][0] == b[0][0] && a[2][1] == b[0][1]) { // 2, 1
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case 4:
|
||||
if a[0][0] == b[0][0] && a[0][1] == b[0][1] { // 1
|
||||
if a[1][0] == b[1][0] && a[1][1] == b[1][1] { // 2
|
||||
if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) || // 3,4
|
||||
(a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) { // 4,3
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[2][0] && a[1][1] == b[2][1] { // 3
|
||||
if (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) || // 2,4
|
||||
(a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) { // 4,2
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[3][0] && a[1][1] == b[3][1] { // 4
|
||||
if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) || // 3,2
|
||||
(a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) { // 2,3
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if a[0][0] == b[1][0] && a[0][1] == b[1][1] { // 2
|
||||
if a[1][0] == b[0][0] && a[1][1] == b[0][1] { // 1
|
||||
if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) || // 3,4
|
||||
(a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) { // 4,3
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[2][0] && a[1][1] == b[2][1] { // 3
|
||||
if (a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) || // 4,1
|
||||
(a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) { // 1,4
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[3][0] && a[1][1] == b[3][1] { // 4
|
||||
if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) || // 3,1
|
||||
(a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) { // 1,3
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if a[0][0] == b[2][0] && a[0][1] == b[2][1] { // 3
|
||||
if a[1][0] == b[0][0] && a[1][1] == b[0][1] { // 1
|
||||
if (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) || // 2,4
|
||||
(a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) { // 4,2
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[1][0] && a[1][1] == b[1][1] { // 2
|
||||
if (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[3][0] && a[3][1] == b[3][1]) || // 1,4
|
||||
(a[2][0] == b[3][0] && a[2][1] == b[3][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) { // 4,1
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[3][0] && a[1][1] == b[3][1] { // 4
|
||||
if (a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) || // 2,1
|
||||
(a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) { // 1,2
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if a[0][0] == b[3][0] && a[0][1] == b[3][1] { // 4
|
||||
if a[1][0] == b[0][0] && a[1][1] == b[0][1] { // 1
|
||||
if (a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) || // 3,2
|
||||
(a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) { // 2,3
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[1][0] && a[1][1] == b[1][1] { // 2
|
||||
if (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[2][0] && a[3][1] == b[2][1]) || // 1,3
|
||||
(a[2][0] == b[2][0] && a[2][1] == b[2][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) { // 3,1
|
||||
return true
|
||||
}
|
||||
}
|
||||
if a[1][0] == b[2][0] && a[1][1] == b[2][1] { // 3
|
||||
if (a[2][0] == b[0][0] && a[2][1] == b[0][1] && a[3][0] == b[1][0] && a[3][1] == b[1][1]) || // 1,2
|
||||
(a[2][0] == b[1][0] && a[2][1] == b[1][1] && a[3][0] == b[0][0] && a[3][1] == b[0][1]) { // 2,1
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
log.Panicf("more than 4 moves were provided: %+v %+v", a, b)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ type GameState struct {
|
|||
*Game
|
||||
PlayerNumber int8
|
||||
Available [][]int8 // Legal moves.
|
||||
Forced bool // A forced move is being played automatically.
|
||||
Spectating bool
|
||||
}
|
||||
|
||||
|
@ -118,7 +119,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.Variant == VariantAceyDeucey && 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)) && len(g.Moves) == 2
|
||||
}
|
||||
|
||||
// MayOK returns whether the player may send the 'ok' command.
|
||||
|
|
2
go.mod
2
go.mod
|
@ -36,7 +36,7 @@ require (
|
|||
github.com/vanng822/css v1.0.1 // indirect
|
||||
github.com/vanng822/go-premailer v1.20.2 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -119,8 +119,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
|
|
|
@ -5,6 +5,7 @@ type account struct {
|
|||
email []byte
|
||||
username []byte
|
||||
password []byte
|
||||
autoplay bool
|
||||
highlight bool
|
||||
pips bool
|
||||
moves bool
|
||||
|
|
|
@ -20,6 +20,7 @@ type serverClient struct {
|
|||
active int64
|
||||
lastPing int64
|
||||
commands chan []byte
|
||||
autoplay bool
|
||||
playerNumber int8
|
||||
terminating bool
|
||||
bgammon.Client
|
||||
|
|
|
@ -47,6 +47,7 @@ CREATE TABLE account (
|
|||
rated_acey_multi integer NOT NULL DEFAULT 150000,
|
||||
rated_tabula_single integer NOT NULL DEFAULT 150000,
|
||||
rated_tabula_multi integer NOT NULL DEFAULT 150000,
|
||||
autoplay smallint NOT NULL DEFAULT 1,
|
||||
highlight smallint NOT NULL DEFAULT 1,
|
||||
pips smallint NOT NULL DEFAULT 1,
|
||||
moves smallint NOT NULL DEFAULT 0,
|
||||
|
@ -341,13 +342,14 @@ func loginAccount(passwordSalt string, username []byte, password []byte) (*accou
|
|||
defer tx.Commit(context.Background())
|
||||
|
||||
a := &account{}
|
||||
var highlight, pips, moves, flip int
|
||||
err = tx.QueryRow(context.Background(), "SELECT id, email, username, password, highlight, pips, moves, flip FROM account WHERE username = $1 OR email = $2", bytes.ToLower(bytes.TrimSpace(username)), bytes.ToLower(bytes.TrimSpace(username))).Scan(&a.id, &a.email, &a.username, &a.password, &highlight, &pips, &moves, &flip)
|
||||
var autoplay, highlight, pips, moves, flip int
|
||||
err = tx.QueryRow(context.Background(), "SELECT id, email, username, password, autoplay, highlight, pips, moves, flip FROM account WHERE username = $1 OR email = $2", bytes.ToLower(bytes.TrimSpace(username)), bytes.ToLower(bytes.TrimSpace(username))).Scan(&a.id, &a.email, &a.username, &a.password, &autoplay, &highlight, &pips, &moves, &flip)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
} else if len(a.password) == 0 {
|
||||
return nil, fmt.Errorf("account disabled")
|
||||
}
|
||||
a.autoplay = autoplay == 1
|
||||
a.highlight = highlight == 1
|
||||
a.pips = pips == 1
|
||||
a.moves = moves == 1
|
||||
|
|
|
@ -3,6 +3,8 @@ package server
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"code.rocket9labs.com/tslocum/bgammon"
|
||||
|
@ -39,6 +41,82 @@ func newServerGame(id int, variant int8) *serverGame {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *serverGame) playForcedMoves() bool {
|
||||
if g.Winner != 0 || len(g.Moves) != 0 || g.client1 == nil || g.client2 == nil {
|
||||
return false
|
||||
}
|
||||
rolls := g.DiceRolls()
|
||||
if len(rolls) == 0 {
|
||||
return false
|
||||
}
|
||||
var playerName string
|
||||
switch g.Turn {
|
||||
case 1:
|
||||
if !g.client1.autoplay {
|
||||
return false
|
||||
}
|
||||
playerName = g.Player1.Name
|
||||
case 2:
|
||||
if !g.client2.autoplay {
|
||||
return false
|
||||
}
|
||||
playerName = g.Player2.Name
|
||||
case 0:
|
||||
return false
|
||||
}
|
||||
allMoves := g.TotalMoves(false)
|
||||
if len(allMoves) == 0 {
|
||||
return false
|
||||
}
|
||||
var forcedMoves [][]int8
|
||||
if len(allMoves) == 1 {
|
||||
forcedMoves = allMoves[0]
|
||||
} else {
|
||||
FORCEDMOVES:
|
||||
for _, m1 := range allMoves[0] {
|
||||
for _, moves2 := range allMoves[1:] {
|
||||
var found bool
|
||||
for _, m2 := range moves2 {
|
||||
if m1[0] == m2[0] && m1[1] == m2[1] {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue FORCEDMOVES
|
||||
}
|
||||
}
|
||||
forcedMoves = append(forcedMoves, m1)
|
||||
}
|
||||
}
|
||||
if len(forcedMoves) == 0 {
|
||||
return false
|
||||
}
|
||||
g.eachClient(func(client *serverClient) {
|
||||
g.sendBoard(client, true)
|
||||
})
|
||||
for _, move := range forcedMoves {
|
||||
if g.HaveDiceRoll(move[0], move[1]) == 0 {
|
||||
break
|
||||
}
|
||||
ok, _ := g.AddMoves([][]int8{move}, false)
|
||||
if !ok {
|
||||
log.Fatalf("failed to play forced move %v: %v %v (%v) (%v)", move, forcedMoves, g.DiceRolls(), g.Game, g.Board)
|
||||
}
|
||||
g.eachClient(func(client *serverClient) {
|
||||
ev := &bgammon.EventMoved{
|
||||
Moves: bgammon.FlipMoves([][]int8{move}, client.playerNumber, g.Variant),
|
||||
}
|
||||
ev.Player = playerName
|
||||
client.sendEvent(ev)
|
||||
})
|
||||
if g.handleWin() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *serverGame) roll(player int8) bool {
|
||||
if g.client1 == nil || g.client2 == nil || g.Winner != 0 {
|
||||
return false
|
||||
|
@ -81,18 +159,17 @@ func (g *serverGame) roll(player int8) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (g *serverGame) sendBoard(client *serverClient) {
|
||||
func (g *serverGame) sendBoard(client *serverClient, forcedMove bool) {
|
||||
if client.json {
|
||||
ev := &bgammon.EventBoard{
|
||||
GameState: bgammon.GameState{
|
||||
Game: g.Game,
|
||||
PlayerNumber: client.playerNumber,
|
||||
Available: g.LegalMoves(false),
|
||||
Forced: forcedMove,
|
||||
Spectating: g.client1 != client && g.client2 != client,
|
||||
},
|
||||
}
|
||||
if g.client1 != client && g.client2 != client {
|
||||
ev.Spectating = true
|
||||
}
|
||||
|
||||
// Reverse spaces for white.
|
||||
if client.playerNumber == 2 {
|
||||
|
@ -203,7 +280,7 @@ func (g *serverGame) addClient(client *serverClient) (spectator bool) {
|
|||
}
|
||||
ev.Player = string(client.name)
|
||||
client.sendEvent(ev)
|
||||
g.sendBoard(client)
|
||||
g.sendBoard(client, false)
|
||||
return spectator
|
||||
}
|
||||
|
||||
|
@ -215,7 +292,7 @@ func (g *serverGame) addClient(client *serverClient) (spectator bool) {
|
|||
}
|
||||
ev.Player = string(client.name)
|
||||
client.sendEvent(ev)
|
||||
g.sendBoard(client)
|
||||
g.sendBoard(client, false)
|
||||
|
||||
if playerNumber == 0 {
|
||||
return
|
||||
|
@ -229,7 +306,7 @@ func (g *serverGame) addClient(client *serverClient) (spectator bool) {
|
|||
}
|
||||
ev.Player = string(client.name)
|
||||
opponent.sendEvent(ev)
|
||||
g.sendBoard(opponent)
|
||||
g.sendBoard(opponent, false)
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -240,7 +317,7 @@ func (g *serverGame) addClient(client *serverClient) (spectator bool) {
|
|||
ev.Player = string(client.name)
|
||||
for _, spectator := range g.spectators {
|
||||
spectator.sendEvent(ev)
|
||||
g.sendBoard(spectator)
|
||||
g.sendBoard(spectator, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,7 +370,7 @@ func (g *serverGame) removeClient(client *serverClient) {
|
|||
|
||||
client.sendEvent(ev)
|
||||
if !client.json {
|
||||
g.sendBoard(client)
|
||||
g.sendBoard(client, false)
|
||||
}
|
||||
|
||||
var opponent *serverClient
|
||||
|
@ -305,14 +382,14 @@ func (g *serverGame) removeClient(client *serverClient) {
|
|||
if opponent != nil {
|
||||
opponent.sendEvent(ev)
|
||||
if !opponent.json {
|
||||
g.sendBoard(opponent)
|
||||
g.sendBoard(opponent, false)
|
||||
}
|
||||
}
|
||||
|
||||
for _, spectator := range g.spectators {
|
||||
spectator.sendEvent(ev)
|
||||
if !spectator.json {
|
||||
g.sendBoard(spectator)
|
||||
g.sendBoard(spectator, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +420,7 @@ func (g *serverGame) removeClient(client *serverClient) {
|
|||
|
||||
client.sendEvent(ev)
|
||||
if !client.json {
|
||||
g.sendBoard(client)
|
||||
g.sendBoard(client, false)
|
||||
}
|
||||
|
||||
client.playerNumber = 0
|
||||
|
@ -392,6 +469,196 @@ func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
|
|||
}
|
||||
}
|
||||
|
||||
func (g *serverGame) recordEvent() {
|
||||
r1, r2, r3 := g.Roll1, g.Roll2, g.Roll3
|
||||
if r2 > r1 {
|
||||
r1, r2 = r2, r1
|
||||
}
|
||||
if r3 > r1 {
|
||||
r1, r3 = r3, r1
|
||||
}
|
||||
if r3 > r2 {
|
||||
r2, r3 = r3, r2
|
||||
}
|
||||
var movesFormatted []byte
|
||||
if len(g.Moves) != 0 {
|
||||
movesFormatted = append([]byte(" "), bgammon.FormatMoves(g.Moves)...)
|
||||
}
|
||||
line := []byte(fmt.Sprintf("%d r %d-%d", g.Turn, r1, r2))
|
||||
if r3 > 0 {
|
||||
line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
|
||||
}
|
||||
line = append(line, movesFormatted...)
|
||||
g.replay = append(g.replay, line)
|
||||
}
|
||||
|
||||
func (g *serverGame) nextTurn(reroll bool) {
|
||||
g.Game.NextTurn(reroll)
|
||||
if reroll {
|
||||
return
|
||||
}
|
||||
|
||||
// Roll automatically.
|
||||
if g.Winner == 0 {
|
||||
gameState := &bgammon.GameState{
|
||||
Game: g.Game,
|
||||
PlayerNumber: g.Turn,
|
||||
Available: g.LegalMoves(false),
|
||||
}
|
||||
if !gameState.MayDouble() {
|
||||
if !g.roll(g.Turn) {
|
||||
g.eachClient(func(client *serverClient) {
|
||||
client.Terminate("Server error")
|
||||
})
|
||||
return
|
||||
}
|
||||
ev := &bgammon.EventRolled{
|
||||
Roll1: g.Roll1,
|
||||
Roll2: g.Roll2,
|
||||
Roll3: g.Roll3,
|
||||
}
|
||||
if g.Turn == 1 {
|
||||
ev.Player = gameState.Player1.Name
|
||||
} else {
|
||||
ev.Player = gameState.Player2.Name
|
||||
}
|
||||
g.eachClient(func(client *serverClient) {
|
||||
client.sendEvent(ev)
|
||||
})
|
||||
|
||||
// Play forced moves automatically.
|
||||
forcedMove := g.playForcedMoves()
|
||||
if forcedMove && len(g.LegalMoves(false)) == 0 {
|
||||
chooseRoll := g.Variant == bgammon.VariantAceyDeucey && ((g.Roll1 == 1 && g.Roll2 == 2) || (g.Roll1 == 2 && g.Roll2 == 1)) && len(g.Moves) == 2
|
||||
if g.Variant != bgammon.VariantAceyDeucey || !chooseRoll {
|
||||
g.recordEvent()
|
||||
g.nextTurn(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.eachClient(func(client *serverClient) {
|
||||
g.sendBoard(client, false)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *serverGame) handleWin() bool {
|
||||
if g.Winner == 0 {
|
||||
return false
|
||||
}
|
||||
var opponent int8 = 1
|
||||
opponentHome := bgammon.SpaceHomePlayer
|
||||
opponentEntered := g.Player1.Entered
|
||||
playerBar := bgammon.SpaceBarPlayer
|
||||
if g.Winner == 1 {
|
||||
opponent = 2
|
||||
opponentHome = bgammon.SpaceHomeOpponent
|
||||
opponentEntered = g.Player2.Entered
|
||||
playerBar = bgammon.SpaceBarOpponent
|
||||
}
|
||||
|
||||
backgammon := bgammon.PlayerCheckers(g.Board[playerBar], opponent) != 0
|
||||
if !backgammon {
|
||||
homeStart, homeEnd := bgammon.HomeRange(g.Winner, g.Variant)
|
||||
bgammon.IterateSpaces(homeStart, homeEnd, g.Variant, func(space int8, spaceCount int8) {
|
||||
if bgammon.PlayerCheckers(g.Board[space], opponent) != 0 {
|
||||
backgammon = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var winPoints int8
|
||||
switch g.Variant {
|
||||
case bgammon.VariantAceyDeucey:
|
||||
for space := int8(0); space < bgammon.BoardSpaces; space++ {
|
||||
if (space == bgammon.SpaceHomePlayer || space == bgammon.SpaceHomeOpponent) && opponentEntered {
|
||||
continue
|
||||
}
|
||||
winPoints += bgammon.PlayerCheckers(g.Board[space], opponent)
|
||||
}
|
||||
case bgammon.VariantTabula:
|
||||
winPoints = 1
|
||||
default:
|
||||
if backgammon {
|
||||
winPoints = 3 // Award backgammon.
|
||||
} else if g.Board[opponentHome] == 0 {
|
||||
winPoints = 2 // Award gammon.
|
||||
} else {
|
||||
winPoints = 1
|
||||
}
|
||||
}
|
||||
|
||||
g.replay = append([][]byte{[]byte(fmt.Sprintf("i %d %s %s %d %d %d %d %d %d", g.Started.Unix(), g.Player1.Name, g.Player2.Name, g.Points, g.Player1.Points, g.Player2.Points, g.Winner, winPoints, g.Variant))}, g.replay...)
|
||||
|
||||
r1, r2, r3 := g.Roll1, g.Roll2, g.Roll3
|
||||
if r2 > r1 {
|
||||
r1, r2 = r2, r1
|
||||
}
|
||||
if r3 > r1 {
|
||||
r1, r3 = r3, r1
|
||||
}
|
||||
if r3 > r2 {
|
||||
r2, r3 = r3, r2
|
||||
}
|
||||
var movesFormatted []byte
|
||||
if len(g.Moves) != 0 {
|
||||
movesFormatted = append([]byte(" "), bgammon.FormatMoves(g.Moves)...)
|
||||
}
|
||||
line := []byte(fmt.Sprintf("%d r %d-%d", g.Turn, r1, r2))
|
||||
if r3 > 0 {
|
||||
line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
|
||||
}
|
||||
line = append(line, movesFormatted...)
|
||||
g.replay = append(g.replay, line)
|
||||
|
||||
winEvent := &bgammon.EventWin{
|
||||
Points: winPoints * g.DoubleValue,
|
||||
}
|
||||
var reset bool
|
||||
if g.Winner == 1 {
|
||||
winEvent.Player = g.Player1.Name
|
||||
g.Player1.Points = g.Player1.Points + winPoints*g.DoubleValue
|
||||
if g.Player1.Points < g.Points {
|
||||
reset = true
|
||||
} else {
|
||||
g.Ended = time.Now()
|
||||
}
|
||||
} else {
|
||||
winEvent.Player = g.Player2.Name
|
||||
g.Player2.Points = g.Player2.Points + winPoints*g.DoubleValue
|
||||
if g.Player2.Points < g.Points {
|
||||
reset = true
|
||||
} else {
|
||||
g.Ended = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
winType := winPoints
|
||||
if g.Variant != bgammon.VariantBackgammon {
|
||||
winType = 1
|
||||
}
|
||||
err := recordGameResult(g.Game, winType, g.client1.account, g.client2.account, g.replay)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record game result: %s", err)
|
||||
}
|
||||
|
||||
if !reset {
|
||||
err := recordMatchResult(g.Game, matchTypeCasual, g.client1.account, g.client2.account)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record match result: %s", err)
|
||||
}
|
||||
} else {
|
||||
g.Reset()
|
||||
g.replay = g.replay[:0]
|
||||
}
|
||||
g.eachClient(func(client *serverClient) {
|
||||
client.sendEvent(winEvent)
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *serverGame) terminated() bool {
|
||||
return g.client1 == nil && g.client2 == nil
|
||||
}
|
||||
|
|
|
@ -196,6 +196,7 @@ func (s *server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
|||
connected: now,
|
||||
active: now,
|
||||
commands: commands,
|
||||
autoplay: true,
|
||||
Client: wsClient,
|
||||
}
|
||||
s.handleClient(c)
|
||||
|
@ -301,6 +302,7 @@ func (s *server) handleConnection(conn net.Conn) {
|
|||
connected: now,
|
||||
active: now,
|
||||
commands: commands,
|
||||
autoplay: true,
|
||||
Client: newSocketClient(conn, commands, events, s.verbose),
|
||||
}
|
||||
s.sendHello(c)
|
||||
|
|
|
@ -167,7 +167,9 @@ COMMANDS:
|
|||
|
||||
cmd.client.account = a.id
|
||||
cmd.client.name = name
|
||||
cmd.client.autoplay = a.autoplay
|
||||
cmd.client.sendEvent(&bgammon.EventSettings{
|
||||
AutoPlay: a.autoplay,
|
||||
Highlight: a.highlight,
|
||||
Pips: a.pips,
|
||||
Moves: a.moves,
|
||||
|
@ -520,7 +522,7 @@ COMMANDS:
|
|||
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
if client.json {
|
||||
clientGame.sendBoard(client)
|
||||
clientGame.sendBoard(client, false)
|
||||
}
|
||||
})
|
||||
case bgammon.CommandResign:
|
||||
|
@ -603,7 +605,7 @@ COMMANDS:
|
|||
}
|
||||
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
clientGame.sendBoard(client)
|
||||
clientGame.sendBoard(client, false)
|
||||
if winEvent != nil {
|
||||
client.sendEvent(winEvent)
|
||||
}
|
||||
|
@ -646,7 +648,7 @@ COMMANDS:
|
|||
client.sendEvent(ev)
|
||||
})
|
||||
|
||||
var skipBoard bool
|
||||
// Re-roll automatically when players roll the same value when starting a game.
|
||||
if clientGame.Turn == 0 && clientGame.Roll1 != 0 && clientGame.Roll2 != 0 {
|
||||
reroll := func() {
|
||||
clientGame.Roll1 = 0
|
||||
|
@ -665,10 +667,8 @@ COMMANDS:
|
|||
ev.Player = string(clientGame.Player2.Name)
|
||||
}
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
clientGame.sendBoard(client)
|
||||
client.sendEvent(ev)
|
||||
})
|
||||
skipBoard = true
|
||||
}
|
||||
|
||||
if clientGame.Roll1 > clientGame.Roll2 {
|
||||
|
@ -730,13 +730,22 @@ COMMANDS:
|
|||
}
|
||||
}
|
||||
}
|
||||
if !skipBoard {
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
if clientGame.Turn != 0 || !client.json {
|
||||
clientGame.sendBoard(client)
|
||||
}
|
||||
})
|
||||
|
||||
forcedMove := clientGame.playForcedMoves()
|
||||
if forcedMove && len(clientGame.LegalMoves(false)) == 0 {
|
||||
chooseRoll := clientGame.Variant == bgammon.VariantAceyDeucey && ((clientGame.Roll1 == 1 && clientGame.Roll2 == 2) || (clientGame.Roll1 == 2 && clientGame.Roll2 == 1)) && len(clientGame.Moves) == 2
|
||||
if clientGame.Variant != bgammon.VariantAceyDeucey || !chooseRoll {
|
||||
clientGame.recordEvent()
|
||||
clientGame.nextTurn(false)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
if clientGame.Turn != 0 || !client.json {
|
||||
clientGame.sendBoard(client, false)
|
||||
}
|
||||
})
|
||||
case bgammon.CommandMove, "m", "mv":
|
||||
if clientGame == nil {
|
||||
cmd.client.sendEvent(&bgammon.EventFailedMove{
|
||||
|
@ -744,7 +753,7 @@ COMMANDS:
|
|||
})
|
||||
continue
|
||||
} else if clientGame.Winner != 0 {
|
||||
clientGame.sendBoard(cmd.client)
|
||||
clientGame.sendBoard(cmd.client, false)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -814,115 +823,6 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
var winEvent *bgammon.EventWin
|
||||
if clientGame.Winner != 0 {
|
||||
var opponent int8 = 1
|
||||
opponentHome := bgammon.SpaceHomePlayer
|
||||
opponentEntered := clientGame.Player1.Entered
|
||||
playerBar := bgammon.SpaceBarPlayer
|
||||
if clientGame.Winner == 1 {
|
||||
opponent = 2
|
||||
opponentHome = bgammon.SpaceHomeOpponent
|
||||
opponentEntered = clientGame.Player2.Entered
|
||||
playerBar = bgammon.SpaceBarOpponent
|
||||
}
|
||||
|
||||
backgammon := bgammon.PlayerCheckers(clientGame.Board[playerBar], opponent) != 0
|
||||
if !backgammon {
|
||||
homeStart, homeEnd := bgammon.HomeRange(clientGame.Winner, clientGame.Variant)
|
||||
bgammon.IterateSpaces(homeStart, homeEnd, clientGame.Variant, func(space int8, spaceCount int8) {
|
||||
if bgammon.PlayerCheckers(clientGame.Board[space], opponent) != 0 {
|
||||
backgammon = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var winPoints int8
|
||||
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 {
|
||||
winPoints = 2 // Award gammon.
|
||||
} else {
|
||||
winPoints = 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, clientGame.Variant))}, clientGame.replay...)
|
||||
|
||||
r1, r2, r3 := clientGame.Roll1, clientGame.Roll2, clientGame.Roll3
|
||||
if r2 > r1 {
|
||||
r1, r2 = r2, r1
|
||||
}
|
||||
if r3 > r1 {
|
||||
r1, r3 = r3, r1
|
||||
}
|
||||
if r3 > r2 {
|
||||
r2, r3 = r3, r2
|
||||
}
|
||||
var movesFormatted []byte
|
||||
if len(clientGame.Moves) != 0 {
|
||||
movesFormatted = append([]byte(" "), bgammon.FormatMoves(clientGame.Moves)...)
|
||||
}
|
||||
line := []byte(fmt.Sprintf("%d r %d-%d", clientGame.Turn, r1, r2))
|
||||
if r3 > 0 {
|
||||
line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
|
||||
}
|
||||
line = append(line, movesFormatted...)
|
||||
clientGame.replay = append(clientGame.replay, line)
|
||||
|
||||
winEvent = &bgammon.EventWin{
|
||||
Points: winPoints * clientGame.DoubleValue,
|
||||
}
|
||||
var reset bool
|
||||
if clientGame.Winner == 1 {
|
||||
winEvent.Player = clientGame.Player1.Name
|
||||
clientGame.Player1.Points = clientGame.Player1.Points + winPoints*clientGame.DoubleValue
|
||||
if clientGame.Player1.Points < clientGame.Points {
|
||||
reset = true
|
||||
} else {
|
||||
clientGame.Ended = time.Now()
|
||||
}
|
||||
} else {
|
||||
winEvent.Player = clientGame.Player2.Name
|
||||
clientGame.Player2.Points = clientGame.Player2.Points + winPoints*clientGame.DoubleValue
|
||||
if clientGame.Player2.Points < clientGame.Points {
|
||||
reset = true
|
||||
} else {
|
||||
clientGame.Ended = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
winType := winPoints
|
||||
if clientGame.Variant != bgammon.VariantBackgammon {
|
||||
winType = 1
|
||||
}
|
||||
err := recordGameResult(clientGame.Game, winType, clientGame.client1.account, clientGame.client2.account, clientGame.replay)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record game result: %s", err)
|
||||
}
|
||||
|
||||
if !reset {
|
||||
err := recordMatchResult(clientGame.Game, matchTypeCasual, clientGame.client1.account, clientGame.client2.account)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to record match result: %s", err)
|
||||
}
|
||||
} else {
|
||||
clientGame.Reset()
|
||||
clientGame.replay = clientGame.replay[:0]
|
||||
}
|
||||
}
|
||||
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
ev := &bgammon.EventMoved{
|
||||
Moves: bgammon.FlipMoves(expandedMoves, client.playerNumber, clientGame.Variant),
|
||||
|
@ -930,12 +830,10 @@ COMMANDS:
|
|||
ev.Player = string(cmd.client.name)
|
||||
client.sendEvent(ev)
|
||||
|
||||
clientGame.sendBoard(client)
|
||||
|
||||
if winEvent != nil {
|
||||
client.sendEvent(winEvent)
|
||||
}
|
||||
clientGame.sendBoard(client, false)
|
||||
})
|
||||
|
||||
clientGame.handleWin()
|
||||
case bgammon.CommandReset:
|
||||
if clientGame == nil {
|
||||
cmd.client.sendNotice("You are not currently in a match.")
|
||||
|
@ -969,7 +867,7 @@ COMMANDS:
|
|||
ev.Player = string(cmd.client.name)
|
||||
|
||||
client.sendEvent(ev)
|
||||
clientGame.sendBoard(client)
|
||||
clientGame.sendBoard(client, false)
|
||||
})
|
||||
}
|
||||
case bgammon.CommandOk, "k":
|
||||
|
@ -1003,7 +901,7 @@ COMMANDS:
|
|||
|
||||
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d d %d 1", clientGame.Turn, clientGame.DoubleValue)))
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
clientGame.sendBoard(client)
|
||||
clientGame.sendBoard(client, false)
|
||||
})
|
||||
} else {
|
||||
cmd.client.sendNotice("Waiting for response from opponent.")
|
||||
|
@ -1029,29 +927,6 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
recordEvent := func() {
|
||||
r1, r2, r3 := clientGame.Roll1, clientGame.Roll2, clientGame.Roll3
|
||||
if r2 > r1 {
|
||||
r1, r2 = r2, r1
|
||||
}
|
||||
if r3 > r1 {
|
||||
r1, r3 = r3, r1
|
||||
}
|
||||
if r3 > r2 {
|
||||
r2, r3 = r3, r2
|
||||
}
|
||||
var movesFormatted []byte
|
||||
if len(clientGame.Moves) != 0 {
|
||||
movesFormatted = append([]byte(" "), bgammon.FormatMoves(clientGame.Moves)...)
|
||||
}
|
||||
line := []byte(fmt.Sprintf("%d r %d-%d", clientGame.Turn, r1, r2))
|
||||
if r3 > 0 {
|
||||
line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
|
||||
}
|
||||
line = append(line, movesFormatted...)
|
||||
clientGame.replay = append(clientGame.replay, line)
|
||||
}
|
||||
|
||||
if clientGame.Variant == bgammon.VariantAceyDeucey && ((clientGame.Roll1 == 1 && clientGame.Roll2 == 2) || (clientGame.Roll1 == 2 && clientGame.Roll2 == 1)) && len(clientGame.Moves) == 2 {
|
||||
var doubles int
|
||||
if len(params) > 0 {
|
||||
|
@ -1064,8 +939,8 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
recordEvent()
|
||||
clientGame.NextTurn(true)
|
||||
clientGame.recordEvent()
|
||||
clientGame.nextTurn(true)
|
||||
clientGame.Roll1, clientGame.Roll2 = int8(doubles), int8(doubles)
|
||||
clientGame.Reroll = true
|
||||
|
||||
|
@ -1077,10 +952,11 @@ COMMANDS:
|
|||
}
|
||||
ev.Player = string(cmd.client.name)
|
||||
client.sendEvent(ev)
|
||||
clientGame.sendBoard(client, false)
|
||||
})
|
||||
} else if clientGame.Variant == bgammon.VariantAceyDeucey && clientGame.Reroll {
|
||||
recordEvent()
|
||||
clientGame.NextTurn(true)
|
||||
clientGame.recordEvent()
|
||||
clientGame.nextTurn(true)
|
||||
clientGame.Roll1, clientGame.Roll2 = 0, 0
|
||||
if !clientGame.roll(cmd.client.playerNumber) {
|
||||
cmd.client.Terminate("Server error")
|
||||
|
@ -1096,43 +972,12 @@ COMMANDS:
|
|||
}
|
||||
ev.Player = string(cmd.client.name)
|
||||
client.sendEvent(ev)
|
||||
clientGame.sendBoard(client)
|
||||
clientGame.sendBoard(client, false)
|
||||
})
|
||||
} else {
|
||||
recordEvent()
|
||||
clientGame.NextTurn(false)
|
||||
if clientGame.Winner == 0 {
|
||||
gameState := &bgammon.GameState{
|
||||
Game: clientGame.Game,
|
||||
PlayerNumber: clientGame.Turn,
|
||||
Available: clientGame.LegalMoves(false),
|
||||
}
|
||||
if !gameState.MayDouble() {
|
||||
if !clientGame.roll(clientGame.Turn) {
|
||||
cmd.client.Terminate("Server error")
|
||||
opponent.Terminate("Server error")
|
||||
continue
|
||||
}
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
ev := &bgammon.EventRolled{
|
||||
Roll1: clientGame.Roll1,
|
||||
Roll2: clientGame.Roll2,
|
||||
Roll3: clientGame.Roll3,
|
||||
}
|
||||
if clientGame.Turn == 1 {
|
||||
ev.Player = gameState.Player1.Name
|
||||
} else {
|
||||
ev.Player = gameState.Player2.Name
|
||||
}
|
||||
client.sendEvent(ev)
|
||||
})
|
||||
}
|
||||
}
|
||||
clientGame.recordEvent()
|
||||
clientGame.nextTurn(false)
|
||||
}
|
||||
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
clientGame.sendBoard(client)
|
||||
})
|
||||
case bgammon.CommandRematch, "rm":
|
||||
if clientGame == nil {
|
||||
cmd.client.sendNotice("You are not currently in a match.")
|
||||
|
@ -1184,7 +1029,7 @@ COMMANDS:
|
|||
ev2.Player = newGame.Player2.Name
|
||||
newGame.client1.sendEvent(ev1)
|
||||
newGame.client1.sendEvent(ev2)
|
||||
newGame.sendBoard(newGame.client1)
|
||||
newGame.sendBoard(newGame.client1, false)
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1200,11 +1045,11 @@ COMMANDS:
|
|||
ev2.Player = newGame.Player1.Name
|
||||
newGame.client2.sendEvent(ev1)
|
||||
newGame.client2.sendEvent(ev2)
|
||||
newGame.sendBoard(newGame.client2)
|
||||
newGame.sendBoard(newGame.client2, false)
|
||||
}
|
||||
|
||||
for _, spectator := range newGame.spectators {
|
||||
newGame.sendBoard(spectator)
|
||||
newGame.sendBoard(spectator, false)
|
||||
}
|
||||
} else {
|
||||
clientGame.rematch = cmd.client.playerNumber
|
||||
|
@ -1219,7 +1064,7 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
clientGame.sendBoard(cmd.client)
|
||||
clientGame.sendBoard(cmd.client, false)
|
||||
case bgammon.CommandPassword:
|
||||
if cmd.client.account == 0 {
|
||||
cmd.client.sendNotice("Failed to change password: you are logged in as a guest.")
|
||||
|
@ -1242,15 +1087,13 @@ COMMANDS:
|
|||
}
|
||||
cmd.client.sendNotice("Password changed successfully.")
|
||||
case bgammon.CommandSet:
|
||||
if cmd.client.account == 0 {
|
||||
continue
|
||||
} else if len(params) < 2 {
|
||||
if len(params) < 2 {
|
||||
cmd.client.sendNotice("Please specify the setting name and value as follows: set <name> <value>")
|
||||
continue
|
||||
}
|
||||
|
||||
name := string(bytes.ToLower(params[0]))
|
||||
settings := []string{"highlight", "pips", "moves", "flip"}
|
||||