major command staff restructure
Heads had to roll
This commit is contained in:
parent
fb3b6162fc
commit
74ce884b05
@ -9,4 +9,5 @@ ADD --chown=user . /app
|
|||||||
RUN chmod +x /app/main.py
|
RUN chmod +x /app/main.py
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
#ENTRYPOINT bash
|
#ENTRYPOINT bash
|
||||||
|
RUN echo "python3 /app/main.py -d /data" > ~/.bash_history
|
||||||
CMD ["/bin/sh", "-c", "python3 /app/main.py -d /data ; bash -i"]
|
CMD ["/bin/sh", "-c", "python3 /app/main.py -d /data ; bash -i"]
|
@ -52,172 +52,184 @@ class SearchNode:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.system.symbol
|
return self.system.symbol
|
||||||
|
|
||||||
class Analyzer:
|
|
||||||
def __init__(self, store):
|
|
||||||
self.store = store
|
|
||||||
|
|
||||||
def find_markets(self, resource, sellbuy):
|
def find_markets(c, resource, sellbuy):
|
||||||
for m in self.store.all(Marketplace):
|
for m in c.store.all(Marketplace):
|
||||||
if 'sell' in sellbuy and resource in m.imports:
|
if 'sell' in sellbuy and resource in m.imports:
|
||||||
yield ('sell', m)
|
yield ('sell', m)
|
||||||
|
|
||||||
elif 'buy' in sellbuy and resource in m.exports:
|
elif 'buy' in sellbuy and resource in m.exports:
|
||||||
yield ('buy', m)
|
yield ('buy', m)
|
||||||
|
|
||||||
elif 'exchange' in sellbuy and resource in m.exchange:
|
elif 'exchange' in sellbuy and resource in m.exchange:
|
||||||
yield ('exchange', m)
|
yield ('exchange', m)
|
||||||
|
|
||||||
def find_closest_markets(self, resource, sellbuy, location):
|
def find_closest_markets(c, resource, sellbuy, location):
|
||||||
if type(location) == str:
|
if type(location) == str:
|
||||||
location = self.store.get(Waypoint, location)
|
location = c.store.get(Waypoint, location)
|
||||||
mkts = self.find_markets(resource, sellbuy)
|
mkts = find_markets(resource, sellbuy)
|
||||||
candidates = []
|
candidates = []
|
||||||
origin = location.system
|
origin = location.system
|
||||||
for typ, m in mkts:
|
for typ, m in mkts:
|
||||||
system = m.waypoint.system
|
system = m.waypoint.system
|
||||||
d = origin.distance(system)
|
d = origin.distance(system)
|
||||||
candidates.append((typ, m, d))
|
candidates.append((typ, m, d))
|
||||||
possibles = sorted(candidates, key=lambda m: m[2])
|
possibles = sorted(candidates, key=lambda m: m[2])
|
||||||
possibles = possibles[:10]
|
possibles = possibles[:10]
|
||||||
results = []
|
results = []
|
||||||
for typ,m,d in possibles:
|
for typ,m,d in possibles:
|
||||||
system = m.waypoint.system
|
system = m.waypoint.system
|
||||||
p = self.find_path(origin, system)
|
p = find_jump_path(origin, system)
|
||||||
if p is None: continue
|
if p is None: continue
|
||||||
results.append((typ,m,d,len(p)))
|
results.append((typ,m,d,len(p)))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def solve_tsp(self, waypoints):
|
def solve_tsp(c, waypoints):
|
||||||
wps = copy(waypoints)
|
wps = copy(waypoints)
|
||||||
path = []
|
path = []
|
||||||
cur = Point(0,0)
|
cur = Point(0,0)
|
||||||
while len(wps) > 0:
|
while len(wps) > 0:
|
||||||
closest = wps[0]
|
closest = wps[0]
|
||||||
for w in wps:
|
for w in wps:
|
||||||
if w.distance(cur) < closest.distance(cur):
|
if w.distance(cur) < closest.distance(cur):
|
||||||
closest = w
|
closest = w
|
||||||
cur = closest
|
cur = closest
|
||||||
path.append(closest)
|
path.append(closest)
|
||||||
wps.remove(closest)
|
wps.remove(closest)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def get_jumpgate(self, system):
|
def get_jumpgate(c, system):
|
||||||
gates = self.store.all_members(system, Jumpgate)
|
gates = c.store.all_members(system, Jumpgate)
|
||||||
return next(gates, None)
|
return next(gates, None)
|
||||||
|
|
||||||
# dijkstra shmijkstra
|
# dijkstra shmijkstra
|
||||||
def find_nav_path(self, orig, to, ran):
|
def find_nav_path(c, orig, to, ran):
|
||||||
path = []
|
path = []
|
||||||
mkts = [m.waypoint for m in self.store.all_members(orig.system, Marketplace)]
|
mkts = [m.waypoint for m in c.store.all_members(orig.system, Marketplace)]
|
||||||
cur = orig
|
cur = orig
|
||||||
if orig == to:
|
if orig == to:
|
||||||
|
|
||||||
return []
|
return []
|
||||||
while cur != to:
|
while cur != to:
|
||||||
best = cur
|
best = cur
|
||||||
bestdist = cur.distance(to)
|
bestdist = cur.distance(to)
|
||||||
if bestdist < ran:
|
if bestdist < ran:
|
||||||
path.append(to)
|
path.append(to)
|
||||||
break
|
break
|
||||||
for m in mkts:
|
for m in mkts:
|
||||||
dist = m.distance(to)
|
dist = m.distance(to)
|
||||||
if dist < bestdist and cur.distance(m) < ran:
|
if dist < bestdist and cur.distance(m) < ran:
|
||||||
best = m
|
best = m
|
||||||
bestdist = dist
|
bestdist = dist
|
||||||
if best == cur:
|
if best == cur:
|
||||||
raise AnalyzerException(f'no path to {to}')
|
raise AnalyzerException(f'no path to {to}')
|
||||||
cur = best
|
cur = best
|
||||||
path.append(cur)
|
path.append(cur)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def find_jump_path(self, orig, to, depth=100, seen=None):
|
def find_jump_path(c, orig, to, depth=100, seen=None):
|
||||||
if depth < 1: return None
|
if depth < 1: return None
|
||||||
if seen is None:
|
if seen is None:
|
||||||
seen = set()
|
seen = set()
|
||||||
if type(orig) == System:
|
if type(orig) == System:
|
||||||
orig = set([SearchNode(orig,None)])
|
orig = set([SearchNode(orig,None)])
|
||||||
result = [n for n in orig if n==to]
|
result = [n for n in orig if n==to]
|
||||||
if len(result) > 0:
|
if len(result) > 0:
|
||||||
return result[0].path()
|
return result[0].path()
|
||||||
dest = set()
|
dest = set()
|
||||||
for o in orig:
|
for o in orig:
|
||||||
jg = self.get_jumpgate(o)
|
jg = get_jumpgate(o)
|
||||||
if jg is None: continue
|
if jg is None: continue
|
||||||
for s in jg.connections:
|
for s in jg.connections:
|
||||||
if s in seen: continue
|
if s in seen: continue
|
||||||
seen.add(s)
|
seen.add(s)
|
||||||
dest.add(SearchNode(s, o))
|
dest.add(SearchNode(s, o))
|
||||||
if len(dest) == 0:
|
if len(dest) == 0:
|
||||||
return None
|
return None
|
||||||
return self.find_path(dest, to, depth-1, seen)
|
return find_jump_path(dest, to, depth-1, seen)
|
||||||
|
|
||||||
def prices(self, system):
|
def prices(c, system):
|
||||||
prices = {}
|
prices = {}
|
||||||
for m in self.store.all_members(system, Marketplace):
|
for m in c.store.all_members(system, Marketplace):
|
||||||
for r, p in m.prices.items():
|
for r, p in m.prices.items():
|
||||||
if not r in prices:
|
if not r in prices:
|
||||||
prices[r] = []
|
prices[r] = []
|
||||||
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,
|
'volume': p.volume,
|
||||||
'category': m.rtype(r)
|
'category': m.rtype(r)
|
||||||
})
|
})
|
||||||
return prices
|
return prices
|
||||||
|
|
||||||
def find_trade(self, system):
|
def find_trade(c, system):
|
||||||
max_traders = 3
|
max_traders = 3
|
||||||
prices = self.prices(system)
|
prices = prices(system)
|
||||||
occupied_routes = dict()
|
occupied_routes = dict()
|
||||||
for s in self.store.all('Ship'):
|
for s in c.store.all('Ship'):
|
||||||
if s.mission != 'trade':
|
if s.mission != 'trade':
|
||||||
continue
|
continue
|
||||||
k = (s.mission_state['site'], s.mission_state['dest'])
|
k = (s.mission_state['site'], s.mission_state['dest'])
|
||||||
if k in occupied_routes:
|
if k in occupied_routes:
|
||||||
occupied_routes[k] += 1
|
occupied_routes[k] += 1
|
||||||
else:
|
else:
|
||||||
occupied_routes[k] = 1
|
occupied_routes[k] = 1
|
||||||
best = None
|
best = None
|
||||||
for resource, markets in prices.items():
|
for resource, markets in prices.items():
|
||||||
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']
|
swp = source['wp']
|
||||||
dwp = dest['wp']
|
dwp = dest['wp']
|
||||||
margin = dest['sell'] -source['buy']
|
margin = dest['sell'] -source['buy']
|
||||||
k = (swp.symbol,dwp.symbol)
|
k = (swp.symbol,dwp.symbol)
|
||||||
if k in occupied_routes and occupied_routes[k] > max_traders:
|
if k in occupied_routes and occupied_routes[k] > max_traders:
|
||||||
continue
|
continue
|
||||||
dist = swp.distance(dwp)
|
dist = swp.distance(dwp)
|
||||||
dist = max(dist, 0.0001)
|
dist = max(dist, 0.0001)
|
||||||
score = margin / dist
|
score = margin / dist
|
||||||
if margin < 2:
|
if margin < 2:
|
||||||
continue
|
continue
|
||||||
o = TradeOption(resource, swp, dwp, source['buy'], 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):
|
def find_deal(c, smkt, dmkt):
|
||||||
best_margin = 0
|
best_margin = 0
|
||||||
best_resource = None
|
best_resource = None
|
||||||
for r, sp in smkt.prices.items():
|
for r, sp in smkt.prices.items():
|
||||||
if not r in dmkt.prices:
|
if not r in dmkt.prices:
|
||||||
continue
|
continue
|
||||||
dp = dmkt.prices[r]
|
dp = dmkt.prices[r]
|
||||||
margin = dp.sell - sp.buy
|
margin = dp.sell - sp.buy
|
||||||
if margin > best_margin:
|
if margin > best_margin:
|
||||||
best_margin = margin
|
best_margin = margin
|
||||||
best_resource = r
|
best_resource = r
|
||||||
return best_resource
|
return best_resource
|
||||||
|
|
||||||
|
def best_sell_market(c, system, r):
|
||||||
|
best_price = 0
|
||||||
|
best_market = None
|
||||||
|
for m in c.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
|
||||||
|
|
||||||
|
def find_gas(c, system):
|
||||||
|
m = [w for w in c.store.all_members(system, 'Waypoint') if w.type == 'GAS_GIANT']
|
||||||
|
if len(m)==0:
|
||||||
|
raise AnalyzerException('no gas giant found')
|
||||||
|
return m[0]
|
||||||
|
|
||||||
|
def find_metal(c, system):
|
||||||
|
m = [w for w in c.store.all_members(system, Waypoint) if 'COMMON_METAL_DEPOSITS' in w.traits]
|
||||||
|
if len(m) == 0:
|
||||||
|
return None
|
||||||
|
origin = Point(0,0)
|
||||||
|
m = sorted(m, key=lambda w: w.distance(origin))
|
||||||
|
return m[0]
|
||||||
|
|
||||||
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
|
|
||||||
|
@ -7,7 +7,8 @@ from nullptr.models.ship import Ship
|
|||||||
from nullptr.models.shipyard import Shipyard
|
from nullptr.models.shipyard import Shipyard
|
||||||
from .util import *
|
from .util import *
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
class ApiError(Exception):
|
|
||||||
|
class ApiError(AppError):
|
||||||
def __init__(self, msg, code):
|
def __init__(self, msg, code):
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
self.code = code
|
self.code = code
|
||||||
@ -16,9 +17,9 @@ class ApiLimitError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class Api:
|
class Api:
|
||||||
def __init__(self, store, agent):
|
def __init__(self, c, agent):
|
||||||
self.agent = agent
|
self.agent = agent
|
||||||
self.store = store
|
self.store = c.store
|
||||||
self.requests_sent = 0
|
self.requests_sent = 0
|
||||||
self.last_meta = None
|
self.last_meta = None
|
||||||
self.last_result = None
|
self.last_result = None
|
||||||
|
@ -6,19 +6,25 @@ from random import choice, randrange
|
|||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from nullptr.atlas_builder import AtlasBuilder
|
from nullptr.atlas_builder import AtlasBuilder
|
||||||
from nullptr.analyzer import Analyzer, Point
|
from nullptr.general import General
|
||||||
|
from nullptr.util import *
|
||||||
|
from nullptr.roles import assign_mission
|
||||||
|
|
||||||
class CentralCommandError(Exception):
|
class CentralCommandError(AppError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CentralCommand:
|
class Captain:
|
||||||
def __init__(self, store, api):
|
def __init__(self, context):
|
||||||
self.missions = {}
|
self.missions = {}
|
||||||
self.stopping = False
|
self.stopping = False
|
||||||
self.store = store
|
self.store = context.store
|
||||||
self.analyzer = Analyzer(store)
|
self.c = context
|
||||||
self.api = api
|
self.general = context.general
|
||||||
self.atlas_builder = AtlasBuilder(store, api)
|
self.api = context.api
|
||||||
|
self.general = context.general
|
||||||
|
self.atlas_builder = AtlasBuilder(self.store, self.api)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
self.update_missions()
|
self.update_missions()
|
||||||
|
|
||||||
def get_ready_missions(self):
|
def get_ready_missions(self):
|
||||||
@ -40,6 +46,7 @@ class CentralCommand:
|
|||||||
mission.step()
|
mission.step()
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
|
self.general.tick()
|
||||||
self.update_missions()
|
self.update_missions()
|
||||||
missions = self.get_ready_missions()
|
missions = self.get_ready_missions()
|
||||||
if len(missions) == 0: return False
|
if len(missions) == 0: return False
|
||||||
@ -64,9 +71,10 @@ class CentralCommand:
|
|||||||
self.stopping = True
|
self.stopping = True
|
||||||
print('stopping...')
|
print('stopping...')
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while not self.stopping:
|
while not self.stopping:
|
||||||
|
# any new orders?
|
||||||
|
self.c.general.tick()
|
||||||
did_step = True
|
did_step = True
|
||||||
request_counter = self.api.requests_sent
|
request_counter = self.api.requests_sent
|
||||||
start = time()
|
start = time()
|
||||||
@ -116,100 +124,12 @@ class CentralCommand:
|
|||||||
if s in self.missions:
|
if s in self.missions:
|
||||||
self.stop_mission(s)
|
self.stop_mission(s)
|
||||||
if s.mission is None:
|
if s.mission is None:
|
||||||
self.assign_mission(s)
|
assign_mission(self.c, s)
|
||||||
if s.mission is not None and s not in self.missions:
|
if s.mission is not None and s not in self.missions:
|
||||||
self.start_mission(s)
|
self.start_mission(s)
|
||||||
if s in self.missions:
|
if s in self.missions:
|
||||||
m = self.missions[s]
|
m = self.missions[s]
|
||||||
|
|
||||||
def find_gas(self, system):
|
|
||||||
m = [w for w in self.store.all_members(system, 'Waypoint') if w.type == 'GAS_GIANT']
|
|
||||||
if len(m)==0:
|
|
||||||
raise CentralCommandError('no gas giant found')
|
|
||||||
return m[0]
|
|
||||||
|
|
||||||
def find_metal(self, system):
|
|
||||||
m = [w for w in self.store.all_members(system, Waypoint) if 'COMMON_METAL_DEPOSITS' in w.traits]
|
|
||||||
if len(m) == 0:
|
|
||||||
return None
|
|
||||||
origin = Point(0,0)
|
|
||||||
m = sorted(m, key=lambda w: w.distance(origin))
|
|
||||||
return m[0]
|
|
||||||
|
|
||||||
def assign_mission(self, s):
|
|
||||||
if s.role == 'trader':
|
|
||||||
self.assign_trade(s)
|
|
||||||
elif s.role == 'probe':
|
|
||||||
self.assign_probe(s)
|
|
||||||
elif s.role == 'siphon':
|
|
||||||
self.assign_siphon(s)
|
|
||||||
elif s.role == 'hauler':
|
|
||||||
self.assign_hauler(s)
|
|
||||||
elif s.role == 'surveyor':
|
|
||||||
self.assign_survey(s)
|
|
||||||
elif s.role == 'miner':
|
|
||||||
self.assign_mine(s)
|
|
||||||
|
|
||||||
def assign_mine(self, s):
|
|
||||||
if s.crew is None:
|
|
||||||
raise CentralCommandError('ship has no crew')
|
|
||||||
w = s.crew.site
|
|
||||||
resources = s.crew.resources
|
|
||||||
self.init_mission(s, 'mine')
|
|
||||||
self.smipa(s, 'site', w)
|
|
||||||
self.smipa(s, 'resources', resources)
|
|
||||||
|
|
||||||
def assign_survey(self, s):
|
|
||||||
if s.crew is None:
|
|
||||||
raise CentralCommandError('ship has no crew')
|
|
||||||
w = s.crew.site
|
|
||||||
self.init_mission(s, 'survey')
|
|
||||||
self.smipa(s, 'site', w)
|
|
||||||
|
|
||||||
|
|
||||||
def assign_hauler(self, s):
|
|
||||||
if s.crew is None:
|
|
||||||
raise CentralCommandError('ship has no crew')
|
|
||||||
w = s.crew.site
|
|
||||||
resources = s.crew.resources
|
|
||||||
resource = choice(resources)
|
|
||||||
m = self.analyzer.best_sell_market(s.location.system, resource)
|
|
||||||
s.log(f'assigning haul mission from {w} to {m}')
|
|
||||||
self.init_mission(s, 'haul')
|
|
||||||
self.smipa(s, 'site', w)
|
|
||||||
self.smipa(s, 'dest', m)
|
|
||||||
self.smipa(s, 'resources', resources)
|
|
||||||
|
|
||||||
def assign_siphon(self, s):
|
|
||||||
if s.crew is None:
|
|
||||||
raise CentralCommandError('ship has no crew')
|
|
||||||
w = s.crew.site
|
|
||||||
self.init_mission(s, 'siphon')
|
|
||||||
self.smipa(s, 'site', w)
|
|
||||||
|
|
||||||
def assign_probe(self, s):
|
|
||||||
system = s.location.system
|
|
||||||
m = [m.waypoint for m in self.store.all_members(system, 'Marketplace')]
|
|
||||||
m = self.analyzer.solve_tsp(m)
|
|
||||||
hops = [w.symbol for w in m]
|
|
||||||
start_hop = randrange(0, len(hops))
|
|
||||||
s.log(f'Assigning {s} to probe {len(hops)} starting at {hops[start_hop]}')
|
|
||||||
self.init_mission(s, 'probe')
|
|
||||||
self.smipa(s, 'hops', hops)
|
|
||||||
self.smipa(s, 'next-hop', start_hop)
|
|
||||||
|
|
||||||
def assign_trade(self, s):
|
|
||||||
t = self.analyzer.find_trade(s.location.system)
|
|
||||||
if t is None:
|
|
||||||
print(f"No trade for {s} found. Idling")
|
|
||||||
self.init_mission(s,'idle')
|
|
||||||
self.smipa(s, 'seconds', 600)
|
|
||||||
return
|
|
||||||
s.log(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, 'site', t.source)
|
|
||||||
self.smipa(s, 'dest', t.dest)
|
|
||||||
|
|
||||||
def init_mission(self, s, mtyp):
|
def init_mission(self, s, mtyp):
|
||||||
if mtyp == 'none':
|
if mtyp == 'none':
|
||||||
s.mission_state = {}
|
s.mission_state = {}
|
||||||
@ -232,7 +152,7 @@ class CentralCommand:
|
|||||||
|
|
||||||
def start_mission(self, s):
|
def start_mission(self, s):
|
||||||
mtype = s.mission
|
mtype = s.mission
|
||||||
m = create_mission(mtype, s, self.store, self.api)
|
m = create_mission(mtype, s, self.c)
|
||||||
self.missions[s] = m
|
self.missions[s] = m
|
||||||
m.status(s.mission_status)
|
m.status(s.mission_status)
|
||||||
return m
|
return m
|
||||||
@ -241,17 +161,4 @@ class CentralCommand:
|
|||||||
if s in self.missions:
|
if s in self.missions:
|
||||||
del self.missions[s]
|
del self.missions[s]
|
||||||
|
|
||||||
def create_default_crews(self):
|
|
||||||
system = self.api.agent.headquarters.system
|
|
||||||
gas_w = self.find_gas(system)
|
|
||||||
metal_w = self.find_metal(system)
|
|
||||||
metal = self.store.get('Crew', 'METAL', create=True)
|
|
||||||
metal.site = metal_w
|
|
||||||
metal.resources = ['COPPER_ORE','IRON_ORE','ALUMINUM_ORE']
|
|
||||||
gas = self.store.get('Crew', 'GAS', create=True)
|
|
||||||
gas.site = gas_w
|
|
||||||
gas.resources = ['HYDROCARBON','LIQUID_HYDROGEN','LIQUID_NITROGEN']
|
|
||||||
return [gas, metal]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3,6 +3,7 @@ import inspect
|
|||||||
import sys
|
import sys
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
|
from nullptr.util import AppError
|
||||||
|
|
||||||
def func_supports_argcount(f, cnt):
|
def func_supports_argcount(f, cnt):
|
||||||
argspec = inspect.getargspec(f)
|
argspec = inspect.getargspec(f)
|
||||||
@ -41,7 +42,7 @@ class CommandLine:
|
|||||||
print(f'command not found; {c}')
|
print(f'command not found; {c}')
|
||||||
|
|
||||||
def handle_error(self, cmd, args, e):
|
def handle_error(self, cmd, args, e):
|
||||||
logging.error(e, exc_info=type(e).__name__ not in ['ApiError','CommandError', 'CentralCommandError'])
|
logging.error(e, exc_info=not issubclass(type(e), AppError))
|
||||||
|
|
||||||
def handle_empty(self):
|
def handle_empty(self):
|
||||||
pass
|
pass
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
from nullptr.command_line import CommandLine
|
from nullptr.command_line import CommandLine
|
||||||
from nullptr.store import Store
|
from nullptr.store import Store
|
||||||
from nullptr.analyzer import Analyzer, Point, path_dist
|
from nullptr.analyzer import *
|
||||||
|
from nullptr.context import Context
|
||||||
import argparse
|
import argparse
|
||||||
from nullptr.models import *
|
from nullptr.models import *
|
||||||
from nullptr.api import Api
|
from nullptr.api import Api
|
||||||
from .util import *
|
from .util import *
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from nullptr.central_command import CentralCommand
|
from nullptr.captain import Captain
|
||||||
|
from nullptr.general import General
|
||||||
import readline
|
import readline
|
||||||
import os
|
import os
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
|
|
||||||
class CommandError(Exception):
|
class CommandError(AppError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Commander(CommandLine):
|
class Commander(CommandLine):
|
||||||
@ -25,15 +27,20 @@ class Commander(CommandLine):
|
|||||||
if os.path.isfile(hist_file):
|
if os.path.isfile(hist_file):
|
||||||
readline.read_history_file(hist_file)
|
readline.read_history_file(hist_file)
|
||||||
self.store = Store(store_file, True)
|
self.store = Store(store_file, True)
|
||||||
|
self.c = Context(self.store)
|
||||||
self.agent = self.select_agent()
|
self.agent = self.select_agent()
|
||||||
self.api = Api(self.store, self.agent)
|
self.c.api = self.api = Api(self.c, self.agent)
|
||||||
self.centcom = CentralCommand(self.store, self.api)
|
self.c.general = self.general = General(self.c)
|
||||||
|
self.c.captain = self.captain = Captain(self.c)
|
||||||
|
|
||||||
|
self.general.setup()
|
||||||
|
self.captain.setup()
|
||||||
|
|
||||||
self.api.info()
|
self.api.info()
|
||||||
self.do_create_crews()
|
|
||||||
self.analyzer = Analyzer(self.store)
|
|
||||||
self.ship = None
|
self.ship = None
|
||||||
|
|
||||||
self.stop_auto= False
|
self.stop_auto = False
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
######## INFRA #########
|
######## INFRA #########
|
||||||
@ -55,7 +62,7 @@ class Commander(CommandLine):
|
|||||||
self.store.flush()
|
self.store.flush()
|
||||||
|
|
||||||
def do_auto(self):
|
def do_auto(self):
|
||||||
self.centcom.run_interactive()
|
self.captain.run_interactive()
|
||||||
|
|
||||||
def do_log(self, level):
|
def do_log(self, level):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
@ -170,7 +177,7 @@ class Commander(CommandLine):
|
|||||||
self.api.list_ships()
|
self.api.list_ships()
|
||||||
for s in self.store.all('Ship'):
|
for s in self.store.all('Ship'):
|
||||||
self.dump(s, 'all')
|
self.dump(s, 'all')
|
||||||
self.centcom.init_mission(s, 'none')
|
self.captain.init_mission(s, 'none')
|
||||||
|
|
||||||
######## Fleet #########
|
######## Fleet #########
|
||||||
def do_info(self, arg=''):
|
def do_info(self, arg=''):
|
||||||
@ -373,17 +380,17 @@ class Commander(CommandLine):
|
|||||||
def do_mission(self, arg=''):
|
def do_mission(self, arg=''):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
if arg:
|
if arg:
|
||||||
self.centcom.init_mission(ship, arg)
|
self.captain.init_mission(ship, arg)
|
||||||
self.print_mission()
|
self.print_mission()
|
||||||
|
|
||||||
def do_mrestart(self, status='init'):
|
def do_mrestart(self, status='init'):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
self.centcom.restart_mission(ship, status)
|
self.captain.restart_mission(ship, status)
|
||||||
self.print_mission()
|
self.print_mission()
|
||||||
|
|
||||||
def do_mstep(self):
|
def do_mstep(self):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
self.centcom.single_step(ship)
|
self.captain.single_step(ship)
|
||||||
self.print_mission()
|
self.print_mission()
|
||||||
|
|
||||||
def do_mreset(self):
|
def do_mreset(self):
|
||||||
@ -392,7 +399,7 @@ class Commander(CommandLine):
|
|||||||
|
|
||||||
def do_mset(self, nm, val):
|
def do_mset(self, nm, val):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
self.centcom.set_mission_param(ship, nm, val)
|
self.captain.set_mission_param(ship, nm, val)
|
||||||
|
|
||||||
def do_crew(self, arg):
|
def do_crew(self, arg):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
@ -402,7 +409,7 @@ class Commander(CommandLine):
|
|||||||
|
|
||||||
######## Crews #########
|
######## Crews #########
|
||||||
def do_create_crews(self):
|
def do_create_crews(self):
|
||||||
crews = self.centcom.create_default_crews()
|
crews = self.captain.create_default_crews()
|
||||||
for c in crews:
|
for c in crews:
|
||||||
print(f'{c.symbol:15s} {c.site}')
|
print(f'{c.symbol:15s} {c.site}')
|
||||||
|
|
||||||
@ -448,8 +455,8 @@ class Commander(CommandLine):
|
|||||||
def do_travel(self, dest):
|
def do_travel(self, dest):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
dest = self.resolve('Waypoint', dest)
|
dest = self.resolve('Waypoint', dest)
|
||||||
self.centcom.init_mission(ship, 'travel')
|
self.captain.init_mission(ship, 'travel')
|
||||||
self.centcom.set_mission_param(ship, 'dest', dest)
|
self.captain.set_mission_param(ship, 'dest', dest)
|
||||||
self.print_mission()
|
self.print_mission()
|
||||||
|
|
||||||
def do_go(self, arg):
|
def do_go(self, arg):
|
||||||
@ -535,7 +542,7 @@ class Commander(CommandLine):
|
|||||||
location = ship.location
|
location = ship.location
|
||||||
resource = resource.upper()
|
resource = resource.upper()
|
||||||
print('Found markets:')
|
print('Found markets:')
|
||||||
for typ, m, d, plen in self.analyzer.find_closest_markets(resource, 'buy,exchange',location):
|
for typ, m, d, plen in find_closest_markets(self.c, resource, 'buy,exchange',location):
|
||||||
price = '?'
|
price = '?'
|
||||||
if resource in m.prices:
|
if resource in m.prices:
|
||||||
price = m.prices[resource]['buy']
|
price = m.prices[resource]['buy']
|
||||||
@ -544,13 +551,13 @@ class Commander(CommandLine):
|
|||||||
def do_findtrade(self):
|
def do_findtrade(self):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
system = ship.location.system
|
system = ship.location.system
|
||||||
t = self.analyzer.find_trade(system)
|
t = find_trade(self.c, system)
|
||||||
pprint(t)
|
pprint(t)
|
||||||
|
|
||||||
def do_prices(self, resource=None):
|
def do_prices(self, resource=None):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
system = ship.location.system
|
system = ship.location.system
|
||||||
prices = self.analyzer.prices(system)
|
prices = prices(self.c, system)
|
||||||
if resource is not None:
|
if resource is not None:
|
||||||
prices = {resource: prices[resource.upper()]}
|
prices = {resource: prices[resource.upper()]}
|
||||||
|
|
||||||
@ -562,5 +569,5 @@ class Commander(CommandLine):
|
|||||||
def do_path(self, waypoint_str):
|
def do_path(self, waypoint_str):
|
||||||
ship = self.has_ship()
|
ship = self.has_ship()
|
||||||
w = self.resolve('Waypoint', waypoint_str)
|
w = self.resolve('Waypoint', waypoint_str)
|
||||||
p = self.analyzer.find_nav_path(ship.location, w, ship.fuel_capacity)
|
p = find_nav_path(self.c, ship.location, w, ship.fuel_capacity)
|
||||||
pprint(p)
|
pprint(p)
|
||||||
|
6
nullptr/context.py
Normal file
6
nullptr/context.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class Context:
|
||||||
|
def __init__(self, store, api=None, captain=None, general=None):
|
||||||
|
self.store = store
|
||||||
|
self.api = api
|
||||||
|
self.captain = captain
|
||||||
|
self.general = general
|
91
nullptr/general.py
Normal file
91
nullptr/general.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
from nullptr.util import *
|
||||||
|
from nullptr.analyzer import find_gas, find_metal
|
||||||
|
class GeneralError(AppError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class General:
|
||||||
|
def __init__(self, context):
|
||||||
|
self.store = context.store
|
||||||
|
self.api = context.api
|
||||||
|
self.c = context
|
||||||
|
agents = self.store.all('Agent')
|
||||||
|
self.agent = next(agents, None)
|
||||||
|
self.phases = {
|
||||||
|
'init': self.phase_startup,
|
||||||
|
'probes': self.phase_probes,
|
||||||
|
'trade': self.phase_trade,
|
||||||
|
'mine': self.phase_mine,
|
||||||
|
'siphon': self.phase_siphon,
|
||||||
|
'rampup': self.phase_rampup,
|
||||||
|
'gate': self.phase_gate
|
||||||
|
}
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.create_default_crews()
|
||||||
|
|
||||||
|
def find_shipyard(self, stype):
|
||||||
|
for shipyard in self.store.all('Shipyard'):
|
||||||
|
if stype in shipyard.types:
|
||||||
|
return stype
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def tick(self):
|
||||||
|
phase = self.agent.phase
|
||||||
|
if phase not in self.phases:
|
||||||
|
raise GeneralError('Invalid phase')
|
||||||
|
hdl = self.phases[phase]
|
||||||
|
new_phase = hdl()
|
||||||
|
if new_phase:
|
||||||
|
self.agent.phase = new_phase
|
||||||
|
|
||||||
|
|
||||||
|
def phase_startup(self):
|
||||||
|
# * first pricing info
|
||||||
|
# * probe at shipyard that sells probes
|
||||||
|
ag = self.agent.symbol
|
||||||
|
command = self.store.get('Ship', f'{ag}-1')
|
||||||
|
probe = self.store.get('Ship', f'{ag}-2')
|
||||||
|
command.role = 'probe'
|
||||||
|
probe.role = 'sitter'
|
||||||
|
|
||||||
|
def phase_probes(self):
|
||||||
|
# * probes on all markets
|
||||||
|
pass
|
||||||
|
|
||||||
|
def phase_trade(self):
|
||||||
|
# 20? traders
|
||||||
|
pass
|
||||||
|
|
||||||
|
def phase_mine(self):
|
||||||
|
# metal mining crew
|
||||||
|
pass
|
||||||
|
|
||||||
|
def phase_siphon(self):
|
||||||
|
# siphon crew
|
||||||
|
pass
|
||||||
|
|
||||||
|
def phase_rampup(self):
|
||||||
|
# stimulate markets for gate building
|
||||||
|
pass
|
||||||
|
|
||||||
|
def phase_gate(self):
|
||||||
|
# build the gate
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_default_crews(self):
|
||||||
|
system = self.api.agent.headquarters.system
|
||||||
|
gas_w = find_gas(self.c, system)
|
||||||
|
metal_w = find_metal(self.c, system)
|
||||||
|
metal = self.store.get('Crew', 'METAL', create=True)
|
||||||
|
metal.site = metal_w
|
||||||
|
metal.resources = ['COPPER_ORE','IRON_ORE','ALUMINUM_ORE']
|
||||||
|
gas = self.store.get('Crew', 'GAS', create=True)
|
||||||
|
gas.site = gas_w
|
||||||
|
gas.resources = ['HYDROCARBON','LIQUID_HYDROGEN','LIQUID_NITROGEN']
|
||||||
|
return [gas, metal]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -25,8 +25,8 @@ def get_mission_class( mtype):
|
|||||||
raise ValueError(f'invalid mission type {mtype}')
|
raise ValueError(f'invalid mission type {mtype}')
|
||||||
return types[mtype]
|
return types[mtype]
|
||||||
|
|
||||||
def create_mission(mtype, ship, store, api):
|
def create_mission(mtype, ship, c):
|
||||||
typ = get_mission_class(mtype)
|
typ = get_mission_class(mtype)
|
||||||
m = typ(ship, store, api)
|
m = typ(ship, c)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from nullptr.models.contract import Contract
|
|||||||
from nullptr.models.system import System
|
from nullptr.models.system import System
|
||||||
from nullptr.models.survey import Survey
|
from nullptr.models.survey import Survey
|
||||||
from nullptr.models.ship import Ship
|
from nullptr.models.ship import Ship
|
||||||
from nullptr.analyzer import Analyzer
|
from nullptr.analyzer import *
|
||||||
from time import time
|
from time import time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
@ -48,13 +48,13 @@ class Mission:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, ship, store, api):
|
def __init__(self, ship, context):
|
||||||
self.ship = ship
|
self.ship = ship
|
||||||
self.store = store
|
self.c = context
|
||||||
self.api = api
|
self.store = context.store
|
||||||
|
self.api = context.api
|
||||||
self.wait_for = None
|
self.wait_for = None
|
||||||
self.next_step = 0
|
self.next_step = 0
|
||||||
self.analyzer = Analyzer(self.store)
|
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
@ -263,15 +263,15 @@ class BaseMission(Mission):
|
|||||||
loc = self.ship.location
|
loc = self.ship.location
|
||||||
loc_sys = loc.system
|
loc_sys = loc.system
|
||||||
|
|
||||||
loc_jg = self.analyzer.get_jumpgate(loc_sys)
|
loc_jg = get_jumpgate(self.c, loc_sys)
|
||||||
loc_jg_wp = self.store.get(Waypoint, loc_jg.symbol)
|
loc_jg_wp = self.store.get(Waypoint, loc_jg.symbol)
|
||||||
dest_sys = dest.system
|
dest_sys = dest.system
|
||||||
dest_jg = self.analyzer.get_jumpgate(dest_sys)
|
dest_jg = get_jumpgate(self.c, dest_sys)
|
||||||
if dest_sys == loc_sys:
|
if dest_sys == loc_sys:
|
||||||
result = self.analyzer.find_nav_path(loc, dest, self.ship.range())
|
result = find_nav_path(self.c, loc, dest, self.ship.range())
|
||||||
self.sts('traject', result)
|
self.sts('traject', result)
|
||||||
return 'done' if len(result) == 0 else 'more'
|
return 'done' if len(result) == 0 else 'more'
|
||||||
path = self.analyzer.find_path(loc_sys, dest_sys)
|
path = find_jump_path(self.c, loc_sys, dest_sys)
|
||||||
result = []
|
result = []
|
||||||
if loc.symbol != loc_jg.symbol:
|
if loc.symbol != loc_jg.symbol:
|
||||||
result.append(loc_jg_wp)
|
result.append(loc_jg_wp)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from nullptr.missions.base import BaseMission, MissionParam
|
from nullptr.missions.base import BaseMission, MissionParam
|
||||||
from nullptr.models.waypoint import Waypoint
|
from nullptr.models.waypoint import Waypoint
|
||||||
|
from time import time
|
||||||
|
|
||||||
class SitMission(BaseMission):
|
class SitMission(BaseMission):
|
||||||
def start_state(self):
|
def start_state(self):
|
||||||
@ -14,10 +15,10 @@ class SitMission(BaseMission):
|
|||||||
def steps(self):
|
def steps(self):
|
||||||
return {
|
return {
|
||||||
**self.travel_steps('to', 'dest', 'sit'),
|
**self.travel_steps('to', 'dest', 'sit'),
|
||||||
'sit': (self.step_pass, 'done', self.wait_forever)
|
'sit': (self.step_sit, 'market'),
|
||||||
|
'market': (self.step_market, 'sit')
|
||||||
}
|
}
|
||||||
|
|
||||||
def wait_forever(self):
|
def step_sit(self):
|
||||||
return 0
|
self.next_step = time() + 15 * 60
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ from nullptr.missions.base import BaseMission, MissionParam
|
|||||||
from nullptr.models.waypoint import Waypoint
|
from nullptr.models.waypoint import Waypoint
|
||||||
from nullptr.models.survey import Survey
|
from nullptr.models.survey import Survey
|
||||||
from nullptr.models.contract import Contract
|
from nullptr.models.contract import Contract
|
||||||
|
from nullptr.analyzer import find_deal
|
||||||
|
|
||||||
class TradeMission(BaseMission):
|
class TradeMission(BaseMission):
|
||||||
def start_state(self):
|
def start_state(self):
|
||||||
return 'travel-to'
|
return 'travel-to'
|
||||||
@ -11,7 +13,7 @@ class TradeMission(BaseMission):
|
|||||||
cargo_space = self.ship.cargo_capacity - self.ship.cargo_units
|
cargo_space = self.ship.cargo_capacity - self.ship.cargo_units
|
||||||
smkt = self.store.get('Marketplace', self.st('site'))
|
smkt = self.store.get('Marketplace', self.st('site'))
|
||||||
dmkt = self.store.get('Marketplace', self.st('dest'))
|
dmkt = self.store.get('Marketplace', self.st('dest'))
|
||||||
resource = self.analyzer.find_deal(smkt, dmkt)
|
resource = find_deal(smkt, dmkt)
|
||||||
if resource is None:
|
if resource is None:
|
||||||
return 'done'
|
return 'done'
|
||||||
price = smkt.buy_price(resource)
|
price = smkt.buy_price(resource)
|
||||||
|
@ -6,6 +6,7 @@ class Agent(Base):
|
|||||||
self.token: str = None
|
self.token: str = None
|
||||||
self.credits: int = 0
|
self.credits: int = 0
|
||||||
self.headquarters: Waypoint = None
|
self.headquarters: Waypoint = None
|
||||||
|
self.phase = 'init'
|
||||||
|
|
||||||
def update(self, d):
|
def update(self, d):
|
||||||
self.seta('credits', d)
|
self.seta('credits', d)
|
||||||
|
20
nullptr/roles/__init__.py
Normal file
20
nullptr/roles/__init__.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from nullptr.roles.trader import assign_trader
|
||||||
|
from nullptr.roles.probe import assign_probe
|
||||||
|
from nullptr.roles.siphon import assign_siphon
|
||||||
|
from nullptr.roles.hauler import assign_hauler
|
||||||
|
from nullptr.roles.surveyor import assign_surveyor
|
||||||
|
from nullptr.roles.miner import assign_miner
|
||||||
|
|
||||||
|
def assign_mission(c, s):
|
||||||
|
if s.role == 'trader':
|
||||||
|
assign_trader(c, s)
|
||||||
|
elif s.role == 'probe':
|
||||||
|
assign_probe(c, s)
|
||||||
|
elif s.role == 'siphon':
|
||||||
|
assign_siphon(c, s)
|
||||||
|
elif s.role == 'hauler':
|
||||||
|
assign_hauler(c, s)
|
||||||
|
elif s.role == 'surveyor':
|
||||||
|
assign_surveyor(c, s)
|
||||||
|
elif s.role == 'miner':
|
||||||
|
assign_miner(c, s)
|
16
nullptr/roles/hauler.py
Normal file
16
nullptr/roles/hauler.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from nullptr.util import AppError
|
||||||
|
from nullptr.analyzer import best_sell_market
|
||||||
|
from random import choice
|
||||||
|
|
||||||
|
def assign_hauler(c, s):
|
||||||
|
if s.crew is None:
|
||||||
|
raise AppError('ship has no crew')
|
||||||
|
w = s.crew.site
|
||||||
|
resources = s.crew.resources
|
||||||
|
resource = choice(resources)
|
||||||
|
m = best_sell_market(c,s.location.system, resource)
|
||||||
|
s.log(f'assigning haul mission from {w} to {m}')
|
||||||
|
c.captain.init_mission(s, 'haul')
|
||||||
|
c.captain.smipa(s, 'site', w)
|
||||||
|
c.captain.smipa(s, 'dest', m)
|
||||||
|
c.captain.smipa(s, 'resources', resources)
|
11
nullptr/roles/miner.py
Normal file
11
nullptr/roles/miner.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from nullptr.util import AppError
|
||||||
|
|
||||||
|
def assign_miner(c, s):
|
||||||
|
if s.crew is None:
|
||||||
|
raise AppError('ship has no crew')
|
||||||
|
w = s.crew.site
|
||||||
|
resources = s.crew.resources
|
||||||
|
c.captain.init_mission(s, 'mine')
|
||||||
|
c.captain.smipa(s, 'site', w)
|
||||||
|
c.captain.smipa(s, 'resources', resources)
|
||||||
|
|
15
nullptr/roles/probe.py
Normal file
15
nullptr/roles/probe.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from nullptr.analyzer import solve_tsp
|
||||||
|
from random import randrange
|
||||||
|
|
||||||
|
def assign_probe(c, s):
|
||||||
|
system = s.location.system
|
||||||
|
m = [m.waypoint for m in c.store.all_members(system, 'Marketplace')]
|
||||||
|
m = solve_tsp(c, m)
|
||||||
|
hops = [w.symbol for w in m]
|
||||||
|
start_hop = randrange(0, len(hops))
|
||||||
|
s.log(f'Assigning {s} to probe {len(hops)} starting at {hops[start_hop]}')
|
||||||
|
|
||||||
|
c.captain.init_mission(s, 'probe')
|
||||||
|
c.captain.smipa(s, 'hops', hops)
|
||||||
|
c.captain.smipa(s, 'next-hop', start_hop)
|
||||||
|
|
8
nullptr/roles/siphon.py
Normal file
8
nullptr/roles/siphon.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from nullptr.util import AppError
|
||||||
|
|
||||||
|
def assign_siphon(c, s):
|
||||||
|
if s.crew is None:
|
||||||
|
raise AppError('ship has no crew')
|
||||||
|
w = s.crew.site
|
||||||
|
c.captain.init_mission(s, 'siphon')
|
||||||
|
c.captain.smipa(s, 'site', w)
|
10
nullptr/roles/surveyor.py
Normal file
10
nullptr/roles/surveyor.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from nullptr.util import AppError
|
||||||
|
|
||||||
|
|
||||||
|
def assign_surveyor(c, s):
|
||||||
|
if s.crew is None:
|
||||||
|
raise AppError('ship has no crew')
|
||||||
|
w = s.crew.site
|
||||||
|
c.init_mission(s, 'survey')
|
||||||
|
c.smipa(s, 'site', w)
|
||||||
|
|
14
nullptr/roles/trader.py
Normal file
14
nullptr/roles/trader.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from nullptr.analyzer import find_trade
|
||||||
|
|
||||||
|
|
||||||
|
def assign_trader(c, s):
|
||||||
|
t = find_trade(c, s.location.system)
|
||||||
|
if t is None:
|
||||||
|
print(f"No trade for {s} found. Idling")
|
||||||
|
c.captain.init_mission(s,'idle')
|
||||||
|
c.captain.smipa(s, 'seconds', 600)
|
||||||
|
return
|
||||||
|
s.log(f'assigning {s} to deliver {t.resource} from {t.source} to {t.dest} at a margin of {t.margin}')
|
||||||
|
c.captain.init_mission(s, 'trade')
|
||||||
|
c.captain.smipa(s, 'site', t.source)
|
||||||
|
c.captain.smipa(s, 'dest', t.dest)
|
@ -4,6 +4,9 @@ import os
|
|||||||
from os.path import isfile, dirname
|
from os.path import isfile, dirname
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
class AppError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def open_file(fn):
|
def open_file(fn):
|
||||||
d = dirname(fn)
|
d = dirname(fn)
|
||||||
os.makedirs(d, exist_ok=True)
|
os.makedirs(d, exist_ok=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user