1 package fibs
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "io"
8 "log"
9 "math/rand"
10 "regexp"
11 "strconv"
12 "strings"
13 "time"
14
15 "github.com/reiver/go-oi"
16 "github.com/reiver/go-telnet"
17 "golang.org/x/text/language"
18 "golang.org/x/text/message"
19 "nhooyr.io/websocket"
20 )
21
22
23 var Debug = 0
24
25 const whoInfoSize = 12
26
27 const (
28 whoInfoDataName = iota
29 whoInfoDataOpponent
30 whoInfoDataWatching
31 whoInfoDataReady
32 whoInfoDataAway
33 whoInfoDataRating
34 whoInfoDataExperience
35 whoInfoDataIdleTime
36 whoInfoDataLoginTime
37 whoInfoDataHostname
38 whoInfoDataClientName
39 whoInfoDataEmail
40 )
41
42 var DefaultProxyAddress = ""
43
44 var (
45 TypeWelcome = []byte("1")
46 TypeOwnInfo = []byte("2")
47 TypeMOTD = []byte("3")
48 TypeEndMOTD = []byte("4")
49 TypeWhoInfo = []byte("5")
50 TypeEndWhoInfo = []byte("6")
51 TypeLogin = []byte("7")
52 TypeLogout = []byte("8")
53 TypeMsg = []byte("9")
54 TypeMsgDelivered = []byte("10")
55 TypeMsgSaved = []byte("11")
56 TypeSay = []byte("12")
57 TypeShout = []byte("13")
58 TypeWhisper = []byte("14")
59 TypeKibitz = []byte("15")
60 TypeYouSay = []byte("16")
61 TypeYouShout = []byte("17")
62 TypeYouWhisper = []byte("18")
63 TypeYouKibitz = []byte("19")
64 TypeBoardState = []byte("board:")
65 )
66
67 var numberPrinter = message.NewPrinter(language.English)
68
69 type WhoInfo struct {
70 Username string
71 Opponent string
72 Watching string
73 Ready bool
74 Away bool
75 Rating int
76 Experience int
77 Idle int
78 LoginTime int
79 ClientName string
80 }
81
82 func (w *WhoInfo) String() string {
83 opponent := "In the lobby"
84 if w.Opponent != "" && w.Opponent != "-" {
85 opponent = "playing against " + w.Opponent
86 }
87 clientName := ""
88 if w.ClientName != "" && w.ClientName != "-" {
89 clientName = " using " + w.ClientName
90 }
91 return fmt.Sprintf("%s (rated %d with %d exp) is %s%s", w.Username, w.Rating, w.Experience, opponent, clientName)
92 }
93
94 type Client struct {
95 In chan []byte
96 Out chan []byte
97 Event chan interface{}
98
99 Username string
100 Password string
101
102 loggedin bool
103 motd []byte
104 rawMode bool
105
106 who map[string]*WhoInfo
107
108 notified map[string]bool
109
110 serverAddress string
111 wsProxyAddress string
112
113 tcpConn io.Writer
114 wsConn *websocket.Conn
115
116 Board *Board
117
118 tvMode bool
119 }
120
121 func NewClient(serverAddress string, username string, password string) *Client {
122 c := &Client{
123 In: make(chan []byte, 100),
124 Out: make(chan []byte, 100),
125 Event: make(chan interface{}, 100),
126
127 serverAddress: serverAddress,
128 wsProxyAddress: DefaultProxyAddress,
129
130 Username: username,
131 Password: password,
132
133 who: make(map[string]*WhoInfo),
134
135 notified: make(map[string]bool),
136 }
137
138 c.Board = NewBoard(c)
139
140 go c.eventLoop()
141
142 return c
143 }
144
145 func (c *Client) handleWrite() {
146 var buffer bytes.Buffer
147 var p []byte
148
149 crlfBuffer := [2]byte{'\r', '\n'}
150 crlf := crlfBuffer[:]
151
152 help := []byte("help")
153 who := []byte("who")
154 quit := []byte("quit")
155 bye := []byte("bye")
156 watch := []byte("watch")
157 tv := []byte("tv")
158 reset := []byte("reset")
159 textBoard := []byte("textboard")
160 about := []byte("about")
161 show := []byte("show")
162 average := []byte("average")
163 dicetest := []byte("dicetest")
164 boardstate := []byte("boardstate")
165 stat := []byte("stat")
166 for b := range c.Out {
167 if bytes.Equal(bytes.ToLower(b), watch) {
168 c.WatchRandomGame()
169 continue
170 } else if bytes.Equal(bytes.ToLower(b), tv) {
171 c.tvMode = !c.tvMode
172 if c.tvMode {
173 l("Now watching backgammon TV")
174 c.WatchRandomGame()
175 } else {
176 l("Stopped watching backgammon TV")
177 }
178 continue
179 } else if bytes.Equal(bytes.ToLower(b), reset) {
180 c.Board.ResetPreMoves()
181 c.Event <- &EventDraw{}
182 l("Reset pre-moves")
183 continue
184 } else if bytes.Equal(bytes.ToLower(b), who) {
185 for username := range c.who {
186 lf("%s", c.who[username])
187 }
188 continue
189 } else if bytes.Equal(bytes.ToLower(b), textBoard) {
190 go func() {
191 c.Out <- []byte("set boardstyle 2")
192 time.Sleep(time.Second)
193 c.Out <- []byte("board")
194 time.Sleep(time.Second)
195 c.Out <- []byte("set boardstyle 3")
196 }()
197 continue
198 } else if bytes.HasPrefix(bytes.ToLower(b), help) || bytes.HasPrefix(bytes.ToLower(b), about) || bytes.HasPrefix(bytes.ToLower(b), average) ||
199 bytes.HasPrefix(bytes.ToLower(b), dicetest) || bytes.HasPrefix(bytes.ToLower(b), show) || bytes.HasPrefix(bytes.ToLower(b), stat) {
200 c.rawMode = true
201 go func() {
202 time.Sleep(time.Second)
203 c.rawMode = false
204 }()
205 } else if bytes.Equal(bytes.ToLower(b), boardstate) {
206 homeBoardStart, homeBoardEnd := c.Board.PlayerHomeSpaces()
207
208 lf("Board state: %s", c.Board.GetState())
209 lf("Player color: %d", c.Board.v[StatePlayerColor])
210 lf("Direction: %d", c.Board.v[StateDirection])
211 lf("Current turn: %d", c.Board.v[StateTurn])
212 lf("Player home spaces: %d-%d",
213 homeBoardStart, homeBoardEnd)
214 lf("Player can bear off: %t", c.Board.PlayerPieceAreHome())
215 lf("Moves: %+v", c.Board.moves)
216 lf("Pre-moves: %+v", c.Board.premove)
217 continue
218 } else if bytes.Equal(bytes.ToLower(b), quit) || bytes.Equal(bytes.ToLower(b), bye) {
219
220 c.rawMode = true
221 }
222
223 if Debug > 0 {
224 l("-> " + string(bytes.TrimSpace(b)))
225 }
226
227 buffer.Write(b)
228 buffer.Write(crlf)
229
230 p = buffer.Bytes()
231
232 if c.wsProxyAddress != "" {
233 err := c.wsConn.Write(context.Background(), websocket.MessageBinary, p)
234 if err != nil {
235 log.Fatalf("Transmission problem: %s", err)
236 }
237 buffer.Reset()
238 continue
239 }
240
241 n, err := oi.LongWrite(c.tcpConn, p)
242 if nil != err {
243 break
244 }
245 if expected, actual := int64(len(p)), n; expected != actual {
246 log.Fatalf("Transmission problem: tried sending %d bytes, but actually only sent %d bytes.", expected, actual)
247 }
248
249 buffer.Reset()
250 }
251 }
252
253 func (c *Client) handleRead(r io.Reader) {
254 var b = &bytes.Buffer{}
255 var buffer [1]byte
256 p := buffer[:]
257
258 var motd bool
259
260 for {
261
262 n, err := r.Read(p)
263 if n <= 0 && nil == err {
264 continue
265 } else if n <= 0 && nil != err {
266 if err.Error() != "EOF" {
267 lf("** Disconnected: %s", err)
268 } else {
269 l("** Disconnected")
270 }
271 return
272 }
273
274 b.WriteByte(p[0])
275
276 if p[0] == '\n' {
277 buf := make([]byte, b.Len())
278 copy(buf, b.Bytes())
279
280 if Debug > 0 {
281 l("<- " + string(bytes.TrimSpace(buf)))
282 }
283
284 if c.loggedin {
285 if !motd {
286 buf = bytes.TrimSpace(buf)
287 if len(buf) == 0 {
288 b.Reset()
289 continue
290 }
291 }
292
293 if c.rawMode {
294 c.In <- append([]byte("** "), buf...)
295 b.Reset()
296 continue
297 }
298
299 if bytes.HasPrefix(b.Bytes(), TypeMOTD) && !motd {
300 motd = true
301 c.motd = append(c.motd, buf[1:]...)
302 b.Reset()
303 continue
304 } else if bytes.HasPrefix(b.Bytes(), TypeEndMOTD) && motd {
305 motd = false
306 c.motd = bytes.TrimSpace(c.motd)
307 c.In <- append([]byte("3 "), c.motd...)
308 b.Reset()
309 continue
310 } else if motd {
311 c.motd = append(c.motd, buf...)
312 b.Reset()
313 continue
314 }
315
316 c.In <- buf
317 }
318 b.Reset()
319 }
320
321 if !c.loggedin {
322 bt := strings.TrimSpace(b.String())
323 if bt == "login:" {
324 b.Reset()
325 c.Out <- []byte(fmt.Sprintf("login bgammon 1008 %s %s", c.Username, c.Password))
326 c.loggedin = true
327 }
328 }
329 }
330 }
331
332
333 func (c *Client) CallTELNET(ctx telnet.Context, w telnet.Writer, r telnet.Reader) {
334 c.tcpConn = w
335
336 go c.handleRead(r)
337
338 c.handleWrite()
339 }
340
341 func (c *Client) keepAlive() {
342 t := time.NewTicker(5 * time.Minute)
343 for range t.C {
344 c.Out <- []byte("set boardstyle 3")
345 }
346 }
347
348 func (c *Client) updateWhoInfo(b []byte) {
349 s := bytes.Split(b, []byte(" "))
350 if len(s) != whoInfoSize {
351 return
352 }
353
354 info := &WhoInfo{
355 Username: string(s[whoInfoDataName]),
356 }
357
358 r := s[whoInfoDataRating]
359 if bytes.ContainsRune(r, '.') {
360 r = s[whoInfoDataRating][:bytes.IndexByte(s[whoInfoDataRating], '.')]
361 }
362 rating, err := strconv.Atoi(string(r))
363 if err != nil {
364 rating = 0
365 }
366 info.Rating = rating
367
368 experience, err := strconv.Atoi(string(s[whoInfoDataExperience]))
369 if err != nil {
370 experience = 0
371 }
372 info.Experience = experience
373
374 opponent := ""
375 if len(s[whoInfoDataOpponent]) > 1 || s[whoInfoDataOpponent][0] != '-' {
376 opponent = string(s[whoInfoDataOpponent])
377 }
378 info.Opponent = opponent
379
380 watching := ""
381 if len(s[whoInfoDataWatching]) > 1 || s[whoInfoDataWatching][0] != '-' {
382 watching = string(s[whoInfoDataWatching])
383 }
384 info.Watching = watching
385
386 ready := false
387 if string(s[whoInfoDataReady]) == "1" {
388 ready = true
389 }
390 info.Ready = ready
391
392 clientName := ""
393 if len(s[whoInfoDataClientName]) > 1 || s[whoInfoDataClientName][0] != '-' {
394 clientName = string(s[whoInfoDataClientName])
395 }
396 info.ClientName = clientName
397
398 status := "Unavailable"
399 if info.Opponent != "" {
400 status = "vs. " + info.Opponent
401 } else if info.Ready {
402 status = "Available"
403 }
404
405 itemText := info.Username + strings.Repeat(" ", 18-len(info.Username))
406
407 ratingString := numberPrinter.Sprintf("%d", info.Rating)
408 itemText += ratingString + strings.Repeat(" ", 8-len(ratingString))
409
410 experienceString := numberPrinter.Sprintf("%d", info.Experience)
411 itemText += experienceString + strings.Repeat(" ", 12-len(experienceString))
412
413 itemText += status
414
415 c.who[string(s[whoInfoDataName])] = info
416 }
417
418 func (c *Client) GetAllWhoInfo() []*WhoInfo {
419 w := make([]*WhoInfo, len(c.who))
420 var i int
421 for _, whoInfo := range c.who {
422 w[i] = whoInfo
423 i++
424 }
425 return w
426 }
427
428 func (c *Client) LoggedIn() bool {
429
430 return c.loggedin
431 }
432
433 func (c *Client) WatchRandomGame() {
434 var options []string
435 for username, whoInfo := range c.who {
436 if username != "" && whoInfo.Opponent != "" &&
437 !strings.Contains(username, "Bot") && !strings.Contains(whoInfo.Opponent, "Bot") {
438 options = append(options, username, whoInfo.Opponent)
439 }
440 }
441 if len(options) == 0 {
442 for username, whoInfo := range c.who {
443 if username != "" && whoInfo.Opponent != "" {
444 options = append(options, username, whoInfo.Opponent)
445 }
446 }
447 if len(options) == 0 {
448 return
449 }
450 }
451
452 option := options[rand.Intn(len(options))]
453 c.Out <- []byte("watch " + option)
454 }
455
456 func (c *Client) callWebSocket() {
457 ctx := context.Background()
458
459 var err error
460 c.wsConn, _, err = websocket.Dial(ctx, c.wsProxyAddress, nil)
461 if err != nil {
462 log.Fatal("dial error", err)
463 }
464 defer c.wsConn.Close(websocket.StatusInternalError, "the sky is falling")
465
466
467
468 r, w := io.Pipe()
469
470 go c.handleRead(r)
471
472 go func() {
473 for {
474 _, data, err := c.wsConn.Read(context.Background())
475 if err != nil {
476 if err.Error() != "EOF" {
477 lf("** Disconnected: %s", err)
478 } else {
479 l("** Disconnected")
480 }
481 }
482 w.Write(data)
483 }
484 }()
485
486 c.handleWrite()
487
488
489 }
490
491 func (c *Client) Connect() error {
492 connectionType := "Telnet"
493 if c.wsProxyAddress != "" {
494 connectionType = fmt.Sprintf("WebSocket proxy (%s)", c.wsProxyAddress)
495 }
496 l(fmt.Sprintf("Connecting via %s to %s...", connectionType, c.serverAddress))
497
498 go c.keepAlive()
499
500 if c.wsProxyAddress != "" {
501 go c.callWebSocket()
502 return nil
503 }
504
505 err := telnet.DialToAndCall(c.serverAddress, c)
506 if err != nil {
507 lf("** Disconnected: %s", err)
508 }
509 return err
510 }
511
512 func (c *Client) boardStateUpdated() {
513 s := make([]string, len(c.Board.s))
514 v := make([]int, len(c.Board.v))
515 copy(s, c.Board.s)
516 copy(v, c.Board.v)
517 c.Event <- &EventBoardState{S: s, V: v}
518 }
519
520 func (c *Client) eventLoop() {
521 var setBoardStyle bool
522 var turnRegexp = regexp.MustCompile(`^turn: (\w+)\.`)
523 var movesRegexp = regexp.MustCompile(`^(\w+) moves (.*)`)
524 var rollsRegexp = regexp.MustCompile(`^(\w+) rolls? (.*)`)
525 var logInOutRegexp = regexp.MustCompile(`^\w+ logs [In|Out]\.`)
526 var startMatchRegexp = regexp.MustCompile(`^\w+ and \w+ start a .*`)
527 var winsMatchRegexp = regexp.MustCompile(`^\w+ wins a [0-9]+ point match against .*`)
528 var winsThisMatchRegexp = regexp.MustCompile(`^\w+ wins the [0-9]+ point match .*`)
529 var newGameRegexp = regexp.MustCompile(`^Starting a new game with .*`)
530 var inviteResumeGameRegexp = regexp.MustCompile(`^*\* You invited \w+ to resume a saved match\..*`)
531 var joinedGameRegexp = regexp.MustCompile(`^\w+ has joined you\..*`)
532
533 var gameBufferRegexp = regexp.MustCompile(`^\w+ (makes|roll|rolls|rolled|move|moves) .*`)
534 var pleaseMoveRegexp = regexp.MustCompile(`^Please move ([0-9]) pieces?.`)
535 var invalidMoveRegexp = regexp.MustCompile(`^\*\* You can't move .*`)
536 var cantMoveRegexp = regexp.MustCompile(`^(\w+) can't move\.`)
537 var doublesRegexp = regexp.MustCompile(`^\w+ doubles.`)
538 var acceptDoubleRegexp = regexp.MustCompile(`^\w+ accepts the double.`)
539
540 for b := range c.In {
541 b = bytes.Replace(b, []byte{7}, []byte{}, -1)
542 b = bytes.TrimSpace(b)
543 bl := bytes.ToLower(b)
544
545
546 if bytes.HasPrefix(b, TypeMsgSaved) {
547 lf("Message to %s saved", b[3:])
548 continue
549 } else if bytes.HasPrefix(b, TypeMsgDelivered) {
550 lf("Message to %s delivered", b[3:])
551 continue
552 } else if bytes.HasPrefix(b, TypeSay) {
553 s := bytes.SplitN(b[3:], []byte(" "), 2)
554 lf("%s says: %s", s[0], s[1])
555 continue
556 } else if bytes.HasPrefix(b, TypeShout) {
557 s := bytes.SplitN(b[3:], []byte(" "), 2)
558 lf("%s shouts: %s", s[0], s[1])
559 continue
560 } else if bytes.HasPrefix(b, TypeWhisper) {
561 s := bytes.SplitN(b[3:], []byte(" "), 2)
562 lf("%s whispers: %s", s[0], s[1])
563 continue
564 } else if bytes.HasPrefix(b, TypeKibitz) {
565 s := bytes.SplitN(b[3:], []byte(" "), 2)
566 lf("%s kibitzes: %s", s[0], s[1])
567 continue
568 } else if bytes.HasPrefix(b, TypeYouSay) {
569 s := bytes.SplitN(b[3:], []byte(" "), 2)
570 lf("You say to %s: %s", s[0], s[1])
571 continue
572 } else if bytes.HasPrefix(b, TypeYouShout) {
573 lf("You shout: %s", b[3:])
574 continue
575 } else if bytes.HasPrefix(b, TypeYouWhisper) {
576 lf("You whisper: %s", b[3:])
577 continue
578 } else if bytes.HasPrefix(b, TypeYouKibitz) {
579 lf("You kibitz: %s", b[3:])
580 continue
581 } else if bytes.HasPrefix(b, TypeWelcome) {
582 s := bytes.Split(b[2:], []byte(" "))
583 loginTimestamp, err := strconv.Atoi(string(s[1]))
584 if err != nil {
585 loginTimestamp = 0
586 }
587 loginTime := time.Unix(int64(loginTimestamp), 0)
588 lf("Welcome, %s! Last login at %s", s[0], loginTime)
589 continue
590 } else if bytes.HasPrefix(b, TypeOwnInfo) {
591
592 continue
593 } else if bytes.HasPrefix(b, TypeMOTD) {
594 for _, line := range bytes.Split(c.motd, []byte("\n")) {
595 l(strings.ReplaceAll(string(line), "|", " "))
596 }
597 if !setBoardStyle {
598 c.Out <- []byte("set boardstyle 3")
599 setBoardStyle = true
600 }
601 continue
602 } else if bytes.HasPrefix(b, TypeWhoInfo) {
603 c.updateWhoInfo(b[2:])
604 continue
605 } else if bytes.HasPrefix(b, TypeEndWhoInfo) {
606 who := make([]*WhoInfo, len(c.who))
607 i := 0
608 for _, whoInfo := range c.who {
609 who[i] = whoInfo
610 i++
611 }
612
613 c.Event <- &EventWho{
614 Who: who,
615 }
616 continue
617 } else if bytes.HasPrefix(b, TypeLogin) || bytes.HasPrefix(b, TypeLogout) {
618 b = b[2:]
619 b = b[bytes.IndexByte(b, ' ')+1:]
620 continue
621 } else if bytes.HasPrefix(b, TypeMsg) {
622 lf("Received message: %s", b[3:])
623 continue
624 } else if bytes.HasPrefix(b, TypeBoardState) {
625 c.Board.SetState(string(bytes.SplitN(b, []byte(" "), 2)[0][6:]))
626 c.boardStateUpdated()
627 continue
628 } else if turnRegexp.Match(b) {
629 turn := turnRegexp.FindSubmatch(b)
630 if string(turn[1]) == c.Username || string(turn[1]) == c.Board.s[0] || string(turn[1]) == "You" {
631 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor]
632 } else {
633 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor] * -1
634 }
635 c.boardStateUpdated()
636 } else if rollsRegexp.Match(b) {
637 roll := rollsRegexp.FindSubmatch(b)
638 periodIndex := bytes.IndexRune(roll[2], '.')
639 if periodIndex > -1 {
640 roll[2] = roll[2][:periodIndex]
641 }
642 s := bytes.Split(roll[2], []byte(" "))
643 var dice [2]int
644 var i int
645 for _, m := range s {
646 v, err := strconv.Atoi(string(m))
647 if err == nil {
648 dice[i] = v
649 i++
650 }
651 }
652
653 if string(roll[1]) == c.Board.s[0] || string(roll[1]) == "You" {
654 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor]
655 c.Board.v[StatePlayerDice1] = dice[0]
656 c.Board.v[StatePlayerDice2] = dice[1]
657 } else {
658 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor] * -1
659 c.Board.v[StateOpponentDice1] = dice[0]
660 c.Board.v[StateOpponentDice2] = dice[1]
661 }
662
663 c.Board.ResetMoves()
664
665 c.boardStateUpdated()
666 } else if movesRegexp.Match(b) {
667 c.boardStateUpdated()
668
669 match := movesRegexp.FindSubmatch(b)
670
671 player := c.Board.v[StatePlayerColor]
672 if string(match[1]) == c.Board.s[1] {
673 player *= -1
674 }
675
676 c.Board.ResetMoves()
677
678 from := -1
679 to := -1
680
681 s := bytes.Split(match[2], []byte(" "))
682 for _, m := range s {
683 move := bytes.Split(m, []byte("-"))
684 if len(move) == 2 {
685 from = c.Board.parseMoveString(player, string(move[0]))
686 to = c.Board.parseMoveString(player, string(move[1]))
687 if from >= 0 && to >= 0 {
688 c.Event <- &EventMove{
689 Player: player,
690 From: from,
691 To: to,
692 }
693 }
694 c.Board.Move(player, string(move[0]), string(move[1]))
695 }
696 }
697
698 c.Board.SimplifyMoves()
699
700 c.boardStateUpdated()
701
702 bs := string(b)
703 if strings.HasSuffix(bs, " .") {
704 bs = bs[:len(bs)-2] + "."
705 }
706 lg(bs)
707
708 continue
709 } else if string(b) == "Value of 'boardstyle' set to 3." {
710 continue
711 } else if string(b) == "It's your turn to move." || strings.TrimSpace(string(b)) == "It's your turn to roll or double." || strings.TrimSpace(string(b)) == "It's your turn. Please roll or double" {
712 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor]
713 c.Board.Draw()
714 if strings.TrimSpace(string(b)) == "It's your turn to roll or double." || strings.TrimSpace(string(b)) == "It's your turn. Please roll or double" {
715 c.Out <- []byte("roll")
716 }
717 } else if invalidMoveRegexp.Match(b) {
718 c.Board.ResetPreMoves()
719 c.Board.Draw()
720 } else if cantMoveRegexp.Match(b) {
721 match := cantMoveRegexp.FindSubmatch(b)
722
723 u := string(match[1])
724 if u == c.Username || u == c.Board.s[0] || u == "You" {
725
726
727 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor] * -1
728 } else if u == c.Board.s[1] {
729
730
731 c.Board.v[StateTurn] = c.Board.v[StatePlayerColor]
732 }
733 } else if pleaseMoveRegexp.Match(b) {
734 match := pleaseMoveRegexp.FindSubmatch(b)
735
736 n, err := strconv.Atoi(string(match[1]))
737 if err == nil {
738 c.Board.v[StateMovablePieces] = n
739 }
740 } else if joinedGameRegexp.Match(b) {
741
742 c.Out <- []byte("board")
743 } else if bytes.HasPrefix(bl, []byte("you're now watching")) {
744
745 c.Out <- []byte("board")
746 } else if inviteResumeGameRegexp.Match(b) {
747
748 go func() {
749 time.Sleep(500 * time.Millisecond)
750 c.Out <- []byte("board")
751 }()
752 } else if logInOutRegexp.Match(b) {
753 continue
754 } else if startMatchRegexp.Match(b) {
755 continue
756 } else if winsThisMatchRegexp.Match(b) {
757 if c.tvMode {
758 go func() {
759 time.Sleep(5 * time.Second)
760 if !c.tvMode {
761 return
762 }
763 c.WatchRandomGame()
764 }()
765 }
766 continue
767 } else if winsMatchRegexp.Match(b) {
768 continue
769 } else if newGameRegexp.Match(b) {
770 c.Board.ResetMoves()
771 c.Board.ResetPreMoves()
772 c.Board.resetSelection()
773 continue
774 }
775
776 if gameBufferRegexp.Match(b) || cantMoveRegexp.Match(b) ||
777 doublesRegexp.Match(b) || acceptDoubleRegexp.Match(b) ||
778 bytes.HasPrefix(bl, []byte("you're now watching")) || bytes.HasPrefix(bl, []byte("** you stop watching")) ||
779 strings.HasPrefix(string(b), "Bearing off:") || strings.HasPrefix(string(b), "The only possible move") {
780 lg(string(b))
781
782 if !bytes.HasPrefix(bl, []byte("you're now watching")) && !bytes.HasPrefix(bl, []byte("** you stop watching")) {
783 continue
784 }
785 }
786
787 l(string(b))
788 }
789 }
790
View as plain text