From 611c3e25cac0a1d061398675bf2cc61072916b12 Mon Sep 17 00:00:00 2001 From: Steve Date: Fri, 1 Oct 2021 13:40:46 -0400 Subject: [PATCH] initial migration --- board.go | 10 ++ card.go | 24 ++++ deck.go | 19 +++ game.go | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++ game_view.go | 36 +++++ go.mod | 10 ++ go.sum | 4 + oracle.go | 158 ++++++++++++++++++++ player.go | 15 ++ 9 files changed, 674 insertions(+) create mode 100644 board.go create mode 100644 card.go create mode 100644 deck.go create mode 100644 game.go create mode 100644 game_view.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 oracle.go create mode 100644 player.go diff --git a/board.go b/board.go new file mode 100644 index 0000000..e61ff5e --- /dev/null +++ b/board.go @@ -0,0 +1,10 @@ +package tome_game + +import . "git.saintnet.tech/tomecraft/tome_lib" + +func NewBoard() *Board { + return &Board{ + Sentinal: [4]*Card{NewEmpty(0), NewEmpty(1), NewEmpty(2), NewEmpty(3)}, + Scourge: [4]*Card{NewEmpty(0), NewEmpty(1), NewEmpty(2), NewEmpty(3)}, + } +} diff --git a/card.go b/card.go new file mode 100644 index 0000000..4388234 --- /dev/null +++ b/card.go @@ -0,0 +1,24 @@ +package tome_game + +import ( + . "git.saintnet.tech/tomecraft/tome_lib" + "github.com/google/uuid" +) + +func NewCard(v, o, p int, id uuid.UUID) *Card { + if id == uuid.Nil { + id = uuid.New() + } + return &Card{ + Type: CardType(v), + BasePower: OraclePower(CardType(v), nil), + Power: OraclePower(CardType(v), nil), + Id: id, + Sick: false, + Counters: 0, + Owner: o, + Position: p, + Spell: OracleSpell(CardType(v), nil), + Effects: []*Effect{}, + } +} diff --git a/deck.go b/deck.go new file mode 100644 index 0000000..eafa9bd --- /dev/null +++ b/deck.go @@ -0,0 +1,19 @@ +package tome_game + +import ( + . "git.saintnet.tech/tomecraft/tome_lib" + "github.com/google/uuid" +) + +func NewDeck(owner int) *Deck { + cards := []*Card{} + for i := 0; i < 3; i++ { + for j := 1; j < 9; j++ { + cards = append(cards, NewCard(j, owner, -1, uuid.Nil)) + } + } + cards = append(cards, NewCard(0, owner, -1, uuid.Nil)) + return &Deck{ + Cards: cards, + } +} diff --git a/game.go b/game.go new file mode 100644 index 0000000..f3d5340 --- /dev/null +++ b/game.go @@ -0,0 +1,398 @@ +package tome_game + +import ( + "fmt" + "log" + "strconv" + "strings" + + . "git.saintnet.tech/tomecraft/tome_lib" +) + +type Game struct { + GameBoard *Board + SentinalPlayer *Player + ScourgePlayer *Player + SentinalDeck *Deck + ScourgeDeck *Deck + CurrentTurn int + CardBuffer *Deck + CanDraw bool + HasDrawn bool + Status GameStatus +} + +func NewGame() *Game { + deckA := NewDeck(SentinalID) + deckB := NewDeck(ScourgeID) + deckA.Shuffle() + deckB.Shuffle() + return &Game{ + GameBoard: NewBoard(), + SentinalPlayer: NewPlayer("Sentinal", SentinalID), + ScourgePlayer: NewPlayer("Scourge", ScourgeID), + SentinalDeck: deckA, + ScourgeDeck: deckB, + CurrentTurn: 0, //start with no turn + CardBuffer: DeckFromCards([]*Card{}), + CanDraw: false, + HasDrawn: false, + Status: StatusLobby, + } +} + +func (g *Game) String() string { + return fmt.Sprintf("Sen(%v): %v\n\n%v\n\nSco(%v): %v\nStatus:%v Draw:%v Turn:%v\n%v\n", g.SentinalPlayer.Life, g.SentinalPlayer.Hand, g.GameBoard, g.ScourgePlayer.Life, g.ScourgePlayer.Hand, g.Status, g.CanDraw, g.CurrentTurn, g.CardBuffer) +} + +func (g *Game) Parse(cmd *Command) *CommandResult { + + if g.Status == StatusDraw || g.Status == StatusScourgeWin || g.Status == StatusSentinalWin || g.Status == StatusStop { + return &CommandResult{ + PlayerID: cmd.PlayerID, + ResultType: cmd.Type, + StateResult: NewView(cmd.PlayerID, g), + ActionResult: nil, + DebugResult: g, + } + } + + var state_res *GameView + var action_res *Deck + var debug_res *Game + var res_type CmdType + + if cmd.Type == ActCmd && g.Status == StatusPlaying { + 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 if cmd.Type == DebugCmd { + state_res = nil + action_res = nil + debug_res = g + res_type = DebugCmd + } else { + state_res = nil + action_res = nil + debug_res = nil + res_type = InvalidCmd + } + g.StateChanges() + return &CommandResult{ + PlayerID: cmd.PlayerID, + ResultType: res_type, + StateResult: state_res, + ActionResult: action_res, + DebugResult: debug_res, + } +} + +func (g *Game) StateChanges() { + //check for no actions first + if len(g.SentinalPlayer.Hand) == 0 && g.SentinalDeck.Size() == 0 { + if g.GameBoard.Empty(SentinalID) { + g.Status = StatusScourgeWin + } + } + + if len(g.ScourgePlayer.Hand) == 0 && g.ScourgeDeck.Size() == 0 { + if g.GameBoard.Empty(ScourgeID) { + g.Status = StatusSentinalWin + } + } + //reapply card effects + g.GameBoard.ResetCards() + for i := 0; i < len(g.GameBoard.Sentinal); i++ { + OracleTick(g.GameBoard.Sentinal[i], g) //apply effect stacks first + OracleTick(g.GameBoard.Scourge[i], g) + } + for i := 0; i < len(g.GameBoard.Sentinal); i++ { + OracleEffect(g.GameBoard.Sentinal[i], g) //activate effects + OracleEffect(g.GameBoard.Scourge[i], g) + } + //check life + //this is second on purpose so that it shadows deck out win conditions + //if you use your last action to win the game, you should actually win + if g.SentinalPlayer.Life < 0 { + g.Status = StatusScourgeWin + } + if g.ScourgePlayer.Life < 0 { + g.Status = StatusSentinalWin + } + if g.ScourgePlayer.Life < 0 && g.SentinalPlayer.Life < 0 { + g.Status = StatusDraw + } +} + +func (g *Game) PlayerStateAct(id int, cmd string) *GameView { + switch cmd { + case "d": + Debug(g) + case "g": + //game state + return NewView(id, g) + case "b": + //begin game + if id == SentinalID { + g.SentinalPlayer.Ready = true + } else if id == ScourgeID { + g.ScourgePlayer.Ready = true + } + if g.SentinalPlayer.Ready && g.ScourgePlayer.Ready && g.Status == StatusLobby { + g.Status = StatusReady + if id == SentinalID { + g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-5 : len(g.SentinalDeck.Cards)] + g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-5] + + g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-6 : len(g.ScourgeDeck.Cards)] + g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-6] + g.CurrentTurn = SentinalID + } else { + g.SentinalPlayer.Hand = g.SentinalDeck.Cards[len(g.SentinalDeck.Cards)-6 : len(g.SentinalDeck.Cards)] + g.SentinalDeck.Cards = g.SentinalDeck.Cards[0 : len(g.SentinalDeck.Cards)-6] + + g.ScourgePlayer.Hand = g.ScourgeDeck.Cards[len(g.ScourgeDeck.Cards)-5 : len(g.ScourgeDeck.Cards)] + g.ScourgeDeck.Cards = g.ScourgeDeck.Cards[0 : len(g.ScourgeDeck.Cards)-5] + g.CurrentTurn = ScourgeID + } + } + case "s": + //start turn + if g.Status == StatusReady { //first turn + g.Status = StatusPlaying + } + //skip draw step when 0 life + if id == SentinalID && g.SentinalPlayer.Life <= 0 { + g.CanDraw = false + g.HasDrawn = true + } else if id == ScourgeID && g.ScourgePlayer.Life <= 0 { + g.CanDraw = false + g.HasDrawn = true + } else { + g.CanDraw = true + g.HasDrawn = false + } + if id != g.CurrentTurn { + return nil + } + if id == SentinalID { + for _, v := range g.GameBoard.Sentinal { + OracleUpkeep(v, g) + } + } else { + for _, v := range g.GameBoard.Scourge { + OracleUpkeep(v, g) + } + } + case "e": + //end turn and clean up + if id != g.CurrentTurn { + return nil + } + g.CardBuffer = DeckFromCards([]*Card{}) + if id == SentinalID { + for _, v := range g.GameBoard.Sentinal { + OracleEndstep(v, g) + } + } else { + for _, v := range g.GameBoard.Scourge { + OracleEndstep(v, g) + } + } + if g.CurrentTurn == SentinalID { + g.CurrentTurn = ScourgeID + } else { + g.CurrentTurn = SentinalID + } + } + return NewView(id, g) +} + +func (g *Game) PlayerAct(id int, cmd string) *Deck { + if id != g.CurrentTurn { + return nil + } + var curr *Player + var opp *Player + var currD *Deck + if id == g.SentinalPlayer.Id { + curr = g.SentinalPlayer + opp = g.ScourgePlayer + currD = g.SentinalDeck + } else { + curr = g.ScourgePlayer + opp = g.SentinalPlayer + currD = g.ScourgeDeck + } + cmd_s := strings.Split(cmd, " ") + if len(cmd_s) < 1 { + return nil + } + switch cmd_s[0] { + case "s": + //scry: return scry options off top of deck + if !g.CanDraw { + return nil + } + if g.CardBuffer.Size() <= 0 { + g.CardBuffer = DeckFromCards(currD.Scry(curr.Life)) + } + return g.CardBuffer + case "d": + //draw: return player hand + if !g.CanDraw { + return nil + } + if len(cmd_s) != 2 || !g.CanDraw { + return nil + } + if curr.Life == 0 { + return DeckFromCards(curr.Hand) + } + x_i, err := strconv.Atoi(cmd_s[1]) + if err != nil { + panic(err) + } + if x_i > 2 || x_i < 0 { + return nil + } + x := g.CardBuffer.Cards[x_i] + if x.Owner != g.CurrentTurn { + log.Println("drew a card from our deck that isn't our own") + } + g.CardBuffer.Cards = append(g.CardBuffer.Cards[:x_i], g.CardBuffer.Cards[x_i+1:]...) + currD.Bottom(g.CardBuffer.Cards) + curr.Hand = append(curr.Hand, x) + g.CanDraw = false + g.HasDrawn = true + return DeckFromCards(curr.Hand) + + case "m": + //move: return player board or [] if invalid + if len(cmd_s) != 3 { + return nil + } + if !g.HasDrawn { + return nil + } + x_i, err := strconv.Atoi(cmd_s[1]) + if err != nil { + log.Println(err) + return nil + } + y_i, err := strconv.Atoi(cmd_s[2]) + if err != nil { + log.Println(err) + } + if g.GameBoard.CanMove(g.CurrentTurn, x_i, y_i) { + OracleMove(g.GameBoard.GetCard(g.CurrentTurn, x_i), x_i, y_i, g) + g.GameBoard.Move(g.CurrentTurn, x_i, y_i) + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) + } else { + return DeckFromCards([]*Card{}) + } + case "a": + //attack + if len(cmd_s) != 3 { + return nil + } + if !g.HasDrawn { + return nil + } + x_i, err := strconv.Atoi(cmd_s[1]) + if err != nil { + log.Println(err) + return nil + } + y_i, err := strconv.Atoi(cmd_s[2]) + if err != nil { + log.Println(err) + return nil + } + if g.GameBoard.CanAttack(g.CurrentTurn, x_i, y_i) { + OracleAttack(g.GameBoard.GetCard(g.CurrentTurn, x_i), g) + res := g.GameBoard.Attack(g.CurrentTurn, x_i, y_i) + var aid, did int + if id == SentinalID { + aid = SentinalID + did = ScourgeID + } else { + aid = ScourgeID + did = SentinalID + } + attacker := g.GameBoard.GetRow(aid)[x_i] + defender := g.GameBoard.GetRow(did)[y_i] + switch res { + case 1: + opp.Life = opp.Life - 1 + case 2: + OracleLeaves(defender, g) + g.GameBoard.Remove(defender) + case 3: + OracleLeaves(attacker, g) + g.GameBoard.Remove(attacker) + case 4: + OracleLeaves(attacker, g) + OracleLeaves(defender, g) + g.GameBoard.Remove(attacker) + g.GameBoard.Remove(defender) + + } + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) + + } else { + log.Println("can't attack") + return DeckFromCards([]*Card{}) + } + case "p": + //play: return player boad or [] if invalid + if len(cmd_s) != 3 { + fmt.Println("not enough arguments") + return nil + } + if !g.HasDrawn { + return nil + } + x_i, err := strconv.Atoi(cmd_s[1]) + if err != nil { + log.Println(err) + return nil + } + y_i, err := strconv.Atoi(cmd_s[2]) + if err != nil { + log.Println(err) + return nil + } + if x_i < 0 || x_i > len(curr.Hand) { + return nil + } + card := curr.Hand[x_i] + shouldPlace := true + if g.GameBoard.CanPlay(g.CurrentTurn, card, y_i) { + shouldPlace = OracleCast(card, g) + placed := g.GameBoard.Play(g.CurrentTurn, card, y_i, shouldPlace) + if placed { + OracleEnters(card, g) + } + curr.Hand = append(curr.Hand[:x_i], curr.Hand[x_i+1:]...) + return DeckFromCards(g.GameBoard.GetRow(g.CurrentTurn)) + } else { + fmt.Println("couldn't play") + return DeckFromCards([]*Card{}) + } + default: + fmt.Println("Invalid act command") + return nil + } + return nil +} + +func Debug(g *Game) { + return +} diff --git a/game_view.go b/game_view.go new file mode 100644 index 0000000..b29abbf --- /dev/null +++ b/game_view.go @@ -0,0 +1,36 @@ +package tome_game + +import ( + . "git.saintnet.tech/tomecraft/tome_lib" +) + +func NewView(id int, g *Game) *GameView { + if id == SentinalID { + return &GameView{ + Board: g.GameBoard, + Player: g.SentinalPlayer, + DeckSize: g.SentinalDeck.Size(), + EnemyLife: g.ScourgePlayer.Life, + EnemyDeckSize: g.ScourgeDeck.Size(), + EnemyHandSize: len(g.ScourgePlayer.Hand), + CurrentTurn: g.CurrentTurn, + CanDraw: g.CanDraw, + HasDrawn: g.HasDrawn, + Status: g.Status, + } + } else { + return &GameView{ + Board: g.GameBoard, + Player: g.ScourgePlayer, + DeckSize: g.ScourgeDeck.Size(), + EnemyLife: g.SentinalPlayer.Life, + EnemyDeckSize: g.SentinalDeck.Size(), + EnemyHandSize: len(g.SentinalPlayer.Hand), + CurrentTurn: g.CurrentTurn, + CanDraw: g.CanDraw, + HasDrawn: g.HasDrawn, + Status: g.Status, + } + + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8620fab --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module tome_game + +go 1.16 +replace git.saintnet.tech/tomecraft/tome_lib => ../tome_lib + +require ( + git.saintnet.tech/tomecraft/tome_lib v0.1.0 // indirect + github.com/google/uuid v1.3.0 // indirect +) + diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..be95d08 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +git.saintnet.tech/tomecraft/tome_lib v0.1.0 h1:c+AMtXgLKUFErrYFx7g8M2zVcrHUwLRY18v/+N8nn2I= +git.saintnet.tech/tomecraft/tome_lib v0.1.0/go.mod h1:Jekqa9ojNDOrcO1aL0IWKuhCQSE5+MNHVcYtTWA6uko= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= diff --git a/oracle.go b/oracle.go new file mode 100644 index 0000000..43506c2 --- /dev/null +++ b/oracle.go @@ -0,0 +1,158 @@ +package tome_game + +import ( + "log" + + . "git.saintnet.tech/tomecraft/tome_lib" +) + +func OracleUpkeep(c *Card, g *Game) { + switch c.Type { + case Eight: + c.Sick = true + if c.Counters >= 3 { + g.GameBoard.Remove(c) + } + default: + c.Sick = false + } + return +} + +func OracleSpell(c CardType, g *Game) bool { + switch c { + case Valk: + return true + default: + return false + } +} + +func OracleCast(c *Card, g *Game) bool { + switch c.Type { + case Valk: + g.GameBoard = NewBoard() + return false + default: + return true + } +} + +func OracleEnters(c *Card, g *Game) { + c.Sick = true + switch c.Type { + case Ace: + c.Sick = false + return + case Four: + AddEffect(c, &Effect{c.Id, c.Id, 2}) + case Eight: + c.Counters = 0 + } +} + +func OracleTick(c *Card, g *Game) { + if c.Type == EmptyValue { + return + } + row := g.GameBoard.GetRow(c.Owner) + + switch c.Type { + case Two: + //+1 to all + for _, v := range row { + if v.Id != c.Id { + AddEffect(v, &Effect{c.Id, v.Id, 1}) + } + } + case Three: + //+1 around it + if c.Position-1 >= 0 { + AddEffect(row[c.Position-1], &Effect{c.Id, row[c.Position-1].Id, 1}) + } + if c.Position+1 <= 3 { + AddEffect(row[c.Position+1], &Effect{c.Id, row[c.Position+1].Id, 1}) + } + } +} + +func OracleLeaves(c *Card, g *Game) { + if c.Empty() { + return + } + row := g.GameBoard.GetRow(c.Owner) + switch c.Type { + case Two: + //remove +1 to all + for _, v := range row { + RemoveEffect(c.Id, v) + } + case Three: + //+1 around it + if c.Position-1 >= 0 { + RemoveEffect(c.Id, row[c.Position-1]) + } + if c.Position+1 <= 3 { + RemoveEffect(c.Id, row[c.Position+1]) + } + + } + return +} + +func OracleEndstep(c *Card, g *Game) { + switch c.Type { + case Eight: + c.Counters = c.Counters + 1 + } + return +} + +func OraclePower(c CardType, g *Game) int { + return int(c) +} + +func OracleMove(c *Card, src, dest int, g *Game) { + c.Sick = true + switch c.Type { + case Three: + row := g.GameBoard.GetRow(c.Owner) + if src-1 >= 0 { + RemoveEffect(c.Id, row[src-1]) + } + if src+1 <= 3 { + RemoveEffect(c.Id, row[src-1]) + } + } +} + +func OracleAttack(c *Card, g *Game) { + c.Sick = true +} + +func OracleEffect(c *Card, g *Game) { + if c.Empty() { + return + } + for _, e := range c.Effects { + switch e.ID { + case 0: + log.Println("dummy effect applied. probably a bug") + case 1: + c.Power = c.Power + 1 + case 2: + if c.Owner == SentinalID { + g.CardBuffer = DeckFromCards(g.SentinalDeck.Scry(1)) + } else if c.Owner == ScourgeID { + g.CardBuffer = DeckFromCards(g.SentinalDeck.Scry(1)) + } else { + log.Println("card draw effect was played but with no owner?") + } + g.CanDraw = true + g.HasDrawn = false + RemoveEffect(e.Owner, c) + default: + log.Println("wrong effect type") + } + } +} diff --git a/player.go b/player.go new file mode 100644 index 0000000..3d095b7 --- /dev/null +++ b/player.go @@ -0,0 +1,15 @@ +package tome_game + +import ( + . "git.saintnet.tech/tomecraft/tome_lib" +) + +func NewPlayer(name string, id int) *Player { + return &Player{ + Name: name, + Id: id, + Hand: []*Card{}, + Life: 3, + Ready: false, + } +}