Skip to content

Commit

Permalink
Add concept of DisplayCards to make UI simpler.
Browse files Browse the repository at this point in the history
  • Loading branch information
marianogappa committed Jul 21, 2024
1 parent a08556e commit faaa10a
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 34 deletions.
26 changes: 24 additions & 2 deletions exampleclient/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,17 @@ func renderScores(rs renderState) {
}

func renderTheirUnrevealedCards(rs renderState) {
renderAt(0, 0, strings.Repeat("[] ", rs.gs.TheirUnrevealedCardLength))
displayText := ""
for _, card := range rs.gs.TheirDisplayUnrevealedCards {
if card.IsHole {
displayText += " "
} else {
displayText += "[]"
}
displayText += " "
}

renderAt(0, 0, displayText)
}

func renderTheirRevealedCards(rs renderState) {
Expand Down Expand Up @@ -167,7 +177,16 @@ func renderYourRevealedCards(rs renderState) {
}

func renderYourUnrevealedCards(rs renderState) {
renderAt(0, rs.viewportHeight-4, getCardsString(rs.gs.YourUnrevealedCards))
displayText := ""
for _, card := range rs.gs.YourDisplayUnrevealedCards {
if !card.IsHole {
displayText += getDisplayCardString(card)
} else {
displayText += " "
}
displayText += " "
}
renderAt(0, rs.viewportHeight-4, displayText)
}

func renderActions(rs renderState) {
Expand Down Expand Up @@ -217,6 +236,9 @@ func getCardsString(cards []truco.Card) string {
func getCardString(card truco.Card) string {
return fmt.Sprintf("[%v%v ]", card.Number, suitEmoji(card.Suit))
}
func getDisplayCardString(card truco.DisplayCard) string {
return fmt.Sprintf("[%v%v ]", card.Number, suitEmoji(card.Suit))
}

func suitEmoji(suit string) string {
switch suit {
Expand Down
6 changes: 6 additions & 0 deletions truco/action_any_quiero.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,15 @@ func (a ActionRevealEnvidoScore) Run(g *GameState) error {
for _, is := range allPossibleReveals[len(curPlayersHand.Unrevealed)] {
// create a candidate hand but only with reveal cards
candidateHand := Hand{Revealed: append([]Card{}, curPlayersHand.Revealed...)}
for i := range curPlayersHand.Unrevealed {
card := curPlayersHand.Unrevealed[i]
candidateHand.displayUnrevealedCards = append(candidateHand.displayUnrevealedCards, DisplayCard{Number: card.Number, Suit: card.Suit})
}

// and reveal the additional cards of this combination
for i := range is {
candidateHand.Revealed = append(candidateHand.Revealed, curPlayersHand.Unrevealed[i])
candidateHand.displayUnrevealedCards[i].IsHole = true
}
// if by revealing these cards we reach the expected envido score, this is the right reveal
// Note: this is only true if the reveal combinations are sorted by reveal count ascending!
Expand Down
71 changes: 68 additions & 3 deletions truco/deck.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ type Card struct {
Number int `json:"number"`
}

func (c Card) ToDisplayCard() DisplayCard {
return DisplayCard{
Suit: c.Suit,
Number: c.Number,
}
}

type DisplayCard struct {
// Suit is the card's suit, which can be "oro", "copa", "espada" or "basto".
Suit string `json:"suit"`

// Number is the card's number, from 1 to 12.
Number int `json:"number"`

// This card is backwards (we don't know the suit & number)
IsBackwards bool `json:"is_backwards"`

// This card is a hole (it used to be the card with this suit & number)
IsHole bool `json:"is_hole"`
}

func (c Card) String() string {
return fmt.Sprintf("%d de %s", c.Number, c.Suit)
}
Expand All @@ -36,6 +57,8 @@ type deck struct {
type Hand struct {
Unrevealed []Card `json:"unrevealed"`
Revealed []Card `json:"revealed"`

displayUnrevealedCards []DisplayCard
}

func (h Hand) DeepCopy() Hand {
Expand Down Expand Up @@ -70,16 +93,56 @@ func (h *Hand) RevealCard(card Card) error {
return errCardAlreadyRevealed
}
}
for i, c := range h.Unrevealed {
for _, c := range h.Unrevealed {
if c == card {
h.Revealed = append(h.Revealed, c)
h.Unrevealed = append(h.Unrevealed[:i], h.Unrevealed[i+1:]...)
h.removeUnrevealedCard(c)
return nil
}
}
return errCardNotInHand
}

func (h *Hand) removeUnrevealedCard(card Card) {
for i, c := range h.Unrevealed {
if c == card {
h.Unrevealed = append(h.Unrevealed[:i], h.Unrevealed[i+1:]...)
break
}
}
for i := range h.displayUnrevealedCards {
if h.displayUnrevealedCards[i].Suit == card.Suit && h.displayUnrevealedCards[i].Number == card.Number {
h.displayUnrevealedCards[i].IsHole = true
break
}
}
}

func (h *Hand) initializeDisplayUnrevealedCards() {
h.displayUnrevealedCards = []DisplayCard{}
for _, c := range h.Unrevealed {
h.displayUnrevealedCards = append(h.displayUnrevealedCards, c.ToDisplayCard())
}
}

// prepareDisplayUnrevealedCards makes sure that display cards are elided when not
// revealed and for the opponent.
func (h *Hand) prepareDisplayUnrevealedCards(isYou bool) []DisplayCard {
result := []DisplayCard{}
result = append(result, h.displayUnrevealedCards...)
if isYou {
return result
}
for i := range result {
if !result[i].IsHole {
result[i].IsBackwards = true
result[i].Suit = ""
result[i].Number = 0
}
}
return result
}

var (
errCardNotInHand = errors.New("card not in hand")
errCardAlreadyRevealed = errors.New("card already revealed")
Expand Down Expand Up @@ -115,7 +178,9 @@ func (d *deck) shuffle() {
}

func (d *deck) dealHand() *Hand {
return d.dealHandFunc()
hand := d.dealHandFunc()
hand.initializeDisplayUnrevealedCards()
return hand
}

func (d *deck) defaultDealHand() *Hand {
Expand Down
76 changes: 47 additions & 29 deletions truco/truco.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,27 +442,28 @@ func (g *GameState) ToClientGameState(youPlayerID int) ClientGameState {
}

cgs := ClientGameState{
RoundTurnPlayerID: g.RoundTurnPlayerID,
RoundNumber: g.RoundNumber,
TurnPlayerID: g.TurnPlayerID,
YouPlayerID: youPlayerID,
ThemPlayerID: themPlayerID,
YourScore: g.Players[youPlayerID].Score,
TheirScore: g.Players[themPlayerID].Score,
YourRevealedCards: g.Players[youPlayerID].Hand.Revealed,
TheirRevealedCards: g.Players[themPlayerID].Hand.Revealed,
YourUnrevealedCards: g.Players[youPlayerID].Hand.Unrevealed,
TheirUnrevealedCardLength: len(g.Players[themPlayerID].Hand.Unrevealed),
PossibleActions: _serializeActions(filteredPossibleActions),
IsGameEnded: g.IsGameEnded,
IsRoundFinished: g.IsRoundFinished,
WinnerPlayerID: g.WinnerPlayerID,
EnvidoWinnerPlayerID: g.RoundsLog[g.RoundNumber].EnvidoWinnerPlayerID,
WasEnvidoAccepted: g.EnvidoSequence.WasAccepted(),
EnvidoPoints: g.RoundsLog[g.RoundNumber].EnvidoPoints,
TrucoWinnerPlayerID: g.RoundsLog[g.RoundNumber].TrucoWinnerPlayerID,
TrucoPoints: g.RoundsLog[g.RoundNumber].TrucoPoints,
WasTrucoAccepted: g.TrucoSequence.WasAccepted(),
RoundTurnPlayerID: g.RoundTurnPlayerID,
RoundNumber: g.RoundNumber,
TurnPlayerID: g.TurnPlayerID,
YouPlayerID: youPlayerID,
ThemPlayerID: themPlayerID,
YourScore: g.Players[youPlayerID].Score,
TheirScore: g.Players[themPlayerID].Score,
YourRevealedCards: g.Players[youPlayerID].Hand.Revealed,
TheirRevealedCards: g.Players[themPlayerID].Hand.Revealed,
YourUnrevealedCards: g.Players[youPlayerID].Hand.Unrevealed,
PossibleActions: _serializeActions(filteredPossibleActions),
IsGameEnded: g.IsGameEnded,
IsRoundFinished: g.IsRoundFinished,
WinnerPlayerID: g.WinnerPlayerID,
EnvidoWinnerPlayerID: g.RoundsLog[g.RoundNumber].EnvidoWinnerPlayerID,
WasEnvidoAccepted: g.EnvidoSequence.WasAccepted(),
EnvidoPoints: g.RoundsLog[g.RoundNumber].EnvidoPoints,
TrucoWinnerPlayerID: g.RoundsLog[g.RoundNumber].TrucoWinnerPlayerID,
TrucoPoints: g.RoundsLog[g.RoundNumber].TrucoPoints,
WasTrucoAccepted: g.TrucoSequence.WasAccepted(),
YourDisplayUnrevealedCards: g.Players[youPlayerID].Hand.prepareDisplayUnrevealedCards(true),
TheirDisplayUnrevealedCards: g.Players[themPlayerID].Hand.prepareDisplayUnrevealedCards(false),
}

if len(g.RoundsLog[g.RoundNumber].ActionsLog) > 0 {
Expand All @@ -489,14 +490,31 @@ type ClientGameState struct {
// They are the same at the beginning of the round.
TurnPlayerID int `json:"turnPlayerID"`

YouPlayerID int `json:"you"`
ThemPlayerID int `json:"them"`
YourScore int `json:"yourScore"`
TheirScore int `json:"theirScore"`
YourRevealedCards []Card `json:"yourRevealedCards"`
TheirRevealedCards []Card `json:"theirRevealedCards"`
YourUnrevealedCards []Card `json:"yourUnrevealedCards"`
TheirUnrevealedCardLength int `json:"theirUnrevealedCardLength"`
YouPlayerID int `json:"you"`
ThemPlayerID int `json:"them"`
YourScore int `json:"yourScore"`
TheirScore int `json:"theirScore"`
YourRevealedCards []Card `json:"yourRevealedCards"`
TheirRevealedCards []Card `json:"theirRevealedCards"`
YourUnrevealedCards []Card `json:"yourUnrevealedCards"`

// YourDisplayUnrevealedCards is like YourUnrevealedCards, but it always has 3 cards
// and it adds two properties: `IsBackwards` and `IsHole`.
//
// `IsBackwards` is true if the card is facing backwards (i.e. the client doesn't know what it is).
// `IsHole` is true if the card was revealed by the opponent, and it used to be that card.
//
// Use this property to render the card
YourDisplayUnrevealedCards []DisplayCard `json:"yourDisplayUnrevealedCards"`

// TheirDisplayUnrevealedCards is like TheirUnrevealedCards, but it always has 3 cards
// and it adds two properties: `IsBackwards` and `IsHole`.
//
// `IsBackwards` is true if the card is facing backwards (i.e. the client doesn't know what it is).
// `IsHole` is true if the card was revealed by the opponent, and it used to be that card.
//
// Use this property to render the card
TheirDisplayUnrevealedCards []DisplayCard `json:"theirDisplayUnrevealedCards"`

// PossibleActions is a list of possible actions that the current player can take.
// Possible actions are calculated based on game state at the beginning of the round and after
Expand Down

0 comments on commit faaa10a

Please sign in to comment.