Add support for up to 6 players.

This commit is contained in:
Greyson Parrelli
2026-07-28 07:36:09 -04:00
parent e542118175
commit a4f5f6910d
38 changed files with 2306 additions and 713 deletions
+21 -10
View File
@@ -19,27 +19,35 @@ type ctx struct {
}
func newCtx(v *game.View, m *Memory) *ctx {
cx := &ctx{v: v, m: m, me: &v.Players[v.YouSeat], oppSeat: m.Opp.Seat, pools: map[int][]game.Card{}}
cx := &ctx{v: v, m: m, me: v.PlayerView(v.YouSeat), oppSeat: v.YourOpponent, pools: map[int][]game.Card{}}
// The round's pairing says exactly who the bot is preparing for, so it
// plans against that one rival even at a six-player table. Falling back to
// any other seat keeps a malformed view from wedging the bot.
if cx.oppSeat == cx.me.Seat || cx.oppSeat < 0 {
// Memory hasn't observed yet (shouldn't happen in practice).
for _, p := range v.Players {
if p.Seat != v.YouSeat {
cx.oppSeat = p.Seat
break
}
}
}
return cx
}
// opp is the model of the opponent this round's battle is against.
func (cx *ctx) opp() *OppModel { return cx.m.Opp(cx.oppSeat) }
func (cx *ctx) nextSimID() string {
cx.simID++
return fmt.Sprintf("sim-%d", cx.simID)
}
// unseenPool lists the printed cards of a tier that the bot cannot account
// for anywhere it can see — its own deck, the opponent model, the shop row.
// for anywhere it can see — its own deck, every opponent model, the shop row.
// Hidden opponent cards are drawn from this pool, so the bot's guesses
// respect card counting without peeking at the real decks.
// respect card counting without peeking at the real decks. It counts against
// the whole table's known cards, and against the combined contents of every
// pack in play, which is what a human counting cards would be working from.
func (cx *ctx) unseenPool(tier int) []game.Card {
if pool, ok := cx.pools[tier]; ok {
return pool
@@ -53,14 +61,16 @@ func (cx *ctx) unseenPool(tier int) []game.Card {
for _, c := range cx.me.Deck {
note(c)
}
for _, c := range cx.m.Opp.Known {
note(c)
for _, opp := range cx.m.Opps {
for _, c := range opp.Known {
note(c)
}
}
for _, c := range cx.v.ShopRow {
note(c)
}
var pool []game.Card
for _, c := range game.TierContentsForPack(cx.v.Pack, tier) {
for _, c := range game.TierContentsForPacks(cx.v.Packs, tier) {
if seen[c.Name] > 0 {
seen[c.Name]--
continue
@@ -75,10 +85,11 @@ func (cx *ctx) unseenPool(tier int) []game.Card {
// known cards as-is, hidden cards drawn from the unseen pool of their tier
// (or their named template, when a pick was later revealed).
func (cx *ctx) sampleOppDeck() []game.Card {
deck := append([]game.Card(nil), cx.m.Opp.Known...)
for _, h := range cx.m.Opp.Hidden {
opp := cx.opp()
deck := append([]game.Card(nil), opp.Known...)
for _, h := range opp.Hidden {
var c game.Card
if t, ok := templateByName(cx.v.Pack, h.Name); ok {
if t, ok := templateByName(cx.v.Packs, h.Name); ok {
c = t
} else if pool := cx.unseenPool(h.Tier); len(pool) > 0 {
c = pool[rand.IntN(len(pool))]