Skip to content

Commit

Permalink
Fixes, cleanup and --detail
Browse files Browse the repository at this point in the history
--detail will show jumps on a multi-jump hop,
Moved several output items to --debug and increased --debug reporting,
Added code to validate data such as links between systems,
Fixed a problem with tdb.itemIDs being populated with { name: name }
  • Loading branch information
kfsone committed Aug 12, 2014
1 parent aa49676 commit 4697912
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 26 deletions.
10 changes: 6 additions & 4 deletions trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def parse_command_line():
parser.add_argument('--unique', help='Only visit each station once', default=False, required=False, action='store_true')
parser.add_argument('--insurance', metavar="<Credits>", help="Reserve at least this many credits", type=int, default=0, required=False)
parser.add_argument('--margin', metavar="<Error Margin>", help="Reduce gains by this much to provide a margin of error for market fluctuations (e.g. 0.25 reduces gains by 1/4). 0<=m<=0.25. Default: 0.02", default=0.02, type=float, required=False)
parser.add_argument('--debug', help="Enable verbose output", default=False, required=False, action='store_true')
parser.add_argument('--detail', help="Give detailed jump information for multi-jump hops", default=False, required=False, action='store_true')
parser.add_argument('--debug', help="Enable diagnostic output", default=False, required=False, action='store_true')
parser.add_argument('--routes', metavar="<MaxRoutes>", help="Maximum number of routes to show", type=int, default=1, required=False)

args = parser.parse_args()
Expand Down Expand Up @@ -133,11 +134,12 @@ def main():
parse_command_line()

startCr = args.credits - args.insurance
routes = [ Route([src], [], startCr, 0, 0) for src in origins ]
routes = [ Route(stations=[src], hops=[], jumps=[], startCr=startCr, gainCr=0) for src in origins ]
numHops = args.hops
lastHop = numHops - 1

print("From %s via %s to %s with %d credits for %d hops" % (originName, viaName, destName, startCr, numHops))
if args.debug:
print("From %s via %s to %s with %d credits for %d hops" % (originName, viaName, destName, startCr, numHops))

calc = TradeCalc(tdb, debug=args.debug, capacity=args.capacity, maxUnits=maxUnits, margin=args.margin, unique=args.unique)
for hopNo in range(numHops):
Expand All @@ -163,7 +165,7 @@ def main():

routes.sort()
for i in range(0, min(len(routes), args.routes)):
print(routes[i])
print(routes[i].detail(args.detail))


if __name__ == "__main__":
Expand Down
45 changes: 32 additions & 13 deletions tradecalc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,40 @@ def __init__(self, stations, hops, startCr, gainCr, jumps):
self.jumps = jumps

def plus(self, dst, hop, jumps):
rvalue = Route(self.route + [dst], self.hops + [hop], self.startCr, self.gainCr + hop[1], self.jumps + jumps)
rvalue = Route(self.route + [dst], self.hops + [hop], self.startCr, self.gainCr + hop[1], self.jumps + [jumps])
return rvalue

def __lt__(self, rhs):
if rhs.gainCr < self.gainCr: # reversed
return True
return rhs.gainCr == self.gainCr and rhs.jumps < self.jumps
return rhs.gainCr == self.gainCr and len(rhs.jumps) < len(self.jumps)
def __eq__(self, rhs):
return self.gainCr == rhs.gainCr and self.jumps == rhs.jumps
return self.gainCr == rhs.gainCr and len(self.jumps) == len(rhs.jumps)

def __repr__(self):
def str(self):
return "%s -> %s" % (self.route[0], self.route[-1])

def detail(self, verbose=False):
src = self.route[0]
credits = self.startCr
gainCr = 0
route = self.route

str = "%s -> %s:\n" % (src, route[-1])
str = self.str() + ":\n"
for i in range(len(route) - 1):
hop = self.hops[i]
str += " @ %-20s Buy" % route[i]
str += " >-> " if i == 0 else " -+- "
str += "%-20s Buy" % route[i]
for item in hop[0]:
str += " %d*%s," % (item[1], item[0])
str += "\n"
if verbose and len(self.jumps[i]) > 2:
str += " | "
str += " -> ".join([ jump.str() for jump in self.jumps[i] ])
str += "\n"
gainCr += hop[1]

str += " $ %s %dcr + %dcr => %dcr total" % (route[-1], credits, gainCr, credits + gainCr)
str += " <-< %s %dcr + %dcr => %dcr total" % (route[-1], credits, gainCr, credits + gainCr)

return str

Expand Down Expand Up @@ -109,9 +117,9 @@ def getBestHopFrom(self, src, credits, capacity=None, maxJumps=None, maxLy=None,
if isinstance(src, str):
src = self.tdb.getStation(src)
hop = None
for (destSys, destStn, jumps, ly) in src.getDestinations(maxJumps=maxJumps, maxLy=maxLy, maxLyPer=maxLyPer):
for (destSys, destStn, jumps, ly, via) in src.getDestinations(maxJumps=maxJumps, maxLy=maxLy, maxLyPer=maxLyPer):
load = self.getBestTrade(src, destStn, credits, capacity=capacity)
if load and (not hop or (load[1] > hop.gainCr or (load[1] == hop.gainCr and jumps < hop.jumps))):
if load and (not hop or (load[1] > hop.gainCr or (load[1] == hop.gainCr and len(jumps) < hop.jumps))):
hop = TradeHop(destSys=destSys, destStn=destStn, load=load[0], gainCr=load[1], jumps=jumps, ly=ly)
return hop

Expand All @@ -128,29 +136,40 @@ def getBestHops(self, routes, credits, restrictTo=None, maxJumps=None, maxLy=Non
perJumpLimit = maxJumpsPer if (maxJumpsPer or 0) > 0 else 0
for route in routes:
src = route.route[-1]
if self.debug: print("# route = %s" % route.str())
startCr = credits + int(route.gainCr * safetyMargin)
routeJumps = route.jumps
routeJumps = len(route.jumps)
jumpLimit = perJumpLimit
if (maxJumps or 0) > 0:
jumpLimit = min(maxJumps - routeJumps, perJumpLimit) if perJumpLimit > 0 else maxJumps - routeJumps
if jumpLimit == 0:
if jumpLimit <= 0:
if self.debug: print("Jump Limit")
continue

for (destSys, destStn, jumps, ly) in src.getDestinations(maxJumps=jumpLimit, maxLy=maxLy, maxLyPer=maxLyPer):
if self.debug:
print("#destSys = %s, destStn = %s, jumps = %s, ly = %s" % (destSys.str(), destStn, "->".join([jump.str() for jump in jumps]), ly))
if not destStn in src.stations:
if self.debug: print("#%s is not in my station list" % destStn)
continue
if restrictTo and destStn != restrictTo:
if self.debug: print("#%s doesn't match restrict %s" % (destStn, restrictTo))
continue
if unique and destStn in route.route:
if self.debug: print("#%s is already in the list, not unique" % destStn)
continue
trade = self.getBestTrade(src, destStn, startCr)
if not trade:
if self.debug: print("#* No trade")
continue
dstID = destStn.ID
try:
best = bestToDest[dstID]
if best[1].gainCr + best[2][1] >= route.gainCr + trade[1]:
oldRouteGainCr = best[1].gainCr + best[2][1]
newRouteGainCr = route.gainCr + trade[1]
if oldRouteGainCr >= newRouteGainCr:
continue
except: pass
except KeyError: pass
bestToDest[dstID] = [ destStn, route, trade, jumps, ly ]

result = []
Expand Down
49 changes: 40 additions & 9 deletions tradedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,19 @@ def organizeTrades(self):

def getDestinations(self, maxJumps=None, maxLy=None, maxLyPer=None):
openList, closedList, destStations = Queue(), dict(), []
openList.put([self.system, 0, 0])
openList.put([self.system, [], 0])
maxJumpDist = maxLyPer or sys.maxint
while not openList.empty():
(sys, jumps, dist) = openList.get()
if (maxJumps and jumps > maxJumps) or (maxLy and dist > maxLy):
if maxJumps and len(jumps) > maxJumps:
continue
if maxLy and dist > maxLy:
continue
for stn in sys.stations:
if stn != self:
destStations.append([sys, stn, jumps, dist])
jumps += 1
if (maxJumps and jumps > maxJumps):
destStations.append([sys, stn, list(jumps), dist])
jumps.append(sys)
if (maxJumps and len(jumps) > maxJumps):
continue
for (destSys, destDist) in sys.links.items():
if dist > maxLyPer:
Expand All @@ -110,7 +112,7 @@ def getDestinations(self, maxJumps=None, maxLy=None, maxLyPer=None):
continue
if destSys in closedList:
continue
openList.put([destSys, jumps, dist + destDist])
openList.put([destSys, list(jumps), dist + destDist])
closedList[destSys] = 1
return destStations

Expand All @@ -120,8 +122,9 @@ def __repr__(self):


class TradeDB(object):
def __init__(self, path=r'.\TradeDangerous.accdb'):
def __init__(self, path=r'.\TradeDangerous.accdb', debug=0):
self.path = "Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=" + path
self.debug = debug
self.load()

def load(self, avoiding=[], ignoreLinks=False):
Expand All @@ -131,6 +134,8 @@ def load(self, avoiding=[], ignoreLinks=False):

cur.execute('SELECT system FROM Stations GROUP BY system')
self.systems = { row[0]: System(row[0]) for row in cur }
if self.debug:
print(self.systems)
cur.execute("""SELECT frmSys.system, toSys.system, Links.distLy
FROM Stations AS frmSys, Links, Stations as toSys
WHERE frmSys.ID = Links.from AND toSys.ID = Links.to""")
Expand All @@ -148,7 +153,7 @@ def load(self, avoiding=[], ignoreLinks=False):
""" Populate 'items' from the database """
cur.execute('SELECT id, item FROM Items')
self.items = { row[0]: row[1] for row in cur }
self.itemIDs = { self.items[name]: name for name in self.items }
self.itemIDs = { name: itemID for (itemID, name) in self.items.items() }

stations, items = self.stations, self.items

Expand All @@ -157,14 +162,40 @@ def load(self, avoiding=[], ignoreLinks=False):
' FROM Prices AS src INNER JOIN Prices AS dst ON src.item_id = dst.item_id'
' WHERE src.buy_cr > 0 AND dst.sell_cr > src.buy_cr'
' AND src.ui_order > 0 AND dst.ui_order > 0'
' ORDER BY (dst.sell_cr - src.buy_cr) DESC')
)
for row in cur:
if not (items[row[2]] in avoiding):
stations[row[0]].addTrade(stations[row[1]], items[row[2]], row[2], row[3], row[4])

for station in stations.values():
station.organizeTrades()

if self.debug:
self.validate()

def validate(self):
# Check that things correctly reference themselves.
for (stnID, stn) in self.stations.items():
if self.stations[stn.ID] != stn:
raise ValueError("Station not pointing to self correctly" % stn.station)
for (stnName, stnID) in self.stationIDs.items():
if self.stations[stnID].station.upper() != stnName:
raise ValueError("Station name not pointing to self correctly" % stnName)
for (itemID, item) in self.items.items():
if self.itemIDs[item] != itemID:
raise ValueError("Item %s not pointing to itself correctly" % item, itemID, item, self.itemIDs[item])
# Check that system links are bi-directional
for (name, sys) in self.systems.items():
if not sys.links:
raise ValueError("System %s has no links" % name)
if sys in sys.links:
raise ValueError("System %s has a link to itself!" % name)
if name in sys.links:
raise ValueError("System %s's name occurs in sys.links" % name)
for link in sys.links:
if not sys in link.links:
raise ValueError("System %s does not have a reciprocal link in %s's links" % (name, link.str()))

def getStation(self, name):
if isinstance(name, Station):
return name
Expand Down

0 comments on commit 4697912

Please sign in to comment.