feat: initial commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Core Engine Validators
|
||||
|
||||
物理校验模块,处理元叙事开关和物理连贯性验证。
|
||||
"""
|
||||
|
||||
from .fourth_wall_validator import (
|
||||
FourthWallValidator,
|
||||
FourthWallState,
|
||||
ValidationMode,
|
||||
PhysicalRule,
|
||||
ValidationRule,
|
||||
ValidationIssue,
|
||||
get_validator,
|
||||
enable_meta_mode,
|
||||
disable_meta_mode,
|
||||
is_meta_mode,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"FourthWallValidator",
|
||||
"FourthWallState",
|
||||
"ValidationMode",
|
||||
"PhysicalRule",
|
||||
"ValidationRule",
|
||||
"ValidationIssue",
|
||||
"get_validator",
|
||||
"enable_meta_mode",
|
||||
"disable_meta_mode",
|
||||
"is_meta_mode",
|
||||
]
|
||||
@@ -0,0 +1,395 @@
|
||||
"""
|
||||
Fourth Wall Validator (第四面墙校验器)
|
||||
|
||||
当元叙事开关开启时,关闭物理连贯性校验,
|
||||
允许"反规则"写法:梦境、意识流、打破第四面墙等。
|
||||
|
||||
当开关关闭时,恢复标准物理校验。
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional, Dict, Any
|
||||
from enum import Enum
|
||||
import json
|
||||
|
||||
|
||||
class ValidationMode(Enum):
|
||||
NORMAL = "normal" # 标准物理校验
|
||||
META = "meta" # 元叙事模式,跳过物理校验
|
||||
|
||||
|
||||
class PhysicalRule(Enum):
|
||||
"""物理校验规则类型"""
|
||||
REALM_CONSISTENCY = "realm_consistency" # 境界一致性
|
||||
TIME_CONTINUITY = "time_continuity" # 时间连续性
|
||||
LOCATION_COHERENCE = "location_coherence" # 地点连贯性
|
||||
POWER_BALANCE = "power_balance" # 战力平衡
|
||||
CAUSALITY = "causality" # 因果关系
|
||||
|
||||
|
||||
@dataclass
|
||||
class ValidationRule:
|
||||
"""校验规则"""
|
||||
type: PhysicalRule
|
||||
enabled: bool
|
||||
description: str
|
||||
severity: str = "high" # critical/high/medium/low
|
||||
|
||||
|
||||
@dataclass
|
||||
class ValidationIssue:
|
||||
"""校验问题"""
|
||||
rule: PhysicalRule
|
||||
chapter: int
|
||||
description: str
|
||||
severity: str
|
||||
entity_id: Optional[str] = None
|
||||
suggested_fix: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class FourthWallState:
|
||||
"""第四面墙状态"""
|
||||
mode: ValidationMode = ValidationMode.NORMAL
|
||||
enabled_rules: List[PhysicalRule] = field(default_factory=list)
|
||||
bypassed_rules: List[PhysicalRule] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def is_meta_mode(self) -> bool:
|
||||
return self.mode == ValidationMode.META
|
||||
|
||||
|
||||
class FourthWallValidator:
|
||||
"""
|
||||
第四面墙校验器
|
||||
|
||||
用法:
|
||||
1. 当 Meta_Narrative_Panel.jsx 的 Toggle 开启时,调用 enable_meta_mode()
|
||||
2. 当 Toggle 关闭时,调用 disable_meta_mode()
|
||||
3. 验证时调用 validate() 方法
|
||||
"""
|
||||
|
||||
# 默认启用的物理校验规则
|
||||
DEFAULT_ENABLED_RULES = [
|
||||
PhysicalRule.REALM_CONSISTENCY,
|
||||
PhysicalRule.TIME_CONTINUITY,
|
||||
PhysicalRule.LOCATION_COHERENCE,
|
||||
PhysicalRule.POWER_BALANCE,
|
||||
PhysicalRule.CAUSALITY,
|
||||
]
|
||||
|
||||
# 元叙事模式下放行的规则(这些在元叙事模式下被跳过)
|
||||
META_BYPASSABLE_RULES = [
|
||||
PhysicalRule.TIME_CONTINUITY, # 允许时间跳跃
|
||||
PhysicalRule.LOCATION_COHERENCE, # 允许瞬间移动
|
||||
PhysicalRule.POWER_BALANCE, # 允许战力波动
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self._state = FourthWallState(
|
||||
enabled_rules=list(self.DEFAULT_ENABLED_RULES),
|
||||
bypassed_rules=[]
|
||||
)
|
||||
self._listeners: List[callable] = []
|
||||
|
||||
@property
|
||||
def state(self) -> FourthWallState:
|
||||
return self._state
|
||||
|
||||
def enable_meta_mode(self) -> None:
|
||||
"""开启元叙事模式,关闭物理校验"""
|
||||
self._state.mode = ValidationMode.META
|
||||
self._state.bypassed_rules = list(self.META_BYPASSABLE_RULES)
|
||||
self._notify_listeners()
|
||||
print("[FourthWall] 元叙事模式已激活 - 物理校验已关闭")
|
||||
|
||||
def disable_meta_mode(self) -> None:
|
||||
"""关闭元叙事模式,恢复物理校验"""
|
||||
self._state.mode = ValidationMode.NORMAL
|
||||
self._state.bypassed_rules = []
|
||||
self._notify_listeners()
|
||||
print("[FourthWall] 标准模式已激活 - 物理校验已恢复")
|
||||
|
||||
def toggle(self) -> ValidationMode:
|
||||
"""切换模式"""
|
||||
if self._state.is_meta_mode:
|
||||
self.disable_meta_mode()
|
||||
else:
|
||||
self.enable_meta_mode()
|
||||
return self._state.mode
|
||||
|
||||
def validate(
|
||||
self,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]] = None
|
||||
) -> List[ValidationIssue]:
|
||||
"""
|
||||
验证章节物理连贯性
|
||||
|
||||
在元叙事模式下,返回空列表(跳过所有物理校验)
|
||||
"""
|
||||
issues: List[ValidationIssue] = []
|
||||
|
||||
# 元叙事模式:跳过所有物理校验
|
||||
if self._state.is_meta_mode:
|
||||
return issues
|
||||
|
||||
# 标准模式:执行校验
|
||||
for rule_type in self._state.enabled_rules:
|
||||
if rule_type not in self._state.bypassed_rules:
|
||||
rule_issues = self._validate_rule(
|
||||
rule_type, chapter, entities, world_state, previous_state
|
||||
)
|
||||
issues.extend(rule_issues)
|
||||
|
||||
return issues
|
||||
|
||||
def _validate_rule(
|
||||
self,
|
||||
rule: PhysicalRule,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]]
|
||||
) -> List[ValidationIssue]:
|
||||
"""根据规则类型执行校验"""
|
||||
validators = {
|
||||
PhysicalRule.REALM_CONSISTENCY: self._check_realm_consistency,
|
||||
PhysicalRule.TIME_CONTINUITY: self._check_time_continuity,
|
||||
PhysicalRule.LOCATION_COHERENCE: self._check_location_coherence,
|
||||
PhysicalRule.POWER_BALANCE: self._check_power_balance,
|
||||
PhysicalRule.CAUSALITY: self._check_causality,
|
||||
}
|
||||
|
||||
validator = validators.get(rule)
|
||||
if validator:
|
||||
return validator(chapter, entities, world_state, previous_state)
|
||||
return []
|
||||
|
||||
def _check_realm_consistency(
|
||||
self,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]]
|
||||
) -> List[ValidationIssue]:
|
||||
"""检查境界一致性"""
|
||||
issues: List[ValidationIssue] = []
|
||||
|
||||
# 检查主角境界是否合理
|
||||
protagonist = next((e for e in entities if e.get("is_protagonist")), None)
|
||||
if protagonist:
|
||||
current_realm = protagonist.get("realm")
|
||||
previous_realm = previous_state.get("protagonist", {}).get("realm") if previous_state else None
|
||||
|
||||
if previous_realm and current_realm != previous_realm:
|
||||
# 境界变化超过1级
|
||||
if not self._is_valid_realm_jump(previous_realm, current_realm):
|
||||
issues.append(ValidationIssue(
|
||||
rule=PhysicalRule.REALM_CONSISTENCY,
|
||||
chapter=chapter,
|
||||
description=f"境界跳跃不合理: {previous_realm} → {current_realm}",
|
||||
severity="high",
|
||||
entity_id=protagonist.get("id"),
|
||||
suggested_fix="补充修炼/机缘说明"
|
||||
))
|
||||
|
||||
return issues
|
||||
|
||||
def _check_time_continuity(
|
||||
self,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]]
|
||||
) -> List[ValidationIssue]:
|
||||
"""检查时间连续性"""
|
||||
issues: List[ValidationIssue] = []
|
||||
|
||||
current_time = world_state.get("time_of_day")
|
||||
previous_time = previous_state.get("world_state", {}).get("time_of_day") if previous_state else None
|
||||
|
||||
if previous_time and current_time:
|
||||
# 检查时间是否倒退
|
||||
time_order = {"早晨": 0, "上午": 1, "中午": 2, "下午": 3, "傍晚": 4, "夜晚": 5, "深夜": 6, "凌晨": 7}
|
||||
if time_order.get(previous_time, -1) > time_order.get(current_time, -1):
|
||||
issues.append(ValidationIssue(
|
||||
rule=PhysicalRule.TIME_CONTINUITY,
|
||||
chapter=chapter,
|
||||
description=f"时间倒退: {previous_time} → {current_time}",
|
||||
severity="medium",
|
||||
suggested_fix="添加时间跳跃说明"
|
||||
))
|
||||
|
||||
return issues
|
||||
|
||||
def _check_location_coherence(
|
||||
self,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]]
|
||||
) -> List[ValidationIssue]:
|
||||
"""检查地点连贯性"""
|
||||
issues: List[ValidationIssue] = []
|
||||
|
||||
current_loc = world_state.get("location")
|
||||
previous_loc = previous_state.get("world_state", {}).get("location") if previous_state else None
|
||||
|
||||
protagonist = next((e for e in entities if e.get("is_protagonist")), None)
|
||||
|
||||
if previous_loc and current_loc and protagonist:
|
||||
# 如果地点变化但没有过渡说明
|
||||
if current_loc != previous_loc:
|
||||
has_transition = self._check_transition_exists(chapter, previous_loc, current_loc)
|
||||
if not has_transition:
|
||||
issues.append(ValidationIssue(
|
||||
rule=PhysicalRule.LOCATION_COHERENCE,
|
||||
chapter=chapter,
|
||||
description=f"地点跳跃无过渡: {previous_loc} → {current_loc}",
|
||||
severity="low",
|
||||
entity_id=protagonist.get("id"),
|
||||
suggested_fix="添加移动过程描写"
|
||||
))
|
||||
|
||||
return issues
|
||||
|
||||
def _check_power_balance(
|
||||
self,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]]
|
||||
) -> List[ValidationIssue]:
|
||||
"""检查战力平衡"""
|
||||
issues: List[ValidationIssue] = []
|
||||
|
||||
# 检查是否有越太多级战斗
|
||||
protagonist = next((e for e in entities if e.get("is_protagonist")), None)
|
||||
opponents = [e for e in entities if e.get("is_opponent")]
|
||||
|
||||
if protagonist and opponents:
|
||||
p_realm = protagonist.get("realm_level", 0)
|
||||
for opp in opponents:
|
||||
o_realm = opp.get("realm_level", 0)
|
||||
if p_realm - o_realm > 2:
|
||||
issues.append(ValidationIssue(
|
||||
rule=PhysicalRule.POWER_BALANCE,
|
||||
chapter=chapter,
|
||||
description=f"战力差距过大: {p_realm} vs {o_realm}",
|
||||
severity="medium",
|
||||
entity_id=opp.get("id"),
|
||||
suggested_fix="增加战斗难度或金手指解释"
|
||||
))
|
||||
|
||||
return issues
|
||||
|
||||
def _check_causality(
|
||||
self,
|
||||
chapter: int,
|
||||
entities: List[Dict[str, Any]],
|
||||
world_state: Dict[str, Any],
|
||||
previous_state: Optional[Dict[str, Any]]
|
||||
) -> List[ValidationIssue]:
|
||||
"""检查因果关系"""
|
||||
# 简化实现:检查是否有突兀的力量获得
|
||||
issues: List[ValidationIssue] = []
|
||||
|
||||
new_skills = world_state.get("new_skills_gained", [])
|
||||
protagonist = next((e for e in entities if e.get("is_protagonist")), None)
|
||||
|
||||
if new_skills and protagonist:
|
||||
has_setup = self._check_setup_exists(chapter, new_skills)
|
||||
if not has_setup:
|
||||
issues.append(ValidationIssue(
|
||||
rule=PhysicalRule.CAUSALITY,
|
||||
chapter=chapter,
|
||||
description=f"突兀获得技能: {new_skills}",
|
||||
severity="high",
|
||||
entity_id=protagonist.get("id"),
|
||||
suggested_fix="补充技能来源铺垫"
|
||||
))
|
||||
|
||||
return issues
|
||||
|
||||
def _is_valid_realm_jump(self, from_realm: str, to_realm: str) -> bool:
|
||||
"""判断境界跳跃是否合理"""
|
||||
# 简化实现
|
||||
valid_jumps = {
|
||||
"炼气期一层": ["炼气期二层"],
|
||||
"炼气期二层": ["炼气期三层"],
|
||||
"筑基期一层": ["筑基期二层"],
|
||||
}
|
||||
return to_realm in valid_jumps.get(from_realm, [])
|
||||
|
||||
def _check_transition_exists(self, chapter: int, from_loc: str, to_loc: str) -> bool:
|
||||
"""检查过渡是否存在"""
|
||||
# 简化实现:应该读取章节正文检查
|
||||
return False
|
||||
|
||||
def _check_setup_exists(self, chapter: int, skills: List[str]) -> bool:
|
||||
"""检查铺垫是否存在"""
|
||||
# 简化实现:应该读取章节正文检查
|
||||
return False
|
||||
|
||||
def subscribe(self, callback: callable) -> None:
|
||||
"""订阅状态变化"""
|
||||
self._listeners.append(callback)
|
||||
|
||||
def unsubscribe(self, callback: callable) -> None:
|
||||
"""取消订阅"""
|
||||
if callback in self._listeners:
|
||||
self._listeners.remove(callback)
|
||||
|
||||
def _notify_listeners(self) -> None:
|
||||
"""通知所有监听器"""
|
||||
for callback in self._listeners:
|
||||
try:
|
||||
callback(self._state)
|
||||
except Exception as e:
|
||||
print(f"[FourthWall] 通知监听器失败: {e}")
|
||||
|
||||
def get_config(self) -> Dict[str, Any]:
|
||||
"""获取当前配置"""
|
||||
return {
|
||||
"mode": self._state.mode.value,
|
||||
"enabled_rules": [r.value for r in self._state.enabled_rules],
|
||||
"bypassed_rules": [r.value for r in self._state.bypassed_rules],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_config(config: Dict[str, Any]) -> "FourthWallValidator":
|
||||
"""从配置恢复"""
|
||||
validator = FourthWallValidator()
|
||||
if config.get("mode") == ValidationMode.META.value:
|
||||
validator.enable_meta_mode()
|
||||
return validator
|
||||
|
||||
|
||||
# 全局实例
|
||||
_validator_instance: Optional[FourthWallValidator] = None
|
||||
|
||||
|
||||
def get_validator() -> FourthWallValidator:
|
||||
"""获取全局校验器实例"""
|
||||
global _validator_instance
|
||||
if _validator_instance is None:
|
||||
_validator_instance = FourthWallValidator()
|
||||
return _validator_instance
|
||||
|
||||
|
||||
def enable_meta_mode() -> None:
|
||||
"""快捷函数:开启元叙事模式"""
|
||||
get_validator().enable_meta_mode()
|
||||
|
||||
|
||||
def disable_meta_mode() -> None:
|
||||
"""快捷函数:关闭元叙事模式"""
|
||||
get_validator().disable_meta_mode()
|
||||
|
||||
|
||||
def is_meta_mode() -> bool:
|
||||
"""快捷函数:检查是否在元叙事模式"""
|
||||
return get_validator().state.is_meta_mode
|
||||
Reference in New Issue
Block a user