Support playing acey-deucey games via BEI

This commit is contained in:
Trevor Slocum 2023-12-22 23:09:02 -08:00
parent 4e0aeae747
commit f11e319116
5 changed files with 172 additions and 76 deletions

97
bei.go
View file

@ -3,6 +3,7 @@ package tabula
import (
"bufio"
"bytes"
"fmt"
"log"
"net"
"strconv"
@ -47,38 +48,15 @@ func (s *BEIServer) handleConnection(conn net.Conn) {
conn.Write([]byte("\n"))
beiCommand = true
case bytes.HasPrefix(scanner.Bytes(), []byte("move ")):
var stateInts []int
for _, v := range bytes.Split(scanner.Bytes()[5:], []byte(",")) {
i, err := strconv.Atoi(string(v))
if err != nil {
log.Printf("error: failed to read from client: failed to decode state: %s", err)
conn.Close()
return
}
stateInts = append(stateInts, i)
}
state, err := bei.DecodeState(stateInts)
b, err := parseState(scanner.Bytes()[5:])
if err != nil {
log.Printf("error: failed to read from client: failed to decode state: %s", err)
log.Println(err)
conn.Close()
return
}
b := Board{}
for i, v := range state.Board {
b[i] = int8(v)
}
b[SpaceRoll1] = int8(state.Roll1)
b[SpaceRoll2] = int8(state.Roll2)
if state.Roll1 == state.Roll2 {
b[SpaceRoll3], b[SpaceRoll4] = int8(state.Roll1), int8(state.Roll2)
}
// TODO entered, acey
b[SpaceEnteredPlayer] = 1
b[SpaceEnteredOpponent] = 1
available, _ := b.Available(1)
b.Analyze(available, &analysis)
if len(analysis) == 0 {
log.Printf("error: failed to read from client: zero moves returned for analysis")
conn.Close()
@ -100,6 +78,39 @@ func (s *BEIServer) handleConnection(conn net.Conn) {
}
conn.Write(buf)
conn.Write([]byte("\n"))
case bytes.HasPrefix(scanner.Bytes(), []byte("choose ")):
b, err := parseState(scanner.Bytes()[7:])
if err != nil {
log.Println(err)
conn.Close()
return
}
if b[SpaceAcey] != 1 {
log.Println("error: failed to choose roll: state does not represent acey-deucey game")
conn.Close()
return
}
roll := b.ChooseDoubles(&analysis)
if roll < 1 || roll > 6 {
log.Printf("error: failed to read from client: invalid roll: %d", roll)
conn.Close()
return
}
buf, err := bei.EncodeEvent(&bei.EventOkChoose{
Rolls: []*bei.ChooseRoll{
{
Roll: roll,
},
},
})
if err != nil {
log.Fatalf("error: failed to encode event: %s", err)
}
conn.Write(buf)
conn.Write([]byte("\n"))
default:
log.Printf("error: received unexpected command from client: %s", scanner.Bytes())
conn.Close()
@ -129,3 +140,39 @@ func (s *BEIServer) Listen(address string) {
go s.handleConnection(conn)
}
}
func parseState(buf []byte) (Board, error) {
var stateInts []int
for _, v := range bytes.Split(buf, []byte(",")) {
i, err := strconv.Atoi(string(v))
if err != nil {
return Board{}, fmt.Errorf("error: failed to read from client: failed to decode state: %s", err)
}
stateInts = append(stateInts, i)
}
state, err := bei.DecodeState(stateInts)
if err != nil {
return Board{}, fmt.Errorf("error: failed to read from client: failed to decode state: %s", err)
}
b := Board{}
for i, v := range state.Board {
b[i] = int8(v)
}
b[SpaceRoll1] = int8(state.Roll1)
b[SpaceRoll2] = int8(state.Roll2)
if state.Roll1 == state.Roll2 {
b[SpaceRoll3], b[SpaceRoll4] = int8(state.Roll1), int8(state.Roll2)
}
b[SpaceEnteredPlayer] = 1
b[SpaceEnteredOpponent] = 1
if state.Acey {
b[SpaceAcey] = 1
if !state.Entered1 {
b[SpaceEnteredPlayer] = 0
}
if !state.Entered2 {
b[SpaceEnteredOpponent] = 0
}
}
return b, nil
}

View file

@ -543,11 +543,94 @@ func (b Board) Analyze(available [][4][2]int8, result *[]*Analysis) {
}
}
if b.StartingPosition(1) && b[SpaceRoll1] != b[SpaceRoll2] {
r1, r2 := b[SpaceRoll1], b[SpaceRoll2]
if r2 > r1 {
r1, r2 = r2, r1
}
var opening [4][2]int8
if r1 == r2 {
switch r1 {
case 1:
opening = [4][2]int8{{24, 23}, {24, 23}, {6, 5}, {6, 5}}
case 2:
opening = [4][2]int8{{13, 11}, {13, 11}, {11, 9}, {11, 9}}
case 3:
opening = [4][2]int8{{13, 10}, {13, 10}, {10, 7}, {10, 7}}
case 4:
opening = [4][2]int8{{13, 9}, {13, 9}, {6, 2}, {6, 2}}
case 5:
opening = [4][2]int8{{13, 8}, {13, 8}, {8, 3}, {8, 3}}
case 6:
opening = [4][2]int8{{24, 18}, {24, 18}, {13, 7}, {13, 7}}
}
} else {
switch r1 {
case 2:
opening = [4][2]int8{{13, 11}, {6, 5}}
case 3:
switch r2 {
case 1:
opening = [4][2]int8{{8, 5}, {6, 5}}
case 2:
opening = [4][2]int8{{13, 11}, {13, 10}}
}
case 4:
switch r2 {
case 1:
opening = [4][2]int8{{24, 23}, {13, 9}}
case 2:
opening = [4][2]int8{{8, 4}, {6, 4}}
case 3:
opening = [4][2]int8{{13, 10}, {13, 9}}
}
case 5:
switch r2 {
case 1:
opening = [4][2]int8{{24, 23}, {13, 8}}
case 2:
opening = [4][2]int8{{24, 22}, {13, 8}}
case 3:
opening = [4][2]int8{{8, 3}, {6, 3}}
case 4:
opening = [4][2]int8{{24, 20}, {13, 8}}
}
case 6:
switch r2 {
case 1:
opening = [4][2]int8{{13, 7}, {8, 7}}
case 2:
opening = [4][2]int8{{24, 18}, {13, 11}}
case 3:
opening = [4][2]int8{{24, 18}, {13, 10}}
case 4:
opening = [4][2]int8{{8, 2}, {6, 2}}
case 5:
opening = [4][2]int8{{24, 18}, {18, 13}}
}
}
}
const priorityScore = -1000000
for _, a := range *result {
if movesEqual(a.Moves, opening) {
a.Score = priorityScore
break
}
}
}
sort.Slice(*result, func(i, j int) bool {
return (*result)[i].Score < (*result)[j].Score
})
}
func (b Board) StartingPosition(player int) bool {
if player == 1 {
return b[6] == 5 && b[8] == 3 && b[13] == 5 && b[24] == 2
}
return b[1] == -2 && b[12] == -5 && b[17] == -3 && b[19] == -5
}
func (b Board) ChooseDoubles(result *[]*Analysis) int {
if b[SpaceAcey] == 0 {
return 0

View file

@ -4,7 +4,6 @@ import (
"flag"
"fmt"
"log"
"time"
"code.rocket9labs.com/tslocum/tabula"
)
@ -32,54 +31,21 @@ func main() {
//b := tabula.Board{0, 0, 0, 0, 0, -1, 8, 0, 4, 0, 0, 0, 0, 0, -1, -1, 0, -1, -1, -1, 1, -2, -2, -3, -2, 0, 2, 0, 0, 0, 0, 4, 1, 1, 0}
b := tabula.Board{0, 0, -2, -2, -2, 4, 0, -1, 0, 0, -2, 4, 0, -2, -1, 0, -2, 4, 0, 2, 0, 0, 0, 0, -1, 0, 1, 0, 4, 1, 0, 0, 1, 1, 1}
//b := tabula.Board{0, 0, -2, -2, -2, 4, 0, -1, 0, 0, -2, 4, 0, -2, -1, 0, -2, 4, 0, 2, 0, 0, 0, 0, -1, 0, 1, 0, 4, 1, 0, 0, 1, 1, 1}
t := time.Now()
available, _ := b.Available(1)
//log.Println(b.Move(15, 19, 2))
//log.Println(b.Move(15, 19, 2).UseRoll(15, 19, 2))
log.Println(available)
//os.Exit(0)
t2 := time.Now()
analysis := make([]*tabula.Analysis, 0, tabula.AnalysisBufferSize)
b.Analyze(available, &analysis)
t3 := time.Now()
log.Println("AVAILABLE TOOK ", t2.Sub(t))
log.Println("ANALYSIS TOOK ", t3.Sub(t2))
log.Println("AVAILABLE", available)
for i, a := range analysis {
log.Printf("%+v %+v", i, a)
for r1 := 1; r1 <= 6; r1++ {
for r2 := 1; r2 <= 6; r2++ {
b := tabula.NewBoard(false)
b[tabula.SpaceRoll1] = int8(r1)
b[tabula.SpaceRoll2] = int8(r2)
if r1 == r2 {
b[tabula.SpaceRoll3] = int8(r1)
b[tabula.SpaceRoll4] = int8(r2)
}
available, _ := b.Available(1)
b.Analyze(available, &analysis)
log.Println("ROLL", r1, r2, analysis[0].Moves)
}
}
//t4 := time.Now()
//_ = b.ChooseDoubles(&analysis)
//log.Println("CHOOSE DOUBLES TOOK ", time.Since(t4))
/*
b := tabula.NewBoard(true)
b[tabula.SpaceRoll1] = 6
b[tabula.SpaceRoll2] = 6
b[tabula.SpaceRoll3] = 6
b[tabula.SpaceRoll4] = 6
b.Print()
t := time.Now()
available, _ := b.Available(1)
t2 := time.Now()
analysis := make([]*tabula.Analysis, 0, tabula.AnalysisBufferSize)
b.Analyze(available, &analysis)
t3 := time.Now()
log.Println("AVAILABLE TOOK ", t2.Sub(t))
log.Println("ANALYSIS TOOK ", t3.Sub(t2))
log.Println("AVAILABLE", available)
for i, a := range analysis {
log.Printf("%+v %+v", i, a)
}*/
}

2
go.mod
View file

@ -2,4 +2,4 @@ module code.rocket9labs.com/tslocum/tabula
go 1.17
require code.rocket9labs.com/tslocum/bei v0.0.0-20231220232111-b1d4144d27bb
require code.rocket9labs.com/tslocum/bei v0.0.0-20231222205835-c070d7c7d5f8

4
go.sum
View file

@ -1,2 +1,2 @@
code.rocket9labs.com/tslocum/bei v0.0.0-20231220232111-b1d4144d27bb h1:gU9+YXaDNq/7OyHJ4Mqu5qAyJFAxmCY++Ptk5T/B7w4=
code.rocket9labs.com/tslocum/bei v0.0.0-20231220232111-b1d4144d27bb/go.mod h1:tS60/VNAJphKvDBkSLQhKALa15msIAuWWfEKNc4oFZc=
code.rocket9labs.com/tslocum/bei v0.0.0-20231222205835-c070d7c7d5f8 h1:QefJxcMg2aneCZuoHPpnDOTsing1mehImilbyyT3SwU=
code.rocket9labs.com/tslocum/bei v0.0.0-20231222205835-c070d7c7d5f8/go.mod h1:tS60/VNAJphKvDBkSLQhKALa15msIAuWWfEKNc4oFZc=