diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2025-07-04 10:08:58 -0400 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-07-06 11:49:19 -0400 |
| commit | 2289d2fb1fd7f6cbedbb1c275c4e82e9763c7b87 (patch) | |
| tree | 909530b995e5de3441fef5b3c1ca344f11241f5b | |
| parent | a1068681725311ab22facfc801168a80fd4d6b05 (diff) | |
| download | muhqs-game-2289d2fb1fd7f6cbedbb1c275c4e82e9763c7b87.tar.gz muhqs-game-2289d2fb1fd7f6cbedbb1c275c4e82e9763c7b87.zip | |
make draft more flexible
| -rw-r--r-- | go/game/draft.go | 109 |
1 files changed, 85 insertions, 24 deletions
diff --git a/go/game/draft.go b/go/game/draft.go index f180e0ca..53090976 100644 --- a/go/game/draft.go +++ b/go/game/draft.go @@ -1,12 +1,15 @@ package game import ( + "errors" "fmt" "log" - "math/rand" "strings" + "sync" ) +// A Draft coordinating drafting among multiple players and bots. +// TODO: support P variable in draft description. type Draft struct { players []*Player cardsPerPack int @@ -15,42 +18,72 @@ type Draft struct { sets []SetIdentifier } -func NewDraftFromDesc(players []*Player, desc string) *Draft { +var ( + ErrMissingDraftSets = errors.New("missing draft sets") +) + +func NewDraftFromDesc(players []*Player, desc string) (*Draft, error) { + if !strings.ContainsRune(desc, ':') { + return nil, ErrMissingDraftSets + } var setList string s := strings.Split(desc, ":") desc, setList = s[0], s[1] if sets, err := SetListToSets(setList); err != nil { - log.Fatal(err) + return nil, err } else { return NewDraft(players, desc, sets) } - // not reachable - return nil } -func NewDraft(players []*Player, desc string, sets []SetIdentifier) *Draft { +func NewDraft(players []*Player, desc string, sets []SetIdentifier) (*Draft, error) { d := &Draft{players: players, sets: sets} - d.parseDesc(desc) + err := d.parseDesc(desc) log.Printf("created new draft %v\n", d) - return d + return d, err } -func (d *Draft) parseDesc(desc string) { - fmt.Sscanf(desc, "%dx[%d;%d]", &d.packsPerPlayer, &d.cardsPerPack, &d.packSize) +func (d *Draft) parseDesc(desc string) error { + // strings.ReplaceAll(desc, "P", fmt.Sprintf("%d", len(d.players))) + _, err := fmt.Sscanf(desc, "%dx[%d;%d]", &d.packsPerPlayer, &d.cardsPerPack, &d.packSize) + return err +} + +func (d *Draft) Valid() bool { + return !(d.packSize < d.cardsPerPack*len(d.players)) } func (d *Draft) PackSize() int { return d.packSize } -func (d *Draft) Valid() bool { - return !(d.packSize < d.cardsPerPack*len(d.players)) +func (d *Draft) PacksPerPlayer() int { + return d.packsPerPlayer } -// run the draft -// This method only returns after the draft has finished. -func (d *Draft) Run() { +func (d *Draft) CardsPerPack() int { + return d.cardsPerPack +} + +func (d *Draft) PlayerNames() []string { + n := make([]string, 0, len(d.players)) + for i, p := range d.players { + n[i] = p.Name + } + return n +} + +func (d *Draft) Desc() string { + return fmt.Sprintf("%dx[%d;%d]", d.packsPerPlayer, d.cardsPerPack, d.packSize) +} + +func (d *Draft) Sets() []SetIdentifier { + return d.sets +} + +// PreparePacks returns enough random packs suitable to run the draft. +func (d *Draft) PreparePacks() []PileOfCards { pool := NewPileOfCards() requiredCards := d.packsPerPlayer * d.packSize for pool.Size() < requiredCards { @@ -68,21 +101,45 @@ func (d *Draft) Run() { nPlayers := len(d.players) packs := make([]PileOfCards, 0, nPlayers*d.packsPerPlayer) - for range len(d.players) * d.packsPerPlayer { + for range nPlayers * d.packsPerPlayer { pack := NewRandomPackFromPool(pool, d.packSize) packs = append(packs, pack) } + return packs +} + +// DealRound concurrently prompts the players for the current pick. +// The pack `i` and the pick `j` determine who is prompted with a specific pack. +func (d *Draft) DealRound(i, j int, packs []PileOfCards) { + nPlayers := len(d.players) + var wg sync.WaitGroup + wg.Add(nPlayers) + for k, player := range d.players { + go func() { + pack := packs[i*nPlayers+(k+j)%nPlayers] + pickPrompt := newDraftPickPrompt(pack) + log.Printf("prompt %s for a pick from pack %d\n", player.Name, i*nPlayers+(k+j)%nPlayers) + pick, err := prompt(player.Ctrl, pickPrompt) + // select a random card on error + if err != nil { + pick = newRandomDraftPick(player, pickPrompt) + } + pick.resolve(player.gameState) + wg.Done() + }() + } + wg.Wait() +} +// Run executes the draft. +// This method only returns after the draft has finished. +func (d *Draft) Run() { + packs := d.PreparePacks() + nPlayers := len(d.players) log.Printf("%d %d %d\n", d.packsPerPlayer, d.cardsPerPack, len(d.players)) for i := range d.packsPerPlayer { - for j := range d.cardsPerPack * len(d.players) { - for k, player := range d.players { - pack := packs[i*nPlayers+(k+j)%nPlayers] - pickPrompt := newDraftPickPrompt(pack) - log.Printf("prompt %s for a pick from pack %d\n", player.Name, i*nPlayers+(k+j)%nPlayers) - pick := prompt(player.Ctrl, pickPrompt) - pick.resolve(player.gameState) - } + for j := range d.cardsPerPack * nPlayers { + d.DealRound(i, j, packs) } } } @@ -94,6 +151,10 @@ func (d *Draft) AddPlayer(p *Player) { d.players = append(d.players, p) } +func (d *Draft) Players() []*Player { + return d.players +} + type randomDraftAiCtrl struct { ai *Player pack PileOfCards |
