unexport game state fields

This commit is contained in:
Branden J Brown 2024-01-21 04:16:48 -06:00
parent bc1685d5ee
commit 46563f9115
3 changed files with 80 additions and 80 deletions

View File

@ -8,28 +8,28 @@ import (
) )
type Game struct { type Game struct {
RNG RNG rng RNG
PP [2]Player players [2]Player
Shells []bool shells []bool
Round uint round uint
Group uint group uint
Turn uint turn uint
HP int8 hp int8
Damage int8 damage int8
Reveal bool reveal bool
Prev *bool prev *bool
// Backing storage for shells. // Backing storage for shells.
ShellArray [8]bool shellArray [8]bool
} }
// New creates a new game started at round 1. // New creates a new game started at round 1.
func New(dealer, challenger player.ID) *Game { func New(dealer, challenger player.ID) *Game {
g := &Game{ g := &Game{
RNG: NewRNG(), rng: NewRNG(),
PP: [2]Player{ players: [2]Player{
{ID: dealer}, {id: dealer},
{ID: challenger}, {id: challenger},
}, },
} }
g.StartRound() g.StartRound()
@ -38,42 +38,42 @@ func New(dealer, challenger player.ID) *Game {
// StartRound starts the next round of a game. // StartRound starts the next round of a game.
func (g *Game) StartRound() { func (g *Game) StartRound() {
g.HP = int8(g.RNG.Intn(3) + 2) g.hp = int8(g.rng.Intn(3) + 2)
g.PP[0].StartRound(g.HP) g.players[0].StartRound(g.hp)
g.PP[1].StartRound(g.HP) g.players[1].StartRound(g.hp)
g.Round++ g.round++
g.Group = 0 g.group = 0
g.StartGroup() g.StartGroup()
} }
// StartGroup starts the next shell group of a round. // StartGroup starts the next shell group of a round.
func (g *Game) StartGroup() { func (g *Game) StartGroup() {
items := g.RNG.Intn(4) + 1 items := g.rng.Intn(4) + 1
g.PP[0].StartGroup(&g.RNG, items) g.players[0].StartGroup(&g.rng, items)
g.PP[1].StartGroup(&g.RNG, items) g.players[1].StartGroup(&g.rng, items)
shells := g.RNG.Intn(6) + 2 shells := g.rng.Intn(6) + 2
for i := 0; i < shells/2; i++ { for i := 0; i < shells/2; i++ {
g.ShellArray[i] = true g.shellArray[i] = true
} }
for i := shells / 2; i < shells; i++ { for i := shells / 2; i < shells; i++ {
g.ShellArray[i] = false g.shellArray[i] = false
} }
g.Shells = g.ShellArray[:shells] g.shells = g.shellArray[:shells]
ShuffleSlice(&g.RNG, g.Shells) ShuffleSlice(&g.rng, g.shells)
g.Group++ g.group++
g.Turn = 0 g.turn = 0
g.Prev = nil g.prev = nil
g.NextTurn() g.NextTurn()
} }
// NextTurn advances the turn. // NextTurn advances the turn.
func (g *Game) NextTurn() { func (g *Game) NextTurn() {
g.Turn++ g.turn++
g.Damage = 1 g.damage = 1
g.Reveal = false g.reveal = false
cur := g.CurrentPlayer() cur := g.CurrentPlayer()
skip := cur.Cuffs == CuffedSkip skip := cur.cuffs == CuffedSkip
cur.Cuffs = cur.Cuffs.NextState() cur.cuffs = cur.cuffs.NextState()
if skip { if skip {
g.NextTurn() g.NextTurn()
} }
@ -81,12 +81,12 @@ func (g *Game) NextTurn() {
// CurrentPlayer gets the index of the current player, either 0 or 1. // CurrentPlayer gets the index of the current player, either 0 or 1.
func (g *Game) CurrentPlayer() *Player { func (g *Game) CurrentPlayer() *Player {
return &g.PP[g.Turn%2] return &g.players[g.turn%2]
} }
// Opponent returns the player who is not the current player. // Opponent returns the player who is not the current player.
func (g *Game) Opponent() *Player { func (g *Game) Opponent() *Player {
return &g.PP[g.Turn%2^1] return &g.players[g.turn%2^1]
} }
// Apply uses an item by index for the current player. // Apply uses an item by index for the current player.
@ -94,14 +94,14 @@ func (g *Game) Opponent() *Player {
// Returns ErrWrongTurn if id does not correspond to the current player. // Returns ErrWrongTurn if id does not correspond to the current player.
func (g *Game) Apply(id player.ID, item int) error { func (g *Game) Apply(id player.ID, item int) error {
cur := g.CurrentPlayer() cur := g.CurrentPlayer()
if cur.ID != id { if cur.id != id {
return ErrWrongTurn return ErrWrongTurn
} }
if item < 0 || item >= len(cur.Items) { if item < 0 || item >= len(cur.items) {
return errors.New("item index out of bounds") return errors.New("item index out of bounds")
} }
if cur.Items[item].Apply(g) { if cur.items[item].Apply(g) {
cur.Items[item] = ItemNone cur.items[item] = ItemNone
} }
return nil return nil
} }
@ -110,32 +110,32 @@ func (g *Game) Apply(id player.ID, item int) error {
// This may cause the shotgun to be empty, but does not start the next group // This may cause the shotgun to be empty, but does not start the next group
// if so. // if so.
func (g *Game) PopShell() bool { func (g *Game) PopShell() bool {
g.Prev = &g.Shells[0] g.prev = &g.shells[0]
g.Shells = g.Shells[1:] g.shells = g.shells[1:]
return *g.Prev return *g.prev
} }
// Peek returns the current turn's shell if it is revealed for the player with // Peek returns the current turn's shell if it is revealed for the player with
// the given ID, or nil otherwise. // the given ID, or nil otherwise.
func (g *Game) Peek(id player.ID) *bool { func (g *Game) Peek(id player.ID) *bool {
if len(g.Shells) == 0 || id != g.CurrentPlayer().ID || !g.Reveal { if len(g.shells) == 0 || id != g.CurrentPlayer().id || !g.reveal {
return nil return nil
} }
return &g.Shells[0] return &g.shells[0]
} }
// Empty returns whether the shotgun is empty. // Empty returns whether the shotgun is empty.
func (g *Game) Empty() bool { func (g *Game) Empty() bool {
return len(g.Shells) == 0 return len(g.shells) == 0
} }
// Winner returns the player who won, or nil if the round is not over. // Winner returns the player who won, or nil if the round is not over.
func (g *Game) Winner() *Player { func (g *Game) Winner() *Player {
if g.PP[0].HP <= 0 { if g.players[0].hp <= 0 {
return &g.PP[1] return &g.players[1]
} }
if g.PP[1].HP <= 0 { if g.players[1].hp <= 0 {
return &g.PP[0] return &g.players[0]
} }
return nil return nil
} }
@ -146,7 +146,7 @@ func (g *Game) Winner() *Player {
// Returns ErrWrongTurn if id does not correspond to the current player. // Returns ErrWrongTurn if id does not correspond to the current player.
func (g *Game) Shoot(id player.ID, self bool) error { func (g *Game) Shoot(id player.ID, self bool) error {
cur := g.CurrentPlayer() cur := g.CurrentPlayer()
if cur.ID != id { if cur.id != id {
return ErrWrongTurn return ErrWrongTurn
} }
target := g.Opponent() target := g.Opponent()
@ -155,7 +155,7 @@ func (g *Game) Shoot(id player.ID, self bool) error {
} }
live := g.PopShell() live := g.PopShell()
if live { if live {
target.HP -= g.Damage target.hp -= g.damage
g.NextTurn() g.NextTurn()
} else if !self { } else if !self {
g.NextTurn() g.NextTurn()
@ -167,20 +167,20 @@ func (g *Game) Shoot(id player.ID, self bool) error {
func (g *Game) DTO(id player.ID) serve.Game { func (g *Game) DTO(id player.ID) serve.Game {
return serve.Game{ return serve.Game{
Players: [2]serve.Player{ Players: [2]serve.Player{
g.PP[0].DTO(), g.players[0].DTO(),
g.PP[1].DTO(), g.players[1].DTO(),
}, },
Round: g.Round, Round: g.round,
Damage: g.Damage, Damage: g.damage,
Shell: g.Peek(id), Shell: g.Peek(id),
Previous: g.Prev, Previous: g.prev,
} }
} }
// ShellCounts returns the number of live and blank shells. // ShellCounts returns the number of live and blank shells.
func (g *Game) ShellCounts() serve.ShellCounts { func (g *Game) ShellCounts() serve.ShellCounts {
var counts serve.ShellCounts var counts serve.ShellCounts
for _, s := range g.Shells { for _, s := range g.shells {
if s { if s {
counts.Live++ counts.Live++
} else { } else {

View File

@ -20,15 +20,15 @@ func (i Item) Apply(g *Game) bool {
case ItemNone: case ItemNone:
return false return false
case ItemLens: case ItemLens:
if g.Reveal { if g.reveal {
return false return false
} }
g.Reveal = true g.reveal = true
return true return true
case ItemCig: case ItemCig:
cur := g.CurrentPlayer() cur := g.CurrentPlayer()
if cur.HP < g.HP { if cur.hp < g.hp {
cur.HP++ cur.hp++
return true return true
} }
return false return false
@ -40,16 +40,16 @@ func (i Item) Apply(g *Game) bool {
return true return true
case ItemCuff: case ItemCuff:
opp := g.Opponent() opp := g.Opponent()
if opp.Cuffs != Uncuffed { if opp.cuffs != Uncuffed {
return false return false
} }
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.Damage = 2 g.damage = 2
return true return true
default: default:
panic("shotgun: unknown item") panic("shotgun: unknown item")

View File

@ -8,35 +8,35 @@ import (
) )
type Player struct { type Player struct {
ID player.ID id player.ID
HP int8 hp int8
Items [8]Item items [8]Item
Cuffs CuffState cuffs CuffState
} }
func (p *Player) StartRound(hp int8) { func (p *Player) StartRound(hp int8) {
p.HP = hp p.hp = hp
clear(p.Items[:]) clear(p.items[:])
} }
func (p *Player) StartGroup(rng *RNG, items int) { func (p *Player) StartGroup(rng *RNG, items int) {
for i := 0; i < items; i++ { for i := 0; i < items; i++ {
k := slices.Index(p.Items[:], ItemNone) k := slices.Index(p.items[:], ItemNone)
if k < 0 { if k < 0 {
break break
} }
p.Items[k] = NewItem(rng) p.items[k] = NewItem(rng)
} }
p.Cuffs = Uncuffed p.cuffs = Uncuffed
} }
func (p *Player) DTO() serve.Player { func (p *Player) DTO() serve.Player {
r := serve.Player{ r := serve.Player{
HP: p.HP, HP: p.hp,
Items: make([]string, 0, 8), Items: make([]string, 0, 8),
Cuffs: p.Cuffs != Uncuffed, Cuffs: p.cuffs != Uncuffed,
} }
for _, i := range p.Items { for _, i := range p.items {
s := i.String() s := i.String()
if s == "" { if s == "" {
continue continue