919 lines
23 KiB
Go
919 lines
23 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"gitlab.com/tslocum/joker"
|
|
. "gitlab.com/tslocum/joker"
|
|
cribbage "gitlab.com/tslocum/joker-cribbage"
|
|
)
|
|
|
|
const (
|
|
PhaseEnd = -1
|
|
PhaseSetup = 0
|
|
PhasePick = 1
|
|
PhasePeg = 2
|
|
PhaseScore = 3
|
|
)
|
|
|
|
const (
|
|
PegPhaseNormal = 0
|
|
PegPhaseSolo = 1
|
|
PegPhaseFinal = 2
|
|
)
|
|
|
|
const WinningScore = 121
|
|
|
|
const shuffleCount = 7
|
|
|
|
type Game struct {
|
|
CommandQueue chan GameCommand `json:"-"`
|
|
|
|
Deck *Deck `json:"-"`
|
|
Phase int `json:"phase"`
|
|
Dealer int `json:"dealer"`
|
|
Turn int `json:"turn"`
|
|
Starter Card `json:"starter"`
|
|
|
|
Player1 *Client `json:"-"`
|
|
Player2 *Client `json:"-"`
|
|
Hand1 Cards `json:"hand1"`
|
|
Hand2 Cards `json:"hand2"`
|
|
Crib Cards `json:"crib"`
|
|
ThrownCrib1 int
|
|
ThrownCrib2 int
|
|
Score1 int `json:"score1"`
|
|
Score2 int `json:"score2"`
|
|
|
|
ThrowPile PlayerCards `json:"throwpile"`
|
|
DiscardPile PlayerCards `json:"-"`
|
|
|
|
PegPhase int `json:"-"`
|
|
sync.RWMutex `json:"-"`
|
|
}
|
|
|
|
func NewGame(player1 *Client, player2 *Client) *Game {
|
|
game := new(Game)
|
|
game.Initialize()
|
|
|
|
game.addPlayer(1, player1)
|
|
game.addPlayer(2, player2)
|
|
|
|
game.Deal()
|
|
return game
|
|
}
|
|
|
|
func (g *Game) Initialize() {
|
|
g.CommandQueue = make(chan GameCommand, 10)
|
|
|
|
g.Dealer = rand.Intn(2) + 1
|
|
if g.Dealer == 1 {
|
|
g.Turn = 2
|
|
} else {
|
|
g.Turn = 1
|
|
}
|
|
|
|
g.Reset()
|
|
}
|
|
|
|
func (g *Game) Reset() {
|
|
g.Hand1 = Cards{}
|
|
g.Hand2 = Cards{}
|
|
g.Crib = Cards{}
|
|
g.Starter = Card{}
|
|
g.ThrowPile = PlayerCards{}
|
|
g.DiscardPile = PlayerCards{}
|
|
g.ThrownCrib1 = 0
|
|
g.ThrownCrib2 = 0
|
|
|
|
g.Deck = NewDeck(StandardCards, 0)
|
|
for i := 0; i < shuffleCount; i++ {
|
|
g.Deck.Shuffle()
|
|
}
|
|
|
|
g.PegPhase = PegPhaseNormal
|
|
g.Phase = PhaseSetup
|
|
}
|
|
|
|
func (g *Game) addPlayer(player int, client *Client) {
|
|
client.game = g
|
|
client.gameplayer = player
|
|
client.gameready = true
|
|
|
|
if player == 1 {
|
|
g.Player1 = client
|
|
} else {
|
|
g.Player2 = client
|
|
}
|
|
}
|
|
|
|
func (g *Game) Deal() {
|
|
if g.Phase != PhaseSetup {
|
|
return
|
|
}
|
|
|
|
var dealtcards joker.Cards
|
|
var ok bool
|
|
g.Phase = PhasePick
|
|
g.ResetGo()
|
|
g.ResetReady()
|
|
|
|
for i := 0; i < 12; i++ {
|
|
dealtcards, ok = g.Deck.Draw(1)
|
|
if !ok {
|
|
log.Fatalf("failed to deal: not enough cards")
|
|
}
|
|
|
|
if i%2 == 0 {
|
|
g.Hand1 = append(g.Hand1, dealtcards[0])
|
|
} else {
|
|
g.Hand2 = append(g.Hand2, dealtcards[0])
|
|
}
|
|
}
|
|
|
|
sort.Sort(g.Hand1)
|
|
sort.Sort(g.Hand2)
|
|
}
|
|
|
|
func (g *Game) Cut(cut int) bool {
|
|
if g.Phase != PhasePick || g.Crib.Len() < 4 || g.Starter.Value() > 0 {
|
|
return false
|
|
}
|
|
|
|
g.logDebug("Cutting @", cut)
|
|
g.Starter = g.Deck.Cards[cut]
|
|
g.Phase = PhasePeg
|
|
|
|
for msgplayer := 1; msgplayer <= 2; msgplayer++ {
|
|
if g.getClient(msgplayer).ConnType == ClientTelnet {
|
|
g.getClient(msgplayer).write("Starter: " + g.Starter.String())
|
|
} else {
|
|
//g.update(msgplayer)
|
|
// TODO updateAll is called
|
|
}
|
|
}
|
|
|
|
if g.Starter.Identifier() == "j" {
|
|
g.award(g.Dealer, 2, cribbage.Peg, "Nibs")
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (g *Game) exportJSON(player int) (string, error) {
|
|
gamestate := make(map[string]string)
|
|
|
|
gamereflected := reflect.ValueOf(g).Elem()
|
|
for i := 0; i < gamereflected.NumField(); i++ {
|
|
fieldname := gamereflected.Type().Field(i).Name
|
|
fieldtag := gamereflected.Type().Field(i).Tag.Get("json")
|
|
if fieldtag == "" || fieldtag == "-" {
|
|
continue
|
|
} else if g.Phase != PhaseScore && ((fieldname == "Hand1" && player != 1) || (fieldname == "Hand2" && player != 2)) {
|
|
hand := gamereflected.Field(i).Interface().(joker.Cards)
|
|
handprinted := ""
|
|
for j := 0; j < hand.Len(); j++ {
|
|
if j > 0 {
|
|
handprinted += ","
|
|
}
|
|
handprinted += "\"??\""
|
|
}
|
|
|
|
gamestate[fieldtag] = fmt.Sprintf("[%s]", handprinted)
|
|
} else if g.Phase != PhaseScore && fieldname == "Crib" {
|
|
hand := gamereflected.Field(i).Interface().(joker.Cards)
|
|
handprinted := ""
|
|
for j := 0; j < hand.Len(); j++ {
|
|
if j > 0 {
|
|
handprinted += ","
|
|
}
|
|
handprinted += "\"??\""
|
|
}
|
|
|
|
gamestate[fieldtag] = fmt.Sprintf("[%s]", handprinted)
|
|
} else {
|
|
jsonvalue, err := json.Marshal(gamereflected.Field(i).Interface())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
gamestate[fieldtag] = string(jsonvalue)
|
|
}
|
|
}
|
|
|
|
if player > 0 {
|
|
gamestate["player"] = strconv.Itoa(player)
|
|
|
|
jsonvalue, err := json.Marshal(g.getClient(player).getStatus())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
gamestate["status"] = string(jsonvalue)
|
|
}
|
|
|
|
throwPileSum := cribbage.Sum(g.ThrowPile.Cards())
|
|
gamestate["throwpilesum"] = strconv.Itoa(throwPileSum)
|
|
|
|
throwpilescore, _ := cribbage.Score(cribbage.Peg, g.ThrowPile.Cards(), g.Starter)
|
|
if g.Phase == PhasePeg && g.PegPhase == PegPhaseFinal && g.PeggingFinished() && throwPileSum != 31 {
|
|
throwpilescore++
|
|
}
|
|
|
|
gamestate["throwpilescore"] = strconv.Itoa(throwpilescore)
|
|
|
|
gamestatelines := []string{}
|
|
for statefield, statevalue := range gamestate {
|
|
gamestatelines = append(gamestatelines, fmt.Sprintf("\"%s\": %s", statefield, statevalue))
|
|
}
|
|
|
|
return fmt.Sprintf("{%s}", strings.Join(gamestatelines, ",")), nil
|
|
}
|
|
|
|
func (g *Game) Peg(player int) int {
|
|
if g.Phase != PhasePeg || g.Turn != player {
|
|
log.Println("Error, not players turn or other invalid state")
|
|
|
|
return -1
|
|
}
|
|
|
|
pegscore, _ := cribbage.Score(cribbage.Peg, g.ThrowPile.Cards(), g.Starter)
|
|
if pegscore > 0 {
|
|
g.award(g.Turn, pegscore, cribbage.Peg, "")
|
|
}
|
|
|
|
return pegscore
|
|
}
|
|
|
|
func (g *Game) award(player int, score int, scoretype cribbage.ScoringType, reason string) int {
|
|
if g.Phase != PhaseEnd {
|
|
points := "point"
|
|
if score != 1 {
|
|
points = "points"
|
|
}
|
|
|
|
st := "scored"
|
|
if scoretype == cribbage.Peg {
|
|
st = "pegged"
|
|
}
|
|
|
|
if reason != "" {
|
|
reason = " - " + reason
|
|
}
|
|
|
|
for msgplayer := 1; msgplayer <= 2; msgplayer++ {
|
|
g.getClient(msgplayer).write(fmt.Sprintf("%s %s %d %s%s", g.getName(msgplayer, player), st, score, points, reason))
|
|
}
|
|
|
|
if player == 1 {
|
|
g.Score1 += score
|
|
} else {
|
|
g.Score2 += score
|
|
}
|
|
|
|
if g.getScore(player) >= WinningScore {
|
|
g.end(player, "Won!")
|
|
}
|
|
}
|
|
|
|
return g.getScore(player)
|
|
}
|
|
|
|
func (g *Game) scoreHands() (int, int, int) {
|
|
if g.Phase != PhasePeg || g.Hand1.Len() > 0 || g.Hand2.Len() > 0 {
|
|
log.Println("Error, not pegging phase or player still has cards")
|
|
|
|
return -1, -1, -1
|
|
}
|
|
|
|
// TODO: Store player in card data or separate slice
|
|
g.Phase = PhaseScore
|
|
for _, card := range g.ThrowPile {
|
|
if card.Player == 1 {
|
|
g.Hand1 = append(g.Hand1, card.Card)
|
|
} else {
|
|
g.Hand2 = append(g.Hand2, card.Card)
|
|
}
|
|
}
|
|
for _, card := range g.DiscardPile {
|
|
if card.Player == 1 {
|
|
g.Hand1 = append(g.Hand1, card.Card)
|
|
} else {
|
|
g.Hand2 = append(g.Hand2, card.Card)
|
|
}
|
|
}
|
|
|
|
sort.Sort(g.Hand1)
|
|
sort.Sort(g.Hand2)
|
|
|
|
opponentHandScore, _ := cribbage.Score(cribbage.ShowHand, *g.getHand(g.getOpponent(g.Dealer)), g.Starter)
|
|
dealerHandScore, _ := cribbage.Score(cribbage.ShowHand, *g.getHand(g.Dealer), g.Starter)
|
|
dealerCribScore, _ := cribbage.Score(cribbage.Peg, g.Crib, g.Starter)
|
|
|
|
// Awarding order is important
|
|
opponentscore := g.award(g.getOpponent(g.Dealer), opponentHandScore, cribbage.ShowHand, "Hand")
|
|
dealerscore := g.award(g.Dealer, dealerHandScore, cribbage.ShowHand, "Hand")
|
|
dealercribscore := g.award(g.Dealer, dealerCribScore, cribbage.ShowHand, "Crib")
|
|
|
|
var yourscore string
|
|
var theirscore string
|
|
dealerscoreprinted := fmt.Sprintf("%d (%d hand + %d crib)", dealerHandScore+dealerCribScore, dealerHandScore, dealerCribScore)
|
|
opponentscoreprinted := fmt.Sprintf("%d", opponentHandScore)
|
|
for msgplayer := 1; msgplayer <= 2; msgplayer++ {
|
|
if g.Dealer == msgplayer {
|
|
yourscore = dealerscoreprinted
|
|
theirscore = opponentscoreprinted
|
|
} else {
|
|
yourscore = opponentscoreprinted
|
|
theirscore = dealerscoreprinted
|
|
}
|
|
|
|
g.getClient(msgplayer).gameready = false
|
|
g.getClient(msgplayer).setStatus("You scored " + yourscore + " - Your opponent scored " + theirscore)
|
|
|
|
log.Println(msgplayer, "You scored "+yourscore+" - Your opponent scored "+theirscore)
|
|
}
|
|
|
|
return opponentscore, dealerscore, dealercribscore
|
|
}
|
|
|
|
func (g *Game) end(player int, reason string) {
|
|
g.CommandQueue <- GameCommand{Player: player, Command: CommandEnd, Value: reason}
|
|
}
|
|
|
|
func (g *Game) Throw(player int, cardidentifier string) bool {
|
|
if g.Phase == PhaseScore {
|
|
return false
|
|
} else if g.Phase == PhasePick {
|
|
// TODO Store players of thrown cards or check number of cards still left in thrower hand
|
|
if (player == 1 && g.ThrownCrib1 == 2) || (player == 2 && g.ThrownCrib2 == 2) {
|
|
log.Println("Error, already thrown")
|
|
|
|
return false
|
|
}
|
|
} else if g.Phase == PhasePeg && (g.Turn != player || g.PegPhase == PegPhaseFinal) {
|
|
log.Println("Error, not players turn")
|
|
|
|
return false
|
|
}
|
|
|
|
card, ok := Parse(cleanIdentifier(cardidentifier))
|
|
if !ok {
|
|
log.Println("Attempted to throw invalid card:", player, cardidentifier)
|
|
|
|
return false
|
|
}
|
|
|
|
playerHand := g.getHand(player)
|
|
opponentHand := g.getHand(g.getOpponent(player))
|
|
|
|
if !playerHand.Contains(card) {
|
|
log.Println("Attempted to throw card not in hand:", player, cardidentifier, playerHand)
|
|
|
|
return false
|
|
}
|
|
|
|
if g.Phase == PhasePeg {
|
|
if cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(card) > 31 {
|
|
g.getClient(player).write("Error: Illegal throw, would exceed 31")
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
*playerHand = (*playerHand).Remove(card)
|
|
|
|
if g.Phase == PhasePick {
|
|
if player == 1 {
|
|
g.ThrownCrib1++
|
|
} else {
|
|
g.ThrownCrib2++
|
|
}
|
|
|
|
g.Crib = append(g.Crib, card)
|
|
sort.Sort(g.Crib)
|
|
|
|
/*if len(g.Crib.Cards) == 4 && player == g.Dealer {
|
|
g.updateOpponent(player)
|
|
}*/
|
|
// TODO: Temporary before cut UI
|
|
if len(g.Crib) == 4 {
|
|
g.Cut(rand.Intn(32) + 4)
|
|
}
|
|
} else if g.Phase == PhasePeg {
|
|
g.ThrowPile = append(g.ThrowPile, PlayerCard{card, player})
|
|
g.Peg(player)
|
|
|
|
if cribbage.Sum(g.ThrowPile.Cards()) == 31 {
|
|
log.Println("=31 move to solo")
|
|
g.PegPhase = PegPhaseSolo
|
|
g.pegTurn(player)
|
|
} else if cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(opponentHand.Low()) > 31 {
|
|
if opponentHand.Len() > 0 && !g.getClient(g.getOpponent(player)).gamego {
|
|
log.Println("Setting Go - Player", g.getOpponent(player))
|
|
g.getClient(g.getOpponent(player)).gamego = true
|
|
g.getClient(g.getOpponent(player)).setStatus("Go")
|
|
g.getClient(g.getOpponent(player)).gameready = false
|
|
|
|
g.NextTurn()
|
|
} else if g.PeggingFinished() {
|
|
log.Println(">31 pegging finished move to solo")
|
|
g.PegPhase = PegPhaseSolo
|
|
g.pegTurn(player)
|
|
}
|
|
} else if opponentHand.Len() == 0 && playerHand.Len() == 0 {
|
|
log.Println("no cards left all move to solo")
|
|
g.PegPhase = PegPhaseSolo
|
|
g.pegTurn(player)
|
|
} else if opponentHand.Len() == 0 {
|
|
log.Println("no cards left opponent move to solo")
|
|
g.PegPhase = PegPhaseSolo
|
|
g.pegTurn(player)
|
|
} else {
|
|
g.NextTurn()
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (g *Game) PeggingFinished() bool {
|
|
return (g.getHand(1).Len() == 0 || cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(g.getHand(1).Low()) > 31) &&
|
|
(g.getHand(2).Len() == 0 || cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(g.getHand(2).Low()) > 31)
|
|
}
|
|
|
|
func (g *Game) ResetGo() {
|
|
if g.Phase > 0 {
|
|
g.logDebug("Reset go")
|
|
g.Player1.gamego = false
|
|
g.Player2.gamego = false
|
|
}
|
|
}
|
|
|
|
func (g *Game) ResetReady() {
|
|
if g.Phase > 0 {
|
|
g.Player1.gameready = true
|
|
g.Player1.setStatus("")
|
|
g.Player2.gameready = true
|
|
g.Player2.setStatus("")
|
|
}
|
|
}
|
|
|
|
func (g *Game) Ready(player int) {
|
|
g.getClient(player).gameready = true
|
|
g.getClient(player).setStatus("Waiting on opponent...")
|
|
}
|
|
|
|
func (g *Game) AllReady() bool {
|
|
return g.Player1.gameready && g.Player2.gameready
|
|
}
|
|
|
|
func (g *Game) NextTurn() {
|
|
g.logDebug("Turn...")
|
|
if g.Turn == 1 {
|
|
g.Turn = 2
|
|
} else {
|
|
g.Turn = 1
|
|
}
|
|
}
|
|
|
|
func (g *Game) NextHand() {
|
|
g.Turn = g.Dealer
|
|
g.Dealer = g.getOpponent(g.Dealer)
|
|
g.Reset()
|
|
g.Deal()
|
|
}
|
|
|
|
func (g *Game) getClient(player int) *Client {
|
|
if player == 1 {
|
|
return g.Player1
|
|
} else {
|
|
return g.Player2
|
|
}
|
|
}
|
|
|
|
func (g *Game) getOpponent(player int) int {
|
|
if player == 1 {
|
|
return 2
|
|
} else {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func (g *Game) getName(player int, clientplayer int) string {
|
|
if player == clientplayer {
|
|
return "You"
|
|
} else {
|
|
return "Your opponent"
|
|
}
|
|
}
|
|
|
|
func (g *Game) getScore(player int) int {
|
|
if player == 1 {
|
|
return g.Score1
|
|
} else {
|
|
return g.Score2
|
|
}
|
|
}
|
|
|
|
func (g *Game) getHand(player int) *Cards {
|
|
if player == 1 {
|
|
return &g.Hand1
|
|
} else {
|
|
return &g.Hand2
|
|
}
|
|
}
|
|
|
|
func (g *Game) update(player int) {
|
|
g.sendGameState(player)
|
|
}
|
|
|
|
func (g *Game) updateOpponent(player int) {
|
|
if player == 1 {
|
|
g.update(2)
|
|
} else {
|
|
g.update(1)
|
|
}
|
|
}
|
|
|
|
func (g *Game) updateAll() {
|
|
g.update(1)
|
|
g.update(2)
|
|
}
|
|
|
|
func (g *Game) printScoring() {
|
|
for player := 1; player <= 2; player++ {
|
|
var gameprinted []string
|
|
|
|
opponentscore, opponentscoreresults := cribbage.Score(cribbage.ShowHand, *g.getHand(g.getOpponent(player)), g.Starter)
|
|
playerscore, dealerscoreresults := cribbage.Score(cribbage.ShowHand, *g.getHand(player), g.Starter)
|
|
cribscore, cribscoreresults := cribbage.Score(cribbage.ShowCrib, g.Crib, g.Starter)
|
|
|
|
if g.Dealer == player {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your hands scored %d (%d hand, %d crib) - Your opponent's hand scored %d", playerscore+cribscore, playerscore, cribscore, opponentscore))
|
|
} else {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your hand scored %d - Your opponent's hands scored %d (%d hand, %d crib)", playerscore, opponentscore+cribscore, opponentscore, cribscore))
|
|
}
|
|
|
|
gameprinted = append(gameprinted, "Starter "+g.Starter.String())
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Player (%2d) %s", opponentscore, g.getHand(player)))
|
|
|
|
if g.Dealer == player {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Player c. (%2d) %s", cribscore, g.Crib))
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Opponent (%2d) %s", opponentscore, g.getHand(g.getOpponent(player))))
|
|
|
|
for _, result := range dealerscoreresults {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your hand scored %s", result))
|
|
}
|
|
for _, result := range cribscoreresults {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your crib scored %s", result))
|
|
}
|
|
for _, result := range opponentscoreresults {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your opponent's hand scored %s", result))
|
|
}
|
|
} else {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Opponent c. (%2d) %s", cribscore, g.Crib))
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Opponent (%2d) %s", opponentscore, g.getHand(g.getOpponent(player))))
|
|
|
|
for _, result := range opponentscoreresults {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your hand scored %s", result))
|
|
}
|
|
for _, result := range dealerscoreresults {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your opponent's hand scored %s", result))
|
|
}
|
|
for _, result := range cribscoreresults {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Your opponent's crib scored %s", result))
|
|
}
|
|
}
|
|
|
|
for _, msg := range gameprinted {
|
|
g.getClient(player).write(msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *Game) printGame(player int) string {
|
|
lightprint := false
|
|
gameprinted := []string{}
|
|
|
|
if g.Phase == PhasePeg {
|
|
gameprinted = append(gameprinted, "Pegging: "+g.ThrowPile.String()+" ("+strconv.Itoa(cribbage.Sum(g.ThrowPile.Cards()))+")")
|
|
|
|
if g.Turn == player {
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Please throw a card. (Enter 1-%d)", g.getHand(player).Len()))
|
|
} else if g.getHand(player).Len() > 0 && cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(g.getHand(player).Low()) > 31 {
|
|
lightprint = true
|
|
gameprinted = append(gameprinted, "Go - Waiting on opponent...")
|
|
} else {
|
|
lightprint = true
|
|
gameprinted = append(gameprinted, "Waiting on opponent...")
|
|
}
|
|
}
|
|
|
|
gameprintedstr := ""
|
|
if g.Phase == PhasePick {
|
|
cardsthrown := 0
|
|
if player == 1 {
|
|
cardsthrown = g.ThrownCrib1
|
|
} else {
|
|
cardsthrown = g.ThrownCrib2
|
|
}
|
|
|
|
if cardsthrown < 2 {
|
|
if cardsthrown == 0 {
|
|
gameprintedstr += "Please throw two cards for "
|
|
} else {
|
|
gameprintedstr += "Please throw one additional card for "
|
|
}
|
|
if g.Dealer == player {
|
|
gameprintedstr += "your"
|
|
} else {
|
|
gameprintedstr += "your opponent's"
|
|
}
|
|
gameprintedstr += fmt.Sprintf(" crib. (Enter 1-%d, throw multiple cards by separating card#'s with a space)", g.getHand(player).Len())
|
|
} else if len(g.Crib) < 4 {
|
|
lightprint = true
|
|
gameprintedstr += "Waiting on opponent..."
|
|
} else if g.Starter.Value() == 0 {
|
|
lightprint = true
|
|
if g.Dealer != player {
|
|
gameprintedstr += "Please cut the deck. (Enter 4-36 or c/cut for a random cut)"
|
|
} else {
|
|
gameprintedstr += "Waiting on opponent..."
|
|
}
|
|
}
|
|
gameprinted = append(gameprinted, gameprintedstr)
|
|
gameprintedstr = ""
|
|
}
|
|
|
|
if !lightprint {
|
|
if len(gameprinted) > 0 {
|
|
gameprinted = append(gameprinted, "")
|
|
}
|
|
gameprinted = append(gameprinted, fmt.Sprintf("Score: (%s) %d / %d (%s)", g.getName(1, player), g.getScore(1), g.getScore(2), g.getName(2, player)))
|
|
gameprinted = append(gameprinted, "Your cards: "+g.getHand(player).String())
|
|
}
|
|
|
|
return strings.Join(gameprinted, "\n")
|
|
}
|
|
|
|
func (g *Game) printAll() {
|
|
log.Println("Printing game state...")
|
|
|
|
var phaseprinted string
|
|
switch g.Phase {
|
|
case PhaseSetup:
|
|
phaseprinted = "Setup"
|
|
break
|
|
case PhasePick:
|
|
phaseprinted = "Pick"
|
|
break
|
|
case PhasePeg:
|
|
phaseprinted = "Peg"
|
|
|
|
switch g.Phase {
|
|
case PegPhaseNormal:
|
|
phaseprinted += " Normal"
|
|
break
|
|
case PegPhaseSolo:
|
|
phaseprinted += " Solo"
|
|
break
|
|
case PegPhaseFinal:
|
|
phaseprinted += " Final"
|
|
break
|
|
}
|
|
|
|
break
|
|
case PhaseScore:
|
|
phaseprinted = "Score"
|
|
break
|
|
case PhaseEnd:
|
|
phaseprinted = "End"
|
|
}
|
|
log.Printf("[%s] Dealer: %d - Turn: %d", phaseprinted, g.Dealer, g.Turn)
|
|
if g.Phase > 0 {
|
|
log.Printf("Player 1 ready: %t - Player 2 ready: %t", g.Player1.gameready, g.Player2.gameready)
|
|
}
|
|
|
|
var starter Card
|
|
if g.Starter.Value() > 0 {
|
|
starter = g.Starter
|
|
}
|
|
|
|
hand1Score, hand1ScoreResults := cribbage.Score(cribbage.ShowHand, g.Hand1, starter)
|
|
log.Println("Hand 1 [", hand1Score, "]", g.Hand1.String(), hand1ScoreResults)
|
|
|
|
cribScore, cribScoreResults := cribbage.Score(cribbage.ShowCrib, g.Crib, starter)
|
|
log.Println("Crib [", cribScore, "]", g.Crib.String(), cribScoreResults)
|
|
|
|
hand2Score, hand2ScoreResults := cribbage.Score(cribbage.ShowHand, g.Hand2, starter)
|
|
log.Println("Hand 2 [", hand2Score, "]", g.Hand2.String(), hand2ScoreResults)
|
|
|
|
if g.Starter.Value() > 0 {
|
|
log.Println("Starter:", g.Starter.String())
|
|
}
|
|
|
|
if g.ThrowPile.Len() > 0 {
|
|
pegScore, pegResults := cribbage.Score(cribbage.Peg, g.ThrowPile.Cards(), starter)
|
|
log.Println("Pegging: [", pegScore, "]", g.ThrowPile.Cards().String(), pegResults)
|
|
}
|
|
|
|
if g.DiscardPile.Len() > 0 {
|
|
log.Println("Discard:", g.DiscardPile.String())
|
|
}
|
|
}
|
|
|
|
func (g *Game) sendGameState(player int) {
|
|
client := g.getClient(player)
|
|
if client.ConnType == ClientWebsocket {
|
|
gamestate, err := g.exportJSON(player)
|
|
if err != nil {
|
|
log.Fatal("failed to marshal game state:", err)
|
|
}
|
|
client.write(string(gamestate))
|
|
} else {
|
|
client.write(g.printGame(player))
|
|
}
|
|
}
|
|
|
|
func (g *Game) pegTurn(player int) {
|
|
if g.Phase != PhasePeg {
|
|
return
|
|
}
|
|
|
|
g.logDebug("PEG CONTINUE")
|
|
if cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(g.getHand(player).Low()) > 31 && g.PegPhase == PegPhaseNormal {
|
|
g.logDebug("PEG NEXT TURN")
|
|
|
|
if g.PeggingFinished() {
|
|
g.ResetGo()
|
|
|
|
g.PegPhase = PegPhaseSolo
|
|
g.getClient(g.getOpponent(player)).gameready = false
|
|
g.getClient(g.getOpponent(player)).setStatus("Go - Pegging finished")
|
|
}
|
|
|
|
g.NextTurn()
|
|
} else if g.PeggingFinished() {
|
|
if g.PegPhase == PegPhaseSolo {
|
|
g.logDebug("ENTERING FINAL PEG")
|
|
g.PegPhase = PegPhaseFinal
|
|
|
|
finalpeg, _ := cribbage.Score(cribbage.Peg, g.ThrowPile.Cards(), g.Starter)
|
|
if cribbage.Sum(g.ThrowPile.Cards()) != 31 {
|
|
finalpeg++
|
|
g.award(g.Turn, 1, cribbage.Peg, "Last card")
|
|
}
|
|
|
|
g.getClient(g.Turn).gameready = false
|
|
g.getClient(g.Turn).setStatus("You pegged " + strconv.Itoa(finalpeg) + " point")
|
|
if finalpeg != 1 {
|
|
g.getClient(g.Turn).gamestatus += "s"
|
|
}
|
|
g.getClient(g.getOpponent(g.Turn)).gameready = false
|
|
g.getClient(g.getOpponent(g.Turn)).setStatus("Your opponent pegged " + strconv.Itoa(finalpeg) + " point")
|
|
if finalpeg != 1 {
|
|
g.getClient(g.getOpponent(g.Turn)).gamestatus += "s"
|
|
}
|
|
g.NextTurn()
|
|
} else if g.PegPhase == PegPhaseFinal {
|
|
g.logDebug("FINAL PEG")
|
|
g.PegPhase = PegPhaseNormal
|
|
|
|
if g.Hand1.Len() == 0 && g.Hand2.Len() == 0 {
|
|
if cribbage.Sum(g.ThrowPile.Cards()) != 31 {
|
|
g.award(g.Turn, 1, cribbage.Peg, "Last card")
|
|
}
|
|
|
|
g.scoreHands()
|
|
g.printScoring()
|
|
|
|
g.NextTurn()
|
|
} else {
|
|
g.DiscardPile = append(g.DiscardPile, g.ThrowPile...)
|
|
g.ThrowPile = PlayerCards{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *Game) processGameCommand(command GameCommand) {
|
|
g.Lock()
|
|
defer g.Unlock()
|
|
|
|
if command.Command == CommandEnd {
|
|
g.Phase = PhaseEnd
|
|
g.updateAll()
|
|
return
|
|
}
|
|
|
|
command_args := strings.Fields(command.Value)
|
|
|
|
if command.Command == CommandRaw {
|
|
if g.Phase == PhasePick && g.Crib.Len() == 4 && g.Starter.Value() == 0 {
|
|
command.Command = CommandCut
|
|
} else {
|
|
command.Command = CommandThrow
|
|
}
|
|
}
|
|
|
|
if g.Phase == PhasePick && command.Command == CommandCut {
|
|
if len(command_args) > 0 {
|
|
cut, err := strconv.Atoi(command_args[0])
|
|
if err == nil && cut >= 4 && cut <= 36 {
|
|
g.Cut(cut - 1)
|
|
}
|
|
}
|
|
|
|
g.Cut(rand.Intn(32) + 4)
|
|
} else if command.Command == CommandContinue {
|
|
if g.getClient(command.Player).gameready {
|
|
return
|
|
}
|
|
|
|
g.Ready(command.Player)
|
|
if g.AllReady() {
|
|
g.Player1.setStatus("")
|
|
g.Player2.setStatus("")
|
|
|
|
g.logDebug("ALL READY")
|
|
if g.Phase == PhasePeg {
|
|
g.pegTurn(command.Player)
|
|
} else if g.Phase == PhaseScore {
|
|
g.NextHand()
|
|
g.update(g.getOpponent(command.Player))
|
|
}
|
|
}
|
|
} else if command.Command == CommandThrow {
|
|
if g.Phase == PhasePick || g.Phase == PhasePeg {
|
|
cards := []string{}
|
|
|
|
lastindex := 0
|
|
for i, cardarg := range command_args {
|
|
cardindex, err := strconv.Atoi(cardarg)
|
|
if err == nil {
|
|
if cardindex == lastindex {
|
|
break
|
|
}
|
|
|
|
if cardindex >= 1 && g.getHand(command.Player).Len() >= cardindex {
|
|
cards = append(cards, (*g.getHand(command.Player))[(cardindex-1)].Identifier())
|
|
lastindex = cardindex
|
|
}
|
|
} else {
|
|
cards = append(cards, cardarg)
|
|
}
|
|
|
|
if i > 1 {
|
|
break
|
|
}
|
|
}
|
|
|
|
for _, card := range cards {
|
|
g.Throw(command.Player, card)
|
|
}
|
|
}
|
|
} else if command.Command == CommandMessage {
|
|
data, err := json.Marshal(command)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
g.getClient(g.getOpponent(command.Player)).write(string(data))
|
|
}
|
|
|
|
g.updateAll()
|
|
}
|
|
|
|
func (g *Game) playGame() {
|
|
log.Println("Starting new game")
|
|
g.updateAll()
|
|
|
|
for command := range g.CommandQueue {
|
|
g.logDebug("Received GameCommand", command.ToStr())
|
|
g.processGameCommand(command)
|
|
|
|
if g.Phase == PhaseEnd {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (g *Game) logDebug(args ...interface{}) {
|
|
log.Println("phase:", g.Phase,
|
|
"turn:", g.Turn,
|
|
"dealer:", g.Dealer, args)
|
|
|
|
}
|
|
|
|
func cleanIdentifier(cardidentifier string) string {
|
|
return strings.ToLower(strings.Trim(cardidentifier, trimNewlinesAndSpace))
|
|
}
|