draft/pod.go

192 lines
3.9 KiB
Go

package draft
import "errors"
// Errors that can happen during draft
var (
ErrNotInPack = errors.New("card picked not in pack")
ErrNoPacksLeft = errors.New("no packs left to open")
ErrNoPendingPack = errors.New("no packs received from other players")
)
// PodDirection is the direction packs are passed between players
type PodDirection string
// All rotations
var (
PRClockwise PodDirection = "left"
PRAnticlockwise PodDirection = "right"
)
// Pod is a group of players drafting packs/cubes
type Pod struct {
Players []*Player
Direction PodDirection
ReadyNextPick chan bool
ReadyNextPack chan bool
}
// Player is a single player partecipating in a pod
type Player struct {
// Packs and picks
CurrentPack Pack
Packs []Pack
Picks []Card
// Nearby players
left *Player
right *Player
// Pod the player is in
pod *Pod
// Channels for passing stuff around
newpack chan Pack
}
// MakePod creates a pod with a specified number of players and a given set of packs
func MakePod(playerCount int, provider PackProvider) *Pod {
// Make player list
players := make([]*Player, playerCount)
// Make pod
pod := &Pod{
Players: players,
Direction: PRAnticlockwise,
ReadyNextPick: make(chan bool, 1),
ReadyNextPack: make(chan bool, 1),
}
// Fill players
for i := range players {
players[i] = &Player{
Packs: provider(),
Picks: []Card{},
pod: pod,
newpack: make(chan Pack, playerCount-1),
}
}
// Second loop to fill nearby players
for i := range players {
if i > 0 {
players[i].left = players[i-1]
} else {
players[i].left = players[playerCount-1]
}
players[i].right = players[(i+1)%playerCount]
}
return pod
}
// OpenPacks opens the next pack of each player
func (p *Pod) OpenPacks() error {
for _, p := range p.Players {
err := p.OpenPack()
if err != nil {
return err
}
}
p.flipDirection()
return nil
}
// NextPacks makes player exchange packs for the next pick
func (p *Pod) NextPacks() error {
for _, p := range p.Players {
err := p.NextPack()
if err != nil {
return err
}
}
return nil
}
func (p *Pod) flipDirection() {
if p.Direction == PRClockwise {
p.Direction = PRAnticlockwise
} else {
p.Direction = PRClockwise
}
}
func (p *Pod) checkPicks() {
for _, player := range p.Players {
if len(player.newpack) < 1 {
// Someone still hasn't passed a pack but has cards
return
}
}
// Everyone has new packs to draft
p.ReadyNextPick <- true
}
func (p *Pod) checkEndPack() {
for _, player := range p.Players {
if len(player.CurrentPack) != 0 {
// Someone still has cards to draft
return
}
}
// Everyone needs to open their next pack
p.ReadyNextPack <- true
}
// OpenPack opens the next pack the player has
func (p *Player) OpenPack() error {
if len(p.Packs) < 1 {
return ErrNoPacksLeft
}
p.CurrentPack, p.Packs = p.Packs[0], p.Packs[1:]
return nil
}
// NextPack picks the next pack passed from other players
func (p *Player) NextPack() error {
if len(p.newpack) < 1 {
return ErrNoPendingPack
}
p.CurrentPack = <-p.newpack
return nil
}
// Pick specified what card a player has picked and gives the pack to the next player
func (p *Player) Pick(pick Card) error {
// Check that pack contains specified card
id := -1
for i, card := range p.CurrentPack {
if pick.ID == card.ID {
id = i
break
}
}
if id < 0 {
return ErrNotInPack
}
// Take card from pack and put it into the picks
p.Picks = append(p.Picks, p.CurrentPack[id])
// Remove pick from pack using slice tricks
p.CurrentPack[id] = p.CurrentPack[len(p.CurrentPack)-1]
p.CurrentPack = p.CurrentPack[:len(p.CurrentPack)-1]
// Out of cards! Check if everyone else is as well
if len(p.CurrentPack) == 0 {
p.pod.checkEndPack()
return nil
}
// Send pack to next player
if p.pod.Direction == PRClockwise {
p.left.newpack <- p.CurrentPack
} else {
p.right.newpack <- p.CurrentPack
}
p.pod.checkPicks()
return nil
}