diff --git a/draftbot/main.go b/draftbot/main.go index 5d99a85..f5994e2 100644 --- a/draftbot/main.go +++ b/draftbot/main.go @@ -4,9 +4,11 @@ import ( "errors" "flag" "fmt" + "math/rand" "os" "os/signal" "syscall" + "time" "git.fromouter.space/Artificiale/moa/sd" "git.fromouter.space/mcg/cardgage/client/bot" @@ -31,6 +33,9 @@ func main() { }, logger) defer registrar.Deregister() + // Seed RNG + rand.Seed(time.Now().UnixNano()) + // Initialize consul client for service discovery sd.InitClient(*consulAddr) diff --git a/draftbot/session.go b/draftbot/session.go index d96208e..e92e418 100644 --- a/draftbot/session.go +++ b/draftbot/session.go @@ -2,17 +2,28 @@ 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 @@ -23,8 +34,15 @@ const ( 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"` + Type string `json:"type"` + Positioning string `json:"positioning"` // Block draft properties Block string `json:"block,omitempty"` @@ -42,6 +60,7 @@ type draftOptions struct { // Shared PackCount int `json:"pack_count,omitempty"` // Set and Cube + } func (do draftOptions) getProvider() (draft.PackProvider, error) { @@ -84,5 +103,108 @@ func newSession(playerCount int, opt draftOptions) (*session, error) { 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 + } + } + } +}