transmit game actions and winner in dto
This commit is contained in:
parent
805fdb13c1
commit
1068060e94
35
game/action.go
Normal file
35
game/action.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
type action uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
actStart action = iota // start of match
|
||||||
|
|
||||||
|
actShoot // fired, note prev indicates whether the shell was live
|
||||||
|
actGameEnd // end of game but not round
|
||||||
|
actBeerGameEnd // used a beer to end the game
|
||||||
|
actChallengerWins // challenger wins the round
|
||||||
|
actDealerWins // dealer wins the match
|
||||||
|
|
||||||
|
// current player uses an item
|
||||||
|
actLens
|
||||||
|
actCig
|
||||||
|
actBeer // note prev indicates whether the shell was live
|
||||||
|
actCuff
|
||||||
|
actKnife
|
||||||
|
|
||||||
|
actDealerConcedes // dealer concedes
|
||||||
|
actChallengerConcedes // challenger concedes
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.17.0 -type=action -trimprefix=act
|
||||||
|
|
||||||
|
// gameStart returns whether the action indicates the start of a game.
|
||||||
|
func (a action) gameStart() bool {
|
||||||
|
switch a {
|
||||||
|
case actStart, actGameEnd, actBeerGameEnd, actChallengerWins:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
35
game/action_string.go
Normal file
35
game/action_string.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generated by "stringer -type=action -trimprefix=act"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[actStart-0]
|
||||||
|
_ = x[actShoot-1]
|
||||||
|
_ = x[actGameEnd-2]
|
||||||
|
_ = x[actBeerGameEnd-3]
|
||||||
|
_ = x[actChallengerWins-4]
|
||||||
|
_ = x[actDealerWins-5]
|
||||||
|
_ = x[actLens-6]
|
||||||
|
_ = x[actCig-7]
|
||||||
|
_ = x[actBeer-8]
|
||||||
|
_ = x[actCuff-9]
|
||||||
|
_ = x[actKnife-10]
|
||||||
|
_ = x[actDealerConcedes-11]
|
||||||
|
_ = x[actChallengerConcedes-12]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _action_name = "StartShootGameEndBeerGameEndChallengerWinsDealerWinsLensCigBeerCuffKnifeDealerConcedesChallengerConcedes"
|
||||||
|
|
||||||
|
var _action_index = [...]uint8{0, 5, 10, 17, 28, 42, 52, 56, 59, 63, 67, 72, 86, 104}
|
||||||
|
|
||||||
|
func (i action) String() string {
|
||||||
|
if i >= action(len(_action_index)-1) {
|
||||||
|
return "action(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _action_name[_action_index[i]:_action_index[i+1]]
|
||||||
|
}
|
19
game/game.go
19
game/game.go
@ -39,6 +39,8 @@ type Match struct {
|
|||||||
// prev is a pointer to the element of shellArray which was fired last this
|
// prev is a pointer to the element of shellArray which was fired last this
|
||||||
// game, or nil if none has been.
|
// game, or nil if none has been.
|
||||||
prev *bool
|
prev *bool
|
||||||
|
// action is a description of the previous action.
|
||||||
|
action action
|
||||||
|
|
||||||
// shellArray is the backing storage for shells and prev.
|
// shellArray is the backing storage for shells and prev.
|
||||||
shellArray [8]bool
|
shellArray [8]bool
|
||||||
@ -188,18 +190,22 @@ func (g *Match) Shoot(id player.ID, self bool) error {
|
|||||||
target = g.CurrentPlayer()
|
target = g.CurrentPlayer()
|
||||||
}
|
}
|
||||||
live := g.popShell()
|
live := g.popShell()
|
||||||
|
g.action = actShoot
|
||||||
if live {
|
if live {
|
||||||
target.hp -= g.damage
|
target.hp -= g.damage
|
||||||
if target.hp <= 0 {
|
if target.hp <= 0 {
|
||||||
target.hp = 0 // in case it goes negative
|
target.hp = 0 // in case it goes negative
|
||||||
|
g.action = actChallengerWins
|
||||||
// If the target is the challenger, the match is over as well.
|
// If the target is the challenger, the match is over as well.
|
||||||
if target == &g.players[1] {
|
if target == &g.players[1] {
|
||||||
|
g.action = actDealerWins
|
||||||
g.round = 3
|
g.round = 3
|
||||||
}
|
}
|
||||||
return ErrRoundEnded
|
return ErrRoundEnded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g.Empty() {
|
if g.Empty() {
|
||||||
|
g.action = actGameEnd
|
||||||
return ErrGameEnded
|
return ErrGameEnded
|
||||||
}
|
}
|
||||||
if !self || live {
|
if !self || live {
|
||||||
@ -214,8 +220,10 @@ func (g *Match) Shoot(id player.ID, self bool) error {
|
|||||||
func (g *Match) Concede(id player.ID) error {
|
func (g *Match) Concede(id player.ID) error {
|
||||||
switch id {
|
switch id {
|
||||||
case g.players[0].id:
|
case g.players[0].id:
|
||||||
|
g.action = actDealerConcedes
|
||||||
g.players[0].hp = 0
|
g.players[0].hp = 0
|
||||||
case g.players[1].id:
|
case g.players[1].id:
|
||||||
|
g.action = actChallengerConcedes
|
||||||
g.players[1].hp = 0
|
g.players[1].hp = 0
|
||||||
default:
|
default:
|
||||||
return ErrWrongTurn
|
return ErrWrongTurn
|
||||||
@ -227,15 +235,24 @@ func (g *Match) Concede(id player.ID) error {
|
|||||||
// DTO returns the current match state as viewed by the given player.
|
// DTO returns the current match state as viewed by the given player.
|
||||||
func (g *Match) DTO(id player.ID) serve.Game {
|
func (g *Match) DTO(id player.ID) serve.Game {
|
||||||
var live, blank int
|
var live, blank int
|
||||||
if g.turn == 1 {
|
if g.action.gameStart() {
|
||||||
live = len(g.shells) / 2
|
live = len(g.shells) / 2
|
||||||
blank = len(g.shells) - live
|
blank = len(g.shells) - live
|
||||||
}
|
}
|
||||||
|
w := serve.NoWinner
|
||||||
|
switch g.RoundWinner() {
|
||||||
|
case &g.players[0]:
|
||||||
|
w = serve.DealerWins
|
||||||
|
case &g.players[1]:
|
||||||
|
w = serve.ChallengerWins
|
||||||
|
}
|
||||||
return serve.Game{
|
return serve.Game{
|
||||||
Players: [2]serve.Player{
|
Players: [2]serve.Player{
|
||||||
g.players[0].DTO(),
|
g.players[0].DTO(),
|
||||||
g.players[1].DTO(),
|
g.players[1].DTO(),
|
||||||
},
|
},
|
||||||
|
Action: g.action.String(),
|
||||||
|
Winner: w,
|
||||||
Round: g.round,
|
Round: g.round,
|
||||||
Dealer: g.CurrentPlayer() == &g.players[0],
|
Dealer: g.CurrentPlayer() == &g.players[0],
|
||||||
Damage: g.damage,
|
Damage: g.damage,
|
||||||
|
@ -446,6 +446,7 @@ func TestGameDTO(t *testing.T) {
|
|||||||
g.players[0].DTO(),
|
g.players[0].DTO(),
|
||||||
g.players[1].DTO(),
|
g.players[1].DTO(),
|
||||||
},
|
},
|
||||||
|
Action: "Start",
|
||||||
Round: g.round,
|
Round: g.round,
|
||||||
Dealer: false,
|
Dealer: false,
|
||||||
Damage: g.damage,
|
Damage: g.damage,
|
||||||
@ -471,6 +472,7 @@ func TestGameDTO(t *testing.T) {
|
|||||||
g.players[0].DTO(),
|
g.players[0].DTO(),
|
||||||
g.players[1].DTO(),
|
g.players[1].DTO(),
|
||||||
},
|
},
|
||||||
|
Action: "Shoot",
|
||||||
Round: g.round,
|
Round: g.round,
|
||||||
Dealer: true,
|
Dealer: true,
|
||||||
Damage: g.damage,
|
Damage: g.damage,
|
||||||
@ -489,6 +491,7 @@ func TestGameDTO(t *testing.T) {
|
|||||||
t.Errorf("observer sees the wrong thing:\nwant %+v\ngot %+v", want, got)
|
t.Errorf("observer sees the wrong thing:\nwant %+v\ngot %+v", want, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO(zeph): many more tests about actions
|
||||||
}
|
}
|
||||||
|
|
||||||
func deref[T any, P ~*T](p P) (x T) {
|
func deref[T any, P ~*T](p P) (x T) {
|
||||||
|
15
game/item.go
15
game/item.go
@ -20,9 +20,8 @@ func (i Item) Apply(g *Match) bool {
|
|||||||
case ItemNone:
|
case ItemNone:
|
||||||
return false
|
return false
|
||||||
case ItemLens:
|
case ItemLens:
|
||||||
if g.reveal {
|
// Lenses are always used, even if they have no effect.
|
||||||
return false
|
g.action = actLens
|
||||||
}
|
|
||||||
g.reveal = true
|
g.reveal = true
|
||||||
return true
|
return true
|
||||||
case ItemCig:
|
case ItemCig:
|
||||||
@ -31,10 +30,13 @@ func (i Item) Apply(g *Match) bool {
|
|||||||
cur.hp++
|
cur.hp++
|
||||||
// Cigs are always used, even if they have no effect.
|
// Cigs are always used, even if they have no effect.
|
||||||
}
|
}
|
||||||
|
g.action = actCig
|
||||||
return true
|
return true
|
||||||
case ItemBeer:
|
case ItemBeer:
|
||||||
g.popShell()
|
g.popShell()
|
||||||
|
g.action = actBeer
|
||||||
if g.Empty() {
|
if g.Empty() {
|
||||||
|
g.action = actBeerGameEnd
|
||||||
g.NextGame()
|
g.NextGame()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -43,12 +45,14 @@ func (i Item) Apply(g *Match) bool {
|
|||||||
if opp.cuffs != Uncuffed {
|
if opp.cuffs != Uncuffed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
g.action = actCuff
|
||||||
opp.cuffs = Cuffed
|
opp.cuffs = Cuffed
|
||||||
return true
|
return true
|
||||||
case ItemKnife:
|
case ItemKnife:
|
||||||
if g.damage != 1 {
|
if g.damage != 1 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
g.action = actKnife
|
||||||
g.damage = 2
|
g.damage = 2
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
@ -56,7 +60,8 @@ func (i Item) Apply(g *Match) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var itemNames = [...]string{"", "🔍", "🚬", "🍺", "👮", "🔪"}
|
||||||
|
|
||||||
func (i Item) String() string {
|
func (i Item) String() string {
|
||||||
s := [...]string{"", "🔍", "🚬", "🍺", "👮", "🔪"}
|
return itemNames[i]
|
||||||
return s[i]
|
|
||||||
}
|
}
|
||||||
|
23
serve/dto.go
23
serve/dto.go
@ -7,6 +7,10 @@ type Game struct {
|
|||||||
// Players is the players in the game.
|
// Players is the players in the game.
|
||||||
// The dealer is always the first player.
|
// The dealer is always the first player.
|
||||||
Players [2]Player `json:"players"`
|
Players [2]Player `json:"players"`
|
||||||
|
// Action indicates the previous action taken in the game.
|
||||||
|
Action string `json:"action"`
|
||||||
|
// Winner indicates the winner of the current round, if any.
|
||||||
|
Winner Winner `json:"winner,omitempty"`
|
||||||
// Round is the current round.
|
// Round is the current round.
|
||||||
Round int8 `json:"round"`
|
Round int8 `json:"round"`
|
||||||
// Dealer indicates whether the current player is the dealer.
|
// Dealer indicates whether the current player is the dealer.
|
||||||
@ -45,3 +49,22 @@ type GameStart struct {
|
|||||||
ID GameID `json:"id"`
|
ID GameID `json:"id"`
|
||||||
Dealer bool `json:"dealer"`
|
Dealer bool `json:"dealer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Winner is a round winner.
|
||||||
|
type Winner int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoWinner Winner = iota
|
||||||
|
DealerWins
|
||||||
|
ChallengerWins
|
||||||
|
)
|
||||||
|
|
||||||
|
var winners = [...][]byte{
|
||||||
|
NoWinner: []byte("null"),
|
||||||
|
DealerWins: []byte("0"),
|
||||||
|
ChallengerWins: []byte("1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Winner) MarshalJSON() ([]byte, error) {
|
||||||
|
return winners[*w], nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user