aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2025-07-04 10:08:58 -0400
committerFlorian Fischer <florian.fischer@muhq.space>2025-07-06 11:49:19 -0400
commit2289d2fb1fd7f6cbedbb1c275c4e82e9763c7b87 (patch)
tree909530b995e5de3441fef5b3c1ca344f11241f5b
parenta1068681725311ab22facfc801168a80fd4d6b05 (diff)
downloadmuhqs-game-2289d2fb1fd7f6cbedbb1c275c4e82e9763c7b87.tar.gz
muhqs-game-2289d2fb1fd7f6cbedbb1c275c4e82e9763c7b87.zip
make draft more flexible
-rw-r--r--go/game/draft.go109
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