refactor world regions

This commit is contained in:
bridge
2025-12-08 23:06:50 +08:00
parent 8124537cff
commit a1b7583f36
6 changed files with 116 additions and 51 deletions

View File

@@ -35,6 +35,7 @@ from .catch import Catch
from .nurture_weapon import NurtureWeapon
from .switch_weapon import SwitchWeapon
from .assassinate import Assassinate
from .move_to_direction import MoveToDirection
# 注册到 ActionRegistry标注是否为实际可执行动作
register_action(actual=False)(Action)
@@ -66,6 +67,7 @@ register_action(actual=True)(Catch)
register_action(actual=True)(NurtureWeapon)
register_action(actual=True)(SwitchWeapon)
register_action(actual=True)(Assassinate)
register_action(actual=True)(MoveToDirection)
# Talk 已移动到 mutual_action 模块,在那里注册
__all__ = [
@@ -100,6 +102,7 @@ __all__ = [
"NurtureWeapon",
"SwitchWeapon",
"Assassinate",
"MoveToDirection",
# Talk 已移动到 mutual_action 模块
]

View File

@@ -1 +1,81 @@
from __future__ import annotations
import random
from src.classes.action import DefineAction, ActualActionMixin, Move
from src.classes.event import Event
from src.classes.action_runtime import ActionResult, ActionStatus
from src.utils.distance import manhattan_distance
from src.classes.region import Region
class MoveToDirection(DefineAction, ActualActionMixin):
"""
向某个方向移动探索固定时长6个月
"""
COMMENT = "向某个方向探索未知区域"
DOABLES_REQUIREMENTS = "任何时候都可以执行"
PARAMS = {"direction": "direction (North/South/East/West)"}
IS_MAJOR = False
# 固定持续时间
DURATION = 6
def __init__(self, avatar, world):
super().__init__(avatar, world)
# 记录本次动作的开始状态
self.start_monthstamp = None
self.direction = None
# 方向向量映射 (假设 (0,0) 在左上角)
# North: y减小
# South: y增加
# West: x减小
# East: x增加
self.DIR_VECTORS = {
"North": (0, -1),
"South": (0, 1),
"West": (-1, 0),
"East": (1, 0),
"": (0, -1),
"": (0, 1),
"西": (-1, 0),
"": (1, 0)
}
def can_start(self, direction: str | None = None) -> tuple[bool, str]:
if not direction:
return False, "缺少方向参数"
if direction not in self.DIR_VECTORS:
return False, f"无效的方向: {direction}"
return True, ""
def start(self, direction: str) -> Event:
self.start_monthstamp = self.world.month_stamp
self.direction = direction
return Event(self.world.month_stamp, f"{self.avatar.name} 开始向{direction}方探索未知区域", related_avatars=[self.avatar.id])
def step(self, direction: str) -> ActionResult:
# 确保方向已设置
self.direction = direction
dx_dir, dy_dir = self.DIR_VECTORS[direction]
# 计算本次移动步长
step_len = getattr(self.avatar, "move_step_length", 1)
# 计算实际位移
dx = dx_dir * step_len
dy = dy_dir * step_len
# 执行移动
Move(self.avatar, self.world).execute(dx, dy)
# 检查是否完成(固定时长)
# 修正:(current - start) >= duration - 1即第1个月执行后差值为0如果duration=1则完成
elapsed = self.world.month_stamp - self.start_monthstamp
is_done = elapsed >= (self.DURATION - 1)
return ActionResult(status=(ActionStatus.COMPLETED if is_done else ActionStatus.RUNNING), events=[])
async def finish(self, direction: str) -> list[Event]:
return [Event(self.world.month_stamp, f"{self.avatar.name} 结束了向{direction}方的探索", related_avatars=[self.avatar.id])]

View File

@@ -71,7 +71,7 @@ class LLMAI(AI):
general_action_infos = ACTION_INFOS_STR
async def decide_one(avatar: Avatar):
# 获取基于该角色已知区域的世界信息(包含距离计算)
world_info = world.get_info(avatar=avatar)
world_info = world.get_info(avatar=avatar, detailed=True)
# 在提示中包含处于角色观测范围内的其他角色
observed = world.get_observable_avatars(avatar)

View File

@@ -91,7 +91,6 @@ class Map():
from src.classes.avatar import Avatar
from src.classes.region import NormalRegion, CultivateRegion, CityRegion
from src.utils.distance import chebyshev_distance
known_region_ids = avatar.known_regions if avatar else None
current_loc = (avatar.pos_x, avatar.pos_y) if avatar else None
@@ -99,23 +98,14 @@ class Map():
def filter_regions(cls):
return {
rid: r for rid, r in self.regions.items()
if known_region_ids is None or rid in known_region_ids
if isinstance(r, cls) and (known_region_ids is None or rid in known_region_ids)
}
def build_regions_info(regions_dict) -> list[str]:
infos = []
step_len = avatar.move_step_length if avatar else 1
for r in regions_dict.values():
base_info = r.get_detailed_info() if detailed else r.get_info()
# 如果有当前位置,追加距离信息
dist = chebyshev_distance(current_loc, r.center_loc)
# 估算到达时间:距离 / 步长 (向上取整)
step_len = avatar.move_step_length
months = (dist + step_len - 1) // step_len
# 避免显示 0 个月
months = max(1, months)
base_info += f"(距离:{months}月)"
base_info = r.get_detailed_info(current_loc, step_len) if detailed else r.get_info(current_loc, step_len)
infos.append(base_info)
return infos

View File

@@ -5,6 +5,7 @@ from abc import ABC, abstractmethod
from src.utils.df import game_configs, get_str, get_int, get_list_int
from src.utils.config import CONFIG
from src.utils.distance import chebyshev_distance
from src.classes.essence import EssenceType, Essence
from src.classes.animal import Animal, animals_by_id
from src.classes.plant import Plant, plants_by_id
@@ -65,11 +66,26 @@ class Region(ABC):
f"描述: {self.desc}",
]
def get_info(self) -> str:
return self.name
@abstractmethod
def _get_desc(self) -> str:
"""返回紧跟在名字后的描述,通常包含括号,例如 '金行灵气5'"""
pass
def get_detailed_info(self) -> str:
return f"{self.name} - {self.desc}"
def _get_distance_desc(self, current_loc: tuple[int, int] = None, step_len: int = 1) -> str:
if current_loc is None:
return ""
dist = chebyshev_distance(current_loc, self.center_loc)
# 估算到达时间:距离 / 步长 (向上取整)
months = (dist + step_len - 1) // step_len
# 避免显示 0 个月
months = max(1, months)
return f"(距离:{months}月)"
def get_info(self, current_loc: tuple[int, int] = None, step_len: int = 1) -> str:
return f"{self.name}{self._get_distance_desc(current_loc, step_len)}"
def get_detailed_info(self, current_loc: tuple[int, int] = None, step_len: int = 1) -> str:
return f"{self.name}{self._get_desc()} - {self.desc}{self._get_distance_desc(current_loc, step_len)}"
def get_structured_info(self) -> dict:
return {
@@ -108,32 +124,16 @@ class NormalRegion(Region):
info_parts.extend([a.get_info() for a in self.animals])
if self.plants:
info_parts.extend([p.get_info() for p in self.plants])
return "; ".join(info_parts) if info_parts else "无特色物种"
return "; ".join(info_parts) if info_parts else "无特色物种"
def _get_species_brief(self) -> str:
briefs: list[str] = []
if self.animals:
briefs.extend([f"{a.name}{a.realm.value}" for a in self.animals])
if self.plants:
briefs.extend([f"{p.name}{p.realm.value}" for p in self.plants])
return "".join(briefs)
def _get_desc(self) -> str:
species_info = self.get_species_info()
return f"(物种分布:{species_info}"
def __str__(self) -> str:
species_info = self.get_species_info()
return f"普通区域:{self.name} - {self.desc} | 物种分布:{species_info}"
def get_info(self) -> str:
brief = self._get_species_brief()
return f"{self.name}{brief}" if brief else self.name
def get_detailed_info(self) -> str:
brief = self._get_species_brief()
name_with_brief = f"{self.name}{brief}" if brief else self.name
species_info = self.get_species_info()
if not species_info or species_info == "暂无特色物种":
return f"{name_with_brief} - {self.desc}"
return f"{name_with_brief} - {self.desc} | 物种分布:{species_info}"
def get_hover_info(self) -> list[str]:
lines = super().get_hover_info()
species_info = self.get_species_info()
@@ -183,15 +183,12 @@ class CultivateRegion(Region):
def get_region_type(self) -> str:
return "cultivate"
def _get_desc(self) -> str:
return f"{self.essence_type}行灵气:{self.essence_density}"
def __str__(self) -> str:
return f"修炼区域:{self.name}{self.essence_type}行灵气:{self.essence_density}- {self.desc}"
def get_info(self) -> str:
return f"{self.name}{self.essence_type}行灵气:{self.essence_density}"
def get_detailed_info(self) -> str:
return f"{self.name}{self.essence_type}行灵气:{self.essence_density}- {self.desc}"
def get_hover_info(self) -> list[str]:
lines = super().get_hover_info()
stars = "" * self.essence_density + "" * (10 - self.essence_density)
@@ -217,12 +214,6 @@ class CityRegion(Region):
def __str__(self) -> str:
return f"城市区域:{self.name} - {self.desc}"
def get_info(self) -> str:
return self.name
def get_detailed_info(self) -> str:
return f"{self.name} - {self.desc}"
def get_structured_info(self) -> dict:
info = super().get_structured_info()
info["type_name"] = "城市区域"

View File

@@ -22,4 +22,5 @@
- 长期目标是非常重要的一个参数,其权重最高
- 执行动作只能从给定的全部动作中选且需满足对应条件见动作的requirements文本
- 一些动作需要先移动满足某些条件才可执行,可以适当规划。
- 和另一个角色交互的动作必须在对应角色附近。执行前可以先MoveToAvatar
- 和另一个角色交互的动作必须在对应角色附近。执行前可以先MoveToAvatar
- 如果对世界了解太少可以先通过MoveToDirection探索世界