Files
ai-town/engine-python/app/factions.py
empty 1fd318c9e3 feat(engine): add faction power system with event triggers
- Add FactionData model with power and members fields
- Add FactionEventResult model for faction event responses
- Add faction field to AgentState
- Implement faction classification based on emotion
- Add faction event triggers (festival/panic) when power >= 5
- Update StepResponse to include triggered_faction_event
- Add tests for new faction system

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 12:01:07 +08:00

139 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""派系系统 - 基于立场分类角色并影响世界"""
from typing import Dict, Tuple
from .models import (
WorldState, AgentState, Factions, FactionData,
FactionEventResult, WorldEffect, Emotion
)
# 派系分类阈值
OPTIMIST_THRESHOLD = 0.6
FEARFUL_THRESHOLD = 0.6
# 阵营技能触发阈值
FACTION_POWER_THRESHOLD = 5
# 阵营事件对情绪的影响
FACTION_EVENT_EFFECTS = {
"festival": 0.2, # 乐观派事件提升情绪
"panic": -0.3, # 恐惧派事件降低情绪
}
def classify_faction(agent: AgentState) -> str:
"""根据 emotion 分类角色所属派系
规则:
- emotion 偏正 (happy) → optimists
- emotion 偏负 (anxious) → fearful
- 其它 (calm) → neutral
"""
if agent.emotion == Emotion.HAPPY:
return "optimists"
elif agent.emotion == Emotion.ANXIOUS:
return "fearful"
else:
return "neutral"
def update_factions(state: WorldState) -> None:
"""统计各派系并更新 world_state
流程:
1. 清空 factions.members
2. 遍历所有 agents根据 emotion 分类
3. 更新 agent.faction 字段
4. 将 agent 加入对应 faction.members
5. 每个 agent 为 faction 增加 power += 1
"""
# 重置 factions
state.factions = Factions(
optimists=FactionData(power=0, members=[]),
fearful=FactionData(power=0, members=[])
)
for agent_id, agent in state.agents.items():
faction = classify_faction(agent)
agent.faction = faction
if faction == "optimists":
state.factions.optimists.members.append(agent_id)
state.factions.optimists.power += 1
elif faction == "fearful":
state.factions.fearful.members.append(agent_id)
state.factions.fearful.power += 1
# neutral 不加入任何派系
def apply_faction_influence(state: WorldState) -> None:
"""派系分布影响世界情绪(保留原有逻辑)"""
optimists = state.factions.optimists.power
fearful = state.factions.fearful.power
if optimists > fearful:
state.town_mood = min(10, state.town_mood + 1)
elif fearful > optimists:
state.town_mood = max(-10, state.town_mood - 1)
def check_and_trigger_faction_event(state: WorldState) -> FactionEventResult:
"""检查并触发阵营事件
规则:
- 当 faction.power >= 5 时,触发对应世界事件
- optimists → festival提升情绪
- fearful → panic降低情绪
- 触发后该 faction.power 归零
"""
result = FactionEventResult(type=None, source_faction=None)
# 检查乐观派
if state.factions.optimists.power >= FACTION_POWER_THRESHOLD:
result = FactionEventResult(type="festival", source_faction="optimists")
_apply_faction_event(state, "festival")
state.factions.optimists.power = 0
# 检查恐惧派
elif state.factions.fearful.power >= FACTION_POWER_THRESHOLD:
result = FactionEventResult(type="panic", source_faction="fearful")
_apply_faction_event(state, "panic")
state.factions.fearful.power = 0
return result
def _apply_faction_event(state: WorldState, event_type: str) -> None:
"""应用阵营事件效果
1. 创建 WorldEffect 写入 world_state.world_effects
2. 影响所有 agent 的 emotion
"""
# 创建持续影响效果
if event_type == "festival":
effect = WorldEffect(
type="faction_event",
name="节日庆典",
intensity=1,
remaining_ticks=3,
mood_modifier=1
)
else: # panic
effect = WorldEffect(
type="faction_event",
name="恐慌蔓延",
intensity=1,
remaining_ticks=3,
mood_modifier=-1
)
state.world_effects.append(effect)
# 影响所有 agent 的 stance间接影响下一轮 emotion
emotion_delta = FACTION_EVENT_EFFECTS.get(event_type, 0)
for agent in state.agents.values():
if emotion_delta > 0:
agent.stance.optimism = min(1.0, agent.stance.optimism + emotion_delta)
agent.stance.fear = max(0.0, agent.stance.fear - emotion_delta * 0.5)
else:
agent.stance.fear = min(1.0, agent.stance.fear + abs(emotion_delta))
agent.stance.optimism = max(0.0, agent.stance.optimism - abs(emotion_delta) * 0.5)