#!/usr/bin/env python3
# encoding: UTF-8
# This file is part of turberfield.
#
# Turberfield is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Turberfield is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with turberfield. If not, see <http://www.gnu.org/licenses/>.
import asyncio
from collections import defaultdict
from datetime import timedelta
import itertools
import logging
from turberfield.common.accounts import Accounts
from turberfield.common.accounts import GoodsOnCredit
from turberfield.common.dramatics import Dramatics
from turberfield.common.inventory import Inventory
from turberfield.common.inventory import Location
from turberfield.common.inventory import Resource
from turberfield.common.types import Move
from turberfield.common.types import RenderGoods
from turberfield.dynamics.types import Stop
from turberfield.eargain.types import Commodity
from turberfield.eargain.types import CreditRelationship
from turberfield.eargain.types import HOUR
from turberfield.eargain.types import WEEK
from turberfield.eargain.types import Mass
from turberfield.eargain.types import TradePosition
__doc__ = """
Here's how you would create regular goods-on-credit exchange between
characters in a simulation.
.. code-block:: python
loop = asyncio.get_event_loop()
# Bits and pieces from turberfield.common
dramatics = Dramatics()
jq = JobQueue(loop=loop)
# The characters have to meet in the same place
tavern = Location("quarry", "furlong", "tavern", None)
# The characters' need to buy or sell is stored as a Drama
dramatics.relations[merchant, Commodity.STON].add(TradePosition.buying)
dramatics.relations[miner, Commodity.FOOD].add(TradePosition.buying)
dramatics.relations[merchant, Commodity.FOOD].add(TradePosition.selling)
dramatics.relations[miner, Commodity.STON].add(TradePosition.selling)
routine = Exchange(merchant, miner, meet=tavern)
tasks = [Simulation.task(loop, routine, jq)]
sim = Simulation(tasks, jq, buf)
"""
[docs]class Exchange:
def __init__(
self, *args,
start:int=None,
end:int=None,
period:int=None,
meet:Location=None
):
self.parties = args
self.start = start or timedelta(days=2, hours=8).total_seconds()
self.end = end or timedelta(
seconds=self.start, hours=4).total_seconds()
self.period = period or WEEK
self.meet = meet
assert self.meet
@asyncio.coroutine
def __call__(self, q, jq, priority):
log = logging.getLogger(
"turberfield.eargain.exchange.{}".format(priority))
accounts = Accounts()
dramatics = Dramatics()
inventory = Inventory()
tick = None
while not isinstance(tick, Stop):
tick = yield from q.get()
if not self.start <= tick.t % self.period < self.end:
continue
parties = list(self.parties)
for p in self.parties:
if p not in inventory.places[self.meet]:
parties.remove(p)
yield from jq.put(
tick.t + HOUR, tick.priority,
Move(p, self.meet))
market = defaultdict(lambda: defaultdict(set))
shouts = (
(a, c, t)
for a, c in itertools.product(parties, Commodity)
for t in dramatics.relations[a, c]
if isinstance(t, TradePosition)
)
for a, c, p in shouts:
market[c][p].add(a)
log.debug(market)
for commodity, positions in market.items():
try:
src = positions[TradePosition.selling].pop()
dst = positions[TradePosition.buying].pop()
except KeyError as e:
log.debug(e)
continue
else:
measure = Mass.Kg # FIXME
quantity = (
inventory.pockets[src][commodity] / measure.value)
if quantity < 1:
log.debug(quantity)
continue
goods = Resource(commodity, measure, quantity)
yield from jq.put(
tick.t, tick.priority,
RenderGoods(
src, dst, goods,
self.meet, CreditRelationship.established)
)
else:
return tick