Initial pass at Golden Pack.

This commit is contained in:
Greyson Parrelli
2026-07-24 07:47:05 -04:00
parent e74f983470
commit dd395f4bbf
27 changed files with 2770 additions and 150 deletions
+46 -6
View File
@@ -35,11 +35,13 @@ import (
// Action is one move the bot wants to make, mirroring the client protocol.
type Action struct {
Type string // "buy" | "sell" | "trade" | "tradeChoose" | "pass" | "arrange" | "ready"
Row int // buy
Cards []string // sell / trade
Pick int // tradeChoose
Order []string // arrange
Type string // buy | buyAvocado | sell | trade | tradeChoose | pass | arrange | ready | revealChoose | battleChoose
Row int // buy / buyAvocado
Cards []string // sell / trade
Pick int // tradeChoose
Order []string // arrange
CardID string // revealChoose (Cockatoo): the pet to reveal
Value int // battleChoose (Nurse Shark): Trumpets to spend
}
// Bot is a computer player at a fixed difficulty level.
@@ -61,6 +63,12 @@ func (b *Bot) Act(v *game.View, mem *Memory) *Action {
me := &v.Players[v.YouSeat]
switch v.Phase {
case game.PhaseShop:
if v.PendingReveal != nil {
if v.PendingReveal.PlayerID == me.ID {
return b.decideReveal(v)
}
return nil
}
if v.Pending != nil {
if v.Pending.PlayerID == me.ID {
return b.decideTradeChoose(v, mem)
@@ -78,6 +86,13 @@ func (b *Bot) Act(v *game.View, mem *Memory) *Action {
return b.decideArrange(v, mem)
}
case game.PhaseBattle:
if v.PendingBattle != nil {
if v.PendingBattle.Seat == v.YouSeat {
// Spend as many Trumpets as allowed — more rocks is better.
return &Action{Type: "battleChoose", Value: v.PendingBattle.Max}
}
return nil
}
if !me.Ready {
return &Action{Type: "ready"}
}
@@ -85,6 +100,21 @@ func (b *Bot) Act(v *game.View, mem *Memory) *Action {
return nil
}
// decideReveal picks the highest-power eligible pet for Cockatoo's reveal, for
// the most apples.
func (b *Bot) decideReveal(v *game.View) *Action {
me := &v.Players[v.YouSeat]
best, bestPow := "", -1
for _, id := range v.PendingReveal.Options {
for _, c := range me.Deck {
if c.ID == id && c.Power > bestPow {
best, bestPow = id, c.Power
}
}
}
return &Action{Type: "revealChoose", CardID: best}
}
// Pending reports whether the seat owes the game an action right now — the
// server uses it to decide when to schedule a bot move.
func Pending(v *game.View) bool {
@@ -94,11 +124,21 @@ func Pending(v *game.View) bool {
me := &v.Players[v.YouSeat]
switch v.Phase {
case game.PhaseShop:
if v.PendingReveal != nil {
return v.PendingReveal.PlayerID == me.ID
}
if v.Pending != nil {
return v.Pending.PlayerID == me.ID
}
return v.Turn == v.YouSeat && !me.Ready
case game.PhaseArrange, game.PhaseBattle:
case game.PhaseArrange:
return !me.Ready
case game.PhaseBattle:
// A pending mid-battle decision is owed only by the deciding seat;
// otherwise everyone owes the battle acknowledgement.
if v.PendingBattle != nil {
return v.PendingBattle.Seat == v.YouSeat
}
return !me.Ready
}
return false