From eb7b405641a0ab8046030f3cd0b99d0eccac063b Mon Sep 17 00:00:00 2001 From: Steve Date: Fri, 23 Jul 2021 16:43:39 -0400 Subject: [PATCH] cards are now passed as PortableCard objects client supports all actions A bunch of stuff uses *Decks now instead of []Card since json marshaling all of this code is absolutely awful --- Makefile | 2 +- cmd/client/main.go | 53 +++++++++++++++++++++++++++++++-- internal/coordinator/server.go | 4 +++ internal/coordinator/session.go | 7 +++-- internal/game/board.go | 37 ++++++++++++++++++----- internal/game/card.go | 42 +++++++++++++++++++++----- internal/game/cmd.go | 16 ++++++++-- internal/game/deck.go | 33 ++++++++++++++++++-- internal/game/game.go | 46 +++++++++++++++------------- internal/game/player.go | 49 ++++++++++++++++++++++++++++++ 10 files changed, 243 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index c0adae5..bcda505 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ all: client server engine client: $(GAMEFILES) $(wildcard cmd/client/*.go) go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/client -server: $(GAMEFILES) $(wildcard cmd/server/*.go) +server: $(GAMEFILES) $(COORDFILEs) $(wildcard cmd/server/*.go) go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/server engine: $(GAMEFILES) go build -ldflags "-X main.GitCommit=$(GIT_COMMIT)" ./cmd/engine diff --git a/cmd/client/main.go b/cmd/client/main.go index b8c0cb3..53d1be1 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -1,20 +1,24 @@ package main import ( + "bufio" "fmt" "log" "os" "os/signal" "strings" "time" + "unicode/utf8" "git.saintnet.tech/stryan/snengame/internal/coordinator" + "git.saintnet.tech/stryan/snengame/internal/game" "github.com/google/uuid" "github.com/gorilla/websocket" ) var done chan interface{} var interrupt chan os.Signal +var pid int func receiveHandler(connection *websocket.Conn) { defer close(done) @@ -23,9 +27,23 @@ func receiveHandler(connection *websocket.Conn) { err := connection.ReadJSON(&resp) if err != nil { log.Println("Error in receive:", err) - return } - log.Printf("Received: %s\n", resp) + if resp.Result == coordinator.SessionRespJoined1 { + pid = game.SentinalID + } else if resp.Result == coordinator.SessionRespJoined2 { + pid = game.ScourgeID + } else if resp.Result == coordinator.SessionRespPlayed { + if resp.GameResult != nil { + switch resp.GameResult.ResultType { + case game.ActCmd: + fmt.Println(resp.GameResult.ActionResult) + case game.StateCmd: + fmt.Println(resp.GameResult.StateResult) + case game.DebugCmd: + fmt.Println(resp.GameResult.DebugResult) + } + } + } } } @@ -97,11 +115,12 @@ func GetCommand(uid uuid.UUID, resp chan coordinator.SessionCommand) { if t == -1 { panic("quitting") } - _, err = fmt.Scanf("%s", &cmd) + cmd, err = bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { log.Println(err) } cmd = strings.TrimSpace(cmd) + fmt.Println(cmd) switch t { case 0: //session @@ -124,6 +143,34 @@ func GetCommand(uid uuid.UUID, resp chan coordinator.SessionCommand) { default: break } + case 1: + //state + resp <- coordinator.SessionCommand{ + ID: uid, + Command: coordinator.SessionCmdPlay, + GameCommand: &game.Command{ + PlayerID: pid, + Type: game.StateCmd, + Cmd: cmd, + }, + } + case 2: + //action + resp <- coordinator.SessionCommand{ + ID: uid, + Command: coordinator.SessionCmdPlay, + GameCommand: &game.Command{ + PlayerID: pid, + Type: game.ActCmd, + Cmd: cmd, + }, + } } + } } + +func trimFirstRune(s string) string { + _, i := utf8.DecodeRuneInString(s) + return s[i:] +} diff --git a/internal/coordinator/server.go b/internal/coordinator/server.go index 81e293c..5bcea60 100644 --- a/internal/coordinator/server.go +++ b/internal/coordinator/server.go @@ -38,6 +38,10 @@ func serveWs(c *Coordinator, w http.ResponseWriter, r *http.Request) { } log.Printf("Received: %s", cmd) resp := c.Coordinate(&cmd) + if err != nil { + panic(err) + } + log.Printf("sending: %v", resp) err = conn.WriteJSON(resp) if err != nil { log.Println("Error during message writing:", err) diff --git a/internal/coordinator/session.go b/internal/coordinator/session.go index cd2c333..26fbaa0 100644 --- a/internal/coordinator/session.go +++ b/internal/coordinator/session.go @@ -74,13 +74,14 @@ func (s *Session) Play(id uuid.UUID, cmd *game.Command) *game.CommandResult { if s.pMap[id] != cmd.PlayerID { return nil } - return s.Game.Parse(cmd) + res := s.Game.Parse(cmd) + return res } type SessionCommand struct { ID uuid.UUID `json:"player_id"` Command SessionCmd `json:"command"` - GameCommand *game.Command `json:"game_command"` + GameCommand *game.Command `json:"game_command,omitempty"` } func (s *SessionCommand) String() string { @@ -90,7 +91,7 @@ func (s *SessionCommand) String() string { type SessionCommandResult struct { ID uuid.UUID `json:"player_id"` Result SessionResp `json:"result"` - GameResult *game.CommandResult `json:"game_result"` + GameResult *game.CommandResult `json:"game_result,omitempty"` } func (s *SessionCommandResult) String() string { diff --git a/internal/game/board.go b/internal/game/board.go index fe8b484..d2a88bf 100644 --- a/internal/game/board.go +++ b/internal/game/board.go @@ -1,6 +1,9 @@ package game -import "fmt" +import ( + "encoding/json" + "fmt" +) type Board struct { Sentinal [4]Card `json:"sentinal"` @@ -9,8 +12,8 @@ type Board struct { func NewBoard() *Board { return &Board{ - Sentinal: [4]Card{NewCard(-1), NewCard(-1), NewCard(-1), NewCard(-1)}, - Scourge: [4]Card{NewCard(-1), NewCard(-1), NewCard(-1), NewCard(-1)}, + Sentinal: [4]Card{CreateCard(-1), CreateCard(-1), CreateCard(-1), CreateCard(-1)}, + Scourge: [4]Card{CreateCard(-1), CreateCard(-1), CreateCard(-1), CreateCard(-1)}, } } @@ -62,7 +65,7 @@ func (b *Board) Move(id, src, dest int) bool { } brd[dest].Act() brd[dest] = brd[src] - brd[src] = NewCard(-1) + brd[src] = CreateCard(-1) if id == 1 { b.Sentinal = brd } else { @@ -150,7 +153,7 @@ func (b *Board) Attack(id int, atk, def int) int { } } if attacker > defender { - dBrd[def] = NewCard(-1) + dBrd[def] = CreateCard(-1) if id == 1 { b.Sentinal = aBrd b.Scourge = dBrd @@ -161,8 +164,8 @@ func (b *Board) Attack(id int, atk, def int) int { return 0 } if attacker == defender { - aBrd[atk] = NewCard(-1) - dBrd[def] = NewCard(-1) + aBrd[atk] = CreateCard(-1) + dBrd[def] = CreateCard(-1) if id == 1 { b.Sentinal = aBrd b.Scourge = dBrd @@ -175,3 +178,23 @@ func (b *Board) Attack(id int, atk, def int) int { return -1 } + +func (b *Board) MarshalJSON() ([]byte, error) { + res := [][]*PortableCard{{b.Sentinal[0].Port(), b.Sentinal[1].Port(), b.Sentinal[2].Port(), b.Sentinal[3].Port()}, {b.Scourge[0].Port(), b.Scourge[1].Port(), b.Scourge[2].Port(), b.Scourge[3].Port()}} + return json.Marshal(res) +} + +func (b *Board) UnmarshalJSON(data []byte) (err error) { + var ported [][]PortableCard + err = json.Unmarshal(data, &ported) + if err != nil { + return err + } + for i := 0; i < 4; i++ { + b.Sentinal[i] = NewCard(ported[0][i].Type, ported[0][i].ID) + } + for i := 0; i < 4; i++ { + b.Scourge[i] = NewCard(ported[1][i].Type, ported[0][i].ID) + } + return +} diff --git a/internal/game/card.go b/internal/game/card.go index 3a8c378..fd5b23e 100644 --- a/internal/game/card.go +++ b/internal/game/card.go @@ -1,6 +1,10 @@ package game -import "fmt" +import ( + "fmt" + + "github.com/google/uuid" +) type Card interface { Cast(g *Game) *Game @@ -13,11 +17,13 @@ type Card interface { Empty() bool String() string CanAttack(int, int) bool + Port() *PortableCard } type GenericCard struct { - Val CardValue - Sick bool + Val CardValue `json:"value"` + Id uuid.UUID `json:"id"` + Sick bool `json:"sick"` } func (g *GenericCard) Cast(game *Game) *Game { @@ -55,6 +61,13 @@ func (g *GenericCard) String() string { return fmt.Sprintf("%v", g.Val) } +func (g *GenericCard) Port() *PortableCard { + return &PortableCard{ + Type: int(g.Val), + ID: g.Id, + } +} + func (g *GenericCard) CanAttack(x, y int) bool { if x == y && !g.Sick { return true @@ -86,12 +99,17 @@ func (c CardValue) String() string { return []string{"V", "A", "2", "3", "4", "5", "6", "7", "8"}[c] } -func NewCard(v int) Card { +func CreateCard(v int) Card { + return NewCard(v, uuid.New()) +} + +func NewCard(v int, id uuid.UUID) Card { switch v { case -1: return &EmptyCard{ &GenericCard{ Val: EmptyValue, + Id: id, Sick: true, }, } @@ -99,6 +117,7 @@ func NewCard(v int) Card { return &AceCard{ &GenericCard{ Val: Ace, + Id: id, Sick: false, }, } @@ -106,6 +125,7 @@ func NewCard(v int) Card { return &FourCard{ &GenericCard{ Val: Four, + Id: id, Sick: true, }, } @@ -113,6 +133,7 @@ func NewCard(v int) Card { return &EightCard{ &GenericCard{ Val: Eight, + Id: id, Sick: true, }, 0, @@ -121,12 +142,14 @@ func NewCard(v int) Card { return &Valkyrie{ &GenericCard{ Val: Valk, + Id: id, Sick: false, }, } default: return &GenericCard{ Val: CardValue(v), + Id: id, Sick: true, } } @@ -155,7 +178,7 @@ func (f *FourCard) Enters(g *Game) *Game { type EightCard struct { *GenericCard - Counters int + Counters int `json:"counters"` } func (e *EightCard) CanAttack(x, y int) bool { @@ -179,8 +202,8 @@ type Valkyrie struct { } func (v *Valkyrie) Cast(g *Game) *Game { - g.GameBoard.Sentinal = [4]Card{NewCard(-1), NewCard(-1), NewCard(-1), NewCard(-1)} - g.GameBoard.Scourge = [4]Card{NewCard(-1), NewCard(-1), NewCard(-1), NewCard(-1)} + g.GameBoard.Sentinal = [4]Card{CreateCard(-1), CreateCard(-1), CreateCard(-1), CreateCard(-1)} + g.GameBoard.Scourge = [4]Card{CreateCard(-1), CreateCard(-1), CreateCard(-1), CreateCard(-1)} return g } @@ -188,3 +211,8 @@ func (v *Valkyrie) Enters(g *Game) *Game { v = nil return g } + +type PortableCard struct { + Type int `json:"type"` + ID uuid.UUID `json:"ID"` +} diff --git a/internal/game/cmd.go b/internal/game/cmd.go index b9d00d7..72cc1be 100644 --- a/internal/game/cmd.go +++ b/internal/game/cmd.go @@ -1,5 +1,7 @@ package game +import "fmt" + type CmdType string const ( @@ -14,10 +16,18 @@ type Command struct { Cmd string `json:"cmd"` } +func (c *Command) String() string { + return fmt.Sprintf("%v %v %v", c.PlayerID, c.Type, c.Cmd) +} + type CommandResult struct { PlayerID int `json:"player_id"` ResultType CmdType `json:"result_type"` - StateResult *GameView `json:"state_result"` - ActionResult []Card `json:"action_result"` - DebugResult *Game `json:"debug_result"` + StateResult *GameView `json:"state_result,omitempty"` + ActionResult *Deck `json:"action_result,omitempty"` + DebugResult *Game `json:"debug_result,omitempty"` +} + +func (c *CommandResult) String() string { + return fmt.Sprintf("%v %v\nState Result: %v\nAction Result: %v\nDebug: %v\n", c.PlayerID, c.ResultType, c.StateResult, c.ActionResult, c.DebugResult) } diff --git a/internal/game/deck.go b/internal/game/deck.go index 9b4d044..733c51d 100644 --- a/internal/game/deck.go +++ b/internal/game/deck.go @@ -1,6 +1,7 @@ package game import ( + "encoding/json" "math/rand" "time" ) @@ -10,10 +11,10 @@ type Deck struct { } func NewDeck() *Deck { - cards := []Card{NewCard(0)} + cards := []Card{CreateCard(0)} for i := 0; i < 3; i++ { for j := 1; j < 9; j++ { - cards = append(cards, Card(NewCard(j))) + cards = append(cards, Card(CreateCard(j))) } } return &Deck{ @@ -21,6 +22,12 @@ func NewDeck() *Deck { } } +func DeckFromCards(c []Card) *Deck { + return &Deck{ + Cards: c, + } +} + func (d *Deck) Shuffle() { cards := d.Cards r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -50,3 +57,25 @@ func (d *Deck) Bottom(result []Card) { func (d *Deck) Size() int { return len(d.Cards) } + +func (d *Deck) MarshalJSON() ([]byte, error) { + var ported []*PortableCard + for i, _ := range d.Cards { + ported = append(ported, d.Cards[i].Port()) + } + return json.Marshal(ported) +} + +func (d *Deck) UnmarshalJSON(data []byte) (err error) { + var ported []PortableCard + err = json.Unmarshal(data, &ported) + if err != nil { + return err + } + cards := []Card{} + for _, v := range ported { + cards = append(cards, NewCard(v.Type, v.ID)) + } + d.Cards = cards + return +} diff --git a/internal/game/game.go b/internal/game/game.go index cd3bd45..abab658 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -34,7 +34,7 @@ type Game struct { SentinalDeck *Deck ScourgeDeck *Deck CurrentTurn int - CardBuffer []Card + CardBuffer *Deck CanDraw bool HasDrawn bool Status GameStatus @@ -52,7 +52,7 @@ func NewGame() *Game { SentinalDeck: deckA, ScourgeDeck: deckB, CurrentTurn: 0, //start with no turn - CardBuffer: []Card{}, + CardBuffer: DeckFromCards([]Card{}), CanDraw: false, HasDrawn: false, Status: StatusLobby, @@ -65,7 +65,7 @@ func (g *Game) String() string { func (g *Game) Parse(cmd *Command) *CommandResult { - if g.Status != StatusLobby || g.Status != StatusReady || g.Status != StatusPlaying { + if g.Status == StatusDraw || g.Status == StatusScourgeWin || g.Status == StatusSentinalWin || g.Status == StatusStop { return &CommandResult{ PlayerID: cmd.PlayerID, ResultType: cmd.Type, @@ -76,17 +76,23 @@ func (g *Game) Parse(cmd *Command) *CommandResult { } var state_res *GameView - var action_res []Card + var action_res *Deck var debug_res *Game var res_type CmdType if cmd.Type == ActCmd { action_res = g.PlayerAct(cmd.PlayerID, cmd.Cmd) + state_res = nil + debug_res = nil res_type = ActCmd } else if cmd.Type == StateCmd { state_res = g.PlayerStateAct(cmd.PlayerID, cmd.Cmd) + action_res = nil + debug_res = nil res_type = StateCmd } else { + state_res = nil + action_res = nil debug_res = g res_type = DebugCmd } @@ -168,7 +174,7 @@ func (g *Game) PlayerStateAct(id int, cmd string) *GameView { if id != g.CurrentTurn { return nil } - g.CardBuffer = []Card{} + g.CardBuffer = DeckFromCards([]Card{}) if id == SentinalID { for _, v := range g.GameBoard.Sentinal { v.Endstep(g) @@ -187,7 +193,7 @@ func (g *Game) PlayerStateAct(id int, cmd string) *GameView { return NewView(id, g) } -func (g *Game) PlayerAct(id int, cmd string) []Card { +func (g *Game) PlayerAct(id int, cmd string) *Deck { if id != g.CurrentTurn { return nil } @@ -210,10 +216,10 @@ func (g *Game) PlayerAct(id int, cmd string) []Card { switch cmd_s[0] { case "s": //scry: return scry options off top of deck - if !g.CanDraw || len(g.CardBuffer) > 0 { + if !g.CanDraw || g.CardBuffer.Size() > 0 { return nil } - g.CardBuffer = currD.Scry(curr.Life) + g.CardBuffer = DeckFromCards(currD.Scry(curr.Life)) return g.CardBuffer case "d": //draw: return player hand @@ -227,18 +233,18 @@ func (g *Game) PlayerAct(id int, cmd string) []Card { if err != nil { panic(err) } - x := g.CardBuffer[x_i] + x := g.CardBuffer.Cards[x_i] buf := g.CardBuffer - for i, v := range buf { + for i, v := range buf.Cards { if v == x { - buf = append(buf[:i], buf[i+1:]...) + buf.Cards = append(buf.Cards[:i], buf.Cards[i+1:]...) } } - currD.Bottom(buf) + currD.Bottom(buf.Cards) curr.Hand = append(curr.Hand, x) g.CanDraw = false g.HasDrawn = true - return curr.Hand + return DeckFromCards(curr.Hand) case "m": //move: return player board or [] if invalid @@ -252,9 +258,9 @@ func (g *Game) PlayerAct(id int, cmd string) []Card { y_i, _ := strconv.Atoi(cmd_s[2]) res := g.GameBoard.Move(g.CurrentTurn, x_i, y_i) if res { - return g.GameBoard.GetRow(g.CurrentTurn) + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else { - return []Card{} + return DeckFromCards([]Card{}) } case "a": //attack @@ -269,12 +275,12 @@ func (g *Game) PlayerAct(id int, cmd string) []Card { res := g.GameBoard.Attack(g.CurrentTurn, x_i, y_i) if res == 1 { opp.Life = opp.Life - 1 - return g.GameBoard.GetRow(g.CurrentTurn) + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else if res == 0 { - return g.GameBoard.GetRow(g.CurrentTurn) + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else { fmt.Println("can't attack") - return []Card{} + return DeckFromCards([]Card{}) } case "p": //play: return player boad or [] if invalid @@ -295,10 +301,10 @@ func (g *Game) PlayerAct(id int, cmd string) []Card { if res { curr.Hand[x_i].Enters(g) curr.Hand = append(curr.Hand[:x_i], curr.Hand[x_i+1:]...) - return g.GameBoard.GetRow(g.CurrentTurn) + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) } else { fmt.Println("couldn't play") - return []Card{} + return DeckFromCards([]Card{}) } default: fmt.Println("Invalid act command") diff --git a/internal/game/player.go b/internal/game/player.go index 4877d1f..0ebaa3a 100644 --- a/internal/game/player.go +++ b/internal/game/player.go @@ -1,5 +1,13 @@ package game +import ( + "encoding/json" + "fmt" + "log" + + "github.com/google/uuid" +) + type Player struct { Name string `json:"name"` Id int `json:"id"` @@ -15,3 +23,44 @@ func NewPlayer(name string, id int) *Player { Life: 3, } } + +func (p *Player) MarshalJSON() ([]byte, error) { + var ported_hand []*PortableCard + for i, _ := range p.Hand { + ported_hand = append(ported_hand, p.Hand[i].Port()) + } + res := []interface{}{p.Name, p.Id, ported_hand, p.Life} + return json.Marshal(res) +} + +func (p *Player) UnmarshalJSON(data []byte) (err error) { + ported := []interface{}{} + err = json.Unmarshal(data, &ported) + if err != nil { + return err + } + fmt.Println(ported) + p.Name = ported[0].(string) + p.Id = int(ported[1].(float64)) + var tmp []interface{} + if ported[2] != nil { + tmp = ported[2].([]interface{}) + } else { + tmp = []interface{}{} + } + p.Life = int(ported[3].(float64)) + hand := []Card{} + for _, v := range tmp { + m := v.(map[string]interface{}) + t := int(m["type"].(float64)) + i := m["ID"].(string) + uid, err := uuid.Parse(i) + if err != nil { + log.Println("invalid card parse") + } else { + hand = append(hand, NewCard(t, uid)) + } + } + p.Hand = hand + return +}