0ptr/nullptr/commander.py
2024-01-09 20:39:11 +01:00

545 lines
16 KiB
Python

from nullptr.command_line import CommandLine
from nullptr.store import Store
from nullptr.analyzer import Analyzer, Point, path_dist
import argparse
from nullptr.models import *
from nullptr.api import Api
from .util import *
from time import sleep, time
from threading import Thread
from nullptr.central_command import CentralCommand
import readline
import os
from copy import copy
class CommandError(Exception):
pass
class Commander(CommandLine):
def __init__(self, data_dir='data'):
store_file = os.path.join(data_dir, 'store.npt')
hist_file = os.path.join(data_dir, 'cmd.hst')
self.hist_file = hist_file
if os.path.isfile(hist_file):
readline.read_history_file(hist_file)
self.store = Store(store_file, False)
self.agent = self.select_agent()
self.api = Api(self.store, self.agent)
self.centcom = CentralCommand(self.store, self.api)
self.analyzer = Analyzer(self.store)
self.ship = None
self.stop_auto= False
super().__init__()
######## INFRA #########
def handle_eof(self):
self.store.close()
readline.write_history_file(self.hist_file)
print("Goodbye!")
def do_pp(self):
pprint(self.api.last_result)
def prompt(self):
if self.ship:
return f'{self.ship.symbol}> '
else:
return '> '
def after_cmd(self):
self.store.flush()
def do_auto(self):
self.centcom.run_interactive()
######## Resolvers #########
def ask_obj(self, typ, prompt):
obj = None
while obj is None:
symbol = input(prompt).strip()
obj = self.store.get(typ, symbol.upper())
if obj is None:
print('not found')
return obj
def has_ship(self):
if self.ship is not None:
return True
else:
print('set a ship')
def select_agent(self):
agents = self.store.all(Agent)
agent = next(agents, None)
if agent is None:
agent = self.agent_setup()
return agent
def resolve(self, typ, arg):
arg = arg.upper()
matches = [c for c in self.store.all(typ) if c.symbol.startswith(arg)]
if len(matches) == 1:
return matches[0]
elif len(matches) > 1:
raise CommandError('multiple matches')
else:
raise CommandError('not found')
def resolve_system(self, system_str):
if type(system_str) == System:
return system_str
if system_str == '':
if not self.has_ship(): return
system = self.ship.location.system
else:
system = self.store.get(System, system_str)
return system
######## First run #########
def agent_setup(self):
symbol = input('agent name: ')
agent = self.store.get(Agent, symbol, create=True)
self.agent = agent
api = Api(self.store, agent)
self.api = api
faction = input('faction or token: ')
if len(faction) > 50:
self.agent.token = faction
else:
api.register(faction.upper().strip())
print('=== agent:')
print(agent)
print('=== ships')
self.do_ships('r')
print('=== contracts')
self.do_contracts('r')
ship = self.store.get(Ship, symbol.upper() + '-2')
print("=== catalog initial system")
self.do_catalog(ship.location.system)
self.do_stats()
self.store.flush()
return agent
def do_token(self):
print(self.agent.token)
def do_register(self, faction):
self.api.register(faction.upper())
pprint(self.api.agent)
######## Fleet #########
def do_info(self, arg=''):
if arg.startswith('r'):
self.api.info()
pprint(self.agent, 100)
def do_ships(self, arg=''):
if arg.startswith('r'):
r = self.api.list_ships()
else:
r = sorted(list(self.store.all('Ship')))
pprint(r)
def do_ship(self, arg=''):
if arg != '':
symbol = f'{self.agent.symbol}-{arg}'
ship = self.store.get('Ship', symbol)
if ship is None:
print('not found')
return
else:
self.ship = ship
pprint(self.ship, 5)
######## Atlas #########
def do_systems(self, page=1):
r = self.api.list_systems(int(page))
pprint(self.api.last_meta)
def do_catalog(self, system_str=''):
system = self.resolve_system(system_str)
r = self.api.list_waypoints(system)
for w in r:
if 'MARKETPLACE' in w.traits:
self.api.marketplace(w)
if w.type == 'JUMP_GATE':
self.api.jumps(w)
def do_system(self, system_str):
system = self.store.get(System, system_str)
r = self.api.list_waypoints(system)
pprint(r)
def do_waypoints(self, system_str=''):
loc = None
if system_str == '':
if not self.has_ship(): return
loc = self.ship.location
system = loc.system
else:
system = self.store.get(System, system_str)
print(f'=== waypoints in {system}')
r = self.store.all_members(system, 'Waypoint')
for w in r:
wname = w.symbol.split('-')[2]
traits = ", ".join(w.traits())
typ = w.type[0]
if typ not in ['F','J'] and len(traits) == 0:
continue
if loc:
dist = loc.distance(w)
print(f'{wname:4} {typ} {dist:6} {traits}')
else:
print(f'{wname:4} {typ} {traits}')
def do_members(self):
if not self.has_ship(): return
system = self.ship.location.system
pprint(list(self.store.all_members(system)))
def do_wp(self, s=''):
self.do_waypoints(s)
######## Specials #########
def do_market(self, arg=''):
if arg == '':
if not self.has_ship(): return
waypoint = self.ship.location
else:
waypoint = self.resolve('Waypoint', arg)
r = self.api.marketplace(waypoint)
pprint(r, 3)
def do_atlas(self, state=None):
atlas = self.store.get(Atlas, 'ATLAS')
if state is not None:
atlas.enabled = True if state == 'on' else 'off'
pprint(atlas, 5)
def do_jumps(self, waypoint_str=None):
if waypoint_str is None:
if not self.has_ship(): return
waypoint = self.ship.location
else:
waypoint = self.store.get(Waypoint, waypoint_str.upper())
r = self.api.jumps(waypoint)
pprint(r)
def do_shipyard(self):
if not self.has_ship(): return
location = self.ship.location
data = self.api.shipyard(location)
for s in must_get(data, 'ships'):
print(s['type'], s['purchasePrice'])
######## Commerce #########
def do_refuel(self, source='market'):
if not self.has_ship(): return
from_cargo = source != 'market'
r = self.api.refuel(self.ship, from_cargo=from_cargo)
pprint(r)
def do_cargo(self):
if not self.has_ship(): return
print(f'== Cargo {self.ship.cargo_units}/{self.ship.cargo_capacity} ==')
for c, units in self.ship.cargo.items():
print(f'{units:4d} {c}')
def do_buy(self, resource, amt=None):
if not self.has_ship(): return
if amt is None:
amt = self.ship.cargo_capacity - self.ship.cargo_units
self.api.buy(self.ship, resource.upper(), amt)
self.do_cargo()
def do_sell(self, resource, amt=None):
if not self.has_ship(): return
self.api.sell(self.ship, resource.upper(), amt)
self.do_cargo()
def do_dump(self, resource):
if not self.has_ship(): return
self.api.jettison(self.ship, resource.upper())
self.do_cargo()
def do_purchase(self, ship_type):
if not self.has_ship(): return
location = self.ship.location
ship_type = ship_type.upper()
if not ship_type.startswith('SHIP'):
ship_type = 'SHIP_' + ship_type
s = self.api.purchase(ship_type, location)
pprint(s)
######## Mining #########
def do_siphon(self):
if not self.has_ship(): return
data = self.api.siphon(self.ship)
pprint(data)
def do_survey(self):
if not self.has_ship(): return
r = self.api.survey(self.ship)
pprint(r)
def do_surveys(self):
pprint(list(self.store.all('Survey')))
def do_extract(self, survey_str=''):
if not self.has_ship(): return
survey = None
if survey_str != '':
survey = self.resolve('Survey', survey_str)
result = self.api.extract(self.ship, survey)
symbol = mg(result,'extraction.yield.symbol')
units = mg(result,'extraction.yield.units')
print(units, symbol)
######## Missions #########
def print_mission(self):
print(f'mission: {self.ship.mission} ({self.ship.mission_status})')
pprint(self.ship.mission_state)
def do_role(self, role):
roles = [None, 'hauler']
if not self.has_ship(): return
if role == 'none':
role = None
if role not in roles:
print(f'role {role} not found. Choose from {roles}')
return
self.ship.role = role
def do_mission(self, arg=''):
if not self.has_ship(): return
if arg:
self.centcom.init_mission(self.ship, arg)
self.print_mission()
def do_mrestart(self, status='init'):
if not self.has_ship(): return
self.centcom.restart_mission(self.ship, status)
self.print_mission()
def do_mreset(self):
if not self.has_ship(): return
self.ship.mission_state = {}
def do_mset(self, nm, val):
if not self.has_ship(): return
self.centcom.set_mission_param(self.ship, nm, val)
######## Contracts #########
def active_contract(self):
for c in self.store.all('Contract'):
if c.accepted and not c.fulfilled: return c
raise CommandError('no active contract')
def do_contracts(self, arg=''):
if arg.startswith('r'):
r = self.api.list_contracts()
else:
r = list(self.store.all('Contract'))
pprint(r)
def do_negotiate(self):
if not self.has_ship(): return
r = self.api.negotiate(self.ship)
pprint(r)
def do_accept(self, c):
contract = self.resolve('Contract', c)
r = self.api.accept_contract(contract)
pprint(r)
def do_deliver(self):
if not self.has_ship(): return
site = self.ship.location
contract = self.active_contract()
delivery = contract.unfinished_delivery()
if delivery is None:
raise CommandError('no delivery')
resource = delivery['trade_symbol']
self.api.deliver(self.ship, resource, contract)
pprint(contract)
def do_fulfill(self):
contract = self.active_contract()
self.api.fulfill(contract)
######## Automissions #########
def do_cmine(self):
if not self.has_ship(): return
site = self.ship.location
contract = self.active_contract()
delivery = contract.unfinished_delivery()
if delivery is None:
raise CommandError('no delivery')
resource = delivery['trade_symbol']
destination = delivery['destination']
self.centcom.init_mission(self.ship, 'mine')
self.centcom.set_mission_param(self.ship, 'site', site)
self.centcom.set_mission_param(self.ship, 'resource', resource)
self.centcom.set_mission_param(self.ship, 'dest', destination)
self.centcom.set_mission_param(self.ship, 'contract', contract)
self.print_mission()
def do_chaul(self):
if not self.has_ship(): return
if len(self.ship.cargo) > 0:
raise CommandError('please dump cargo first')
contract = self.active_contract()
delivery = contract.unfinished_delivery()
if delivery is None:
raise CommandError('no delivery')
resource = delivery['trade_symbol']
destination = delivery['destination']
m = self.analyzer.find_closest_markets(resource, 'buy', destination)
if len(m) == 0:
m = self.analyzer.find_closest_markets(resource, 'exchange', destination)
if len(m) == 0:
print('no market found')
return
_, m, _, _ = m[0]
site = self.store.get(Waypoint, m.symbol)
self.centcom.init_mission(self.ship, 'haul')
self.centcom.set_mission_param(self.ship, 'site', site)
self.centcom.set_mission_param(self.ship, 'resource', resource)
self.centcom.set_mission_param(self.ship, 'dest', destination)
self.centcom.set_mission_param(self.ship, 'contract', contract)
self.print_mission()
def do_cprobe(self):
if not self.has_ship(): return
contract = self.active_contract()
delivery = contract.unfinished_delivery()
if delivery is None:
raise CommandError('no delivery')
resource = delivery['trade_symbol']
destination = delivery['destination']
m = self.analyzer.find_closest_markets(resource, 'buy,exchange', destination)
if len(m) == 0:
print('no market found')
return
markets = [ mkt[1] for mkt in m]
markets = self.analyzer.solve_tsp(markets)
self.centcom.init_mission(self.ship, 'probe')
self.centcom.set_mission_param(self.ship, 'hops', markets)
self.print_mission()
def do_sprobe(self):
if not self.has_ship(): return
system = self.ship.location.system
m = [m.waypoint for m in self.store.all_members(system, 'Marketplace')]
print("pre", path_dist(m))
m = self.analyzer.solve_tsp(m)
print("post", path_dist(m))
hops = [w.symbol for w in m]
self.centcom.init_mission(self.ship, 'probe')
self.centcom.set_mission_param(self.ship, 'hops', hops)
self.print_mission()
######## Travel #########
def do_travel(self, dest):
dest = self.resolve('Waypoint', dest)
self.centcom.init_mission(self.ship, 'travel')
self.centcom.set_mission_param(self.ship, 'dest', dest)
self.print_mission()
def do_go(self, arg):
if not self.has_ship(): return
system = self.ship.location.system
symbol = f'{system}-{arg}'
dest = self.resolve('Waypoint', symbol)
self.api.navigate(self.ship, dest)
pprint(self.ship)
def do_dock(self):
if not self.has_ship(): return
self.api.dock(self.ship)
pprint(self.ship)
def do_orbit(self):
if not self.has_ship(): return
self.api.orbit(self.ship)
pprint(self.ship)
def do_speed(self, speed):
if not self.has_ship(): return
speed = speed.upper()
speeds = ['DRIFT', 'STEALTH','CRUISE','BURN']
if speed not in speeds:
print('please choose from:', speeds)
self.api.flight_mode(self.ship, speed)
def do_jump(self, waypoint_str):
if not self.has_ship(): return
w = self.resolve('Waypoint', waypoint_str)
self.api.jump(self.ship, w)
pprint(self.ship)
######## Analysis #########
def do_stats(self):
total = 0
for t in self.store.data:
num = len(self.store.data[t])
nam = t.__name__
total += num
print(f'{num:5d} {nam}')
print(f'{total:5d} total')
def do_defrag(self):
self.store.defrag()
def do_obj(self, oid):
if not '.' in oid:
print('Usage: obj SYMBOL.ext')
return
symbol, ext = oid.split('.')
symbol = symbol.upper()
if not ext in self.store.extensions:
print('unknown extension')
typ = self.store.extensions[ext]
obj = self.store.get(typ, symbol)
if obj is None:
print('object not found')
pprint(obj.__getstate__())
print('=== store ===')
h = self.store.get_header(obj)
if h:
pprint(h, 3)
else:
print('Not stored')
print('Dirty: ', obj in self.store.dirty_objects)
def do_query(self, resource):
if not self.has_ship(): return
location = self.ship.location
resource = resource.upper()
print('Found markets:')
for typ, m, d, plen in self.analyzer.find_closest_markets(resource, 'buy,exchange',location):
price = '?'
if resource in m.prices:
price = m.prices[resource]['buy']
print(m, typ[0], f'{plen-1:3} hops {price}')
def do_findtrade(self):
if not self.has_ship(): return
system = self.ship.location.system
t = self.analyzer.find_trade(system)
pprint(t)
def do_prices(self, resource=None):
if not self.has_ship(): return
system = self.ship.location.system
prices = self.analyzer.prices(system)
if resource is not None:
pprint(prices[resource.upper()])
else:
pprint(prices)