diff --git a/nullptr/analyzer.py b/nullptr/analyzer.py index ff18f83..c0cc9f5 100644 --- a/nullptr/analyzer.py +++ b/nullptr/analyzer.py @@ -32,11 +32,23 @@ class Analyzer: resources = m.imports if sellbuy == 'sell' else m.exports if resource in resources: yield m + + def find_closest_market(self, resource, sellbuy, location): + market = None + distance = None + for m in self.find_markets(resource, sellbuy): + system = self.store.get(System, m.system()) + p = self.find_path(location, system) + if p is None: continue + if distance is None or len(p) < distance: + market = m + distance = len(p) + return market + def get_jumpgate(self, system): gates = self.store.all_members(system, Jumpgate) return next(gates, None) - def find_path(self, orig, to, depth=100, seen=None): if depth < 1: return None diff --git a/nullptr/api.py b/nullptr/api.py index c66a5d3..8fd56c6 100644 --- a/nullptr/api.py +++ b/nullptr/api.py @@ -221,8 +221,10 @@ class Api: return ship def jump(self, ship, system): + if type(system) == System: + system = system.symbol data = { - "systemSymbol": system.symbol + "systemSymbol": system } data = self.request('post', f'my/ships/{ship}/jump', data) if 'nav' in data: diff --git a/nullptr/commander.py b/nullptr/commander.py index e9eb09c..58a18dd 100644 --- a/nullptr/commander.py +++ b/nullptr/commander.py @@ -99,9 +99,8 @@ class Commander(CommandLine): self.set_mission(arg) self.print_mission() - def do_mset(self, args): + def do_mset(self, nm, val): if not self.has_ship(): return - nm, val = args.split(' ') self.centcom.set_mission_param(self.ship, nm, val) def active_contract(self): @@ -124,6 +123,23 @@ class Commander(CommandLine): self.centcom.set_mission_param(self.ship, 'destination', destination) self.centcom.set_mission_param(self.ship, 'contract', contract.symbol) self.print_mission() + + def do_chaul(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_market(resource, 'buy', destination) + site = self.store.get(Waypoint, m.symbol) + self.set_mission('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, 'destination', destination) + self.centcom.set_mission_param(self.ship, 'contract', contract.symbol) + self.print_mission() def do_register(self, faction): self.api.register(faction.upper()) diff --git a/nullptr/mission.py b/nullptr/mission.py index 9574ab2..c4cab2e 100644 --- a/nullptr/mission.py +++ b/nullptr/mission.py @@ -2,8 +2,10 @@ from nullptr.store import Store from nullptr.models.base import Base from nullptr.models.waypoint import Waypoint from nullptr.models.contract import Contract +from nullptr.models.system import System from nullptr.models.survey import Survey from nullptr.models.ship import Ship +from nullptr.analyzer import Analyzer from time import time import logging from nullptr.util import * @@ -42,6 +44,7 @@ class Mission: self.store = store self.api = api self.next_step = 0 + self.analyzer = Analyzer(self.store) def sts(self, nm, v): if issubclass(type(v), Base): @@ -121,8 +124,109 @@ class Mission: self.status(next_step[result]) print(f'{self.ship} {status} -> {self.status()}') +class BaseMission(Mission): + def step_go_dest(self): + destination = self.rst(Waypoint, 'destination') + if self.ship.location() == destination: + return + self.api.navigate(self.ship, destination) + self.next_step = self.ship.arrival + + def step_go_site(self): + site = self.rst(Waypoint,'site') + if self.ship.location() == site: + return + self.api.navigate(self.ship, site) + self.next_step = self.ship.arrival + + def step_unload(self): + contract = self.rst(Contract, 'contract') + delivery = self.st('delivery') + if delivery == 'sell': + return self.step_sell(False) + typs = self.ship.deliverable_cargo(contract) + if len(typs) == 0: + return 'done' + self.api.deliver(self.ship, typs[0], contract) + if len(typs) == 1: + return 'done' + else: + return 'more' -class MiningMission(Mission): + def step_sell(self, except_resource=True): + target = self.st('resource') + market = self.store.get('Marketplace', self.ship.location_str) + sellables = market.sellable_items(self.ship.cargo.keys()) + if target in sellables and except_resource: + sellables.remove(target) + if len(sellables) == 0: + return 'done' + self.api.sell(self.ship, sellables[0]) + if len(sellables) == 1: + return 'done' + else: + return 'more' + + def step_load(self): + cargo_space = self.ship.cargo_capacity - self.ship.cargo_units + resource = self.st('resource') + self.api.buy(self.ship, resource, cargo_space) + + def travel(self, nm): + dest = self.rst(Waypoint, nm) + traject = self.st('traject') + loc = self.ship.location() + if dest == loc: + self.sts('traject', None) + return 'done' + elif traject is None: + print(f'calculating path to {dest}') + traject = self.calculate_traject(dest) + hop = traject.pop(0) + if len(hop.split('-')) == 3: + self.api.navigate(self.ship, hop) + self.next_step = self.ship.arrival + else: + self.api.jump(self.ship, hop) + self.next_step = self.ship.cooldown + + self.sts('traject', traject) + return 'more' + + def calculate_traject(self, dest): + loc = self.ship.location() + loc_sys = self.store.get(System, loc.system()) + loc_jg = self.analyzer.get_jumpgate(loc_sys) + dest_sys = self.store.get(System, dest.system()) + dest_jg = self.analyzer.get_jumpgate(dest_sys) + path = self.analyzer.find_path(loc_sys, dest_sys) + result = [] + print(loc.symbol, loc_jg.symbol) + if loc.symbol != loc_jg.symbol: + result.append(loc_jg.symbol) + result += [s.symbol for s in path[1:]] + if dest_jg.symbol != dest.symbol: + result.append(dest.symbol) + print(result) + return result + + def step_travel_site(self): + return self.travel('site') + + def step_travel_dest(self): + return self.travel('destination') + + def step_dock(self): + self.api.dock(self.ship) + + def step_refuel(self): + if self.ship.fuel_current < 100: + self.api.refuel(self.ship) + + def step_orbit(self): + self.api.orbit(self.ship) + +class MiningMission(BaseMission): @classmethod def params(cls): return { @@ -185,48 +289,7 @@ class MiningMission(Mission): return 'more' else: return 'done' - - def step_sell(self, except_resource=True): - target = self.st('resource') - market = self.store.get('Marketplace', self.ship.location_str) - sellables = market.sellable_items(self.ship.cargo.keys()) - if target in sellables and except_resource: - sellables.remove(target) - if len(sellables) == 0: - return 'done' - self.api.sell(self.ship, sellables[0]) - if len(sellables) == 1: - return 'done' - else: - return 'more' - - def step_go_dest(self): - destination = self.rst(Waypoint, 'destination') - if self.ship.location() == destination: - return - self.api.navigate(self.ship, destination) - self.next_step = self.ship.arrival - - def step_dock(self): - self.api.dock(self.ship) - - def step_unload(self): - contract = self.rst(Contract, 'contract') - delivery = self.st('delivery') - if delivery == 'sell': - return self.step_sell(False) - typs = self.ship.deliverable_cargo(contract) - if len(typs) == 0: - return 'done' - self.api.deliver(self.ship, typs[0], contract) - if len(typs) == 1: - return 'done' - else: - return 'more' - def step_refuel(self): - self.api.refuel(self.ship) - def step_dispose(self): contract = self.rst(Contract, 'contract') typs = self.ship.nondeliverable_cargo(contract) @@ -239,22 +302,10 @@ class MiningMission(Mission): else: return 'done' - - def step_orbit(self): - self.api.orbit(self.ship) - - def step_go_site(self): - site = self.rst(Waypoint,'site') - if self.ship.location() == site: - return - self.api.navigate(self.ship, site) - self.next_step = self.ship.arrival - -class SurveyMission(Mission): +class SurveyMission(BaseMission): def start_state(self): return 'survey' - def steps(self): return { 'survey': (self.step_survey, 'survey') @@ -265,10 +316,45 @@ class SurveyMission(Mission): #pprint(result, 2) self.next_step = self.ship.cooldown +class HaulMission(BaseMission): + def start_state(self): + return 'orbit2' + + @classmethod + def params(cls): + return { + 'site': MissionParam(Waypoint, True), + 'resource': MissionParam(str, True), + 'destination': MissionParam(Waypoint, True), + 'delivery': MissionParam(str, True, 'deliver'), + 'contract': MissionParam(Contract, False) + } + + def steps(self): + return { + 'travel': (self.step_travel_site, { + 'more': 'travel', + 'done': 'dock' + }), + 'dock': (self.step_dock, 'load'), + 'load': (self.step_load, 'orbit'), + 'orbit': (self.step_orbit, 'travel_back'), + 'travel_back': (self.step_travel_dest, { + 'more': 'travel_back', + 'done': 'dock2' + }), + 'dock2': (self.step_dock, 'unload'), + 'unload': (self.step_unload, 'refuel'), + 'refuel': (self.step_refuel, 'orbit2'), + 'orbit2': (self.step_orbit, 'travel') + } + + def create_mission(mtype, ship, store, api): types = { 'survey': SurveyMission, - 'mine': MiningMission + 'mine': MiningMission, + 'haul': HaulMission } if mtype not in types: logging.warning(f'invalid mission type {mtype}') diff --git a/nullptr/models/base.py b/nullptr/models/base.py index dad656a..5f80271 100644 --- a/nullptr/models/base.py +++ b/nullptr/models/base.py @@ -7,11 +7,12 @@ class Base: store: object def __init__(self, symbol, store): - self.disable_dirty = False + self.disable_dirty = True self.store = store self.symbol = symbol self.define() - + self.disable_dirty = False + def define(self): pass diff --git a/nullptr/store.py b/nullptr/store.py index 4e411fe..91ef91d 100644 --- a/nullptr/store.py +++ b/nullptr/store.py @@ -124,6 +124,7 @@ class Store: if system not in self.system_members: return + print('typ', typ) for m in self.system_members[system]: if typ is None or type(m) == typ: yield m