Add engine validation script with attack_coin_status effect handler

- Add attack_coin_status effect handler for coin-flip status conditions
  (e.g., Thunder Shock paralysis on heads)
- Create comprehensive engine_validation.py script (~1250 lines) that
  validates game engine behavior with 29 test cases:
  - Illegal moves (attack without energy, wrong turn, evolution rules)
  - Energy mechanics (attachment limits, cost validation)
  - Weakness calculation (+20 additive mode)
  - Status conditions (paralysis blocks actions, poison damage)
  - Knockout flow (points, forced actions, state cleanup)
  - Win conditions (4 points triggers game over)
- Update game_walkthrough.py Thunder Shock to use new effect handler
- Interactive prompts between sections (Enter to continue, q to quit)
- Uses seed=42 for deterministic, reproducible coin flips

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-01-28 00:15:12 -06:00
parent 4cdb544162
commit 5cf2198542
3 changed files with 1308 additions and 2 deletions

View File

@ -445,6 +445,53 @@ def handle_remove_status(ctx: EffectContext) -> EffectResult:
# See: engine.py _execute_attack() for the TODO on integrating this
@effect_handler("attack_coin_status")
def handle_attack_coin_status(ctx: EffectContext) -> EffectResult:
"""Flip a coin; on heads, apply a status condition.
This is the common "flip coin, apply status on heads" pattern used by many
attacks like Thunder Shock (paralysis on heads).
Params:
status (str): StatusCondition to apply on heads. Required.
One of: poisoned, burned, asleep, paralyzed, confused
Target:
Opponent's active Pokemon.
Returns:
Success with flip result and status application details.
"""
status_str = ctx.get_str_param("status")
if not status_str:
return EffectResult.failure("No status specified")
try:
status = StatusCondition(status_str)
except ValueError:
return EffectResult.failure(f"Invalid status: {status_str}")
flip = ctx.flip_coin()
target = ctx.get_target_pokemon()
if target is None:
return EffectResult.failure("No valid target")
if flip:
target.add_status(status)
return EffectResult.success_result(
f"Flipped heads - applied {status.value}!",
effect_type=EffectType.STATUS,
details={"flip": "heads", "status": status.value, "target_id": target.instance_id},
)
else:
return EffectResult.success_result(
"Flipped tails - no effect",
effect_type=EffectType.STATUS,
details={"flip": "tails", "status": None},
)
@effect_handler("coin_flip_damage")
def handle_coin_flip_damage(ctx: EffectContext) -> EffectResult:
"""Deal damage based on coin flip results.

File diff suppressed because it is too large Load Diff

View File

@ -255,8 +255,8 @@ def create_card_definitions() -> dict[str, CardDefinition]:
name="Thunder Shock",
cost=[EnergyType.LIGHTNING],
damage=30,
effect_id="coin_flip_damage",
effect_params={"base_damage": 30, "bonus_on_heads": 0, "apply_paralysis": True},
effect_id="attack_coin_status",
effect_params={"status": "paralyzed"},
effect_description="Flip a coin. If heads, the Defending Pokemon is now Paralyzed.",
),
],