Cleaned up boolean if statements, and other requested changes
Added Face type
Created more polished, functional Pass screen
Change-Id: I877a6c9b633893e1bfd5803701bce5a5cf7d5bbe
diff --git a/go/src/hearts/assets/Club.png b/go/src/hearts/assets/Club.png
new file mode 100644
index 0000000..987d86b
--- /dev/null
+++ b/go/src/hearts/assets/Club.png
Binary files differ
diff --git a/go/src/hearts/assets/Diamond.png b/go/src/hearts/assets/Diamond.png
new file mode 100644
index 0000000..d7dba1c
--- /dev/null
+++ b/go/src/hearts/assets/Diamond.png
Binary files differ
diff --git a/go/src/hearts/assets/Heart.png b/go/src/hearts/assets/Heart.png
new file mode 100644
index 0000000..53e2773
--- /dev/null
+++ b/go/src/hearts/assets/Heart.png
Binary files differ
diff --git a/go/src/hearts/assets/Spade.png b/go/src/hearts/assets/Spade.png
new file mode 100644
index 0000000..51a0631
--- /dev/null
+++ b/go/src/hearts/assets/Spade.png
Binary files differ
diff --git a/go/src/hearts/assets/acrossArrow.png b/go/src/hearts/assets/acrossArrow.png
new file mode 100644
index 0000000..4aaa946
--- /dev/null
+++ b/go/src/hearts/assets/acrossArrow.png
Binary files differ
diff --git a/go/src/hearts/assets/blue.png b/go/src/hearts/assets/blue.png
new file mode 100644
index 0000000..e35155c
--- /dev/null
+++ b/go/src/hearts/assets/blue.png
Binary files differ
diff --git a/go/src/hearts/assets/croupierName.png b/go/src/hearts/assets/croupierName.png
new file mode 100644
index 0000000..8a5a1b6
--- /dev/null
+++ b/go/src/hearts/assets/croupierName.png
Binary files differ
diff --git a/go/src/hearts/assets/gray.jpeg b/go/src/hearts/assets/gray.jpeg
new file mode 100644
index 0000000..a47db1a
--- /dev/null
+++ b/go/src/hearts/assets/gray.jpeg
Binary files differ
diff --git a/go/src/hearts/assets/leftArrow.png b/go/src/hearts/assets/leftArrow.png
new file mode 100644
index 0000000..3361ac6
--- /dev/null
+++ b/go/src/hearts/assets/leftArrow.png
Binary files differ
diff --git a/go/src/hearts/assets/passPressed.png b/go/src/hearts/assets/passPressed.png
new file mode 100644
index 0000000..cd8c0aa
--- /dev/null
+++ b/go/src/hearts/assets/passPressed.png
Binary files differ
diff --git a/go/src/hearts/assets/passUnpressed.png b/go/src/hearts/assets/passUnpressed.png
new file mode 100644
index 0000000..1c5db41
--- /dev/null
+++ b/go/src/hearts/assets/passUnpressed.png
Binary files differ
diff --git a/go/src/hearts/assets/rightArrow.png b/go/src/hearts/assets/rightArrow.png
new file mode 100644
index 0000000..048b571
--- /dev/null
+++ b/go/src/hearts/assets/rightArrow.png
Binary files differ
diff --git a/go/src/hearts/assets/white.png b/go/src/hearts/assets/white.png
new file mode 100644
index 0000000..e878403
--- /dev/null
+++ b/go/src/hearts/assets/white.png
Binary files differ
diff --git a/go/src/hearts/img/staticImg.go b/go/src/hearts/img/staticImg.go
new file mode 100644
index 0000000..e5309c5
--- /dev/null
+++ b/go/src/hearts/img/staticImg.go
@@ -0,0 +1,100 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package img
+
+import (
+ "golang.org/x/mobile/exp/sprite"
+ "hearts/logic/card"
+)
+
+func NewStaticImg() *StaticImg {
+ return &StaticImg{}
+}
+
+// Static Images may be buttons, drop targets, or any other image that is not a card object
+type StaticImg struct {
+ node *sprite.Node
+ image sprite.SubTex
+ // alt may or may not be used
+ // can be used as the 'pressed' image if the StaticImg instance is a button
+ // also can be used as a 'blank' image if the StaticImg instance may disappear
+ alt sprite.SubTex
+ x float32
+ y float32
+ initialX float32
+ initialY float32
+ width float32
+ height float32
+ // cardHere is used if the StaticImg instance is a drop target
+ cardHere *card.Card
+}
+
+func (s *StaticImg) GetNode() *sprite.Node {
+ return s.node
+}
+
+func (s *StaticImg) GetImage() sprite.SubTex {
+ return s.image
+}
+
+func (s *StaticImg) GetAlt() sprite.SubTex {
+ return s.alt
+}
+
+func (s *StaticImg) GetX() float32 {
+ return s.x
+}
+
+func (s *StaticImg) GetY() float32 {
+ return s.y
+}
+
+func (s *StaticImg) GetInitialX() float32 {
+ return s.initialX
+}
+
+func (s *StaticImg) GetInitialY() float32 {
+ return s.initialY
+}
+
+func (s *StaticImg) GetWidth() float32 {
+ return s.width
+}
+
+func (s *StaticImg) GetHeight() float32 {
+ return s.height
+}
+
+func (s *StaticImg) GetCardHere() *card.Card {
+ return s.cardHere
+}
+
+func (s *StaticImg) SetNode(n *sprite.Node) {
+ s.node = n
+}
+
+func (s *StaticImg) SetImage(t sprite.SubTex) {
+ s.image = t
+}
+
+func (s *StaticImg) SetAlt(t sprite.SubTex) {
+ s.alt = t
+}
+
+func (s *StaticImg) SetPos(newX float32, newY float32, newWidth float32, newHeight float32) {
+ s.x = newX
+ s.y = newY
+ s.width = newWidth
+ s.height = newHeight
+}
+
+func (s *StaticImg) SetInitialPos(newX float32, newY float32) {
+ s.initialX = newX
+ s.initialY = newY
+}
+
+func (s *StaticImg) SetCardHere(c *card.Card) {
+ s.cardHere = c
+}
diff --git a/go/src/hearts/logic/card/card.go b/go/src/hearts/logic/card/card.go
new file mode 100644
index 0000000..50691d6
--- /dev/null
+++ b/go/src/hearts/logic/card/card.go
@@ -0,0 +1,115 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package card
+
+import (
+ "golang.org/x/mobile/exp/sprite"
+)
+
+type Suit string
+type Face int
+
+const (
+ Heart Suit = "H"
+ Diamond Suit = "D"
+ Spade Suit = "S"
+ Club Suit = "C"
+)
+
+const (
+ Two Face = iota + 2
+ Three
+ Four
+ Five
+ Six
+ Seven
+ Eight
+ Nine
+ Ten
+ Jack
+ Queen
+ King
+ // note: in Hearts, Aces are high
+ Ace
+)
+
+func NewCard(f Face, suit Suit) *Card {
+ return &Card{
+ s: suit,
+ face: f,
+ }
+}
+
+type Card struct {
+ s Suit
+ face Face
+ node *sprite.Node
+ x float32
+ y float32
+ initialX float32
+ initialY float32
+ width float32
+ height float32
+}
+
+func (c *Card) GetSuit() Suit {
+ return c.s
+}
+
+func (c *Card) GetFace() Face {
+ return c.face
+}
+
+func (c *Card) GetNode() *sprite.Node {
+ return c.node
+}
+
+func (c *Card) GetX() float32 {
+ return c.x
+}
+
+func (c *Card) GetY() float32 {
+ return c.y
+}
+
+func (c *Card) GetInitialX() float32 {
+ return c.initialX
+}
+
+func (c *Card) GetInitialY() float32 {
+ return c.initialY
+}
+
+func (c *Card) GetWidth() float32 {
+ return c.width
+}
+
+func (c *Card) GetHeight() float32 {
+ return c.height
+}
+
+func (c *Card) SetNode(n *sprite.Node) {
+ c.node = n
+}
+
+func (c *Card) SetPos(newX float32, newY float32, newWidth float32, newHeight float32) {
+ c.x = newX
+ c.y = newY
+ c.width = newWidth
+ c.height = newHeight
+}
+
+func (c *Card) SetInitialPos(newX float32, newY float32) {
+ c.initialX = newX
+ c.initialY = newY
+}
+
+func (c *Card) WorthPoints() bool {
+ worthPoints := false
+ if c.s == Heart || (c.s == Spade && c.face == Queen) {
+ worthPoints = true
+ }
+ return worthPoints
+}
diff --git a/go/src/hearts/logic/player/player.go b/go/src/hearts/logic/player/player.go
new file mode 100644
index 0000000..9d5be79
--- /dev/null
+++ b/go/src/hearts/logic/player/player.go
@@ -0,0 +1,92 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package player
+
+import (
+ "hearts/logic/card"
+)
+
+func NewPlayer(index int) *Player {
+ return &Player{
+ hand: nil,
+ tricks: make([]*card.Card, 0),
+ score: 0,
+ playerIndex: index,
+ }
+}
+
+type Player struct {
+ hand []*card.Card
+ tricks []*card.Card
+ score int
+ playerIndex int
+}
+
+func (p *Player) GetHand() []*card.Card {
+ return p.hand
+}
+
+func (p *Player) GetScore() int {
+ return p.score
+}
+
+func (p *Player) GetPlayerIndex() int {
+ return p.playerIndex
+}
+
+func (p *Player) AddToHand(card *card.Card) {
+ p.hand = append(p.hand, card)
+}
+
+func (p *Player) TakeTrick(cards []*card.Card) {
+ p.tricks = append(p.tricks, cards...)
+}
+
+func (p *Player) UpdateScore(score int) {
+ p.score += score
+}
+
+func (p *Player) CalculateScore() int {
+ score := 0
+ for _, c := range p.tricks {
+ if c.GetSuit() == card.Heart {
+ score += 1
+ } else if c.GetSuit() == card.Spade && c.GetFace() == card.Queen {
+ score += 13
+ }
+ }
+ return score
+}
+
+func (p *Player) ResetTricks() {
+ p.tricks = nil
+}
+
+func (p *Player) HasSuit(suit card.Suit) bool {
+ for _, c := range p.hand {
+ if c.GetSuit() == suit {
+ return true
+ }
+ }
+ return false
+}
+
+func (p *Player) HasOnlyHearts() bool {
+ if p.HasSuit(card.Club) || p.HasSuit(card.Diamond) || p.HasSuit(card.Spade) || !p.HasSuit(card.Heart) {
+ return false
+ }
+ return true
+}
+
+func (p *Player) HasAllPoints() bool {
+ for _, c := range p.hand {
+ if c.GetSuit() == card.Diamond || c.GetSuit() == card.Club {
+ return false
+ } else if c.GetSuit() == card.Spade && c.GetFace() != card.Queen {
+ return false
+ }
+ }
+ return true
+}
diff --git a/go/src/hearts/logic/table/table.go b/go/src/hearts/logic/table/table.go
new file mode 100644
index 0000000..027caa8
--- /dev/null
+++ b/go/src/hearts/logic/table/table.go
@@ -0,0 +1,187 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package table
+
+import (
+ "hearts/logic/card"
+ "hearts/logic/player"
+ "math/rand"
+)
+
+func NewTable(p []*player.Player) *Table {
+ return &Table{
+ players: p,
+ trick: make([]*card.Card, len(p)),
+ firstPlayed: -1,
+ allCards: nil,
+ heartsBroken: false,
+ firstTrick: true,
+ winCondition: 100,
+ }
+}
+
+type Table struct {
+ // players contains all players in the game, indexed by playerIndex
+ players []*player.Player
+ // trick contains all cards in the current trick, indexed by the playerIndex of the player who played them
+ trick []*card.Card
+ // firstPlayed is the index in trick of the card played first
+ firstPlayed int
+ // allCards contains all 52 cards in the deck. GenerateCards() populates this
+ allCards []*card.Card
+ // heartsBroken returns true if a heart has been played yet in the round, otherwise false
+ heartsBroken bool
+ // firstTrick returns true if the current trick is the first in the round, otherwise false
+ firstTrick bool
+ // winCondition is the number of points needed to win the game
+ // traditionally 100, could set higher or lower for longer or shorter game
+ winCondition int
+}
+
+func (t *Table) GetPlayers() []*player.Player {
+ return t.players
+}
+
+func (t *Table) SetFirstPlayed(index int) {
+ t.firstPlayed = index
+}
+
+// This function generates a traditional deck of 52 cards, with 13 in each of the four suits
+// Each card has a suit (Club, Diamond, Spade, or Heart)
+// Each card also has a number from 2-14, where 11 through 14 represent Jack, Queen, King and Ace, respectively
+func (t *Table) GenerateCards() {
+ cardsPerSuit := 13
+ t.allCards = make([]*card.Card, 0)
+ cardFaces := []card.Face{card.Two, card.Three, card.Four, card.Five, card.Six, card.Seven, card.Eight, card.Nine,
+ card.Ten, card.Jack, card.Queen, card.King, card.Ace}
+ for i := 0; i < cardsPerSuit; i++ {
+ t.allCards = append(t.allCards, card.NewCard(cardFaces[i], card.Club))
+ t.allCards = append(t.allCards, card.NewCard(cardFaces[i], card.Diamond))
+ t.allCards = append(t.allCards, card.NewCard(cardFaces[i], card.Spade))
+ t.allCards = append(t.allCards, card.NewCard(cardFaces[i], card.Heart))
+ }
+}
+
+func (t *Table) PlayCard(c *card.Card, playerIndex int) {
+ t.trick[playerIndex] = c
+ if c.GetSuit() == card.Heart && !t.heartsBroken {
+ t.heartsBroken = true
+ }
+}
+
+func (t *Table) ValidPlay(c *card.Card, playerIndex int) bool {
+ player := t.players[playerIndex]
+ if t.firstPlayed == playerIndex {
+ if !t.firstTrick {
+ if c.GetSuit() != card.Heart || t.heartsBroken {
+ return true
+ } else {
+ if player.HasOnlyHearts() {
+ return true
+ }
+ }
+ } else if c.GetSuit() == card.Club && c.GetFace() == card.Two {
+ return true
+ }
+ } else {
+ firstPlayedSuit := t.trick[t.firstPlayed].GetSuit()
+ if c.GetSuit() == firstPlayedSuit || !player.HasSuit(firstPlayedSuit) {
+ if !t.firstTrick {
+ return true
+ } else if !c.WorthPoints() {
+ return true
+ } else if player.HasAllPoints() {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (t *Table) SendTrick() {
+ trickSuit := t.trick[t.firstPlayed].GetSuit()
+ highestCardFace := card.Two
+ highestIndex := -1
+ for i := 0; i < len(t.trick); i++ {
+ curCard := t.trick[i]
+ if curCard.GetSuit() == trickSuit && curCard.GetFace() >= highestCardFace {
+ highestCardFace = curCard.GetFace()
+ highestIndex = i
+ }
+ }
+ // clear trick
+ t.players[highestIndex].TakeTrick(t.trick)
+ t.trick = make([]*card.Card, len(t.players))
+ if t.firstTrick {
+ t.firstTrick = false
+ }
+}
+
+func (t *Table) ScoreRound() {
+ allPoints := 26
+ roundScores := make([]int, len(t.players))
+ shotMoon := false
+ shooter := -1
+ for i := 0; i < len(t.players); i++ {
+ roundScores[i] = t.players[i].CalculateScore()
+ if roundScores[i] == allPoints {
+ shotMoon = true
+ shooter = i
+ }
+ }
+ if shotMoon {
+ for i := 0; i < len(t.players); i++ {
+ if i == shooter {
+ roundScores[i] = 0
+ } else {
+ roundScores[i] = allPoints
+ }
+ }
+ }
+ // sending scores to players
+ for i := 0; i < len(t.players); i++ {
+ t.players[i].UpdateScore(roundScores[i])
+ }
+}
+
+func (t *Table) Deal() {
+ numPlayers := len(t.players)
+ if t.allCards == nil {
+ t.GenerateCards()
+ }
+ shuffle := rand.Perm(len(t.allCards))
+ for i := 0; i < len(t.allCards); i++ {
+ t.players[i%numPlayers].AddToHand(t.allCards[shuffle[i]])
+ }
+}
+
+// returns -1 if the game hasn't been won, playerIndex of the winner if it has
+// to-do: return a list of players in the event of a tie
+func (t *Table) EndRound() int {
+ t.ScoreRound()
+ lowestScore := -1
+ winningPlayer := -1
+ winTriggered := false
+ for _, p := range t.players {
+ p.ResetTricks()
+ if p.GetScore() >= t.winCondition {
+ winTriggered = true
+ }
+ if p.GetScore() < lowestScore || lowestScore == -1 {
+ lowestScore = p.GetScore()
+ winningPlayer = p.GetPlayerIndex()
+ }
+ }
+ if winTriggered {
+ return winningPlayer
+ }
+ return -1
+}
+
+func (t *Table) NewRound() {
+ t.heartsBroken = false
+ t.firstTrick = true
+ t.Deal()
+}
diff --git a/go/src/hearts/main.go b/go/src/hearts/main.go
index e2bfe76..5f13480 100644
--- a/go/src/hearts/main.go
+++ b/go/src/hearts/main.go
@@ -7,11 +7,18 @@
import (
"image"
"log"
- "math"
+ "sort"
+ "strconv"
"time"
+ _ "image/jpeg"
_ "image/png"
+ "hearts/img"
+ "hearts/logic/card"
+ "hearts/logic/player"
+ "hearts/logic/table"
+
"golang.org/x/mobile/app"
"golang.org/x/mobile/asset"
"golang.org/x/mobile/event/paint"
@@ -25,33 +32,49 @@
)
var (
- startTime = time.Now()
- eng = glsprite.Engine()
- scene *sprite.Node
- cards []*sprite.Node
- //cardsXY and initialCardsXY are in Pt
- cardsXY [][]float32
- initialCardsXY [][]float32
- curCardIndex = -1
- //lastMouseXY is in Px: divide by ppp to get Pt
+ startTime = time.Now()
+ eng = glsprite.Engine()
+ scene *sprite.Node
+ cards []*card.Card
+ backgroundImgs []*img.StaticImg
+ emptySuitImgs []*img.StaticImg
+ dropTargets []*img.StaticImg
+ buttons []*img.StaticImg
+ curCard *card.Card
+ numPlayers = 4
+ // lastMouseXY is in Px: divide by ppp to get Pt
lastMouseXY = []float32{-1, -1}
- cardWidth = float32(50)
- cardHeight = float32(50)
- //windowSize is in Pt
- windowSize = []float32{-1, -1}
- //ppp stands for pixels per pt
+ cardSize = 35
+ cardWidth = float32(cardSize)
+ cardHeight = float32(cardSize)
+ // windowSize is in Pt
+ windowSize = []float32{-1, -1}
+ padding = float32(5)
+ topPadding = float32(7)
+ bottomPadding = float32(5)
+ // ppp stands for pixels per pt
ppp float32
- //to-do: test and fine-tune these thresholds
+ // to-do: test and fine-tune these thresholds
swipeMoveThreshold = 70
swipeTimeThreshold = .5
swipeStart time.Time
animSpeedScaler = .1
animRotationScaler = .15
+ d Direction
+)
+
+type Direction string
+
+const (
+ right Direction = "R"
+ across Direction = "A"
+ left Direction = "L"
)
func main() {
app.Main(func(a app.App) {
var sz size.Event
+ d = right
for e := range a.Events() {
switch e := app.Filter(e).(type) {
case size.Event:
@@ -62,117 +85,214 @@
windowSize[1] = float32(sz.HeightPt)
pixelsX := float32(sz.WidthPx)
ppp = pixelsX / windowSize[0]
- adjustCards(oldWidth, oldHeight)
+ adjustImgs(oldWidth, oldHeight)
case touch.Event:
- onTouch(e)
+ onTouch(e, d)
case paint.Event:
- onPaint(sz)
+ onPaint(sz, d)
a.EndPaint(e)
}
}
})
}
-func onTouch(t touch.Event) {
+func onTouch(t touch.Event, dir Direction) {
switch t.Type.String() {
case "begin":
+ // i goes from the end backwards so that it checks cards displayed on top of other cards first
for i := len(cards) - 1; i >= 0; i-- {
- if t.X/ppp >= getCardX(i) &&
- t.Y/ppp >= getCardY(i) &&
- t.X/ppp <= cardWidth+getCardX(i) &&
- t.Y/ppp <= cardHeight+getCardY(i) {
+ curCard = cards[i]
+ if t.X/ppp >= curCard.GetX() &&
+ t.Y/ppp >= curCard.GetY() &&
+ t.X/ppp <= curCard.GetWidth()+curCard.GetX() &&
+ t.Y/ppp <= curCard.GetHeight()+curCard.GetY() {
swipeStart = time.Now()
- curCardIndex = i
- node := getCardNode(i)
+ node := curCard.GetNode()
node.Arranger = nil
lastMouseXY[0] = t.X
lastMouseXY[1] = t.Y
return
+ } else {
+ curCard = nil
+ }
+ }
+ for _, b := range buttons {
+ if t.X/ppp >= b.GetX() &&
+ t.Y/ppp >= b.GetY() &&
+ t.X/ppp <= b.GetWidth()+b.GetX() &&
+ t.Y/ppp <= b.GetHeight()+b.GetY() {
+ eng.SetSubTex(b.GetNode(), b.GetAlt())
+ for _, d := range dropTargets {
+ passCard := d.GetCardHere()
+ if passCard != nil {
+ animate(passCard, dir, t)
+ d.SetCardHere(nil)
+ }
+ }
}
}
case "move":
- if curCardIndex > -1 {
- eng.SetTransform(cards[curCardIndex], f32.Affine{
- {cardWidth, 0, getCardX(curCardIndex) + (t.X-lastMouseXY[0])/ppp},
- {0, cardHeight, getCardY(curCardIndex) + (t.Y-lastMouseXY[1])/ppp},
+ if curCard != nil {
+ newX := curCard.GetX() + (t.X-lastMouseXY[0])/ppp
+ newY := curCard.GetY() + (t.Y-lastMouseXY[1])/ppp
+ width := curCard.GetWidth()
+ height := curCard.GetHeight()
+ eng.SetTransform(curCard.GetNode(), f32.Affine{
+ {width, 0, newX},
+ {0, height, newY},
})
- setCardX(curCardIndex, getCardX(curCardIndex)+(t.X-lastMouseXY[0])/ppp)
- setCardY(curCardIndex, getCardY(curCardIndex)+(t.Y-lastMouseXY[1])/ppp)
+ curCard.SetPos(newX, newY, width, height)
lastMouseXY[0] = t.X
lastMouseXY[1] = t.Y
}
case "end":
- if curCardIndex > -1 {
- xDiff := getCardX(curCardIndex) - getInitialX(curCardIndex)
- yDiff := getCardY(curCardIndex) - getInitialY(curCardIndex)
- timeDiff := time.Since(swipeStart).Seconds()
- if (math.Abs(float64(xDiff))+math.Abs(float64(yDiff))) >= float64(swipeMoveThreshold) && timeDiff <= float64(swipeTimeThreshold) {
- animate(xDiff, yDiff, t)
- } else {
- eng.SetTransform(cards[curCardIndex], f32.Affine{
- {cardWidth, 0, getInitialX(curCardIndex)},
- {0, cardHeight, getInitialY(curCardIndex)},
- })
- setCardX(curCardIndex, getInitialX(curCardIndex))
- setCardY(curCardIndex, getInitialY(curCardIndex))
+ if curCard != nil {
+ successfulDrop := false
+ for _, d := range dropTargets {
+ // checking to see if card was dropped onto a drop target
+ if t.X/ppp >= d.GetX() &&
+ t.Y/ppp >= d.GetY() &&
+ t.X/ppp <= d.GetWidth()+d.GetX() &&
+ t.Y/ppp <= d.GetHeight()+d.GetY() {
+ lastDroppedCard := d.GetCardHere()
+ if lastDroppedCard != nil {
+ resetCardPosition(lastDroppedCard)
+ }
+ oldY := curCard.GetInitialY()
+ suit := curCard.GetSuit()
+ newX := d.GetX()
+ newY := d.GetY()
+ width := curCard.GetWidth()
+ height := curCard.GetHeight()
+ eng.SetTransform(curCard.GetNode(), f32.Affine{
+ {width, 0, newX},
+ {0, height, newY},
+ })
+ curCard.SetPos(newX, newY, width, height)
+ d.SetCardHere(curCard)
+ successfulDrop = true
+ // realign suit the card just left
+ realignSuit(suit, oldY)
+ } else {
+ // checking to see if card was removed from a dop target
+ if d.GetCardHere() == curCard {
+ d.SetCardHere(nil)
+ }
+ }
+ }
+ if !successfulDrop {
+ resetCardPosition(curCard)
}
}
- curCardIndex = -1
+ for _, b := range buttons {
+ eng.SetSubTex(b.GetNode(), b.GetImage())
+ }
+ curCard = nil
}
}
-func adjustCards(oldWidth, oldHeight float32) {
- for i := 0; i < len(cards); i++ {
- node := getCardNode(i)
- oldX := getCardX(i)
- newX := (oldX+cardWidth/2)/oldWidth*windowSize[0] - cardWidth/2
- oldY := getCardY(i)
- newY := (oldY+cardHeight/2)/oldHeight*windowSize[1] - cardHeight/2
+func resetCardPosition(c *card.Card) {
+ newX := c.GetInitialX()
+ newY := c.GetInitialY()
+ eng.SetTransform(c.GetNode(), f32.Affine{
+ {c.GetWidth(), 0, newX},
+ {0, c.GetHeight(), newY},
+ })
+ c.SetPos(newX, newY, c.GetWidth(), c.GetHeight())
+ realignSuit(c.GetSuit(), newY)
+}
+
+// returns coordinates for images with same width and height but in new positions proportional to the screen
+func adjustKeepDimensions(oldX, oldY, oldInitialX, oldInitialY, oldImgWidth, oldImgHeight, oldWindowWidth, oldWindowHeight float32) (float32, float32, float32, float32, float32, float32) {
+ newX := (oldX+oldImgWidth/2)/oldWindowWidth*windowSize[0] - oldImgWidth/2
+ newY := (oldY+oldImgHeight/2)/oldWindowHeight*windowSize[1] - oldImgHeight/2
+ newInitialX := (oldInitialX+oldImgWidth/2)/oldWindowWidth*windowSize[0] - oldImgWidth/2
+ newInitialY := (oldInitialY+oldImgHeight/2)/oldWindowHeight*windowSize[1] - oldImgHeight/2
+ return newX, newY, newInitialX, newInitialY, oldImgWidth, oldImgHeight
+}
+
+// returns coordinates for images with position, width and height scaled proportional to the screen
+func adjustScaleDimensions(oldX, oldY, oldInitialX, oldInitialY, oldImgWidth, oldImgHeight, oldWindowWidth, oldWindowHeight float32) (float32, float32, float32, float32, float32, float32) {
+ newImgWidth := oldImgWidth / oldWindowWidth * windowSize[0]
+ newImgHeight := oldImgHeight / oldWindowHeight * windowSize[1]
+ newX := oldX / oldWindowWidth * windowSize[0]
+ newY := oldY / oldWindowHeight * windowSize[1]
+ newInitialX := oldInitialX / oldWindowWidth * windowSize[0]
+ newInitialY := oldInitialY / oldWindowHeight * windowSize[1]
+ return newX, newY, newInitialX, newInitialY, newImgWidth, newImgHeight
+}
+
+func adjustImgArray(imgs []*img.StaticImg, oldWindowWidth, oldWindowHeight float32) {
+ for _, s := range imgs {
+ node := s.GetNode()
+ oldImgWidth := s.GetWidth()
+ oldImgHeight := s.GetHeight()
+ oldX := s.GetX()
+ oldY := s.GetY()
+ oldInitialX := s.GetInitialX()
+ oldInitialY := s.GetInitialY()
+ newX, newY, newInitialX, newInitialY, newImgWidth, newImgHeight := adjustScaleDimensions(oldX, oldY,
+ oldInitialX, oldInitialY, oldImgWidth, oldImgHeight, oldWindowWidth, oldWindowHeight)
eng.SetTransform(node, f32.Affine{
- {cardWidth, 0, newX},
- {0, cardHeight, newY},
+ {newImgWidth, 0, newX},
+ {0, newImgHeight, newY},
})
- setCardX(i, newX)
- setCardY(i, newY)
- oldInitialX := getInitialX(i)
- newInitialX := (oldInitialX+cardWidth/2)/oldWidth*windowSize[0] - cardWidth/2
- oldInitialY := getInitialY(i)
- newInitialY := (oldInitialY+cardHeight/2)/oldHeight*windowSize[1] - cardHeight/2
- setInitialX(i, newInitialX)
- setInitialY(i, newInitialY)
+ s.SetPos(newX, newY, newImgWidth, newImgHeight)
+ s.SetInitialPos(newInitialX, newInitialY)
}
}
-func animate(xDiff float32, yDiff float32, touch touch.Event) {
- index := curCardIndex
- curCardIndex = -1
- node := getCardNode(index)
+func adjustImgs(oldWindowWidth, oldWindowHeight float32) {
+ if windowSize[0] > -1 && oldWindowWidth > -1 {
+ padding = padding * windowSize[0] / oldWindowWidth
+ }
+ for _, c := range cards {
+ node := c.GetNode()
+ oldCardWidth := c.GetWidth()
+ oldCardHeight := c.GetHeight()
+ oldX := c.GetX()
+ oldInitialX := c.GetInitialX()
+ oldY := c.GetY()
+ oldInitialY := c.GetInitialY()
+ newX, newY, newInitialX, newInitialY, newCardWidth, newCardHeight := adjustScaleDimensions(oldX, oldY,
+ oldInitialX, oldInitialY, oldCardWidth, oldCardHeight, oldWindowWidth, oldWindowHeight)
+ eng.SetTransform(node, f32.Affine{
+ {newCardWidth, 0, newX},
+ {0, newCardHeight, newY},
+ })
+ c.SetPos(newX, newY, newCardWidth, newCardHeight)
+ c.SetInitialPos(newInitialX, newInitialY)
+ }
+ adjustImgArray(dropTargets, oldWindowWidth, oldWindowHeight)
+ adjustImgArray(backgroundImgs, oldWindowWidth, oldWindowHeight)
+ adjustImgArray(buttons, oldWindowWidth, oldWindowHeight)
+ adjustImgArray(emptySuitImgs, oldWindowWidth, oldWindowHeight)
+}
+
+func animate(animCard *card.Card, dir Direction, touch touch.Event) {
+ node := animCard.GetNode()
startTime := -1
node.Arranger = arrangerFunc(func(eng sprite.Engine, node *sprite.Node, t clock.Time) {
if startTime == -1 {
startTime = int(t)
}
moveSpeed := float32(int(t)-startTime) * float32(animSpeedScaler)
- x := getCardX(index)
- y := getCardY(index)
- if math.Abs(float64(xDiff)) > math.Abs(float64(yDiff)) {
- if xDiff > 0 {
- x = x + moveSpeed
- } else {
- x = x - moveSpeed
- }
- } else {
- if yDiff > 0 {
- y = y + moveSpeed
- } else {
- y = y - moveSpeed
- }
+ x := animCard.GetX()
+ y := animCard.GetY()
+ width := animCard.GetWidth()
+ height := animCard.GetHeight()
+ switch dir {
+ case right:
+ x = x + moveSpeed
+ case left:
+ x = x - moveSpeed
+ case across:
+ y = y - moveSpeed
}
- setCardX(index, x)
- setCardY(index, y)
+ animCard.SetPos(x, y, width, height)
position := f32.Affine{
- {cardWidth, 0, x + cardWidth/2},
- {0, cardHeight, y + cardHeight/2},
+ {width, 0, x + width/2},
+ {0, height, y + height/2},
}
position.Rotate(&position, float32(t)*float32(animRotationScaler))
position.Translate(&position, -.5, -.5)
@@ -180,9 +300,9 @@
})
}
-func onPaint(sz size.Event) {
+func onPaint(sz size.Event, dir Direction) {
if scene == nil {
- loadScene()
+ loadPassScreen(dir)
}
gl.ClearColor(1, 1, 1, 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
@@ -197,59 +317,146 @@
return n
}
-//x and y should be in Pt
-func addCard(x float32, y float32, spriteTex sprite.SubTex) {
+func initializeGame() *table.Table {
+ players := make([]*player.Player, 0)
+ for i := 0; i < numPlayers; i++ {
+ players = append(players, player.NewPlayer(i))
+ }
+ return table.NewTable(players)
+}
+
+func realignSuit(suitName card.Suit, oldY float32) {
+ cardsToAlign := make([]*card.Card, 0)
+ for _, c := range cards {
+ if c.GetSuit() == suitName && c.GetY() == oldY {
+ cardsToAlign = append(cardsToAlign, c)
+ }
+ }
+ var emptySuitImg *img.StaticImg
+ switch suitName {
+ case card.Club:
+ emptySuitImg = emptySuitImgs[0]
+ case card.Diamond:
+ emptySuitImg = emptySuitImgs[1]
+ case card.Spade:
+ emptySuitImg = emptySuitImgs[2]
+ case card.Heart:
+ emptySuitImg = emptySuitImgs[3]
+ }
+ if len(cardsToAlign) == 0 {
+ eng.SetSubTex(emptySuitImg.GetNode(), emptySuitImg.GetImage())
+ } else {
+ eng.SetSubTex(emptySuitImg.GetNode(), emptySuitImg.GetAlt())
+ }
+ for i, c := range cardsToAlign {
+ width := c.GetWidth()
+ height := c.GetHeight()
+ diff := float32(len(cardsToAlign))*(padding+width) - (windowSize[0] - padding)
+ x := padding + float32(i)*(padding+width)
+ if diff > 0 && i > 0 {
+ x -= diff * float32(i) / float32(len(cardsToAlign)-1)
+ }
+ y := oldY
+ c.SetPos(x, y, width, height)
+ c.SetInitialPos(x, y)
+ eng.SetTransform(c.GetNode(), f32.Affine{
+ {width, 0, x},
+ {0, height, y},
+ })
+ }
+}
+
+func addCard(c *card.Card, texs map[string]sprite.SubTex, numInSuit, clubCount, diamondCount, spadeCount, heartCount int) {
+ var texKey string
+ var suitCount float32
+ var heightScaler float32
+ switch c.GetSuit() {
+ case card.Club:
+ texKey = "Clubs-"
+ suitCount = float32(clubCount)
+ heightScaler = 4
+ case card.Diamond:
+ texKey = "Diamonds-"
+ suitCount = float32(diamondCount)
+ heightScaler = 3
+ case card.Spade:
+ texKey = "Spades-"
+ suitCount = float32(spadeCount)
+ heightScaler = 2
+ case card.Heart:
+ texKey = "Hearts-"
+ suitCount = float32(heartCount)
+ heightScaler = 1
+ }
+ log.Println(c.GetFace())
+ log.Println(card.Two)
+ switch c.GetFace() {
+ case card.Jack:
+ texKey += "Jack"
+ case card.Queen:
+ texKey += "Queen"
+ case card.King:
+ texKey += "King"
+ case card.Ace:
+ texKey += "Ace"
+ default:
+ texKey += strconv.Itoa(int(c.GetFace()))
+ }
+ texKey += ".png"
+ log.Println(texKey)
n := newNode()
- eng.SetSubTex(n, spriteTex)
- eng.SetTransform(n, f32.Affine{
- {cardWidth, 0, x},
- {0, cardHeight, y},
+ eng.SetSubTex(n, texs[texKey])
+ c.SetNode(n)
+ diff := suitCount*(padding+cardWidth) - (windowSize[0] - padding)
+ x := padding + float32(numInSuit)*(padding+cardWidth)
+ if diff > 0 && numInSuit > 0 {
+ x -= diff * float32(numInSuit) / (suitCount - 1)
+ }
+ y := windowSize[1] - heightScaler*(cardHeight+padding) - bottomPadding
+ width := cardWidth
+ height := cardHeight
+ c.SetPos(x, y, width, height)
+ c.SetInitialPos(x, y)
+ eng.SetTransform(c.GetNode(), f32.Affine{
+ {width, 0, x},
+ {0, height, y},
})
- cards = append(cards, n)
- cardsXY = append(cardsXY, []float32{x, y})
- initialCardsXY = append(initialCardsXY, []float32{x, y})
}
-func getCardX(cardIndex int) float32 {
- return cardsXY[cardIndex][0]
+func addImgWithoutAlt(t sprite.SubTex, x, y, width, height float32) *img.StaticImg {
+ n := newNode()
+ eng.SetSubTex(n, t)
+ eng.SetTransform(n, f32.Affine{
+ {width, 0, x},
+ {0, height, y},
+ })
+ s := img.NewStaticImg()
+ s.SetNode(n)
+ s.SetImage(t)
+ s.SetPos(x, y, width, height)
+ s.SetInitialPos(x, y)
+ return s
}
-func setCardX(cardIndex int, newX float32) {
- cardsXY[cardIndex][0] = newX
+func addImgWithAlt(t sprite.SubTex, alt sprite.SubTex, x, y, width, height float32, displayImage bool) *img.StaticImg {
+ n := newNode()
+ if displayImage {
+ eng.SetSubTex(n, t)
+ }
+ eng.SetTransform(n, f32.Affine{
+ {width, 0, x},
+ {0, height, y},
+ })
+ s := img.NewStaticImg()
+ s.SetNode(n)
+ s.SetImage(t)
+ s.SetAlt(alt)
+ s.SetPos(x, y, width, height)
+ s.SetInitialPos(x, y)
+ return s
}
-func getCardY(cardIndex int) float32 {
- return cardsXY[cardIndex][1]
-}
-
-func setCardY(cardIndex int, newY float32) {
- cardsXY[cardIndex][1] = newY
-}
-
-func getCardNode(cardIndex int) *sprite.Node {
- return cards[cardIndex]
-}
-
-func getInitialX(cardIndex int) float32 {
- return initialCardsXY[cardIndex][0]
-}
-
-func getInitialY(cardIndex int) float32 {
- return initialCardsXY[cardIndex][1]
-}
-
-func setInitialX(cardIndex int, newX float32) {
- initialCardsXY[cardIndex][0] = newX
-}
-
-func setInitialY(cardIndex int, newY float32) {
- initialCardsXY[cardIndex][1] = newY
-}
-
-func loadScene() {
- cards = make([]*sprite.Node, 0)
- cardsXY = make([][]float32, 0)
- initialCardsXY = make([][]float32, 0)
+func loadPassScreen(dir Direction) {
texs := loadTextures()
scene = &sprite.Node{}
eng.Register(scene)
@@ -257,10 +464,162 @@
{1, 0, 0},
{0, 1, 0},
})
- for _, v := range texs {
- x := windowSize[0]/2 - cardWidth/2
- y := windowSize[1]/2 - cardHeight/2
- addCard(x, y, v)
+ t := initializeGame()
+ t.Deal()
+ cards = t.GetPlayers()[1].GetHand()
+ dropTargets = make([]*img.StaticImg, 0)
+ backgroundImgs = make([]*img.StaticImg, 0)
+ emptySuitImgs = make([]*img.StaticImg, 0)
+ buttons = make([]*img.StaticImg, 0)
+ sort.Sort(cardSorter(cards))
+ clubCount := 0
+ diamondCount := 0
+ spadeCount := 0
+ heartCount := 0
+ for i := 0; i < len(cards); i++ {
+ switch cards[i].GetSuit() {
+ case card.Club:
+ clubCount++
+ case card.Diamond:
+ diamondCount++
+ case card.Spade:
+ spadeCount++
+ case card.Heart:
+ heartCount++
+ }
+ }
+ suitCounts := []int{clubCount, diamondCount, spadeCount, heartCount}
+ numSuits := 4
+ numDropTargets := 3
+ // adding blue banner for croupier header
+ image := texs["blue.png"]
+ headerX := float32(0)
+ headerY := float32(0)
+ headerWidth := windowSize[0]
+ var headerHeight float32
+ if 2*cardHeight < headerWidth/4 {
+ headerHeight = 2 * cardHeight
+ } else {
+ headerHeight = headerWidth / 4
+ }
+ header := addImgWithoutAlt(image, headerX, headerY, headerWidth, headerHeight)
+ backgroundImgs = append(backgroundImgs, header)
+ // adding croupier name on top of banner
+ image = texs["croupierName.png"]
+ var width float32
+ var height float32
+ if headerHeight-topPadding > headerWidth/6 {
+ width = headerWidth / 2
+ height = width / 3
+ } else {
+ height = 2 * headerHeight / 3
+ width = height * 3
+ }
+ x := headerX + (headerWidth-width)/2
+ y := headerY + (headerHeight-height+topPadding)/2
+ headerText := addImgWithoutAlt(image, x, y, width, height)
+ backgroundImgs = append(backgroundImgs, headerText)
+ // adding blue background banner for drop targets
+ topOfHand := windowSize[1] - 5*(cardHeight+padding) - (2 * padding / 5) - bottomPadding
+ image = texs["blue.png"]
+ x = float32(0)
+ passBannerY := topOfHand - (2 * padding)
+ width = windowSize[0]
+ height = cardHeight + (4 * padding / 5)
+ newImg := addImgWithoutAlt(image, x, passBannerY, width, height)
+ backgroundImgs = append(backgroundImgs, newImg)
+ // adding drop targets
+ for i := 0; i < numDropTargets; i++ {
+ image := texs["white.png"]
+ width := cardWidth
+ height := cardHeight
+ x := windowSize[0]/2 - (width+float32(numDropTargets)*(padding+width))/2 + float32(i)*(padding+width)
+ y := passBannerY + (2 * padding / 5)
+ newTarget := addImgWithoutAlt(image, x, y, width, height)
+ dropTargets = append(dropTargets, newTarget)
+ }
+ // adding pass button
+ pressedImg := texs["passPressed.png"]
+ unpressedImg := texs["passUnpressed.png"]
+ width = cardWidth
+ height = cardHeight / 2
+ x = windowSize[0]/2 + (float32(numDropTargets)*(padding+width)-width)/2
+ passY := passBannerY + (2 * padding / 5)
+ newButton := addImgWithAlt(unpressedImg, pressedImg, x, passY, width, height, true)
+ buttons = append(buttons, newButton)
+ // adding arrow below pass button
+ var a *img.StaticImg
+ if dir == right {
+ image := texs["rightArrow.png"]
+ width := cardWidth
+ height := cardHeight / 2
+ x := windowSize[0]/2 + (float32(numDropTargets)*(padding+cardWidth)-width)/2
+ y := passY + cardHeight/2
+ a = addImgWithoutAlt(image, x, y, width, height)
+ } else if dir == left {
+ image := texs["leftArrow.png"]
+ width := cardWidth
+ height := cardHeight / 2
+ x := windowSize[0]/2 + (float32(numDropTargets)*(padding+cardWidth)-width)/2
+ y := passY + cardHeight/2
+ a = addImgWithoutAlt(image, x, y, width, height)
+ } else {
+ image := texs["acrossArrow.png"]
+ width := cardWidth / 4
+ height := cardHeight / 2
+ x := windowSize[0]/2 + (float32(numDropTargets)*(padding+cardWidth)-width)/2
+ y := passY + cardHeight/2
+ a = addImgWithoutAlt(image, x, y, width, height)
+ }
+ backgroundImgs = append(backgroundImgs, a)
+ // adding gray background banners for each suit
+ for i := 0; i < numSuits; i++ {
+ image := texs["gray.jpeg"]
+ x := float32(0)
+ y := windowSize[1] - float32(i+1)*(cardHeight+padding) - (2 * padding / 5) - bottomPadding
+ width := windowSize[0]
+ height := cardHeight + (4 * padding / 5)
+ newImg := addImgWithoutAlt(image, x, y, width, height)
+ backgroundImgs = append(backgroundImgs, newImg)
+ }
+ // adding suit image to any empty suit in hand
+ for i, c := range suitCounts {
+ var texKey string
+ switch i {
+ case 0:
+ texKey = "Club.png"
+ case 1:
+ texKey = "Diamond.png"
+ case 2:
+ texKey = "Spade.png"
+ case 3:
+ texKey = "Heart.png"
+ }
+ image := texs[texKey]
+ alt := texs["gray.png"]
+ x := windowSize[0]/2 - cardWidth/3
+ y := windowSize[1] - float32(4-i)*(cardHeight+padding) + cardHeight/6 - bottomPadding
+ width := 2 * cardWidth / 3
+ height := 2 * cardHeight / 3
+ display := c == 0
+ newSuitImg := addImgWithAlt(image, alt, x, y, width, height, display)
+ emptySuitImgs = append(emptySuitImgs, newSuitImg)
+ }
+ // adding clubs
+ for i := 0; i < clubCount; i++ {
+ addCard(cards[i], texs, i, clubCount, diamondCount, spadeCount, heartCount)
+ }
+ // adding diamonds
+ for i := clubCount; i < clubCount+diamondCount; i++ {
+ addCard(cards[i], texs, i-clubCount, clubCount, diamondCount, spadeCount, heartCount)
+ }
+ // adding spades
+ for i := clubCount + diamondCount; i < clubCount+diamondCount+spadeCount; i++ {
+ addCard(cards[i], texs, i-clubCount-diamondCount, clubCount, diamondCount, spadeCount, heartCount)
+ }
+ // adding hearts
+ for i := clubCount + diamondCount + spadeCount; i < clubCount+diamondCount+spadeCount+heartCount; i++ {
+ addCard(cards[i], texs, i-clubCount-diamondCount-spadeCount, clubCount, diamondCount, spadeCount, heartCount)
}
}
@@ -273,7 +632,9 @@
"Spades-2.png", "Spades-3.png", "Spades-4.png", "Spades-5.png", "Spades-6.png", "Spades-7.png", "Spades-8.png",
"Spades-9.png", "Spades-10.png", "Spades-Jack.png", "Spades-Queen.png", "Spades-King.png", "Spades-Ace.png",
"Hearts-2.png", "Hearts-3.png", "Hearts-4.png", "Hearts-5.png", "Hearts-6.png", "Hearts-7.png", "Hearts-8.png",
- "Hearts-9.png", "Hearts-10.png", "Hearts-Jack.png", "Hearts-Queen.png", "Hearts-King.png", "Hearts-Ace.png"}
+ "Hearts-9.png", "Hearts-10.png", "Hearts-Jack.png", "Hearts-Queen.png", "Hearts-King.png", "Hearts-Ace.png",
+ "Club.png", "Diamond.png", "Spade.png", "Heart.png", "gray.jpeg", "blue.png", "white.png", "passPressed.png",
+ "passUnpressed.png", "leftArrow.png", "rightArrow.png", "acrossArrow.png", "croupierName.png"}
for _, f := range files {
a, err := asset.Open(f)
if err != nil {
@@ -289,7 +650,15 @@
if err != nil {
log.Fatal(err)
}
- allTexs[f] = sprite.SubTex{t, image.Rect(0, 0, 100, 100)}
+ imgWidth, imgHeight := t.Bounds()
+ if f == "Club.png" || f == "Diamond.png" || f == "Spade.png" || f == "Heart.png" || f == "rightArrow.png" ||
+ f == "leftArrow.png" || f == "acrossArrow.png" || f == "passUnpressed.png" || f == "passPressed.png" ||
+ f == "croupierName.png" {
+ allTexs[f] = sprite.SubTex{t, image.Rect(1, 1, imgWidth-1, imgHeight-1)}
+ } else {
+ allTexs[f] = sprite.SubTex{t, image.Rect(0, 0, imgWidth, imgHeight)}
+ }
+
}
return allTexs
}
@@ -297,3 +666,31 @@
type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)
func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }
+
+type cardSorter []*card.Card
+
+func (cs cardSorter) Len() int {
+ return len(cs)
+}
+
+func (cs cardSorter) Swap(i, j int) {
+ cs[i], cs[j] = cs[j], cs[i]
+}
+
+func (cs cardSorter) Less(i, j int) bool {
+ if cs[i].GetSuit() == cs[j].GetSuit() {
+ return cs[i].GetFace() < cs[j].GetFace()
+ } else {
+ switch cs[i].GetSuit() {
+ case card.Club:
+ return true
+ case card.Diamond:
+ return cs[j].GetSuit() != card.Club
+ case card.Spade:
+ return cs[j].GetSuit() == card.Heart
+ case card.Heart:
+ return false
+ }
+ }
+ return true
+}