...

Source file src/code.rocketnine.space/tslocum/fibs/board.go

Documentation: code.rocketnine.space/tslocum/fibs

     1  package fibs
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  )
    11  
    12  const (
    13  	BoxDrawingsLightVertical = '|'
    14  )
    15  
    16  const (
    17  	StateLength = iota
    18  	StatePlayerScore
    19  	StateOpponentScore
    20  	StateBoardSpace0
    21  )
    22  
    23  const (
    24  	StatePlayerName = iota
    25  	StateOpponentName
    26  )
    27  
    28  const (
    29  	StateTurn = 29 + iota
    30  	StatePlayerDice1
    31  	StatePlayerDice2
    32  	StateOpponentDice1
    33  	StateOpponentDice2
    34  	StateDoublingValue
    35  	StatePlayerMayDouble
    36  	StateOpponentMayDouble
    37  	StateWasDoubled
    38  	StatePlayerColor
    39  	StateDirection
    40  	StateObsoleteHome
    41  	StateObsoleteBar
    42  	StatePlayerHome
    43  	StateOpponentHome
    44  	StatePlayerBar
    45  	StateOpponentBar
    46  	StateMovablePieces
    47  	StateObsoletePlayerForced
    48  	StateObsoleteOpponentForced
    49  	StateRedoubles
    50  )
    51  
    52  const (
    53  	SpaceUnknown = -1
    54  )
    55  
    56  const initialState = "FIBS:Welcome:5:0:2:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:-1:0:0:0:0:1:1:1:0:1:-1:0:25:0:0:0:0:4:0:0:0"
    57  
    58  var boardTopWhite = []byte("+13-14-15-16-17-18-+---+19-20-21-22-23-24-+")
    59  var boardBottomWhite = []byte("+12-11-10--9--8--7-+---+-6--5--4--3--2--1-+")
    60  
    61  var boardTopBlack = []byte("+-1--2--3--4--5--6-+---+-7--8--9-10-11-12-+")
    62  var boardBottomBlack = []byte("+24-23-22-21-20-19-+---+18-17-16-15-14-13-+")
    63  
    64  type Board struct {
    65  	client *Client
    66  
    67  	state string
    68  
    69  	s []string
    70  	v []int
    71  
    72  	moves      [][2]int
    73  	movesColor int
    74  
    75  	validMoves map[int][][]int
    76  
    77  	from map[int]int
    78  	to   map[int]int
    79  
    80  	selectedNum   int
    81  	selectedSpace int
    82  
    83  	premove     [][2]int
    84  	Premovefrom map[int]int
    85  	Premoveto   map[int]int
    86  
    87  	dragFromX int
    88  	dragFromY int
    89  
    90  	sync.Mutex
    91  }
    92  
    93  func NewBoard(client *Client) *Board {
    94  	b := &Board{
    95  		client: client,
    96  		s:      make([]string, 52),
    97  		v:      make([]int, 50),
    98  	}
    99  
   100  	b.ResetMoves()
   101  	b.ResetPreMoves()
   102  
   103  	b.SetState(initialState)
   104  
   105  	// TODO
   106  	/*
   107  		b.v[StatePlayerColor] = -1
   108  		b.v[StateBoardSpace0+11] = 12
   109  		b.v[StateBoardSpace0+9] = 7
   110  		b.v[StateBoardSpace0+13] = -13
   111  		b.v[StateBoardSpace0+24] = -3
   112  		b.v[StatePlayerBar] = 3
   113  		b.Update()
   114  	*/
   115  
   116  	return b
   117  }
   118  
   119  // TODO refactor
   120  func (b *Board) GetStringState() []string {
   121  	b.Lock()
   122  	defer b.Unlock()
   123  	return b.s
   124  }
   125  
   126  func (b *Board) GetIntState() []int {
   127  	b.Lock()
   128  	defer b.Unlock()
   129  	return b.v
   130  }
   131  
   132  func (b *Board) resetSelection() {
   133  	b.selectedSpace = 0
   134  	b.selectedNum = 0
   135  }
   136  
   137  func (b *Board) autoSendMoves() {
   138  	movable := 2
   139  	if b.v[StatePlayerDice1] > 0 && b.v[StatePlayerDice1] == b.v[StatePlayerDice2] {
   140  		movable = 4
   141  	}
   142  	if b.v[StateMovablePieces] > 0 {
   143  		movable = b.v[StateMovablePieces]
   144  	}
   145  	if len(b.premove) < movable {
   146  		return
   147  	}
   148  
   149  	moveCommand := []byte("move")
   150  	for j := 0; j < 2; j++ {
   151  		for i := range b.premove {
   152  			var from string
   153  			if b.premove[i][0] == 0 || b.premove[i][0] == 25 {
   154  				from = "bar"
   155  			} else {
   156  				from = strconv.Itoa(b.premove[i][0])
   157  			}
   158  
   159  			if (j == 0) != (from == "bar") {
   160  				continue // Always send bar moves first
   161  			}
   162  
   163  			var to string
   164  			if b.premove[i][1] == b.PlayerBearOffSpace() {
   165  				to = "off"
   166  			} else {
   167  				to = strconv.Itoa(b.premove[i][1])
   168  			}
   169  
   170  			moveCommand = append(moveCommand, []byte(" "+from+"-"+to)...)
   171  		}
   172  
   173  	}
   174  
   175  	b.client.Out <- moveCommand
   176  }
   177  
   178  func (b *Board) GetState() string {
   179  	var s = strings.Join(b.s, ":")
   180  	for i := range b.v {
   181  		s += ":" + strconv.Itoa(b.v[i])
   182  	}
   183  	return s
   184  }
   185  
   186  func (b *Board) SetState(state string) {
   187  	b.Lock()
   188  
   189  	s := strings.Split(state, ":")
   190  	newPlayers := s[StatePlayerName] != b.s[StatePlayerName] || s[StateOpponentName] != b.s[StateOpponentName]
   191  	copy(b.s, s)
   192  
   193  	v := make([]int, 50)
   194  	var err error
   195  	for i := 0; i < 50; i++ {
   196  		v[i], err = strconv.Atoi(b.s[i+2])
   197  		if err != nil {
   198  			log.Fatal(err)
   199  		}
   200  	}
   201  
   202  	newTurn := v[StateTurn] != b.v[StateTurn]
   203  
   204  	// Retain dice rolls
   205  	if !newPlayers && !newTurn {
   206  		copyDice := []int{
   207  			StatePlayerDice1,
   208  			StatePlayerDice2,
   209  			StateOpponentDice1,
   210  			StateOpponentDice2,
   211  		}
   212  		for _, vi := range copyDice {
   213  			if v[vi] == 0 {
   214  				v[vi] = b.v[vi]
   215  			}
   216  		}
   217  	}
   218  
   219  	copy(b.v, v)
   220  
   221  	b.ResetPreMoves()
   222  
   223  	b.Unlock()
   224  	b.Draw()
   225  }
   226  
   227  func (b *Board) Draw() {
   228  	b.client.Event <- &EventDraw{}
   229  }
   230  
   231  func (b *Board) renderSpace(index int, spaceValue int) []byte {
   232  	var playerColor = "x"
   233  	var opponentColor = "o"
   234  	if b.v[StatePlayerColor] == 1 {
   235  		playerColor = "o"
   236  		opponentColor = "x"
   237  	}
   238  
   239  	var pieceColor string
   240  	value := b.v[StateBoardSpace0+index]
   241  	if index == b.PlayerBarSpace() {
   242  		value = b.v[StatePlayerBar]
   243  		pieceColor = playerColor
   244  	} else if index == 25-b.PlayerBarSpace() {
   245  		value = b.v[StateOpponentBar]
   246  		pieceColor = opponentColor
   247  	} else {
   248  		if value < 0 {
   249  			pieceColor = "x"
   250  		} else if value > 0 {
   251  			pieceColor = "o"
   252  		} else {
   253  			pieceColor = playerColor
   254  		}
   255  	}
   256  
   257  	abs := value
   258  	if value < 0 {
   259  		abs = value * -1
   260  	}
   261  
   262  	top := index <= 12
   263  	if b.v[StatePlayerColor] == 1 {
   264  		top = !top
   265  	}
   266  
   267  	firstDigit := 4
   268  	secondDigit := 5
   269  	if !top {
   270  		firstDigit = 5
   271  		secondDigit = 4
   272  	}
   273  
   274  	var firstNumeral string
   275  	var secondNumeral string
   276  	if abs > 5 {
   277  		if abs > 9 {
   278  			firstNumeral = "1"
   279  		} else {
   280  			firstNumeral = strconv.Itoa(abs)
   281  		}
   282  		if abs > 9 {
   283  			secondNumeral = strconv.Itoa(abs - 10)
   284  		}
   285  
   286  		if spaceValue == firstDigit && (!top || abs > 9) {
   287  			pieceColor = firstNumeral
   288  		} else if spaceValue == secondDigit && abs > 9 {
   289  			pieceColor = secondNumeral
   290  		} else if top && spaceValue == secondDigit {
   291  			pieceColor = firstNumeral
   292  		}
   293  	}
   294  
   295  	if abs > 5 {
   296  		abs = 5
   297  	}
   298  
   299  	var r []byte
   300  	foregroundColor := "#FFFFFF"
   301  	backgroundColor := "#000000"
   302  	if index != 0 && index != 25 {
   303  		if true { // default theme
   304  			if index%2 == 0 {
   305  				backgroundColor = "#303030"
   306  			} else {
   307  				backgroundColor = "#101010"
   308  			}
   309  		} else { // rainbow
   310  			foregroundColor = "#000000"
   311  			switch index % 6 {
   312  			case 1:
   313  				backgroundColor = "#FF0000"
   314  			case 2:
   315  				backgroundColor = "#FFA500"
   316  			case 3:
   317  				backgroundColor = "#FFFF00"
   318  			case 4:
   319  				backgroundColor = "#008000"
   320  			case 5:
   321  				backgroundColor = "#0000FF"
   322  			case 0:
   323  				backgroundColor = "#4B0082"
   324  			}
   325  		}
   326  	}
   327  	// Highlight legal moves
   328  	highlightSpace := b.ValidMove(b.selectedSpace, index)
   329  	highlightSpace = false // TODO Make configurable, disable by default
   330  	if b.selectedNum > 0 && highlightSpace && index != 25 && index != 0 {
   331  		foregroundColor = "black"
   332  		backgroundColor = "yellow"
   333  	}
   334  	if abs > 0 && spaceValue <= abs {
   335  		r = []byte(pieceColor)
   336  	} else {
   337  		r = []byte(" ")
   338  	}
   339  
   340  	rightArrowFrom := (b.v[StateDirection] == b.movesColor) == (index > 12)
   341  	if b.selectedSpace == index && b.selectedNum > 0 && spaceValue <= abs && spaceValue > abs-b.selectedNum {
   342  		r = []byte("*")
   343  	} else if b.Premovefrom[index] > 0 && spaceValue > (abs+b.Premoveto[index])-b.Premovefrom[index] && spaceValue <= abs+b.Premoveto[index] {
   344  		if index == 25-b.PlayerBarSpace() {
   345  			r = []byte("▾")
   346  		} else if index == b.PlayerBarSpace() {
   347  			r = []byte("▴")
   348  		} else if rightArrowFrom {
   349  			r = []byte("▸")
   350  		} else {
   351  			r = []byte("◂")
   352  		}
   353  		foregroundColor = "yellow"
   354  	} else if b.Premoveto[index] > 0 && spaceValue > abs && spaceValue <= abs+(b.Premoveto[index]) {
   355  		r = []byte(playerColor)
   356  		foregroundColor = "yellow"
   357  	} else if b.from[index] > 0 && spaceValue > abs && spaceValue <= abs+b.from[index] {
   358  		if rightArrowFrom {
   359  			r = []byte("▸")
   360  		} else {
   361  			r = []byte("◂")
   362  		}
   363  		if b.movesColor == b.v[StatePlayerColor] {
   364  			foregroundColor = "yellow"
   365  		} else {
   366  			foregroundColor = "green"
   367  		}
   368  	} else if b.to[index] > 0 && spaceValue > abs-(b.to[index]+b.from[index]) {
   369  		if b.movesColor == b.v[StatePlayerColor] {
   370  			foregroundColor = "yellow"
   371  		} else {
   372  			foregroundColor = "green"
   373  		}
   374  	}
   375  
   376  	return append(append([]byte(fmt.Sprintf("[\"space-%d\"][%s:%s:b] ", index, foregroundColor, backgroundColor)), r...), []byte(" [-:-:-][\"\"]")...)
   377  }
   378  
   379  func (b *Board) ResetMoves() {
   380  	b.moves = nil
   381  	b.movesColor = 0
   382  	b.validMoves = make(map[int][][]int)
   383  	b.from = make(map[int]int)
   384  	b.to = make(map[int]int)
   385  }
   386  
   387  func (b *Board) ResetPreMoves() {
   388  	b.premove = nil
   389  	b.Premovefrom = make(map[int]int)
   390  	b.Premoveto = make(map[int]int)
   391  }
   392  
   393  func (b *Board) PlayerHomeSpaces() (int, int) {
   394  	homeBoardStart := 1
   395  	homeBoardEnd := 6
   396  	if (b.v[StateDirection] == -1) == (b.v[StatePlayerColor] == -1) {
   397  		homeBoardStart = 19
   398  		homeBoardEnd = 24
   399  	}
   400  	return homeBoardStart, homeBoardEnd
   401  }
   402  
   403  func (b *Board) PlayerPieceAreHome() bool {
   404  	homeBoardStart, homeBoardEnd := b.PlayerHomeSpaces()
   405  	hasPlayerPiece := func(index int) bool {
   406  		if index < 0 || index > 25 {
   407  			return false
   408  		}
   409  		value := b.v[StateBoardSpace0+index]
   410  
   411  		// Include pre-moves
   412  		mod := b.v[StatePlayerColor]
   413  		value -= b.client.Board.Premovefrom[index] * mod
   414  
   415  		if b.v[StatePlayerColor] == -1 {
   416  			return value < 0
   417  		}
   418  		return value > 0
   419  	}
   420  	for i := 1; i < 24; i++ {
   421  		if i >= homeBoardStart && i <= homeBoardEnd {
   422  			continue
   423  		}
   424  		if hasPlayerPiece(i) {
   425  			return false
   426  		}
   427  	}
   428  	return true
   429  }
   430  
   431  func (b *Board) spaceAvailable(index int) bool {
   432  	if index < 0 || index > 25 {
   433  		return false
   434  	}
   435  	if index == 0 || index == 25 {
   436  		return b.PlayerPieceAreHome()
   437  	}
   438  	return (b.v[StatePlayerColor] == 1 && b.v[StateBoardSpace0+index] >= -1) ||
   439  		(b.v[StatePlayerColor] == -1 && b.v[StateBoardSpace0+index] <= 1)
   440  }
   441  
   442  func (b *Board) GetValidMoves(from int) [][]int {
   443  	if validMoves, ok := b.validMoves[from]; ok {
   444  		return validMoves
   445  	}
   446  
   447  	var validMoves [][]int
   448  	defer func() {
   449  		b.validMoves[from] = validMoves
   450  	}()
   451  
   452  	if b.v[StateTurn] != b.v[StatePlayerColor] || b.v[StatePlayerDice1] == 0 || b.v[StatePlayerDice2] == 0 {
   453  		return validMoves
   454  	}
   455  
   456  	trySpaces := [][]int{
   457  		{b.v[StatePlayerDice1]},
   458  		{b.v[StatePlayerDice2]},
   459  		{b.v[StatePlayerDice1], b.v[StatePlayerDice2]},
   460  		{b.v[StatePlayerDice2], b.v[StatePlayerDice1]},
   461  	}
   462  	if b.v[StatePlayerDice1] == b.v[StatePlayerDice2] {
   463  		trySpaces = append(trySpaces,
   464  			[]int{b.v[StatePlayerDice1], b.v[StatePlayerDice1], b.v[StatePlayerDice1]},
   465  			[]int{b.v[StatePlayerDice1], b.v[StatePlayerDice1], b.v[StatePlayerDice1], b.v[StatePlayerDice1]})
   466  	}
   467  
   468  	if b.PlayerPieceAreHome() {
   469  		homeSpace := b.PlayerBearOffSpace()
   470  		spacesHome := from - homeSpace
   471  		if spacesHome < 0 {
   472  			spacesHome *= -1
   473  		}
   474  		if spacesHome <= b.v[StatePlayerDice1] || spacesHome <= b.v[StatePlayerDice2] {
   475  			trySpaces = append(trySpaces, []int{spacesHome})
   476  		}
   477  	}
   478  	foundMoves := make(map[int]bool)
   479  CHECKSPACES:
   480  	for i := range trySpaces {
   481  		checkSpace := 0
   482  		for _, space := range trySpaces[i] {
   483  			checkSpace += space
   484  			if !b.spaceAvailable(from + (checkSpace * b.v[StateDirection])) {
   485  				continue CHECKSPACES
   486  			}
   487  		}
   488  		space := from + (checkSpace * b.v[StateDirection])
   489  		if _, value := foundMoves[space]; !value {
   490  			foundMoves[space] = true
   491  			validMoves = append(validMoves, trySpaces[i])
   492  		}
   493  	}
   494  
   495  	return validMoves
   496  }
   497  
   498  func (b *Board) PlayerBarSpace() int {
   499  	return 25 - b.PlayerBearOffSpace()
   500  }
   501  
   502  func (b *Board) PlayerBearOffSpace() int {
   503  	if b.v[StateDirection] == -1 {
   504  		return 0
   505  	}
   506  	return 25
   507  }
   508  
   509  func (b *Board) ValidMove(f int, t int) bool {
   510  	if b.v[StateTurn] != b.v[StatePlayerColor] || b.v[StatePlayerDice1] == 0 || b.v[StatePlayerDice2] == 0 {
   511  		return false
   512  	}
   513  
   514  	if t == b.PlayerBearOffSpace() {
   515  		// TODO bear off logic, only allow high roll
   516  		return b.PlayerPieceAreHome()
   517  	}
   518  
   519  	validMoves := b.GetValidMoves(f)
   520  CHECKVALID:
   521  	for i := range validMoves {
   522  		checkSpace := 0
   523  		for _, space := range validMoves[i] {
   524  			checkSpace += space
   525  			if !b.spaceAvailable(f + (checkSpace * b.v[StateDirection])) {
   526  				continue CHECKVALID
   527  			}
   528  		}
   529  		if f+(checkSpace*b.v[StateDirection]) == t {
   530  			return true
   531  		}
   532  	}
   533  	return false
   534  }
   535  
   536  func (b *Board) parseMoveString(player int, s string) int {
   537  	space, err := strconv.Atoi(s)
   538  	if err != nil {
   539  		space = SpaceUnknown
   540  		if s == "bar" {
   541  			barSpace := b.PlayerBarSpace()
   542  			if b.v[StatePlayerColor] == player {
   543  				space = barSpace
   544  			} else {
   545  				space = 25 - barSpace
   546  			}
   547  		} else if s == "off" {
   548  			space = b.PlayerBearOffSpace()
   549  		}
   550  	}
   551  	return space
   552  }
   553  
   554  func (b *Board) Move(player int, f string, t string) {
   555  	from := b.parseMoveString(player, f)
   556  	to := b.parseMoveString(player, t)
   557  
   558  	if from == SpaceUnknown || to == SpaceUnknown {
   559  		lf("WARNING: Unknown move %s-%s", f, t)
   560  		return
   561  	}
   562  
   563  	b.moves = append(b.moves, [2]int{from, to})
   564  	b.movesColor = player
   565  
   566  	b.from[from]++
   567  	b.to[to]++
   568  
   569  	spaceValue := b.v[StateBoardSpace0+to]
   570  
   571  	// Hit.
   572  	if (spaceValue == -1 && player == 1) || (spaceValue == 1 && player == -1) {
   573  		bar := 25 - b.PlayerBarSpace()
   574  		if player == b.v[StatePlayerColor] {
   575  			bar = b.PlayerBarSpace()
   576  		}
   577  
   578  		b.v[StateBoardSpace0+bar] -= player
   579  	}
   580  
   581  	b.v[StateBoardSpace0+from] -= player
   582  	b.v[StateBoardSpace0+to] += player
   583  
   584  	b.v[StateTurn] = player * -1
   585  
   586  	b.validMoves = make(map[int][][]int)
   587  	b.ResetPreMoves()
   588  }
   589  
   590  func (b *Board) SimplifyMoves() {
   591  	for i := range b.moves {
   592  		for j := range b.moves {
   593  			if b.moves[i][1] == b.moves[j][0] {
   594  				// Same to space as from space
   595  				b.moves[j][0] = b.moves[i][0] // Set from space to earlier from space
   596  				b.moves = append(b.moves[:i], b.moves[i+1:]...)
   597  				b.SimplifyMoves()
   598  				return
   599  			} else if b.moves[i][0] == b.moves[j][1] {
   600  				// Same to space as from space
   601  				b.moves[j][1] = b.moves[i][1] // Set to space to earlier to space
   602  				b.moves = append(b.moves[:i], b.moves[i+1:]...)
   603  				b.SimplifyMoves()
   604  				return
   605  			}
   606  		}
   607  	}
   608  }
   609  
   610  func (b *Board) GetSelection() (num int, space int) {
   611  	return b.selectedNum, b.selectedSpace
   612  }
   613  
   614  func (b *Board) SetSelection(num int, space int) {
   615  	b.selectedNum, b.selectedSpace = num, space
   616  }
   617  
   618  func (b *Board) ResetSelection() {
   619  	b.selectedNum, b.selectedSpace = 0, 0
   620  }
   621  
   622  func (b *Board) addPreMove(from int, to int, num int) bool {
   623  	// Allow bearing off when the player moves their own pieces on to the bar
   624  	if to == 0 || to == 25 {
   625  		to = b.PlayerBearOffSpace()
   626  	}
   627  
   628  	// Expand combined move
   629  	moves := b.client.Board.GetValidMoves(from)
   630  
   631  CHECKPREMOVES:
   632  	for i := range moves {
   633  		checkSpace := 0
   634  		for _, space := range moves[i] {
   635  			checkSpace += space
   636  			if !b.spaceAvailable(from + (checkSpace * b.v[StateDirection])) {
   637  				continue CHECKPREMOVES
   638  			}
   639  		}
   640  		if (from+(checkSpace*b.v[StateDirection]) == to) && len(moves[i]) > 1 {
   641  			for j := 0; j < num; j++ {
   642  				checkSpace = 0
   643  				lastSpace := 0
   644  				for _, space := range moves[i] {
   645  					checkSpace += space
   646  					if !b.addPreMove(from+(lastSpace*b.v[StateDirection]), from+(checkSpace*b.v[StateDirection]), 1) {
   647  						return false
   648  					}
   649  					lastSpace = checkSpace
   650  				}
   651  			}
   652  			return true
   653  		}
   654  	}
   655  
   656  	if !b.ValidMove(from, to) {
   657  		return false
   658  	}
   659  
   660  	for i := 0; i < num; i++ {
   661  		b.premove = append(b.premove, [2]int{from, to})
   662  		b.Premovefrom[from]++
   663  		b.Premoveto[to]++
   664  	}
   665  	return true
   666  }
   667  
   668  func (b *Board) AddPreMove(from int, to int) bool {
   669  	if !b.addPreMove(from, to, b.selectedNum) {
   670  		return false
   671  	}
   672  
   673  	b.resetSelection()
   674  	b.autoSendMoves()
   675  	return true
   676  }
   677  
   678  func (b *Board) GetPreMoves() [][2]int {
   679  	return b.premove
   680  }
   681  
   682  func (b *Board) Render() []byte {
   683  	b.Lock()
   684  
   685  	var white bool
   686  	if b.v[StatePlayerColor] == 1 {
   687  		white = true
   688  	}
   689  
   690  	var opponentName = b.s[1]
   691  	var playerName = b.s[0]
   692  
   693  	var playerColor = "x"
   694  	var opponentColor = "o"
   695  	if white {
   696  		playerColor = "o"
   697  		opponentColor = "x"
   698  	}
   699  
   700  	var t bytes.Buffer
   701  	t.WriteString("[\"space-off\"]                                                            [\"\"]\n")
   702  	t.WriteString("[\"space-off\"]                                                            [\"\"]\n")
   703  	t.WriteString("[\"space-off\"]    ")
   704  	if white {
   705  		t.Write(boardTopWhite)
   706  	} else {
   707  		t.Write(boardTopBlack)
   708  	}
   709  	t.WriteString("[\"\"] ")
   710  	t.WriteByte('\n')
   711  
   712  	space := func(i int, j int) []byte {
   713  		spaceValue := i + 1
   714  		if i > 5 {
   715  			spaceValue = 5 - (i - 6)
   716  		}
   717  
   718  		if j == -1 {
   719  			if i <= 4 {
   720  				return b.renderSpace(25-b.PlayerBarSpace(), spaceValue)
   721  			}
   722  			return b.renderSpace(b.PlayerBarSpace(), spaceValue)
   723  		}
   724  
   725  		var index int
   726  		if !white {
   727  			if i < 6 {
   728  				j = 12 - j
   729  			} else {
   730  				j = 11 - j
   731  			}
   732  
   733  			index = 12 + j
   734  			if i > 5 {
   735  				index = 12 - j
   736  			}
   737  		} else {
   738  			index = 12 + j
   739  			if i > 5 {
   740  				index = 11 - j
   741  			}
   742  		}
   743  		if !white {
   744  			index = 24 - index
   745  		}
   746  		index++ // increment to get actual space number (0 is bar)
   747  
   748  		if i == 5 {
   749  			return []byte("[-:#000000]   [-:-]")
   750  		}
   751  
   752  		return b.renderSpace(index, spaceValue)
   753  	}
   754  
   755  	for i := 0; i < 11; i++ {
   756  		t.Write([]byte("[\"space-off\"]"))
   757  
   758  		if i == 5 && b.v[StateDoublingValue] > 1 {
   759  			t.WriteString(fmt.Sprintf("%2d ", b.v[StateDoublingValue]))
   760  			if b.v[StatePlayerMayDouble] == 1 {
   761  				t.WriteByte('v')
   762  			} else {
   763  				t.WriteByte('^')
   764  			}
   765  		} else {
   766  			t.WriteByte(' ')
   767  			t.WriteByte(' ')
   768  			t.WriteByte(' ')
   769  			t.WriteByte(' ')
   770  		}
   771  
   772  		t.WriteRune(BoxDrawingsLightVertical)
   773  		t.Write([]byte("[\"\"]"))
   774  		for j := 0; j < 12; j++ {
   775  			t.Write(space(i, j))
   776  
   777  			if j == 5 {
   778  				t.WriteRune(BoxDrawingsLightVertical)
   779  				t.Write(space(i, -1))
   780  				t.WriteRune(BoxDrawingsLightVertical)
   781  			}
   782  		}
   783  
   784  		t.Write([]byte("[\"space-off\"]" + string(BoxDrawingsLightVertical) + "  "))
   785  
   786  		playerRollColor := "yellow"
   787  		playerBold := "b"
   788  		opponentRollColor := "white"
   789  		opponentBold := ""
   790  		if b.v[StateTurn] != b.v[StatePlayerColor] {
   791  			playerRollColor = "white"
   792  			opponentRollColor = "green"
   793  			playerBold = ""
   794  			opponentBold = "b"
   795  		}
   796  
   797  		if i == 0 {
   798  			t.Write([]byte("[" + opponentRollColor + "::" + opponentBold + "]" + opponentColor + " " + opponentName + " (" + b.s[4] + ")"))
   799  			if b.v[StateOpponentHome] > 0 {
   800  				t.Write([]byte(fmt.Sprintf("  %d off", b.v[StateOpponentHome])))
   801  			}
   802  			t.Write([]byte("[-::-]"))
   803  		} else if i == 2 {
   804  			if b.v[StateOpponentDice1] > 0 {
   805  				t.Write([]byte(fmt.Sprintf("  [%s::%s]%d  %d[-::-]  ", opponentRollColor, opponentBold, b.v[StateOpponentDice1], b.v[StateOpponentDice2])))
   806  			} else {
   807  				t.Write([]byte(fmt.Sprintf("  [%s]-  -[-]  ", opponentRollColor)))
   808  			}
   809  		} else if i == 8 {
   810  			if b.v[StatePlayerDice1] > 0 {
   811  				t.Write([]byte(fmt.Sprintf("  [%s::%s]%d  %d[-::-]  ", playerRollColor, playerBold, b.v[StatePlayerDice1], b.v[StatePlayerDice2])))
   812  			} else {
   813  				t.Write([]byte(fmt.Sprintf("  [%s]-  -[-]  ", playerRollColor)))
   814  			}
   815  		} else if i == 10 {
   816  			t.Write([]byte("[" + playerRollColor + "::" + playerBold + "]" + playerColor + " " + playerName + " (" + b.s[3] + ")"))
   817  			if b.v[StatePlayerHome] > 0 {
   818  				t.Write([]byte(fmt.Sprintf("  %d off", b.v[StatePlayerHome])))
   819  			}
   820  			t.Write([]byte("[-::-]"))
   821  		}
   822  
   823  		t.Write([]byte("[\"\"] "))
   824  		t.WriteByte('\n')
   825  	}
   826  
   827  	t.WriteString("[\"space-off\"]    ")
   828  	if white {
   829  		t.Write(boardBottomWhite)
   830  	} else {
   831  		t.Write(boardBottomBlack)
   832  	}
   833  	t.WriteString("                 [\"\"]\n")
   834  	t.WriteString("[\"space-off\"]                                                            [\"\"]")
   835  
   836  	b.Unlock()
   837  
   838  	return t.Bytes()
   839  }
   840  

View as plain text