Check for win condition
This commit is contained in:
parent
042a9599b8
commit
daa8bce760
8 changed files with 106 additions and 15 deletions
|
@ -131,6 +131,9 @@ provide clients with the initial game state.
|
|||
- Sent after sending `ok` when there are one or more legal moves still available to the player.
|
||||
- Players must make moves using all available dice rolls before ending their turn.
|
||||
|
||||
- `win <player:text> wins!`
|
||||
- Sent after a player bears their final checker off the board.
|
||||
|
||||
- `say <player:text> <message:line>`
|
||||
- Chat message from another player.
|
||||
|
||||
|
|
20
board.go
20
board.go
|
@ -1,6 +1,10 @@
|
|||
package bgammon
|
||||
|
||||
import "sort"
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
SpaceHomePlayer = 0 // Current player's home.
|
||||
|
@ -89,6 +93,20 @@ func CanBearOff(board []int, player int) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
func ParseSpace(space string) int {
|
||||
i, err := strconv.Atoi(space)
|
||||
if err != nil {
|
||||
switch strings.ToLower(space) {
|
||||
case "bar", "b":
|
||||
return SpaceBarPlayer
|
||||
case "off", "o", "home", "h":
|
||||
return SpaceHomePlayer
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func compareMoveFunc(moves [][]int) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
if moves[j][0] == moves[i][0] {
|
||||
|
|
|
@ -55,6 +55,8 @@ func (c *serverClient) sendEvent(e interface{}) {
|
|||
ev.Type = bgammon.EventTypeFailedMove
|
||||
case *bgammon.EventFailedOk:
|
||||
ev.Type = bgammon.EventTypeFailedOk
|
||||
case *bgammon.EventWin:
|
||||
ev.Type = bgammon.EventTypeWin
|
||||
default:
|
||||
log.Panicf("unknown event type %+v", ev)
|
||||
}
|
||||
|
@ -111,6 +113,8 @@ func (c *serverClient) sendEvent(e interface{}) {
|
|||
c.Write([]byte(fmt.Sprintf("failedmove %d/%d %s", ev.From, ev.To, ev.Reason)))
|
||||
case *bgammon.EventFailedOk:
|
||||
c.Write([]byte(fmt.Sprintf("failedok %s", ev.Reason)))
|
||||
case *bgammon.EventWin:
|
||||
c.Write([]byte(fmt.Sprintf("win %s wins!", ev.Player)))
|
||||
default:
|
||||
log.Panicf("unknown event type %+v", ev)
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ func newServerGame(id int) *serverGame {
|
|||
}
|
||||
|
||||
func (g *serverGame) roll(player int) bool {
|
||||
if g.Winner != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if g.Turn == 0 {
|
||||
if player == 1 {
|
||||
if g.Roll1 != 0 {
|
||||
|
|
|
@ -30,7 +30,7 @@ type server struct {
|
|||
commands chan serverCommand
|
||||
|
||||
gamesLock sync.RWMutex
|
||||
clientsLock sync.RWMutex // TODO need RW?
|
||||
clientsLock sync.Mutex
|
||||
}
|
||||
|
||||
func newServer() *server {
|
||||
|
@ -48,7 +48,7 @@ func newServer() *server {
|
|||
}
|
||||
|
||||
func (s *server) listen(network string, address string) {
|
||||
log.Printf("Listening for %s connections on %s...", network, address)
|
||||
log.Printf("Listening for %s connections on %s...", strings.ToUpper(network), address)
|
||||
listener, err := net.Listen(network, address)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to listen on %s: %s", address, err)
|
||||
|
@ -468,6 +468,7 @@ COMMANDS:
|
|||
})
|
||||
continue
|
||||
}
|
||||
|
||||
ev := &bgammon.EventRolled{
|
||||
Roll1: clientGame.Roll1,
|
||||
Roll2: clientGame.Roll2,
|
||||
|
@ -522,13 +523,13 @@ COMMANDS:
|
|||
sendUsage()
|
||||
continue COMMANDS
|
||||
}
|
||||
from, err := strconv.Atoi(string(split[0]))
|
||||
if err != nil {
|
||||
from := bgammon.ParseSpace(string(split[0]))
|
||||
if from == -1 {
|
||||
sendUsage()
|
||||
continue COMMANDS
|
||||
}
|
||||
to, err := strconv.Atoi(string(split[1]))
|
||||
if err != nil {
|
||||
to := bgammon.ParseSpace(string(split[1]))
|
||||
if to == -1 {
|
||||
sendUsage()
|
||||
continue COMMANDS
|
||||
}
|
||||
|
@ -557,14 +558,29 @@ COMMANDS:
|
|||
})
|
||||
continue
|
||||
}
|
||||
|
||||
var winEvent *bgammon.EventWin
|
||||
if clientGame.Winner != 0 {
|
||||
winEvent = &bgammon.EventWin{}
|
||||
if clientGame.Winner == 1 {
|
||||
winEvent.Player = clientGame.Player1.Name
|
||||
} else {
|
||||
winEvent.Player = clientGame.Player2.Name
|
||||
}
|
||||
}
|
||||
|
||||
clientGame.eachClient(func(client *serverClient) {
|
||||
ev := &bgammon.EventMoved{
|
||||
Moves: bgammon.FlipMoves(moves, client.playerNumber),
|
||||
}
|
||||
ev.Player = string(cmd.client.name)
|
||||
|
||||
client.sendEvent(ev)
|
||||
|
||||
clientGame.sendBoard(client)
|
||||
|
||||
if winEvent != nil {
|
||||
client.sendEvent(winEvent)
|
||||
}
|
||||
})
|
||||
case bgammon.CommandReset:
|
||||
if clientGame == nil {
|
||||
|
@ -607,12 +623,10 @@ COMMANDS:
|
|||
|
||||
legalMoves := clientGame.LegalMoves()
|
||||
if len(legalMoves) != 0 {
|
||||
playerNumber := 1
|
||||
if clientGame.client2 == cmd.client {
|
||||
playerNumber = 2
|
||||
}
|
||||
available := bgammon.FlipMoves(legalMoves, cmd.client.playerNumber)
|
||||
bgammon.SortMoves(available)
|
||||
cmd.client.sendEvent(&bgammon.EventFailedOk{
|
||||
Reason: fmt.Sprintf("The following legal moves are available: %s", bgammon.FormatAndFlipMoves(legalMoves, playerNumber)),
|
||||
Reason: fmt.Sprintf("The following legal moves are available: %s", bgammon.FormatMoves(available)),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -41,4 +41,5 @@ const (
|
|||
EventTypeMoved = "moved"
|
||||
EventTypeFailedMove = "failedmove"
|
||||
EventTypeFailedOk = "failedok"
|
||||
EventTypeWin = "win"
|
||||
)
|
||||
|
|
6
event.go
6
event.go
|
@ -108,6 +108,10 @@ type EventFailedOk struct {
|
|||
Reason string
|
||||
}
|
||||
|
||||
type EventWin struct {
|
||||
Event
|
||||
}
|
||||
|
||||
func DecodeEvent(message []byte) (interface{}, error) {
|
||||
e := &Event{}
|
||||
err := json.Unmarshal(message, e)
|
||||
|
@ -147,6 +151,8 @@ func DecodeEvent(message []byte) (interface{}, error) {
|
|||
ev = &EventFailedMove{}
|
||||
case EventTypeFailedOk:
|
||||
ev = &EventFailedOk{}
|
||||
case EventTypeWin:
|
||||
ev = &EventWin{}
|
||||
default:
|
||||
return nil, fmt.Errorf("failed to decode event: unknown event type: %s", e.Type)
|
||||
}
|
||||
|
|
45
game.go
45
game.go
|
@ -18,6 +18,7 @@ type Game struct {
|
|||
Player1 Player
|
||||
Player2 Player
|
||||
Turn int
|
||||
Winner int
|
||||
Roll1 int
|
||||
Roll2 int
|
||||
Moves [][]int // Pending moves.
|
||||
|
@ -39,6 +40,7 @@ func (g *Game) Copy() *Game {
|
|||
Player1: g.Player1,
|
||||
Player2: g.Player2,
|
||||
Turn: g.Turn,
|
||||
Winner: g.Winner,
|
||||
Roll1: g.Roll1,
|
||||
Roll2: g.Roll2,
|
||||
Moves: make([][]int, len(g.Moves)),
|
||||
|
@ -51,6 +53,10 @@ func (g *Game) Copy() *Game {
|
|||
}
|
||||
|
||||
func (g *Game) NextTurn() {
|
||||
if g.Winner != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
nextTurn := 1
|
||||
if g.Turn == 1 {
|
||||
nextTurn = 2
|
||||
|
@ -100,6 +106,10 @@ 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 {
|
||||
return false
|
||||
}
|
||||
|
||||
delta := 1
|
||||
if g.Turn == 2 {
|
||||
delta = -1
|
||||
|
@ -139,6 +149,7 @@ VALIDATEMOVES:
|
|||
return false
|
||||
}
|
||||
|
||||
var checkWin bool
|
||||
ADDMOVES:
|
||||
for _, move := range addMoves {
|
||||
l := gameCopy.LegalMoves()
|
||||
|
@ -165,6 +176,10 @@ ADDMOVES:
|
|||
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
|
||||
}
|
||||
|
@ -191,11 +206,26 @@ ADDMOVES:
|
|||
g.Board = gameCopy.Board
|
||||
g.Moves = gameCopy.Moves
|
||||
g.boardStates = gameCopy.boardStates
|
||||
|
||||
if checkWin {
|
||||
var foundChecker bool
|
||||
for space := 1; space <= 24; space++ {
|
||||
log.Println(space)
|
||||
if PlayerCheckers(g.Board[space], g.Turn) != 0 {
|
||||
foundChecker = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundChecker {
|
||||
g.Winner = g.Turn
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *Game) LegalMoves() [][]int {
|
||||
if g.Roll1 == 0 || g.Roll2 == 0 {
|
||||
if g.Winner != 0 || g.Roll1 == 0 || g.Roll2 == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -665,13 +695,24 @@ func FlipMoves(moves [][]int, player int) [][]int {
|
|||
return m
|
||||
}
|
||||
|
||||
func FormatSpace(space int) []byte {
|
||||
if space >= 1 && space <= 24 {
|
||||
return []byte(strconv.Itoa(space))
|
||||
} else if space == SpaceBarPlayer || space == SpaceBarOpponent {
|
||||
return []byte("bar")
|
||||
} else if space == SpaceHomePlayer || space == SpaceHomeOpponent {
|
||||
return []byte("off")
|
||||
}
|
||||
return []byte("?")
|
||||
}
|
||||
|
||||
func FormatMoves(moves [][]int) []byte {
|
||||
var out bytes.Buffer
|
||||
for i := range moves {
|
||||
if i != 0 {
|
||||
out.WriteByte(' ')
|
||||
}
|
||||
out.Write([]byte(fmt.Sprintf("%d/%d", moves[i][0], moves[i][1])))
|
||||
out.Write([]byte(fmt.Sprintf("%s/%s", FormatSpace(moves[i][0]), FormatSpace(moves[i][1]))))
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue