Require players to make the maximum possible moves

This commit is contained in:
Trevor Slocum 2023-09-10 15:01:09 -07:00
parent 433ba94719
commit d735899304
3 changed files with 121 additions and 42 deletions

View file

@ -10,14 +10,15 @@ import (
)
type serverGame struct {
id int
created int64
lastActive int64
name []byte
password []byte
client1 *serverClient
client2 *serverClient
r *rand.Rand
id int
created int64
lastActive int64
name []byte
password []byte
client1 *serverClient
client2 *serverClient
r *rand.Rand
allowedPlayers [][]byte // Only matching player names are allowed to join.
*bgammon.Game
}
@ -33,7 +34,7 @@ func newServerGame(id int) *serverGame {
}
func (g *serverGame) roll(player int) bool {
if g.Winner != 0 {
if g.client1 == nil || g.client2 == nil || g.Winner != 0 {
return false
}
@ -43,17 +44,26 @@ func (g *serverGame) roll(player int) bool {
return false
}
g.Roll1 = g.r.Intn(6) + 1
if len(g.allowedPlayers) == 0 {
g.allowedPlayers = [][]byte{g.client1.name, g.client2.name}
}
return true
} else {
if g.Roll2 != 0 {
return false
}
g.Roll2 = g.r.Intn(6) + 1
if len(g.allowedPlayers) == 0 {
g.allowedPlayers = [][]byte{g.client1.name, g.client2.name}
}
return true
}
} else if player != g.Turn || g.Roll1 != 0 || g.Roll2 != 0 {
return false
}
g.Roll1, g.Roll2 = g.r.Intn(6)+1, g.r.Intn(6)+1
return true
}
@ -120,7 +130,20 @@ func (g *serverGame) eachClient(f func(client *serverClient)) {
}
}
func (g *serverGame) addClient(client *serverClient) bool {
func (g *serverGame) addClient(client *serverClient) (bool, string) {
if len(g.allowedPlayers) > 0 {
var found bool
for _, allowed := range g.allowedPlayers {
if bytes.Equal(client.name, allowed) {
found = true
break
}
}
if !found {
return false, "Game has already started."
}
}
var playerNumber int
defer func() {
if playerNumber == 0 {
@ -171,12 +194,16 @@ func (g *serverGame) addClient(client *serverClient) bool {
playerNumber = 2
}
}
return playerNumber != 0
ok := playerNumber != 0
var reason string
if !ok {
reason = "Game is full."
}
return ok, reason
}
func (g *serverGame) removeClient(client *serverClient) {
// TODO game is considered paused when only one player is present
// once started, only the same player may join and continue the game
var playerNumber int
defer func() {
if playerNumber == 0 {

View file

@ -359,12 +359,12 @@ COMMANDS:
continue
}
if clientGame == nil {
cmd.client.sendNotice("Message not sent. You are not currently in a game.")
cmd.client.sendNotice("Message not sent: You are not currently in a game.")
continue
}
opponent := clientGame.opponent(cmd.client)
if opponent == nil {
cmd.client.sendNotice("Message not sent. There is no one else in the game.")
cmd.client.sendNotice("Message not sent: There is no one else in the game.")
continue
}
ev := &bgammon.EventSay{
@ -429,8 +429,9 @@ COMMANDS:
g := newServerGame(<-s.newGameIDs)
g.name = gameName
g.password = gamePassword
if !g.addClient(cmd.client) {
log.Panicf("failed to add client to newly created game %+v %+v", g, cmd.client)
ok, reason := g.addClient(cmd.client)
if !ok {
log.Panicf("failed to add client to newly created game %+v %+v: %s", g, cmd.client, reason)
}
s.gamesLock.Lock()
@ -500,9 +501,10 @@ COMMANDS:
continue COMMANDS
}
if !g.addClient(cmd.client) {
ok, reason := g.addClient(cmd.client)
if !ok {
cmd.client.sendEvent(&bgammon.EventFailedJoin{
Reason: "Game is full.",
Reason: reason,
})
}
s.gamesLock.Unlock()

96
game.go
View file

@ -104,9 +104,9 @@ func (g *Game) iterateSpaces(from int, to int, f func(space int, spaceCount int)
}
}
// AddMoves adds moves to the game state. Adding a backwards move will remove the equivalent existing move.
func (g *Game) AddMoves(moves [][]int) bool {
if g.Winner != 0 {
func (g *Game) addMove(move []int) bool {
opponentCheckers := OpponentCheckers(g.Board[move[1]], g.Turn)
if opponentCheckers > 1 {
return false
}
@ -115,6 +115,34 @@ func (g *Game) AddMoves(moves [][]int) bool {
delta = -1
}
boardState := make([]int, len(g.Board))
copy(boardState, g.Board)
g.boardStates = append(g.boardStates, boardState)
g.Board[move[0]] -= delta
if opponentCheckers == 1 { // Hit checker.
g.Board[move[1]] = delta
// Move opponent checker to bar.
barSpace := SpaceBarOpponent
if g.Turn == 2 {
barSpace = SpaceBarPlayer
}
g.Board[barSpace] += delta * -1
} else {
g.Board[move[1]] += delta
}
g.Moves = append(g.Moves, []int{move[0], move[1]})
return true
}
// AddMoves adds moves to the game state. Adding a backwards move will remove the equivalent existing move.
func (g *Game) AddMoves(moves [][]int) bool {
if g.Player1.Name == "" || g.Player2.Name == "" || g.Winner != 0 {
return false
}
var addMoves [][]int
var undoMoves [][]int
@ -155,32 +183,13 @@ ADDMOVES:
l := gameCopy.LegalMoves()
for _, lm := range l {
if lm[0] == move[0] && lm[1] == move[1] {
boardState := make([]int, len(gameCopy.Board))
copy(boardState, gameCopy.Board)
gameCopy.boardStates = append(gameCopy.boardStates, boardState)
gameCopy.Board[move[0]] -= delta
opponentCheckers := OpponentCheckers(gameCopy.Board[lm[1]], gameCopy.Turn)
if opponentCheckers == 1 { // Hit checker.
gameCopy.Board[move[1]] = delta
// Move opponent checker to bar.
barSpace := SpaceBarOpponent
if g.Turn == 2 {
barSpace = SpaceBarPlayer
}
gameCopy.Board[barSpace] += delta * -1
} else if opponentCheckers != 0 {
if !gameCopy.addMove(move) {
return false
} else {
gameCopy.Board[move[1]] += delta
}
if move[1] == SpaceHomePlayer || move[1] == SpaceHomeOpponent {
checkWin = true
}
gameCopy.Moves = append(gameCopy.Moves, []int{move[0], move[1]})
continue ADDMOVES
}
}
@ -372,6 +381,43 @@ func (g *Game) LegalMoves() [][]int {
}
}
// 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 []int) int
totalMoves = func(in *Game, move []int) int {
gc := in.Copy()
if !gc.addMove(move) {
log.Panicf("failed to add move %+v to game %+v", move, in)
}
maxTotal := 1
for _, m := range gc.LegalMoves() {
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 int
moveCounts := make([]int, len(moves))
for i, move := range moves {
moveCounts[i] = totalMoves(g, move)
if moveCounts[i] > maxMoves {
maxMoves = moveCounts[i]
}
}
if maxMoves > 1 {
var newMoves [][]int
for i, move := range moves {
if moveCounts[i] >= maxMoves {
newMoves = append(newMoves, move)
}
}
moves = newMoves
}
return moves
}
@ -706,6 +752,10 @@ func FormatSpace(space int) []byte {
}
func FormatMoves(moves [][]int) []byte {
if len(moves) == 0 {
return []byte("none")
}
var out bytes.Buffer
for i := range moves {
if i != 0 {