...

Source file src/code.rocketnine.space/tslocum/tabula/bei.go

Documentation: code.rocketnine.space/tslocum/tabula

     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