i think i fixed things
This commit is contained in:
@@ -11,7 +11,3 @@ class TradingAgent(BaseAgent):
|
|||||||
name = "TradingAgent"
|
name = "TradingAgent"
|
||||||
|
|
||||||
|
|
||||||
class TradingAgentLogic():
|
|
||||||
"""Logic for trading agent"""
|
|
||||||
def __init__(self,) -> None:
|
|
||||||
pass
|
|
||||||
|
|||||||
@@ -76,8 +76,7 @@ class Registry:
|
|||||||
|
|
||||||
See Registry class docstring for example.
|
See Registry class docstring for example.
|
||||||
"""
|
"""
|
||||||
if cls_name.lower() not in self._lookup:
|
if cls_name.lower() not in self._lookup: raise KeyError('"{}" is not a name of a registered class'.format(cls_name))
|
||||||
raise KeyError('"{}" is not a name of a registered class'.format(cls_name))
|
|
||||||
return self._lookup[cls_name.lower()]
|
return self._lookup[cls_name.lower()]
|
||||||
|
|
||||||
def has(self, cls_name):
|
def has(self, cls_name):
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class ContinuousDoubleAuction(BaseComponent):
|
|||||||
name = "ContinuousDoubleAuction"
|
name = "ContinuousDoubleAuction"
|
||||||
component_type = "Trade"
|
component_type = "Trade"
|
||||||
required_entities = ["Coin", "Labor"]
|
required_entities = ["Coin", "Labor"]
|
||||||
agent_subclasses = ["BasicMobileAgent"]
|
agent_subclasses = ["BasicMobileAgent","TradingAgent"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -417,7 +417,7 @@ class ContinuousDoubleAuction(BaseComponent):
|
|||||||
"""
|
"""
|
||||||
# This component adds 2*(1+max_bid_ask)*n_resources possible actions:
|
# This component adds 2*(1+max_bid_ask)*n_resources possible actions:
|
||||||
# buy/sell x each-price x each-resource
|
# buy/sell x each-price x each-resource
|
||||||
if agent_cls_name == "BasicMobileAgent":
|
if agent_cls_name in self.agent_subclasses:
|
||||||
trades = []
|
trades = []
|
||||||
for c in self.commodities:
|
for c in self.commodities:
|
||||||
trades.append(
|
trades.append(
|
||||||
|
|||||||
@@ -79,6 +79,6 @@ class Gem(Resource):
|
|||||||
|
|
||||||
name = "Gem"
|
name = "Gem"
|
||||||
color = np.array([241, 233, 219]) / 255.0
|
color = np.array([241, 233, 219]) / 255.0
|
||||||
collectible = False
|
collectible = True
|
||||||
craft_recp= {"Gem_Raw": 1}
|
craft_recp= {"Gem_Raw": 1}
|
||||||
craft_labour_base= 1
|
craft_labour_base= 1
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
from . import(
|
from . import(
|
||||||
simple_gather,
|
simple_gather,
|
||||||
simple_build
|
simple_build,
|
||||||
|
crafting,
|
||||||
|
external_market
|
||||||
)
|
)
|
||||||
+10
-5
@@ -47,7 +47,9 @@ class Craft(BaseComponent):
|
|||||||
skill_dist="none",
|
skill_dist="none",
|
||||||
**base_component_kwargs
|
**base_component_kwargs
|
||||||
):
|
):
|
||||||
|
assert len(commodities)>0
|
||||||
#setup commodities
|
#setup commodities
|
||||||
|
self.recip_map={}
|
||||||
for v in commodities:
|
for v in commodities:
|
||||||
res_class=resource_registry.get(v)
|
res_class=resource_registry.get(v)
|
||||||
res=res_class()
|
res=res_class()
|
||||||
@@ -56,6 +58,7 @@ class Craft(BaseComponent):
|
|||||||
assert res.craft_recp!={}
|
assert res.craft_recp!={}
|
||||||
assert res.craft_labour_base >= 0
|
assert res.craft_labour_base >= 0
|
||||||
self.required_entities.append(v)
|
self.required_entities.append(v)
|
||||||
|
self.recip_map[res.name]=res.craft_recp
|
||||||
self.commodities.append(res)
|
self.commodities.append(res)
|
||||||
|
|
||||||
|
|
||||||
@@ -67,16 +70,17 @@ class Craft(BaseComponent):
|
|||||||
assert self.max_skill_labour_benefit <= 1
|
assert self.max_skill_labour_benefit <= 1
|
||||||
|
|
||||||
self.skill_dist = skill_dist.lower()
|
self.skill_dist = skill_dist.lower()
|
||||||
assert self.skill_dist in ["none", "pareto", "lognormal"]
|
assert self.skill_dist in ["none", "pareto"]
|
||||||
|
|
||||||
self.sampled_skills = {}
|
self.sampled_skills = {}
|
||||||
|
|
||||||
self.builds = []
|
self.builds = []
|
||||||
super().__init__(*base_component_args, **base_component_kwargs)
|
super().__init__(*base_component_args, **base_component_kwargs)
|
||||||
|
|
||||||
def agent_can_build(self, agent, recipe):
|
def agent_can_build(self, agent, res):
|
||||||
"""Return True if agent can actually build in its current location."""
|
"""Return True if agent can actually build in its current location."""
|
||||||
# See if the agent has the resources necessary to complete the action
|
# See if the agent has the resources necessary to complete the action
|
||||||
|
recipe= self.recip_map[res]
|
||||||
for resource, cost in recipe.items():
|
for resource, cost in recipe.items():
|
||||||
if agent.state["inventory"][resource] < cost:
|
if agent.state["inventory"][resource] < cost:
|
||||||
return False
|
return False
|
||||||
@@ -171,9 +175,10 @@ class Craft(BaseComponent):
|
|||||||
obs_dict = dict()
|
obs_dict = dict()
|
||||||
for agent in self.world.agents:
|
for agent in self.world.agents:
|
||||||
if agent.name in self.agent_subclasses:
|
if agent.name in self.agent_subclasses:
|
||||||
obs_dict[agent.idx]["craft_skill"]={}
|
obs_dict[agent.idx]={}
|
||||||
|
|
||||||
for k in self.commodities:
|
for k in self.commodities:
|
||||||
obs_dict[agent.idx]["craft_skill"][k.name] = agent.state["craft_skill"][k.name]
|
obs_dict[agent.idx]["craft_skill_{}".format(k.name)] = agent.state["craft_skill"][k.name]
|
||||||
|
|
||||||
|
|
||||||
return obs_dict
|
return obs_dict
|
||||||
@@ -238,7 +243,7 @@ class Craft(BaseComponent):
|
|||||||
|
|
||||||
|
|
||||||
for agent in world.agents:
|
for agent in world.agents:
|
||||||
if agent.name not in self.agent_subclasses | agent.is_setup:
|
if (agent.name not in self.agent_subclasses) | agent.is_setup:
|
||||||
continue
|
continue
|
||||||
agent.state["craft_skill"]={}
|
agent.state["craft_skill"]={}
|
||||||
agent.state["craft_labour"]={}
|
agent.state["craft_labour"]={}
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# Copyright (c) 2020, salesforce.com, inc.
|
||||||
|
# All rights reserved.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
# For full license text, see the LICENSE file in the repo root
|
||||||
|
# or https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from ai_economist.foundation.base.base_component import (
|
||||||
|
BaseComponent,
|
||||||
|
component_registry,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@component_registry.add
|
||||||
|
class ExternalMarket(BaseComponent):
|
||||||
|
"""
|
||||||
|
Allows mobile agents to build house landmarks in the world using stone and wood,
|
||||||
|
earning income.
|
||||||
|
|
||||||
|
Can be configured to include heterogeneous building skill where agents earn
|
||||||
|
different levels of income when building.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
payment (int): Default amount of coin agents earn from building.
|
||||||
|
Must be >= 0. Default is 10.
|
||||||
|
market_demand (dict): Resource name -> amout of money
|
||||||
|
skill_dist (str): Distribution type for sampling skills. Default ("none")
|
||||||
|
gives all agents identical skill equal to a multiplier of 1. "pareto" and
|
||||||
|
"lognormal" sample skills from the associated distributions.
|
||||||
|
build_labor (float): Labor cost associated with building a house.
|
||||||
|
Must be >= 0. Default is 10.
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "ExternalMarket"
|
||||||
|
component_type = "Trade"
|
||||||
|
required_entities = ["Coin", "Labor"]
|
||||||
|
agent_subclasses = ["TradingAgent"]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*base_component_args,
|
||||||
|
market_demand={},
|
||||||
|
trade_labor=1.0,
|
||||||
|
**base_component_kwargs
|
||||||
|
):
|
||||||
|
super().__init__(*base_component_args, **base_component_kwargs)
|
||||||
|
|
||||||
|
self.market_demand = market_demand
|
||||||
|
self.action_res_map={}
|
||||||
|
for k in market_demand.keys():
|
||||||
|
self.action_res_map[len(self.action_res_map)+1]=k
|
||||||
|
|
||||||
|
self.trade_labor = float(trade_labor)
|
||||||
|
assert self.trade_labor >= 0
|
||||||
|
|
||||||
|
self.builds = []
|
||||||
|
|
||||||
|
def agent_can_sell(self, agent,res):
|
||||||
|
"""Return True if agent can sell a res."""
|
||||||
|
# See if the agent has the resources necessary to complete the action
|
||||||
|
|
||||||
|
if agent.state["inventory"][res]>= 1:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Required methods for implementing components
|
||||||
|
# --------------------------------------------
|
||||||
|
|
||||||
|
def get_n_actions(self, agent_cls_name):
|
||||||
|
"""
|
||||||
|
See base_component.py for detailed description.
|
||||||
|
|
||||||
|
Add a single action (build) for mobile agents.
|
||||||
|
"""
|
||||||
|
# This component adds 1 action that mobile agents can take: build a house
|
||||||
|
if agent_cls_name in self.agent_subclasses:
|
||||||
|
return len(self.action_res_map)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_additional_state_fields(self, agent_cls_name):
|
||||||
|
"""
|
||||||
|
See base_component.py for detailed description.
|
||||||
|
|
||||||
|
For mobile agents, add state fields for building skill.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def component_step(self):
|
||||||
|
"""
|
||||||
|
See base_component.py for detailed description.
|
||||||
|
|
||||||
|
Convert stone+wood to house+coin for agents that choose to build and can.
|
||||||
|
"""
|
||||||
|
world = self.world
|
||||||
|
build = []
|
||||||
|
# Apply any building actions taken by the mobile agents
|
||||||
|
for agent in world.get_random_order_agents():
|
||||||
|
|
||||||
|
action = agent.get_component_action(self.name)
|
||||||
|
|
||||||
|
# This component doesn't apply to this agent!
|
||||||
|
if action is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# NO-OP!
|
||||||
|
if action == 0:
|
||||||
|
pass
|
||||||
|
action-=1
|
||||||
|
res_name=self.action_res_map[action]
|
||||||
|
# Build! (If you can.)
|
||||||
|
|
||||||
|
if self.agent_can_sell(agent,res_name):
|
||||||
|
# Remove the resources
|
||||||
|
agent.state["inventory"][res_name] -= 1
|
||||||
|
|
||||||
|
# Receive payment for the house
|
||||||
|
agent.state["inventory"]["Coin"] += self.market_demand[res_name]
|
||||||
|
|
||||||
|
# Incur the labor cost for building
|
||||||
|
agent.state["endogenous"]["Labor"] += self.trade_labor
|
||||||
|
|
||||||
|
build.append(
|
||||||
|
{
|
||||||
|
"seller": agent.idx,
|
||||||
|
"commodity": res_name,
|
||||||
|
"income": self.market_demand[res_name],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
self.builds.append(build)
|
||||||
|
|
||||||
|
def generate_observations(self):
|
||||||
|
"""
|
||||||
|
See base_component.py for detailed description.
|
||||||
|
|
||||||
|
Here, agents observe their build skill. The planner does not observe anything
|
||||||
|
from this component.
|
||||||
|
"""
|
||||||
|
|
||||||
|
obs_dict = dict()
|
||||||
|
for agent in self.world.agents:
|
||||||
|
if agent.name in self.agent_subclasses:
|
||||||
|
|
||||||
|
obs_dict[agent.idx] = {}
|
||||||
|
for res_name,coin in self.market_demand.items():
|
||||||
|
obs_dict[agent.idx]["external_{}_price".format(res_name)]: self.inv_scale*coin
|
||||||
|
|
||||||
|
return obs_dict
|
||||||
|
|
||||||
|
def generate_masks(self, completions=0):
|
||||||
|
"""
|
||||||
|
See base_component.py for detailed description.
|
||||||
|
|
||||||
|
Prevent building only if a landmark already occupies the agent's location.
|
||||||
|
"""
|
||||||
|
|
||||||
|
masks = {}
|
||||||
|
# Mobile agents' build action is masked if they cannot build with their
|
||||||
|
# current location and/or endowment
|
||||||
|
for agent in self.world.agents:
|
||||||
|
mask=[]
|
||||||
|
for res in self.market_demand:
|
||||||
|
mask.append(self.agent_can_sell(agent,res))
|
||||||
|
masks[agent.idx] = mask
|
||||||
|
|
||||||
|
return masks
|
||||||
|
|
||||||
|
# For non-required customization
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
def get_metrics(self):
|
||||||
|
"""
|
||||||
|
Metrics that capture what happened through this component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
metrics (dict): A dictionary of {"metric_name": metric_value},
|
||||||
|
where metric_value is a scalar.
|
||||||
|
"""
|
||||||
|
world = self.world
|
||||||
|
|
||||||
|
build_stats = {a.idx: {"n_builds": 0} for a in world.agents}
|
||||||
|
for builds in self.builds:
|
||||||
|
for build in builds:
|
||||||
|
idx = build["builder"]
|
||||||
|
build_stats[idx]["n_builds"] += 1
|
||||||
|
|
||||||
|
out_dict = {}
|
||||||
|
for a in world.agents:
|
||||||
|
for k, v in build_stats[a.idx].items():
|
||||||
|
out_dict["{}/{}".format(a.idx, k)] = v
|
||||||
|
|
||||||
|
num_houses = np.sum(world.maps.get("House") > 0)
|
||||||
|
out_dict["total_builds"] = num_houses
|
||||||
|
|
||||||
|
return out_dict
|
||||||
|
|
||||||
|
def additional_reset_steps(self):
|
||||||
|
"""
|
||||||
|
See base_component.py for detailed description.
|
||||||
|
|
||||||
|
Re-sample agents' building skills.
|
||||||
|
"""
|
||||||
|
self.builds = []
|
||||||
|
|
||||||
|
def get_dense_log(self):
|
||||||
|
"""
|
||||||
|
Log builds.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
builds (list): A list of build events. Each entry corresponds to a single
|
||||||
|
timestep and contains a description of any builds that occurred on
|
||||||
|
that timestep.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.builds
|
||||||
@@ -8,6 +8,7 @@ from sb3_contrib.ppo_mask import MaskablePPO
|
|||||||
import envs
|
import envs
|
||||||
import wrapper
|
import wrapper
|
||||||
import resources
|
import resources
|
||||||
|
from agents import trading_agent
|
||||||
from wrapper.base_econ_wrapper import BaseEconWrapper
|
from wrapper.base_econ_wrapper import BaseEconWrapper
|
||||||
from wrapper.reciever_econ_wrapper import RecieverEconWrapper
|
from wrapper.reciever_econ_wrapper import RecieverEconWrapper
|
||||||
from wrapper.sb3_econ_converter import SB3EconConverter
|
from wrapper.sb3_econ_converter import SB3EconConverter
|
||||||
@@ -36,11 +37,14 @@ env_config = {
|
|||||||
# The order in which components reset, step, and generate obs follows their listed order below.
|
# The order in which components reset, step, and generate obs follows their listed order below.
|
||||||
'components': [
|
'components': [
|
||||||
# (1) Building houses
|
# (1) Building houses
|
||||||
('SimpleCraft', {'skill_dist': "none", 'payment_max_skill_multiplier': 3}),
|
('Craft', {'skill_dist': "pareto", 'commodities': ["Gem"]}),
|
||||||
# (2) Trading collectible resources
|
# (2) Trading collectible resources
|
||||||
#('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
||||||
# (3) Movement and resource collection
|
# (3) Movement and resource collection
|
||||||
('SimpleGather', {}),
|
('SimpleGather', {}),
|
||||||
|
('ExternalMarket',{'market_demand':{
|
||||||
|
'Gem': 10
|
||||||
|
}}),
|
||||||
],
|
],
|
||||||
|
|
||||||
# ===== SCENARIO CLASS ARGUMENTS =====
|
# ===== SCENARIO CLASS ARGUMENTS =====
|
||||||
@@ -51,7 +55,7 @@ env_config = {
|
|||||||
|
|
||||||
# ===== STANDARD ARGUMENTS ======
|
# ===== STANDARD ARGUMENTS ======
|
||||||
# kwargs that are used by every Scenario class (i.e. defined in BaseEnvironment)
|
# kwargs that are used by every Scenario class (i.e. defined in BaseEnvironment)
|
||||||
'agent_composition': {"BasicMobileAgent": 20}, # Number of non-planner agents (must be > 1)
|
'agent_composition': {"BasicMobileAgent": 20,"TradingAgent":5}, # Number of non-planner agents (must be > 1)
|
||||||
'world_size': [5, 5], # [Height, Width] of the env world
|
'world_size': [5, 5], # [Height, Width] of the env world
|
||||||
'episode_length': 256, # Number of timesteps per episode
|
'episode_length': 256, # Number of timesteps per episode
|
||||||
'allow_observation_scaling': True,
|
'allow_observation_scaling': True,
|
||||||
@@ -88,11 +92,14 @@ eval_env_config = {
|
|||||||
# The order in which components reset, step, and generate obs follows their listed order below.
|
# The order in which components reset, step, and generate obs follows their listed order below.
|
||||||
'components': [
|
'components': [
|
||||||
# (1) Building houses
|
# (1) Building houses
|
||||||
('SimpleCraft', {'skill_dist': "none", 'payment_max_skill_multiplier': 3}),
|
('Craft', {'skill_dist': "pareto", 'commodities': ["Gem"]}),
|
||||||
# (2) Trading collectible resources
|
# (2) Trading collectible resources
|
||||||
#('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
||||||
# (3) Movement and resource collection
|
# (3) Movement and resource collection
|
||||||
('SimpleGather', {}),
|
('SimpleGather', {}),
|
||||||
|
('ExternalMarket',{'market_demand':{
|
||||||
|
'Gem': 10
|
||||||
|
}}),
|
||||||
],
|
],
|
||||||
|
|
||||||
# ===== SCENARIO CLASS ARGUMENTS =====
|
# ===== SCENARIO CLASS ARGUMENTS =====
|
||||||
@@ -103,7 +110,7 @@ eval_env_config = {
|
|||||||
|
|
||||||
# ===== STANDARD ARGUMENTS ======
|
# ===== STANDARD ARGUMENTS ======
|
||||||
# kwargs that are used by every Scenario class (i.e. defined in BaseEnvironment)
|
# kwargs that are used by every Scenario class (i.e. defined in BaseEnvironment)
|
||||||
'agent_composition': {"BasicMobileAgent": 20}, # Number of non-planner agents (must be > 1)
|
'agent_composition': {"BasicMobileAgent": 20,"TradingAgent":5}, # Number of non-planner agents (must be > 1)
|
||||||
'world_size': [1, 1], # [Height, Width] of the env world
|
'world_size': [1, 1], # [Height, Width] of the env world
|
||||||
'episode_length': 256, # Number of timesteps per episode
|
'episode_length': 256, # Number of timesteps per episode
|
||||||
'allow_observation_scaling': True,
|
'allow_observation_scaling': True,
|
||||||
@@ -209,9 +216,13 @@ def printReplay(econ,agentid):
|
|||||||
|
|
||||||
#Setup Env Objects
|
#Setup Env Objects
|
||||||
econ=foundation.make_env_instance(**env_config)
|
econ=foundation.make_env_instance(**env_config)
|
||||||
|
|
||||||
|
market=econ.get_component("ContinuousDoubleAuction")
|
||||||
|
action=market.get_n_actions("TradingAgent")
|
||||||
baseEconWrapper=BaseEconWrapper(econ)
|
baseEconWrapper=BaseEconWrapper(econ)
|
||||||
baseEconWrapper.run()
|
baseEconWrapper.run()
|
||||||
mobileRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="BasicMobileAgent")
|
mobileRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="BasicMobileAgent")
|
||||||
|
tradeRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="TradingAgent")
|
||||||
sb3Converter=SB3EconConverter(mobileRecieverEconWrapper,econ,"BasicMobileAgent")
|
sb3Converter=SB3EconConverter(mobileRecieverEconWrapper,econ,"BasicMobileAgent")
|
||||||
#obs=sb3Converter.reset()
|
#obs=sb3Converter.reset()
|
||||||
#vecenv=EconVecEnv(env_config=env_config)
|
#vecenv=EconVecEnv(env_config=env_config)
|
||||||
@@ -261,7 +272,7 @@ while True:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#market=eval_econ.get_component("ContinuousDoubleAuction")
|
market=eval_econ.get_component("ContinuousDoubleAuction")
|
||||||
craft=eval_econ.get_component("SimpleCraft")
|
craft=eval_econ.get_component("SimpleCraft")
|
||||||
# trades=market.get_dense_log()
|
# trades=market.get_dense_log()
|
||||||
build=craft.get_dense_log()
|
build=craft.get_dense_log()
|
||||||
|
|||||||
Reference in New Issue
Block a user