Add pets for tiers 1-3.

This commit is contained in:
Greyson Parrelli
2026-07-23 00:10:17 -04:00
parent 612a4e6227
commit 8d43bbf61e
14 changed files with 1915 additions and 406 deletions
+157 -19
View File
@@ -84,24 +84,60 @@ func TestTurnValidation(t *testing.T) {
}
}
func TestDiscardConvertsToApples(t *testing.T) {
func TestSellConvertsToApples(t *testing.T) {
g, _, _ := testGame(t)
p := current(g)
// Sell a plain pet (no sell effect) for exactly one apple.
plain := g.pet("Plain", 2)
p.Deck = append(p.Deck, plain)
if err := g.Sell(p.ID, []string{plain.ID}); err != nil {
t.Fatal(err)
}
if p.Coins != CoinsPerRound-1 {
t.Fatalf("sell should cost 1 coin, coins=%d", p.Coins)
}
if p.PetCount() != 0 || len(p.Deck) != 1 || p.Deck[0].Food != FoodApple {
t.Fatalf("sold pet should become an apple: %+v", p.Deck)
}
}
func TestSellDuckAddsExtraApple(t *testing.T) {
g, _, _ := testGame(t)
p := current(g)
duck := g.tier1(t, "Duck")
p.Deck = append(p.Deck, duck)
if err := g.Sell(p.ID, []string{duck.ID}); err != nil {
t.Fatal(err)
}
apples := 0
for _, c := range p.Deck {
if c.Food == FoodApple {
apples++
}
}
if apples != 2 {
t.Fatalf("selling a Duck should yield 2 apples (1 base + 1 effect), got %d", apples)
}
}
func TestBuyOtterAddsApple(t *testing.T) {
g, _, _ := testGame(t)
p := current(g)
g.ShopRow[0] = g.tier1(t, "Otter")
if err := g.Buy(p.ID, 0); err != nil {
t.Fatal(err)
}
// Skip opponent back to p.
if err := g.Buy(current(g).ID, 0); err != nil {
t.Fatal(err)
hasOtter, apples := false, 0
for _, c := range p.Deck {
if c.Name == "Otter" {
hasOtter = true
}
if c.Food == FoodApple {
apples++
}
}
if err := g.Discard(p.ID, deckIDs(p, Card.IsPet)); err != nil {
t.Fatal(err)
}
if p.Coins != CoinsPerRound-2 {
t.Fatalf("discard should cost 1 coin, coins=%d", p.Coins)
}
if p.PetCount() != 0 || len(p.Deck) != 1 || p.Deck[0].Food != FoodApple {
t.Fatalf("discarded pet should become an apple: %+v", p.Deck)
if !hasOtter || apples != 1 {
t.Fatalf("buying an Otter should also add an apple: %+v", p.Deck)
}
}
@@ -111,7 +147,7 @@ func TestTradeInThreeMatchingSuits(t *testing.T) {
// Hand p three same-suit tier-1 pets directly.
for range 3 {
c := g.pet("Fodder", 1)
c.Suit = SuitMoon
c.Suit = SuitBlue
p.Deck = append(p.Deck, c)
}
nextDeckBefore := len(g.ShopDecks[1])
@@ -150,11 +186,113 @@ func TestTradeInThreeMatchingSuits(t *testing.T) {
}
}
// Fish fires its Triple effect when traded in, and a pet received from the
// trade fires its Buy effect.
func TestTradeTriggersTripleAndBuyEffects(t *testing.T) {
g, _, _ := testGame(t)
p := current(g)
countApples := func() int {
n := 0
for _, c := range p.Deck {
if c.Food == FoodApple {
n++
}
}
return n
}
var ids []string
for range 3 {
f := g.tier1(t, "Fish")
f.Suit = SuitYellow
p.Deck = append(p.Deck, f)
ids = append(ids, f.ID)
}
if err := g.TradeStart(p.ID, ids); err != nil {
t.Fatal(err)
}
if countApples() != 3 {
t.Fatalf("each traded Fish should add an apple, got %d", countApples())
}
// Rig the revealed options so the chosen card is an Otter (Buy effect).
g.Pending.Options[0] = g.tier1(t, "Otter")
if err := g.TradeChoose(p.ID, 0); err != nil {
t.Fatal(err)
}
if countApples() != 4 {
t.Fatalf("trade-received Otter should fire its Buy effect, got %d apples", countApples())
}
}
func TestBuyWormAddsTwoApples(t *testing.T) {
g, _, _ := testGame(t)
p := current(g)
g.ShopRow[0] = g.realPet(t, "Worm")
if err := g.Buy(p.ID, 0); err != nil {
t.Fatal(err)
}
apples := 0
for _, c := range p.Deck {
if c.Food == FoodApple {
apples++
}
}
if apples != 2 {
t.Fatalf("buying a Worm should add 2 apples, got %d", apples)
}
}
// Swan's Triple refreshes a spent gold, but only from round 3 on.
func TestSwanTripleRefreshesGold(t *testing.T) {
for _, tc := range []struct {
round int
wantCoins int
}{
{round: 1, wantCoins: CoinsPerRound - 1}, // too early: coin stays spent
{round: 3, wantCoins: CoinsPerRound}, // refreshed (capped at 3)
} {
g, _, _ := testGame(t)
g.Round = tc.round
p := current(g)
var ids []string
for range 3 {
s := g.realPet(t, "Swan")
s.Suit = SuitRed
p.Deck = append(p.Deck, s)
ids = append(ids, s.ID)
}
if err := g.TradeStart(p.ID, ids); err != nil {
t.Fatal(err)
}
if p.Coins != tc.wantCoins {
t.Fatalf("round %d: coins after swan trade = %d, want %d", tc.round, p.Coins, tc.wantCoins)
}
}
}
// Giraffe's Battle Prep hands out apples the moment arranging begins.
func TestGiraffeBattlePrep(t *testing.T) {
g, p1, _ := testGame(t)
p1.Deck = append(p1.Deck, g.realPet(t, "Giraffe"))
spendAllCoins(t, g)
if g.Phase != PhaseArrange {
t.Fatalf("expected arrange, got %s", g.Phase)
}
apples := 0
for _, c := range p1.Deck {
if c.Food == FoodApple {
apples++
}
}
if apples != 2 {
t.Fatalf("giraffe should add 2 apples at battle prep, got %d", apples)
}
}
func TestTradeRequiresMatchingSuit(t *testing.T) {
g, _, _ := testGame(t)
p := current(g)
a, b, c := g.pet("A", 1), g.pet("B", 1), g.pet("C", 1)
a.Suit, b.Suit, c.Suit = SuitSun, SuitSun, SuitMoon
a.Suit, b.Suit, c.Suit = SuitRed, SuitRed, SuitBlue
p.Deck = append(p.Deck, a, b, c)
if err := g.TradeStart(p.ID, []string{a.ID, b.ID, c.ID}); err == nil {
t.Fatal("mismatched suits should be rejected")
@@ -170,7 +308,7 @@ func TestTradeBlockedOnFinalRound(t *testing.T) {
p := current(g)
for range 3 {
c := g.pet("Fodder", 1)
c.Suit = SuitMoon
c.Suit = SuitBlue
p.Deck = append(p.Deck, c)
}
if err := g.TradeStart(p.ID, deckIDs(p, nil)); err == nil {
@@ -206,10 +344,10 @@ func TestForcedDiscardOverPetLimit(t *testing.T) {
t.Fatalf("player with %d pets must be forced to discard, got phase %s", MaxPets+2, g.Phase)
}
// Wrong count rejected.
if err := g.CleanupDiscard(p1.ID, deckIDs(p1, Card.IsPet)[:1]); err == nil {
t.Fatal("must discard exactly the excess")
if err := g.CleanupSell(p1.ID, deckIDs(p1, Card.IsPet)[:1]); err == nil {
t.Fatal("must sell exactly the excess")
}
if err := g.CleanupDiscard(p1.ID, deckIDs(p1, Card.IsPet)[:2]); err != nil {
if err := g.CleanupSell(p1.ID, deckIDs(p1, Card.IsPet)[:2]); err != nil {
t.Fatal(err)
}
if p1.PetCount() != MaxPets {
@@ -313,7 +451,7 @@ func TestViewHidesSecrets(t *testing.T) {
// Pending trade options hidden from the opponent.
for range 3 {
c := g.pet("Fodder", 1)
c.Suit = SuitLeaf
c.Suit = SuitYellow
p1.Deck = append(p1.Deck, c)
}
g.Turn = p1.Seat