commit
08e4d196bf
12 changed files with 1361 additions and 0 deletions
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
.idea/ |
||||
dist/ |
||||
vendor/ |
||||
*.sh |
||||
jack |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
image: golang:latest |
||||
|
||||
stages: |
||||
- validate |
||||
- build |
||||
|
||||
fmt: |
||||
stage: validate |
||||
script: |
||||
- gofmt -l -s -e . |
||||
- exit $(gofmt -l -s -e . | wc -l) |
||||
|
||||
vet: |
||||
stage: validate |
||||
script: |
||||
- go vet -composites=false ./... |
||||
|
||||
test: |
||||
stage: validate |
||||
script: |
||||
- go test -race -v ./... |
||||
|
||||
build: |
||||
stage: build |
||||
script: |
||||
- go build |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
MIT License |
||||
|
||||
Copyright (c) 2020 Trevor Slocum <trevor@rocketnine.space> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
# jack |
||||
[](https://gitlab.com/tslocum/jack/commits/master) |
||||
[](https://liberapay.com/rocketnine.space) |
||||
|
||||
Cribbage server |
||||
|
||||
## Demo |
||||
|
||||
[Cribbage.World](https://cribbage.world) is powered by jack. |
||||
|
||||
## Support |
||||
|
||||
Please share issues/suggestions [here](https://gitlab.com/tslocum/jack/issues). |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
|
||||
"gitlab.com/tslocum/joker" |
||||
) |
||||
|
||||
type PlayerCard struct { |
||||
joker.Card |
||||
Player int |
||||
} |
||||
|
||||
func (c PlayerCard) String() string { |
||||
return fmt.Sprintf("{%d}%s", c.Player, c.Card) |
||||
} |
||||
|
||||
type PlayerCards []PlayerCard |
||||
|
||||
func (c PlayerCards) String() string { |
||||
var s strings.Builder |
||||
for i := range c { |
||||
if i > 0 { |
||||
s.WriteRune(',') |
||||
} |
||||
s.WriteString(c[i].String()) |
||||
} |
||||
return s.String() |
||||
} |
||||
|
||||
func (c PlayerCards) Len() int { |
||||
return len(c) |
||||
} |
||||
|
||||
func (c PlayerCards) Less(i, j int) bool { |
||||
return c[i].Value() < c[j].Value() |
||||
} |
||||
|
||||
func (c PlayerCards) Swap(i, j int) { |
||||
c[i], c[j] = c[j], c[i] |
||||
} |
||||
|
||||
func (c PlayerCards) Cards() joker.Cards { |
||||
var cards = make(joker.Cards, len(c)) |
||||
for i, card := range c { |
||||
cards[i] = card.Card |
||||
} |
||||
return cards |
||||
} |
||||
|
||||
func (c PlayerCards) PlayerCards(player int) int { |
||||
var i int |
||||
for _, card := range c { |
||||
if card.Player == player { |
||||
i++ |
||||
} |
||||
} |
||||
return i |
||||
} |
@ -0,0 +1,190 @@
@@ -0,0 +1,190 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"log" |
||||
"net" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
"github.com/gorilla/websocket" |
||||
"gitlab.com/tslocum/joker" |
||||
) |
||||
|
||||
const ClientTerminating = -1 |
||||
const ClientTelnet = 1 |
||||
const ClientWebsocket = 2 |
||||
|
||||
type Client struct { |
||||
ConnType int |
||||
ConnTelnet net.Conn |
||||
ConnWebsocket *websocket.Conn |
||||
game *Game |
||||
gameplayer int |
||||
gameready bool |
||||
gamewaiting string |
||||
gamego bool |
||||
readbuffer chan string |
||||
writebuffer chan string |
||||
} |
||||
|
||||
func (c *Client) initialize() { |
||||
c.readbuffer = make(chan string, 10) |
||||
c.writebuffer = make(chan string, 10) |
||||
} |
||||
|
||||
func (c *Client) processRead() { |
||||
var command_pieces []string |
||||
var handled bool |
||||
for command := range c.readbuffer { |
||||
if command == "" || c.game == nil { |
||||
break |
||||
} else if c.ConnType == ClientTerminating { |
||||
continue |
||||
} |
||||
handled = false |
||||
|
||||
command = strings.TrimLeft(command, " \r\n"+string(0)) |
||||
command = strings.TrimRight(command, " \r\n"+string(0)) |
||||
command_pieces = strings.Fields(strings.ToLower(command)) |
||||
if len(command_pieces) > 0 { |
||||
handled = true |
||||
|
||||
args := "" |
||||
if len(command_pieces) > 1 { |
||||
for i := 1; i < len(command_pieces); i++ { |
||||
command_piece := command_pieces[i] |
||||
command_piece = strings.TrimLeft(command_piece, " \r\n"+string(0)) |
||||
command_piece = strings.TrimRight(command_piece, " \r\n"+string(0)) |
||||
|
||||
if command_piece != "" { |
||||
if args != "" { |
||||
args += " " |
||||
} |
||||
args += command_piece |
||||
} |
||||
} |
||||
} |
||||
|
||||
card, sentCardIdentifier := joker.Parse(command_pieces[0]) |
||||
|
||||
if _, err := strconv.Atoi(command_pieces[0]); err == nil { |
||||
c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandRaw, Value: command_pieces[0] + " " + args} |
||||
} else if sentCardIdentifier && c.game.getHand(c.gameplayer).Contains(card) { |
||||
c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandRaw, Value: command_pieces[0] + " " + args} |
||||
} else if command_pieces[0] == "continue" { |
||||
c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandContinue, Value: args} |
||||
} else if command_pieces[0] == "cut" || command_pieces[0] == "c" || command_pieces[0] == "cu" { |
||||
c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandCut, Value: args} |
||||
} else if command_pieces[0] == "throw" || command_pieces[0] == "t" || command_pieces[0] == "th" { |
||||
c.game.CommandQueue <- GameCommand{Player: c.gameplayer, Command: CommandThrow, Value: args} |
||||
} else if command_pieces[0] == "print" && c.game != nil { |
||||
c.game.printAll() |
||||
} else { |
||||
handled = false |
||||
} |
||||
} |
||||
|
||||
if !handled { |
||||
c.write(c.game.printGame(c.gameplayer)) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (c *Client) read(message string) { |
||||
if c.ConnType == ClientTerminating { |
||||
return |
||||
} |
||||
|
||||
c.readbuffer <- message |
||||
} |
||||
|
||||
func (c *Client) handleRead() { |
||||
if c.ConnType == ClientWebsocket { |
||||
for { |
||||
_, message, err := c.ConnWebsocket.ReadMessage() |
||||
if err != nil { |
||||
log.Println("WebSocket read error:", err) |
||||
break |
||||
} |
||||
|
||||
log.Println("WebSocket read:", string(message)) |
||||
c.readbuffer <- string(message) |
||||
} |
||||
} else { |
||||
buf := make([]byte, 4096) |
||||
var readtext string |
||||
var nextline int |
||||
for { |
||||
n, err := c.ConnTelnet.Read(buf) |
||||
if err != nil || n == 0 { |
||||
c.ConnTelnet.Close() |
||||
|
||||
break |
||||
} |
||||
readtext += strings.TrimLeft(string(buf), "\r\n"+string(0)) |
||||
for { |
||||
readtext = strings.TrimLeft(readtext, "\r\n"+string(0)) |
||||
nextline = strings.Index(readtext, "\n") |
||||
if nextline <= 0 { |
||||
if readtext != "" { |
||||
c.read(readtext) |
||||
readtext = "" |
||||
} |
||||
|
||||
break |
||||
} |
||||
c.read(readtext[:nextline+1]) |
||||
readtext = readtext[nextline+1:] |
||||
} |
||||
buf = make([]byte, 4096) |
||||
} |
||||
log.Printf("Telnet connection closed") |
||||
} |
||||
|
||||
c.terminate() |
||||
|
||||
if c.game != nil && c.game.Phase != PhaseEnd { |
||||
c.game.end(c.game.getOpponent(c.gameplayer), "Player disconnected") |
||||
} |
||||
} |
||||
|
||||
func (c *Client) terminate() { |
||||
c.write("") |
||||
c.ConnType = ClientTerminating |
||||
c.readbuffer <- "" |
||||
} |
||||
|
||||
func (c *Client) write(message string) { |
||||
if c.ConnType == ClientTerminating { |
||||
return |
||||
} |
||||
|
||||
c.writebuffer <- message |
||||
} |
||||
|
||||
func (c *Client) handleWrite() { |
||||
for message := range c.writebuffer { |
||||
if message == "" { |
||||
break |
||||
} else if c.ConnType == ClientTerminating { |
||||
continue |
||||
} |
||||
|
||||
if c.ConnType == ClientWebsocket { |
||||
err := c.ConnWebsocket.WriteMessage(websocket.TextMessage, []byte(message)) |
||||
if err != nil { |
||||
log.Printf("WebSocket connection closed: %v", err) |
||||
|
||||
break |
||||
} |
||||
} else { |
||||
_, err := c.ConnTelnet.Write([]byte(message + "\n\n")) |
||||
if err != nil { |
||||
c.ConnTelnet.Close() |
||||
log.Printf("Telnet connection closed: %v", err) |
||||
|
||||
break |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,867 @@
@@ -0,0 +1,867 @@
|
||||
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 |
||||
|
||||
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 PlayerCards `json:"crib"` |
||||
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.Deck = joker.NewDeck(joker.StandardCards, 0) |
||||
|
||||
g.Dealer = rand.Intn(2) + 1 |
||||
if g.Dealer == 1 { |
||||
g.Turn = 2 |
||||
} else { |
||||
g.Turn = 1 |
||||
} |
||||
|
||||
g.Reset() |
||||
} |
||||
|
||||
func (g *Game) Reset() { |
||||
g.Deck.Shuffle() |
||||
g.Hand1 = Cards{} |
||||
g.Hand2 = Cards{} |
||||
g.Crib = PlayerCards{} |
||||
g.Starter = Card{} |
||||
g.ThrowPile = PlayerCards{} |
||||
g.DiscardPile = PlayerCards{} |
||||
|
||||
// TODO SHUFFLE_MIN + rand.Intn(SHUFFLE_MAX-SHUFFLE_MIN)
|
||||
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) |
||||
} |
||||
} |
||||
|
||||
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().(PlayerCards) |
||||
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).gamewaiting) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
gamestate["waiting"] = 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.Cards(), 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-%d]", 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).gamewaiting = "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 g.Crib.PlayerCards(player) == 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 { |
||||
g.Crib = append(g.Crib, PlayerCard{card, player}) |
||||
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)).gamewaiting = "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 { |
||||
g.NextTurn() |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
|
||||
func (g *Game) PeggingFinished() bool { |
||||
return cribbage.Sum(g.ThrowPile.Cards())+cribbage.Value(g.getHand(1).Low()) > 31 && 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.gamewaiting = "" |
||||
g.Player2.gameready = true |
||||
g.Player2.gamewaiting = "" |
||||
} |
||||
} |
||||
|
||||
func (g *Game) Ready(player int) { |
||||
g.getClient(player).gameready = true |
||||
g.getClient(player).gamewaiting = "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++ { |
||||
gameprinted := []string{} |
||||
|
||||
opponentscore, _ := cribbage.Score(cribbage.ShowHand, *g.getHand(g.getOpponent(g.Dealer)), g.Starter) |
||||
dealerscore, _ := cribbage.Score(cribbage.ShowHand, *g.getHand(g.Dealer), g.Starter) |
||||
dealercribscore, _ := cribbage.Score(cribbage.ShowCrib, g.Crib.Cards(), g.Starter) |
||||
|
||||
dealerscoreprinted := fmt.Sprintf("%d [%d-%d]", dealerscore+dealercribscore, dealerscore, dealercribscore) |
||||
opponentscoreprinted := fmt.Sprintf("%d", opponentscore) |
||||
|
||||
var yourscore string |
||||
var theirscore string |
||||
if g.Dealer == player { |
||||
yourscore = dealerscoreprinted |
||||
theirscore = opponentscoreprinted |
||||
} else { |
||||
yourscore = opponentscoreprinted |
||||
theirscore = dealerscoreprinted |
||||
} |
||||
gameprinted = append(gameprinted, "Your hand(s) scored "+yourscore+" - Your opponent's hand(s) scored "+theirscore) |
||||
|
||||
gameprinted = append(gameprinted, "Starter "+g.Starter.String()) |
||||
gameprinted = append(gameprinted, fmt.Sprintf("Opponent (%2d) %s", opponentscore, g.getHand(g.getOpponent(g.Dealer)))) |
||||
gameprinted = append(gameprinted, fmt.Sprintf("Dealer (%2d) %s", dealerscore, g.getHand(g.Dealer))) |
||||
gameprinted = append(gameprinted, fmt.Sprintf("Dealer c (%2d) %s", dealercribscore, g.Crib)) |
||||
|
||||
g.getClient(player).write(strings.Join(gameprinted, "\n")) |
||||
} |
||||
} |
||||
|
||||
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 |
||||
for _, card := range g.Crib { |
||||
if card.Player == player { |
||||
cardsthrown++ |
||||
} |
||||
} |
||||
|
||||
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.Cards(), starter) |
||||
log.Println("Crib [", cribScore, "]", g.Crib.Cards().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)).gamewaiting = "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).gamewaiting = "You pegged " + strconv.Itoa(finalpeg) + " point" |
||||
if finalpeg != 1 { |
||||
g.getClient(g.Turn).gamewaiting += "s" |
||||
} |
||||
g.getClient(g.getOpponent(g.Turn)).gameready = false |
||||
g.getClient(g.getOpponent(g.Turn)).gamewaiting = "Your opponent pegged " + strconv.Itoa(finalpeg) + " point" |
||||
if finalpeg != 1 { |
||||
g.getClient(g.getOpponent(g.Turn)).gamewaiting += "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.gamewaiting = "" |
||||
g.Player2.gamewaiting = "" |
||||
|
||||
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) |
||||
} |
||||
} |
||||
} |
||||
|
||||
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.TrimLeft(strings.TrimRight(cardidentifier, " \r\n"+string(0)), " \r\n"+string(0))) |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
package main |
||||
|
||||
import "fmt" |
||||
|
||||
const CommandEnd = -1 |
||||
const CommandRaw = 0 |
||||
const CommandContinue = 1 |
||||
const CommandThrow = 2 |
||||
const CommandCut = 3 |
||||
|
||||
type GameCommand struct { |
||||
Player int |
||||
Command int |
||||
Value string |
||||
} |
||||
|
||||
func (gc GameCommand) ToStr() string { |
||||
var commandprinted string |
||||
switch gc.Command { |
||||
case CommandEnd: |
||||
commandprinted = "End" |
||||
break |
||||
case CommandRaw: |
||||
commandprinted = "Raw" |
||||
case CommandContinue: |
||||
commandprinted = "Continue" |
||||
break |
||||
case CommandCut: |
||||
commandprinted = "Cut" |
||||
break |
||||
case CommandThrow: |
||||
commandprinted = "Throw" |
||||
} |
||||
return fmt.Sprintf("Player %d - %s - %s", gc.Player, commandprinted, gc.Value) |
||||
} |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
module gitlab.com/tslocum/jack |
||||
|
||||
go 1.14 |
||||
|
||||
require ( |
||||
github.com/gorilla/websocket v1.4.2 |
||||
gitlab.com/tslocum/joker v0.1.3-0.20200529165915-df7bd71a3bcd |
||||
gitlab.com/tslocum/joker-cribbage v0.1.2-0.20200206160759-4543b916d838 |
||||
) |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= |
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |
||||
gitlab.com/tslocum/joker v0.1.2 h1:7ujvgkGNUJbrvpXvNHSvKWFDYIKTCWbvFcWL1IbRVWA= |
||||
gitlab.com/tslocum/joker v0.1.2/go.mod h1:bxTQ0FFmBP465r9z76zcm97S4Ld9eCLa3q20TyVM82A= |
||||
gitlab.com/tslocum/joker v0.1.3-0.20200529165915-df7bd71a3bcd h1:nDmVh1g7yCbffyAozEPQsCRHc1MQ30P+u5FQQlZwa8A= |
||||
gitlab.com/tslocum/joker v0.1.3-0.20200529165915-df7bd71a3bcd/go.mod h1:bxTQ0FFmBP465r9z76zcm97S4Ld9eCLa3q20TyVM82A= |
||||
gitlab.com/tslocum/joker-cribbage v0.1.2-0.20200206160759-4543b916d838 h1:Z3Sv+ArlCL2NezHmv6U6HA2GOY40Ed8+eCu4WrIZvdk= |
||||
gitlab.com/tslocum/joker-cribbage v0.1.2-0.20200206160759-4543b916d838/go.mod h1:/fD4xxguXPg+N9s5+Q6t6tib1xfvh7zZ6AQ6TZXViDA= |
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"log" |
||||
"math/rand" |
||||
"net/http" |
||||
"time" |
||||
) |
||||
|
||||
func main() { |
||||
rand.Seed(time.Now().UTC().UnixNano()) |
||||
|
||||
go func() { |
||||
log.Fatal(http.ListenAndServe(":8880", nil)) |
||||
}() |
||||
|
||||
log.Println("CribServer initialized") |
||||
|
||||
cs := CribServer{} |
||||
cs.clientqueuealert = make(chan bool) |
||||
|
||||
go cs.matchPlayers() |
||||
go cs.listenWebSocket() |
||||
|
||||
cs.listenTelnet() |
||||
|
||||
// TODO
|
||||
select {} |
||||
} |
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"log" |
||||
"net" |
||||
"net/http" |
||||
"sync" |
||||
|
||||
"github.com/gorilla/websocket" |
||||
) |
||||
|
||||
var websocketUpgrader = websocket.Upgrader{ |
||||
ReadBufferSize: 1024, |
||||
WriteBufferSize: 1024, |
||||
CheckOrigin: func(r *http.Request) bool { |
||||
return true |
||||
}, |
||||
} |
||||
|
||||
type CribServer struct { |
||||
clients []*Client |
||||
clientqueue []*Client |
||||
clientqueuealert chan bool |
||||
sync.RWMutex |
||||
} |
||||
|
||||
func (cs *CribServer) handleReadWebSocket(w http.ResponseWriter, r *http.Request) { |
||||
log.Println("New WebSocket") |
||||
|
||||
c, err := websocketUpgrader.Upgrade(w, r, nil) |
||||
if err != nil { |
||||
log.Println("WebSocket upgrade error:", err) |
||||
return |
||||
} |
||||
defer c.Close() |
||||
|
||||
client := &Client{ConnType: ClientWebsocket, ConnWebsocket: c} |
||||
client.initialize() |
||||
cs.addClient(client) |
||||
} |
||||
|
||||
func (cs *CribServer) listenWebSocket() { |
||||
http.HandleFunc("/crib", cs.handleReadWebSocket) |
||||
http.ListenAndServe(":8884", nil) |
||||
} |
||||
|
||||
func (cs *CribServer) addClient(client *Client) { |
||||
cs.Lock() |
||||
cs.clients = append(cs.clients, client) |
||||
cs.Unlock() |
||||
|
||||
go client.processRead() |
||||
go client.handleWrite() |
||||
cs.queueClient(client) |
||||
client.handleRead() |
||||
} |
||||
|
||||
func (cs *CribServer) listenTelnet() { |
||||
ln, err := net.Listen("tcp", ":8888") |
||||
if err != nil { |
||||
log.Println(err) |
||||
return |
||||
} |
||||
|
||||
for { |
||||
conn, err := ln.Accept() |
||||
if err != nil { |
||||
log.Println(err) |
||||
continue |
||||
} |
||||
client := &Client{ConnType: ClientTelnet, ConnTelnet: conn} |
||||
client.initialize() |
||||
go cs.addClient(client) |
||||
} |
||||
} |
||||
|
||||
func (cs *CribServer) queueClient(client *Client) { |
||||
cs.Lock() |
||||
cs.clientqueue = append(cs.clientqueue, client) |
||||
cs.Unlock() |
||||
cs.clientqueuealert <- true |
||||
} |
||||
|
||||
func (cs *CribServer) matchPlayers() { |
||||
for range cs.clientqueuealert { |
||||
cs.Lock() |
||||
if len(cs.clientqueue) > 1 { |
||||
go cs.startGame(cs.clientqueue[0], cs.clientqueue[1]) |
||||
cs.clientqueue = cs.clientqueue[2:] |
||||
} |
||||
cs.Unlock() |
||||
} |
||||
} |
||||
|
||||
func (cs *CribServer) startGame(player1, player2 *Client) { |
||||
game := NewGame(player1, player2) |
||||
game.playGame() |
||||
} |
Loading…
Reference in new issue