Flip spaces for player 2

This commit is contained in:
Trevor Slocum 2023-08-25 01:39:43 -07:00
parent 2408478221
commit 138a91e57d
5 changed files with 184 additions and 45 deletions

View file

@ -143,6 +143,12 @@ type Game struct {
}
```
### `failedok <reason:line>`
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.
### `say <player:text> <message:line>`
Chat message from another player.

View file

@ -7,6 +7,7 @@ import (
"fmt"
"log"
"math/rand"
"slices"
"time"
"code.rocket9labs.com/tslocum/bgammon"
@ -58,10 +59,26 @@ func (g *serverGame) roll(player int) bool {
}
func (g *serverGame) sendBoard(client *serverClient) {
playerNumber := 1
if g.client2 == client {
playerNumber = 2
}
if client.json {
gameState := bgammon.GameState{
Game: g.Game,
Available: g.LegalMoves(),
gameState := ServerGameState{
GameState: bgammon.GameState{
Game: g.Game,
Available: g.LegalMoves(),
},
Board: g.Game.Board,
}
if playerNumber == 2 {
log.Println(gameState.Board)
log.Println(g.Game.Board)
slices.Reverse(gameState.Board)
log.Println(gameState.Board)
log.Println(g.Game.Board)
}
buf, err := json.Marshal(gameState)
if err != nil {
@ -71,10 +88,6 @@ func (g *serverGame) sendBoard(client *serverClient) {
return
}
playerNumber := 1
if g.client2 == client {
playerNumber = 2
}
scanner := bufio.NewScanner(bytes.NewReader(g.BoardState(playerNumber)))
for scanner.Scan() {
client.events <- append([]byte("notice "), scanner.Bytes()...)
@ -98,9 +111,11 @@ func (g *serverGame) addClient(client *serverClient) bool {
}
joinMessage := []byte(fmt.Sprintf("joined %d %s %s", g.id, client.name, g.name))
client.events <- joinMessage
g.sendBoard(client)
opponent := g.opponent(client)
if opponent != nil {
opponent.events <- joinMessage
g.sendBoard(opponent)
}
}()
switch {
@ -108,16 +123,20 @@ func (g *serverGame) addClient(client *serverClient) bool {
ok = false
case g.client1 != nil:
g.client2 = client
g.Player2.Name = string(client.name)
ok = true
case g.client2 != nil:
g.client1 = client
g.Player1.Name = string(client.name)
ok = true
default:
i := rand.Intn(2)
if i == 0 {
g.client1 = client
g.Player1.Name = string(client.name)
} else {
g.client2 = client
g.Player2.Name = string(client.name)
}
ok = true
}
@ -125,17 +144,34 @@ func (g *serverGame) addClient(client *serverClient) bool {
}
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
log.Println("remove client", client)
ok := true
defer func() {
if !ok {
return
}
opponent := g.opponent(client)
if opponent == nil {
return
}
opponent.events <- []byte(fmt.Sprintf("left %d %s %s", g.id, client.name, g.name))
if !opponent.json {
g.sendBoard(opponent)
}
}()
switch {
case g.client1 == client:
g.client1 = nil
g.Player1.Name = ""
case g.client2 == client:
g.client2 = nil
g.Player2.Name = ""
default:
ok = false
return
}
// TODO game is considered paused when only one player is present
// once started, only the same player may join and continue the game
log.Println("removed client", client)
}
func (g *serverGame) opponent(client *serverClient) *serverClient {
@ -146,3 +182,8 @@ func (g *serverGame) opponent(client *serverClient) *serverClient {
}
return nil
}
type ServerGameState struct {
bgammon.GameState
Board []int
}

View file

@ -19,17 +19,17 @@ func main() {
g := newServerGame(1)
g.Board[bgammon.SpaceBarPlayer] = 3
g.Board[bgammon.SpaceBarOpponent] = -2
g.Roll1 = 3
g.Roll2 = 2
g.Turn = 1
g.Roll1 = 1
g.Roll2 = 3
g.Turn = 2
log.Println("initial legal moves")
log.Printf("%+v", g.LegalMoves())
g.Moves = append(g.Moves, []int{6, 4})
//g.Moves = append(g.Moves, []int{6, 4})
log.Printf("Legal moves after %+v", g.Moves)
log.Printf("%+v", g.LegalMoves())
playerNumber := 1
playerNumber := 2
go func() {
time.Sleep(100 * time.Millisecond)

View file

@ -299,8 +299,6 @@ COMMANDS:
log.Panicf("failed to add client to newly created game %+v %+v", g, cmd.client)
}
s.games = append(s.games, g) // TODO lock
g.sendBoard(cmd.client)
case bgammon.CommandJoin, "j":
if clientGame != nil {
cmd.client.events <- []byte("failedjoin Please leave the game you are in before joining another game.")
@ -330,10 +328,7 @@ COMMANDS:
if !g.addClient(cmd.client) {
cmd.client.events <- []byte("failedjoin Game is full.")
continue COMMANDS
}
g.sendBoard(cmd.client)
continue COMMANDS
}
}
@ -368,19 +363,45 @@ COMMANDS:
}
if !clientGame.roll(playerNumber) {
cmd.client.events <- []byte("notice It is not your turn to roll.")
} else {
clientGame.eachClient(func(client *serverClient) {
client.events <- []byte(fmt.Sprintf("rolled %d %d", clientGame.Roll1, clientGame.Roll2))
})
continue
}
clientGame.eachClient(func(client *serverClient) {
roll1 := 0
roll2 := 0
if playerNumber == 1 {
roll1 = clientGame.Roll1
} else {
roll2 = clientGame.Roll2
}
client.events <- []byte(fmt.Sprintf("rolled %s %d %d", cmd.client.name, roll1, roll2))
})
if clientGame.Turn == 0 && clientGame.Roll1 != 0 && clientGame.Roll2 != 0 {
if clientGame.Roll1 > clientGame.Roll2 {
clientGame.Turn = 1
} else if clientGame.Roll2 > clientGame.Roll1 {
clientGame.Turn = 2
} else {
clientGame.Roll1 = 0
clientGame.Roll2 = 0
}
}
case bgammon.CommandMove, "m", "mv":
if clientGame == nil {
cmd.client.events <- []byte("notice You are not currently in a game.")
cmd.client.events <- []byte("failedmove You are not currently in a game.")
continue
}
playerNumber := 1
if clientGame.client2 == cmd.client {
playerNumber = 2
}
if clientGame.Turn != playerNumber {
cmd.client.events <- []byte("failedmove It is not your turn to move.")
continue
}
sendUsage := func() {
cmd.client.events <- []byte("notice Specify one or more moves in the form FROM/TO. For example: 8/4 6/4")
cmd.client.events <- []byte("failedmove Specify one or more moves in the form FROM/TO. For example: 8/4 6/4")
}
if len(params) == 0 {
@ -390,7 +411,6 @@ COMMANDS:
gameCopy := bgammon.Game{}
gameCopy = *clientGame.Game
gameCopy.Moves = [][]int{}
copy(gameCopy.Moves, clientGame.Moves)
var moves [][]int
@ -411,6 +431,9 @@ COMMANDS:
continue COMMANDS
}
originalFrom, originalTo := from, to
from, to = bgammon.FlipSpace(from, playerNumber), bgammon.FlipSpace(to, playerNumber)
legalMoves := gameCopy.LegalMoves()
var found bool
for j := range legalMoves {
@ -420,7 +443,8 @@ COMMANDS:
}
}
if !found {
cmd.client.events <- []byte(fmt.Sprintf("failedmove %d/%d Illegal move.", from, to))
log.Printf("available legal moves: %s", bgammon.FormatMoves(legalMoves, playerNumber))
cmd.client.events <- []byte(fmt.Sprintf("failedmove %d/%d Illegal move.", originalFrom, originalTo))
continue COMMANDS
}
@ -429,25 +453,48 @@ COMMANDS:
gameCopy.Moves = append(gameCopy.Moves, move)
}
paramsText := bytes.Join(params, []byte(" "))
clientGame.Moves = gameCopy.Moves
clientGame.eachClient(func(client *serverClient) {
client.events <- []byte(fmt.Sprintf("move %s %s", cmd.client.name, paramsText))
clientGame.sendBoard(client)
player := 1
if clientGame.client2 == client {
player = 2
}
client.events <- []byte(fmt.Sprintf("move %s %s", cmd.client.name, bgammon.FormatMoves(moves, player)))
if !client.json {
clientGame.sendBoard(client)
}
})
case bgammon.CommandBoard, "b":
case bgammon.CommandOk, "k":
if clientGame == nil {
cmd.client.events <- []byte("notice You are not currently in a game.")
} else {
continue
}
legalMoves := clientGame.LegalMoves()
if len(legalMoves) != 0 {
playerNumber := 1
if clientGame.client2 == cmd.client {
playerNumber = 2
}
cmd.client.events <- []byte(fmt.Sprintf("failedok You still have the following legal moves available: %s", bgammon.FormatMoves(legalMoves, playerNumber)))
continue
}
scanner := bufio.NewScanner(bytes.NewReader(clientGame.BoardState(playerNumber)))
for scanner.Scan() {
cmd.client.events <- append([]byte("notice "), scanner.Bytes()...)
}
log.Println("legal to pass turn")
case bgammon.CommandBoard, "b":
if clientGame == nil {
cmd.client.events <- []byte("notice You are not currently in a game.")
continue
}
playerNumber := 1
if clientGame.client2 == cmd.client {
playerNumber = 2
}
scanner := bufio.NewScanner(bytes.NewReader(clientGame.BoardState(playerNumber)))
for scanner.Scan() {
cmd.client.events <- append([]byte("notice "), scanner.Bytes()...)
}
case bgammon.CommandDisconnect:
if clientGame != nil {

61
game.go
View file

@ -284,9 +284,13 @@ func (g *Game) BoardState(player int) []byte {
var playerColor = "x"
var opponentColor = "o"
playerRoll := g.Roll1
opponentRoll := g.Roll2
if white {
playerColor = "o"
opponentColor = "x"
playerRoll = g.Roll2
opponentRoll = g.Roll1
}
if white {
@ -365,16 +369,36 @@ func (g *Game) BoardState(player int) []byte {
t.Write([]byte(fmt.Sprintf(" %d off", v)))
}
} else if i == 2 {
if g.Turn != player && g.Roll1 > 0 {
t.Write([]byte(fmt.Sprintf(" %d %d ", g.Roll1, g.Roll2)))
} else {
t.Write([]byte(fmt.Sprintf(" - - ")))
if g.Turn == 0 {
if g.Player1.Name != "" && g.Player2.Name != "" {
if opponentRoll != 0 {
t.Write([]byte(fmt.Sprintf(" %d", opponentRoll)))
} else {
t.Write([]byte(fmt.Sprintf(" -")))
}
}
} else if g.Turn != player {
if g.Roll1 > 0 {
t.Write([]byte(fmt.Sprintf(" %d %d ", g.Roll1, g.Roll2)))
} else if opponentName != "" {
t.Write([]byte(fmt.Sprintf(" - - ")))
}
}
} else if i == 8 {
if g.Turn == player && g.Roll1 > 0 {
t.Write([]byte(fmt.Sprintf(" %d %d ", g.Roll1, g.Roll2)))
} else {
t.Write([]byte(fmt.Sprintf(" - - ")))
if g.Turn == 0 {
if g.Player1.Name != "" && g.Player2.Name != "" {
if playerRoll != 0 {
t.Write([]byte(fmt.Sprintf(" %d", playerRoll)))
} else {
t.Write([]byte(fmt.Sprintf(" -")))
}
}
} else if g.Turn == player {
if g.Roll1 > 0 {
t.Write([]byte(fmt.Sprintf(" %d %d ", g.Roll1, g.Roll2)))
} else if playerName != "" {
t.Write([]byte(fmt.Sprintf(" - - ")))
}
}
} else if i == 10 {
t.Write([]byte(playerColor + " " + playerName + " (" + playerRating + ")"))
@ -437,6 +461,27 @@ func numOpponentCheckers(checkers int, player int) int {
}
}
func FlipSpace(space int, player int) int {
if player == 1 {
return space
}
if space < 1 || space > 24 {
return space // TODO fix
}
return 24 - space + 1
}
func FormatMoves(moves [][]int, player int) []byte {
var out bytes.Buffer
for i := range moves {
if i != 0 {
out.WriteByte(' ')
}
out.Write([]byte(fmt.Sprintf("%d/%d", FlipSpace(moves[i][0], player), FlipSpace(moves[i][1], player))))
}
return out.Bytes()
}
const (
VerticalBar rune = '\u2502' // │
)