diff --git a/nullptr/analyzer.py b/nullptr/analyzer.py index cc63255..4963456 100644 --- a/nullptr/analyzer.py +++ b/nullptr/analyzer.py @@ -153,8 +153,7 @@ class Analyzer: def prices(self, system): prices = {} for m in self.store.all_members(system, Marketplace): - for p in m.prices.values(): - r = p['symbol'] + for r, p in m.prices.items(): if not r in prices: prices[r] = [] prices[r].append({ @@ -168,7 +167,7 @@ class Analyzer: prices = self.prices(system) occupied_resources = set() for s in self.store.all('Ship'): - if s.mission != 'haul': + if s.mission != 'trade': continue occupied_resources.add(s.mission_state['resource']) best = None diff --git a/nullptr/central_command.py b/nullptr/central_command.py index 46f2f46..b211312 100644 --- a/nullptr/central_command.py +++ b/nullptr/central_command.py @@ -1,7 +1,7 @@ from nullptr.store import Store from nullptr.models.ship import Ship from nullptr.missions import create_mission, get_mission_class -from random import choice +from random import choice, randrange from time import sleep from threading import Thread from nullptr.atlas_builder import AtlasBuilder @@ -106,10 +106,23 @@ class CentralCommand: def assign_mission(self, s): - if s.role == 'hauler': - self.assign_haul(s) + if s.role == 'trader': + self.assign_trade(s) + elif s.role == 'probe': + self.assign_probe(s) - def assign_haul(self, s): + 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)) + print(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") @@ -117,7 +130,7 @@ class CentralCommand: self.smipa(s, 'seconds', 600) return print(f'assigning {s} to deliver {t.resource} from {t.source} to {t.dest} at a margin of {t.margin}') - self.init_mission(s, 'haul') + self.init_mission(s, 'trade') self.smipa(s, 'resource', t.resource) self.smipa(s, 'site', t.source) self.smipa(s, 'dest', t.dest) diff --git a/nullptr/commander.py b/nullptr/commander.py index 8c8b9bf..3ae6718 100644 --- a/nullptr/commander.py +++ b/nullptr/commander.py @@ -20,6 +20,7 @@ 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.cred_file = os.path.join(data_dir, 'creds.txt') self.hist_file = hist_file if os.path.isfile(hist_file): readline.read_history_file(hist_file) @@ -107,7 +108,7 @@ class Commander(CommandLine): if len(faction) > 50: self.agent.token = faction else: - api.register(faction.upper().strip()) + self.do_register(faction) print('=== agent:') print(agent) print('=== ships') @@ -126,6 +127,10 @@ class Commander(CommandLine): def do_register(self, faction): self.api.register(faction.upper()) + with open(self.cred_file, 'w') as f: + f.write(self.api.agent.symbol) + f.write('\n') + f.write(self.api.agent.token) pprint(self.api.agent) ######## Fleet ######### @@ -306,7 +311,7 @@ class Commander(CommandLine): pprint(self.ship.mission_state) def do_role(self, role): - roles = [None, 'hauler'] + roles = [None, 'trader', 'probe'] if not self.has_ship(): return if role == 'none': role = None @@ -407,7 +412,7 @@ class Commander(CommandLine): return _, m, _, _ = m[0] site = self.store.get(Waypoint, m.symbol) - self.centcom.init_mission(self.ship, 'haul') + self.centcom.init_mission(self.ship, 'trade') 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) @@ -432,18 +437,6 @@ class Commander(CommandLine): 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) diff --git a/nullptr/missions/__init__.py b/nullptr/missions/__init__.py index adb5c87..6d181aa 100644 --- a/nullptr/missions/__init__.py +++ b/nullptr/missions/__init__.py @@ -1,6 +1,6 @@ from nullptr.missions.survey import SurveyMission from nullptr.missions.mine import MiningMission -from nullptr.missions.haul import HaulMission +from nullptr.missions.trade import TradeMission from nullptr.missions.travel import TravelMission from nullptr.missions.probe import ProbeMission from nullptr.missions.idle import IdleMission @@ -10,7 +10,7 @@ def get_mission_class( mtype): types = { 'survey': SurveyMission, 'mine': MiningMission, - 'haul': HaulMission, + 'trade': TradeMission, 'travel': TravelMission, 'probe': ProbeMission, 'idle': IdleMission diff --git a/nullptr/missions/mine.py b/nullptr/missions/mine.py index ca128fc..6ebcce9 100644 --- a/nullptr/missions/mine.py +++ b/nullptr/missions/mine.py @@ -49,7 +49,7 @@ class MiningMission(BaseMission): site = self.rst(Waypoint,'site') # todo optimize for s in self.store.all(Survey): - if resource in s.deposits and site.symbol == s.waypoint(): + if resource in s.deposits and site.symbol == s.waypoint: return s return None diff --git a/nullptr/missions/haul.py b/nullptr/missions/trade.py similarity index 96% rename from nullptr/missions/haul.py rename to nullptr/missions/trade.py index 1b2bdd4..812406b 100644 --- a/nullptr/missions/haul.py +++ b/nullptr/missions/trade.py @@ -2,7 +2,7 @@ from nullptr.missions.base import BaseMission, MissionParam from nullptr.models.waypoint import Waypoint from nullptr.models.survey import Survey from nullptr.models.contract import Contract -class HaulMission(BaseMission): +class TradeMission(BaseMission): def start_state(self): return 'travel-to' diff --git a/nullptr/models/base.py b/nullptr/models/base.py index aec041c..9276670 100644 --- a/nullptr/models/base.py +++ b/nullptr/models/base.py @@ -14,6 +14,9 @@ class Reference: def resolve(self): return self.store.get(self.typ, self.symbol) + + def f(self, detail): + return f'{self.symbol}.{self.typ.ext()}' def __repr__(self): return f'*REF*{self.symbol}.{self.typ.ext()}' @@ -46,6 +49,9 @@ class Base: def define(self): pass + def created(self): + pass + def __hash__(self): return hash((str(type(self)), self.symbol)) @@ -84,12 +90,16 @@ class Base: def __setattr__(self, name, value): if not name.startswith('_') and not self._disable_dirty: - self.store.dirty(self) + self.dirty() if issubclass(type(value), Base): value = Reference.create(value) super().__setattr__(name, value) def __getattribute__(self, nm): + if nm == 'system': + return self.get_system() + if nm == 'waypoint': + return self.get_waypoint() val = super().__getattribute__(nm) if type(val) == Reference: val = val.resolve() diff --git a/nullptr/models/jumpgate.py b/nullptr/models/jumpgate.py index e525029..9dd16b4 100644 --- a/nullptr/models/jumpgate.py +++ b/nullptr/models/jumpgate.py @@ -5,7 +5,6 @@ from dataclasses import field class Jumpgate(Base): def define(self): self.connections: list = [] - self.system = self.get_system() def update(self, d): getter = self.store.getter(Waypoint, create=True) diff --git a/nullptr/models/marketplace.py b/nullptr/models/marketplace.py index f8f5f58..ee83a76 100644 --- a/nullptr/models/marketplace.py +++ b/nullptr/models/marketplace.py @@ -1,5 +1,5 @@ -from .base import Base +from .base import Base, Reference from time import time from nullptr.util import * from dataclasses import field, dataclass @@ -32,12 +32,9 @@ class Marketplace(Base): self.exchange:list = [] self.prices:dict = {} self.last_prices:int = 0 - self.set_waypoint() - self.system = self.get_system() - def set_waypoint(self): - waypoint = self.store.get(Waypoint, self.symbol, create=True) - self.waypoint = waypoint + def get_waypoint(self): + return self.store.get('Waypoint', self.symbol, create=True) def is_fuel(self): return self.imports + self.exports + self.exchange == ['FUEL'] @@ -51,7 +48,7 @@ class Marketplace(Base): e = self.prices[symbol] = MarketEntry() buy = mg(g, 'purchasePrice') sell = mg(g, 'sellPrice') - volume = mg(g, 'tradeVolumes') + volume = mg(g, 'tradeVolume') supply = SUPPLY.index(mg(g, 'supply')) activity = ACTIVITY.index(sg(g, 'activity','STRONG')) e.add(buy, sell, volume, supply, activity) @@ -68,12 +65,12 @@ class Marketplace(Base): def buy_price(self, resource): if resource not in self.prices: return None - return self.prices[resource]['buy'] + return self.prices[resource].buy def volume(self, resource): if resource not in self.prices: return None - return self.prices[resource]['volume'] + return self.prices[resource].volume def sellable_items(self, resources): return [r for r in resources if r in self.prices] diff --git a/nullptr/models/survey.py b/nullptr/models/survey.py index 4b248a5..77b7667 100644 --- a/nullptr/models/survey.py +++ b/nullptr/models/survey.py @@ -28,7 +28,7 @@ class Survey(Base): def api_dict(self): return { 'signature': self.symbol, - 'symbol': self.waypoint(), + 'symbol': self.waypoint, 'deposits': [{'symbol': d} for d in self.deposits], 'expiration': self.expires_str, 'size': size_names[self.size] diff --git a/nullptr/models/waypoint.py b/nullptr/models/waypoint.py index 041b218..d42bb10 100644 --- a/nullptr/models/waypoint.py +++ b/nullptr/models/waypoint.py @@ -12,7 +12,6 @@ class Waypoint(Base): self.traits:list = [] self.faction:str = '' self.is_under_construction:bool = False - self.system = self.get_system() self.uncharted = True @@ -24,6 +23,9 @@ class Waypoint(Base): self.seta('is_under_construction', d, 'isUnderConstruction') self.setlst('traits', d, 'traits', 'symbol') self.uncharted = 'UNCHARTED' in self.traits + + def created(self): + self.get_system() def distance(self, other): return int(sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)) diff --git a/nullptr/store.py b/nullptr/store.py index 6a24fd2..d2dee16 100644 --- a/nullptr/store.py +++ b/nullptr/store.py @@ -72,6 +72,7 @@ class ChunkHeader: class Store: def __init__(self, data_file, verbose=False): self.init_models() + self.data_file = data_file self.data_dir = os.path.dirname(data_file) self.fil = open_file(data_file) self.data = {m: {} for m in self.models} @@ -90,6 +91,9 @@ class Store: return print(m) + def f(self, detail): + return f'Store {self.data_file}' + def close(self): self.flush() self.fil.close() @@ -112,6 +116,9 @@ class Store: buf = BytesIO(data) p = StoreUnpickler(buf, self) obj = p.load() + x = self.get(type(obj), obj.symbol) + if x is not None and x in self.dirty_objects: + self.dirty_objects.remove(obj) obj._file_offset = offset self.hold(obj) @@ -138,6 +145,8 @@ class Store: offset = self.fil.tell() dur = time() - start_time + # just in case any temp objects were created + self.dirty_objects = set() self.p(f'Loaded {cnt} objects in {dur:.2f} seconds') self.p(f'Fragmented space: {free} / {total} bytes') @@ -210,14 +219,15 @@ class Store: symbol = obj.symbol obj.store = self self.data[typ][symbol] = obj - if hasattr(obj, 'system'): - system_str = obj.get_system().symbol + if type(obj).__name__ in ['Waypoint','Marketplace', 'Jumpgate', 'Survey']: + system_str = obj.system.symbol if system_str not in self.system_members: self.system_members[system_str] = set() self.system_members[system_str].add(obj) def create(self, typ, symbol): obj = typ(symbol, self) + obj.created() self.hold(obj) self.dirty(obj) return obj