Break a 9-clash pet deadlock by fainting both combatants.
This commit is contained in:
@@ -440,6 +440,14 @@ func (g *Game) resolveBattle() {
|
|||||||
// The exchange loop terminates: every clash kills at least one pet or
|
// The exchange loop terminates: every clash kills at least one pet or
|
||||||
// is a detected stalemate, and summons/auras are finite. The guard is
|
// is a detected stalemate, and summons/auras are finite. The guard is
|
||||||
// just insurance as effects get richer.
|
// just insurance as effects get richer.
|
||||||
|
//
|
||||||
|
// clashStreak counts how many times the current pair has clashed without
|
||||||
|
// either fainting. Some effects (e.g. eat-an-apple-when-hurt) can make two
|
||||||
|
// pets un-killable, looping forever; if they trade blows deadlockLimit
|
||||||
|
// times in a row, both drop so the battle can proceed. It resets whenever
|
||||||
|
// the matchup changes (a fresh pet enters).
|
||||||
|
const deadlockLimit = 9
|
||||||
|
clashStreak := 0
|
||||||
for range 10_000 {
|
for range 10_000 {
|
||||||
// Reveal until every side has a pet in play or runs out. Play
|
// Reveal until every side has a pet in play or runs out. Play
|
||||||
// effects queue up and resolve after all reveals (simultaneous).
|
// effects queue up and resolve after all reveals (simultaneous).
|
||||||
@@ -525,6 +533,15 @@ func (g *Game) resolveBattle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A fresh pet on either side is a new matchup, so the deadlock streak
|
||||||
|
// starts over.
|
||||||
|
for _, np := range newlyPlayed {
|
||||||
|
if np {
|
||||||
|
clashStreak = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Battle over? A side that couldn't field a pet is out; no further
|
// Battle over? A side that couldn't field a pet is out; no further
|
||||||
// effects resolve.
|
// effects resolve.
|
||||||
anyOut := false
|
anyOut := false
|
||||||
@@ -657,9 +674,25 @@ func (g *Game) resolveBattle() {
|
|||||||
if dealtB > 0 && ua.hasKnockout() {
|
if dealtB > 0 && ua.hasKnockout() {
|
||||||
ub.Damage = max(ub.Damage, ub.Power())
|
ub.Damage = max(ub.Damage, ub.Power())
|
||||||
}
|
}
|
||||||
|
// Deadlock guard: if the pair keeps surviving, count the exchanges and
|
||||||
|
// force a mutual knockout once they hit the limit.
|
||||||
|
deadlocked := false
|
||||||
|
if ua.Alive() && ub.Alive() {
|
||||||
|
clashStreak++
|
||||||
|
if clashStreak >= deadlockLimit {
|
||||||
|
deadlocked = true
|
||||||
|
ua.Damage = max(ua.Damage, ua.Power())
|
||||||
|
ub.Damage = max(ub.Damage, ub.Power())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clashStreak = 0
|
||||||
|
}
|
||||||
clashTxt := fmt.Sprintf("%s's %s and %s's %s trade blows.",
|
clashTxt := fmt.Sprintf("%s's %s and %s's %s trade blows.",
|
||||||
pname(0), ua.Card.Name, pname(1), ub.Card.Name)
|
pname(0), ua.Card.Name, pname(1), ub.Card.Name)
|
||||||
switch da, db := !ua.Alive(), !ub.Alive(); {
|
switch da, db := !ua.Alive(), !ub.Alive(); {
|
||||||
|
case deadlocked:
|
||||||
|
clashTxt = fmt.Sprintf("%s's %s and %s's %s are deadlocked after %d clashes — both faint.",
|
||||||
|
pname(0), ua.Card.Name, pname(1), ub.Card.Name, deadlockLimit)
|
||||||
case da && db:
|
case da && db:
|
||||||
clashTxt += " Both faint."
|
clashTxt += " Both faint."
|
||||||
case da:
|
case da:
|
||||||
|
|||||||
@@ -736,3 +736,28 @@ func TestSkunkStripFaintsDebuffedPet(t *testing.T) {
|
|||||||
t.Fatalf("skunk should win once the stripped pet faints, got %d", res.WinnerSeat)
|
t.Fatalf("skunk should win once the stripped pet faints, got %d", res.WinnerSeat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Two pets that block every hit could clash forever; the deadlock rule makes
|
||||||
|
// them both faint after 9 clashes so the battle can proceed.
|
||||||
|
func TestBattleDeadlockBreaksAfterNineClashes(t *testing.T) {
|
||||||
|
g, _, _ := testGame(t)
|
||||||
|
wall := func(name string) Card {
|
||||||
|
return Card{
|
||||||
|
ID: g.newCardID(), Kind: KindPet, Name: name, Tier: 1, Power: 3, Suit: SuitRed,
|
||||||
|
Effects: []Effect{{Trigger: TriggerPlay, Action: ActionShieldSelf, Count: 20}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res := forceBattle(t, g, []Card{wall("Wall")}, []Card{wall("Fort")})
|
||||||
|
|
||||||
|
clashes := eventsOfType(res, "clash")
|
||||||
|
if len(clashes) != 9 {
|
||||||
|
t.Fatalf("deadlock should resolve after 9 clashes, got %d", len(clashes))
|
||||||
|
}
|
||||||
|
last := clashes[len(clashes)-1]
|
||||||
|
if !last.Died[0] || !last.Died[1] {
|
||||||
|
t.Fatalf("the 9th clash should faint both pets: %+v", last)
|
||||||
|
}
|
||||||
|
if res.WinnerSeat != -1 {
|
||||||
|
t.Fatalf("a deadlock is a draw, got winner=%d", res.WinnerSeat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user