Package horizons :: Package ai :: Package aiplayer :: Module internationaltrademanager
[hide private]
[frames] | no frames]

Source Code for Module horizons.ai.aiplayer.internationaltrademanager

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import logging 
 23  from collections import defaultdict 
 24   
 25  from horizons.component.storagecomponent import StorageComponent 
 26  from horizons.component.tradepostcomponent import TradePostComponent 
 27  from horizons.constants import RES, TRADER 
 28   
 29  from .mission.internationaltrade import InternationalTrade 
 30   
 31   
32 -class InternationalTradeManager:
33 """ 34 An object of this class manages the international trade routes of one AI player. 35 36 The current implementation is limited to one active route between each pair of our 37 settlement and another player's settlement where each route can have at most one 38 bought and one sold resource. The routes are automatically removed when they have 39 been used once or when the ship gets destroyed. 40 """ 41 42 log = logging.getLogger("ai.aiplayer.internationaltrade") 43
44 - def __init__(self, owner):
45 super().__init__() 46 self.owner = owner 47 self.world = owner.world 48 self.session = owner.session 49 self.personality = owner.personality_manager.get('InternationalTradeManager')
50
51 - def _trade_mission_exists(self, settlement, settlement_manager):
52 """Return a boolean showing whether there is a trade route between the settlements.""" 53 for mission in self.owner.missions: 54 if not isinstance(mission, InternationalTrade): 55 continue 56 if mission.settlement is settlement and mission.settlement_manager is settlement_manager: 57 return True 58 return False
59
60 - def _add_route(self):
61 """Add a new international trade route if possible.""" 62 ship = None 63 for possible_ship, state in self.owner.ships.items(): 64 if state is self.owner.shipStates.idle: 65 ship = possible_ship 66 break 67 if not ship: 68 #self.log.info('%s international trade: no available ships', self) 69 return 70 71 # find all possible legal trade route options 72 options = defaultdict(list) # {(settlement, settlement_manager): (total value, amount, resource id, bool(selling)), ...} 73 for settlement in self.world.settlements: 74 if settlement.owner is self.owner: 75 continue # don't allow routes of this type between the player's own settlements 76 for settlement_manager in self.owner.settlement_managers: 77 if self._trade_mission_exists(settlement, settlement_manager): 78 continue # allow only one international trade route between a pair of settlements 79 my_inventory = settlement_manager.settlement.get_component(StorageComponent).inventory 80 resource_manager = settlement_manager.resource_manager 81 82 # add the options where we sell to the other player 83 for resource_id, limit in settlement.get_component(TradePostComponent).buy_list.items(): 84 if resource_id not in resource_manager.resource_requirements: 85 continue # not a well-known resource: ignore it 86 if limit <= settlement.get_component(StorageComponent).inventory[resource_id]: 87 continue # they aren't actually buying the resource 88 if my_inventory[resource_id] <= resource_manager.resource_requirements[resource_id]: 89 continue # my settlement is unable to sell the resource 90 price = int(self.session.db.get_res_value(resource_id) * TRADER.PRICE_MODIFIER_SELL) 91 tradable_amount = min(my_inventory[resource_id] - resource_manager.resource_requirements[resource_id], 92 limit - settlement.get_component(StorageComponent).inventory[resource_id], ship.get_component(StorageComponent).inventory.get_limit(), settlement.owner.get_component(StorageComponent).inventory[RES.GOLD] // price) 93 options[(settlement, settlement_manager)].append((tradable_amount * price, tradable_amount, resource_id, True)) 94 95 # add the options where we buy from the other player 96 for resource_id, limit in settlement.get_component(TradePostComponent).sell_list.items(): 97 if resource_id not in resource_manager.resource_requirements: 98 continue # not a well-known resource: ignore it 99 if limit >= settlement.get_component(StorageComponent).inventory[resource_id]: 100 continue # they aren't actually selling the resource 101 if my_inventory[resource_id] >= resource_manager.resource_requirements[resource_id]: 102 continue # my settlement doesn't want to buy the resource 103 price = int(self.session.db.get_res_value(resource_id) * TRADER.PRICE_MODIFIER_BUY) 104 tradable_amount = min(resource_manager.resource_requirements[resource_id] - my_inventory[resource_id], 105 settlement.get_component(StorageComponent).inventory[resource_id] - limit, ship.get_component(StorageComponent).inventory.get_limit(), self.owner.get_component(StorageComponent).inventory[RES.GOLD] // price) 106 options[(settlement, settlement_manager)].append((tradable_amount * price, tradable_amount, resource_id, False)) 107 if not options: 108 #self.log.info('%s international trade: no interesting options', self) 109 return 110 111 # make up final options where a route is limited to at most one resource bought and one resource sold 112 final_options = [] # [(value, bought resource id or None, sold resource id or None, settlement, settlement_manager), ...] 113 for (settlement, settlement_manager), option in sorted(options.items()): 114 best_buy = None # largest amount of resources 115 best_sale = None # most expensive sale 116 for total_price, tradable_amount, resource_id, selling in option: 117 if selling: 118 if best_sale is None or best_sale[0] < total_price: 119 best_sale = (total_price, tradable_amount, resource_id) 120 else: 121 if best_buy is None or best_buy[1] < tradable_amount: 122 best_buy = (total_price, tradable_amount, resource_id) 123 buy_coefficient = self.personality.buy_coefficient_rich if self.owner.get_component(StorageComponent).inventory[RES.GOLD] > self.personality.little_money else self.personality.buy_coefficient_poor 124 total_value = (best_sale[0] if best_sale else 0) + (best_buy[1] if best_buy else 0) * buy_coefficient 125 final_options.append((total_value, best_buy[2] if best_buy else None, best_sale[2] if best_sale else None, settlement, settlement_manager)) 126 127 bought_resource, sold_resource, settlement, settlement_manager = sorted(final_options, reverse=True, key=lambda x: x[0])[0][1:] 128 self.owner.start_mission(InternationalTrade(settlement_manager, settlement, ship, bought_resource, sold_resource, self.owner.report_success, self.owner.report_failure))
129
130 - def tick(self):
131 self._add_route()
132