合成游戏

概述

  • 本示例主要展示如何修改初始化文件配置中的4_item.csv文件构造一个复杂的合成游戏。
  • 该游戏的最终目标是合成FancyColorDiamond,具有很长的合成表的合成路径。

具体设计

动植物设计

目前环境中配备了10个being,具体信息可见config/3_being.csv,下面给出它们的主要信息:

名称 介绍 掉落 需求
Pig 击杀掉落Leather、Meat、Tendon StoneSword及以上
Rabbit 兔子 击杀掉落Meat WoodSword及以上
Ox 公牛 击杀掉落Meat IronSword及以上
Goat 击杀掉落Meat WoodSword及以上
Tree 可以采集Branch,击杀额外掉落Wood attack 需要equip任意Axe
AppleTree 苹果树 可以采集Apple,击杀额外掉落Wood attack 需要equip任意Axe
Grass 采集和击杀掉落Cutgrass equip任意具有攻击力装备
Rock 石头 可以采集Stone,击杀额外掉落Stone attack 需要equip任意Pickaxe
IronMine 铁矿 击杀掉落IronOre StonePickaxe及以上
GemMine 宝石矿 击杀掉落GemStone SteelPickaxe及以上

游戏机制为受到的伤害=攻击者攻击力-被攻击者防御力,攻击being时,不产生效果的原因既可能为没有装备对应的装备,也可能是攻击力小于目标的防御力。

合成表设计

名称 描述 合成表 合成需求
StoneSmelter smelter,提供Smelter_A类buff Stone:8  
IronSmelter smelter,提供Smelter_AA类buff IronIngot:8 Smelter_A类及以上buff
SteelSmelter smelter,提供Smelter_AAA类buff SteelIngot:8 Smelter_AA类及以上buff
Spear Weapon,增加攻击力和攻击距离 Branch:1;Cutgrass:2;Stone:1  
Bow Weapon,增加攻击力和攻击距离 Branch:2;Cutgrass:2;Tendon:1  
LeatherArmor Armor,增加防御力 Leather:2;Cutgrass:4  
WoodArmor Armor,增加防御力 Wood:2;Cutgrass:4  
WarmStone Other,增加体温 Stone:5;Cutgrass:5;Torch:1  
ColdStone Other,降低体温 Stone:5;Cutgrass:6;Ice:4  
Coal Wood:2  
IronIngot IronOre:1;Coal:1 Smelter_A类及以上buff
SteelIngot IronIngot:1;Coal:2 Smelter_AA类及以上buff
WoodAxe Weapon,加攻击力,可砍树 Branch:3  
WoodPickaxe Weapon,加攻击力,可采矿石 Branch:4  
WoodSword Weapon,增加攻击力和攻击范围 Branch:3  
StoneAxe Weapon,加攻击力,可砍树 Wood:2;Stone:2  
StonePickaxe Weapon,加攻击力,可采矿石 Wood:2;Stone:3  
StoneSword Weapon,增加攻击力和攻击范围 Wood:1;Stone:2  
IronAxe Weapon,加攻击力,可砍树 Wood:2;IronIngot:2 Smelter_AA类及以上buff
IronPickaxe Weapon,加攻击力,可采矿石 Wood:2;IronIngot:3 Smelter_AA类及以上buff
IronSword Weapon,增加攻击力和攻击范围 Wood:1;IronIngot:2 Smelter_AA类及以上buff
SteelAxe Weapon,加攻击力,可砍树 Wood:2;Steel:2 Smelter_AAA类及以上buff
SteelPickaxe Weapon,加攻击力,可采矿石 Wood:2;Steel:3 Smelter_AAA类及以上buff
SteelSword Weapon,增加攻击力和攻击范围 Wood:1;Steel:2 Smelter_AAA类及以上buff
Diamond 钻石 Coal:4 Smelter_AAA类及以上buff
Zircon 锆英石 GemStone:2;IronMine:4 Smelter_AAA类及以上buff
Peridot 橄榄石 GemStone:3;IronMine:3 Smelter_AAA类及以上buff
Tanzanite 坦桑石 GemStone:6 Smelter_AAA类及以上buff
Spinel 尖晶石 IronMine:6 Smelter_AAA类及以上buff
Kunzite 锂辉石 GemStone:4;IronMine:2 Smelter_AAA类及以上buff
Citrine 黄宝石 Zircon:1;Diamond:1 Smelter_AAA类及以上buff
Emerald 绿宝石 Peridot:1;Diamond:1 Smelter_AAA类及以上buff
Saphire 蓝宝石 Tanzanite:1;Diamond:1 Smelter_AAA类及以上buff
Ruby 红宝石 Spinel:1;Diamond:1 Smelter_AAA类及以上buff
Amethyst 紫宝石 Kunzite:1;Diamond:1 Smelter_AAA类及以上buff
FancyColorDiamond 彩钻 Citrine:1;Emerald:1; Sapphire:1;Rubi:1;Amethyst:1 Smelter_AAA类及以上buff

玩法策略设计

在合成游戏中,玩家的最终目标是获得彩钻,即FancyColorDiamond。为了达成这一目的,玩家需要在保证自己生存的同时收集材料、升级装备,最终通过多步合成获取。

以下为在默认配置下,快速获取彩钻的方式。当然,使用者可以根据自身需要,调整config文件夹下的配置信息,来增加或者降低游戏难度。如,若增加苹果树的重生周期和生产周期,则会迫使agent需要在游戏中期合成部分装备去杀死动物保证生存,就给予了Agent更大的生存压力。

保证生存

在默认配置中,AppleTree的重生周期比较快,因此玩家只需要定期的砍AppleTree、捡取Apple,在需要时consume就能保证饱食度。饥渴度通过采集Pool中的Water保证,在移动时看见新的Pool可以去采集,保证Water和Apple都有50+基本就足够了。

合成装备

在合成游戏下,采集/合成物品有时需要身上的buff满足特定条件(Condition)。在目前配置下,获得彩钻的过程中需要的buff都是由装备提供的,在保证生存的情况下,获得这些装备可以参考如下步骤:

  1. 捡Branch,用Branch合成WoodAxe并装备,不断砍树获得Wood和Branch,当然顺带也可能获得Apple;
  2. 捡Stone,用Wood和Stone合成StonePickaxe并装备,不断挖IronMine和StoneMine获得Stone和IronOre;
  3. 用Stone合成StoneSmelter并装备;
  4. 用Wood合成Coal,用Coal和IronOre合成IronIngot;
  5. 用IronIngot合成IronSmelter并装备;
  6. 用Coal和IronIngot合成SteelIngot;
  7. 用SteelIngot合成SteelSmelter并装备;
  8. 用SteelIngot和Wood合成SteelPickaxe并装备;
  9. 开始挖GemMine获得GemStone;
  10. 上述所有Item准备充足,即通过合成表一步一步合成获取彩钻即可。

可视化运行Demo

合成游戏的运行Demo如下图中所示,由于合成到FancyColorDiamond所需要的步骤过多,只显示智能体合成到StoneSword

demo

算法运行Demo

训练方法

算法既可以在原始动作空间上训练,也可以在简化后的动作空间上训练。下面展示如何在原始动作空间上使用stable_baselines3库中的PPO算法进行训练。

import gym, eden
import time
from eden.wrappers import SelectActionTarget, ObsScale, Reward,ObsReader,ActScale
from stable_baselines3 import PPO
from eden.wrappers.one_agent_wrapper import OneAgent
import tensorboard
from eden.interactive import Render

if __name__ == '__main__':
    e = gym.make('eden-v1')
    e.reset()
    e = Reward(e)#reward函数
    e = OneAgent(e)#将多智能体环境变为单智能体环境
    #e = ObsReader(e)#解析observation的wrapper,若想使用原始obs可删掉
    #e = ActScale(e)#简化动作空间的wrapper,若想采用原始动作空间删除这行即可
    e.reset()
    tensorboard_dir = "PPO_compound_test"
    
    model = PPO("MlpPolicy", e, verbose=1,tensorboard_log=tensorboard_dir + '/')
    model.learn(total_timesteps=1000000)
    model.save("ppo_test")#保存文件位置
    
    #以下为观察模型效果的代码,可以展示游戏界面
    #render = Render(e,10)
    #model = PPO.load("ppo_test")
    #obs = e.reset()
    #while True:
        #action, _states = model.predict(obs)
        #obs, rewards, dones, info = e.step(action)
        #time.sleep(.5)
        #if not render.fresh():
            #break

如此就可以训练并将模型保存。

动作空间简化

原始动作空间为([动作id,x坐标,y坐标]),其中动作有9种:Idle, Attack, Collect, Pickup, Consume, Equip, Synthesize, Discard, Move。当然,在enden-v1下动作空间被包装为[左键/右键,目标的x,目标的y]。在40x40的地图配置中,x有82个取值,y有40个取值。在这么大的离散空间中,采样获得正确动作的概率太小,baseline算法如PPO等很难学到一个有意义的结果。因而需要Wrapper对action进行降维、简化,帮助算法学习一个有意义的结果。

因此在游戏中配置了一种action_wrapper的样例,通过env = ActScale(env)包装后就可以完成动作空间的映射。

为了达成合成IronIngot的目标,将动作空间修改如下,

原始动作 修改方式 描述
Idle 删除。 在合成环境中没有意义的动作
Move 不单独进行move,而是其他动作失败后,往目标移动/无目标随机往一个位置移动  
Attack/collect/pickup 合并后分解为找Apple,找Water,找Wood,找stone,找IronIngot。若有目标在附近且对应产品没有达到最大容量,则move到附近,优先级pickup>attack>equip>collect;否则随机移动。 目的类似,获得特定材料
equip 不独立设置一个编号,嵌入到其他动作中,为了完成目的去装备  
Consume 分为吃苹果和吃水。没有吃的去做找Apple/Water任务 -
Synthesize/Discard 不独立设置一个编号,嵌入到其他动作中,为了完成目的去合成装备。Discard在有更高级装备时,可以丢弃低级装备,但目前没有用到 智能合成

具体动作编号设置如下:

编号 规则
0(吃苹果) 判断Hunger是否小于给定值(设定为400):小于,若背包有Apple则consume,否则执行动作找苹果;大于,若背包Apple数小于上限(60),则执行动作找苹果。
1(吃水) 判断Thirst是否小于给定值(设定为400):小于,若背包有Water则consume,否则执行动作找水;大于,若背包Water数小于上限(60),则执行动作找Water。
2(找水) 判断Water数量是否大于给定值(设定为100):大于,则不执行此动作;小于,判断附近是否有Pool,若能Collect直接Collect,否则向Pool移动。
3(找苹果) 如果Apple数量大于于给定值(设定为100),则不执行,否则:a.判断附近是否有能Pickup的Apple,能捡直接捡,否则向目标位置移动;b.若没有装备WoodAxe则去完成找Wood工作;c.若已经装备WoodAxe且能看见AppleTree,能砍AppleTree直接砍,否则向其移动。
4(找铁) a.若没有装备StoneSmelter,完成找StoneSmelter;b.若能合成IronIngot直接合成;c .IronOre不够找生铁;d.Stone不够找石头。
5(找生铁) a.若视野内有IronOre,则捡/向其移动;b.若已经装备StoneAxe,则去找IronMine砍;c.找石头。
6(找StoneSmelter) a.若有能装备但没装备的StoneSmelter,则装备StoneSmelter;b.判断Stone够不够合成,够则合成,不够则去找石头。
7(找石头) a.若附近有能捡的Stone,则捡/向其移动;b.若有能装备但没装备的StonePickaxe,则装备;c.若已经装备StonePickaxe,则去找Rock砍;d.若没有StonePickaxe,判断Stone够不够合成,够则合成,不够则去找Rock收集Stone。
8(找木头) a.若附近有能捡的Wood,则捡/向Wood移动;b.若有能装备但没装备的WoodAxe,则装备WoodAxe;c.若已经装备WoodAxe,则去找Tree砍;d.若没有木斧,判断Branch够不够合成,够则合成,不够则去找树收集(Collect)Branch。
默认 上面任务没有满足的目标时,按照"能捡Branch捡branch(防止太多不捡挡路),否则随机移动Speed距离"执行操作

相较于默认环境,为了简化任务难度,在实验中做了以下修改:

1.agent理应记录pool的储存数目,否则会做无意义的采水动作;为了简便,设置pool的储量为无限;

2.设定初始Hunger和Thirst为500;

3.苹果只能通过击杀苹果树得到,掉落30个苹果。

在这个配置下,测试一直执行4(找铁)动作是可以获得IronIngot的,说明任务并不难,这个action_wrapper可以看作是一种简单的人工策略。

动作设计机制就参考动作3-动作8的联动,优先级捡>获取所需装备/材料>找。需要完成更高级的合成任务时,可按此机制增加动作(如增加找IronSmelter/Steel这些操作)。

结果样例

在简化动作空间上训练出的一个结果图如下,

ppo

可见,智能体可以完成采集树枝、合成/装备木斧、砍苹果树、收集苹果这些操作。实际训练时发现,设置一个合适的reward是能否训练出优秀策略的关键:把生存类的reward调高时(如吃苹果),训练出的模型就只会去吃水/吃苹果,虽然一直死不了,但也不会合成东西;把合成类的reward调高时(如合成铁的reward),训练出的模型就会不顾死活,一直去合成。因此,目前利用PPO训练出的模型很难在生存和合成之间找到一个平衡。