some things work

This commit is contained in:
2023-01-26 14:58:41 +01:00
parent be83cbd988
commit 966db6e5ad
24 changed files with 242 additions and 68 deletions

View File

@@ -1,7 +1,7 @@
kind: production
spec:
- name: Food
amount: 1
amount: 100
prod:
- "Grain": 1
- "Fruit": 1

View File

@@ -12,29 +12,33 @@ class AutoProductionAgent(BaseAgent):
self.worker=worker
self.employment_rate=employment_rate
self.employment_index=worker
self.target=0
def set_worker(self,workers):
if workers>1:
self.worker=workers
else:
self.worker=1
def set_target(self,qty):
self.target=qty
def can_produce(self):
# If can produce item
for k,cost in self.prod["craft"].items():
if cost > self.business.inventory[k]:
return False
for com in self.prod["prod"]:
for k,v in com.items():
if v > self.business.inventory[k]:
return False
# check if we should produce
if self.business.resource_in_possesion()>self.target:
return False
return True
def tick(self,step,epi):
for i in range(self.worker):
if not self.can_produce():
self.employment_index-=self.employment_rate
continue
self.employment_index+=self.employment_rate
self.set_worker(int(self.employment_index))
# remove cost from inventory
for k,cost in self.prod["craft"].items():
self.business.inventory[k]-=cost
for com in self.prod["prod"]:
for k,cost in com.items():
self.business.inventory[k]-=cost
# add commodity
self.business.inventory[self.prod['name']]+=self.prod["amount"]

View File

@@ -18,7 +18,7 @@ class BaseAgent(ABC):
assert "No Tick method has been provided"
pass
def reset(self):
def reset(self,episode):
"""
Resets agent to new episode.
"""

View File

@@ -92,8 +92,9 @@ class Base_Aquire_Agent(BaseAgent,ABC):
cx=self.exchanges[cx_id]
orders=self.orders[cx_id]
if len(orders)>0:
for o in orders:
trades.append(cx.order_trades_map[o.order_id])
for k,o in orders.items():
if k in cx.order_trades_map:
trades.append(cx.order_trades_map[k])
self.trades=trades
return trades

View File

@@ -1,5 +1,5 @@
from .base_aquire_agent import Base_Aquire_Agent
import random
class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
"""
Aquire agent with internal price believe system.
@@ -8,14 +8,13 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
super().__init__(simulation, business, resource, exchanges)
self.lr=lr
self.max_price_adj_rate=max_price_adj_rate
self.price_believe={i: -1 for i in range(len(self.exchanges))}
self.price_believe={i: 0 for i in range(len(self.exchanges))}
self.open_orders={i: [] for i in range(len(self.exchanges))}
self.open_qty=0
def tick(self,tick,episode):
if self.price_believe==-1:
self.price_believe=self.max_price
order_error=self.open_qty+self.target_error()
if order_error<0:
@@ -23,7 +22,7 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
cx_id=self.select_best_cx()
order=self.order_resource(self.price_believe[cx_id],order_error*-1,cx_id)
if not order==None:
self.register_order(0,order)
self.register_order(cx_id,order)
else:
# order failed due to missing balance.. we need to adjust our price believe
self.collect_balance_from_cxs()
@@ -32,14 +31,16 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
self.tick_open_orders()
def select_best_cx(self):
best_id=0
best_id=-1
best=0
for cx_id in range(len(self.exchanges)):
cx=self.exchanges[cx_id]
potential=cx.total_supply[self.resource]*self.price_believe[cx_id]
potential=cx.get_total_supply(self.resource)*self.price_believe[cx_id]
if potential<best:
best=potential
best_id=cx_id
if best_id==-1:
best_id=random.randint(0,len(self.exchanges)-1)
return best_id
def register_order(self,cx_id,order):
@@ -63,7 +64,7 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
if o.leaves_qty==0:
#order is done
self.open_orders[cx_id].remove(i) # remove order from open
self.update_believe(-1) # update price believe
self.update_believe(cx_id,-1) # update price believe
self.collect_balance_from_cxs()
self.collect_resource_from_cxs(self.resource)
continue
@@ -75,7 +76,7 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
cx.cancel_order(i["id"])
self.collect_balance_from_cxs()
self.collect_resource_from_cxs(self.resource)
self.update_believe(1)
self.update_believe(cx_id,1)
self.open_orders[cx_id].remove(i)
def update_believe(self,cx_id,modifier):
@@ -85,8 +86,10 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
If negative will sub lr to believe
"""
self.price_believe[cx_id]+=modifier*self.lr
if self.price_believe[cx_id]<0:
self.price_believe[cx_id]=0
def reset(self):
def reset(self,episode):
# Clean shop for today
for cx_id in range(len(self.exchanges)):
cx=self.exchanges[cx_id]
@@ -98,4 +101,4 @@ class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
# book keeping
self.update_trades()
return super().reset()
return super().reset(episode)

View File

@@ -1,5 +1,5 @@
from .base_distribution_agent import Base_Distribution_Agent
import random
class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
"""
Aquire agent with internal price believe system.
@@ -8,31 +8,31 @@ class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
super().__init__(simulation, business, resource, exchanges)
self.lr=lr
self.max_price_adj_rate=max_price_adj_rate
self.price_believe=-1
self.price_believe={i: 0 for i in range(len(self.exchanges))}
self.open_orders={i: [] for i in range(len(self.exchanges))}
self.open_qty=0
def tick(self):
if self.price_believe==-1:
self.price_believe=self.min_price
def tick(self,step,episode):
order_error=self.target_error()
if order_error>0:
# aquire based on current price belive
cx_id=self.select_best_cx()
order=self.distribute_resource(self.price_believe,order_error,cx_id)
self.register_order(0,order)
order=self.distribute_resource(self.price_believe[cx_id],order_error,cx_id)
self.register_order(cx_id,order)
self.tick_open_orders()
def select_best_cx(self):
best_id=0
best_id=-1
best=0
for cx_id in range(len(self.exchanges)):
cx=self.exchanges[cx_id]
potential=cx.total_supply[self.resource]*self.price_believe[cx_id]
potential=cx.get_total_supply(self.resource)*self.price_believe[cx_id]
if potential>best:
best=potential
best_id=cx_id
if best_id==-1:
best_id=random.randint(0,len(self.exchanges)-1)
return best_id
def register_order(self,cx_id,order):
@@ -56,7 +56,7 @@ class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
if o.leaves_qty==0:
#order is done
self.open_orders[cx_id].remove(i) # remove order from open
self.update_believe(1) # update price believe
self.update_believe(cx_id,1) # update price believe
self.collect_balance_from_cxs()
self.collect_resource_from_cxs(self.resource)
continue
@@ -68,7 +68,7 @@ class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
cx.cancel_order(i["id"])
self.collect_balance_from_cxs()
self.collect_resource_from_cxs(self.resource)
self.update_believe(-1)
self.update_believe(cx_id,-1)
self.open_orders[cx_id].remove(i)
@@ -83,8 +83,10 @@ class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
If negative will sub lr to believe
"""
self.price_believe[cx_id]+=modifier*self.lr
if self.price_believe[cx_id]<0:
self.price_believe[cx_id]=0
def reset(self):
def reset(self,episode):
# Clean shop for today
for cx_id in range(len(self.exchanges)):
cx=self.exchanges[cx_id]
@@ -96,4 +98,4 @@ class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
# book keeping
self.update_trades()
return super().reset()
return super().reset(episode)

View File

@@ -6,20 +6,26 @@ class Price_Believe_Business(Business):
def __init__(self, id, production, balance,exchange,simulation) -> None:
super().__init__(id, production, balance)
self.distribute=Price_Believe_Distribiute_Agent(simulation,self,production["name"],exchange,0.1,50)
self.distribute=Price_Believe_Distribiute_Agent(simulation,self,production["name"],exchange,0.5,10)
self.craft=AutoProductionAgent(simulation,self)
self.aquire={}
for k,v in production["craft"].items():
a=Price_Believe_Aquire_Agent(simulation,self,k,exchange,1,50)
a.set_target(v*2)
a.set_price_max(10)
self.aquire[k]=a
for l in production["prod"]:
for k,v in l.items():
a=Price_Believe_Aquire_Agent(simulation,self,k,exchange,1,10)
a.set_target(v*2)
a.set_price_max(10)
self.aquire[k]=a
self.distribute.set_price_min(10)
self.distribute.set_target(0)
def step_business_decisions(self):
for k,v in self.production["craft"].items():
def tick_business_decisions(self,step):
for comp in self.production["prod"]:
for k,v in comp.items():
modifier=self.craft.worker+1
self.aquire[k].set_target(v*modifier)
max_amount=self.production["amount"]*5
self.craft.set_target(max_amount)
def resource_in_possesion(self):
return self.distribute.open_qty+self.inventory[self.production["name"]]

View File

@@ -13,14 +13,33 @@ class Business(ABC):
"""
self.id=id
self.production=production
self.balance_history=[balance]
self.balance=balance
#Setup Inventory
self.inventory={production["name"]: 0}
for k,v in production["craft"].items():
self.inventory[k]=0
for l in production["prod"]:
for k,v in l.items():
self.inventory[k]=0
pass
def step_business_decisions(self):
def tick(self,step):
self.tick_business_decisions(step)
def tick_business_decisions(self,step):
assert "no business decision method has been created"
pass
def tick_episode(self,episode_count):
"""
Call for resetting for the next episode
"""
self.balance_history.append(self.balance)
self.tick_business_episode(episode_count)
def tick_business_episode(self,episode_count):
"""
Call for resetting for the next episode
"""
assert "no business episode tick method has been created"

View File

@@ -1,5 +1,6 @@
from ..exchange import Exchange
from ..commoditys import commoditys as cm
from lightmatchingengine.lightmatchingengine import Side
@@ -23,11 +24,12 @@ class Cell:
exchange: None
def __init__(self,name,x,y,area,pop,demand,world) -> None:
pop=pop*1000
self.name=name
self.loc_x=x
self.loc_y=y
self.area=area
self.pop=pop*1000
self.pop=pop
self.demand_tags=demand
self.demand={}
self.world_tags=world
@@ -55,6 +57,21 @@ class Cell:
else:
self.world[k]+=v*area
def setup_demand_for_step(self,episode_length):
pop_per_step=self.pop/episode_length
for demand_key,value in self.demand.items():
qty=value/episode_length
comm=cm.commoditys[demand_key]
max_price=comm["max_world_price"]*qty
self.exchange.add_to_account(self.name,"balance",max_price)
self.exchange.submit_order(self.name,demand_key,qty,comm["max_world_price"],Side.BUY)
def setup_supply_for_episode(self):
self.exchange.reset()
for supply_key,value in self.world.items():
self.exchange.add_to_account(self.name,supply_key,value)
self.exchange.submit_order(self.name,supply_key,value,0,Side.SELL)
def create_cells_from_world_cells(cells) -> list:
"""

View File

@@ -1,12 +1,13 @@
import os
import yaml
import uuid
# create an empty dictionary to store the demand tags
demand_tags = {} # demand tags with demands
world_tags = {} # available resources tags with resources available
commoditys = {} # commoditys by name
productions = {} # list of production rules by commodity name
productions_id_map={}
productions_uses = {} # list of productions that are a key commodity
data={}
def search_yaml_files(directory = "db"):
@@ -34,9 +35,11 @@ def search_yaml_files(directory = "db"):
commoditys[comm['name']] = comm
elif data['kind'] == "production":
for comm in data['spec']:
comm["id"]=uuid.uuid4()
if comm['name'] not in productions:
productions[comm['name']]=[]
productions[comm['name']].append(comm)
productions_id_map[comm["id"]]=comm
for comp in comm["prod"]:
k=list(comp.keys())[0]
if k not in productions_uses:

View File

@@ -7,6 +7,10 @@ class Exchange():
def __init__(self) -> None:
self.reset()
pass
def reset(self):
self.account={}
self.escrow={}
self.lme=LightMatchingEngine()
@@ -21,8 +25,7 @@ class Exchange():
self.demand={}
self.total_supply={}
self.supply={}
pass
def add_to_account(self,account_id,resource,amount):
"""
Adds resources to account escrow
@@ -237,4 +240,13 @@ class Exchange():
if account_id in self.account:
if resource in self.account[account_id]:
return self.account[account_id][resource]
return 0
def get_total_demand(self,resource):
if resource in self.total_demand:
return self.total_demand[resource]
return 0
def get_total_supply(self,resource):
if resource in self.total_supply:
return self.total_supply[resource]
return 0

View File

@@ -1,4 +1,8 @@
import random
from .commoditys import commoditys as cm
from .exchange import Exchange
from .business import Price_Believe_Business
import uuid
class Simulation():
"""
Class for controlling the different calculation steps of the Simulation.
@@ -8,8 +12,22 @@ class Simulation():
self.reset_funcs={}
self.tick_count=0
self.episode_count=0
self.cells={}
self.businesses=[]
self.production_util={}
for k in cm.productions.items():
l=k[1]
for i in l:
self.production_util[i["id"]]=0
self.cx=Exchange()
pass
def set_cells(self,cells):
for c in cells:
self.cells[c.name]=c
def set_businesses(self,bus):
self.businesses=bus
def seed(self,a):
"""
Sets the random seed
@@ -19,8 +37,8 @@ class Simulation():
self.tick_funcs[id]=tickfunc
self.reset_funcs[id]=resetfunc
def tick_random_order(self):
def tick_agents_random_order(self):
"""
Ticks all agents in a Random Order
"""
@@ -29,6 +47,18 @@ class Simulation():
for k in keys:
fun=self.tick_funcs[k]
fun(self.tick_count,self.episode_count)
def tick(self,episode_length):
"""
Executes a tick of the simulation
"""
self.tick_agents_random_order()
for cell_id,c in self.cells.items():
c.setup_demand_for_step(episode_length)
for b in self.businesses:
b.tick(self.tick_count)
self.tick_count+=1
def reset(self):
@@ -37,5 +67,83 @@ class Simulation():
"""
for k,v in self.reset_funcs.items():
v(self.episode_count)
for cell_id,cell in self.cells.items():
cell.setup_supply_for_episode()
self.tick_count=0
self.episode_count+=1
self.episode_count+=1
def get_underutelized_prods(self,threshold):
"""
Returns all productions that fall under the given threshold
"""
under=[]
for k,v in self.production_util.items():
if v<threshold:
under.append(k)
return under
def create_bussiness(self):
"""
Create a new business based on given econemy state.
Return Business
"""
selected_prod=None
# Create a business for every production rule out there
under_prods=self.get_underutelized_prods(1)
if len(under_prods)>0:
# we need to create more businesses
selected_prod=random.choice(under_prods)
else:
selected_prod=self.get_max_profit_prod()
prod=cm.productions_id_map[selected_prod]
# Now select cell to place business
cells=self.select_business_cell(prod)
cell_id=random.choice(cells)
cell=self.cells[cell_id]
cxs=[cell.exchange,self.cx]
business=Price_Believe_Business.Price_Believe_Business(uuid.uuid4(),prod,1000,cxs,self)
self.businesses.append(business)
self.production_util[selected_prod]+=1
return business
def get_max_profit_prod(self):
"""
Returns the prod with the highest profit in last episode
"""
profit=self.businesses[0].balance-self.businesses[0].balance_history[-1]
prod=self.businesses[0].production
for b in self.businesses:
diff=b.balance-b.balance_history[-1]
if diff>profit:
profit=diff
prod=b.production
return prod
def select_business_cell(self,prod):
"""
Calculate score for placement. Return cell with best score.
"""
cell_score={self.cells[k].name: 0 for k in self.cells}
for id,cell in self.cells.items():
cx=cell.exchange
# Best prod resource availability
# Using the exchange in that cell, lookup supply or demand of resources. Supply +1 / Demand -1
for prod_item in prod["prod"]:
key=list(prod_item.keys())[0]
val=prod_item[key]
a=cx.get_total_supply(key)
cell_score[cell.name]+=(cx.get_total_supply(key)/val)
cell_score[cell.name]-=(cx.get_total_demand(key)/val)
# If demand for prod item in cell then score +1/ score -1 if supply is already there
a=cx.get_total_demand(prod["name"])
cell_score[cell.name]+=cx.get_total_demand(prod["name"])
cell_score[cell.name]-=cx.get_total_supply(prod["name"])
max_keys = [key for key, value in cell_score.items() if value == max(cell_score.values())]
return max_keys

19
main.py
View File

@@ -28,14 +28,13 @@ cx.submit_order(w,"Wood",0,1000,Side.BUY)
cx.submit_order(2,"Gem",1,10,Side.SELL)
sim=Simulation()
bus=Price_Believe_Business(1,{
'name': 'Wood',
'amount': 1,
'craft': {
'Raw_Wood': 1,
}
},50,cxs,sim)
for i in range(100):
sim.tick_random_order()
sim.set_cells(cells)
sim.reset()
sim.tick(1)
# create info
while len(sim.get_underutelized_prods(1))>0:
sim.create_bussiness()
sim.reset()
for i in range(1000):
sim.tick(1000)
print("help")