Support multi-point matches

This commit is contained in:
Trevor Slocum 2024-11-26 12:57:28 -08:00
parent c272a0881d
commit 1f8ee17cc0
5 changed files with 120 additions and 18 deletions

View file

@ -10,6 +10,7 @@ import (
"net"
"os"
"regexp"
"sort"
"strings"
"time"
@ -149,8 +150,7 @@ func (c *Client) handleWebSocketRead(conn *websocket.Conn) {
ev, err := bgammon.DecodeEvent(msg)
if err != nil {
log.Printf("error: failed to parse message: %s", msg)
conn.Close(websocket.StatusNormalClosure, "Read error")
return
continue
}
c.Events <- ev
@ -252,8 +252,7 @@ func (c *Client) handleTCPRead(conn *net.TCPConn) {
ev, err := bgammon.DecodeEvent(scanner.Bytes())
if err != nil {
log.Printf("error: failed to parse message: %s", scanner.Bytes())
conn.Close()
return
continue
}
c.Events <- ev
@ -376,14 +375,37 @@ func (c *Client) handleEvents() {
*Game = ev.GameState
*Game.Game = *ev.GameState.Game
g := Game
if Game.DoubleOffered && Game.DoublePlayer != Game.PlayerNumber {
panic("TODO")
if Game.DoubleOffered {
if g.Turn == Game.PlayerNumber {
continue
}
moves, err := analyze(Game.Game)
if err != nil {
c.Out <- []byte("say failed to communicate with gnubg service")
time.Sleep(100 * time.Millisecond)
log.Fatalf("failed to communicate with gnubg service: %s", err)
} else if moves[0][0] == -1 && moves[0][1] == -1 {
c.Out <- []byte("ok")
} else {
c.Out <- []byte("resign")
}
c.lastActivity = time.Now()
continue
}
if Game.Roll1 == 0 && len(Game.Player2.Name) != 0 && (Game.Turn == 0 || Game.Turn == Game.PlayerNumber) && Game.MayRoll() {
if Game.MayDouble() {
panic("TODO")
moves, err := analyze(Game.Game)
if err != nil {
c.Out <- []byte("say failed to communicate with gnubg service")
time.Sleep(100 * time.Millisecond)
log.Fatalf("failed to communicate with gnubg service: %s", err)
} else if moves[0][0] == -2 && moves[0][1] == -2 {
c.Out <- []byte("double")
continue
}
}
c.Out <- []byte("r")
c.rolled = true
@ -411,12 +433,45 @@ func (c *Client) handleEvents() {
continue
}
var mv [][2]int8
for i := 0; i < 4; i++ {
from, to := moves[i][0], moves[i][1]
if from == 0 && to == 0 {
break
} else if from == 0 || from == 25 {
mv = append(mv, [2]int8{from, to})
} else if to == 0 || to == 25 {
diff := bgammon.SpaceDiff(from, to, g.Variant)
if diff > 6 {
c := from - g.Roll1
last := from
for c > to {
if bgammon.OpponentCheckers(g.Board[c], 1) <= 1 {
mv = append(mv, [2]int8{last, c})
last = c
}
c -= g.Roll1
}
mv = append(mv, [2]int8{last, to})
} else {
mv = append(mv, [2]int8{from, to})
}
} else {
mv = append(mv, [2]int8{from, to})
}
}
sort.Slice(mv, func(i int, j int) bool {
if mv[i][0] == mv[j][0] {
return mv[i][1] > mv[j][1]
}
return mv[i][0] > mv[j][0]
})
for _, m := range mv {
from, to := m[0], m[1]
if from == 0 || from == 25 {
c.Out <- []byte(fmt.Sprintf("mv bar/%d", to))
} else if to == 0 || to == 25 {
c.Out <- []byte(fmt.Sprintf("mv %d/off", from))
} else {
c.Out <- []byte(fmt.Sprintf("mv %d/%d", from, to))
}

View file

@ -8,6 +8,7 @@ import (
"log"
"os/exec"
"strconv"
"strings"
"time"
"code.rocket9labs.com/tslocum/bgammon"
@ -224,9 +225,11 @@ func analyze(g *bgammon.Game) ([4][2]int8, error) {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
buf := scanner.Bytes()
if bytes.Contains(buf, []byte("The dice have been set")) {
if bytes.Contains(buf, []byte("The dice have been set")) || bytes.Contains(buf, []byte("The cube has been set")) {
setDice = true
} else if setDice && (bytes.Contains(buf, []byte("gnubg moves")) || bytes.Contains(buf, []byte("gnubg offers to resign"))) {
} else if setDice && moves == nil && (bytes.Contains(buf, []byte("gnubg moves")) || bytes.Contains(buf, []byte("gnubg doubles")) || bytes.Contains(buf, []byte("gnubg accepts")) || bytes.Contains(buf, []byte("gnubg refuses the cube")) || bytes.Contains(buf, []byte("gnubg offers to resign"))) {
moves = buf
} else if setDice && moves == nil && bytes.Contains(buf, []byte("Rolled")) {
moves = buf
}
log.Println(string(buf))
@ -240,7 +243,23 @@ func analyze(g *bgammon.Game) ([4][2]int8, error) {
}
}()
stdin.Write([]byte(fmt.Sprintf("set automatic roll off\nset automatic move off\nnew game\nset board %s\nset turn 0\nset dice %d %d\nplay\n", gnubgPosition(g), g.Roll1, g.Roll2)))
var extra []string
if g.DoublePlayer != 0 {
extra = append(extra, fmt.Sprintf("set cube owner %d", g.DoublePlayer-1))
}
if g.DoubleValue != 0 {
extra = append(extra, fmt.Sprintf("set cube value %d", g.DoubleValue))
}
if g.DoubleOffered {
extra = append(extra, "double")
} else if g.Roll1 != 0 && g.Roll2 != 0 {
extra = append(extra, fmt.Sprintf("set dice %d %d", g.Roll1, g.Roll2))
extra = append(extra, "play")
} else {
extra = append(extra, "play")
}
stdin.Write([]byte(fmt.Sprintf("set automatic roll off\nset automatic move off\nnew game\nset beavers 0\nset score %d %d %d\nset board %s\nset turn %d\n%s\n", g.Player1.Points, g.Player2.Points, g.Points, gnubgPosition(g), g.Turn-1, strings.Join(extra, "\n"))))
t := time.NewTicker(50 * time.Millisecond)
for {
<-t.C
@ -256,11 +275,26 @@ func analyze(g *bgammon.Game) ([4][2]int8, error) {
return [4][2]int8{}, err
}
resign := bytes.Contains(moves, []byte("gnubg offers to resign"))
if resign {
acceptCube := bytes.Contains(moves, []byte("gnubg accepts"))
if acceptCube {
return [4][2]int8{{-1, -1}}, nil
}
double := bytes.Contains(moves, []byte("gnubg doubles"))
if double {
return [4][2]int8{{-2, -2}}, nil
}
rollDice := bytes.Contains(moves, []byte("Rolled"))
if rollDice {
return [4][2]int8{{-3, -3}}, nil
}
resign := bytes.Contains(moves, []byte("gnubg offers to resign")) || bytes.Contains(moves, []byte("gnubg refuses the cube"))
if resign {
return [4][2]int8{{-4, -4}}, nil
}
movesWord := bytes.Index(moves, []byte("moves"))
if movesWord == -1 {
return [4][2]int8{}, fmt.Errorf("failed to parse gnubg moves: %s", moves)

5
go.mod
View file

@ -5,11 +5,12 @@ go 1.23
toolchain go1.23.2
require (
code.rocket9labs.com/tslocum/bgammon v0.0.0-20241118091417-1188379900a1
code.rocket9labs.com/tslocum/bgammon v0.0.0-20241120221233-00e4e4da52c2
nhooyr.io/websocket v1.8.17
)
require (
code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b // indirect
code.rocket9labs.com/tslocum/tabula v0.0.0-20241024013344-d112a9463c51 // indirect
code.rocket9labs.com/tslocum/tabula v0.0.0-20241123064436-a9529056c6e2 // indirect
golang.org/x/text v0.20.0 // indirect
)

10
go.sum
View file

@ -1,8 +1,10 @@
code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b h1:Y0a14Kf/hSYepSmp4ZfDeE4CZZGBGBS97CNjCbKJm0c=
code.rocket9labs.com/tslocum/bei v0.0.0-20240108012722-6db380cc190b/go.mod h1:tS60/VNAJphKvDBkSLQhKALa15msIAuWWfEKNc4oFZc=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20241118091417-1188379900a1 h1:X0sTclnDOUSv0EOxwwBYWUnrq7kXFpfSvlDI/H3T7Fk=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20241118091417-1188379900a1/go.mod h1:FzdCJoZoG/6nkxLl6OufdfFTXggr5ZArtl7eJ/CCcj0=
code.rocket9labs.com/tslocum/tabula v0.0.0-20241024013344-d112a9463c51 h1:JkjRKoSSmtHFSVIOAZV6e+Z+7r0mIxClsDpJxC/ZlOs=
code.rocket9labs.com/tslocum/tabula v0.0.0-20241024013344-d112a9463c51/go.mod h1:WEJXESKXqrMFLAArikQ79lpRibNeeE1C0VruxXYMF5M=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20241120221233-00e4e4da52c2 h1:sRRgE5J+OsvLgHgJeO4pI2yraGO+6VwdfoXF4Zc+fA0=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20241120221233-00e4e4da52c2/go.mod h1:FzdCJoZoG/6nkxLl6OufdfFTXggr5ZArtl7eJ/CCcj0=
code.rocket9labs.com/tslocum/tabula v0.0.0-20241123064436-a9529056c6e2 h1:yqzMXKgBx+3/e4DbZNwcuNNjVpRTGF0+FKvO55F81og=
code.rocket9labs.com/tslocum/tabula v0.0.0-20241123064436-a9529056c6e2/go.mod h1:+yChyzCoVWgw6wMQrfh/9EQsB3bMkM21i/33C9e72+c=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y=
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=

10
main.go
View file

@ -23,6 +23,16 @@ func main() {
}
//moves, err := parseMoves([]byte("10/off(2) 7/2(2)"))
/*g := bgammon.NewGame(bgammon.VariantBackgammon)
for i := range g.Board {
g.Board[i] = 0
}
g.Board[1] = 1
g.Board[24] = -1
g.Turn = 2
g.DoublePlayer = 2
g.DoubleOffered = true
log.Fatal(analyze(g))*/
c := newClient(serverAddress, username, password, points)