游戏可以运行
This commit is contained in:
148
card_game/ai.py
Normal file
148
card_game/ai.py
Normal file
@@ -0,0 +1,148 @@
|
||||
"""AI player: simple rule-based decision making."""
|
||||
|
||||
from card_game.config import CARD_DATABASE
|
||||
|
||||
|
||||
class AIPlayer:
|
||||
def __init__(self, battlefield):
|
||||
self.battlefield = battlefield
|
||||
|
||||
def execute_turn(self):
|
||||
actions = []
|
||||
ai = self.battlefield.ai
|
||||
|
||||
actions.extend(self._play_untargeted_orders(ai))
|
||||
actions.extend(self._deploy_units(ai))
|
||||
actions.extend(self._play_targeted_orders(ai))
|
||||
actions.extend(self._move_units(ai))
|
||||
actions.extend(self._attack(ai))
|
||||
|
||||
return actions
|
||||
|
||||
def _play_untargeted_orders(self, ai):
|
||||
actions = []
|
||||
for card in ai.hand[:]:
|
||||
if card.card_type != "order":
|
||||
continue
|
||||
if card.cost > ai.provisions:
|
||||
continue
|
||||
if not self.battlefield.needs_target(card):
|
||||
self.battlefield.apply_order_effect(card, ai)
|
||||
ai.play_order(card)
|
||||
actions.append(("order", card))
|
||||
return actions
|
||||
|
||||
def _deploy_units(self, ai):
|
||||
actions = []
|
||||
units = [c for c in ai.hand if c.card_type == "unit" and ai.can_play_card(c)]
|
||||
units.sort(key=lambda c: c.cost)
|
||||
for card in units:
|
||||
if not ai.can_play_card(card):
|
||||
continue
|
||||
from card_game.factions import apply_faction_passive
|
||||
apply_faction_passive(card, ai.faction_id)
|
||||
slot = ai.deploy_unit(card)
|
||||
if slot >= 0:
|
||||
actions.append(("deploy", card, slot))
|
||||
self._handle_deploy(card, ai)
|
||||
return actions
|
||||
|
||||
def _play_targeted_orders(self, ai):
|
||||
actions = []
|
||||
for card in ai.hand[:]:
|
||||
if card.card_type != "order":
|
||||
continue
|
||||
if card.cost > ai.provisions:
|
||||
continue
|
||||
if not self.battlefield.needs_target(card):
|
||||
continue
|
||||
targets = self.battlefield.get_valid_targets(card, ai)
|
||||
if targets:
|
||||
target = self._pick_best_target(card, targets, ai)
|
||||
if target:
|
||||
self.battlefield.apply_order_effect(card, ai, target)
|
||||
ai.play_order(card)
|
||||
actions.append(("order_targeted", card, target))
|
||||
return actions
|
||||
|
||||
def _move_units(self, ai):
|
||||
actions = []
|
||||
if not self.battlefield.can_move_to_frontline(ai):
|
||||
return actions
|
||||
for unit in ai.get_support_units():
|
||||
op_cost = ai._get_op_cost(unit)
|
||||
if ai.provisions >= op_cost:
|
||||
slot = ai.move_to_frontline(unit)
|
||||
if slot >= 0:
|
||||
if self.battlefield.frontline_controller is None:
|
||||
self.battlefield.claim_frontline(ai)
|
||||
actions.append(("move", unit, slot))
|
||||
return actions
|
||||
|
||||
def _attack(self, ai):
|
||||
actions = []
|
||||
player = self.battlefield.player
|
||||
for unit in ai.get_frontline_units():
|
||||
if not unit.can_attack or unit.has_attacked:
|
||||
continue
|
||||
enemy_units = player.get_frontline_units()
|
||||
if enemy_units:
|
||||
killable = [u for u in enemy_units if u.current_hp <= unit.get_effective_attack()]
|
||||
if killable:
|
||||
target = min(killable, key=lambda u: u.current_hp)
|
||||
else:
|
||||
target = max(enemy_units, key=lambda u: u.get_effective_attack())
|
||||
dead = self.battlefield.resolve_attack(unit, target)
|
||||
actions.append(("attack_unit", unit, target))
|
||||
else:
|
||||
self.battlefield.attack_capital(unit)
|
||||
actions.append(("attack_capital", unit))
|
||||
|
||||
# Ranged units in support attack
|
||||
for unit in ai.get_support_units():
|
||||
if not unit.can_attack or unit.has_attacked or not unit.is_ranged():
|
||||
continue
|
||||
enemy_units = player.get_frontline_units()
|
||||
if enemy_units:
|
||||
killable = [u for u in enemy_units if u.current_hp <= unit.get_effective_attack()]
|
||||
target = min(killable, key=lambda u: u.current_hp) if killable else min(enemy_units, key=lambda u: u.current_hp)
|
||||
self.battlefield.resolve_attack(unit, target)
|
||||
actions.append(("attack_unit", unit, target))
|
||||
else:
|
||||
self.battlefield.attack_capital(unit)
|
||||
actions.append(("attack_capital", unit))
|
||||
|
||||
return actions
|
||||
|
||||
def _handle_deploy(self, card, ai):
|
||||
for ability in card.abilities:
|
||||
if ability.startswith("draw_on_deploy:"):
|
||||
count = int(ability.split(":")[1])
|
||||
for _ in range(count):
|
||||
ai.draw_card()
|
||||
elif ability.startswith("damage_on_deploy:"):
|
||||
dmg = int(ability.split(":")[1])
|
||||
opponent = self.battlefield.get_opponent(ai)
|
||||
targets = opponent.get_all_units()
|
||||
if targets:
|
||||
import random
|
||||
target = random.choice(targets)
|
||||
target.take_damage(dmg)
|
||||
|
||||
def _pick_best_target(self, card, targets, ai):
|
||||
if not targets:
|
||||
return None
|
||||
if card.effect_type == "destroy_damaged":
|
||||
dmg_targets = [t for t in targets if hasattr(t, 'current_hp') and t.current_hp < t.max_hp]
|
||||
if dmg_targets:
|
||||
return min(dmg_targets, key=lambda u: u.current_hp)
|
||||
return None
|
||||
if card.effect_type == "bounce":
|
||||
return max(targets, key=lambda u: u.get_effective_attack() if hasattr(u, 'get_effective_attack') else 0)
|
||||
if card.effect_type in ("buff_single", "move_to_front"):
|
||||
return max(targets, key=lambda u: u.get_effective_attack() if hasattr(u, 'get_effective_attack') else 0)
|
||||
if card.effect_type == "damage":
|
||||
unit_targets = [t for t in targets if hasattr(t, 'get_effective_attack')]
|
||||
if unit_targets:
|
||||
return max(unit_targets, key=lambda u: u.get_effective_attack())
|
||||
return targets[0] if targets else None
|
||||
Reference in New Issue
Block a user