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 }