1 package tabula
2
3 import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "log"
8 "net"
9 "strconv"
10
11 "code.rocket9labs.com/tslocum/bei"
12 )
13
14 type BEIServer struct {
15 }
16
17 func NewBEIServer() *BEIServer {
18 return &BEIServer{}
19 }
20
21 func (s *BEIServer) handleConnection(conn net.Conn) {
22 analysis := make([]*Analysis, 0, AnalysisBufferSize)
23 var beiCommand bool
24 scanner := bufio.NewScanner(conn)
25 for scanner.Scan() {
26 if scanner.Err() != nil {
27 log.Printf("error: failed to read from client: %s", scanner.Err())
28 conn.Close()
29 return
30 }
31 if !beiCommand && !bytes.Equal(scanner.Bytes(), []byte("bei")) {
32 log.Printf("error: failed to read from client: failed to receive bei command")
33 conn.Close()
34 return
35 }
36 switch {
37 case bytes.Equal(scanner.Bytes(), []byte("bei")):
38 buf, err := bei.EncodeEvent(&bei.EventOkBEI{
39 Version: 1,
40 ID: map[string]string{
41 "name": "tabula",
42 },
43 })
44 if err != nil {
45 log.Fatalf("error: failed to encode event: %s", err)
46 }
47 conn.Write(buf)
48 conn.Write([]byte("\n"))
49 beiCommand = true
50 case bytes.HasPrefix(scanner.Bytes(), []byte("move ")):
51 b, err := parseState(scanner.Bytes()[5:])
52 if err != nil {
53 log.Println(err)
54 conn.Close()
55 return
56 }
57
58 available, _ := b.Available(1)
59 b.Analyze(available, &analysis)
60 var move *bei.Move
61 if len(analysis) > 0 {
62 move = &bei.Move{}
63 for _, m := range analysis[0].Moves {
64 if m[0] == 0 && m[1] == 0 {
65 break
66 }
67 move.Play = append(move.Play, &bei.Play{From: int(m[0]), To: int(m[1])})
68 }
69 }
70 result := &bei.EventOkMove{
71 Moves: []*bei.Move{},
72 }
73 if move != nil {
74 result.Moves = append(result.Moves, move)
75 }
76 buf, err := bei.EncodeEvent(result)
77 if err != nil {
78 log.Fatalf("error: failed to encode event: %s", err)
79 }
80 conn.Write(buf)
81 conn.Write([]byte("\n"))
82 case bytes.HasPrefix(scanner.Bytes(), []byte("choose ")):
83 b, err := parseState(scanner.Bytes()[7:])
84 if err != nil {
85 log.Println(err)
86 conn.Close()
87 return
88 }
89
90 if b[SpaceVariant] != VariantAceyDeucey {
91 log.Println("error: failed to choose roll: state does not represent acey-deucey game")
92 conn.Close()
93 return
94 }
95
96 roll := b.ChooseDoubles(&analysis)
97 if roll < 1 || roll > 6 {
98 log.Printf("error: failed to read from client: invalid roll: %d", roll)
99 conn.Close()
100 return
101 }
102
103 buf, err := bei.EncodeEvent(&bei.EventOkChoose{
104 Rolls: []*bei.ChooseRoll{
105 {
106 Roll: roll,
107 },
108 },
109 })
110 if err != nil {
111 log.Fatalf("error: failed to encode event: %s", err)
112 }
113 conn.Write(buf)
114 conn.Write([]byte("\n"))
115 default:
116 log.Printf("error: received unexpected command from client: %s", scanner.Bytes())
117 conn.Close()
118 return
119 }
120 }
121 if scanner.Err() != nil {
122 log.Printf("error: failed to read from client: %s", scanner.Err())
123 conn.Close()
124 return
125 }
126 }
127
128 func (s *BEIServer) Listen(address string) {
129 listener, err := net.Listen("tcp", address)
130 if err != nil {
131 log.Fatalf("failed to listen on %s: %s", address, err)
132 }
133 log.Printf("Listening for connections on %s...", address)
134
135 for {
136 conn, err := listener.Accept()
137 if err != nil {
138 log.Fatalf("failed to listen on %s: %s", address, err)
139 }
140
141 go s.handleConnection(conn)
142 }
143 }
144
145 func parseState(buf []byte) (Board, error) {
146 var stateInts []int
147 for _, v := range bytes.Split(buf, []byte(",")) {
148 i, err := strconv.Atoi(string(v))
149 if err != nil {
150 return Board{}, fmt.Errorf("error: failed to read from client: failed to decode state: %s", err)
151 }
152 stateInts = append(stateInts, i)
153 }
154 state, err := bei.DecodeState(stateInts)
155 if err != nil {
156 return Board{}, fmt.Errorf("error: failed to read from client: failed to decode state: %s", err)
157 }
158 b := Board{}
159 for i, v := range state.Board {
160 b[i] = int8(v)
161 }
162 b[SpaceRoll1] = int8(state.Roll1)
163 b[SpaceRoll2] = int8(state.Roll2)
164 if int8(state.Variant) != VariantTabula && state.Roll1 == state.Roll2 {
165 b[SpaceRoll3], b[SpaceRoll4] = int8(state.Roll1), int8(state.Roll2)
166 } else {
167 b[SpaceRoll3] = int8(state.Roll3)
168 }
169 if int8(state.Variant) != VariantBackgammon {
170 b[SpaceVariant] = int8(state.Variant)
171 if state.Entered1 {
172 b[SpaceEnteredPlayer] = 1
173 }
174 if state.Entered2 {
175 b[SpaceEnteredOpponent] = 1
176 }
177 } else {
178 b[SpaceEnteredPlayer] = 1
179 b[SpaceEnteredOpponent] = 1
180 }
181
182 if Verbose {
183 var logMessage []byte
184 for _, v := range b {
185 logMessage = append(logMessage, []byte(fmt.Sprintf("%4d", int(v)))...)
186 }
187 log.Println(string(logMessage))
188 }
189
190 return b, nil
191 }
192
View as plain text