30 Commits

Author SHA1 Message Date
Richard Bronkhorst 5d0595967d Update central_command.py, commander.py and one other file 2023-07-03 19:12:26 +02:00
Richard Bronkhorst bb64880822 Add Readme.md 2023-07-02 14:33:19 +02:00
Richard Bronkhorst 9d124179bf Update base.py 2023-06-26 20:50:29 +02:00
Richard Bronkhorst 9b9a149e3f Update base.py 2023-06-26 20:19:13 +02:00
Richard Bronkhorst 9e6583ac24 Update haul.py 2023-06-26 14:55:25 +02:00
Richard Bronkhorst 6c98eec738 Update commander.py 2023-06-26 13:40:11 +02:00
Richard Bronkhorst 11031599cf Update base.py 2023-06-26 05:48:19 +02:00
Richard Bronkhorst 7eea63ac82 Update mine.py 2023-06-25 22:39:33 +02:00
Richard Bronkhorst dc862088cd Update mine.py 2023-06-25 22:37:33 +02:00
Richard Bronkhorst 35bc586b72 Update api.py 2023-06-25 20:21:49 +02:00
Richard Bronkhorst 2a5680c16d Update api.py 2023-06-25 19:32:35 +02:00
Richard Bronkhorst 4d51ad53c0 Update analyzer.py, central_command.py and five other files 2023-06-25 17:35:06 +02:00
Richard Bronkhorst 5fbce54285 Update analyzer.py and commander.py 2023-06-23 13:49:09 +02:00
Richard Bronkhorst 27bd054e8b Update analyzer.py and commander.py 2023-06-23 13:32:08 +02:00
Richard Bronkhorst 38a2ee7870 Update central_command.py, command_line.py and seven other files 2023-06-22 19:57:07 +02:00
Richard Bronkhorst 7c3eaa825f Update commander.py and mission.py 2023-06-22 14:56:51 +02:00
Richard Bronkhorst ddd693a66e Update commander.py 2023-06-22 09:22:34 +02:00
Richard Bronkhorst b43568f476 Update commander.py 2023-06-22 08:49:43 +02:00
Richard Bronkhorst ff4643d7ac Update commander.py 2023-06-22 08:46:47 +02:00
Richard Bronkhorst 0e3f939b9a Update commander.py 2023-06-22 07:34:45 +02:00
Richard Bronkhorst 2d792dffae Update mission.py 2023-06-21 09:54:07 +02:00
Richard Bronkhorst 4043c5585e Update commander.py 2023-06-21 09:37:14 +02:00
Richard Bronkhorst b19e3ed2b2 Update analyzer.py and commander.py 2023-06-21 09:32:31 +02:00
Richard Bronkhorst b7d3347fac Update commander.py 2023-06-20 23:14:17 +02:00
Richard Bronkhorst 42e370fde5 Update commander.py and mission.py 2023-06-20 23:12:57 +02:00
Richard Bronkhorst b202b80541 Update mission.py 2023-06-20 22:38:29 +02:00
Richard Bronkhorst b023718450 Update mission.py 2023-06-20 22:30:21 +02:00
Richard Bronkhorst fbda97df61 Update analyzer.py 2023-06-20 22:18:20 +02:00
Richard Bronkhorst 707f142e7a Update analyzer.py, api.py and four other files 2023-06-20 21:46:05 +02:00
Richard Bronkhorst 35ea9e2e04 Update commander.py 2023-06-19 10:36:30 +02:00
16 changed files with 527 additions and 174 deletions
+83
View File
@@ -0,0 +1,83 @@
# 0ptr script for spacetraders.io
This is my script for running spacetraders.io. It has been completely written and operated from an iphone.
Because i'm using a less-than-optimal development platform, some 'best-practice' code standards have been thrown out of the window. For instance, some variables have obscure names just to keep them short.
The script offers a command-line interface allowing you to do most ST operations by hand. More importantly, it offers the functionality to assign missions to ships and execute them automatically.
# Getting started
I'm using the [pythonista app](http://omz-software.com/pythonista/) to develop and test the script. Just open main.py and hit run.
To keep the script running for longer periods of time, I'm using a VPS with docker. Start the script like so:
`docker run -v 0ptr:/data -ti 0ptr`
The script will ask for an agent name. Make sure you choose a unique one. After hitting enter, you will be presented with the prompt: `>`
Now you'll want to register. You need to provide your faction:
`> register cosmic`
If all goes well, you should have an account now.
Next is building up our view of the universe: the atlas.
`> universe`
This command will iterate over all systems and obtain all waypoint information. **this will take several hours**. However, if you are impatient, you can stop the process after a couple of minutes by hitting enter. It should at least have indexed your local system to continue.
Now, let's see what ships we own.
`> ships refresh`
This will populate our internal cache of present ships and their state. After this, the state is updated continually by each command that alters it. You should be able to omit the 'refresh' after this.
We should also populate the cache of contracts:
`> contracts refresh`
Same here: you can omit the 'refresh' from now on and view your contracts with the bare `contracts` command without sending a http request.
You are now ready to begin operations.
# Operations
To get a ship to do something, you first have to select it:
```
> ship 1
KILO-1 DOCKED [1035/1200] X1-YU85-03282C
KILO-1>
```
This prints the current state of the ship. Notice that the prompt now includes the name of the ship. Any command you now give will be executed by that ship.
A quick, non-complete list of commands:
* go: intra-system travel to another waypoint
* orbit
* dock
* market: show local market data
* shipyard: show local shipyard data
* jumps: show local jumpgate data
* mission: set ships mission type
* mine: mine and deliver a resource
* probe: find resource prices in nearby markets
* survey: generate surveys in a loop
* travel: travel to any waypoint in the universe using jumpgates (assuming a route is found)
* haul: buy goods at a site and bring them to a destination
* mset: set a mission parameter
* purchase: purchase a ship at a shipyard
* buy: buy a resource at the local market
* sell: sell a resource at the local market
* jump: jump to a remote system using the local jumpgate
* cmine: configures a mining mission for the current contract
* chaul configures a hauling mission for the current contract
* cprobe: configures a probing mission for the current contract
* query: lists nearest markets and buy prices (if known) of a resource
Look in commander.py for more available commands.
# Data storage
The script stores each object in a separate json file in the data directory. The default location for this directory is in your workingdir. The docker image places the data dir in '/data'
If you want to re-register an agent, or there is a sever reset, just delete all data in that directory.
+33 -4
View File
@@ -1,6 +1,7 @@
from nullptr.models.marketplace import Marketplace
from nullptr.models.jumpgate import Jumpgate
from nullptr.models.system import System
from nullptr.models.waypoint import Waypoint
from dataclasses import dataclass
@dataclass
@@ -29,14 +30,42 @@ class Analyzer:
def find_markets(self, resource, sellbuy):
for m in self.store.all(Marketplace):
resources = m.imports if sellbuy == 'sell' else m.exports
if resource in resources:
yield m
if 'sell' in sellbuy and resource in m.imports:
yield ('sell', m)
elif 'buy' in sellbuy and resource in m.exports:
yield ('buy', m)
elif 'exchange' in sellbuy and resource in m.exchange:
yield ('exchange', m)
def find_closest_markets(self, resource, sellbuy, location):
if type(location) == str:
location = self.store.get(Waypoint, location)
mkts = self.find_markets(resource, sellbuy)
candidates = []
origin = self.store.get(System, location.system())
for typ, m in mkts:
system = self.store.get(System, m.system())
d = origin.distance(system)
candidates.append((typ, m, d))
possibles = sorted(candidates, key=lambda m: m[2])
possibles = possibles[:10]
results = []
for typ,m,d in possibles:
system = self.store.get(System, m.system())
p = self.find_path(origin, system)
if p is None: continue
results.append((typ,m,d,len(p)))
return results
def solve_tsp(self, waypoints):
# todo actually try to solve it
return waypoints
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
+4 -2
View File
@@ -31,7 +31,7 @@ class Api:
def request(self, method, path, data=None, need_token=True, params={}):
try:
return self.request_once(method, path, data, need_token, params)
except ApiLimitError:
except (ApiLimitError, requests.exceptions.Timeout):
print('oops, hit the limit. take a break')
sleep(10)
return self.request_once(method, path, data, need_token, params)
@@ -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:
+30 -10
View File
@@ -1,10 +1,13 @@
from nullptr.store import Store
from nullptr.models.ship import Ship
from nullptr.mission import *
from nullptr.missions import create_mission, get_mission_class
from random import choice
from time import sleep
from threading import Thread
class CentralCommandError(Exception):
pass
class CentralCommand:
def __init__(self, store, api):
self.missions = {}
@@ -27,14 +30,6 @@ class CentralCommand:
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')
@@ -43,7 +38,17 @@ class CentralCommand:
t.start()
self.run()
print('manual mode')
def wait_for_stop(self):
try:
input()
except EOFError:
pass
self.stopping = True
print('stopping...')
def run(self):
self.update_missions()
while not self.stopping:
@@ -86,6 +91,21 @@ class CentralCommand:
m = self.missions[s]
m.next_step = max(s.cooldown, s.arrival)
def init_mission(self, s, mtyp):
if mtyp == 'none':
s.mission_state = {}
s.mission_status = None
s.mission = None
return
try:
mclass = get_mission_class(mtyp)
except ValueError:
raise CentralCommandError('no such mission')
s.mission = mtyp
s.mission_status = 'init'
s.mission_state = {k: v.default for k,v in mclass.params().items()}
self.start_mission(s)
def start_mission(self, s):
mtype = s.mission
m = create_mission(mtype, s, self.store, self.api)
+5 -2
View File
@@ -41,7 +41,7 @@ class CommandLine:
print(f'command not found; {c}')
def handle_error(self, cmd, args, e):
logging.error(e, exc_info=type(e).__name__ not in ['ApiError','CommandError'])
logging.error(e, exc_info=type(e).__name__ not in ['ApiError','CommandError', 'CentralCommandError'])
def handle_empty(self):
pass
@@ -90,5 +90,8 @@ class CommandLine:
except EOFError:
self.handle_eof()
break
self.handle_cmd(c)
try:
self.handle_cmd(c)
except Exception as e:
logging.error(e, exc_info=True)
+75 -27
View File
@@ -82,13 +82,6 @@ class Commander(CommandLine):
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)
@@ -96,12 +89,15 @@ class Commander(CommandLine):
def do_mission(self, arg=''):
if not self.has_ship(): return
if arg:
self.set_mission(arg)
self.centcom.init_mission(self.ship, arg)
self.print_mission()
def do_mset(self, args):
def do_mreset(self):
if not self.has_ship(): return
self.ship.mission_state = {}
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):
@@ -118,20 +114,66 @@ class Commander(CommandLine):
raise CommandError('no delivery')
resource = delivery['trade_symbol']
destination = delivery['destination']
self.set_mission('mine')
self.centcom.init_mission(self.ship, '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, 'dest', destination)
self.centcom.set_mission_param(self.ship, 'contract', contract.symbol)
self.print_mission()
def do_chaul(self):
if not self.has_ship(): return
if len(ship.cargo) > 0:
raise CommandError('please dump cargo first')
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_markets(resource, 'buy', destination)
if len(m) == 0:
m = self.analyzer.find_closest_markets(resource, 'exchange', destination)
if len(m) == 0:
print('no market found')
return
_, m, _, _ = m[0]
site = self.store.get(Waypoint, m.symbol)
self.centcom.init_mission(self.ship, 'haul')
self.centcom.set_mission_param(self.ship, 'site', site.symbol)
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, 'contract', contract.symbol)
self.print_mission()
def do_cprobe(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_markets(resource, 'buy,exchange', destination)
if len(m) is None:
print('no market found')
return
markets = [ mkt[1] for mkt in m]
markets = self.analyzer.solve_tsp(markets)
hops = ','.join([m.symbol for m in markets])
self.centcom.init_mission(self.ship, 'probe')
self.centcom.set_mission_param(self.ship, 'hops', hops)
self.print_mission()
def do_travel(self, dest):
dest = self.resolve('Waypoint', dest)
self.centcom.init_mission(self.ship, 'travel')
self.centcom.set_mission_param(self.ship, 'dest', dest.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)
pprint(self.api.agent)
def do_universe(self, page=1):
self.atlas_builder.run(page)
@@ -184,16 +226,16 @@ class Commander(CommandLine):
r = self.api.jumps(waypoint)
pprint(r)
def do_query(self):
location = self.ask_obj(System, 'Where are you? ')
resource = input('what resource?').upper()
sellbuy = self.ask_multichoice(['sell','buy'], 'do you want to sell or buy?')
def do_query(self, resource):
if not self.has_ship(): return
location = self.ship.location()
resource = resource.upper()
print('Found markets:')
for m in self.analyzer.find_markets(resource, sellbuy):
system = self.store.get(System, m.system())
p = self.analyzer.find_path(location, system)
if p is None: continue
print(m, f'{len(p)-1} hops')
for typ, m, d, plen in self.analyzer.find_closest_markets(resource, 'buy,exchange',location):
price = '?'
if resource in m.prices:
price = m.prices[resource]['buy']
print(m, typ[0], f'{plen-1:3} hops {price}')
def do_path(self):
orig = self.ask_obj(System, 'from: ')
@@ -227,6 +269,10 @@ class Commander(CommandLine):
resource = delivery['trade_symbol']
self.api.deliver(self.ship, resource, contract)
pprint(contract)
def do_fulfill(self):
contract = self.active_contract()
self.api.fulfill(contract)
def do_ship(self, arg=''):
if arg != '':
@@ -309,7 +355,9 @@ class Commander(CommandLine):
def do_shipyard(self):
if not self.has_ship(): return
location = self.ship.location()
pprint(self.api.shipyard(location))
data = self.api.shipyard(location)
for s in must_get(data, 'ships'):
print(s['type'], s['purchasePrice'])
def do_jump(self, system_str):
if not self.has_ship(): return
+23
View File
@@ -0,0 +1,23 @@
from nullptr.missions.survey import SurveyMission
from nullptr.missions.mine import MiningMission
from nullptr.missions.haul import HaulMission
from nullptr.missions.travel import TravelMission
from nullptr.missions.probe import ProbeMission
def get_mission_class( mtype):
types = {
'survey': SurveyMission,
'mine': MiningMission,
'haul': HaulMission,
'travel': TravelMission,
'probe': ProbeMission
}
if mtype not in types:
raise ValueError(f'invalid mission type {mtype}')
return types[mtype]
def create_mission(mtype, ship, store, api):
typ = get_mission_class(mtype)
m = typ(ship, store, api)
return m
+100 -127
View File
@@ -2,9 +2,12 @@ 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
from functools import partial
import logging
from nullptr.util import *
@@ -22,6 +25,8 @@ class MissionParam:
return str(val)
elif self.cls == int:
return int(val)
elif self.cls == list:
return [i.strip() for i in val.split(',')]
elif issubclass(self.cls, Base):
data = store.get(self.cls, val)
if data is None:
@@ -42,6 +47,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,95 +127,21 @@ class Mission:
self.status(next_step[result])
print(f'{self.ship} {status} -> {self.status()}')
class MiningMission(Mission):
@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 start_state(self):
return 'go_site'
def steps(self):
return {
'extract': (self.step_extract, {
'done': 'dock',
'more': 'extract'
}),
'dock': (self.step_dock, 'sell'),
'sell': (self.step_sell, {
'more': 'sell',
'done': 'orbit',
}),
'orbit': (self.step_orbit, 'jettison'),
'jettison': (self.step_dispose, {
'more': 'jettison',
'done': 'extract',
'full': 'go_dest'
}),
'go_dest': (self.step_go_dest, 'dock_dest'),
'dock_dest': (self.step_dock, 'unload'),
'unload': (self.step_unload, {
'done': 'refuel',
'more': 'unload'
}),
'refuel': (self.step_refuel, 'orbit_dest'),
'orbit_dest': (self.step_orbit, 'go_site'),
'go_site': (self.step_go_site, 'extract')
}
def get_survey(self):
resource = self.st('resource')
site = self.rst(Waypoint,'site')
# todo optimize
for s in self.store.all(Survey):
if resource in s.deposits and site.symbol == s.waypoint():
return s
return None
def step_extract(self):
survey = self.get_survey()
print('using survey:', str(survey))
result = self.api.extract(self.ship, survey)
symbol = sg(result,'extraction.yield.symbol')
units = sg(result,'extraction.yield.units')
print('extracted:', units, symbol)
self.next_step = self.ship.cooldown
if self.ship.cargo_units < self.ship.cargo_capacity:
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'
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_dock(self):
self.api.dock(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
def step_unload(self):
contract = self.rst(Contract, 'contract')
delivery = self.st('delivery')
@@ -223,55 +155,96 @@ class MiningMission(Mission):
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)
if len(typs) > 0:
self.api.jettison(self.ship, typs[0])
if len(typs) > 1:
return 'more'
elif self.ship.cargo_units > self.ship.cargo_capacity - 3:
return 'full'
else:
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 step_travel(self):
traject = self.st('traject')
if traject is None or traject == []:
return 'done'
dest = self.store.get(Waypoint, traject[-1])
loc = self.ship.location()
print(dest, loc)
if dest == loc:
self.sts('traject', None)
return 'done'
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
if traject == []:
traject= None
self.sts('traject', traject)
return 'more'
def step_calculate_traject(self, dest):
if type(dest) == str:
dest = self.store.get(Waypoint, 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)
if dest_sys == loc_sys:
result = [dest.symbol]
self.sts('traject', result)
return
path = self.analyzer.find_path(loc_sys, dest_sys)
result = []
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)
self.sts('traject', result)
print(result)
return result
def step_dock(self):
self.api.dock(self.ship)
def step_refuel(self):
if self.ship.fuel_capacity == 0:
return
if self.ship.fuel_current / self.ship.fuel_capacity < 0.5:
try:
self.api.refuel(self.ship)
except Exception as e:
pass
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):
def start_state(self):
return 'survey'
def steps(self):
def travel_steps(self, nm, destination, next_step):
destination = self.st(destination)
calc = partial(self.step_calculate_traject, destination)
return {
'survey': (self.step_survey, 'survey')
f'travel-{nm}': (self.step_orbit, f'calc-trav-{nm}'),
f'calc-trav-{nm}': (calc, f'go-{nm}'),
f'go-{nm}': (self.step_travel, {
'done': f'dock-{nm}',
'more': f'go-{nm}'
}),
f'dock-{nm}': (self.step_dock, f'refuel-{nm}'),
f'refuel-{nm}': (self.step_refuel, next_step)
}
def step_survey(self):
result = self.api.survey(self.ship)
#pprint(result, 2)
self.next_step = self.ship.cooldown
def create_mission(mtype, ship, store, api):
types = {
'survey': SurveyMission,
'mine': MiningMission
}
if mtype not in types:
logging.warning(f'invalid mission type {mtype}')
return
m = types[mtype](ship, store, api)
return m
+25
View File
@@ -0,0 +1,25 @@
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):
def start_state(self):
return 'travel-to'
@classmethod
def params(cls):
return {
'site': MissionParam(Waypoint, True),
'resource': MissionParam(str, True),
'dest': MissionParam(Waypoint, True),
'delivery': MissionParam(str, True, 'deliver'),
'contract': MissionParam(Contract, False)
}
def steps(self):
return {
**self.travel_steps('to', 'site', 'load'),
'load': (self.step_load, 'travel-back'),
**self.travel_steps('back', 'dest', 'unload'),
'unload': (self.step_unload, 'travel-to'),
}
+80
View File
@@ -0,0 +1,80 @@
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
from nullptr.util import *
class MiningMission(BaseMission):
@classmethod
def params(cls):
return {
'site': MissionParam(Waypoint, True),
'resource': MissionParam(str, True),
'dest': MissionParam(Waypoint, True),
'delivery': MissionParam(str, True, 'deliver'),
'contract': MissionParam(Contract, False)
}
def start_state(self):
return 'travel-to'
def steps(self):
return {
**self.travel_steps('to', 'site', 'orbit1'),
'orbit1': (self.step_orbit, 'extract'),
'extract': (self.step_extract, {
'done': 'dock',
'more': 'extract'
}),
'dock': (self.step_dock, 'sell'),
'sell': (self.step_sell, {
'more': 'sell',
'done': 'orbit2',
}),
'orbit2': (self.step_orbit, 'jettison'),
'jettison': (self.step_dispose, {
'more': 'jettison',
'done': 'extract',
'full': 'travel-back'
}),
**self.travel_steps('back', 'dest', 'unload'),
'unload': (self.step_unload, {
'done': 'travel-to',
'more': 'unload'
}),
}
def get_survey(self):
resource = self.st('resource')
site = self.rst(Waypoint,'site')
# todo optimize
for s in self.store.all(Survey):
if resource in s.deposits and site.symbol == s.waypoint():
return s
return None
def step_extract(self):
survey = self.get_survey()
print('using survey:', str(survey))
result = self.api.extract(self.ship, survey)
symbol = sg(result,'extraction.yield.symbol')
units = sg(result,'extraction.yield.units')
print('extracted:', units, symbol)
self.next_step = self.ship.cooldown
if self.ship.cargo_units < self.ship.cargo_capacity:
return 'more'
else:
return 'done'
def step_dispose(self):
contract = self.rst(Contract, 'contract')
typs = self.ship.nondeliverable_cargo(contract)
if len(typs) > 0:
self.api.jettison(self.ship, typs[0])
if len(typs) > 1:
return 'more'
elif self.ship.cargo_units > self.ship.cargo_capacity - 3:
return 'full'
else:
return 'done'
+33
View File
@@ -0,0 +1,33 @@
from nullptr.missions.base import BaseMission, MissionParam
from nullptr.models.waypoint import Waypoint
class ProbeMission(BaseMission):
def start_state(self):
return 'next-hop'
@classmethod
def params(cls):
return {
'hops': MissionParam(list, True),
'next-hop': MissionParam(int, True, 0)
}
def steps(self):
return {
'next-hop': (self.step_next_hop, 'travel-to'),
**self.travel_steps('to', 'site', 'market'),
'market': (self.step_market, 'next-hop'),
}
def step_market(self):
loc = self.ship.location()
self.api.marketplace(loc)
def step_next_hop(self):
hops = self.st('hops')
next_hop = self.st('next-hop')
hop = hops[next_hop]
self.sts('site', hop)
self.sts('next-hop', (next_hop+1) % len(hops))
+15
View File
@@ -0,0 +1,15 @@
from nullptr.missions.base import BaseMission, MissionParam
class SurveyMission(BaseMission):
def start_state(self):
return 'survey'
def steps(self):
return {
'survey': (self.step_survey, 'survey')
}
def step_survey(self):
result = self.api.survey(self.ship)
#pprint(result, 2)
self.next_step = self.ship.cooldown
+16
View File
@@ -0,0 +1,16 @@
from nullptr.missions.base import BaseMission, MissionParam
from nullptr.models.waypoint import Waypoint
class TravelMission(BaseMission):
def start_state(self):
return 'travel-to'
@classmethod
def params(cls):
return {
'dest': MissionParam(Waypoint, True)
}
def steps(self):
return self.travel_steps('to', 'dest', 'done')
+3 -2
View File
@@ -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
+1
View File
@@ -25,6 +25,7 @@ class Marketplace(SystemMember):
price['symbol'] = symbol
price['buy'] = mg(g, 'purchasePrice')
price['sell'] = mg(g, 'sellPrice')
price['volume'] = mg(g, 'tradeVolume')
prices[symbol] = price
self.prices = prices
+1
View File
@@ -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