From 4d51ad53c091edff35345e1a3930484012434b68 Mon Sep 17 00:00:00 2001 From: Richard Bronkhorst Date: Sun, 25 Jun 2023 17:35:06 +0200 Subject: [PATCH] Update analyzer.py, central_command.py and five other files --- nullptr/analyzer.py | 4 ++- nullptr/central_command.py | 20 +++++++++++++- nullptr/command_line.py | 2 +- nullptr/commander.py | 51 +++++++++++++++++++++--------------- nullptr/missions/__init__.py | 17 +++++++----- nullptr/missions/base.py | 2 ++ nullptr/missions/probe.py | 33 +++++++++++++++++++++++ 7 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 nullptr/missions/probe.py diff --git a/nullptr/analyzer.py b/nullptr/analyzer.py index bcc9bba..53ad12c 100644 --- a/nullptr/analyzer.py +++ b/nullptr/analyzer.py @@ -59,7 +59,9 @@ class Analyzer: results.append((typ,m,d,len(p))) return results - + def solve_tsp(self, waypoints): + # todo actually try to solve it + return waypoints def get_jumpgate(self, system): gates = self.store.all_members(system, Jumpgate) diff --git a/nullptr/central_command.py b/nullptr/central_command.py index 29c92ba..cd87dad 100644 --- a/nullptr/central_command.py +++ b/nullptr/central_command.py @@ -1,10 +1,13 @@ from nullptr.store import Store from nullptr.models.ship import Ship -from nullptr.missions import create_mission +from nullptr.missions import create_mission, get_mission_class from random import choice from time import sleep from threading import Thread +class CentralCommandError(Exception): + pass + class CentralCommand: def __init__(self, store, api): self.missions = {} @@ -86,6 +89,21 @@ class CentralCommand: m = self.missions[s] m.next_step = max(s.cooldown, s.arrival) + def init_mission(self, s, mtyp): + if mtyp == 'none': + s.mission_state = {} + s.mission_status = None + s.mission = None + return + try: + mclass = get_mission_class(mtyp) + except ValueError: + raise CentralCommandError('no such mission') + s.mission = mtyp + s.mission_status = 'init' + s.mission_state = {k: v.default for k,v in mclass.params().items()} + self.start_mission(s) + def start_mission(self, s): mtype = s.mission m = create_mission(mtype, s, self.store, self.api) diff --git a/nullptr/command_line.py b/nullptr/command_line.py index 923fa5d..100fe2c 100644 --- a/nullptr/command_line.py +++ b/nullptr/command_line.py @@ -41,7 +41,7 @@ class CommandLine: print(f'command not found; {c}') def handle_error(self, cmd, args, e): - logging.error(e, exc_info=type(e).__name__ not in ['ApiError','CommandError']) + logging.error(e, exc_info=type(e).__name__ not in ['ApiError','CommandError', 'CentralCommandError']) def handle_empty(self): pass diff --git a/nullptr/commander.py b/nullptr/commander.py index af20623..108a877 100644 --- a/nullptr/commander.py +++ b/nullptr/commander.py @@ -82,15 +82,6 @@ class Commander(CommandLine): def do_auto(self): self.centcom.run_interactive() - def set_mission(self, arg=''): - if arg == 'none': - arg = None - self.ship.mission = arg - self.ship.mission_status = 'init' - if 'traject' in self.ship.mission_state: - del self.ship.mission_state['traject'] - self.centcom.start_mission(self.ship) - def print_mission(self): print(f'mission: {self.ship.mission} ({self.ship.mission_status})') pprint(self.ship.mission_state) @@ -98,7 +89,7 @@ class Commander(CommandLine): def do_mission(self, arg=''): if not self.has_ship(): return if arg: - self.set_mission(arg) + self.centcom.init_mission(self.ship, arg) self.print_mission() def do_mreset(self): @@ -123,7 +114,7 @@ class Commander(CommandLine): raise CommandError('no delivery') resource = delivery['trade_symbol'] destination = delivery['destination'] - self.set_mission('mine') + 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) @@ -146,26 +137,41 @@ class Commander(CommandLine): return _, m, _, _ = m[0] site = self.store.get(Waypoint, m.symbol) - self.set_mission('haul') + self.centcom.init_mission(self.ship, 'haul') self.centcom.set_mission_param(self.ship, 'site', site.symbol) 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.symbol) 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) is None: + print('no market found') + return + markets = [ mkt[1] for mkt in m] + markets = self.analyzer.solve_tsp(markets) + hops = ','.join([m.symbol for m in markets]) + self.centcom.init_mission(self.ship, 'probe') + self.centcom.set_mission_param(self.ship, 'hops', hops) + self.print_mission() + def do_travel(self, dest): dest = self.resolve('Waypoint', dest) - self.set_mission('travel') + self.centcom.init_mission(self.ship, 'travel') self.centcom.set_mission_param(self.ship, 'dest', dest.symbol) self.print_mission() def do_register(self, faction): self.api.register(faction.upper()) - site = self.ship.location_str - contract = self.active_contract() - self.do_mission('mine') - self.centcom.set_mission_param(self.ship, 'site', site) - self.centcom.set_mission_param(self.ship, 'contract', contract) + pprint(self.api.agent) def do_universe(self, page=1): self.atlas_builder.run(page) @@ -218,13 +224,16 @@ class Commander(CommandLine): r = self.api.jumps(waypoint) pprint(r) - def do_query(self): + def do_query(self, resource): if not self.has_ship(): return location = self.ship.location() - resource = input('what resource?').upper() + resource = resource.upper() print('Found markets:') for typ, m, d, plen in self.analyzer.find_closest_markets(resource, 'buy,exchange',location): - print(m, typ[0], f'{plen-1} hops') + price = '?' + if resource in m.prices: + price = m.prices[resource]['buy'] + print(m, typ[0], f'{plen-1:3} hops {price}') def do_path(self): orig = self.ask_obj(System, 'from: ') diff --git a/nullptr/missions/__init__.py b/nullptr/missions/__init__.py index c095596..3abf95a 100644 --- a/nullptr/missions/__init__.py +++ b/nullptr/missions/__init__.py @@ -2,17 +2,22 @@ from nullptr.missions.survey import SurveyMission from nullptr.missions.mine import MiningMission from nullptr.missions.haul import HaulMission from nullptr.missions.travel import TravelMission +from nullptr.missions.probe import ProbeMission - -def create_mission(mtype, ship, store, api): +def get_mission_class( mtype): types = { 'survey': SurveyMission, 'mine': MiningMission, 'haul': HaulMission, - 'travel': TravelMission + 'travel': TravelMission, + 'probe': ProbeMission } if mtype not in types: - logging.warning(f'invalid mission type {mtype}') - return - m = types[mtype](ship, store, api) + raise ValueError(f'invalid mission type {mtype}') + return types[mtype] + +def create_mission(mtype, ship, store, api): + typ = get_mission_class(mtype) + m = typ(ship, store, api) return m + diff --git a/nullptr/missions/base.py b/nullptr/missions/base.py index 6515f54..ef36be2 100644 --- a/nullptr/missions/base.py +++ b/nullptr/missions/base.py @@ -25,6 +25,8 @@ class MissionParam: return str(val) elif self.cls == int: return int(val) + elif self.cls == list: + return [i.strip() for i in val.split(',')] elif issubclass(self.cls, Base): data = store.get(self.cls, val) if data is None: diff --git a/nullptr/missions/probe.py b/nullptr/missions/probe.py new file mode 100644 index 0000000..586fb49 --- /dev/null +++ b/nullptr/missions/probe.py @@ -0,0 +1,33 @@ +from nullptr.missions.base import BaseMission, MissionParam +from nullptr.models.waypoint import Waypoint + +class ProbeMission(BaseMission): + def start_state(self): + return 'next-hop' + + @classmethod + def params(cls): + return { + 'hops': MissionParam(list, True), + 'next-hop': MissionParam(int, True, 0) + } + + def steps(self): + return { + 'next-hop': (self.step_next_hop, 'travel-to'), + **self.travel_steps('to', 'site', 'market'), + 'market': (self.step_market, 'next-hop'), + + } + + def step_market(self): + loc = self.ship.location() + self.api.marketplace(loc) + + def step_next_hop(self): + hops = self.st('hops') + next_hop = self.st('next-hop') + hop = hops[next_hop] + self.sts('site', hop) + self.sts('next-hop', (next_hop+1) % len(hops)) +