Improve card selection

This commit is contained in:
Trevor Slocum 2020-08-19 15:22:41 -07:00
parent a3f41edc26
commit fe5fbfc818
6 changed files with 139 additions and 40 deletions

View File

@ -2,7 +2,7 @@
[![CI status](https://gitlab.com/tslocum/crib/badges/master/pipeline.svg)](https://gitlab.com/tslocum/crib/commits/master)
[![Donate](https://img.shields.io/liberapay/receives/rocketnine.space.svg?logo=liberapay)](https://liberapay.com/rocketnine.space)
[**Cribbage.World**](https://cribbage.world) terminal client
[**Cribbage.World**](https://cribbage.world) terminal-based client
## Demo

View File

@ -1,6 +1,7 @@
package main
import (
"math"
"sync"
"github.com/gdamore/tcell"
@ -17,10 +18,11 @@ const (
type cardStackWidget struct {
*cview.Box
Cards []*cardWidget
maxSelection int // TODO != 0 unnecessary?
Cards []*cardWidget
EverSelectable bool
Text string
maxSelection int // TODO != 0 unnecessary?
sync.RWMutex
}
@ -58,7 +60,7 @@ func (c *cardStackWidget) SetCards(cards joker.Cards) {
c.Cards = make([]*cardWidget, len(cards))
for i := 0; i < len(cards); i++ {
c.Cards[i] = NewCardWidget(cards[i])
c.Cards[i] = NewCardWidget(cards[i], selectCard)
}
}
@ -76,7 +78,6 @@ func (c *cardStackWidget) Draw(screen tcell.Screen) {
}
stackX, stackY, _, _ := c.GetInnerRect()
//log.Fatal(stackX)
for i, cardWidget := range c.Cards {
lastOffsetY = cardOffsetY
@ -122,6 +123,11 @@ func (c *cardStackWidget) Draw(screen tcell.Screen) {
screen.SetContent(stackX+(i*cardBufferX)+2, stackY+cardOffsetY, tcell.RuneBTee, nil, tcell.StyleDefault)
}
}
if c.Text != "" && len(c.Cards) > 0 {
cview.Print(screen, c.Text, (stackX+((len(c.Cards)-1)*cardBufferX))+(cardWidth/2), stackY, math.MaxInt64, cview.AlignLeft, tcell.ColorDefault)
cview.Print(screen, c.Text, (stackX+((len(c.Cards)-1)*cardBufferX))+(cardWidth/2), (stackY+cardHeight)-1, math.MaxInt64, cview.AlignLeft, tcell.ColorDefault)
}
}
// MouseHandler returns the mouse handler for this primitive.

View File

@ -11,17 +11,29 @@ import (
type cardWidget struct {
*cview.Box
joker.Card
selected bool
selected bool
selectedFunc func(joker.Card)
}
func NewCardWidget(card joker.Card) *cardWidget {
c := cardWidget{Box: cview.NewBox().ShowFocus(false), Card: card}
func NewCardWidget(card joker.Card, selectedFunc func(joker.Card)) *cardWidget {
c := cardWidget{Box: cview.NewBox().ShowFocus(false), Card: card, selectedFunc: selectedFunc}
c.Box.SetBorder(true)
return &c
}
func (c *cardWidget) Select() {
c.selected = !c.selected
if c.selectedFunc != nil {
c.selectedFunc(c.Card)
}
app.Draw()
}
func (c *cardWidget) Draw(screen tcell.Screen) {
c.Box.Draw(screen)
@ -118,12 +130,8 @@ func (c *cardWidget) MouseHandler() func(action cview.MouseAction, event *tcell.
// Process mouse event.
if action == cview.MouseLeftClick {
setFocus(c)
consumed = true
c.selected = !c.selected
app.Draw()
c.Select()
}
return

5
go.mod
View File

@ -1,6 +1,6 @@
module gitlab.com/tslocum/crib
go 1.14
go 1.15
require (
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606
@ -8,5 +8,6 @@ require (
gitlab.com/tslocum/cbind v0.1.1
gitlab.com/tslocum/cview v1.4.8
gitlab.com/tslocum/joker v0.1.3
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed // indirect
gitlab.com/tslocum/joker-cribbage v0.1.1
golang.org/x/sys v0.0.0-20200819171115-d785dc25833f // indirect
)

9
go.sum
View File

@ -21,15 +21,18 @@ gitlab.com/tslocum/cbind v0.1.1 h1:JXXtxMWHgWLvoF+QkrvcNvOQ59juy7OE1RhT7hZfdt0=
gitlab.com/tslocum/cbind v0.1.1/go.mod h1:rX7vkl0pUSg/yy427MmD1FZAf99S7WwpUlxF/qTpPqk=
gitlab.com/tslocum/cview v1.4.8 h1:xYUO+4QUGDbrqIhgqUqXWzCXB1kZDNpmoOR2MwkxNb0=
gitlab.com/tslocum/cview v1.4.8/go.mod h1:mSTNW1JUAfb9QlCB9GSaqy97Z3reIAJyUSbFSQcF8GE=
gitlab.com/tslocum/joker v0.1.2-0.20200123002530-2570d6c23aff/go.mod h1:bxTQ0FFmBP465r9z76zcm97S4Ld9eCLa3q20TyVM82A=
gitlab.com/tslocum/joker v0.1.3 h1:6AVQuc0Rt7LczppZKaAXfr5B5Yg/4sQky2SFaWaF9E4=
gitlab.com/tslocum/joker v0.1.3/go.mod h1:bxTQ0FFmBP465r9z76zcm97S4Ld9eCLa3q20TyVM82A=
gitlab.com/tslocum/joker-cribbage v0.1.1 h1:oiCKebyppYsl4x1ZJNHQZcSCcHSBHUCMqSFTGDGv1fY=
gitlab.com/tslocum/joker-cribbage v0.1.1/go.mod h1:KyNZDgGv6fFmYGpHD9txZCBFuVDgKwCq4mJ4ZAgQ0hw=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200819171115-d785dc25833f h1:KJuwZVtZBVzDmEDtB2zro9CXkD9O0dpCv4o2LHbQIAw=
golang.org/x/sys v0.0.0-20200819171115-d785dc25833f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

123
main.go
View File

@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"log"
"strconv"
"time"
"github.com/gdamore/tcell"
@ -12,6 +13,7 @@ import (
"gitlab.com/tslocum/cbind"
"gitlab.com/tslocum/cview"
"gitlab.com/tslocum/joker"
cribbage "gitlab.com/tslocum/joker-cribbage"
)
var (
@ -104,8 +106,6 @@ func connect(address string) {
currentGameState = gameState
if gameState.Player > 0 {
setStatusText(currentGameState.Status)
playerHand := gameState.Hand1
oppHand := gameState.Hand2
if gameState.Player == 2 {
@ -120,31 +120,72 @@ func connect(address string) {
oppCrib = gameState.Crib
}
if currentGameState.Status == "" {
if currentGameState.Phase == PhasePick {
if len(playerHand) > 4 {
if gameState.Dealer == gameState.Player {
currentGameState.Status = "Throw for your crib"
} else {
currentGameState.Status = "Throw for your opponent's crib"
}
} else {
currentGameState.Status = "Waiting..."
}
} else if currentGameState.Phase == PhasePeg && currentGameState.Turn != currentGameState.Player {
currentGameState.Status = "Waiting..."
}
}
setStatusText(currentGameState.Status)
starterWidget.Card = currentGameState.Starter
if currentGameState.Phase == PhasePick {
mainHandStack.SetMaxSelection(2)
} else if currentGameState.Phase == PhasePeg && currentGameState.Turn == currentGameState.Player {
mainHandStack.SetMaxSelection(1)
var canPeg bool
for i := range playerHand {
if cribbage.Sum(currentGameState.ThrowPile.Cards())+cribbage.Value(playerHand[i]) <= 31 {
canPeg = true
break
}
}
if canPeg {
mainHandStack.SetMaxSelection(1)
} else {
mainHandStack.SetMaxSelection(-1)
}
} else {
mainHandStack.SetMaxSelection(-1)
}
if currentGameState.Phase == PhasePick {
if gameState.Dealer == gameState.Player {
throwStack.SetCards(oppCrib)
} else {
mainCribStack.SetCards(playerCrib)
oppCribStack.SetCards(joker.Cards{})
} else {
oppCribStack.SetCards(oppCrib)
mainCribStack.SetCards(joker.Cards{})
}
mainHandStack.SetCards(playerHand)
throwStack.SetCards(joker.Cards{})
} else if currentGameState.Phase == PhaseScore {
mainHandStack.SetCards(playerHand)
mainCribStack.SetCards(playerCrib)
throwStack.SetCards(oppHand)
oppCribStack.SetCards(oppCrib)
} else {
} else { // Peg
throwStack.SetCards(currentGameState.ThrowPile.Cards())
mainHandStack.SetCards(playerHand)
mainCribStack.SetCards(playerCrib)
oppCribStack.SetCards(joker.Cards{})
var throwValue int
for i := range currentGameState.ThrowPile {
throwValue += cribbage.Value(currentGameState.ThrowPile[i].Card)
}
throwStack.Text = strconv.Itoa(throwValue)
}
} else {
setStatusText("Waiting for opponent...")
@ -155,6 +196,8 @@ func connect(address string) {
lastPhase = currentGameState.Phase
}
updateGameText()
statusBuf.Write([]byte(fmt.Sprintf("decode: %+v\n", gameState)))
app.Draw()
@ -180,31 +223,50 @@ func connect(address string) {
}
func setupThrowGrid() {
grid := cview.NewGrid()
if throwGrid == nil {
throwGrid = cview.NewGrid()
}
throwGrid.Clear()
if currentGameState.Phase == PhasePeg {
grid.AddItem(throwStack, 0, 0, 1, 1, 0, 0, false)
throwGrid.SetColumns(-1)
throwGrid.AddItem(throwStack, 0, 0, 1, 1, 0, 0, false)
} else {
grid.SetColumns((cardBufferX * 5) + cardWidth)
throwGrid.SetColumns((cardBufferX * 5) + cardWidth)
grid.AddItem(throwStack, 0, 0, 1, 1, 0, 0, false)
grid.AddItem(oppCribStack, 0, 1, 1, 1, 0, 0, false)
throwGrid.AddItem(throwStack, 0, 0, 1, 1, 0, 0, false)
throwGrid.AddItem(oppCribStack, 0, 1, 1, 1, 0, 0, false)
}
throwGrid = grid
}
// Starts at 1
func selectCard(cardNumber int) {
func selectCardIndex(cardNumber int) {
if cardNumber <= 0 || cardNumber > len(mainHandStack.Cards) {
return
}
if !mainHandStack.Cards[cardNumber-1].selected {
var selected int
for _, card := range mainHandStack.Cards {
if card.selected {
selected++
}
}
if selected >= mainHandStack.GetMaxSelection() {
return
}
}
mainHandStack.Cards[cardNumber-1].Select()
return // TODO
if mainHandStack.GetMaxSelection() == 1 {
for i, card := range mainHandStack.Cards {
card.selected = false
if i == cardNumber-1 {
if i == cardNumber-1 && !card.selected {
card.selected = true
} else {
card.selected = false
}
}
return
@ -225,6 +287,16 @@ func selectCard(cardNumber int) {
mainHandStack.Cards[cardNumber-1].selected = !mainHandStack.Cards[cardNumber-1].selected
}
func selectCard(card joker.Card) {
if currentGameState.Phase == PhasePeg && currentGameState.Turn == currentGameState.Player {
if cribbage.Sum(currentGameState.ThrowPile.Cards())+cribbage.Value(card) > 31 {
setStatusText("Can not throw card: illegal move")
} else {
writeBuffer <- fmt.Sprintf("throw %s", card.Identifier())
}
}
}
func doAction() {
var cards joker.Cards
for _, card := range mainHandStack.Cards {
@ -233,7 +305,10 @@ func doAction() {
}
}
if len(cards) > 0 {
if len(cards) == 1 {
selectCard(cards[0])
return
} else if len(cards) > 1 {
for _, card := range cards {
writeBuffer <- fmt.Sprintf("throw %s", card.Identifier())
}
@ -247,6 +322,10 @@ func setStatusText(status string) {
statusText.SetText(" " + status)
}
func updateGameText() {
gameText.SetText(fmt.Sprintf("[%d - %d]", currentGameState.Score1, currentGameState.Score2))
}
func main() {
var connectAddress string
flag.StringVar(&connectAddress, "server", "wss://play.cribbage.world/crib", "Server address")
@ -269,7 +348,7 @@ func main() {
for i := 1; i <= 6; i++ {
i := i // Capture
inputConfig.SetRune(tcell.ModNone, '0'+rune(i), func(ev *tcell.EventKey) *tcell.EventKey {
selectCard(i)
selectCardIndex(i)
app.Draw()
return nil
})
@ -281,7 +360,7 @@ func main() {
return false
})
starterWidget = NewCardWidget(joker.Card{})
starterWidget = NewCardWidget(joker.Card{}, nil)
mainHandStack = NewCardStackWidget()
mainCribStack = NewCardStackWidget()
throwStack = NewCardStackWidget()
@ -300,18 +379,20 @@ func main() {
SetRows(cardHeight, 1, cardHeight+1, -1)
gameText = cview.NewTextView().SetText("[0 - 0]")
gameText.SetTextAlign(cview.AlignCenter)
statusButton = cview.NewButton("Continue").SetSelectedFunc(doAction)
statusGrid = cview.NewGrid().
SetColumns(1, -1, 1).
SetRows(1, 3, 1, -1)
statusGrid.AddItem(cview.NewTextView(), 0, 0, 3, 1, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 0, 1, 1, 2, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 1, 0, 1, 1, 0, 0, false)
statusGrid.AddItem(statusButton, 1, 1, 1, 1, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 1, 2, 1, 1, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 2, 1, 1, 1, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 2, 1, 1, 2, 0, 0, false)
statusGrid.AddItem(gameText, 3, 1, 1, 1, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 0, 0, 3, 1, 0, 0, false)
statusGrid.AddItem(cview.NewTextView(), 3, 2, 1, 1, 0, 0, false)
statusText = cview.NewTextView()