Update central_command.py, commander.py and eleven other files

This commit is contained in:
Richard Bronkhorst 2023-06-18 19:15:51 +02:00
parent a229b9e300
commit 8b29ca8f58
13 changed files with 192 additions and 81 deletions

View File

@ -1,13 +1,15 @@
from nullptr.store import store
from nullptr.store import Store
from nullptr.models.ship import Ship
from nullptr.mission import *
from random import choice
from time import sleep
from threading import Thread
class CentralCommand:
def __init__(self, api):
def __init__(self, store, api):
self.missions = {}
self.stopping = False
self.store = store
self.api = api
self.update_missions()
@ -20,16 +22,36 @@ class CentralCommand:
def tick(self):
missions = self.get_ready_missions()
if len(missions) == 0: return
if len(missions) == 0: return False
ship = choice(missions)
mission = self.missions[ship]
mission.step()
return True
def wait_for_stop(self):
try:
input()
except EOFError:
pass
self.stopping = True
print('stopping...')
def run_interactive(self):
print('auto mode. hit enter to stop')
t = Thread(target=self.wait_for_stop)
t.daemon = True
t.start()
self.run()
print('manual mode')
def run(self):
self.update_missions()
while not self.stopping:
self.tick()
self.api.save()
did_step = True
request_counter = self.api.requests_sent
while request_counter == self.api.requests_sent and did_step:
did_step = self.tick()
self.store.flush()
sleep(0.5)
self.stopping = False
@ -47,15 +69,14 @@ class CentralCommand:
return
param = params[nm]
try:
parsed_val = param.parse(val)
parsed_val = param.parse(val, self.store)
except ValueError as e:
print(e)
raise MissionError(e)
return
print('ok')
ship.mission_state[nm] = parsed_val
ship.set_mission_state(nm, parsed_val)
def update_missions(self):
for s in store.all(Ship):
for s in self.store.all(Ship):
if s.mission is None:
if s in self.missions:
self.stop_mission(s)
@ -64,7 +85,7 @@ class CentralCommand:
def start_mission(self, s):
mtype = s.mission
m = create_mission(mtype, s, self.api)
m = create_mission(mtype, s, self.store, self.api)
self.missions[s] = m
return m

View File

@ -12,7 +12,7 @@ from .util import *
from time import sleep, time
from threading import Thread
from nullptr.atlas_builder import AtlasBuilder
from nullptr.central_command import CentralCommand
class CommandError(Exception):
pass
@ -24,6 +24,7 @@ class Commander(CommandLine):
self.agent = self.select_agent()
self.api = Api(self.store, self.agent)
self.atlas_builder = AtlasBuilder(self.store, self.api)
self.centcom = CentralCommand(self.store, self.api)
self.analyzer = Analyzer(self.store)
self.ship = None
@ -45,7 +46,7 @@ class Commander(CommandLine):
def ask_obj(self, typ, prompt):
obj = None
while obj is None:
symbol = input(prompt)
symbol = input(prompt).strip()
obj = self.store.get(typ, symbol.upper())
if obj is None:
print('not found')
@ -77,10 +78,61 @@ class Commander(CommandLine):
self.api.info()
pprint(self.agent, 100)
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'
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)
def do_mission(self, arg=''):
if not self.has_ship(): return
if arg:
self.set_mission(arg)
self.print_mission()
def do_mset(self, args):
if not self.has_ship(): return
nm, val = args.split(' ')
self.centcom.set_mission_param(self.ship, nm, val)
def active_contract(self):
for c in self.store.all('Contract'):
if c.accepted and not c.fulfilled: return c
raise CommandError('no active contract')
def do_cmine(self):
if not self.has_ship(): return
site = self.ship.location_str
contract = self.active_contract()
delivery = contract.unfinished_delivery()
if delivery is None:
raise CommandError('no delivery')
resource = delivery['trade_symbol']
destination = delivery['destination']
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())
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)
def do_universe(self, page=1):
self.atlas_builder.run(page)

View File

@ -1,48 +1,57 @@
from nullptr.store import store
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.survey import Survey
from nullptr.models.ship import Ship
from time import time
import logging
from util import *
from nullptr.util import *
class MissionError(Exception):
pass
class MissionParam:
def __init__(self, cls, required=True, default=None):
self.cls = cls
self.required = required
self.default = default
def parse(self, val):
def parse(self, val, store):
if self.cls == str:
return str(val)
elif self.cls == int:
return int(val)
elif issubclass(self.cls, StoreObject):
elif issubclass(self.cls, Base):
data = store.get(self.cls, val)
if data is None:
raise ValueError('object not found')
return data
return data.symbol
else:
raise ValueError('unknown param typr')
class Mission:
ship: Ship
next_step: int = 0
@classmethod
def params(cls):
return {
}
def __init__(self, ship, api):
def __init__(self, ship, store, api):
self.ship = ship
self.store = store
self.api = api
self.next_step = 0
def sts(self, nm, v):
self.ship.mission_state[nm] = v
if issubclass(type(v), Base):
v = v.symbol
self.ship.set_mission_state(nm, v)
def rst(self, typ, nm):
symbol = self.st(nm)
return self.store.get(typ, symbol)
def st(self, nm):
if not nm in self.ship.mission_state:
return None
@ -98,7 +107,7 @@ class Mission:
try:
result = handler()
except Exception as e:
logging.error(e)
logging.error(e, exc_info=True)
self.status('error')
return
if type(next_step) == str:
@ -157,10 +166,10 @@ class MiningMission(Mission):
def get_survey(self):
resource = self.st('resource')
site = self.st('site')
site = self.rst(Waypoint,'site')
# todo optimize
for s in store.all(Survey):
if resource in s.deposits and site == s.waypoint:
for s in self.store.all(Survey):
if resource in s.deposits and site.symbol == s.waypoint():
return s
return None
@ -192,8 +201,8 @@ class MiningMission(Mission):
return 'more'
def step_go_dest(self):
destination = self.st('destination')
if self.ship.location == destination:
destination = self.rst(Waypoint, 'destination')
if self.ship.location() == destination:
return
self.api.navigate(self.ship, destination)
self.next_step = self.ship.arrival
@ -202,7 +211,7 @@ class MiningMission(Mission):
self.api.dock(self.ship)
def step_unload(self):
contract = self.st('contract')
contract = self.rst(Contract, 'contract')
delivery = self.st('delivery')
if delivery == 'sell':
return self.step_sell(False)
@ -219,7 +228,7 @@ class MiningMission(Mission):
self.api.refuel(self.ship)
def step_dispose(self):
contract = self.st('contract')
contract = self.rst(Contract, 'contract')
typs = self.ship.nondeliverable_cargo(contract)
if len(typs) > 0:
self.api.jettison(self.ship, typs[0])
@ -235,8 +244,8 @@ class MiningMission(Mission):
self.api.orbit(self.ship)
def step_go_site(self):
site = self.st('site')
if self.ship.location == site:
site = self.rst(Waypoint,'site')
if self.ship.location() == site:
return
self.api.navigate(self.ship, site)
self.next_step = self.ship.arrival
@ -256,7 +265,7 @@ class SurveyMission(Mission):
#pprint(result, 2)
self.next_step = self.ship.cooldown
def create_mission(mtype, ship, api):
def create_mission(mtype, ship, store, api):
types = {
'survey': SurveyMission,
'mine': MiningMission
@ -264,5 +273,5 @@ def create_mission(mtype, ship, api):
if mtype not in types:
logging.warning(f'invalid mission type {mtype}')
return
m = types[mtype](ship, api)
m = types[mtype](ship, store, api)
return m

View File

@ -1,8 +1,9 @@
from .base import Base
class Agent(Base):
token: str = None
credits: int = 0
def define(self):
self.token: str = None
self.credits: int = 0
def update(self, d):
self.seta('credits', d)

View File

@ -9,7 +9,11 @@ class Base:
def __init__(self, symbol, store):
self.store = store
self.symbol = symbol
self.define()
def define(self):
pass
def __hash__(self):
return hash((str(type(self)), self.symbol))

View File

@ -5,13 +5,14 @@ from .base import Base
class Contract(Base):
identifier = 'id'
type: str
deliveries: list
accepted: bool
fulfilled: bool
expires: int
expires_str: str
pay: int
def define(self):
self.type: str = ''
self.deliveries: list = []
self.accepted: bool = False
self.fulfilled: bool = False
self.expires: int = 0
self.expires_str: str = ''
self.pay: int = 0
@classmethod
def ext(cls):
@ -29,6 +30,18 @@ class Contract(Base):
'expiration': self.expires_str,
}
def is_done(self):
for d in self.deliveries:
if d['units_fulfilled'] > d['units_requires']:
return False
return False
def unfinished_delivery(self):
for d in self.deliveries:
if d['units_required'] > d['units_fulfilled']:
return d
return None
def update(self, d):
self.seta('expires',d, 'terms.deadline',parse_timestamp)
self.seta('expires_str', d,'terms.deadline')
@ -46,6 +59,7 @@ class Contract(Base):
delivery['destination'] = must_get(e, 'destinationSymbol')
self.deliveries.append(delivery)
def f(self, detail=1):
hours = int(max(0, self.expires - time()) / 3600)
accepted = 'A' if self.accepted else '-'

View File

@ -2,9 +2,10 @@ from .system_member import SystemMember
from dataclasses import field
class Jumpgate(SystemMember):
range: int
faction: str
systems: list = []
def define(self):
self.range: int = 0
self.faction: str = ''
self.systems: list = []
def update(self, d):
self.setlst('systems', d, 'connectedSystems', 'symbol')

View File

@ -5,11 +5,12 @@ from nullptr.util import *
from dataclasses import field
class Marketplace(SystemMember):
imports:list = []
exports:list = []
exchange:list = []
prices:dict = {}
last_prices:int = 0
def define(self):
self.imports:list = []
self.exports:list = []
self.exchange:list = []
self.prices:dict = {}
self.last_prices:int = 0
def update(self, d):
self.setlst('imports', d, 'imports', 'symbol')

View File

@ -4,18 +4,19 @@ from nullptr.util import *
from dataclasses import dataclass, field
class Ship(Base):
cargo:dict = {}
mission_state:dict = {}
status:str = ''
cargo_capacity:int = 0
cargo_units:int = 0
location_str = ''
cooldown:int = 0
arrival:int = 0
fuel_current:int = 0
fuel_capacity:int = 0
mission:str = None
mission_status:str = 'init'
def define(self):
self.cargo:dict = {}
self.mission_state:dict = {}
self.status:str = ''
self.cargo_capacity:int = 0
self.cargo_units:int = 0
self.location_str = ''
self.cooldown:int = 0
self.arrival:int = 0
self.fuel_current:int = 0
self.fuel_capacity:int = 0
self.mission:str = None
self.mission_status:str = 'init'
@classmethod
def ext(self):
@ -50,6 +51,10 @@ class Ship(Base):
def is_travelling(self):
return self.status == 'IN_TRANSIT'
def set_mission_state(self, nm, val):
self.mission_state[nm] = val
self.store.dirty(self)
def get_cargo(self, typ):
if typ not in self.cargo:

View File

@ -6,12 +6,13 @@ size_names = ['SMALL','MODERATE','LARGE']
class Survey(SystemMember):
identifier = 'signature'
type: str = ''
deposits: list[str] = []
size: int = 0
expires: int = 0
expires_str: str = ''
exhausted: bool = False
def define(self):
self.type: str = ''
self.deposits: list[str] = []
self.size: int = 0
self.expires: int = 0
self.expires_str: str = ''
self.exhausted: bool = False
@classmethod
def ext(cls):

View File

@ -3,9 +3,10 @@ from .base import Base
from math import sqrt
class System(Base):
x:int = 0
y:int = 0
type:str = 'unknown'
def define(self):
self.x:int = 0
self.y:int = 0
self.type:str = 'unknown'
def update(self, d):
self.seta('x', d)

View File

@ -3,11 +3,12 @@ from nullptr.util import *
from dataclasses import field
class Waypoint(SystemMember):
x:int = 0
y:int = 0
type:str = 'unknown'
traits:list = []
faction:str = ''
def define(self):
self.x:int = 0
self.y:int = 0
self.type:str = 'unknown'
self.traits:list = []
self.faction:str = ''
def update(self, d):
self.seta('x', d)

View File

@ -129,7 +129,7 @@ class Store:
yield m
def cleanup(self):
if time() > self.last_cleanup + self.cleanup_interval:
if time() < self.last_cleanup + self.cleanup_interval:
return
start_time = time()
expired = list()