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
|
||||
// game, or nil if none has been.
|
||||
prev *bool
|
||||
// action is a description of the previous action.
|
||||
action action
|
||||
|
||||
// shellArray is the backing storage for shells and prev.
|
||||
shellArray [8]bool
|
||||
@ -188,18 +190,22 @@ func (g *Match) Shoot(id player.ID, self bool) error {
|
||||
target = g.CurrentPlayer()
|
||||
}
|
||||
live := g.popShell()
|
||||
g.action = actShoot
|
||||
if live {
|
||||
target.hp -= g.damage
|
||||
if target.hp <= 0 {
|
||||
target.hp = 0 // in case it goes negative
|
||||
g.action = actChallengerWins
|
||||
// If the target is the challenger, the match is over as well.
|
||||
if target == &g.players[1] {
|
||||
g.action = actDealerWins
|
||||
g.round = 3
|
||||
}
|
||||
return ErrRoundEnded
|
||||
}
|
||||
}
|
||||
if g.Empty() {
|
||||
g.action = actGameEnd
|
||||
return ErrGameEnded
|
||||
}
|
||||
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 {
|
||||
switch id {
|
||||
case g.players[0].id:
|
||||
g.action = actDealerConcedes
|
||||
g.players[0].hp = 0
|
||||
case g.players[1].id:
|
||||
g.action = actChallengerConcedes
|
||||
g.players[1].hp = 0
|
||||
default:
|
||||
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.
|
||||
func (g *Match) DTO(id player.ID) serve.Game {
|
||||
var live, blank int
|
||||
if g.turn == 1 {
|
||||
if g.action.gameStart() {
|
||||
live = len(g.shells) / 2
|
||||
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{
|
||||
Players: [2]serve.Player{
|
||||
g.players[0].DTO(),
|
||||
g.players[1].DTO(),
|
||||
},
|
||||
Action: g.action.String(),
|
||||
Winner: w,
|
||||
Round: g.round,
|
||||
Dealer: g.CurrentPlayer() == &g.players[0],
|
||||
Damage: g.damage,
|
||||
|
@ -446,6 +446,7 @@ func TestGameDTO(t *testing.T) {
|
||||
g.players[0].DTO(),
|
||||
g.players[1].DTO(),
|
||||
},
|
||||
Action: "Start",
|
||||
Round: g.round,
|
||||
Dealer: false,
|
||||
Damage: g.damage,
|
||||
@ -471,6 +472,7 @@ func TestGameDTO(t *testing.T) {
|
||||
g.players[0].DTO(),
|
||||
g.players[1].DTO(),
|
||||
},
|
||||
Action: "Shoot",
|
||||
Round: g.round,
|
||||
Dealer: true,
|
||||
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)
|
||||
}
|
||||
}
|
||||
// TODO(zeph): many more tests about actions
|
||||
}
|
||||
|
||||
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:
|
||||
return false
|
||||
case ItemLens:
|
||||
if g.reveal {
|
||||
return false
|
||||
}
|
||||
// Lenses are always used, even if they have no effect.
|
||||
g.action = actLens
|
||||
g.reveal = true
|
||||
return true
|
||||
case ItemCig:
|
||||
@ -31,10 +30,13 @@ func (i Item) Apply(g *Match) bool {
|
||||
cur.hp++
|
||||
// Cigs are always used, even if they have no effect.
|
||||
}
|
||||
g.action = actCig
|
||||
return true
|
||||
case ItemBeer:
|
||||
g.popShell()
|
||||
g.action = actBeer
|
||||
if g.Empty() {
|
||||
g.action = actBeerGameEnd
|
||||
g.NextGame()
|
||||
}
|
||||
return true
|
||||
@ -43,12 +45,14 @@ func (i Item) Apply(g *Match) bool {
|
||||
if opp.cuffs != Uncuffed {
|
||||
return false
|
||||
}
|
||||
g.action = actCuff
|
||||
opp.cuffs = Cuffed
|
||||
return true
|
||||
case ItemKnife:
|
||||
if g.damage != 1 {
|
||||
return false
|
||||
}
|
||||
g.action = actKnife
|
||||
g.damage = 2
|
||||
return true
|
||||
default:
|
||||
@ -56,7 +60,8 @@ func (i Item) Apply(g *Match) bool {
|
||||
}
|
||||
}
|
||||
|
||||
var itemNames = [...]string{"", "🔍", "🚬", "🍺", "👮", "🔪"}
|
||||
|
||||
func (i Item) String() string {
|
||||
s := [...]string{"", "🔍", "🚬", "🍺", "👮", "🔪"}
|
||||
return s[i]
|
||||
return itemNames[i]
|
||||
}
|
||||
|
23
serve/dto.go
23
serve/dto.go
@ -7,6 +7,10 @@ type Game struct {
|
||||
// Players is the players in the game.
|
||||
// The dealer is always the first player.
|
||||
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 int8 `json:"round"`
|
||||
// Dealer indicates whether the current player is the dealer.
|
||||
@ -45,3 +49,22 @@ type GameStart struct {
|
||||
ID GameID `json:"id"`
|
||||
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