Allow displaying previous moves on board
This commit is contained in:
parent
67380585cf
commit
e530ba7fa1
5 changed files with 142 additions and 54 deletions
|
@ -1,5 +1,6 @@
|
|||
1.1.8:
|
||||
- Allow bearing checkers off by double clicking
|
||||
- Allow displaying previous moves on board
|
||||
- Fade out dice rolls instead of hiding them
|
||||
|
||||
1.1.7:
|
||||
|
|
168
game/board.go
168
game/board.go
|
@ -1,6 +1,7 @@
|
|||
package game
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
@ -61,6 +62,9 @@ type board struct {
|
|||
playerRoll1, playerRoll2 int
|
||||
playerRollStale bool
|
||||
|
||||
opponentMoves [][]int
|
||||
playerMoves [][]int
|
||||
|
||||
debug int // Print and draw debug information
|
||||
|
||||
Client *Client
|
||||
|
@ -85,8 +89,8 @@ type board struct {
|
|||
opponentLabel *Label
|
||||
playerLabel *Label
|
||||
|
||||
opponentMoves *etk.Text
|
||||
playerMoves *etk.Text
|
||||
opponentMovesLabel *etk.Text
|
||||
playerMovesLabel *etk.Text
|
||||
|
||||
opponentPipCount *etk.Text
|
||||
playerPipCount *etk.Text
|
||||
|
@ -97,8 +101,9 @@ type board struct {
|
|||
|
||||
menuGrid *etk.Grid
|
||||
|
||||
showPipCountCheckbox *etk.Checkbox
|
||||
highlightCheckbox *etk.Checkbox
|
||||
showPipCountCheckbox *etk.Checkbox
|
||||
showMovesCheckbox *etk.Checkbox
|
||||
settingsGrid *etk.Grid
|
||||
|
||||
matchStatusGrid *etk.Grid
|
||||
|
@ -121,8 +126,9 @@ type board struct {
|
|||
lineHeight int
|
||||
lineOffset int
|
||||
|
||||
showPipCount bool
|
||||
highlightAvailable bool
|
||||
showPipCount bool
|
||||
showMoves bool
|
||||
|
||||
widget *BoardWidget
|
||||
|
||||
|
@ -157,8 +163,8 @@ func NewBoard() *board {
|
|||
foundMoves: make(map[int]bool),
|
||||
opponentLabel: NewLabel(color.RGBA{255, 255, 255, 255}),
|
||||
playerLabel: NewLabel(color.RGBA{0, 0, 0, 255}),
|
||||
opponentMoves: etk.NewText(""),
|
||||
playerMoves: etk.NewText(""),
|
||||
opponentMovesLabel: etk.NewText(""),
|
||||
playerMovesLabel: etk.NewText(""),
|
||||
opponentPipCount: etk.NewText("0"),
|
||||
playerPipCount: etk.NewText("0"),
|
||||
buttonsGrid: etk.NewGrid(),
|
||||
|
@ -191,20 +197,20 @@ func NewBoard() *board {
|
|||
t.SetScrollBarVisible(false)
|
||||
}
|
||||
|
||||
centerText(b.opponentMoves)
|
||||
centerText(b.playerMoves)
|
||||
centerText(b.opponentMovesLabel)
|
||||
centerText(b.playerMovesLabel)
|
||||
|
||||
centerText(b.opponentPipCount)
|
||||
centerText(b.playerPipCount)
|
||||
|
||||
b.opponentMoves.SetHorizontal(messeji.AlignStart)
|
||||
b.playerMoves.SetHorizontal(messeji.AlignEnd)
|
||||
b.opponentMovesLabel.SetHorizontal(messeji.AlignStart)
|
||||
b.playerMovesLabel.SetHorizontal(messeji.AlignEnd)
|
||||
|
||||
b.opponentPipCount.SetHorizontal(messeji.AlignEnd)
|
||||
b.playerPipCount.SetHorizontal(messeji.AlignStart)
|
||||
|
||||
b.opponentMoves.SetForegroundColor(color.RGBA{255, 255, 255, 255})
|
||||
b.playerMoves.SetForegroundColor(color.RGBA{0, 0, 0, 255})
|
||||
b.opponentMovesLabel.SetForegroundColor(color.RGBA{255, 255, 255, 255})
|
||||
b.playerMovesLabel.SetForegroundColor(color.RGBA{0, 0, 0, 255})
|
||||
|
||||
b.opponentPipCount.SetForegroundColor(color.RGBA{255, 255, 255, 255})
|
||||
b.playerPipCount.SetForegroundColor(color.RGBA{0, 0, 0, 255})
|
||||
|
@ -224,20 +230,6 @@ func NewBoard() *board {
|
|||
settingsLabel := etk.NewText(gotext.Get("Settings"))
|
||||
settingsLabel.SetHorizontal(messeji.AlignCenter)
|
||||
|
||||
b.showPipCountCheckbox = etk.NewCheckbox(b.togglePipCountCheckbox)
|
||||
b.showPipCountCheckbox.SetBorderColor(triangleA)
|
||||
b.showPipCountCheckbox.SetCheckColor(triangleA)
|
||||
b.showPipCountCheckbox.SetSelected(b.showPipCount)
|
||||
|
||||
pipCountLabel := &ClickableText{
|
||||
Text: etk.NewText(gotext.Get("Show pip count")),
|
||||
onSelected: func() {
|
||||
b.showPipCountCheckbox.SetSelected(!b.showPipCountCheckbox.Selected())
|
||||
b.togglePipCountCheckbox()
|
||||
},
|
||||
}
|
||||
pipCountLabel.SetVertical(messeji.AlignCenter)
|
||||
|
||||
b.highlightCheckbox = etk.NewCheckbox(b.toggleHighlightCheckbox)
|
||||
b.highlightCheckbox.SetBorderColor(triangleA)
|
||||
b.highlightCheckbox.SetCheckColor(triangleA)
|
||||
|
@ -252,16 +244,46 @@ func NewBoard() *board {
|
|||
}
|
||||
highlightLabel.SetVertical(messeji.AlignCenter)
|
||||
|
||||
b.showPipCountCheckbox = etk.NewCheckbox(b.togglePipCountCheckbox)
|
||||
b.showPipCountCheckbox.SetBorderColor(triangleA)
|
||||
b.showPipCountCheckbox.SetCheckColor(triangleA)
|
||||
b.showPipCountCheckbox.SetSelected(b.showPipCount)
|
||||
|
||||
pipCountLabel := &ClickableText{
|
||||
Text: etk.NewText(gotext.Get("Show pip count")),
|
||||
onSelected: func() {
|
||||
b.showPipCountCheckbox.SetSelected(!b.showPipCountCheckbox.Selected())
|
||||
b.togglePipCountCheckbox()
|
||||
},
|
||||
}
|
||||
pipCountLabel.SetVertical(messeji.AlignCenter)
|
||||
|
||||
b.showMovesCheckbox = etk.NewCheckbox(b.toggleMovesCheckbox)
|
||||
b.showMovesCheckbox.SetBorderColor(triangleA)
|
||||
b.showMovesCheckbox.SetCheckColor(triangleA)
|
||||
b.showMovesCheckbox.SetSelected(b.showMoves)
|
||||
|
||||
movesLabel := &ClickableText{
|
||||
Text: etk.NewText(gotext.Get("Show moves")),
|
||||
onSelected: func() {
|
||||
b.showMovesCheckbox.SetSelected(!b.showMovesCheckbox.Selected())
|
||||
b.toggleMovesCheckbox()
|
||||
},
|
||||
}
|
||||
movesLabel.SetVertical(messeji.AlignCenter)
|
||||
|
||||
checkboxGrid := etk.NewGrid()
|
||||
checkboxGrid.SetRowSizes(-1, 20, -1)
|
||||
checkboxGrid.AddChildAt(b.showPipCountCheckbox, 0, 0, 1, 1)
|
||||
checkboxGrid.AddChildAt(pipCountLabel, 1, 0, 4, 1)
|
||||
checkboxGrid.AddChildAt(b.highlightCheckbox, 0, 2, 1, 1)
|
||||
checkboxGrid.AddChildAt(highlightLabel, 1, 2, 4, 1)
|
||||
checkboxGrid.SetRowSizes(-1, 20, -1, 20, -1)
|
||||
checkboxGrid.AddChildAt(b.highlightCheckbox, 0, 0, 1, 1)
|
||||
checkboxGrid.AddChildAt(highlightLabel, 1, 0, 4, 1)
|
||||
checkboxGrid.AddChildAt(b.showPipCountCheckbox, 0, 2, 1, 1)
|
||||
checkboxGrid.AddChildAt(pipCountLabel, 1, 2, 4, 1)
|
||||
checkboxGrid.AddChildAt(b.showMovesCheckbox, 0, 4, 1, 1)
|
||||
checkboxGrid.AddChildAt(movesLabel, 1, 4, 4, 1)
|
||||
|
||||
b.settingsGrid.SetBackground(color.RGBA{40, 24, 9, 255})
|
||||
b.settingsGrid.SetColumnSizes(20, -1, -1, 20)
|
||||
b.settingsGrid.SetRowSizes(72, 72+20+72, 20, -1)
|
||||
b.settingsGrid.SetRowSizes(72, 72+20+72+20+72, 20, -1)
|
||||
b.settingsGrid.AddChildAt(settingsLabel, 1, 0, 2, 1)
|
||||
b.settingsGrid.AddChildAt(checkboxGrid, 1, 1, 2, 1)
|
||||
b.settingsGrid.AddChildAt(etk.NewBox(), 1, 2, 1, 1)
|
||||
|
@ -323,13 +345,13 @@ func NewBoard() *board {
|
|||
|
||||
{
|
||||
f := etk.NewFrame()
|
||||
f.AddChild(b.opponentMoves)
|
||||
f.AddChild(b.opponentMovesLabel)
|
||||
f.AddChild(b.opponentPipCount)
|
||||
f.AddChild(b.opponentLabel)
|
||||
f.AddChild(b.opponentLabel)
|
||||
f.AddChild(b.playerLabel)
|
||||
f.AddChild(b.playerPipCount)
|
||||
f.AddChild(b.playerMoves)
|
||||
f.AddChild(b.playerMovesLabel)
|
||||
f.AddChild(b.uiGrid)
|
||||
b.frame.AddChild(f)
|
||||
}
|
||||
|
@ -421,8 +443,8 @@ func (b *board) fontUpdated() {
|
|||
b.timerLabel.SetFont(b.fontFace, fontMutex)
|
||||
b.clockLabel.SetFont(b.fontFace, fontMutex)
|
||||
|
||||
b.opponentMoves.SetFont(bufferFont, fontMutex)
|
||||
b.playerMoves.SetFont(bufferFont, fontMutex)
|
||||
b.opponentMovesLabel.SetFont(bufferFont, fontMutex)
|
||||
b.playerMovesLabel.SetFont(bufferFont, fontMutex)
|
||||
|
||||
b.opponentPipCount.SetFont(bufferFont, fontMutex)
|
||||
b.playerPipCount.SetFont(bufferFont, fontMutex)
|
||||
|
@ -664,6 +686,11 @@ func (b *board) selectResign() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *board) toggleHighlightCheckbox() error {
|
||||
b.highlightAvailable = b.highlightCheckbox.Selected()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *board) togglePipCountCheckbox() error {
|
||||
b.showPipCount = b.showPipCountCheckbox.Selected()
|
||||
b.updatePlayerLabel()
|
||||
|
@ -671,8 +698,9 @@ func (b *board) togglePipCountCheckbox() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *board) toggleHighlightCheckbox() error {
|
||||
b.highlightAvailable = b.highlightCheckbox.Selected()
|
||||
func (b *board) toggleMovesCheckbox() error {
|
||||
b.showMoves = b.showMovesCheckbox.Selected()
|
||||
b.processState()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1241,7 +1269,7 @@ func (b *board) setRect(x, y, w, h int) {
|
|||
if dialogWidth > game.screenW {
|
||||
dialogWidth = game.screenW
|
||||
}
|
||||
dialogHeight := 72 + 72 + 20 + 72 + 20 + game.scale(baseButtonHeight)
|
||||
dialogHeight := 72 + 72 + 20 + 72 + 20 + 72 + 20 + game.scale(baseButtonHeight)
|
||||
if dialogHeight > game.screenH {
|
||||
dialogHeight = game.screenH
|
||||
}
|
||||
|
@ -1286,8 +1314,8 @@ func (b *board) setRect(x, y, w, h int) {
|
|||
} else if b.w >= 100 {
|
||||
padding = 5
|
||||
}
|
||||
b.opponentMoves.SetPadding(padding)
|
||||
b.playerMoves.SetPadding(padding / 2)
|
||||
b.opponentMovesLabel.SetPadding(padding)
|
||||
b.playerMovesLabel.SetPadding(padding / 2)
|
||||
b.opponentPipCount.SetPadding(padding / 2)
|
||||
b.playerPipCount.SetPadding(padding)
|
||||
}
|
||||
|
@ -1328,8 +1356,8 @@ func (b *board) updateOpponentLabel() {
|
|||
return
|
||||
}
|
||||
{
|
||||
newRect := image.Rect(int(b.horizontalBorderSize), y-bounds.Dy(), x, y+bounds.Dy()*2)
|
||||
b.opponentMoves.SetRect(newRect)
|
||||
newRect := image.Rect(int(b.horizontalBorderSize), y-bounds.Dy()*2, x, y+bounds.Dy()*4)
|
||||
b.opponentMovesLabel.SetRect(newRect)
|
||||
}
|
||||
{
|
||||
newRect := r.Inset(-padding)
|
||||
|
@ -1342,6 +1370,14 @@ func (b *board) updateOpponentLabel() {
|
|||
b.opponentPipCount.SetRect(newRect)
|
||||
}
|
||||
|
||||
var moves []byte
|
||||
if len(b.opponentMoves) != 0 {
|
||||
moves = bytes.ReplaceAll(bgammon.FormatMoves(b.opponentMoves), []byte(" "), []byte("\n"))
|
||||
}
|
||||
if b.opponentMovesLabel.Text() != string(moves) {
|
||||
b.opponentMovesLabel.SetText(string(moves))
|
||||
}
|
||||
|
||||
if b.showPipCount {
|
||||
b.opponentPipCount.SetVisible(true)
|
||||
pipCount := strconv.Itoa(b.gameState.Pips(player.Number))
|
||||
|
@ -1388,8 +1424,8 @@ func (b *board) updatePlayerLabel() {
|
|||
return
|
||||
}
|
||||
{
|
||||
newRect := image.Rect(x+bounds.Dx(), y-bounds.Dy(), int(b.horizontalBorderSize)+b.innerW, y+bounds.Dy()*2)
|
||||
b.playerMoves.SetRect(newRect)
|
||||
newRect := image.Rect(x+bounds.Dx(), y-bounds.Dy()*2, int(b.horizontalBorderSize)+b.innerW, y+bounds.Dy()*4)
|
||||
b.playerMovesLabel.SetRect(newRect)
|
||||
}
|
||||
{
|
||||
newRect := r.Inset(-padding)
|
||||
|
@ -1404,6 +1440,14 @@ func (b *board) updatePlayerLabel() {
|
|||
}
|
||||
}
|
||||
|
||||
var moves []byte
|
||||
if len(b.playerMoves) != 0 {
|
||||
moves = bytes.ReplaceAll(bgammon.FormatMoves(b.playerMoves), []byte(" "), []byte("\n"))
|
||||
}
|
||||
if b.playerMovesLabel.Text() != string(moves) {
|
||||
b.playerMovesLabel.SetText(string(moves))
|
||||
}
|
||||
|
||||
if b.showPipCount {
|
||||
b.playerPipCount.SetVisible(true)
|
||||
pipCount := strconv.Itoa(b.gameState.Pips(player.Number))
|
||||
|
@ -1653,6 +1697,14 @@ func (b *board) processState() {
|
|||
|
||||
b._positionCheckers()
|
||||
|
||||
if b.showMoves && b.gameState.Turn == 1 {
|
||||
b.playerMoves = expandMoves(b.gameState.Moves)
|
||||
} else if b.showMoves && b.gameState.Turn == 2 {
|
||||
b.opponentMoves = expandMoves(b.gameState.Moves)
|
||||
} else {
|
||||
b.playerMoves, b.opponentMoves = nil, nil
|
||||
}
|
||||
|
||||
b.updateOpponentLabel()
|
||||
b.updatePlayerLabel()
|
||||
|
||||
|
@ -2062,3 +2114,29 @@ func allMoves(in *bgammon.Game, from int, to int) []int {
|
|||
}
|
||||
return moves
|
||||
}
|
||||
|
||||
func expandMoves(moves [][]int) [][]int {
|
||||
var expanded bool
|
||||
for _, m := range moves {
|
||||
expandedMoves, ok := game.Board.gameState.ExpandMove(m, m[0], nil, true)
|
||||
if !ok {
|
||||
return moves
|
||||
}
|
||||
if len(expandedMoves) != 1 {
|
||||
expanded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !expanded {
|
||||
return moves
|
||||
}
|
||||
var newMoves [][]int
|
||||
for _, m := range moves {
|
||||
expandedMoves, ok := game.Board.gameState.ExpandMove(m, m[0], nil, true)
|
||||
if !ok {
|
||||
return moves
|
||||
}
|
||||
newMoves = append(newMoves, expandedMoves...)
|
||||
}
|
||||
return newMoves
|
||||
}
|
||||
|
|
|
@ -970,6 +970,8 @@ func (g *Game) handleEvent(e interface{}) {
|
|||
g.Board.opponentRoll1, g.Board.opponentRoll2 = 0, 0
|
||||
g.Board.playerRollStale = false
|
||||
g.Board.opponentRollStale = false
|
||||
g.Board.playerMoves = nil
|
||||
g.Board.opponentMoves = nil
|
||||
g.Board.processState()
|
||||
g.Board.Unlock()
|
||||
setViewBoard(true)
|
||||
|
@ -1087,6 +1089,13 @@ func (g *Game) handleEvent(e interface{}) {
|
|||
g.Board.movePiece(move[0], move[1])
|
||||
}
|
||||
g.Lock()
|
||||
if g.Board.showMoves {
|
||||
if g.Board.gameState.Turn == 1 {
|
||||
g.Board.playerMoves = expandMoves(g.Board.gameState.Moves)
|
||||
} else if g.Board.gameState.Turn == 2 {
|
||||
g.Board.opponentMoves = expandMoves(g.Board.gameState.Moves)
|
||||
}
|
||||
}
|
||||
g.Board.Unlock()
|
||||
case *bgammon.EventFailedMove:
|
||||
g.Client.Out <- []byte("board") // Refresh game state.
|
||||
|
|
6
go.mod
6
go.mod
|
@ -3,8 +3,8 @@ module code.rocket9labs.com/tslocum/boxcars
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231203064241-5abce61fabb1
|
||||
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20231207002804-464a7b1265ff
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231208220405-8c4b5112ba77
|
||||
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20231209094518-612426a93d9e
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231120184929-cba07aae6454
|
||||
code.rocketnine.space/tslocum/kibodo v1.0.2
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231128010227-689683b75174
|
||||
|
@ -18,7 +18,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
code.rocket9labs.com/tslocum/tabula v0.0.0-20231207014630-bbec0a941eda // indirect
|
||||
code.rocket9labs.com/tslocum/tabula v0.0.0-20231209094435-959d43321c78 // indirect
|
||||
github.com/ebitengine/oto/v3 v3.1.0 // indirect
|
||||
github.com/ebitengine/purego v0.5.1 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
|
|
12
go.sum
12
go.sum
|
@ -1,11 +1,11 @@
|
|||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231203064241-5abce61fabb1 h1:DU4h2XiS2bz4FdaeHRfYTBNwhWuaYGmnb/NA0VgsVMc=
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231203064241-5abce61fabb1/go.mod h1:u3nbSwxWnwOXbCNPQD4s3abGTfvA4/gi9U626f7ZN9Q=
|
||||
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20231207002804-464a7b1265ff h1:FhQTN1yQH9Na5h7gYYWpGwbbGZnp6paCqNGSw4LK7jE=
|
||||
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20231207002804-464a7b1265ff/go.mod h1:ZZMqz1qLpKY2ZS59vPrc6Rx1fcI/KN40JswsEQIksgA=
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231208220405-8c4b5112ba77 h1:MnAW9Icj5mDOaFnyT3BcH/xRoa0stZEJjs+hQg0lyj0=
|
||||
code.rocket9labs.com/tslocum/bgammon v0.0.0-20231208220405-8c4b5112ba77/go.mod h1:u3nbSwxWnwOXbCNPQD4s3abGTfvA4/gi9U626f7ZN9Q=
|
||||
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20231209094518-612426a93d9e h1:l9VIZMa9Zpw7oh6jwQ42qTl+fTRERuMIz9OW+5GFIE0=
|
||||
code.rocket9labs.com/tslocum/bgammon-tabula-bot v0.0.0-20231209094518-612426a93d9e/go.mod h1:HzEVxqMCixYFDVGAesdp9bc9onxNzCEJUf+eyDRMZWw=
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231120184929-cba07aae6454 h1:R+KAg2+ORkMT/bcljmlno1nr21OBOxo6hY0Qa2qOk+Y=
|
||||
code.rocket9labs.com/tslocum/etk v0.0.0-20231120184929-cba07aae6454/go.mod h1:bDrMbdTIhIy9egS6ISZat3Cw1dusd/Raeiq8vKHo9b4=
|
||||
code.rocket9labs.com/tslocum/tabula v0.0.0-20231207014630-bbec0a941eda h1:4AUhF3+uRGROFrHB65U3OgmTvAACzMDAPcV0vs320/E=
|
||||
code.rocket9labs.com/tslocum/tabula v0.0.0-20231207014630-bbec0a941eda/go.mod h1:XtS6M8qcK0fnUn4k5I7h4JcZ2sz2WEAXOcF2DLaQBME=
|
||||
code.rocket9labs.com/tslocum/tabula v0.0.0-20231209094435-959d43321c78 h1:5E+Av3zTjMndkoz9n7QSl0icA1eBjnzFXtpbVADhXy8=
|
||||
code.rocket9labs.com/tslocum/tabula v0.0.0-20231209094435-959d43321c78/go.mod h1:XtS6M8qcK0fnUn4k5I7h4JcZ2sz2WEAXOcF2DLaQBME=
|
||||
code.rocketnine.space/tslocum/kibodo v1.0.2 h1:0RfvVz+IUku8MFx9wvDb+p8byns5gAjQLUo4ZenWP44=
|
||||
code.rocketnine.space/tslocum/kibodo v1.0.2/go.mod h1:mAYs1JKFnWlRFzo9BtteAlwjKdk1MIKgEyhQaPdeQDI=
|
||||
code.rocketnine.space/tslocum/messeji v1.0.6-0.20231128010227-689683b75174 h1:jxE3JcaE4ovMWaZHLA6MvYgkmdkdbEEUv943Bp+MoxU=
|
||||
|
|
Loading…
Reference in a new issue