From 4e0aeae7471302132cdb3b48a61410d9e74c4003 Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Wed, 20 Dec 2023 16:47:51 -0800 Subject: [PATCH] Add initial support for BEI protocol --- bei.go | 131 +++++++++++++++++++++++++++++++++++++++++++++ cmd/tabula/main.go | 7 +++ go.mod | 2 + go.sum | 2 + 4 files changed, 142 insertions(+) create mode 100644 bei.go diff --git a/bei.go b/bei.go new file mode 100644 index 0000000..a17a882 --- /dev/null +++ b/bei.go @@ -0,0 +1,131 @@ +package tabula + +import ( + "bufio" + "bytes" + "log" + "net" + "strconv" + + "code.rocket9labs.com/tslocum/bei" +) + +type BEIServer struct { +} + +func NewBEIServer() *BEIServer { + return &BEIServer{} +} + +func (s *BEIServer) handleConnection(conn net.Conn) { + analysis := make([]*Analysis, 0, AnalysisBufferSize) + var beiCommand bool + scanner := bufio.NewScanner(conn) + for scanner.Scan() { + if scanner.Err() != nil { + log.Printf("error: failed to read from client: %s", scanner.Err()) + conn.Close() + return + } + if !beiCommand && !bytes.Equal(scanner.Bytes(), []byte("bei")) { + log.Printf("error: failed to read from client: failed to receive bei command") + conn.Close() + return + } + switch { + case bytes.Equal(scanner.Bytes(), []byte("bei")): + buf, err := bei.EncodeEvent(&bei.EventOkBEI{ + Version: 1, + ID: map[string]string{ + "name": "tabula", + }, + }) + if err != nil { + log.Fatalf("error: failed to encode event: %s", err) + } + conn.Write(buf) + 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) + if err != nil { + log.Printf("error: failed to read from client: failed to decode state: %s", 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() + return + } + + move := &bei.Move{} + for _, m := range analysis[0].Moves { + if m[0] == 0 && m[1] == 0 { + break + } + move.Play = append(move.Play, &bei.Play{From: int(m[0]), To: int(m[1])}) + } + buf, err := bei.EncodeEvent(&bei.EventOkMove{ + Moves: []*bei.Move{move}, + }) + 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() + return + } + } + if scanner.Err() != nil { + log.Printf("error: failed to read from client: %s", scanner.Err()) + conn.Close() + return + } +} + +func (s *BEIServer) Listen(address string) { + listener, err := net.Listen("tcp", address) + if err != nil { + log.Fatalf("failed to listen on %s: %s", address, err) + } + log.Printf("Listening for connections on %s...", address) + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatalf("failed to listen on %s: %s", address, err) + } + + go s.handleConnection(conn) + } +} diff --git a/cmd/tabula/main.go b/cmd/tabula/main.go index b906817..ede132a 100644 --- a/cmd/tabula/main.go +++ b/cmd/tabula/main.go @@ -10,7 +10,9 @@ import ( ) func main() { + var address string var pips bool + flag.StringVar(&address, "address", "", "Listen for BEI connections on specified address (TCP)") flag.BoolVar(&pips, "pips", false, "Print table of pseudopip values") flag.Parse() @@ -23,6 +25,11 @@ func main() { return } + if address != "" { + s := tabula.NewBEIServer() + s.Listen(address) + } + //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} diff --git a/go.mod b/go.mod index ca756c9..7dd5e26 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module code.rocket9labs.com/tslocum/tabula go 1.17 + +require code.rocket9labs.com/tslocum/bei v0.0.0-20231220232111-b1d4144d27bb diff --git a/go.sum b/go.sum index e69de29..5e20046 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +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=