From 5d47efdbda29ddd7dd39a69062c2b381b682abbc Mon Sep 17 00:00:00 2001 From: Richard Date: Sat, 27 Jan 2024 15:05:33 +0100 Subject: [PATCH] mission step priorities, fixed the store again --- nullptr/analyzer.py | 12 +++++++++--- nullptr/api.py | 7 +++++-- nullptr/central_command.py | 7 ++++++- nullptr/commander.py | 26 +++++++++++++++++++++----- nullptr/missions/base.py | 21 ++++++++++++--------- nullptr/missions/haul.py | 8 +++++--- nullptr/models/marketplace.py | 3 +++ nullptr/models/ship.py | 3 +++ nullptr/store.py | 24 +++++++++++------------- nullptr/util.py | 3 +++ 10 files changed, 78 insertions(+), 36 deletions(-) diff --git a/nullptr/analyzer.py b/nullptr/analyzer.py index 5941372..8fce5a1 100644 --- a/nullptr/analyzer.py +++ b/nullptr/analyzer.py @@ -167,12 +167,17 @@ class Analyzer: return prices def find_trade(self, system): + max_traders = 3 prices = self.prices(system) - occupied_routes = set() + occupied_routes = dict() for s in self.store.all('Ship'): if s.mission != 'trade': continue - occupied_routes.add((s.mission_state['site'], s.mission_state['dest'])) + k = (s.mission_state['site'], s.mission_state['dest']) + if k in occupied_routes: + occupied_routes[k] += 1 + else: + occupied_routes[k] = 1 best = None for resource, markets in prices.items(): source = sorted(markets, key=lambda x: x['buy'])[0] @@ -180,7 +185,8 @@ class Analyzer: swp = source['wp'] dwp = dest['wp'] margin = dest['sell'] -source['buy'] - if (swp.symbol,dwp.symbol) in occupied_routes: + k = (swp.symbol,dwp.symbol) + if k in occupied_routes and occupied_routes[k] > max_traders: continue dist = swp.distance(dwp) dist = max(dist, 0.0001) diff --git a/nullptr/api.py b/nullptr/api.py index ba589de..b9fec1e 100644 --- a/nullptr/api.py +++ b/nullptr/api.py @@ -122,6 +122,9 @@ class Api: ######## Fleet ######### def list_ships(self): data = self.request('get', 'my/ships') + tp = total_pages(self.last_meta) + for p in range(1, tp): + data += self.request('get', 'my/ships', params={'page': p+1}) return self.store.update_list(Ship, data) def refuel(self, ship, from_cargo=False): @@ -244,8 +247,8 @@ class Api: else: raise e ship.update(data) - amt = mg(data, 'extraction.yield.units') - rec = mg(data, 'extraction.yield.symbol') + amt = sg(data, 'extraction.yield.units', 0) + rec = sg(data, 'extraction.yield.symbol', 'nothing') ship.log(f"extracted {amt} {rec}") ship.location.extracted += amt return data diff --git a/nullptr/central_command.py b/nullptr/central_command.py index c5742dd..abd71b7 100644 --- a/nullptr/central_command.py +++ b/nullptr/central_command.py @@ -23,9 +23,14 @@ class CentralCommand: def get_ready_missions(self): result = [] + prio = 1 for ship, mission in self.missions.items(): - if mission.is_ready(): + p = mission.is_ready() + if p == prio: result.append(ship) + elif p > prio: + prio = p + result = [ship] return result def single_step(self, ship): diff --git a/nullptr/commander.py b/nullptr/commander.py index 3e1e993..3003bc8 100644 --- a/nullptr/commander.py +++ b/nullptr/commander.py @@ -24,7 +24,7 @@ class Commander(CommandLine): self.hist_file = hist_file if os.path.isfile(hist_file): readline.read_history_file(hist_file) - self.store = Store(store_file, False) + self.store = Store(store_file, True) self.agent = self.select_agent() self.api = Api(self.store, self.agent) self.centcom = CentralCommand(self.store, self.api) @@ -141,6 +141,7 @@ class Commander(CommandLine): print(agent) print('=== ships') self.do_ships('r') + self.do_create_crews() print('=== contracts') self.do_contracts('r') ship = self.store.get(Ship, symbol.upper() + '-2') @@ -161,6 +162,14 @@ class Commander(CommandLine): f.write(self.api.agent.token) pprint(self.api.agent) + def do_reset(self, really): + if really != 'yes': + print('really? type: reset yes') + self.api.list_ships() + for s in self.store.all('Ship'): + self.dump(s, 'all') + self.centcom.init_mission(s, 'none') + ######## Fleet ######### def do_info(self, arg=''): if arg.startswith('r'): @@ -288,10 +297,17 @@ class Commander(CommandLine): ship = self.has_ship() self.api.sell(ship, resource.upper(), amt) self.do_cargo() - + + def dump(self, ship, resource): + if resource == 'all': + for r in ship.cargo.keys(): + self.api.jettison(ship, r) + else: + self.api.jettison(ship, resource.upper()) + def do_dump(self, resource): ship = self.has_ship() - self.api.jettison(ship, resource.upper()) + self.dump(ship, resource) self.do_cargo() def do_transfer(self, resource, dship, amount=None): @@ -501,11 +517,11 @@ class Commander(CommandLine): symbol, ext = oid.split('.') symbol = symbol.upper() if not ext in self.store.extensions: - print('unknown extension') + raise CommandError('unknown extension') typ = self.store.extensions[ext] obj = self.store.get(typ, symbol) if obj is None: - print('object not found') + raise CommandError('object not found') pprint(obj.__getstate__()) print('=== store ===') h = self.store.get_header(obj) diff --git a/nullptr/missions/base.py b/nullptr/missions/base.py index aab52c2..d654395 100644 --- a/nullptr/missions/base.py +++ b/nullptr/missions/base.py @@ -11,6 +11,7 @@ from functools import partial import logging from nullptr.util import * + class MissionError(Exception): pass @@ -113,23 +114,24 @@ class Mission: def step_done(self): self.ship.log(f'mission {type(self).__name__} finished with balance {self.balance()}', 3) - def is_waiting(self): + def get_prio(self): if self.next_step > time() or self.ship.cooldown > time() or self.ship.arrival > time(): - return True + return 0 if self.wait_for is not None: - if self.wait_for(): + p = int(self.wait_for()) + if p > 0: self.wait_for = None - return False - else: - return True - return False + return p + return 3 def is_finished(self): return self.status() in ['done','error'] def is_ready(self): - return not self.is_waiting() and not self.is_finished() + if self.is_finished(): + return 0 + return self.get_prio() def step(self): steps = self.steps() @@ -147,7 +149,8 @@ class Mission: try: result = handler() except Exception as e: - logging.error(e, exc_info=True) + self.ship.log(fmtex(e)) + self.ship.log(self.api.last_result) self.status('error') return if type(next_step) == str: diff --git a/nullptr/missions/haul.py b/nullptr/missions/haul.py index 19413fc..05d0f3a 100644 --- a/nullptr/missions/haul.py +++ b/nullptr/missions/haul.py @@ -17,14 +17,16 @@ class HaulMission(BaseMission): continue if s.mission_status != 'load': continue - return False - return True + return 0 + return 5 def step_load(self): pass def cargo_full(self): - return self.ship.cargo_space() == 0 + if self.ship.cargo_space() == 0: + return 5 + return 0 @classmethod def params(cls): diff --git a/nullptr/models/marketplace.py b/nullptr/models/marketplace.py index 56bdc93..88dbdc7 100644 --- a/nullptr/models/marketplace.py +++ b/nullptr/models/marketplace.py @@ -16,6 +16,9 @@ class MarketEntry: activity: int history: List[Tuple[int, int, int, int, int, int]] = [] + def f(self, detail=1): + return f'b: {self.buy} s:{self.sell} hist: {len(self.history)}' + def add(self, buy, sell, volume, supply, activity): self.buy = buy self.sell = sell diff --git a/nullptr/models/ship.py b/nullptr/models/ship.py index a7fafb9..89ea5de 100644 --- a/nullptr/models/ship.py +++ b/nullptr/models/ship.py @@ -26,6 +26,9 @@ class Ship(Base): self._log_level = 5 def log(self, m, l=3): + if m is None: return + if type(m) != str: + m = pretty(m) if self._log_file is None: fn = os.path.join(self.store.data_dir, f'{self.symbol}.{self.ext()}.log') self._log_file = open(fn, 'a') diff --git a/nullptr/store.py b/nullptr/store.py index 7d9d49e..f873a49 100644 --- a/nullptr/store.py +++ b/nullptr/store.py @@ -131,7 +131,7 @@ class Store: self.fil.seek(0) offset = 0 while (hdr := ChunkHeader.parse(self.fil)): - self.p(hdr) + # self.p(hdr) total += hdr.size if not hdr.in_use: # print(f"skip {hdr.size} {self.fil.tell()}") @@ -296,21 +296,18 @@ class Store: self.system_members[system].remove(m) def cleanup(self): - if time() < self.last_cleanup + self.cleanup_interval: - return self.last_cleanup = time() start_time = time() expired = list() for t in self.data: - for o in self.all(t): + for o in self.data[t].values(): if o.is_expired(): expired.append(o) for o in expired: - self.purge(obj) - - del self.data[type(o)][o.symbol] + self.purge(o) + dur = time() - start_time - self.p(f'cleaned {len(expired)} in {dur:.03f} seconds') + # self.p(f'cleaned {len(expired)} in {dur:.03f} seconds') def flush(self): self.cleanup() @@ -318,16 +315,17 @@ class Store: start_time = time() for obj in copy(self.dirty_objects): it += 1 - if obj.is_expired(): - self.purge(obj) - else: - self.store(obj) + if obj.symbol not in self.data[type(obj)] or self.data[type(obj)][obj.symbol] != obj: + print(f"Dirty object not in data {type(obj)} {obj.symbol} {obj}") + continue + self.store(obj) self.fil.flush() self.dirty_objects = set() dur = time() - start_time - self.p(f'flush done {it} items {dur:.2f}') + #self.p(f'flush done {it} items {dur:.2f}') def defrag(self): + self.flush() nm = self.fil.name self.fil.close() bakfile = nm+'.bak' diff --git a/nullptr/util.py b/nullptr/util.py index c63fbca..e7ea553 100644 --- a/nullptr/util.py +++ b/nullptr/util.py @@ -2,6 +2,7 @@ from datetime import datetime from math import ceil import os from os.path import isfile, dirname +import traceback def open_file(fn): d = dirname(fn) @@ -82,3 +83,5 @@ def parse_timestamp(ts): def render_timestamp(ts): return datetime.utcfromtimestamp(ts).isoformat() +def fmtex(e): + return ''.join(traceback.TracebackException.from_exception(e).format())