diff --git a/docs/contests/RL9/pve/game/actions.md b/docs/contests/RL9/pve/game/actions.md index 037c4ede..cf99d55b 100644 --- a/docs/contests/RL9/pve/game/actions.md +++ b/docs/contests/RL9/pve/game/actions.md @@ -1,6 +1,8 @@ # 动作空间 -PVE 使用 **8 个离散动作**: +PVE 使用 **28 个离散动作**(`N_ACTIONS = 28`),分为基础操作、商品交易、生产链、科技四大类。 + +## 基础动作(0–5) | 编号 | 动作 | 含义 | 有效性条件 | |:----:|:----:|------|------------| @@ -9,18 +11,68 @@ PVE 使用 **8 个离散动作**: | 2 | `MOVE_DOWN` | 向下移动 (x+1) | 同上 | | 3 | `MOVE_LEFT` | 向左移动 (y−1) | 同上 | | 4 | `MOVE_RIGHT` | 向右移动 (y+1) | 同上 | -| 5 | `BUY` | 在相邻市场买最便宜的可负担商品 | Manhattan ≤1 有市场,背包有空间,现金充足 | -| 6 | `SELL` | 在相邻市场卖出背包内所有商品 | Manhattan ≤1 有市场,背包有商品 | -| 7 | `HARVEST` | 从附近资源点采集原材料 | Manhattan ≤2 有未耗尽资源,背包有空间 | +| 5 | `BUY` | 在相邻市场购买利润最高的可负担商品 | Manhattan ≤1 有市场,背包有空间,现金充足 | + +## 卖出动作(6–10,按商品类型) + +| 编号 | 动作 | 含义 | 有效性条件 | +|:----:|:----:|------|------------| +| 6 | `SELL_0` | 卖出背包内所有**半导体** | Manhattan ≤1 有市场,背包有该商品 | +| 7 | `SELL_1` | 卖出背包内所有**药品** | 同上 | +| 8 | `SELL_2` | 卖出背包内所有**小商品** | 同上 | +| 9 | `SELL_3` | 卖出背包内所有**服饰** | 同上 | +| 10 | `SELL_4` | 卖出背包内所有**食品** | 同上 | + +- **SELL_pid**:卖出指定类型的所有商品,获得当前市场价 +- 卖出成功时:`score += revenue × score_factor`(默认 ×10),`money += revenue` + +## 生产链动作(11–18) + +| 编号 | 动作 | 含义 | 有效性条件 | +|:----:|:----:|------|------------| +| 11 | `HARVEST` | 从附近资源点采集原材料 | Manhattan ≤2 有未耗尽资源,背包有空间 | +| 12 | `DEPOSIT` | 将背包原材料存入工厂 | 在工厂格,背包 raw_inv > 0 | +| 13 | `PRODUCE_0` | 工厂开始生产**半导体**(消耗 5 原材料) | 在工厂格,工厂 raw_stock ≥ 5,生产队列未满 | +| 14 | `PRODUCE_1` | 工厂开始生产**药品**(消耗 3 原材料) | 在工厂格,工厂 raw_stock ≥ 3,生产队列未满 | +| 15 | `PRODUCE_2` | 工厂开始生产**小商品**(消耗 1 原材料) | 在工厂格,工厂 raw_stock ≥ 1,生产队列未满 | +| 16 | `PRODUCE_3` | 工厂开始生产**服饰**(消耗 4 原材料) | 在工厂格,工厂 raw_stock ≥ 4,生产队列未满 | +| 17 | `PRODUCE_4` | 工厂开始生产**食品**(消耗 2 原材料) | 在工厂格,工厂 raw_stock ≥ 2,生产队列未满 | +| 18 | `LOAD` | 从工厂仓库装载已完成成品到背包 | 在工厂格,工厂有成品,背包有空间 | + +- **HARVEST**:采集范围 2 格(Manhattan 距离),每次采集 `unit_harvest_rate × time_step` 单位 +- **DEPOSIT**:将背包中原材料存入工厂仓库,供后续生产使用 +- **PRODUCE_pid**:将工厂原材料转换为产品,加入生产队列 +- **LOAD**:从工厂仓库取出已完成的成品装入背包 + +## 算力与科技动作(19–27) + +| 编号 | 动作 | 含义 | 消耗 | 前置 | +|:----:|:----:|------|:----:|:----:| +| 19 | `OCCUPY` | 推进相邻算力中心的占领进度 | — | — | +| 20 | `TECH_0` | **降低成本**:商品购买成本 −2 | 50 算力 | — | +| 21 | `TECH_1` | **效率提升**:生产时间 ×0.5 | 40 算力 | — | +| 22 | `TECH_2` | **市场营销**:卖价 ×1.1 | 80 算力 | — | +| 23 | `TECH_3` | **耐久强化**:单位 max HP +50% | 30 算力 | — | +| 24 | `TECH_4` | **多产线**:工厂 +1 生产线 | 60 算力 | — | +| 25 | `TECH_5` | **路径优化**:移动不再产生 busy tick | 50 算力 | TECH_1 | +| 26 | `TECH_6` | **市场分析**:观测中标记已购买(可重复) | 40 算力 | — | +| 27 | `TECH_7` | **算力扩张**:算力积累速率 +30% | 70 算力 | — | + +- **OCCUPY**:需相邻有未开放的算力中心,每次 tick 推进 `time_step` 秒进度 +- **TECH_0~5, TECH_7**:持久科技,每种只能购买一次,在工厂格执行 +- **TECH_6**:非持久科技,可重复购买 +- 科技动作需要在工厂格、算力足够、前置科技满足时才有效 + +## 通用规则 -- **BUY**:自动购买当前市场价格最低的可负担商品 -- **SELL**:一次性卖出背包中所有商品,获得当前市场价 -- **HARVEST**:采集范围 2 格(Manhattan 距离) -- 执行无效动作不会报错,但受到 **-0.05 分惩罚**并浪费步数 +- `busy_ticks > 0` 时单位忙碌,**仅 WAIT 有效**,其他动作均被掩码屏蔽 +- 移动默认消耗 1 busy_tick(购买 TECH_5 后变为 0) +- BUY/SELL/LOAD 消耗 busy_ticks(`0.25 / time_step` 个 tick) +- 执行无效动作不会报错,但受到 **−0.05 奖励惩罚**并浪费步数 ## 动作掩码 -环境提供 `action_masks()` 方法,返回 `(8,)` 布尔数组,`True` 表示该动作当前有效。使用 `MaskablePPO` 可以自动过滤无效动作: +环境提供 `action_masks()` 方法,返回 `(28,)` 布尔数组,`True` 表示该动作当前有效。使用 `MaskablePPO` 可以自动过滤无效动作: ```python from sb3_contrib import MaskablePPO diff --git a/docs/contests/RL9/pve/game/state.md b/docs/contests/RL9/pve/game/state.md index 8955d676..553267df 100644 --- a/docs/contests/RL9/pve/game/state.md +++ b/docs/contests/RL9/pve/game/state.md @@ -1,5 +1,13 @@ # 游戏状态 +## 时间系统 + +| 参数 | 值 | +|:----:|:--:| +| 每 tick 时长 | 0.25s | +| easy/medium 总时长 | 300s(1200 ticks) | +| hard 总时长 | 500s(2000 ticks) | + ## 单位属性 | 属性 | 值 | @@ -9,8 +17,10 @@ | 采集速率 | 10/s | | 算力中心占领时间 | 10s | -- 背包分为**原材料**和**成品**两部分 -- `busy_ticks > 0` 时单位忙碌,忽略新指令 +- 背包分为**原材料**(`raw_inv`)和**成品**(`prod_inv[pid]`)两部分 +- `busy_ticks > 0` 时单位忙碌,仅 WAIT 有效 +- 移动默认消耗 1 busy_tick(购买 path_optimization 科技后变为 0) +- BUY/SELL/LOAD 消耗 `int(0.25 / time_step)` 个 busy_tick ## 工厂 @@ -20,10 +30,72 @@ | 仓储上限 | 300 | | 初始生产线数 | 3 | -## 算力产出 +### 生产系统 + +工厂将原材料加工为成品。生产队列最大长度 = `生产线数 × 5`。 + +生产流程: +1. 采集原材料(HARVEST) +2. 存入工厂(DEPOSIT) +3. 排队生产(PRODUCE_pid)——消耗原材料,加入生产队列 +4. 等待工厂 tick 推进完成 +5. 装载成品(LOAD) + +生产时间受 efficiency 科技影响(×0.5)。 -- 每个已占领算力中心:**1 算力/秒** -- 可花费算力招募新单位(Phase 2) +## 商品 + +| ID | 名称 | 购买成本 | 原材料消耗 | 市场价格范围 | 生产时间 | +|:--:|:----:|:--------:|:----------:|:------------:|:--------:| +| 0 | 半导体 | 10 | 5 | 40–120 | 5.0s | +| 1 | 药品 | 5 | 3 | 20–60 | 4.0s | +| 2 | 小商品 | 1 | 1 | 4–12 | 2.0s | +| 3 | 服饰 | 8 | 4 | 32–96 | 6.0s | +| 4 | 食品 | 3 | 2 | 12–24 | 1.0s | + +- 购买成本受 cost_reduction 科技影响(−2,最低为 0) +- 生产时间受 efficiency 科技影响(×0.5) + +## 市场定价 + +每个市场对每种商品有独立的正弦价格函数: + +``` +price(t) = base + amplitude × (1 + sin(2π·t / period + phase)) / 2 +``` + +- 不同市场的价格**相位随机不同步**,套利窗口随时间移动 +- `price_volatility` 控制波动幅度(easy=0.3, medium=1.0, hard=2.0) +- 卖价受 marketing 科技影响(×1.1) + +## 算力系统 + +- **基础产出**:每个已开放算力中心产出 **1 算力/秒** +- 购买 compute_expansion 科技后:+30% 算力速率 +- 可消耗算力招募新单位(40 算力/个,最多 5 个单位)——Phase 2 + +### 算力中心 + +- 占领方式:在相邻格执行 OCCUPY,每次 tick 推进 0.25s 进度 +- 进度达到 `unit_occupy_time`(10s)后开放 +- 开放后持续产出算力 + +## 科技树 + +| 键名 | 效果 | 消耗 | 前置 | 持久 | +|:-----|------|:----:|:----:|:----:| +| cost_reduction | 商品购买成本 −2 | 50 | — | ✓ | +| efficiency | 生产时间 ×0.5 | 40 | — | ✓ | +| marketing | 卖价 ×1.1 | 80 | — | ✓ | +| durability | 单位 max HP +50% | 30 | — | ✓ | +| multi_line | 工厂 +1 生产线 | 60 | — | ✓ | +| path_optimization | 移动不产生 busy_tick | 50 | efficiency | ✓ | +| market_analysis | 观测中标记已购买 | 40 | — | ✗ | +| compute_expansion | 算力速率 +30% | 70 | — | ✓ | + +- 持久科技每种只能购买一次 +- market_analysis 可重复购买 +- 科技在工厂格执行 ## 资源再生 @@ -34,4 +106,12 @@ regen(t) = rate × (1 + sin(2π·t / period)) / 2 ``` - 再生倍率:easy=2.0, medium=1.0, hard=0.5 -- 资源耗尽(`depleted=True`)后停止再生 +- 资源耗尽(累计采集量 ≥ max_stock)后停止再生 +- 最大库存为初始库存的 2 倍 + +## 得分与终止 + +- **得分**:`score += revenue × score_factor`(默认 ×10),仅在 SELL 成功时增加 +- **terminated**:`money < 0`(破产) +- **truncated**:`step >= max_steps`(时间耗尽) +- 最终排名以多 seed 下的 `info["score"]` 平均为准 diff --git a/docs/contests/RL9/pve/interface/gym.md b/docs/contests/RL9/pve/interface/gym.md index 6435ad12..9990a579 100644 --- a/docs/contests/RL9/pve/interface/gym.md +++ b/docs/contests/RL9/pve/interface/gym.md @@ -33,8 +33,8 @@ obs, info = env.reset(seed=0) ```python obs, reward, terminated, truncated, info = env.step(action) -# action: int, 0-7 -# obs: np.ndarray, shape (32,), dtype float32 +# action: int, 0-27 +# obs: np.ndarray, shape (58,), dtype float32 # reward: float # terminated: bool (money < 0,破产) # truncated: bool (步数耗尽,正常结束) @@ -44,36 +44,100 @@ obs, reward, terminated, truncated, info = env.step(action) ### `action_masks()` ```python -mask = env.action_masks() # np.ndarray[bool], shape (8,) +mask = env.action_masks() # np.ndarray[bool], shape (28,) ``` 返回当前有效的动作掩码。 -## 观测向量(32 维 float32) +## 观测向量(58 维 float32) + +### 单位状态([0–9]) | 索引 | 含义 | 归一化 | |:----:|------|--------| | 0–1 | 单位位置 (x, y) | / (H, W) | | 2 | 单位 HP | / max_hp | -| 3 | 原材料背包占比 | raw_inv / capacity | -| 4 | 成品背包占比 | prod_inv / capacity | -| 5 | busy 倒计时 | / 10,截断到 1 | -| 6 | 现金 | log10(money+1) / 5 | -| 7 | 算力 | / 100,截断到 2 | -| 8 | 游戏进度 | time / max_time | -| 9 | 价格相位 sin | sin(2π·t / period) | -| 10 | 价格相位 cos | cos(2π·t / period) | -| 11 | 工厂原料库存 | / storage_cap | -| 12 | 工厂成品库存 | / storage_cap | -| 13 | 生产队列长度 | / 10,截断到 1 | -| 14–16 | 资源点 0 | 相对位置 (dx/H, dy/W) + 库存比 | -| 17–19 | 资源点 1 | 同上 | -| 20–22 | 算力中心 0 | 相对位置 (dx/H, dy/W) + is_open | -| 23–25 | 算力中心 1 | 同上 | -| 26–28 | 市场 0 | 相对位置 (dx/H, dy/W) + best_price | -| 29–31 | 市场 1 | 同上 | - -> 如果实体数量不足(如只有 2 个市场),多余索引保持 0。 +| 3 | 原材料背包 | raw_inv / capacity | +| 4 | 半导体背包 | prod_inv[0] / capacity | +| 5 | 药品背包 | prod_inv[1] / capacity | +| 6 | 小商品背包 | prod_inv[2] / capacity | +| 7 | 服饰背包 | prod_inv[3] / capacity | +| 8 | 食品背包 | prod_inv[4] / capacity | +| 9 | busy 倒计时 | / 10,截断到 1 | + +### 经济状态([10–14]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 10 | 现金(对数) | log10(money+1) / 5 | +| 11 | 算力 | / 100,截断到 2 | +| 12 | 游戏进度 | time / max_game_time | +| 13 | 价格相位 sin | sin(2π·t / market_period) | +| 14 | 价格相位 cos | cos(2π·t / market_period) | + +### 工厂状态([15–21]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 15 | 工厂原材料库存 | / storage_cap | +| 16 | 工厂半导体库存 | / storage_cap | +| 17 | 工厂药品库存 | / storage_cap | +| 18 | 工厂小商品库存 | / storage_cap | +| 19 | 工厂服饰库存 | / storage_cap | +| 20 | 工厂食品库存 | / storage_cap | +| 21 | 生产队列长度 | / 10,截断到 1 | + +### 资源点([22–27]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 22–23 | 资源点 0 相对位置 (dx, dy) | / (H, W) | +| 24 | 资源点 0 库存比例 | stock / max_stock | +| 25–26 | 资源点 1 相对位置 (dx, dy) | / (H, W) | +| 27 | 资源点 1 库存比例 | stock / max_stock | + +### 算力中心([28–35]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 28–29 | 算力中心 0 相对位置 (dx, dy) | / (H, W) | +| 30 | 算力中心 0 是否开放 | 0 或 1 | +| 31 | 算力中心 0 占领进度 | / unit_occupy_time | +| 32–33 | 算力中心 1 相对位置 (dx, dy) | / (H, W) | +| 34 | 算力中心 1 是否开放 | 0 或 1 | +| 35 | 算力中心 1 占领进度 | / unit_occupy_time | + +### 市场([36–49]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 36–37 | 市场 0 相对位置 (dx, dy) | / (H, W) | +| 38 | 市场 0 半导体价格 | 按 val_range 归一化 | +| 39 | 市场 0 药品价格 | 同上 | +| 40 | 市场 0 小商品价格 | 同上 | +| 41 | 市场 0 服饰价格 | 同上 | +| 42 | 市场 0 食品价格 | 同上 | +| 43–44 | 市场 1 相对位置 (dx, dy) | / (H, W) | +| 45 | 市场 1 半导体价格 | 按 val_range 归一化 | +| 46 | 市场 1 药品价格 | 同上 | +| 47 | 市场 1 小商品价格 | 同上 | +| 48 | 市场 1 服饰价格 | 同上 | +| 49 | 市场 1 食品价格 | 同上 | + +### 科技状态([50–57]) + +| 索引 | 含义 | +|:----:|------| +| 50 | 是否已购买 cost_reduction | +| 51 | 是否已购买 efficiency | +| 52 | 是否已购买 marketing | +| 53 | 是否已购买 durability | +| 54 | 是否已购买 multi_line | +| 55 | 是否已购买 path_optimization | +| 56 | 是否已购买 market_analysis(可重复) | +| 57 | 是否已购买 compute_expansion | + +> 如果实体数量不足(如只有 1 个市场),多余索引保持 0。 ## 禁止访问的对象 @@ -99,7 +163,7 @@ while True: if info["money"] > 50: action = 5 # BUY else: - action = 7 # HARVEST + action = 11 # HARVEST obs, reward, terminated, truncated, info = env.step(action) if terminated or truncated: break diff --git a/docs/contests/THUAI9/README.md b/docs/contests/THUAI9/README.md index e433e5da..7ff67c81 100644 --- a/docs/contests/THUAI9/README.md +++ b/docs/contests/THUAI9/README.md @@ -14,13 +14,13 @@ AI 工厂模拟(THUAI9) | 编程语言 | C++ | Python | | 交互方式 | gRPC 连接服务器 | Gymnasium 本地环境 | | 控制对象 | 1 Team + 3 Character | 1 个单位 | -| 动作 | 连续移动 + 多种操作 | 8 个离散动作 | -| 得分机制 | 售卖 + 战斗 + 摧毁工厂 | 售卖 × 10 | +| 动作 | 连续移动 + 多种操作 | 28 个离散动作 | +| 得分机制 | 售卖 + 战斗 + 摧毁工厂 | 售卖 × 10(SELL成功时) | | 文档入口 | [`pvp/`](pvp/intro/rule.md) | [`pve/`](pve/intro/rule.md) | ## PVP 概要 -2~4 支队伍在同一地图对抗。每队拥有 1 座工厂和最多 3 个角色(Drone / Robot / AutonomousCar),通过采集资源、生产产品、占领算力中心、市场售卖、攻击敌方来获取分数。游戏时长 2 分钟,得分高者获胜。 +2~4 支队伍在同一地图对抗。每队拥有 1 座工厂和最多 3 个角色(Drone / Robot / AutonomousCar),通过采集资源、生产产品、占领算力中心、市场售卖、攻击敌方来获取分数。游戏时长 10 分钟,得分高者获胜。 ## PVE 概要 @@ -38,7 +38,7 @@ AI 工厂模拟(THUAI9) ### PVE 赛道(Python) -不熟悉强化学习的可以访问https://github.com/konpoku/THUAI9-RL,在小项目中学习一下 +不熟悉强化学习的可以访问https://github.com/konpoku/THUAI9-RL ,在小项目中学习一下 详细的使用说明参照选手包里的logic\pve\docs\CONTESTANT_GUIDE.md diff --git a/docs/contests/THUAI9/pve/game/actions.md b/docs/contests/THUAI9/pve/game/actions.md index 037c4ede..cf99d55b 100644 --- a/docs/contests/THUAI9/pve/game/actions.md +++ b/docs/contests/THUAI9/pve/game/actions.md @@ -1,6 +1,8 @@ # 动作空间 -PVE 使用 **8 个离散动作**: +PVE 使用 **28 个离散动作**(`N_ACTIONS = 28`),分为基础操作、商品交易、生产链、科技四大类。 + +## 基础动作(0–5) | 编号 | 动作 | 含义 | 有效性条件 | |:----:|:----:|------|------------| @@ -9,18 +11,68 @@ PVE 使用 **8 个离散动作**: | 2 | `MOVE_DOWN` | 向下移动 (x+1) | 同上 | | 3 | `MOVE_LEFT` | 向左移动 (y−1) | 同上 | | 4 | `MOVE_RIGHT` | 向右移动 (y+1) | 同上 | -| 5 | `BUY` | 在相邻市场买最便宜的可负担商品 | Manhattan ≤1 有市场,背包有空间,现金充足 | -| 6 | `SELL` | 在相邻市场卖出背包内所有商品 | Manhattan ≤1 有市场,背包有商品 | -| 7 | `HARVEST` | 从附近资源点采集原材料 | Manhattan ≤2 有未耗尽资源,背包有空间 | +| 5 | `BUY` | 在相邻市场购买利润最高的可负担商品 | Manhattan ≤1 有市场,背包有空间,现金充足 | + +## 卖出动作(6–10,按商品类型) + +| 编号 | 动作 | 含义 | 有效性条件 | +|:----:|:----:|------|------------| +| 6 | `SELL_0` | 卖出背包内所有**半导体** | Manhattan ≤1 有市场,背包有该商品 | +| 7 | `SELL_1` | 卖出背包内所有**药品** | 同上 | +| 8 | `SELL_2` | 卖出背包内所有**小商品** | 同上 | +| 9 | `SELL_3` | 卖出背包内所有**服饰** | 同上 | +| 10 | `SELL_4` | 卖出背包内所有**食品** | 同上 | + +- **SELL_pid**:卖出指定类型的所有商品,获得当前市场价 +- 卖出成功时:`score += revenue × score_factor`(默认 ×10),`money += revenue` + +## 生产链动作(11–18) + +| 编号 | 动作 | 含义 | 有效性条件 | +|:----:|:----:|------|------------| +| 11 | `HARVEST` | 从附近资源点采集原材料 | Manhattan ≤2 有未耗尽资源,背包有空间 | +| 12 | `DEPOSIT` | 将背包原材料存入工厂 | 在工厂格,背包 raw_inv > 0 | +| 13 | `PRODUCE_0` | 工厂开始生产**半导体**(消耗 5 原材料) | 在工厂格,工厂 raw_stock ≥ 5,生产队列未满 | +| 14 | `PRODUCE_1` | 工厂开始生产**药品**(消耗 3 原材料) | 在工厂格,工厂 raw_stock ≥ 3,生产队列未满 | +| 15 | `PRODUCE_2` | 工厂开始生产**小商品**(消耗 1 原材料) | 在工厂格,工厂 raw_stock ≥ 1,生产队列未满 | +| 16 | `PRODUCE_3` | 工厂开始生产**服饰**(消耗 4 原材料) | 在工厂格,工厂 raw_stock ≥ 4,生产队列未满 | +| 17 | `PRODUCE_4` | 工厂开始生产**食品**(消耗 2 原材料) | 在工厂格,工厂 raw_stock ≥ 2,生产队列未满 | +| 18 | `LOAD` | 从工厂仓库装载已完成成品到背包 | 在工厂格,工厂有成品,背包有空间 | + +- **HARVEST**:采集范围 2 格(Manhattan 距离),每次采集 `unit_harvest_rate × time_step` 单位 +- **DEPOSIT**:将背包中原材料存入工厂仓库,供后续生产使用 +- **PRODUCE_pid**:将工厂原材料转换为产品,加入生产队列 +- **LOAD**:从工厂仓库取出已完成的成品装入背包 + +## 算力与科技动作(19–27) + +| 编号 | 动作 | 含义 | 消耗 | 前置 | +|:----:|:----:|------|:----:|:----:| +| 19 | `OCCUPY` | 推进相邻算力中心的占领进度 | — | — | +| 20 | `TECH_0` | **降低成本**:商品购买成本 −2 | 50 算力 | — | +| 21 | `TECH_1` | **效率提升**:生产时间 ×0.5 | 40 算力 | — | +| 22 | `TECH_2` | **市场营销**:卖价 ×1.1 | 80 算力 | — | +| 23 | `TECH_3` | **耐久强化**:单位 max HP +50% | 30 算力 | — | +| 24 | `TECH_4` | **多产线**:工厂 +1 生产线 | 60 算力 | — | +| 25 | `TECH_5` | **路径优化**:移动不再产生 busy tick | 50 算力 | TECH_1 | +| 26 | `TECH_6` | **市场分析**:观测中标记已购买(可重复) | 40 算力 | — | +| 27 | `TECH_7` | **算力扩张**:算力积累速率 +30% | 70 算力 | — | + +- **OCCUPY**:需相邻有未开放的算力中心,每次 tick 推进 `time_step` 秒进度 +- **TECH_0~5, TECH_7**:持久科技,每种只能购买一次,在工厂格执行 +- **TECH_6**:非持久科技,可重复购买 +- 科技动作需要在工厂格、算力足够、前置科技满足时才有效 + +## 通用规则 -- **BUY**:自动购买当前市场价格最低的可负担商品 -- **SELL**:一次性卖出背包中所有商品,获得当前市场价 -- **HARVEST**:采集范围 2 格(Manhattan 距离) -- 执行无效动作不会报错,但受到 **-0.05 分惩罚**并浪费步数 +- `busy_ticks > 0` 时单位忙碌,**仅 WAIT 有效**,其他动作均被掩码屏蔽 +- 移动默认消耗 1 busy_tick(购买 TECH_5 后变为 0) +- BUY/SELL/LOAD 消耗 busy_ticks(`0.25 / time_step` 个 tick) +- 执行无效动作不会报错,但受到 **−0.05 奖励惩罚**并浪费步数 ## 动作掩码 -环境提供 `action_masks()` 方法,返回 `(8,)` 布尔数组,`True` 表示该动作当前有效。使用 `MaskablePPO` 可以自动过滤无效动作: +环境提供 `action_masks()` 方法,返回 `(28,)` 布尔数组,`True` 表示该动作当前有效。使用 `MaskablePPO` 可以自动过滤无效动作: ```python from sb3_contrib import MaskablePPO diff --git a/docs/contests/THUAI9/pve/game/state.md b/docs/contests/THUAI9/pve/game/state.md index 8955d676..553267df 100644 --- a/docs/contests/THUAI9/pve/game/state.md +++ b/docs/contests/THUAI9/pve/game/state.md @@ -1,5 +1,13 @@ # 游戏状态 +## 时间系统 + +| 参数 | 值 | +|:----:|:--:| +| 每 tick 时长 | 0.25s | +| easy/medium 总时长 | 300s(1200 ticks) | +| hard 总时长 | 500s(2000 ticks) | + ## 单位属性 | 属性 | 值 | @@ -9,8 +17,10 @@ | 采集速率 | 10/s | | 算力中心占领时间 | 10s | -- 背包分为**原材料**和**成品**两部分 -- `busy_ticks > 0` 时单位忙碌,忽略新指令 +- 背包分为**原材料**(`raw_inv`)和**成品**(`prod_inv[pid]`)两部分 +- `busy_ticks > 0` 时单位忙碌,仅 WAIT 有效 +- 移动默认消耗 1 busy_tick(购买 path_optimization 科技后变为 0) +- BUY/SELL/LOAD 消耗 `int(0.25 / time_step)` 个 busy_tick ## 工厂 @@ -20,10 +30,72 @@ | 仓储上限 | 300 | | 初始生产线数 | 3 | -## 算力产出 +### 生产系统 + +工厂将原材料加工为成品。生产队列最大长度 = `生产线数 × 5`。 + +生产流程: +1. 采集原材料(HARVEST) +2. 存入工厂(DEPOSIT) +3. 排队生产(PRODUCE_pid)——消耗原材料,加入生产队列 +4. 等待工厂 tick 推进完成 +5. 装载成品(LOAD) + +生产时间受 efficiency 科技影响(×0.5)。 -- 每个已占领算力中心:**1 算力/秒** -- 可花费算力招募新单位(Phase 2) +## 商品 + +| ID | 名称 | 购买成本 | 原材料消耗 | 市场价格范围 | 生产时间 | +|:--:|:----:|:--------:|:----------:|:------------:|:--------:| +| 0 | 半导体 | 10 | 5 | 40–120 | 5.0s | +| 1 | 药品 | 5 | 3 | 20–60 | 4.0s | +| 2 | 小商品 | 1 | 1 | 4–12 | 2.0s | +| 3 | 服饰 | 8 | 4 | 32–96 | 6.0s | +| 4 | 食品 | 3 | 2 | 12–24 | 1.0s | + +- 购买成本受 cost_reduction 科技影响(−2,最低为 0) +- 生产时间受 efficiency 科技影响(×0.5) + +## 市场定价 + +每个市场对每种商品有独立的正弦价格函数: + +``` +price(t) = base + amplitude × (1 + sin(2π·t / period + phase)) / 2 +``` + +- 不同市场的价格**相位随机不同步**,套利窗口随时间移动 +- `price_volatility` 控制波动幅度(easy=0.3, medium=1.0, hard=2.0) +- 卖价受 marketing 科技影响(×1.1) + +## 算力系统 + +- **基础产出**:每个已开放算力中心产出 **1 算力/秒** +- 购买 compute_expansion 科技后:+30% 算力速率 +- 可消耗算力招募新单位(40 算力/个,最多 5 个单位)——Phase 2 + +### 算力中心 + +- 占领方式:在相邻格执行 OCCUPY,每次 tick 推进 0.25s 进度 +- 进度达到 `unit_occupy_time`(10s)后开放 +- 开放后持续产出算力 + +## 科技树 + +| 键名 | 效果 | 消耗 | 前置 | 持久 | +|:-----|------|:----:|:----:|:----:| +| cost_reduction | 商品购买成本 −2 | 50 | — | ✓ | +| efficiency | 生产时间 ×0.5 | 40 | — | ✓ | +| marketing | 卖价 ×1.1 | 80 | — | ✓ | +| durability | 单位 max HP +50% | 30 | — | ✓ | +| multi_line | 工厂 +1 生产线 | 60 | — | ✓ | +| path_optimization | 移动不产生 busy_tick | 50 | efficiency | ✓ | +| market_analysis | 观测中标记已购买 | 40 | — | ✗ | +| compute_expansion | 算力速率 +30% | 70 | — | ✓ | + +- 持久科技每种只能购买一次 +- market_analysis 可重复购买 +- 科技在工厂格执行 ## 资源再生 @@ -34,4 +106,12 @@ regen(t) = rate × (1 + sin(2π·t / period)) / 2 ``` - 再生倍率:easy=2.0, medium=1.0, hard=0.5 -- 资源耗尽(`depleted=True`)后停止再生 +- 资源耗尽(累计采集量 ≥ max_stock)后停止再生 +- 最大库存为初始库存的 2 倍 + +## 得分与终止 + +- **得分**:`score += revenue × score_factor`(默认 ×10),仅在 SELL 成功时增加 +- **terminated**:`money < 0`(破产) +- **truncated**:`step >= max_steps`(时间耗尽) +- 最终排名以多 seed 下的 `info["score"]` 平均为准 diff --git a/docs/contests/THUAI9/pve/interface/gym.md b/docs/contests/THUAI9/pve/interface/gym.md index 6435ad12..9990a579 100644 --- a/docs/contests/THUAI9/pve/interface/gym.md +++ b/docs/contests/THUAI9/pve/interface/gym.md @@ -33,8 +33,8 @@ obs, info = env.reset(seed=0) ```python obs, reward, terminated, truncated, info = env.step(action) -# action: int, 0-7 -# obs: np.ndarray, shape (32,), dtype float32 +# action: int, 0-27 +# obs: np.ndarray, shape (58,), dtype float32 # reward: float # terminated: bool (money < 0,破产) # truncated: bool (步数耗尽,正常结束) @@ -44,36 +44,100 @@ obs, reward, terminated, truncated, info = env.step(action) ### `action_masks()` ```python -mask = env.action_masks() # np.ndarray[bool], shape (8,) +mask = env.action_masks() # np.ndarray[bool], shape (28,) ``` 返回当前有效的动作掩码。 -## 观测向量(32 维 float32) +## 观测向量(58 维 float32) + +### 单位状态([0–9]) | 索引 | 含义 | 归一化 | |:----:|------|--------| | 0–1 | 单位位置 (x, y) | / (H, W) | | 2 | 单位 HP | / max_hp | -| 3 | 原材料背包占比 | raw_inv / capacity | -| 4 | 成品背包占比 | prod_inv / capacity | -| 5 | busy 倒计时 | / 10,截断到 1 | -| 6 | 现金 | log10(money+1) / 5 | -| 7 | 算力 | / 100,截断到 2 | -| 8 | 游戏进度 | time / max_time | -| 9 | 价格相位 sin | sin(2π·t / period) | -| 10 | 价格相位 cos | cos(2π·t / period) | -| 11 | 工厂原料库存 | / storage_cap | -| 12 | 工厂成品库存 | / storage_cap | -| 13 | 生产队列长度 | / 10,截断到 1 | -| 14–16 | 资源点 0 | 相对位置 (dx/H, dy/W) + 库存比 | -| 17–19 | 资源点 1 | 同上 | -| 20–22 | 算力中心 0 | 相对位置 (dx/H, dy/W) + is_open | -| 23–25 | 算力中心 1 | 同上 | -| 26–28 | 市场 0 | 相对位置 (dx/H, dy/W) + best_price | -| 29–31 | 市场 1 | 同上 | - -> 如果实体数量不足(如只有 2 个市场),多余索引保持 0。 +| 3 | 原材料背包 | raw_inv / capacity | +| 4 | 半导体背包 | prod_inv[0] / capacity | +| 5 | 药品背包 | prod_inv[1] / capacity | +| 6 | 小商品背包 | prod_inv[2] / capacity | +| 7 | 服饰背包 | prod_inv[3] / capacity | +| 8 | 食品背包 | prod_inv[4] / capacity | +| 9 | busy 倒计时 | / 10,截断到 1 | + +### 经济状态([10–14]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 10 | 现金(对数) | log10(money+1) / 5 | +| 11 | 算力 | / 100,截断到 2 | +| 12 | 游戏进度 | time / max_game_time | +| 13 | 价格相位 sin | sin(2π·t / market_period) | +| 14 | 价格相位 cos | cos(2π·t / market_period) | + +### 工厂状态([15–21]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 15 | 工厂原材料库存 | / storage_cap | +| 16 | 工厂半导体库存 | / storage_cap | +| 17 | 工厂药品库存 | / storage_cap | +| 18 | 工厂小商品库存 | / storage_cap | +| 19 | 工厂服饰库存 | / storage_cap | +| 20 | 工厂食品库存 | / storage_cap | +| 21 | 生产队列长度 | / 10,截断到 1 | + +### 资源点([22–27]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 22–23 | 资源点 0 相对位置 (dx, dy) | / (H, W) | +| 24 | 资源点 0 库存比例 | stock / max_stock | +| 25–26 | 资源点 1 相对位置 (dx, dy) | / (H, W) | +| 27 | 资源点 1 库存比例 | stock / max_stock | + +### 算力中心([28–35]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 28–29 | 算力中心 0 相对位置 (dx, dy) | / (H, W) | +| 30 | 算力中心 0 是否开放 | 0 或 1 | +| 31 | 算力中心 0 占领进度 | / unit_occupy_time | +| 32–33 | 算力中心 1 相对位置 (dx, dy) | / (H, W) | +| 34 | 算力中心 1 是否开放 | 0 或 1 | +| 35 | 算力中心 1 占领进度 | / unit_occupy_time | + +### 市场([36–49]) + +| 索引 | 含义 | 归一化 | +|:----:|------|--------| +| 36–37 | 市场 0 相对位置 (dx, dy) | / (H, W) | +| 38 | 市场 0 半导体价格 | 按 val_range 归一化 | +| 39 | 市场 0 药品价格 | 同上 | +| 40 | 市场 0 小商品价格 | 同上 | +| 41 | 市场 0 服饰价格 | 同上 | +| 42 | 市场 0 食品价格 | 同上 | +| 43–44 | 市场 1 相对位置 (dx, dy) | / (H, W) | +| 45 | 市场 1 半导体价格 | 按 val_range 归一化 | +| 46 | 市场 1 药品价格 | 同上 | +| 47 | 市场 1 小商品价格 | 同上 | +| 48 | 市场 1 服饰价格 | 同上 | +| 49 | 市场 1 食品价格 | 同上 | + +### 科技状态([50–57]) + +| 索引 | 含义 | +|:----:|------| +| 50 | 是否已购买 cost_reduction | +| 51 | 是否已购买 efficiency | +| 52 | 是否已购买 marketing | +| 53 | 是否已购买 durability | +| 54 | 是否已购买 multi_line | +| 55 | 是否已购买 path_optimization | +| 56 | 是否已购买 market_analysis(可重复) | +| 57 | 是否已购买 compute_expansion | + +> 如果实体数量不足(如只有 1 个市场),多余索引保持 0。 ## 禁止访问的对象 @@ -99,7 +163,7 @@ while True: if info["money"] > 50: action = 5 # BUY else: - action = 7 # HARVEST + action = 11 # HARVEST obs, reward, terminated, truncated, info = env.step(action) if terminated or truncated: break diff --git a/docs/contests/THUAI9/pvp/intro/rule.md b/docs/contests/THUAI9/pvp/intro/rule.md index 19e0f5ff..1510ace6 100644 --- a/docs/contests/THUAI9/pvp/intro/rule.md +++ b/docs/contests/THUAI9/pvp/intro/rule.md @@ -3,7 +3,7 @@ - PVP 模式支持 **2~4 支队伍**同时对抗,每支队伍由一名选手控制。 - 所有队伍起始条件完全相同:拥有一座**工厂**(位于地图角落),**100 点初始算力**,**0 点初始资源**。 - 双方需要在地图上采集资源、占领算力中心、生产产品、在市场售卖、攻击敌方单位,尽可能获取更高的分数。游戏结束时得分高者获胜。 -- 每局游戏时长为 **2 分钟(120 秒)**。 +- 每局游戏时长为 **10 分钟(600 秒)**。 - 游戏采用流式通信,服务器每 **50ms** 推送一帧数据,选手的 `play()` 函数每帧被调用一次。 - 选手需要为 **1 个 Team(PlayerID=0)** 和 **最多 3 个 Character(PlayerID=1,2,3)** 分别编写 AI 代码。 @@ -21,7 +21,7 @@ ## 游戏结束条件 -- 游戏时间达到 2 分钟(120 秒),游戏结束并结算最终得分。 +- 游戏时间达到 10 分钟(600 秒),游戏结束并结算最终得分。 - 工厂被摧毁的队伍无法再进行生产和招募,但已存在的角色仍可继续操作。 ## 关键时间参数 @@ -29,7 +29,7 @@ | 参数 | 值 | |:----:|:--:| | 每帧时长 | 50ms | -| 游戏总时长 | 120s | +| 游戏总时长 | 600s | | 工厂被攻击后停摆时间 | 10s | | 算力中心占领基础时间 | 10s | | AskAI 超时 | 60s |