Require players to make the maximum possible moves
This commit is contained in:
parent
433ba94719
commit
d735899304
3 changed files with 121 additions and 42 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
96
game.go
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue