Support playing acey-deucey games

This commit is contained in:
Trevor Slocum 2023-12-10 19:09:59 -08:00
parent 612426a93d
commit 44ad618f32
4 changed files with 63 additions and 27 deletions

View file

@ -36,11 +36,13 @@ type Client struct {
sentGreeting bool
rolled bool
lastActivity time.Time
acey bool
quiet bool
thinkTime time.Duration
analysis []*tabula.Analysis
}
func NewClient(address string, username string, password string, points int, quiet bool, thinkTime time.Duration) *Client {
func NewClient(address string, username string, password string, points int, acey bool, quiet bool, thinkTime time.Duration) *Client {
const bufferSize = 10
c := &Client{
Address: address,
@ -49,15 +51,17 @@ func NewClient(address string, username string, password string, points int, qui
Events: make(chan interface{}, bufferSize),
Out: make(chan []byte, bufferSize),
points: points,
acey: acey,
quiet: quiet,
thinkTime: thinkTime,
analysis: make([]*tabula.Analysis, 0, tabula.AnalysisBufferSize),
}
go c.handleTimeout()
return c
}
func NewLocalClient(conn net.Conn, address string, username string, password string, points int, quiet bool, thinkTime time.Duration) {
c := NewClient(address, username, password, points, quiet, thinkTime)
func NewLocalClient(conn net.Conn, address string, username string, password string, points int, acey bool, quiet bool, thinkTime time.Duration) {
c := NewClient(address, username, password, points, acey, quiet, thinkTime)
c.connecting = true
go c.connectTCP(conn)
c.HandleEvents()
@ -279,7 +283,11 @@ func (c *Client) handleTCPRead(conn net.Conn) {
}
func (c *Client) createMatch() {
c.Out <- []byte(fmt.Sprintf("c public %d", c.points))
acey := 0
if c.acey {
acey = 1
}
c.Out <- []byte(fmt.Sprintf("c public %d %d", c.points, acey))
c.sentGreeting = false
c.rolled = false
}
@ -407,31 +415,25 @@ func (c *Client) HandleEvents() {
continue
}
b := Game.Board
var roll1, roll2, roll3, roll4 int8
roll1, roll2 = int8(Game.Roll1), int8(Game.Roll2)
if roll1 == roll2 {
roll3, roll4 = int8(Game.Roll1), int8(Game.Roll2)
}
boardState := tabula.Board{int8(b[0]), int8(b[1]), int8(b[2]), int8(b[3]), int8(b[4]), int8(b[5]), int8(b[6]), int8(b[7]), int8(b[8]), int8(b[9]), int8(b[10]), int8(b[11]), int8(b[12]), int8(b[13]), int8(b[14]), int8(b[15]), int8(b[16]), int8(b[17]), int8(b[18]), int8(b[19]), int8(b[20]), int8(b[21]), int8(b[22]), int8(b[23]), int8(b[24]), int8(b[25]), int8(b[26]), int8(b[27]), roll1, roll2, roll3, roll4}
var t time.Time
if c.thinkTime != 0 {
t = time.Now()
}
boardState := tabulaBoard(Game.Board)
available, _ := boardState.Available(1)
result := boardState.Analyze(available)
if len(result) == 0 {
boardState.Analyze(available, &c.analysis)
if len(c.analysis) == 0 {
log.Printf("Legal moves: %+v", Game.Available)
log.Fatalf("NO PLAYABLE MOVES RETURNED: %+v", result)
log.Fatalf("NO PLAYABLE MOVES RETURNED: %+v", c.analysis)
}
if !c.quiet {
log.Println("==========")
log.Println("Legal moves:", Game.Available)
log.Println("Past:", result[0].Past)
log.Println("Past:", c.analysis[0].Past)
builder := &strings.Builder{}
builder.Write([]byte("Board: "))
@ -446,15 +448,17 @@ func (c *Client) HandleEvents() {
const padding = 3
w := tabwriter.NewWriter(os.Stderr, 0, 0, padding, ' ', 0)
fmt.Fprintln(w, "Moves\tScore\tPlayer Score\tPips\tBlots\tHits\tOpponent Score\tPips\tBlots\tHits\t")
for _, r := range result {
for _, r := range c.analysis {
fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%d\t%d\t%d\t%.2f\t%.2f\t%.2f\t%.2f\t\n", fmt.Sprint(r.Moves), r.Score, r.PlayerScore, r.Pips, r.Blots, r.Hits, r.OppScore, r.OppPips, r.OppBlots, r.OppHits)
}
w.Flush()
log.Println("==========")
}
for _, move := range result[0].Moves {
if move[0] == 0 || move[0] == 25 {
for _, move := range c.analysis[0].Moves {
if move[0] == 0 {
c.Out <- []byte(fmt.Sprintf("mv off/%d", move[1]))
} else if move[0] == 25 {
c.Out <- []byte(fmt.Sprintf("mv bar/%d", move[1]))
} else {
c.Out <- []byte(fmt.Sprintf("mv %d/%d", move[0], move[1]))
@ -467,7 +471,17 @@ func (c *Client) HandleEvents() {
time.Sleep(c.thinkTime - s)
}
}
c.Out <- []byte("ok")
if Game.MayChooseRoll() {
newGame := Game.Copy()
for _, move := range c.analysis[0].Moves {
newGame.AddLocalMove(move)
}
newBoardState := tabulaBoard(newGame.Board)
doubles := newBoardState.ChooseDoubles(&c.analysis)
c.Out <- []byte(fmt.Sprintf("ok %d", doubles))
} else {
c.Out <- []byte("ok")
}
c.lastActivity = time.Now()
case *bgammon.EventRolled:
Game.Roll1 = ev.Roll1
@ -518,3 +532,23 @@ func (c *Client) HandleEvents() {
}
}
}
func tabulaBoard(b []int) tabula.Board {
var roll1, roll2, roll3, roll4 int8
roll1, roll2 = int8(Game.Roll1), int8(Game.Roll2)
if roll1 == roll2 {
roll3, roll4 = int8(Game.Roll1), int8(Game.Roll2)
}
entered1, entered2 := int8(1), int8(1)
acey := int8(0)
if Game.Acey {
if !Game.Player1.Entered {
entered1 = 0
}
if !Game.Player2.Entered {
entered2 = 0
}
acey = 1
}
return tabula.Board{int8(b[0]), int8(b[1]), int8(b[2]), int8(b[3]), int8(b[4]), int8(b[5]), int8(b[6]), int8(b[7]), int8(b[8]), int8(b[9]), int8(b[10]), int8(b[11]), int8(b[12]), int8(b[13]), int8(b[14]), int8(b[15]), int8(b[16]), int8(b[17]), int8(b[18]), int8(b[19]), int8(b[20]), int8(b[21]), int8(b[22]), int8(b[23]), int8(b[24]), int8(b[25]), int8(b[26]), int8(b[27]), roll1, roll2, roll3, roll4, entered1, entered2, acey}
}

4
go.mod
View file

@ -3,7 +3,7 @@ module code.rocket9labs.com/tslocum/bgammon-tabula-bot
go 1.17
require (
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231208220405-8c4b5112ba77
code.rocket9labs.com/tslocum/tabula v0.0.0-20231209094435-959d43321c78
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231211185248-f939d053a819
code.rocket9labs.com/tslocum/tabula v0.0.0-20231212190831-bfc39a406f20
nhooyr.io/websocket v1.8.10
)

8
go.sum
View file

@ -1,6 +1,6 @@
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231208220405-8c4b5112ba77 h1:MnAW9Icj5mDOaFnyT3BcH/xRoa0stZEJjs+hQg0lyj0=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231208220405-8c4b5112ba77/go.mod h1:u3nbSwxWnwOXbCNPQD4s3abGTfvA4/gi9U626f7ZN9Q=
code.rocket9labs.com/tslocum/tabula v0.0.0-20231209094435-959d43321c78 h1:5E+Av3zTjMndkoz9n7QSl0icA1eBjnzFXtpbVADhXy8=
code.rocket9labs.com/tslocum/tabula v0.0.0-20231209094435-959d43321c78/go.mod h1:XtS6M8qcK0fnUn4k5I7h4JcZ2sz2WEAXOcF2DLaQBME=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231211185248-f939d053a819 h1:QUps5Kz32BpKMvVISbRZe2gtr1BYqfPOSV1UifpHADU=
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231211185248-f939d053a819/go.mod h1:u3nbSwxWnwOXbCNPQD4s3abGTfvA4/gi9U626f7ZN9Q=
code.rocket9labs.com/tslocum/tabula v0.0.0-20231212190831-bfc39a406f20 h1:Y/OwzfDQcm6FLCMKa8eMI+MjpcVDgmnHwsewd22KbHo=
code.rocket9labs.com/tslocum/tabula v0.0.0-20231212190831-bfc39a406f20/go.mod h1:XtS6M8qcK0fnUn4k5I7h4JcZ2sz2WEAXOcF2DLaQBME=
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=

View file

@ -18,6 +18,7 @@ func main() {
username string
password string
points int
acey bool
quiet bool
thinkTime time.Duration
debug int
@ -26,6 +27,7 @@ func main() {
flag.StringVar(&username, "username", "", "Username")
flag.StringVar(&password, "password", "", "Password")
flag.IntVar(&points, "points", 1, "Match points")
flag.BoolVar(&acey, "acey", false, "Create acey-deucey match")
flag.Float64Var(&tabula.WeightBlot, "weight-blot", tabula.WeightBlot, "Weight (multiplier) when scoring blots")
flag.Float64Var(&tabula.WeightHit, "weight-hit", tabula.WeightHit, "Weight (multiplier) when scoring hits")
flag.Float64Var(&tabula.WeightOppScore, "weight-score", tabula.WeightOppScore, "Weight (multiplier) when adding opponent score to overall score")
@ -40,7 +42,7 @@ func main() {
}()
}
c := bot.NewClient(serverAddress, username, password, points, quiet, thinkTime)
c := bot.NewClient(serverAddress, username, password, points, acey, quiet, thinkTime)
go c.Connect()
c.HandleEvents()