add new actions and AI
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
- ✅ 修炼境界体系
|
||||
- ✅ 灵根系统
|
||||
- ✅ 基础移动动作
|
||||
- [ ] 动态的突破成功概率
|
||||
- [ ] 角色关系系统
|
||||
- [ ] 性格系统设计
|
||||
- [ ] 角色特殊能力
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from __future__ import annotations
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
import random
|
||||
|
||||
from src.classes.essence import Essence, EssenceType
|
||||
from src.classes.root import Root, corres_essence_type
|
||||
from src.classes.tile import Region
|
||||
from src.classes.event import Event, NullEvent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
@@ -24,7 +27,7 @@ class Action(ABC):
|
||||
self.world = world
|
||||
|
||||
@abstractmethod
|
||||
def execute(self):
|
||||
def execute(self) -> Event|NullEvent:
|
||||
pass
|
||||
|
||||
class DefineAction(Action):
|
||||
@@ -45,7 +48,7 @@ class Move(DefineAction):
|
||||
"""
|
||||
最基础的移动动作,在tile之间进行切换。
|
||||
"""
|
||||
def execute(self, delta_x: int, delta_y: int):
|
||||
def execute(self, delta_x: int, delta_y: int) -> Event|NullEvent:
|
||||
"""
|
||||
移动到某个tile
|
||||
"""
|
||||
@@ -62,20 +65,42 @@ class Move(DefineAction):
|
||||
else:
|
||||
# 超出边界:不改变位置与tile
|
||||
pass
|
||||
return NullEvent()
|
||||
|
||||
class MoveToRegion(DefineAction):
|
||||
"""
|
||||
移动到某个region
|
||||
"""
|
||||
def execute(self, region: Region) -> Event|NullEvent:
|
||||
"""
|
||||
移动到某个region
|
||||
"""
|
||||
cur_loc = (self.avatar.pos_x, self.avatar.pos_y)
|
||||
region_center_loc = region.center_loc
|
||||
delta_x = region_center_loc[0] - cur_loc[0]
|
||||
delta_y = region_center_loc[1] - cur_loc[1]
|
||||
# 横纵向一次最多移动一格(可以同时横纵移动)
|
||||
delta_x = max(-1, min(1, delta_x))
|
||||
delta_y = max(-1, min(1, delta_y))
|
||||
Move(self.avatar, self.world).execute(delta_x, delta_y)
|
||||
return Event(self.world.year, self.world.month, f"{self.avatar.name} 移动向 {region.name}")
|
||||
|
||||
class Cultivate(DefineAction):
|
||||
"""
|
||||
修炼动作,可以增加修仙进度。
|
||||
"""
|
||||
def execute(self, root: Root, essence: Essence):
|
||||
def execute(self) -> Event|NullEvent:
|
||||
"""
|
||||
修炼
|
||||
获得的exp增加取决于essence的对应灵根的大小。
|
||||
"""
|
||||
root = self.avatar.root
|
||||
essence = self.avatar.tile.region.essence
|
||||
essence_type = corres_essence_type[root]
|
||||
essence_density = essence.get_density(essence_type)
|
||||
exp = self.get_exp(essence_density)
|
||||
self.avatar.cultivation_progress.add_exp(exp)
|
||||
return Event(self.world.year, self.world.month, f"{self.avatar.name} 在 {self.avatar.tile.region.name} 修炼")
|
||||
|
||||
def get_exp(self, essence_density: int) -> int:
|
||||
"""
|
||||
@@ -83,4 +108,33 @@ class Cultivate(DefineAction):
|
||||
公式为:base * essence_density
|
||||
"""
|
||||
base = 100
|
||||
return base * essence_density
|
||||
return base * essence_density
|
||||
|
||||
|
||||
# 突破境界class
|
||||
class Breakthrough(DefineAction):
|
||||
"""
|
||||
突破境界
|
||||
"""
|
||||
def calc_success_rate(self) -> float:
|
||||
"""
|
||||
计算突破境界的成功率
|
||||
"""
|
||||
return 0.5
|
||||
|
||||
def execute(self) -> Event|NullEvent:
|
||||
"""
|
||||
突破境界
|
||||
"""
|
||||
assert self.avatar.cultivation_progress.can_break_through()
|
||||
success_rate = self.calc_success_rate()
|
||||
if random.random() < success_rate:
|
||||
self.avatar.cultivation_progress.break_through()
|
||||
is_success = True
|
||||
else:
|
||||
is_success = False
|
||||
res = "成功" if is_success else "失败"
|
||||
return Event(self.world.year, self.world.month, f"{self.avatar.name} 突破境界{res}")
|
||||
|
||||
|
||||
ALL_ACTION_CLASSES = [Move, Cultivate, Breakthrough, MoveToRegion]
|
||||
72
src/classes/ai.py
Normal file
72
src/classes/ai.py
Normal file
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
NPC AI的类。
|
||||
这里指的不是LLM或者Machine Learning,而是NPC的决策机制
|
||||
分为两类:规则AI和LLM AI
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from src.classes.world import World
|
||||
from src.classes.tile import Region
|
||||
from src.classes.root import corres_essence_type
|
||||
|
||||
class AI(ABC):
|
||||
"""
|
||||
AI的基类
|
||||
"""
|
||||
def __init__(self, avatar: 'Avatar'):
|
||||
self.avatar = avatar
|
||||
|
||||
@abstractmethod
|
||||
def decide(self, world: World) -> tuple[str, dict]:
|
||||
"""
|
||||
决定做什么
|
||||
"""
|
||||
pass
|
||||
|
||||
# def create_event(self, world: World, content: str) -> Event:
|
||||
# """
|
||||
# 创建事件
|
||||
# """
|
||||
# return Event(world.year, world.month, content)
|
||||
|
||||
class RuleAI(AI):
|
||||
"""
|
||||
规则AI
|
||||
"""
|
||||
def decide(self, world: World) -> tuple[str, dict]:
|
||||
"""
|
||||
决定做什么
|
||||
先做一个简单的:
|
||||
1. 找到自己灵根对应的最好的区域
|
||||
2. 检测自己是否在最好的区域
|
||||
3. 如果不在,则移动到最好的区域
|
||||
4. 如果已经到达最好的区域,则进行修炼
|
||||
5. 如果需要突破境界了,则突破境界
|
||||
"""
|
||||
best_region = self.get_best_region(list(world.map.regions.values()))
|
||||
if self.avatar.is_in_region(best_region):
|
||||
if self.avatar.cultivation_progress.can_break_through():
|
||||
return "Breakthrough", {}
|
||||
else:
|
||||
return "Cultivate", {}
|
||||
else:
|
||||
return "MoveToRegion", {"region": best_region}
|
||||
|
||||
def get_best_region(self, regions: list[Region]) -> Region:
|
||||
"""
|
||||
根据avatar的灵根找到最适合的区域
|
||||
"""
|
||||
root = self.avatar.root
|
||||
essence_type = corres_essence_type[root]
|
||||
region_with_best_essence = max(regions, key=lambda region: region.essence.get_density(essence_type))
|
||||
return region_with_best_essence
|
||||
|
||||
class LLMAI(AI):
|
||||
"""
|
||||
LLM AI
|
||||
"""
|
||||
def decide(self, world: World) -> tuple[str, dict]:
|
||||
"""
|
||||
决定做什么
|
||||
"""
|
||||
pass
|
||||
@@ -5,13 +5,14 @@ from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from src.classes.calendar import Month, Year
|
||||
from src.classes.action import Action, Move, Cultivate
|
||||
from src.classes.action import Action, ALL_ACTION_CLASSES
|
||||
from src.classes.world import World
|
||||
from src.classes.tile import Tile
|
||||
from src.classes.tile import Tile, Region
|
||||
from src.classes.cultivation import CultivationProgress, Realm
|
||||
from src.classes.root import Root
|
||||
from src.classes.age import Age
|
||||
from src.utils.strings import to_snake_case
|
||||
from src.classes.ai import AI, RuleAI
|
||||
|
||||
class Gender(Enum):
|
||||
MALE = "male"
|
||||
@@ -44,19 +45,22 @@ class Avatar:
|
||||
tile: Optional[Tile] = None
|
||||
actions: dict[str, Action] = field(default_factory=dict)
|
||||
root: Root = field(default_factory=lambda: random.choice(list(Root)))
|
||||
ai: AI = None
|
||||
|
||||
def __post_init__(self):
|
||||
"""
|
||||
在Avatar创建后自动绑定基础动作
|
||||
在Avatar创建后自动绑定基础动作和AI
|
||||
"""
|
||||
self.tile = self.world.map.get_tile(self.pos_x, self.pos_y)
|
||||
self.ai = RuleAI(self)
|
||||
self._bind_basic_actions()
|
||||
|
||||
def _bind_basic_actions(self):
|
||||
"""
|
||||
绑定基础动作,如移动等
|
||||
"""
|
||||
self.bind_action(Move)
|
||||
self.bind_action(Cultivate)
|
||||
for action in ALL_ACTION_CLASSES:
|
||||
self.bind_action(action)
|
||||
|
||||
|
||||
def bind_action(self, action_class: type[Action]):
|
||||
@@ -80,16 +84,10 @@ class Avatar:
|
||||
角色执行动作。
|
||||
实际上分为两步:决定做什么(decide)和实习上去做(do)
|
||||
"""
|
||||
action_name, action_args = self.decide()
|
||||
action_name, action_args = self.ai.decide(self.world)
|
||||
action = self.actions[action_name]
|
||||
action.execute(**action_args)
|
||||
|
||||
def decide(self):
|
||||
"""
|
||||
决定做什么。
|
||||
"""
|
||||
# 目前只做一个事情,就是随机移动。
|
||||
return "Move", {"delta_x": random.randint(-1, 1), "delta_y": random.randint(-1, 1)}
|
||||
event = action.execute(**action_args)
|
||||
return event
|
||||
|
||||
def update_cultivation(self, new_level: int):
|
||||
"""
|
||||
@@ -136,6 +134,9 @@ class Avatar:
|
||||
"realm": self.cultivation_progress.realm.value
|
||||
}
|
||||
|
||||
def is_in_region(self, region: Region) -> bool:
|
||||
return self.tile.region == region
|
||||
|
||||
def get_new_avatar_from_ordinary(world: World, current_year: Year, name: str, age: Age):
|
||||
"""
|
||||
从凡人中来的新修士
|
||||
@@ -162,4 +163,4 @@ def get_new_avatar_from_ordinary(world: World, current_year: Year, name: str, ag
|
||||
cultivation_progress=cultivation_progress,
|
||||
pos_x=pos_x,
|
||||
pos_y=pos_y,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -26,6 +26,12 @@ level_to_stage = {
|
||||
20: Stage.Late_Stage,
|
||||
}
|
||||
|
||||
level_to_break_through = {
|
||||
30: Realm.Foundation_Establishment,
|
||||
60: Realm.Core_Formation,
|
||||
90: Realm.Nascent_Soul,
|
||||
}
|
||||
|
||||
class CultivationProgress:
|
||||
"""
|
||||
修仙进度(包含等级、境界和经验值)
|
||||
@@ -62,7 +68,7 @@ class CultivationProgress:
|
||||
def __str__(self) -> str:
|
||||
return f"{self.realm.value}{self.stage.value}({self.level}级)"
|
||||
|
||||
def get_exp_required(self, target_level: int) -> int:
|
||||
def get_exp_required(self) -> int:
|
||||
"""
|
||||
计算升级到指定等级需要的经验值
|
||||
使用简单的代数加法:base_exp + (level - 1) * increment + realm_bonus
|
||||
@@ -73,17 +79,16 @@ class CultivationProgress:
|
||||
返回:
|
||||
需要的经验值
|
||||
"""
|
||||
if target_level <= 0 or target_level > 120:
|
||||
return 0
|
||||
next_level = self.level + 1
|
||||
|
||||
base_exp = 100 # 基础经验值
|
||||
increment = 50 # 每级增加50点经验值
|
||||
|
||||
# 基础经验值计算
|
||||
exp_required = base_exp + (target_level - 1) * increment
|
||||
exp_required = base_exp + (next_level - 1) * increment
|
||||
|
||||
# 境界加成:每跨越一个境界,额外增加1000点经验值
|
||||
realm_bonus = (target_level // 30) * 1000
|
||||
realm_bonus = (next_level // 30) * 1000
|
||||
|
||||
return exp_required + realm_bonus
|
||||
|
||||
@@ -94,7 +99,7 @@ class CultivationProgress:
|
||||
返回:
|
||||
如果经验值足够升级则返回True
|
||||
"""
|
||||
required_exp = self.get_exp_required(self.level + 1)
|
||||
required_exp = self.get_exp_required()
|
||||
return self.exp >= required_exp
|
||||
|
||||
def get_exp_progress(self) -> tuple[int, int]:
|
||||
@@ -104,7 +109,7 @@ class CultivationProgress:
|
||||
返回:
|
||||
(当前经验值, 升级所需经验值)
|
||||
"""
|
||||
required_exp = self.get_exp_required(self.level + 1)
|
||||
required_exp = self.get_exp_required()
|
||||
return self.exp, required_exp
|
||||
|
||||
def add_exp(self, exp_amount: int) -> bool:
|
||||
@@ -130,3 +135,17 @@ class CultivationProgress:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def break_through(self):
|
||||
"""
|
||||
突破境界
|
||||
"""
|
||||
self.level += 1
|
||||
self.realm = self.get_realm(self.level)
|
||||
self.stage = self.get_stage(self.level)
|
||||
|
||||
def can_break_through(self) -> bool:
|
||||
"""
|
||||
检查是否可以突破
|
||||
"""
|
||||
return self.level in level_to_break_through.keys()
|
||||
@@ -12,4 +12,8 @@ class Event:
|
||||
content: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.year}年{self.month}月: {self.content}"
|
||||
return f"{self.year}年{self.month}月: {self.content}"
|
||||
|
||||
class NullEvent:
|
||||
def __str__(self) -> str:
|
||||
return ""
|
||||
@@ -39,10 +39,13 @@ class Region():
|
||||
description: str
|
||||
essence: Essence
|
||||
id: int = field(init=False)
|
||||
|
||||
center_loc: tuple[int, int] = field(init=False)
|
||||
area: int = field(init=False)
|
||||
|
||||
def __post_init__(self):
|
||||
self.id = next(region_id_counter)
|
||||
|
||||
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.id)
|
||||
|
||||
@@ -55,6 +58,7 @@ class Region():
|
||||
# 其他
|
||||
|
||||
default_region = Region(name="平原", description="最普通的平原,没有什么可说的", essence=Essence(density={EssenceType.GOLD: 1, EssenceType.WOOD: 1, EssenceType.WATER: 1, EssenceType.FIRE: 1, EssenceType.EARTH: 1}))
|
||||
default_region.area = 1 # 默认区域面积为1
|
||||
|
||||
@dataclass
|
||||
class Tile():
|
||||
@@ -70,6 +74,7 @@ class Map():
|
||||
"""
|
||||
def __init__(self, width: int, height: int):
|
||||
self.tiles = {}
|
||||
self.regions = {}
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
@@ -90,10 +95,39 @@ class Map():
|
||||
创建一个region。
|
||||
"""
|
||||
region = Region(name=name, description=description, essence=essence)
|
||||
center_loc = self.get_center_locs(locs)
|
||||
for loc in locs:
|
||||
self.tiles[loc].region = region
|
||||
region.center_loc = center_loc
|
||||
region.area = len(locs)
|
||||
self.regions[region.id] = region
|
||||
return region
|
||||
|
||||
def get_center_locs(self, locs: list[tuple[int, int]]) -> tuple[int, int]:
|
||||
"""
|
||||
获取locs的中心位置。
|
||||
如果几何中心恰好在位置列表中,返回几何中心;
|
||||
否则返回距离几何中心最近的实际位置。
|
||||
"""
|
||||
if not locs:
|
||||
return (0, 0)
|
||||
|
||||
# 分别计算x和y坐标的平均值
|
||||
avg_x = sum(loc[0] for loc in locs) // len(locs)
|
||||
avg_y = sum(loc[1] for loc in locs) // len(locs)
|
||||
center = (avg_x, avg_y)
|
||||
|
||||
# 如果几何中心恰好在位置列表中,直接返回
|
||||
if center in locs:
|
||||
return center
|
||||
|
||||
# 否则找到距离几何中心最近的实际位置
|
||||
def distance_squared(loc: tuple[int, int]) -> int:
|
||||
"""计算到中心点的距离平方(避免开方运算)"""
|
||||
return (loc[0] - avg_x) ** 2 + (loc[1] - avg_y) ** 2
|
||||
|
||||
return min(locs, key=distance_squared)
|
||||
|
||||
def get_region(self, x: int, y: int) -> Region | None:
|
||||
"""
|
||||
获取一个region。
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from src.classes.tile import Map
|
||||
from src.classes.calendar import Year, Month
|
||||
|
||||
@dataclass
|
||||
class World():
|
||||
map: Map
|
||||
map: Map
|
||||
year: Year
|
||||
month: Month
|
||||
@@ -6,7 +6,7 @@ from src.sim.simulator import Simulator
|
||||
from src.classes.world import World
|
||||
from src.classes.tile import TileType
|
||||
from src.classes.avatar import Avatar, Gender
|
||||
from src.sim.event import Event
|
||||
from src.classes.event import Event
|
||||
|
||||
|
||||
class Front:
|
||||
@@ -206,7 +206,7 @@ class Front:
|
||||
def _draw_year_month_info(self, y_pos: int, padding: int):
|
||||
"""绘制年月信息"""
|
||||
# 获取年月数据
|
||||
year = int(self.simulator.year)
|
||||
year = int(self.simulator.world.year)
|
||||
month_num = self._get_month_number()
|
||||
|
||||
# 构建年月文本
|
||||
@@ -222,7 +222,7 @@ class Front:
|
||||
def _get_month_number(self) -> int:
|
||||
"""获取月份数字"""
|
||||
try:
|
||||
month_num = list(type(self.simulator.month)).index(self.simulator.month) + 1
|
||||
month_num = list(type(self.simulator.world.month)).index(self.simulator.world.month) + 1
|
||||
return month_num
|
||||
except Exception:
|
||||
return 1
|
||||
@@ -279,39 +279,31 @@ class Front:
|
||||
m = self.margin
|
||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
||||
|
||||
# 收集每个region的所有地块中心点
|
||||
region_to_points = self._collect_region_points(map_obj, ts, m)
|
||||
|
||||
if not region_to_points:
|
||||
return None
|
||||
|
||||
# 绘制每个region的标签
|
||||
hovered_region = None
|
||||
for region, points in region_to_points.items():
|
||||
if not points:
|
||||
continue
|
||||
|
||||
# 计算质心
|
||||
avg_x = sum(p[0] for p in points) // len(points)
|
||||
avg_y = sum(p[1] for p in points) // len(points)
|
||||
|
||||
for region in map_obj.regions.values():
|
||||
name = getattr(region, "name", None)
|
||||
if not name:
|
||||
continue
|
||||
|
||||
# 计算字体大小
|
||||
font_size = self._calculate_font_size(len(points))
|
||||
# 使用region的center_loc计算屏幕位置
|
||||
center_x, center_y = region.center_loc
|
||||
screen_x = m + center_x * ts + ts // 2
|
||||
screen_y = m + center_y * ts + ts // 2
|
||||
|
||||
# 计算字体大小(基于region面积)
|
||||
font_size = self._calculate_font_size_by_area(region.area)
|
||||
region_font = self._get_region_font(font_size)
|
||||
|
||||
# 渲染文字
|
||||
text_surface = region_font.render(str(name), True, self.colors["text"])
|
||||
shadow_surface = region_font.render(str(name), True, (0, 0, 0))
|
||||
|
||||
# 计算位置
|
||||
# 计算位置(居中显示)
|
||||
text_w = text_surface.get_width()
|
||||
text_h = text_surface.get_height()
|
||||
x = int(avg_x - text_w / 2)
|
||||
y = int(avg_y - text_h / 2)
|
||||
x = int(screen_x - text_w / 2)
|
||||
y = int(screen_y - text_h / 2)
|
||||
|
||||
# 检测鼠标悬停
|
||||
if (x <= mouse_x <= x + text_w and y <= mouse_y <= y + text_h):
|
||||
@@ -323,23 +315,10 @@ class Front:
|
||||
|
||||
return hovered_region
|
||||
|
||||
def _collect_region_points(self, map_obj, ts, m):
|
||||
"""收集region的点位信息"""
|
||||
region_to_points = {}
|
||||
|
||||
for (x, y), tile in getattr(map_obj, "tiles", {}).items():
|
||||
if getattr(tile, "region", None) is None:
|
||||
continue
|
||||
|
||||
region_obj = tile.region
|
||||
cx = m + x * ts + ts // 2
|
||||
cy = m + y * ts + ts // 2
|
||||
region_to_points.setdefault(region_obj, []).append((cx, cy))
|
||||
|
||||
return region_to_points
|
||||
|
||||
def _calculate_font_size(self, area):
|
||||
"""根据区域大小计算字体大小"""
|
||||
|
||||
def _calculate_font_size_by_area(self, area):
|
||||
"""根据区域面积计算字体大小"""
|
||||
base = int(self.tile_size * 1.1)
|
||||
growth = int(max(0, min(24, (area ** 0.5))))
|
||||
return max(16, min(40, base + growth))
|
||||
|
||||
@@ -3,15 +3,12 @@ import random
|
||||
from src.classes.calendar import Month, Year, next_month
|
||||
from src.classes.avatar import Avatar, get_new_avatar_from_ordinary
|
||||
from src.classes.age import Age
|
||||
from src.classes.avatar import Gender
|
||||
from src.classes.world import World
|
||||
from src.sim.event import Event
|
||||
from src.classes.event import Event, NullEvent
|
||||
|
||||
class Simulator:
|
||||
def __init__(self, world: World):
|
||||
self.avatars = {} # dict of str -> Avatar
|
||||
self.year = Year(1)
|
||||
self.month = Month.JANUARY
|
||||
self.world = world
|
||||
self.brith_rate = 0.01
|
||||
|
||||
@@ -28,12 +25,14 @@ class Simulator:
|
||||
|
||||
# 结算角色行为
|
||||
for avatar_id, avatar in self.avatars.items():
|
||||
avatar.act()
|
||||
event = avatar.act()
|
||||
if event is not NullEvent:
|
||||
events.append(event)
|
||||
if avatar.death_by_old_age():
|
||||
death_avatar_ids.append(avatar_id)
|
||||
event = Event(self.year, self.month, f"{avatar.name} 老死了,时年{avatar.age.get_age()}岁")
|
||||
event = Event(self.world.year, self.world.month, f"{avatar.name} 老死了,时年{avatar.age.get_age()}岁")
|
||||
events.append(event)
|
||||
avatar.update_age(self.month, self.year)
|
||||
avatar.update_age(self.world.month, self.world.year)
|
||||
|
||||
# 删除死亡的角色
|
||||
for avatar_id in death_avatar_ids:
|
||||
@@ -43,12 +42,12 @@ class Simulator:
|
||||
if random.random() < self.brith_rate:
|
||||
name = f"无名"
|
||||
age = random.randint(16, 60)
|
||||
new_avatar = get_new_avatar_from_ordinary(self.world, self.year, name, Age(age))
|
||||
new_avatar = get_new_avatar_from_ordinary(self.world, self.world.year, name, Age(age))
|
||||
self.avatars[new_avatar.id] = new_avatar
|
||||
event = Event(self.year, self.month, f"{new_avatar.name}晋升为修士了。")
|
||||
event = Event(self.world.year, self.world.month, f"{new_avatar.name}晋升为修士了。")
|
||||
events.append(event)
|
||||
|
||||
# 最后结算年月
|
||||
self.month, self.year = next_month(self.month, self.year)
|
||||
self.world.month, self.world.year = next_month(self.world.month, self.world.year)
|
||||
|
||||
return events
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import os
|
||||
import sys
|
||||
import random
|
||||
import uuid
|
||||
from typing import List, Tuple, Dict, Any
|
||||
|
||||
# 将项目根目录加入 Python 路径,确保可以导入 `src` 包
|
||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
if PROJECT_ROOT not in sys.path:
|
||||
sys.path.insert(0, PROJECT_ROOT)
|
||||
|
||||
# 依赖项目内部模块
|
||||
from src.front.front import Front
|
||||
from src.sim.simulator import Simulator
|
||||
@@ -21,7 +14,7 @@ from src.classes.essence import Essence, EssenceType
|
||||
from src.classes.cultivation import CultivationProgress
|
||||
from src.classes.root import Root
|
||||
from src.classes.age import Age
|
||||
from create_map import create_cultivation_world_map
|
||||
from src.tools.create_map import create_cultivation_world_map
|
||||
|
||||
|
||||
def clamp(value: int, lo: int, hi: int) -> int:
|
||||
@@ -92,15 +85,13 @@ def main():
|
||||
# 为了每次更丰富,使用随机种子;如需复现可将 seed 固定
|
||||
|
||||
game_map = create_cultivation_world_map()
|
||||
world = World(map=game_map)
|
||||
world = World(map=game_map, year=Year(100), month=Month.JANUARY)
|
||||
|
||||
# 设置模拟器从第100年开始
|
||||
# 创建模拟器
|
||||
sim = Simulator(world)
|
||||
sim.year = Year(100) # 设置初始年份为100年
|
||||
sim.month = Month.JANUARY # 设置初始月份为1月
|
||||
|
||||
# 创建角色,传入当前年份确保年龄与生日匹配
|
||||
sim.avatars.update(make_avatars(world, count=14, current_year=sim.year))
|
||||
sim.avatars.update(make_avatars(world, count=14, current_year=world.year))
|
||||
|
||||
front = Front(
|
||||
simulator=sim,
|
||||
|
||||
@@ -14,7 +14,7 @@ def test_basic():
|
||||
for y in range(2):
|
||||
map.create_tile(x, y, TileType.PLAIN)
|
||||
|
||||
world = World(map=map)
|
||||
world = World(map=map, year=Year(1), month=Month.JANUARY)
|
||||
|
||||
avatar = Avatar(
|
||||
world=world,
|
||||
|
||||
@@ -18,13 +18,13 @@ def test_simulator_step_moves_avatar_and_sets_tile():
|
||||
for y in range(3):
|
||||
game_map.create_tile(x, y, TileType.PLAIN)
|
||||
|
||||
world = World(map=game_map)
|
||||
world = World(map=game_map, year=Year(1), month=Month.JANUARY)
|
||||
|
||||
# 将角色放在地图中心,避免越界
|
||||
avatar = Avatar(
|
||||
world=world,
|
||||
name="Tester",
|
||||
id=1,
|
||||
id="1",
|
||||
birth_month=Month.JANUARY,
|
||||
birth_year=Year(2000),
|
||||
age=20,
|
||||
@@ -34,8 +34,8 @@ def test_simulator_step_moves_avatar_and_sets_tile():
|
||||
)
|
||||
|
||||
|
||||
sim = Simulator()
|
||||
sim.avatars.append(avatar)
|
||||
sim = Simulator(world)
|
||||
sim.avatars["1"] = avatar
|
||||
|
||||
# 执行一步模拟
|
||||
sim.step()
|
||||
|
||||
Reference in New Issue
Block a user