You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
919 lines
23 KiB
919 lines
23 KiB
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)) |
|
}
|
|
|