2021-08-31 01:26:34 +00:00
package fibs
import (
"bytes"
2021-09-08 01:09:53 +00:00
"context"
2021-08-31 01:26:34 +00:00
"fmt"
2021-09-08 01:09:53 +00:00
"io"
2021-08-31 01:26:34 +00:00
"log"
"math/rand"
"regexp"
"strconv"
"strings"
"time"
"github.com/reiver/go-oi"
"github.com/reiver/go-telnet"
"golang.org/x/text/language"
"golang.org/x/text/message"
2021-09-08 01:09:53 +00:00
"nhooyr.io/websocket"
2021-08-31 01:26:34 +00:00
)
const debug = 1 // TODO
const whoInfoSize = 12
const (
whoInfoDataName = iota
whoInfoDataOpponent
whoInfoDataWatching
whoInfoDataReady
whoInfoDataAway
whoInfoDataRating
whoInfoDataExperience
whoInfoDataIdleTime
whoInfoDataLoginTime
whoInfoDataHostname
whoInfoDataClientName
whoInfoDataEmail
)
2021-09-08 01:09:53 +00:00
var DefaultProxyAddress = ""
2021-08-31 01:26:34 +00:00
var (
TypeWelcome = [ ] byte ( "1" )
TypeOwnInfo = [ ] byte ( "2" )
TypeMOTD = [ ] byte ( "3" )
TypeEndMOTD = [ ] byte ( "4" )
TypeWhoInfo = [ ] byte ( "5" )
TypeEndWhoInfo = [ ] byte ( "6" )
TypeLogin = [ ] byte ( "7" )
TypeLogout = [ ] byte ( "8" )
TypeMsg = [ ] byte ( "9" )
TypeMsgDelivered = [ ] byte ( "10" )
TypeMsgSaved = [ ] byte ( "11" )
TypeSay = [ ] byte ( "12" )
TypeShout = [ ] byte ( "13" )
TypeWhisper = [ ] byte ( "14" )
TypeKibitz = [ ] byte ( "15" )
TypeYouSay = [ ] byte ( "16" )
TypeYouShout = [ ] byte ( "17" )
TypeYouWhisper = [ ] byte ( "18" )
TypeYouKibitz = [ ] byte ( "19" )
TypeBoardState = [ ] byte ( "board:" )
)
var numberPrinter = message . NewPrinter ( language . English )
type WhoInfo struct {
Username string
Opponent string
Watching string
Ready bool
Away bool
Rating int
Experience int
Idle int
LoginTime int
ClientName string
}
func ( w * WhoInfo ) String ( ) string {
opponent := "In the lobby"
if w . Opponent != "" && w . Opponent != "-" {
opponent = "playing against " + w . Opponent
}
clientName := ""
if w . ClientName != "" && w . ClientName != "-" {
clientName = " using " + w . ClientName
}
return fmt . Sprintf ( "%s (rated %d with %d exp) is %s%s" , w . Username , w . Rating , w . Experience , opponent , clientName )
}
type Client struct {
In chan [ ] byte
Out chan [ ] byte
Event chan interface { }
Username string
Password string
loggedin bool
motd [ ] byte
rawMode bool
who map [ string ] * WhoInfo
notified map [ string ] bool
2021-09-08 01:09:53 +00:00
serverAddress string
wsProxyAddress string // WebSocket->TCP proxy address
tcpConn io . Writer
wsConn * websocket . Conn
2021-08-31 01:26:34 +00:00
Board * Board
2021-09-08 01:09:53 +00:00
tvMode bool
2021-08-31 01:26:34 +00:00
}
func NewClient ( serverAddress string , username string , password string ) * Client {
c := & Client {
In : make ( chan [ ] byte , 100 ) ,
Out : make ( chan [ ] byte , 100 ) ,
Event : make ( chan interface { } , 100 ) ,
2021-09-08 01:09:53 +00:00
serverAddress : serverAddress ,
wsProxyAddress : DefaultProxyAddress ,
2021-08-31 01:26:34 +00:00
Username : username ,
Password : password ,
who : make ( map [ string ] * WhoInfo ) ,
notified : make ( map [ string ] bool ) ,
}
c . Board = NewBoard ( c )
go c . eventLoop ( )
return c
}
2021-09-08 01:09:53 +00:00
func ( c * Client ) handleWrite ( ) {
2021-08-31 01:26:34 +00:00
var buffer bytes . Buffer
var p [ ] byte
var crlfBuffer [ 2 ] byte = [ 2 ] byte { '\r' , '\n' }
crlf := crlfBuffer [ : ]
help := [ ] byte ( "help" )
who := [ ] byte ( "who" )
quit := [ ] byte ( "quit" )
bye := [ ] byte ( "bye" )
watch := [ ] byte ( "watch" )
2021-09-08 01:09:53 +00:00
tv := [ ] byte ( "tv" )
reset := [ ] byte ( "reset" )
2021-08-31 01:26:34 +00:00
about := [ ] byte ( "about" )
show := [ ] byte ( "show" )
average := [ ] byte ( "average" )
dicetest := [ ] byte ( "dicetest" )
boardstate := [ ] byte ( "boardstate" )
stat := [ ] byte ( "stat" )
for b := range c . Out {
if bytes . Equal ( bytes . ToLower ( b ) , watch ) {
c . WatchRandomGame ( )
continue
2021-09-08 01:09:53 +00:00
} else if bytes . Equal ( bytes . ToLower ( b ) , tv ) {
c . tvMode = ! c . tvMode
if c . tvMode {
l ( "Now watching backgammon TV" )
c . WatchRandomGame ( )
} else {
l ( "Stopped watching backgammon TV" )
}
continue
} else if bytes . Equal ( bytes . ToLower ( b ) , reset ) {
c . Board . ResetPreMoves ( )
c . Event <- & EventDraw { }
l ( "Reset pre-moves" )
continue
2021-08-31 01:26:34 +00:00
} else if bytes . Equal ( bytes . ToLower ( b ) , who ) {
for username := range c . who {
lf ( "%s" , c . who [ username ] )
}
continue
} else if bytes . HasPrefix ( bytes . ToLower ( b ) , help ) || bytes . HasPrefix ( bytes . ToLower ( b ) , about ) || bytes . HasPrefix ( bytes . ToLower ( b ) , average ) ||
bytes . HasPrefix ( bytes . ToLower ( b ) , dicetest ) || bytes . HasPrefix ( bytes . ToLower ( b ) , show ) || bytes . HasPrefix ( bytes . ToLower ( b ) , stat ) {
c . rawMode = true
go func ( ) {
time . Sleep ( time . Second )
c . rawMode = false
} ( )
} else if bytes . Equal ( bytes . ToLower ( b ) , boardstate ) {
2021-09-08 01:09:53 +00:00
homeBoardStart , homeBoardEnd := c . Board . homeBoardSpaces ( )
2021-08-31 01:26:34 +00:00
lf ( "Board state: %s" , c . Board . GetState ( ) )
lf ( "Player color: %d" , c . Board . v [ StatePlayerColor ] )
lf ( "Direction: %d" , c . Board . v [ StateDirection ] )
lf ( "Current turn: %d" , c . Board . v [ StateTurn ] )
2021-09-08 01:09:53 +00:00
lf ( "Player home spaces: %d-%d" ,
homeBoardStart , homeBoardEnd )
lf ( "Player can bear off: %t" , c . Board . allPlayerPiecesInHomeBoard ( ) )
2021-08-31 01:26:34 +00:00
lf ( "Moves: %+v" , c . Board . moves )
lf ( "Pre-moves: %+v" , c . Board . premove )
continue
} else if bytes . Equal ( bytes . ToLower ( b ) , quit ) || bytes . Equal ( bytes . ToLower ( b ) , bye ) {
// TODO match command, not exact string
c . rawMode = true
}
if debug > 0 {
l ( "-> " + string ( bytes . TrimSpace ( b ) ) )
}
buffer . Write ( b )
buffer . Write ( crlf )
p = buffer . Bytes ( )
2021-09-08 01:09:53 +00:00
if c . wsProxyAddress != "" {
err := c . wsConn . Write ( context . Background ( ) , websocket . MessageBinary , p )
if err != nil {
log . Fatalf ( "Transmission problem: %s" , err )
}
buffer . Reset ( )
continue
}
n , err := oi . LongWrite ( c . tcpConn , p )
2021-08-31 01:26:34 +00:00
if nil != err {
break
}
if expected , actual := int64 ( len ( p ) ) , n ; expected != actual {
log . Fatalf ( "Transmission problem: tried sending %d bytes, but actually only sent %d bytes." , expected , actual )
}
buffer . Reset ( )
}
}
2021-09-08 01:09:53 +00:00
func ( c * Client ) handleRead ( r io . Reader ) {
var b = & bytes . Buffer { }
var buffer [ 1 ] byte // Seems like the length of the buffer needs to be small, otherwise will have to wait for buffer to fill up.
p := buffer [ : ]
var motd bool // Parse all messages as MOTD text until MsgEndMOTD
for {
// Read 1 byte.
n , err := r . Read ( p )
if n <= 0 && nil == err {
continue
} else if n <= 0 && nil != err {
if err . Error ( ) != "EOF" {
lf ( "** Disconnected: %s" , err )
} else {
l ( "** Disconnected" )
}
return
}
b . WriteByte ( p [ 0 ] )
if p [ 0 ] == '\n' {
buf := make ( [ ] byte , b . Len ( ) )
copy ( buf , b . Bytes ( ) )
if debug > 0 {
l ( "<- " + string ( bytes . TrimSpace ( buf ) ) )
}
if c . loggedin {
if ! motd {
buf = bytes . TrimSpace ( buf )
if len ( buf ) == 0 {
b . Reset ( )
continue
}
}
if c . rawMode {
c . In <- append ( [ ] byte ( "** " ) , buf ... )
b . Reset ( )
continue
}
if bytes . HasPrefix ( b . Bytes ( ) , TypeMOTD ) && ! motd {
motd = true
c . motd = append ( c . motd , buf [ 1 : ] ... )
b . Reset ( )
continue
} else if bytes . HasPrefix ( b . Bytes ( ) , TypeEndMOTD ) && motd {
motd = false
c . motd = bytes . TrimSpace ( c . motd )
c . In <- append ( [ ] byte ( "3 " ) , c . motd ... )
b . Reset ( )
continue
} else if motd {
c . motd = append ( c . motd , buf ... )
b . Reset ( )
continue
}
c . In <- buf
}
b . Reset ( )
}
if ! c . loggedin {
bt := strings . TrimSpace ( b . String ( ) )
if bt == "login:" {
b . Reset ( )
c . Out <- [ ] byte ( fmt . Sprintf ( "login bgammon 1008 %s %s" , c . Username , c . Password ) )
c . loggedin = true
}
}
}
}
// CallTELNET is called when a connection is made with the server.
func ( c * Client ) CallTELNET ( ctx telnet . Context , w telnet . Writer , r telnet . Reader ) {
c . tcpConn = w
go c . handleRead ( r )
c . handleWrite ( )
}
2021-08-31 01:26:34 +00:00
func ( c * Client ) keepAlive ( ) {
t := time . NewTicker ( 5 * time . Minute )
for range t . C {
c . Out <- [ ] byte ( "set boardstyle 3" )
}
}
func ( c * Client ) updateWhoInfo ( b [ ] byte ) {
s := bytes . Split ( b , [ ] byte ( " " ) )
if len ( s ) != whoInfoSize {
return
}
info := & WhoInfo {
Username : string ( s [ whoInfoDataName ] ) ,
}
r := s [ whoInfoDataRating ]
if bytes . ContainsRune ( r , '.' ) {
r = s [ whoInfoDataRating ] [ : bytes . IndexByte ( s [ whoInfoDataRating ] , '.' ) ]
}
rating , err := strconv . Atoi ( string ( r ) )
if err != nil {
rating = 0
}
info . Rating = rating
experience , err := strconv . Atoi ( string ( s [ whoInfoDataExperience ] ) )
if err != nil {
experience = 0
}
info . Experience = experience
opponent := ""
if len ( s [ whoInfoDataOpponent ] ) > 1 || s [ whoInfoDataOpponent ] [ 0 ] != '-' {
opponent = string ( s [ whoInfoDataOpponent ] )
}
info . Opponent = opponent
watching := ""
if len ( s [ whoInfoDataWatching ] ) > 1 || s [ whoInfoDataWatching ] [ 0 ] != '-' {
watching = string ( s [ whoInfoDataWatching ] )
}
info . Watching = watching
ready := false
if string ( s [ whoInfoDataReady ] ) == "1" {
ready = true
}
info . Ready = ready
clientName := ""
if len ( s [ whoInfoDataClientName ] ) > 1 || s [ whoInfoDataClientName ] [ 0 ] != '-' {
clientName = string ( s [ whoInfoDataClientName ] )
}
info . ClientName = clientName
status := "Unavailable"
if info . Opponent != "" {
status = "vs. " + info . Opponent
} else if info . Ready {
status = "Available"
}
itemText := info . Username + strings . Repeat ( " " , 18 - len ( info . Username ) )
ratingString := numberPrinter . Sprintf ( "%d" , info . Rating )
itemText += ratingString + strings . Repeat ( " " , 8 - len ( ratingString ) )
experienceString := numberPrinter . Sprintf ( "%d" , info . Experience )
itemText += experienceString + strings . Repeat ( " " , 12 - len ( experienceString ) )
itemText += status
c . who [ string ( s [ whoInfoDataName ] ) ] = info
// TODO who info event
}
func ( c * Client ) GetAllWhoInfo ( ) [ ] * WhoInfo {
w := make ( [ ] * WhoInfo , len ( c . who ) )
var i int
for _ , whoInfo := range c . who {
w [ i ] = whoInfo
i ++
}
return w
}
func ( c * Client ) LoggedIn ( ) bool {
// TODO lock
return c . loggedin
}
func ( c * Client ) WatchRandomGame ( ) {
var options [ ] string
for username , whoInfo := range c . who {
if username != "" && whoInfo . Opponent != "" &&
! strings . Contains ( username , "Bot" ) && ! strings . Contains ( whoInfo . Opponent , "Bot" ) {
options = append ( options , username , whoInfo . Opponent )
}
}
if len ( options ) == 0 {
for username , whoInfo := range c . who {
if username != "" && whoInfo . Opponent != "" {
options = append ( options , username , whoInfo . Opponent )
}
}
if len ( options ) == 0 {
return
}
}
option := options [ rand . Intn ( len ( options ) ) ]
c . Out <- [ ] byte ( "watch " + option )
}
2021-09-08 01:09:53 +00:00
func ( c * Client ) callWebSocket ( ) {
ctx := context . Background ( )
var err error
c . wsConn , _ , err = websocket . Dial ( ctx , c . wsProxyAddress , nil )
if err != nil {
log . Fatal ( "dial error" , err )
}
defer c . wsConn . Close ( websocket . StatusInternalError , "the sky is falling" )
//c.wsConn.Close(websocket.StatusNormalClosure, "")
r , w := io . Pipe ( )
go c . handleRead ( r )
go func ( ) {
for {
_ , data , err := c . wsConn . Read ( context . Background ( ) )
if err != nil {
if err . Error ( ) != "EOF" {
lf ( "** Disconnected: %s" , err )
} else {
l ( "** Disconnected" )
}
}
w . Write ( data )
}
} ( )
c . handleWrite ( )
/ * TODO
err := conn . WriteMessage ( websocket . CloseMessage , websocket . FormatCloseMessage ( websocket . CloseNormalClosure , "" ) )
if err != nil {
log . Println ( "write close:" , err )
return nil
}
select {
case <- done :
case <- time . After ( time . Second ) :
} * /
}
2021-08-31 01:26:34 +00:00
func ( c * Client ) Connect ( ) error {
2021-09-08 01:09:53 +00:00
connectionType := "Telnet"
if c . wsProxyAddress != "" {
connectionType = fmt . Sprintf ( "WebSocket proxy (%s)" , c . wsProxyAddress )
}
l ( fmt . Sprintf ( "Connecting via %s to %s..." , connectionType , c . serverAddress ) )
2021-08-31 01:26:34 +00:00
go c . keepAlive ( )
2021-09-08 01:09:53 +00:00
if c . wsProxyAddress != "" {
go c . callWebSocket ( )
return nil
}
2021-08-31 01:26:34 +00:00
err := telnet . DialToAndCall ( c . serverAddress , c )
if err != nil {
lf ( "** Disconnected: %s" , err )
}
return err
}
func ( c * Client ) eventLoop ( ) {
var setBoardStyle bool
var turnRegexp = regexp . MustCompile ( ` ^turn: (\w+)\. ` )
var movesRegexp = regexp . MustCompile ( ` ^(\w+) moves (.*) ` )
var rollsRegexp = regexp . MustCompile ( ` ^(\w+) rolls? (.*) ` )
var logInOutRegexp = regexp . MustCompile ( ` ^\w+ logs [In|Out]\. ` )
var dropsConnection = regexp . MustCompile ( ` ^\w+ drops connection\. ` )
var startMatchRegexp = regexp . MustCompile ( ` ^\w+ and \w+ start a .* ` )
var winsMatchRegexp = regexp . MustCompile ( ` ^\w+ wins a [0-9]+ point match against .* ` )
2021-09-08 01:09:53 +00:00
var winsThisMatchRegexp = regexp . MustCompile ( ` ^\w+ wins the [0-9]+ point match .* ` )
2021-08-31 01:26:34 +00:00
var newGameRegexp = regexp . MustCompile ( ` ^Starting a new game with .* ` )
2021-09-12 05:20:10 +00:00
var joinedGameRegexp = regexp . MustCompile ( ` ^\w+ has joined you\..* ` )
2021-08-31 01:26:34 +00:00
var gameBufferRegexp = regexp . MustCompile ( ` ^\w+ (makes|roll|rolls|rolled|move|moves) .* ` )
var pleaseMoveRegexp = regexp . MustCompile ( ` ^Please move ([0-9]) pieces?. ` )
var cantMoveRegexp = regexp . MustCompile ( ` ^(\w+) can't move. ` )
var doublesRegexp = regexp . MustCompile ( ` ^\w+ doubles. ` )
var acceptDoubleRegexp = regexp . MustCompile ( ` ^\w+ accepts the double. ` )
for b := range c . In {
b = bytes . Replace ( b , [ ] byte { 7 } , [ ] byte { } , - 1 )
b = bytes . TrimSpace ( b )
bl := bytes . ToLower ( b )
// Select 10+ first to read prefixes correctly
if bytes . HasPrefix ( b , TypeMsgSaved ) {
lf ( "Message to %s saved" , b [ 3 : ] )
continue
} else if bytes . HasPrefix ( b , TypeMsgDelivered ) {
lf ( "Message to %s delivered" , b [ 3 : ] )
continue
} else if bytes . HasPrefix ( b , TypeSay ) {
s := bytes . SplitN ( b [ 3 : ] , [ ] byte ( " " ) , 2 )
lf ( "%s says: %s" , s [ 0 ] , s [ 1 ] )
continue
} else if bytes . HasPrefix ( b , TypeShout ) {
s := bytes . SplitN ( b [ 3 : ] , [ ] byte ( " " ) , 2 )
lf ( "%s shouts: %s" , s [ 0 ] , s [ 1 ] )
continue
} else if bytes . HasPrefix ( b , TypeWhisper ) {
s := bytes . SplitN ( b [ 3 : ] , [ ] byte ( " " ) , 2 )
lf ( "%s whispers: %s" , s [ 0 ] , s [ 1 ] )
continue
} else if bytes . HasPrefix ( b , TypeKibitz ) {
s := bytes . SplitN ( b [ 3 : ] , [ ] byte ( " " ) , 2 )
lf ( "%s kibitzes: %s" , s [ 0 ] , s [ 1 ] )
continue
} else if bytes . HasPrefix ( b , TypeYouSay ) {
s := bytes . SplitN ( b [ 3 : ] , [ ] byte ( " " ) , 2 )
lf ( "You say to %s: %s" , s [ 0 ] , s [ 1 ] )
continue
} else if bytes . HasPrefix ( b , TypeYouShout ) {
lf ( "You shout: %s" , b [ 3 : ] )
continue
} else if bytes . HasPrefix ( b , TypeYouWhisper ) {
lf ( "You whisper: %s" , b [ 3 : ] )
continue
} else if bytes . HasPrefix ( b , TypeYouKibitz ) {
lf ( "You kibitz: %s" , b [ 3 : ] )
continue
} else if bytes . HasPrefix ( b , TypeWelcome ) {
s := bytes . Split ( b [ 2 : ] , [ ] byte ( " " ) )
loginTimestamp , err := strconv . Atoi ( string ( s [ 1 ] ) )
if err != nil {
loginTimestamp = 0
}
loginTime := time . Unix ( int64 ( loginTimestamp ) , 0 )
lf ( "Welcome, %s! Last login at %s" , s [ 0 ] , loginTime )
continue
} else if bytes . HasPrefix ( b , TypeOwnInfo ) {
// TODO Print own info
continue
} else if bytes . HasPrefix ( b , TypeMOTD ) {
for _ , line := range bytes . Split ( c . motd , [ ] byte ( "\n" ) ) {
l ( string ( line ) )
}
if ! setBoardStyle {
c . Out <- [ ] byte ( "set boardstyle 3" )
setBoardStyle = true
}
continue
} else if bytes . HasPrefix ( b , TypeWhoInfo ) {
c . updateWhoInfo ( b [ 2 : ] )
// TODO who window
continue
} else if bytes . HasPrefix ( b , TypeEndWhoInfo ) {
// TODO draw who info event
continue
} else if bytes . HasPrefix ( b , TypeLogin ) || bytes . HasPrefix ( b , TypeLogout ) {
b = b [ 2 : ]
b = b [ bytes . IndexByte ( b , ' ' ) + 1 : ]
l ( string ( b ) )
// TODO enable showing log In and Out messages
// TODO remove from who
continue
} else if bytes . HasPrefix ( b , TypeMsg ) {
lf ( "Received message: %s" , b [ 3 : ] )
continue
} else if bytes . HasPrefix ( b , TypeBoardState ) {
c . Board . SetState ( string ( bytes . SplitN ( b , [ ] byte ( " " ) , 2 ) [ 0 ] [ 6 : ] ) )
2021-09-08 01:09:53 +00:00
c . Event <- & EventBoardState { S : c . Board . s , V : c . Board . v }
2021-08-31 01:26:34 +00:00
continue
} else if turnRegexp . Match ( b ) {
turn := turnRegexp . FindSubmatch ( b )
if string ( turn [ 1 ] ) == c . Username || string ( turn [ 1 ] ) == c . Board . s [ 0 ] || string ( turn [ 1 ] ) == "You" {
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ]
} else {
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ] * - 1
}
2021-09-12 05:20:10 +00:00
c . Event <- EventBoardState { S : c . Board . s , V : c . Board . v }
2021-08-31 01:26:34 +00:00
} else if rollsRegexp . Match ( b ) {
roll := rollsRegexp . FindSubmatch ( b )
periodIndex := bytes . IndexRune ( roll [ 2 ] , '.' )
if periodIndex > - 1 {
roll [ 2 ] = roll [ 2 ] [ : periodIndex ]
}
s := bytes . Split ( roll [ 2 ] , [ ] byte ( " " ) )
var dice [ 2 ] int
var i int
for _ , m := range s {
v , err := strconv . Atoi ( string ( m ) )
if err == nil {
dice [ i ] = v
i ++
}
}
if string ( roll [ 1 ] ) == c . Board . s [ 0 ] || string ( roll [ 1 ] ) == "You" {
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ]
2021-09-08 01:09:53 +00:00
c . Board . v [ StatePlayerDice1 ] = dice [ 0 ]
c . Board . v [ StatePlayerDice2 ] = dice [ 1 ]
2021-08-31 01:26:34 +00:00
} else {
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ] * - 1
2021-09-08 01:09:53 +00:00
c . Board . v [ StateOpponentDice1 ] = dice [ 0 ]
c . Board . v [ StateOpponentDice2 ] = dice [ 1 ]
2021-08-31 01:26:34 +00:00
}
2021-09-08 01:09:53 +00:00
c . Event <- & EventBoardState { S : c . Board . s , V : c . Board . v }
2021-08-31 01:26:34 +00:00
c . Board . ResetMoves ( )
c . Board . Draw ( )
} else if movesRegexp . Match ( b ) {
match := movesRegexp . FindSubmatch ( b )
player := c . Board . v [ StatePlayerColor ]
if string ( match [ 1 ] ) == c . Board . s [ 1 ] {
player *= - 1
}
c . Board . ResetMoves ( )
from := - 1
to := - 1
s := bytes . Split ( match [ 2 ] , [ ] byte ( " " ) )
for _ , m := range s {
move := bytes . Split ( m , [ ] byte ( "-" ) )
if len ( move ) == 2 {
from = c . Board . parseMoveString ( player , string ( move [ 0 ] ) )
to = c . Board . parseMoveString ( player , string ( move [ 1 ] ) )
if from >= 0 && to >= 0 {
c . Event <- & EventMove {
Player : player ,
From : from ,
To : to ,
}
}
c . Board . Move ( player , string ( move [ 0 ] ) , string ( move [ 1 ] ) )
}
}
c . Board . SimplifyMoves ( )
2021-09-13 00:18:54 +00:00
c . Board . v [ StateTurn ] = player * - 1
if c . Board . v [ StateTurn ] == c . Board . v [ StatePlayerColor ] {
c . Board . v [ StatePlayerDice1 ] = 0
c . Board . v [ StatePlayerDice2 ] = 0
} else {
c . Board . v [ StateOpponentDice1 ] = 0
c . Board . v [ StateOpponentDice2 ] = 0
}
2021-08-31 01:26:34 +00:00
c . Board . Draw ( )
bs := string ( b )
if strings . HasSuffix ( bs , " ." ) {
bs = bs [ : len ( bs ) - 2 ] + "."
}
lg ( bs )
continue
} else if string ( b ) == "Value of 'boardstyle' set to 3." {
continue
} 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" {
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ]
c . Board . Draw ( )
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" {
c . Out <- [ ] byte ( "roll" ) // TODO Delay and allow configuring
}
} else if cantMoveRegexp . Match ( b ) {
match := cantMoveRegexp . FindSubmatch ( b )
u := string ( match [ 1 ] )
if u == c . Username || u == c . Board . s [ 0 ] || u == "You" {
2021-09-08 01:09:53 +00:00
//c.Board.opponentDice[0] = 0
//c.Board.opponentDice[1] = 0
2021-08-31 01:26:34 +00:00
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ] * - 1
} else if u == c . Board . s [ 1 ] {
2021-09-08 01:09:53 +00:00
//c.Board.playerDice[0] = 0
//c.Board.playerDice[1] = 0
2021-08-31 01:26:34 +00:00
c . Board . v [ StateTurn ] = c . Board . v [ StatePlayerColor ]
}
} else if pleaseMoveRegexp . Match ( b ) {
match := pleaseMoveRegexp . FindSubmatch ( b )
n , err := strconv . Atoi ( string ( match [ 1 ] ) )
if err == nil {
c . Board . v [ StateMovablePieces ] = n
}
2021-09-12 05:20:10 +00:00
} else if joinedGameRegexp . Match ( b ) {
// Board state is not sent automatically when joining resumed game
c . Out <- [ ] byte ( "board" )
2021-08-31 01:26:34 +00:00
} else if bytes . HasPrefix ( bl , [ ] byte ( "you're now watching" ) ) {
// Board state is not sent automatically when watching
c . Out <- [ ] byte ( "board" )
} else if logInOutRegexp . Match ( b ) {
continue
} else if dropsConnection . Match ( b ) {
continue
} else if startMatchRegexp . Match ( b ) {
continue
2021-09-08 01:09:53 +00:00
} else if winsThisMatchRegexp . Match ( b ) {
if c . tvMode {
go func ( ) {
time . Sleep ( 5 * time . Second )
if ! c . tvMode {
return
}
c . WatchRandomGame ( )
} ( )
}
continue
2021-08-31 01:26:34 +00:00
} else if winsMatchRegexp . Match ( b ) {
continue
} else if newGameRegexp . Match ( b ) {
c . Board . ResetMoves ( )
c . Board . resetSelection ( )
// TODO reset premove
continue
}
if gameBufferRegexp . Match ( b ) || cantMoveRegexp . Match ( b ) ||
doublesRegexp . Match ( b ) || acceptDoubleRegexp . Match ( b ) ||
bytes . HasPrefix ( bl , [ ] byte ( "you're now watching" ) ) || bytes . HasPrefix ( bl , [ ] byte ( "** you stop watching" ) ) ||
strings . HasPrefix ( string ( b ) , "Bearing off:" ) || strings . HasPrefix ( string ( b ) , "The only possible move" ) {
lg ( string ( b ) )
if ! bytes . HasPrefix ( bl , [ ] byte ( "you're now watching" ) ) && ! bytes . HasPrefix ( bl , [ ] byte ( "** you stop watching" ) ) {
continue
}
}
l ( string ( b ) )
}
}