store fixes and probe role

This commit is contained in:
Richard 2024-01-13 21:42:49 +01:00
parent 188ef320cc
commit 7d92a45d12
12 changed files with 65 additions and 42 deletions

View File

@ -153,8 +153,7 @@ class Analyzer:
def prices(self, system): def prices(self, system):
prices = {} prices = {}
for m in self.store.all_members(system, Marketplace): for m in self.store.all_members(system, Marketplace):
for p in m.prices.values(): for r, p in m.prices.items():
r = p['symbol']
if not r in prices: if not r in prices:
prices[r] = [] prices[r] = []
prices[r].append({ prices[r].append({
@ -168,7 +167,7 @@ class Analyzer:
prices = self.prices(system) prices = self.prices(system)
occupied_resources = set() occupied_resources = set()
for s in self.store.all('Ship'): for s in self.store.all('Ship'):
if s.mission != 'haul': if s.mission != 'trade':
continue continue
occupied_resources.add(s.mission_state['resource']) occupied_resources.add(s.mission_state['resource'])
best = None best = None

View File

@ -1,7 +1,7 @@
from nullptr.store import Store from nullptr.store import Store
from nullptr.models.ship import Ship from nullptr.models.ship import Ship
from nullptr.missions import create_mission, get_mission_class from nullptr.missions import create_mission, get_mission_class
from random import choice from random import choice, randrange
from time import sleep from time import sleep
from threading import Thread from threading import Thread
from nullptr.atlas_builder import AtlasBuilder from nullptr.atlas_builder import AtlasBuilder
@ -106,10 +106,23 @@ class CentralCommand:
def assign_mission(self, s): def assign_mission(self, s):
if s.role == 'hauler': if s.role == 'trader':
self.assign_haul(s) 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) t = self.analyzer.find_trade(s.location.system)
if t is None: if t is None:
print(f"No trade for {s} found. Idling") print(f"No trade for {s} found. Idling")
@ -117,7 +130,7 @@ class CentralCommand:
self.smipa(s, 'seconds', 600) self.smipa(s, 'seconds', 600)
return return
print(f'assigning {s} to deliver {t.resource} from {t.source} to {t.dest} at a margin of {t.margin}') 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, 'resource', t.resource)
self.smipa(s, 'site', t.source) self.smipa(s, 'site', t.source)
self.smipa(s, 'dest', t.dest) self.smipa(s, 'dest', t.dest)

View File

@ -20,6 +20,7 @@ class Commander(CommandLine):
def __init__(self, data_dir='data'): def __init__(self, data_dir='data'):
store_file = os.path.join(data_dir, 'store.npt') store_file = os.path.join(data_dir, 'store.npt')
hist_file = os.path.join(data_dir, 'cmd.hst') hist_file = os.path.join(data_dir, 'cmd.hst')
self.cred_file = os.path.join(data_dir, 'creds.txt')
self.hist_file = hist_file self.hist_file = hist_file
if os.path.isfile(hist_file): if os.path.isfile(hist_file):
readline.read_history_file(hist_file) readline.read_history_file(hist_file)
@ -107,7 +108,7 @@ class Commander(CommandLine):
if len(faction) > 50: if len(faction) > 50:
self.agent.token = faction self.agent.token = faction
else: else:
api.register(faction.upper().strip()) self.do_register(faction)
print('=== agent:') print('=== agent:')
print(agent) print(agent)
print('=== ships') print('=== ships')
@ -126,6 +127,10 @@ class Commander(CommandLine):
def do_register(self, faction): def do_register(self, faction):
self.api.register(faction.upper()) 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) pprint(self.api.agent)
######## Fleet ######### ######## Fleet #########
@ -306,7 +311,7 @@ class Commander(CommandLine):
pprint(self.ship.mission_state) pprint(self.ship.mission_state)
def do_role(self, role): def do_role(self, role):
roles = [None, 'hauler'] roles = [None, 'trader', 'probe']
if not self.has_ship(): return if not self.has_ship(): return
if role == 'none': if role == 'none':
role = None role = None
@ -407,7 +412,7 @@ class Commander(CommandLine):
return return
_, m, _, _ = m[0] _, m, _, _ = m[0]
site = self.store.get(Waypoint, m.symbol) 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, 'site', site)
self.centcom.set_mission_param(self.ship, 'resource', resource) 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, 'dest', destination)
@ -432,18 +437,6 @@ class Commander(CommandLine):
self.centcom.set_mission_param(self.ship, 'hops', markets) self.centcom.set_mission_param(self.ship, 'hops', markets)
self.print_mission() 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 ######### ######## Travel #########
def do_travel(self, dest): def do_travel(self, dest):
dest = self.resolve('Waypoint', dest) dest = self.resolve('Waypoint', dest)

View File

@ -1,6 +1,6 @@
from nullptr.missions.survey import SurveyMission from nullptr.missions.survey import SurveyMission
from nullptr.missions.mine import MiningMission 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.travel import TravelMission
from nullptr.missions.probe import ProbeMission from nullptr.missions.probe import ProbeMission
from nullptr.missions.idle import IdleMission from nullptr.missions.idle import IdleMission
@ -10,7 +10,7 @@ def get_mission_class( mtype):
types = { types = {
'survey': SurveyMission, 'survey': SurveyMission,
'mine': MiningMission, 'mine': MiningMission,
'haul': HaulMission, 'trade': TradeMission,
'travel': TravelMission, 'travel': TravelMission,
'probe': ProbeMission, 'probe': ProbeMission,
'idle': IdleMission 'idle': IdleMission

View File

@ -49,7 +49,7 @@ class MiningMission(BaseMission):
site = self.rst(Waypoint,'site') site = self.rst(Waypoint,'site')
# todo optimize # todo optimize
for s in self.store.all(Survey): 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 s
return None return None

View File

@ -2,7 +2,7 @@ from nullptr.missions.base import BaseMission, MissionParam
from nullptr.models.waypoint import Waypoint from nullptr.models.waypoint import Waypoint
from nullptr.models.survey import Survey from nullptr.models.survey import Survey
from nullptr.models.contract import Contract from nullptr.models.contract import Contract
class HaulMission(BaseMission): class TradeMission(BaseMission):
def start_state(self): def start_state(self):
return 'travel-to' return 'travel-to'

View File

@ -14,6 +14,9 @@ class Reference:
def resolve(self): def resolve(self):
return self.store.get(self.typ, self.symbol) return self.store.get(self.typ, self.symbol)
def f(self, detail):
return f'{self.symbol}.{self.typ.ext()}'
def __repr__(self): def __repr__(self):
return f'*REF*{self.symbol}.{self.typ.ext()}' return f'*REF*{self.symbol}.{self.typ.ext()}'
@ -46,6 +49,9 @@ class Base:
def define(self): def define(self):
pass pass
def created(self):
pass
def __hash__(self): def __hash__(self):
return hash((str(type(self)), self.symbol)) return hash((str(type(self)), self.symbol))
@ -84,12 +90,16 @@ class Base:
def __setattr__(self, name, value): def __setattr__(self, name, value):
if not name.startswith('_') and not self._disable_dirty: if not name.startswith('_') and not self._disable_dirty:
self.store.dirty(self) self.dirty()
if issubclass(type(value), Base): if issubclass(type(value), Base):
value = Reference.create(value) value = Reference.create(value)
super().__setattr__(name, value) super().__setattr__(name, value)
def __getattribute__(self, nm): def __getattribute__(self, nm):
if nm == 'system':
return self.get_system()
if nm == 'waypoint':
return self.get_waypoint()
val = super().__getattribute__(nm) val = super().__getattribute__(nm)
if type(val) == Reference: if type(val) == Reference:
val = val.resolve() val = val.resolve()

View File

@ -5,7 +5,6 @@ from dataclasses import field
class Jumpgate(Base): class Jumpgate(Base):
def define(self): def define(self):
self.connections: list = [] self.connections: list = []
self.system = self.get_system()
def update(self, d): def update(self, d):
getter = self.store.getter(Waypoint, create=True) getter = self.store.getter(Waypoint, create=True)

View File

@ -1,5 +1,5 @@
from .base import Base from .base import Base, Reference
from time import time from time import time
from nullptr.util import * from nullptr.util import *
from dataclasses import field, dataclass from dataclasses import field, dataclass
@ -32,12 +32,9 @@ class Marketplace(Base):
self.exchange:list = [] self.exchange:list = []
self.prices:dict = {} self.prices:dict = {}
self.last_prices:int = 0 self.last_prices:int = 0
self.set_waypoint()
self.system = self.get_system()
def set_waypoint(self): def get_waypoint(self):
waypoint = self.store.get(Waypoint, self.symbol, create=True) return self.store.get('Waypoint', self.symbol, create=True)
self.waypoint = waypoint
def is_fuel(self): def is_fuel(self):
return self.imports + self.exports + self.exchange == ['FUEL'] return self.imports + self.exports + self.exchange == ['FUEL']
@ -51,7 +48,7 @@ class Marketplace(Base):
e = self.prices[symbol] = MarketEntry() e = self.prices[symbol] = MarketEntry()
buy = mg(g, 'purchasePrice') buy = mg(g, 'purchasePrice')
sell = mg(g, 'sellPrice') sell = mg(g, 'sellPrice')
volume = mg(g, 'tradeVolumes') volume = mg(g, 'tradeVolume')
supply = SUPPLY.index(mg(g, 'supply')) supply = SUPPLY.index(mg(g, 'supply'))
activity = ACTIVITY.index(sg(g, 'activity','STRONG')) activity = ACTIVITY.index(sg(g, 'activity','STRONG'))
e.add(buy, sell, volume, supply, activity) e.add(buy, sell, volume, supply, activity)
@ -68,12 +65,12 @@ class Marketplace(Base):
def buy_price(self, resource): def buy_price(self, resource):
if resource not in self.prices: if resource not in self.prices:
return None return None
return self.prices[resource]['buy'] return self.prices[resource].buy
def volume(self, resource): def volume(self, resource):
if resource not in self.prices: if resource not in self.prices:
return None return None
return self.prices[resource]['volume'] return self.prices[resource].volume
def sellable_items(self, resources): def sellable_items(self, resources):
return [r for r in resources if r in self.prices] return [r for r in resources if r in self.prices]

View File

@ -28,7 +28,7 @@ class Survey(Base):
def api_dict(self): def api_dict(self):
return { return {
'signature': self.symbol, 'signature': self.symbol,
'symbol': self.waypoint(), 'symbol': self.waypoint,
'deposits': [{'symbol': d} for d in self.deposits], 'deposits': [{'symbol': d} for d in self.deposits],
'expiration': self.expires_str, 'expiration': self.expires_str,
'size': size_names[self.size] 'size': size_names[self.size]

View File

@ -12,7 +12,6 @@ class Waypoint(Base):
self.traits:list = [] self.traits:list = []
self.faction:str = '' self.faction:str = ''
self.is_under_construction:bool = False self.is_under_construction:bool = False
self.system = self.get_system()
self.uncharted = True self.uncharted = True
@ -24,6 +23,9 @@ class Waypoint(Base):
self.seta('is_under_construction', d, 'isUnderConstruction') self.seta('is_under_construction', d, 'isUnderConstruction')
self.setlst('traits', d, 'traits', 'symbol') self.setlst('traits', d, 'traits', 'symbol')
self.uncharted = 'UNCHARTED' in self.traits self.uncharted = 'UNCHARTED' in self.traits
def created(self):
self.get_system()
def distance(self, other): def distance(self, other):
return int(sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)) return int(sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2))

View File

@ -72,6 +72,7 @@ class ChunkHeader:
class Store: class Store:
def __init__(self, data_file, verbose=False): def __init__(self, data_file, verbose=False):
self.init_models() self.init_models()
self.data_file = data_file
self.data_dir = os.path.dirname(data_file) self.data_dir = os.path.dirname(data_file)
self.fil = open_file(data_file) self.fil = open_file(data_file)
self.data = {m: {} for m in self.models} self.data = {m: {} for m in self.models}
@ -90,6 +91,9 @@ class Store:
return return
print(m) print(m)
def f(self, detail):
return f'Store {self.data_file}'
def close(self): def close(self):
self.flush() self.flush()
self.fil.close() self.fil.close()
@ -112,6 +116,9 @@ class Store:
buf = BytesIO(data) buf = BytesIO(data)
p = StoreUnpickler(buf, self) p = StoreUnpickler(buf, self)
obj = p.load() 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 obj._file_offset = offset
self.hold(obj) self.hold(obj)
@ -138,6 +145,8 @@ class Store:
offset = self.fil.tell() offset = self.fil.tell()
dur = time() - start_time 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'Loaded {cnt} objects in {dur:.2f} seconds')
self.p(f'Fragmented space: {free} / {total} bytes') self.p(f'Fragmented space: {free} / {total} bytes')
@ -210,14 +219,15 @@ class Store:
symbol = obj.symbol symbol = obj.symbol
obj.store = self obj.store = self
self.data[typ][symbol] = obj self.data[typ][symbol] = obj
if hasattr(obj, 'system'): if type(obj).__name__ in ['Waypoint','Marketplace', 'Jumpgate', 'Survey']:
system_str = obj.get_system().symbol system_str = obj.system.symbol
if system_str not in self.system_members: if system_str not in self.system_members:
self.system_members[system_str] = set() self.system_members[system_str] = set()
self.system_members[system_str].add(obj) self.system_members[system_str].add(obj)
def create(self, typ, symbol): def create(self, typ, symbol):
obj = typ(symbol, self) obj = typ(symbol, self)
obj.created()
self.hold(obj) self.hold(obj)
self.dirty(obj) self.dirty(obj)
return obj return obj