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
source: Waypoint
dest: Waypoint
buy: int
margin: int
dist: int
score: float
@ -159,31 +160,49 @@ class Analyzer:
prices[r].append({
'wp': m.waypoint,
'buy': p.buy,
'sell': p.sell
'sell': p.sell,
'volume': p.volume
})
return prices
def find_trade(self, system):
prices = self.prices(system)
occupied_resources = set()
occupied_routes = set()
for s in self.store.all('Ship'):
if s.mission != 'trade':
continue
occupied_resources.add(s.mission_state['resource'])
occupied_routes.add((s.mission_state['site'], s.mission_state['dest']))
best = None
for resource, markets in prices.items():
if resource in occupied_resources:
continue
source = sorted(markets, key=lambda x: x['buy'])[0]
dest = sorted(markets, key=lambda x: x['sell'])[-1]
swp = source['wp']
dwp = dest['wp']
margin = dest['sell'] -source['buy']
dist = source['wp'].distance(dest['wp'])
if (swp.symbol,dwp.symbol) in occupied_routes:
continue
dist = swp.distance(dwp)
dist = max(dist, 0.0001)
score = margin / dist
if margin < 0:
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:
best = o
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
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.smipa(s, 'resource', t.resource)
self.smipa(s, 'site', t.source)
self.smipa(s, 'dest', t.dest)
self.smipa(s, 'delivery', 'sell')
def init_mission(self, s, mtyp):
if mtyp == 'none':

View File

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

View File

@ -61,6 +61,8 @@ class Mission:
def rst(self, typ, nm):
symbol = self.st(nm)
if symbol is None:
return None
return self.store.get(typ, symbol)
def st(self, nm):
@ -186,21 +188,6 @@ class BaseMission(Mission):
else:
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):
traject = self.st('traject')
if traject is None or traject == []:

View File

@ -6,14 +6,29 @@ class TradeMission(BaseMission):
def start_state(self):
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
def params(cls):
return {
'site': MissionParam(Waypoint, True),
'resource': MissionParam(str, True),
'dest': MissionParam(Waypoint, True),
'delivery': MissionParam(str, True, 'deliver'),
'contract': MissionParam(Contract, False)
}
def steps(self):
@ -21,7 +36,10 @@ class TradeMission(BaseMission):
**self.travel_steps('to', 'site', 'dock'),
'dock': (self.step_dock, 'market-pre'),
'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'),
**self.travel_steps('back', 'dest', 'dock-dest'),
'dock-dest': (self.step_dock, 'unload'),

View File

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

View File

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