diff options
| author | Florian Fischer <florian.fischer@muhq.space> | 2025-07-04 10:08:58 -0400 |
|---|---|---|
| committer | Florian Fischer <florian.fischer@muhq.space> | 2025-08-20 15:57:23 +0200 |
| commit | ba45b9ce0f916a8d5f4cca2233fc421ce989c8df (patch) | |
| tree | 9e9a1e38ac770216a90b8046ba47a53636422e2a | |
| parent | 9dcbe08bc2fddb736891849aabc3d748f1426e01 (diff) | |
| download | muhqs-game-ba45b9ce0f916a8d5f4cca2233fc421ce989c8df.tar.gz muhqs-game-ba45b9ce0f916a8d5f4cca2233fc421ce989c8df.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 |
