Support multi-point matches
This commit is contained in:
parent
c272a0881d
commit
1f8ee17cc0
5 changed files with 120 additions and 18 deletions
69
client.go
69
client.go
|
@ -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))
|
||||
}
|
||||
|
|
44
gnubg.go
44
gnubg.go
|
@ -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
5
go.mod
|
@ -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
10
go.sum
|
@ -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
10
main.go
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue