add actual action space
This commit is contained in:
37
README.md
37
README.md
@@ -104,7 +104,7 @@
|
||||
- ✅ LLM接口集成
|
||||
- ✅ 角色AI系统(规则AI + LLM AI)
|
||||
- ✅ 协程化决策机制,异步运行
|
||||
- [ ] AI动作链系统(长期规划和目标导向行为)
|
||||
- [ ] 长期规划和目标导向行为)
|
||||
- [ ] 突发动作响应系统(对外界刺激的即时反应)
|
||||
- [ ] LLM驱动的NPC对话、思考、互动、事件总结
|
||||
- [ ] 智能决策系统
|
||||
@@ -160,41 +160,6 @@
|
||||
|
||||
我希望能够创造出纯粹的、快乐的、直接的、活着的修仙世界的沉浸感。不是像一些游戏公司的纯粹宣传工具,也不是像斯坦福小镇那样的纯粹研究,而是能给玩家提供真实代入感和沉浸感的实际世界。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
cultivation-world-simulator/
|
||||
├── src/ # 核心源代码
|
||||
│ ├── classes/ # 核心数据类
|
||||
│ │ ├── avatar.py # 角色系统
|
||||
│ │ ├── world.py # 世界模型
|
||||
│ │ ├── tile.py # 地图系统
|
||||
│ │ └── ... # 其他核心类
|
||||
│ ├── front/ # 前端显示模块
|
||||
│ │ └── front.py # pygame界面
|
||||
│ ├── sim/ # 模拟引擎
|
||||
│ │ └── simulator.py # 核心模拟器
|
||||
│ ├── run/ # 运行脚本
|
||||
│ │ ├── run.py # 主程序入口
|
||||
│ │ └── create_map.py # 地图生成工具
|
||||
│ └── utils/ # 实用工具
|
||||
│ ├── config.py # 配置管理
|
||||
│ ├── llm.py # LLM接口
|
||||
│ └── strings.py # 字符串处理
|
||||
├── assets/ # 游戏资源
|
||||
│ ├── tiles/ # 地形贴图
|
||||
│ ├── males/ # 男性角色头像
|
||||
│ └── females/ # 女性角色头像
|
||||
├── static/ # 静态配置文件
|
||||
│ ├── config.yml # 基础配置
|
||||
│ ├── local_config.yml # 本地配置(优先级更高)
|
||||
│ └── templates/ # AI提示词模板
|
||||
├── configs/ # 其他配置文件
|
||||
├── tests/ # 测试代码
|
||||
├── requirements.txt # Python依赖列表
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 技术架构
|
||||
|
||||
- **前端显示**: pygame (未来可能支持Web)
|
||||
|
||||
@@ -128,12 +128,21 @@ class ActualActionMixin():
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def is_doable(self) -> bool:
|
||||
"""
|
||||
判断动作是否可以执行
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Move(DefineAction, ChunkActionMixin):
|
||||
"""
|
||||
最基础的移动动作,在tile之间进行切换。
|
||||
"""
|
||||
COMMENT = "移动到某个相对位置"
|
||||
PARAMS = {"delta_x": "int", "delta_y": "int"}
|
||||
def _execute(self, delta_x: int, delta_y: int) -> None:
|
||||
"""
|
||||
移动到某个tile
|
||||
@@ -157,6 +166,7 @@ class MoveToRegion(DefineAction, ActualActionMixin):
|
||||
移动到某个region
|
||||
"""
|
||||
COMMENT = "移动到某个区域"
|
||||
PARAMS = {"region": "region_name"}
|
||||
def _execute(self, region: Region|str) -> None:
|
||||
"""
|
||||
移动到某个region
|
||||
@@ -194,12 +204,20 @@ class MoveToRegion(DefineAction, ActualActionMixin):
|
||||
region_name = str(region)
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 开始移动向 {region_name}")
|
||||
|
||||
@property
|
||||
def is_doable(self) -> bool:
|
||||
"""
|
||||
判断移动到区域动作是否可以执行
|
||||
"""
|
||||
return True
|
||||
|
||||
@long_action(step_month=10)
|
||||
class Cultivate(DefineAction, ActualActionMixin):
|
||||
"""
|
||||
修炼动作,可以增加修仙进度。
|
||||
"""
|
||||
COMMENT = "修炼,增进修为"
|
||||
PARAMS = {}
|
||||
def _execute(self) -> None:
|
||||
"""
|
||||
修炼
|
||||
@@ -226,6 +244,13 @@ class Cultivate(DefineAction, ActualActionMixin):
|
||||
"""
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 在 {self.avatar.tile.region.name} 开始修炼")
|
||||
|
||||
@property
|
||||
def is_doable(self) -> bool:
|
||||
"""
|
||||
判断修炼动作是否可以执行
|
||||
"""
|
||||
return self.avatar.cultivation_progress.can_cultivate()
|
||||
|
||||
|
||||
# 突破境界class
|
||||
@long_action(step_month=1)
|
||||
@@ -234,6 +259,7 @@ class Breakthrough(DefineAction, ActualActionMixin):
|
||||
突破境界
|
||||
"""
|
||||
COMMENT = "尝试突破境界"
|
||||
PARAMS = {}
|
||||
def calc_success_rate(self) -> float:
|
||||
"""
|
||||
计算突破境界的成功率
|
||||
@@ -255,12 +281,20 @@ class Breakthrough(DefineAction, ActualActionMixin):
|
||||
"""
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 开始尝试突破境界")
|
||||
|
||||
@property
|
||||
def is_doable(self) -> bool:
|
||||
"""
|
||||
判断突破动作是否可以执行
|
||||
"""
|
||||
return self.avatar.cultivation_progress.can_break_through()
|
||||
|
||||
@long_action(step_month=6)
|
||||
class Play(DefineAction, ActualActionMixin):
|
||||
"""
|
||||
游戏娱乐动作,持续半年时间
|
||||
"""
|
||||
COMMENT = "游戏娱乐,放松身心"
|
||||
PARAMS = {}
|
||||
|
||||
def _execute(self) -> None:
|
||||
"""
|
||||
@@ -276,12 +310,11 @@ class Play(DefineAction, ActualActionMixin):
|
||||
"""
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 开始玩耍")
|
||||
|
||||
@property
|
||||
def is_doable(self) -> bool:
|
||||
return True
|
||||
|
||||
ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion, Play]
|
||||
ACTION_SPACE = [
|
||||
# {"action": "Move", "params": {"delta_x": int, "delta_y": int}, "comment": Move.COMMENT},
|
||||
{"action": "Cultivate", "params": {}, "comment": Cultivate.COMMENT},
|
||||
{"action": "Breakthrough", "params": {}, "comment": Breakthrough.COMMENT},
|
||||
{"action": "MoveToRegion", "params": {"region": "region_name"}, "comment": MoveToRegion.COMMENT},
|
||||
{"action": "Play", "params": {}, "comment": Play.COMMENT},
|
||||
]
|
||||
ACTION_SPACE_STR = json.dumps(ACTION_SPACE, ensure_ascii=False)
|
||||
ALL_ACTUAL_ACTION_CLASSES = [Cultivate, Breakthrough, MoveToRegion, Play]
|
||||
ALL_ACTION_NAMES = ["Move", "Cultivate", "Breakthrough", "MoveToRegion", "Play"]
|
||||
ALL_ACTUAL_ACTION_NAMES = ["Cultivate", "Breakthrough", "MoveToRegion", "Play"]
|
||||
@@ -11,7 +11,6 @@ import random
|
||||
from src.classes.world import World
|
||||
from src.classes.tile import Region
|
||||
from src.classes.root import corres_essence_type
|
||||
from src.classes.action import ACTION_SPACE_STR
|
||||
from src.classes.event import Event, NULL_EVENT
|
||||
from src.utils.llm import get_ai_prompt_and_call_llm_async
|
||||
from src.classes.typings import ACTION_NAME, ACTION_PARAMS, ACTION_PAIR
|
||||
@@ -84,9 +83,7 @@ class RuleAI(AI):
|
||||
class LLMAI(AI):
|
||||
"""
|
||||
LLM AI
|
||||
"""
|
||||
# TODO:动作链
|
||||
"""
|
||||
一些思考:
|
||||
AI动作应该分两类:
|
||||
1. 动作链,一定时间内的长期规划,动作按照这个动作链来执行(以及何时终止并执行下一个动作)
|
||||
2. 突发情况,比如突然有人要攻击NPC,这个时候的反应
|
||||
@@ -97,7 +94,7 @@ class LLMAI(AI):
|
||||
"""
|
||||
异步决策逻辑:通过LLM决定执行什么动作和参数
|
||||
"""
|
||||
action_space_str = ACTION_SPACE_STR
|
||||
action_space_str = self.avatar.get_action_space_str()
|
||||
avatar_infos_str = str(self.avatar)
|
||||
regions_str = "\n".join([str(region) for region in world.map.regions.values()])
|
||||
avatar_persona = self.avatar.persona.prompt
|
||||
|
||||
@@ -2,9 +2,10 @@ import random
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
import json
|
||||
|
||||
from src.classes.calendar import MonthStamp
|
||||
from src.classes.action import Action, ALL_ACTION_CLASSES
|
||||
from src.classes.action import Action, ALL_ACTUAL_ACTION_CLASSES, ALL_ACTION_CLASSES, ALL_ACTUAL_ACTION_NAMES
|
||||
from src.classes.world import World
|
||||
from src.classes.tile import Tile, Region
|
||||
from src.classes.cultivation import CultivationProgress
|
||||
@@ -190,6 +191,20 @@ class Avatar:
|
||||
"""
|
||||
return "\n".join([f"{action.__class__.__name__}: {action_params}" for action, action_params in self.history_action_pairs])
|
||||
|
||||
def get_action_space_str(self) -> str:
|
||||
action_space = self.get_action_space()
|
||||
action_space_str = json.dumps(action_space, ensure_ascii=False)
|
||||
return action_space_str
|
||||
|
||||
def get_action_space(self) -> list[dict]:
|
||||
"""
|
||||
获取动作空间
|
||||
"""
|
||||
actual_actions = [self.create_action(action_cls_name) for action_cls_name in ALL_ACTUAL_ACTION_NAMES]
|
||||
doable_actions = [action for action in actual_actions if action.is_doable]
|
||||
action_space = [{"action": action.__class__.__name__, "params": action.PARAMS, "comment": action.COMMENT} for action in doable_actions]
|
||||
return action_space
|
||||
|
||||
def get_new_avatar_from_ordinary(world: World, current_month_stamp: MonthStamp, name: str, age: Age):
|
||||
"""
|
||||
从凡人中来的新修士
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
|
||||
|
||||
|
||||
你是一个决策者,这是一个修仙的仙侠世界,你负责来决定一些NPC的下一步行为。
|
||||
每个角色均拥有的动作空间和需要的参数为:
|
||||
{action_space}
|
||||
世界地图上存在的区域为:
|
||||
{regions}
|
||||
你需要进行决策的NPC的基本信息为:
|
||||
{avatar_infos}
|
||||
其个性为:{avatar_persona}
|
||||
决策时需参考这个角色的个性。
|
||||
该角色的动作空间及其参数为:
|
||||
{action_space}
|
||||
|
||||
注意,只返回json格式的动作
|
||||
返回格式:
|
||||
|
||||
Reference in New Issue
Block a user