Skip to content

Python Quick Peek

Tromador edited this page Aug 2, 2018 · 2 revisions

Curious about what it takes to command the power of Trade Dangerous to your own nefarious or perfectly legitimate and under supported needs? Or, now that you've installed the "Python" thing, perhaps you'd just like to know wtf that was?

Based on http://pastebin.com/EB6FYskA

Python is designed to be "interactive", and TD is built in a way that tries to take advantage of that - but feel free to call me out where it doesn't do it so well.

This quick guide will walk you through some of the powerful things you can do very easily, show you a little bit of Python, and then probably not call you back the next day.

To get started: Start an instance of Python (at the command prompt, type python), or the GUI editor that ships with Python, "IDLE" (Open the start menu and type IDLE). Alternatively, you could get bold and install "ipython" (by typing pip install ipython) and then at the command prompt, type ipython.

Whichever you choose, get to the >>> Python prompt and then walk through the following. If you make errors or want to re-run a line, just press the up-arrow on your keyboard. Each of the tools keeps a history of commands, some are just better at it than others.

Ideal approach: Type or paste each non-comment line and observe the results

The core of TradeDangerous is tradedb.py, or in python terms, the tradedb module.

Python only loads the modules you ask it for, and theimport command is the way to ask.

#!python
import tradedb

You can now reference things in the module by modulename.thingname The cornerstone of TD is the TradeDB class and most of what you can do with TD depends on you creating an instance of TradeDB. (Imagine you ask a barman for a beer and he hands you a pint glass full of beer; beer is a general concept, the frothy cold one on the bar is an instance of that concept)

#!python
import tradedb
tdb = tradedb.TradeDB()

Or in our beer drinking terms: frothyColdOne = bar.Beer().

tradedb.TradeDB() is a function call that returns a TradeDangerous DB object, which is loaded up with all kinds of data and has a whole bunch of routines you can call to query that data.

Lets start by asking it to look up a particular system.

#!python
# Look up a particular system
sol = tdb.lookupSystem("sol")

How did I know the command was 'lookupSystem'? I found it in the TradeDB help: don't try to read it all right now...

#!python
help(tdb)

The sol thing, what's that then? Well, its the system that was looked up.

#!python
print("Sol is at {}, {}, {}.".format(sol.posX, sol.posY, sol.posZ))

# in a normal python program, this next line does nothing. in
# interactive python, it's a shortcut for "print(sol.stations)".

sol.stations

How did I know it has posX, posY and posZ, or stations?

#!python
help(sol)

(this actually gives me help from the type-of-thing that sol is)

More things we can do:

#!python
# Look up a station
abe = tdb.lookupStation("Abraham Lincoln")
# go on ... find out more... use help

# Look up a station in a particular system
abe = tdb.lookupStation("Abraham Lincoln", sol)

# Look up a system or station using the flexible naming mechanism
phoenix = tdb.lookupPlace("ascendingp")
type(phoenix)	# tell me what type of thing "phoenix" is...

sol = tdb.lookupPlace("@sol")
type(sol)
lave = tdb.lookupPlace("lave")

abe = tdb.lookupPlace("sol/hamlinc")

Lets do something more complex and show off a wee-bit of Python.

  • Pick an 'origin' system,
  • Pick 5 random systems,
  • Work out which is closest to 'origin',
  • Tell me how to get there.

tdb has a lookup table - an index, where the numeric database ID is the key and the System object is the value. This is tdb.systemByID. It's implemented using Python's dict (dictionary) class, which is handy because we can get a list of all systems known to your TD by doing this:

#!python
systemTable = tdb.systemByID.values()
len(systemTable)
# first entry in a python list is entry 0
systemTable[0]
# you can use negative numbers to start from the back
systemTable[-1].name()

How about that picking 5 at random. That's going to be fiddly, isn't it? Nope. In Python there's almost always a module for whatever ails you.

#!python
import random
help(random.sample)
visitMe = random.sample(list(systemTable), 5)
print(visitMe)
visitMe[0]
visitMe[0].name()

The list(systemTable) is there because I'm taking some short cuts. Forgetaboutit.

So now we have our list of 5 systems. Which is closest to "Eranin"? (Feel free to replace Eranin with a system of your own choosing)

#!python
origin = tdb.lookupPlace("Eranin")

# Call distanceTo(origin) on every member of visitMe and
# then retrieve the one with the lowest distance.
closest = min(visitMe, key=lambda candidate: candidate.distanceTo(origin))
print("{start} -> {dest}: {dist:.2f} ly".format(
	start=origin.name(), dest=closest.name(),
	dist=origin.distanceTo(closest),
))

That's great, and all, but how would we get there?

#!python
help(tdb.getRoute)

route = tdb.getRoute(origin, closest, 15)
if not route:
	print("Shame, couldn't find a route.")
else:
	# Route is a list of Systems. Turn it into a list of
	# System names...
	routeNames = [ system.name() for system, distance in route ]
	print("Route:", routeNames)

Oh, that's handy. But what if I want to do that for several difference groups of places? It would be nice if there was some way to repeat a given set of operations for arbitrary combinations of parameters.

It's called a function, denoted by a definition with a list of the parameters we'll take. The function ends when we stop indenting.

#!python
def route_to_closest(origin, destinations, maxLy=15):
    
    closest = min(destinations, key=lambda candidate: candidate.distanceTo(origin))
    print("Closest:", closest.name(), closest.distanceTo(origin))
    route = tdb.getRoute(origin, closest, maxLy)
    if not route:
        print("No route found.")
    else:
        print("Route:", ", ".join(system.name() for system, distance in route))

# hit return a few times to end the block

This defines a function (aka a sub-routine, a callable piece of code) that takes some parameters. The =15 makes maxLy optional; it will assume a value of 15 if we don't call it with a value of our own.

#!python
route_to_closest(origin, visitMe)
route_to_closest(origin, visitMe, 20)

# lets change origin
origin = tdb.lookupSystem("Diso")
route_to_closest(origin, visitMe)

# python also lets you name which parameters you are passing:

route_to_closest(origin=origin, destinations=visitMe, maxLy=9)

# lets try a different route:
sol = tdb.lookupSystem("sol")
lave = tdb.lookupSystem("lave")
sirius = tdb.lookupSystem("sirius")

# in python, you can make a list by saying:
#  list( thing1, thing2, ..., thingN )
# or
# [ thing1, thing2, ..., thingN ]
# our function is expecting "destinations" to be a list,

route_to_closest(sol, [ lave, sirius ])

This made a list (containing lave and sirius) and passed that as destination. If we'd tried to do route_to_closest(sol, lave, sirius) Python would have thought sirius was our answer to maxLy...

Other interesting parts of tradedb:

  • tradeenv.TradeEnv for putting together command parameters,
  • tradecalc.TradeCalc for trading calculations,
  • formatting,
  • transfers, for upload/download

See also: help(tradedb.TradeDB)

Want to do more?

Python has excellent online documentation: https://docs.python.org/3/ Look into "ipython": http://ipython.org/, in particular "ipython qtconsole" (requires pyside and pygments: pip install pyside pygments)

Clone this wiki locally