trade routes instead of resources

This commit is contained in:
Richard 2024-01-15 19:39:08 +01:00
parent 7d92a45d12
commit 560ac056ff
7 changed files with 89 additions and 41 deletions

View File

@ -27,6 +27,7 @@ class TradeOption:
resource: str resource: str
source: Waypoint source: Waypoint
dest: Waypoint dest: Waypoint
buy: int
margin: int margin: int
dist: int dist: int
score: float score: float
@ -159,31 +160,49 @@ class Analyzer:
prices[r].append({ prices[r].append({
'wp': m.waypoint, 'wp': m.waypoint,
'buy': p.buy, 'buy': p.buy,
'sell': p.sell 'sell': p.sell,
'volume': p.volume
}) })
return prices return prices
def find_trade(self, system): def find_trade(self, system):
prices = self.prices(system) prices = self.prices(system)
occupied_resources = set() occupied_routes = set()
for s in self.store.all('Ship'): for s in self.store.all('Ship'):
if s.mission != 'trade': if s.mission != 'trade':
continue continue
occupied_resources.add(s.mission_state['resource']) occupied_routes.add((s.mission_state['site'], s.mission_state['dest']))
best = None best = None
for resource, markets in prices.items(): for resource, markets in prices.items():
if resource in occupied_resources:
continue
source = sorted(markets, key=lambda x: x['buy'])[0] source = sorted(markets, key=lambda x: x['buy'])[0]
dest = sorted(markets, key=lambda x: x['sell'])[-1] dest = sorted(markets, key=lambda x: x['sell'])[-1]
swp = source['wp']
dwp = dest['wp']
margin = dest['sell'] -source['buy'] margin = dest['sell'] -source['buy']
if (swp.symbol,dwp.symbol) in occupied_routes:
dist = source['wp'].distance(dest['wp']) continue
dist = swp.distance(dwp)
dist = max(dist, 0.0001) dist = max(dist, 0.0001)
score = margin / dist score = margin / dist
if margin < 0: if margin < 0:
continue continue
o = TradeOption(resource, source['wp'], dest['wp'], margin, dist, score) o = TradeOption(resource, swp, dwp, source['buy'], margin, dist, score)
if best is None or best.score < o.score: if best is None or best.score < o.score:
best = o best = o
return best return best
def find_deal(self, smkt, dmkt):
best_margin = 0
best_resource = None
print(f'finding deal from {smkt} to {dmkt}')
for r, sp in smkt.prices.items():
if not r in dmkt.prices:
print(r, 'not in dmkt')
continue
dp = dmkt.prices[r]
margin = dp.sell - sp.buy
print(r, margin)
if margin > best_margin:
best_margin = margin
best_resource = r
return best_resource

View File

@ -131,10 +131,8 @@ class CentralCommand:
return return
print(f'assigning {s} to deliver {t.resource} from {t.source} to {t.dest} at a margin of {t.margin}') print(f'assigning {s} to deliver {t.resource} from {t.source} to {t.dest} at a margin of {t.margin}')
self.init_mission(s, 'trade') self.init_mission(s, 'trade')
self.smipa(s, 'resource', t.resource)
self.smipa(s, 'site', t.source) self.smipa(s, 'site', t.source)
self.smipa(s, 'dest', t.dest) self.smipa(s, 'dest', t.dest)
self.smipa(s, 'delivery', 'sell')
def init_mission(self, s, mtyp): def init_mission(self, s, mtyp):
if mtyp == 'none': if mtyp == 'none':

View File

@ -85,7 +85,7 @@ class Commander(CommandLine):
elif len(matches) > 1: elif len(matches) > 1:
raise CommandError('multiple matches') raise CommandError('multiple matches')
else: else:
raise CommandError('not found') raise CommandError(f'{arg} not found')
def resolve_system(self, system_str): def resolve_system(self, system_str):
if type(system_str) == System: if type(system_str) == System:
@ -97,6 +97,20 @@ class Commander(CommandLine):
system = self.store.get(System, system_str) system = self.store.get(System, system_str)
return system return system
def resolve_waypoint(self, w):
if type(w) == Waypoint:
return w
if w == '':
if not self.has_ship(): return
return self.ship.location
p = w.split('-')
if len(p) == 1:
if not self.has_ship(): return
s = self.ship.location.system
w = f'{s}-{w}'
return self.store.get(Waypoint, w)
######## First run ######### ######## First run #########
def agent_setup(self): def agent_setup(self):
symbol = input('agent name: ') symbol = input('agent name: ')
@ -232,12 +246,17 @@ class Commander(CommandLine):
r = self.api.jumps(waypoint) r = self.api.jumps(waypoint)
pprint(r) pprint(r)
def do_shipyard(self): def do_shipyard(self, w=''):
if not self.has_ship(): return location = self.resolve_waypoint(w)
location = self.ship.location if location is None:
raise CommandError(f'waypoint {w} not found')
data = self.api.shipyard(location) data = self.api.shipyard(location)
if 'ships' in data:
for s in must_get(data, 'ships'): for s in must_get(data, 'ships'):
print(s['type'], s['purchasePrice']) print(s['type'], s['purchasePrice'])
else:
for s in must_get(data, 'shipTypes'):
print(s['type'])
######## Commerce ######### ######## Commerce #########
def do_refuel(self, source='market'): def do_refuel(self, source='market'):
@ -532,9 +551,12 @@ class Commander(CommandLine):
system = self.ship.location.system system = self.ship.location.system
prices = self.analyzer.prices(system) prices = self.analyzer.prices(system)
if resource is not None: if resource is not None:
pprint(prices[resource.upper()]) prices = {resource: prices[resource.upper()]}
else:
pprint(prices) for res, p in prices.items():
print('==' + res)
for m in p:
print(f"{m['wp'].symbol:12s} {m['volume']:5d} {m['buy']:5d} {m['sell']:5d}")
def do_path(self, waypoint_str): def do_path(self, waypoint_str):
if not self.has_ship(): return if not self.has_ship(): return

View File

@ -61,6 +61,8 @@ class Mission:
def rst(self, typ, nm): def rst(self, typ, nm):
symbol = self.st(nm) symbol = self.st(nm)
if symbol is None:
return None
return self.store.get(typ, symbol) return self.store.get(typ, symbol)
def st(self, nm): def st(self, nm):
@ -186,21 +188,6 @@ class BaseMission(Mission):
else: else:
return 'more' return 'more'
def step_load(self):
credits = self.api.agent.credits
cargo_space = self.ship.cargo_capacity - self.ship.cargo_units
resource = self.st('resource')
loc = self.ship.location
market = self.store.get('Marketplace', loc.symbol)
price = market.buy_price(resource)
volume = market.volume(resource)
affordable = credits // price
amount = min(cargo_space, affordable)
while amount > 0:
amt = min(amount, volume)
self.api.buy(self.ship, resource, amt)
amount -= amt
def step_travel(self): def step_travel(self):
traject = self.st('traject') traject = self.st('traject')
if traject is None or traject == []: if traject is None or traject == []:

View File

@ -6,14 +6,29 @@ class TradeMission(BaseMission):
def start_state(self): def start_state(self):
return 'travel-to' return 'travel-to'
def step_load(self):
credits = self.api.agent.credits
cargo_space = self.ship.cargo_capacity - self.ship.cargo_units
smkt = self.store.get('Marketplace', self.st('site'))
dmkt = self.store.get('Marketplace', self.st('dest'))
resource = self.analyzer.find_deal(smkt, dmkt)
if resource is None:
return 'done'
price = smkt.buy_price(resource)
volume = smkt.volume(resource)
affordable = credits // price
print(cargo_space, affordable, volume)
amount = min(cargo_space, affordable, volume)
if amount == 0:
return 'done'
self.api.buy(self.ship, resource, amount)
return 'done' if amount == cargo_space else 'more'
@classmethod @classmethod
def params(cls): def params(cls):
return { return {
'site': MissionParam(Waypoint, True), 'site': MissionParam(Waypoint, True),
'resource': MissionParam(str, True),
'dest': MissionParam(Waypoint, True), 'dest': MissionParam(Waypoint, True),
'delivery': MissionParam(str, True, 'deliver'),
'contract': MissionParam(Contract, False)
} }
def steps(self): def steps(self):
@ -21,7 +36,10 @@ class TradeMission(BaseMission):
**self.travel_steps('to', 'site', 'dock'), **self.travel_steps('to', 'site', 'dock'),
'dock': (self.step_dock, 'market-pre'), 'dock': (self.step_dock, 'market-pre'),
'market-pre': (self.step_market, 'load'), 'market-pre': (self.step_market, 'load'),
'load': (self.step_load, 'market-post'), 'load': (self.step_load, {
'more': 'market-pre',
'done': 'market-post'
}),
'market-post': (self.step_market, 'travel-back'), 'market-post': (self.step_market, 'travel-back'),
**self.travel_steps('back', 'dest', 'dock-dest'), **self.travel_steps('back', 'dest', 'dock-dest'),
'dock-dest': (self.step_dock, 'unload'), 'dock-dest': (self.step_dock, 'unload'),

View File

@ -100,7 +100,7 @@ class Marketplace(Base):
r += 'X: ' + ', '.join(self.exchange) + '\n' r += 'X: ' + ', '.join(self.exchange) + '\n'
r += '\n' r += '\n'
for p in self.prices.values(): for res, p in self.prices.items():
t = self.rtype(p['symbol']) t = self.rtype(res)
r += f'{t} {p["symbol"]:25s} {p["sell"]:5d} {p["buy"]:5d}\n' r += f'{t} {res:25s} {p.sell:5d} {p.buy:5d}\n'
return r return r

View File

@ -99,6 +99,8 @@ class Ship(Base):
def deliverable_cargo(self, contract): def deliverable_cargo(self, contract):
result = [] result = []
if contract is None:
return result
for d in contract.deliveries: for d in contract.deliveries:
if self.get_cargo(d['trade_symbol']) > 0: if self.get_cargo(d['trade_symbol']) > 0:
result.append(d['trade_symbol']) result.append(d['trade_symbol'])
@ -126,6 +128,8 @@ class Ship(Base):
mstatus = self.mission_status mstatus = self.mission_status
if mstatus == 'error': if mstatus == 'error':
mstatus = mstatus.upper() mstatus = mstatus.upper()
if mstatus is None:
mstatus = 'none'
status = self.status.lower() status = self.status.lower()
if status.startswith('in_'): if status.startswith('in_'):
status = status[3:] status = status[3:]