0ptr/nullptr/analyzer.py

224 lines
5.7 KiB
Python
Raw Normal View History

from nullptr.models.marketplace import Marketplace
from nullptr.models.jumpgate import Jumpgate
from nullptr.models.system import System
2023-06-21 07:32:31 +00:00
from nullptr.models.waypoint import Waypoint
from dataclasses import dataclass
2024-01-13 10:27:32 +00:00
from nullptr.util import pprint
2024-01-06 06:17:53 +00:00
from copy import copy
2024-01-06 06:17:53 +00:00
class AnalyzerException(Exception):
pass
def path_dist(m):
t = 0
o = Point(0,0)
for w in m:
t +=w.distance(o)
o = w
return t
2024-01-06 06:17:53 +00:00
@dataclass
class Point:
x: int
y: int
2024-01-04 20:34:31 +00:00
@dataclass
class TradeOption:
resource: str
source: Waypoint
dest: Waypoint
2024-01-15 18:39:08 +00:00
buy: int
2024-01-04 20:34:31 +00:00
margin: int
dist: int
score: float
@dataclass
class SearchNode:
system: System
parent: 'SearchNode'
def __hash__(self):
return hash(self.system.symbol)
def path(self):
result = []
n = self
while n is not None:
result.append(n.system)
n = n.parent
result.reverse()
return result
def __repr__(self):
return self.system.symbol
class Analyzer:
def __init__(self, store):
self.store = store
2023-06-14 14:55:45 +00:00
def find_markets(self, resource, sellbuy):
for m in self.store.all(Marketplace):
2023-06-23 11:32:08 +00:00
if 'sell' in sellbuy and resource in m.imports:
yield ('sell', m)
elif 'buy' in sellbuy and resource in m.exports:
yield ('buy', m)
elif 'exchange' in sellbuy and resource in m.exchange:
yield ('exchange', m)
def find_closest_markets(self, resource, sellbuy, location):
2023-06-20 20:18:20 +00:00
if type(location) == str:
2023-06-21 07:32:31 +00:00
location = self.store.get(Waypoint, location)
mkts = self.find_markets(resource, sellbuy)
2023-06-23 11:32:08 +00:00
candidates = []
origin = location.system
2023-06-23 11:32:08 +00:00
for typ, m in mkts:
system = m.waypoint.system
2023-06-23 11:49:09 +00:00
d = origin.distance(system)
candidates.append((typ, m, d))
possibles = sorted(candidates, key=lambda m: m[2])
possibles = possibles[:10]
results = []
for typ,m,d in possibles:
system = m.waypoint.system
2023-06-21 07:32:31 +00:00
p = self.find_path(origin, system)
if p is None: continue
2023-06-23 11:49:09 +00:00
results.append((typ,m,d,len(p)))
return results
def solve_tsp(self, waypoints):
2024-01-06 06:17:53 +00:00
wps = copy(waypoints)
path = []
cur = Point(0,0)
while len(wps) > 0:
closest = wps[0]
for w in wps:
if w.distance(cur) < closest.distance(cur):
closest = w
cur = closest
path.append(closest)
wps.remove(closest)
return path
def get_jumpgate(self, system):
gates = self.store.all_members(system, Jumpgate)
return next(gates, None)
2024-01-06 06:17:53 +00:00
# dijkstra shmijkstra
def find_nav_path(self, orig, to, ran):
path = []
mkts = [m.waypoint for m in self.store.all_members(orig.system, Marketplace)]
cur = orig
if orig == to:
2024-01-13 10:27:32 +00:00
return []
2024-01-06 06:17:53 +00:00
while cur != to:
best = cur
bestdist = cur.distance(to)
if bestdist < ran:
path.append(to)
break
for m in mkts:
dist = m.distance(to)
if dist < bestdist and cur.distance(m) < ran:
best = m
bestdist = dist
if best == cur:
raise AnalyzerException(f'no path to {to}')
cur = best
path.append(cur)
return path
def find_jump_path(self, orig, to, depth=100, seen=None):
if depth < 1: return None
2023-06-14 19:26:46 +00:00
if seen is None:
seen = set()
if type(orig) == System:
orig = set([SearchNode(orig,None)])
2024-01-06 06:17:53 +00:00
result = [n for n in orig if n==to]
if len(result) > 0:
return result[0].path()
dest = set()
for o in orig:
2024-01-06 06:17:53 +00:00
jg = self.get_jumpgate(o)
if jg is None: continue
2024-01-06 06:17:53 +00:00
for s in jg.connections:
2023-06-14 19:26:46 +00:00
if s in seen: continue
2023-06-14 13:54:32 +00:00
seen.add(s)
2023-07-12 20:26:25 +00:00
dest.add(SearchNode(s, o))
2023-06-14 19:20:47 +00:00
if len(dest) == 0:
return None
2023-06-14 19:26:46 +00:00
return self.find_path(dest, to, depth-1, seen)
2024-01-04 20:34:31 +00:00
def prices(self, system):
prices = {}
for m in self.store.all_members(system, Marketplace):
2024-01-13 20:42:49 +00:00
for r, p in m.prices.items():
2024-01-04 20:34:31 +00:00
if not r in prices:
prices[r] = []
prices[r].append({
'wp': m.waypoint,
2024-01-13 10:27:32 +00:00
'buy': p.buy,
2024-01-15 18:39:08 +00:00
'sell': p.sell,
2024-01-20 19:33:50 +00:00
'volume': p.volume,
'category': m.rtype(r)
2024-01-04 20:34:31 +00:00
})
return prices
def find_trade(self, system):
max_traders = 3
2024-01-04 20:34:31 +00:00
prices = self.prices(system)
occupied_routes = dict()
for s in self.store.all('Ship'):
2024-01-13 20:42:49 +00:00
if s.mission != 'trade':
continue
k = (s.mission_state['site'], s.mission_state['dest'])
if k in occupied_routes:
occupied_routes[k] += 1
else:
occupied_routes[k] = 1
2024-01-04 20:34:31 +00:00
best = None
for resource, markets in prices.items():
source = sorted(markets, key=lambda x: x['buy'])[0]
dest = sorted(markets, key=lambda x: x['sell'])[-1]
2024-01-15 18:39:08 +00:00
swp = source['wp']
dwp = dest['wp']
2024-01-04 20:34:31 +00:00
margin = dest['sell'] -source['buy']
k = (swp.symbol,dwp.symbol)
if k in occupied_routes and occupied_routes[k] > max_traders:
2024-01-15 18:39:08 +00:00
continue
dist = swp.distance(dwp)
2024-01-04 20:34:31 +00:00
dist = max(dist, 0.0001)
score = margin / dist
if margin < 0:
continue
2024-01-15 18:39:08 +00:00
o = TradeOption(resource, swp, dwp, source['buy'], margin, dist, score)
2024-01-04 20:34:31 +00:00
if best is None or best.score < o.score:
best = o
return best
2024-01-15 18:39:08 +00:00
def find_deal(self, smkt, dmkt):
best_margin = 0
best_resource = None
for r, sp in smkt.prices.items():
if not r in dmkt.prices:
continue
dp = dmkt.prices[r]
margin = dp.sell - sp.buy
if margin > best_margin:
best_margin = margin
best_resource = r
return best_resource
2024-01-20 19:33:50 +00:00
def best_sell_market(self, system, r):
best_price = 0
best_market = None
for m in self.store.all_members(system, Marketplace):
if r not in m.prices: continue
price = m.prices[r].sell
if price > best_price:
best_price = price
best_market = m
return best_market