package main import ( "errors" "math" "math/rand" "git.fromouter.space/mcg/draft" "git.fromouter.space/mcg/draft/mlp" "git.fromouter.space/mcg/mlp-server-tools/draftbot/bot" ) // Session-related errors var ( ErrTooManyPlayers = errors.New("too many players") ErrNotEnoughPlayers = errors.New("not enough players") ) type session struct { Options draftOptions Players map[string]*draft.Player Bots []*bot.Bot Pod *draft.Pod // Channels for communication while the session is running exit chan bool } // Types of drafts const ( draftBlock = "block" draftSet = "set" draftCube = "cube" draftI8PCube = "i8pcube" ) // Ways in which players can be positioned along the draft pod const ( posRandom = "random" // Place players randomly posEven = "even" // Place players spaced as evenly as possible ) type draftOptions struct { Type string `json:"type"` Positioning string `json:"positioning"` // Block draft properties Block string `json:"block,omitempty"` // Set draft properties Set string `json:"set,omitempty"` // Cube draft properties CubeURL string `json:"cube_url,omitempty"` PackSize int `json:"pack_size,omitempty"` // I8PCube properties MainCount int `json:"main_count,omitempty"` ProblemCount int `json:"problem_count,omitempty"` // Shared PackCount int `json:"pack_count,omitempty"` // Set and Cube } func (do draftOptions) getProvider() (draft.PackProvider, error) { switch do.Type { case draftBlock: return mlp.BlockPacks(mlp.BlockID(do.Block)) case draftSet: set, err := mlp.LoadSetHTTP(mlp.SetID(do.Set)) if err != nil { return nil, err } return draft.PacksFromSet(do.PackCount, set), nil case draftCube: cards, err := loadCube(do.CubeURL) if err != nil { return nil, err } cube := &draft.GenericCube{ Cards: cards, PackSize: do.PackSize, } return draft.PacksFromSet(do.PackCount, cube), nil case draftI8PCube: cube, err := loadI8PCube(do.CubeURL) if err != nil { return nil, err } return cube.PackProvider(do.MainCount, do.ProblemCount), nil } return nil, errors.New("unknown draft type") } func newSession(playerCount int, opt draftOptions) (*session, error) { // Get pack provider for given options provider, err := opt.getProvider() if err != nil { return nil, err } return &session{ Options: opt, Pod: draft.MakePod(playerCount, provider), exit: make(chan bool), }, nil } func (s *session) Start() error { // Figure out how many players there are vs spots to be filled spots := len(s.Pod.Players) players := len(s.Players) if players > spots { return ErrTooManyPlayers } if players < 1 { return ErrNotEnoughPlayers } // Assign players to their spot on the drafting pod playerSpot := make(map[int]string) switch s.Options.Positioning { case posRandom: // Assign a random number to each player for pname := range s.Players { var pos int for { pos = rand.Intn(spots) // Make sure chosen number wasn't already picked if _, ok := playerSpot[pos]; !ok { break } } playerSpot[pos] = pname } case posEven: // Space players evenly playerRatio := float64(spots) / float64(players) i := 0 for name := range s.Players { pos := int(math.Floor(playerRatio * float64(i))) playerSpot[pos] = name i++ } } // Assign player instances and make bots where needed for i := range s.Pod.Players { if name, ok := playerSpot[i]; ok { s.Players[name] = s.Pod.Players[i] } else { s.Bots = append(s.Bots, bot.MakeBot(s.Pod.Players[i])) } } // Notify players of the order //TODO // Start handling packs go s.handlePicks() return nil } func (s *session) handlePicks() { // Pack loop, this `for` handles an entire draft session for { err := s.Pod.OpenPacks() if err != nil { if err == draft.ErrNoPacksLeft { // Notify players that the draft is over //TODO return } else { //TODO } } // Pick loop, this `for` handles exactly one round of packs for { // Notify players that their next pack is ready //TODO // Make bots pick their cards for _, bot := range s.Bots { bot.PickNext() } select { case <-s.Pod.ReadyNextPick: // Pass packs around err := s.Pod.NextPacks() if err != nil { if err == draft.ErrNoPendingPack { // No more picks to do for this round of packs, go to next break } else { // Something wrong! //TODO } } case <-s.Pod.ReadyNextPack: // Break of pick loop, get next packs break case <-s.exit: // Room closed, exit early return } } } }