Initial pass at unicorn pack.

This commit is contained in:
Greyson Parrelli
2026-07-25 00:37:44 -04:00
parent f3783b44bf
commit a1d57fe1dd
21 changed files with 2419 additions and 44 deletions
+31 -2
View File
@@ -35,12 +35,12 @@ import (
// Action is one move the bot wants to make, mirroring the client protocol.
type Action struct {
Type string // buy | buyAvocado | sell | trade | tradeChoose | pass | arrange | ready | revealChoose
Type string // buy | buyAvocado | sell | trade | tradeChoose | pass | arrange | ready | revealChoose | sacrificeChoose
Row int // buy / buyAvocado
Cards []string // sell / trade
Pick int // tradeChoose
Order []string // arrange
CardID string // revealChoose (Cockatoo): the pet to reveal
CardID string // revealChoose (Cockatoo) / sacrificeChoose (Water of Youth): the pet
}
// Bot is a computer player at a fixed difficulty level.
@@ -62,6 +62,12 @@ func (b *Bot) Act(v *game.View, mem *Memory) *Action {
me := &v.Players[v.YouSeat]
switch v.Phase {
case game.PhaseShop:
if v.PendingSacrifice != nil {
if v.PendingSacrifice.PlayerID == me.ID {
return b.decideSacrifice(v)
}
return nil
}
if v.PendingReveal != nil {
if v.PendingReveal.PlayerID == me.ID {
return b.decideReveal(v)
@@ -107,6 +113,26 @@ func (b *Bot) decideReveal(v *game.View) *Action {
return &Action{Type: "revealChoose", CardID: best}
}
// decideSacrifice resolves Water of Youth (Unicorn pack): give up the
// lowest-value eligible pet to upgrade into a next-tier card.
func (b *Bot) decideSacrifice(v *game.View) *Action {
me := &v.Players[v.YouSeat]
worst, worstVal := "", math.Inf(1)
for _, id := range v.PendingSacrifice.Options {
for _, c := range me.Deck {
if c.ID == id {
if val := keepValue(c); val < worstVal {
worst, worstVal = id, val
}
}
}
}
if worst == "" && len(v.PendingSacrifice.Options) > 0 {
worst = v.PendingSacrifice.Options[0]
}
return &Action{Type: "sacrificeChoose", CardID: worst}
}
// 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 {
@@ -116,6 +142,9 @@ func Pending(v *game.View) bool {
me := &v.Players[v.YouSeat]
switch v.Phase {
case game.PhaseShop:
if v.PendingSacrifice != nil {
return v.PendingSacrifice.PlayerID == me.ID
}
if v.PendingReveal != nil {
return v.PendingReveal.PlayerID == me.ID
}