i think i fixed things
This commit is contained in:
@@ -11,7 +11,3 @@ class TradingAgent(BaseAgent):
|
||||
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.
|
||||
"""
|
||||
if cls_name.lower() not in self._lookup:
|
||||
raise KeyError('"{}" is not a name of a registered class'.format(cls_name))
|
||||
if cls_name.lower() not in self._lookup: raise KeyError('"{}" is not a name of a registered class'.format(cls_name))
|
||||
return self._lookup[cls_name.lower()]
|
||||
|
||||
def has(self, cls_name):
|
||||
|
||||
@@ -37,7 +37,7 @@ class ContinuousDoubleAuction(BaseComponent):
|
||||
name = "ContinuousDoubleAuction"
|
||||
component_type = "Trade"
|
||||
required_entities = ["Coin", "Labor"]
|
||||
agent_subclasses = ["BasicMobileAgent"]
|
||||
agent_subclasses = ["BasicMobileAgent","TradingAgent"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -417,7 +417,7 @@ class ContinuousDoubleAuction(BaseComponent):
|
||||
"""
|
||||
# This component adds 2*(1+max_bid_ask)*n_resources possible actions:
|
||||
# buy/sell x each-price x each-resource
|
||||
if agent_cls_name == "BasicMobileAgent":
|
||||
if agent_cls_name in self.agent_subclasses:
|
||||
trades = []
|
||||
for c in self.commodities:
|
||||
trades.append(
|
||||
|
||||
@@ -79,6 +79,6 @@ class Gem(Resource):
|
||||
|
||||
name = "Gem"
|
||||
color = np.array([241, 233, 219]) / 255.0
|
||||
collectible = False
|
||||
collectible = True
|
||||
craft_recp= {"Gem_Raw": 1}
|
||||
craft_labour_base= 1
|
||||
@@ -1,4 +1,6 @@
|
||||
from . import(
|
||||
simple_gather,
|
||||
simple_build
|
||||
simple_build,
|
||||
crafting,
|
||||
external_market
|
||||
)
|
||||
@@ -47,7 +47,9 @@ class Craft(BaseComponent):
|
||||
skill_dist="none",
|
||||
**base_component_kwargs
|
||||
):
|
||||
assert len(commodities)>0
|
||||
#setup commodities
|
||||
self.recip_map={}
|
||||
for v in commodities:
|
||||
res_class=resource_registry.get(v)
|
||||
res=res_class()
|
||||
@@ -56,6 +58,7 @@ class Craft(BaseComponent):
|
||||
assert res.craft_recp!={}
|
||||
assert res.craft_labour_base >= 0
|
||||
self.required_entities.append(v)
|
||||
self.recip_map[res.name]=res.craft_recp
|
||||
self.commodities.append(res)
|
||||
|
||||
|
||||
@@ -67,16 +70,17 @@ class Craft(BaseComponent):
|
||||
assert self.max_skill_labour_benefit <= 1
|
||||
|
||||
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.builds = []
|
||||
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."""
|
||||
# See if the agent has the resources necessary to complete the action
|
||||
recipe= self.recip_map[res]
|
||||
for resource, cost in recipe.items():
|
||||
if agent.state["inventory"][resource] < cost:
|
||||
return False
|
||||
@@ -171,9 +175,10 @@ class Craft(BaseComponent):
|
||||
obs_dict = dict()
|
||||
for agent in self.world.agents:
|
||||
if agent.name in self.agent_subclasses:
|
||||
obs_dict[agent.idx]["craft_skill"]={}
|
||||
obs_dict[agent.idx]={}
|
||||
|
||||
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
|
||||
@@ -238,7 +243,7 @@ class Craft(BaseComponent):
|
||||
|
||||
|
||||
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
|
||||
agent.state["craft_skill"]={}
|
||||
agent.state["craft_labour"]={}
|
||||
|
||||
220
components/external_market.py
Normal file
220
components/external_market.py
Normal file
@@ -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
|
||||
25
main.py
25
main.py
@@ -8,6 +8,7 @@ from sb3_contrib.ppo_mask import MaskablePPO
|
||||
import envs
|
||||
import wrapper
|
||||
import resources
|
||||
from agents import trading_agent
|
||||
from wrapper.base_econ_wrapper import BaseEconWrapper
|
||||
from wrapper.reciever_econ_wrapper import RecieverEconWrapper
|
||||
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.
|
||||
'components': [
|
||||
# (1) Building houses
|
||||
('SimpleCraft', {'skill_dist': "none", 'payment_max_skill_multiplier': 3}),
|
||||
('Craft', {'skill_dist': "pareto", 'commodities': ["Gem"]}),
|
||||
# (2) Trading collectible resources
|
||||
#('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
||||
('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
||||
# (3) Movement and resource collection
|
||||
('SimpleGather', {}),
|
||||
('ExternalMarket',{'market_demand':{
|
||||
'Gem': 10
|
||||
}}),
|
||||
],
|
||||
|
||||
# ===== SCENARIO CLASS ARGUMENTS =====
|
||||
@@ -51,7 +55,7 @@ env_config = {
|
||||
|
||||
# ===== STANDARD ARGUMENTS ======
|
||||
# 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
|
||||
'episode_length': 256, # Number of timesteps per episode
|
||||
'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.
|
||||
'components': [
|
||||
# (1) Building houses
|
||||
('SimpleCraft', {'skill_dist': "none", 'payment_max_skill_multiplier': 3}),
|
||||
('Craft', {'skill_dist': "pareto", 'commodities': ["Gem"]}),
|
||||
# (2) Trading collectible resources
|
||||
#('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
||||
('ContinuousDoubleAuction', {'max_num_orders': 10}),
|
||||
# (3) Movement and resource collection
|
||||
('SimpleGather', {}),
|
||||
('ExternalMarket',{'market_demand':{
|
||||
'Gem': 10
|
||||
}}),
|
||||
],
|
||||
|
||||
# ===== SCENARIO CLASS ARGUMENTS =====
|
||||
@@ -103,7 +110,7 @@ eval_env_config = {
|
||||
|
||||
# ===== STANDARD ARGUMENTS ======
|
||||
# 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
|
||||
'episode_length': 256, # Number of timesteps per episode
|
||||
'allow_observation_scaling': True,
|
||||
@@ -209,9 +216,13 @@ def printReplay(econ,agentid):
|
||||
|
||||
#Setup Env Objects
|
||||
econ=foundation.make_env_instance(**env_config)
|
||||
|
||||
market=econ.get_component("ContinuousDoubleAuction")
|
||||
action=market.get_n_actions("TradingAgent")
|
||||
baseEconWrapper=BaseEconWrapper(econ)
|
||||
baseEconWrapper.run()
|
||||
mobileRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="BasicMobileAgent")
|
||||
tradeRecieverEconWrapper=RecieverEconWrapper(base_econ=baseEconWrapper,agent_classname="TradingAgent")
|
||||
sb3Converter=SB3EconConverter(mobileRecieverEconWrapper,econ,"BasicMobileAgent")
|
||||
#obs=sb3Converter.reset()
|
||||
#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")
|
||||
# trades=market.get_dense_log()
|
||||
build=craft.get_dense_log()
|
||||
|
||||
Reference in New Issue
Block a user