diff --git a/CHANGES.txt b/CHANGES.txt index f7f75b1d..b8d50292 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,15 @@ TradeDangerous, Copyright (C) Oliver "kfsone" Smith, July 2014 ============================================================================== +v6.12.2 Feb 26 2015 +. (kfsone) "run" command: + - added "--direct" option: + - only allows 1 hop, + - --jumps and --ly-per are ignored entirely, + - ignores route/distance between stations, + e.g. + trade.py run --from achenar --to lave --cap 50 --cr 100000 --direct + v6.12.1 Feb 26 2015 . (kfsone) "buy" command: - now accepts multiple arguments (e.g. explosives,clothing), diff --git a/README.txt b/README.txt index 0985602f..eef1b449 100644 --- a/README.txt +++ b/README.txt @@ -213,7 +213,7 @@ RUN sub-command: How many credits to start with e.g. --credits 20000 - + --ly-per N.NN --ly N.NN Maximum distance your ship can jump between systems at full capacity. @@ -272,18 +272,6 @@ RUN sub-command: e.g. --from iBootis --to LiuBese - --start-jumps N - -s N - Considers stations from systems upto this many jumps from your - specified start location. - --from beagle2 --ly-per 7.56 --empty 10.56 -s 2 - - --end-jumps N - -e N - Considers stations from systems upto this many jumps from your - specified destination (--to). - --to lave -e 3 (find runs that end within 3 jumps of lave) - --via Lets you specify a station that must be between the second and final hop. Requires that hops be at least 2. @@ -304,6 +292,24 @@ RUN sub-command: e.g. -jumps-per 5 + --direct + Assumes a single hop and doesn't worry about travel between + source and destination. + e.g. + --from achenar --to lave --direct + + --start-jumps N + -s N + Considers stations from systems upto this many jumps from your + specified start location. + --from beagle2 --ly-per 7.56 --empty 10.56 -s 2 + + --end-jumps N + -e N + Considers stations from systems upto this many jumps from your + specified destination (--to). + --to lave -e 3 (find runs that end within 3 jumps of lave) + Filter options: --max-days-old N.NN diff --git a/commands/run_cmd.py b/commands/run_cmd.py index d348bf14..f6c0e0a0 100644 --- a/commands/run_cmd.py +++ b/commands/run_cmd.py @@ -2,6 +2,7 @@ from commands.commandenv import ResultRow from commands.exceptions import * from commands.parsing import MutuallyExclusiveGroup, ParseArgument +from itertools import chain from formatting import RowFormat, ColumnFormat from tradedb import TradeDB, System, Station, describeAge from tradecalc import TradeCalc, Route @@ -25,12 +26,6 @@ metavar='CR', type=int, ), - ParseArgument('--ly-per', - help='Maximum light years per jump.', - dest='maxLyPer', - metavar='N.NN', - type=float, - ), ] switches = [ @@ -74,11 +69,22 @@ metavar='N', ), ParseArgument('--jumps-per', - help='Maximum number of jumps (system-to-system) per hop.', - default=2, - dest='maxJumpsPer', - metavar='N', - type=int, + help='Maximum number of jumps (system-to-system) per hop.', + default=2, + dest='maxJumpsPer', + metavar='N', + type=int, + ), + ParseArgument('--direct', + help="Assume destinations are reachable without worrying " + "about jumps.", + action='store_true', + ), + ParseArgument('--ly-per', + help='Maximum light years per jump.', + dest='maxLyPer', + metavar='N.NN', + type=float, ), ParseArgument('--empty-ly', help='Maximum light years ship can jump when empty.', @@ -424,6 +430,14 @@ def checkAnchorNotInVia(hops, anchorName, place, viaSet): def checkStationSuitability(cmdenv, station, src=None): + if station.market == 'N': + if src: + raise CommandLineError( + "{} station {} is flagged as having no market".format( + src, station.name() + ) + ) + return False if not station.itemCount: if src: raise NoDataError( @@ -478,9 +492,7 @@ def filterStationSet(src, cmdenv, stnList): if not stnList: return stnList filtered = [ - place for place in stnList - if not (isinstance(place, Station) and \ - not checkStationSuitability(cmdenv, place)) + place for place in stnList if checkStationSuitability(cmdenv, place) ] if not stnList: raise CommandLineError( @@ -506,14 +518,17 @@ def validateRunArguments(tdb, cmdenv): if cmdenv.hops < 1: raise CommandLineError("Minimum of 1 hop required") - if cmdenv.hops > 64: + if cmdenv.hops > 32: raise CommandLineError("Too many hops without more optimization") if cmdenv.maxJumpsPer < 0: raise CommandLineError("Negative jumps: you're already there?") + if cmdenv.direct: + cmdenv.hops = 1 if cmdenv.origPlace: if isinstance(cmdenv.origPlace, System): + cmdenv.DEBUG0("origPlace: System: {}", cmdenv.origPlace.name()) if not cmdenv.origPlace.stations: raise CommandLineError( "No stations at --from system, {}" @@ -525,6 +540,7 @@ def validateRunArguments(tdb, cmdenv): if checkStationSuitability(cmdenv, station) ] else: + cmdenv.DEBUG0("origPlace: Station: {}", cmdenv.origPlace.name()) checkStationSuitability(cmdenv, cmdenv.origPlace, '--from') cmdenv.origins = [ cmdenv.origPlace ] cmdenv.startStation = cmdenv.origPlace @@ -539,6 +555,7 @@ def validateRunArguments(tdb, cmdenv): cmdenv.origins, cmdenv.startJumps ) else: + cmdenv.DEBUG0("using all suitable origins") cmdenv.origins = [ station for station in tdb.stationByID.values() @@ -550,13 +567,19 @@ def validateRunArguments(tdb, cmdenv): cmdenv.destinations = None if cmdenv.destPlace: if isinstance(cmdenv.destPlace, Station): + cmdenv.DEBUG0("destPlace: Station: {}", cmdenv.destPlace.name()) checkStationSuitability(cmdenv, cmdenv.destPlace, '--to') cmdenv.destinations = [ cmdenv.destPlace ] - elif isinstance(cmdenv.destPlace, System): - cmdenv.destinations = [ cmdenv.destPlace ] + else: + cmdenv.DEBUG0("destPlace: System: {}", cmdenv.destPlace.name()) + cmdenv.destinations = [ + station + for station in cmdenv.destPlace.stations + if checkStationSuitability(cmdenv, station) + ] cmdenv.destinations = expandForJumps( tdb, cmdenv, - [ cmdenv.destPlace ], + cmdenv.destinations, cmdenv.endJumps, "--to" ) @@ -565,6 +588,7 @@ def validateRunArguments(tdb, cmdenv): cmdenv.destinations, cmdenv.endJumps ) else: + cmdenv.DEBUG0("Using all available destinations") if cmdenv.endJumps: raise CommandLineError("--end-jumps (-e) only works with --to") if cmdenv.goalSystem: @@ -572,6 +596,11 @@ def validateRunArguments(tdb, cmdenv): raise CommandLineError("--towards requires --from") dest = tdb.lookupPlace(cmdenv.goalSystem) cmdenv.goalSystem = dest.system + cmdenv.destinations = [ + station + for station in tdb.stationByID.values() + if checkStationSuitability(cmdenv, station) + ] origins, destns = cmdenv.origins or [], cmdenv.destinations or [] @@ -628,7 +657,7 @@ def validateRunArguments(tdb, cmdenv): if cmdenv.capacity is None: raise CommandLineError("Missing '--capacity'") - if cmdenv.maxLyPer is None: + if cmdenv.maxLyPer is None and not cmdenv.direct: raise CommandLineError("Missing '--ly-per'") if cmdenv.capacity < 0: raise CommandLineError("Invalid (negative) cargo capacity") @@ -733,6 +762,7 @@ def run(results, cmdenv, tdb): avoidPlaces = cmdenv.avoidPlaces stopStations = cmdenv.destinations goalSystem = cmdenv.goalSystem + maxLs = cmdenv.maxLsFromStar startCr = cmdenv.credits - cmdenv.insurance @@ -788,20 +818,28 @@ def run(results, cmdenv, tdb): newRoutes = calc.getBestHops(routes, restrictTo=restrictTo) if not newRoutes and hopNo > 0: if restrictTo: - restrictions = list(restrictTo) - restrictSystems = list(set([ - place if isinstance(place, System) else place.system + restrictTo = set(chain.from_iterable( + [place] if isinstance(place, Station) else place.stations for place in restrictTo - ])) - if len(restrictions) == 1: - dests = restrictions[0].name() - elif len(restrictSystems) == 1: - dests = restrictSystems[0].name() + )) + if not maxLs: + lsCheck = lambda stn: True else: - dests = ", ".join([ - place.name() for place in restrictions[0:-1] - ]) - dests += " or " + restrictions[-1].name() + lsCheck = lambda stn: \ + stn.maxLsFromStar > 0 and \ + stn.maxLsFromStar < maxLs + restrictTo = set( + stn for stn in restrictTo + if stn not in avoidPlaces + and stn.system not in avoidPlaces + and stn.checkPadSize(maxPadSize) + and lsCheck(stn) + ) + dests = ", ".join([ + place.name() for place in restrictTo[0:-1] + ]) + if len(restrictTo) > 1: + dests += " or " + restrictTo[-1].name() results.summary.exception += ( "SORRY: Could not find any routes that " "delivered a profit to {} at hop #{}\n" diff --git a/tradecalc.py b/tradecalc.py index a1d84ec4..ac9ee28d 100644 --- a/tradecalc.py +++ b/tradecalc.py @@ -39,6 +39,7 @@ from collections import defaultdict from collections import namedtuple from tradedb import System, Station, Trade, TradeDB, describeAge +from tradedb import Destination from tradeexcept import TradeException import locale @@ -721,6 +722,8 @@ def getBestHops(self, routes, restrictTo=None): assert not restrictTo or isinstance(restrictTo, set) maxJumpsPer = tdenv.maxJumpsPer maxLyPer = tdenv.maxLyPer + maxPadSize = tdenv.padSize + maxLsFromStar = tdenv.maxLsFromStar or float('inf') reqBlackMarket = getattr(tdenv, 'blackMarket', False) or False maxAge = getattr(tdenv, 'maxAge') or 0 credits = tdenv.credits - (getattr(tdenv, 'insurance', 0) or 0) @@ -746,6 +749,40 @@ def getBestHops(self, routes, restrictTo=None): restrictStations.update(place.stations) restrictStations = set(restrictStations) + # Are we doing direct routes? + if tdenv.direct: + if goalSystem and not restrictTo: + restrictTo = [ goalSystem ] + restrictStations = set(goalSystem.stations) + if avoidPlaces: + restrictStations = set( + stn for stn in restrictStations + if stn not in avoidPlaces and \ + stn.system not in avoidPlaces + ) + def _get_destinations(srcStation): + srcSys = srcStation.system + srcDist = srcSys.distanceTo + for stn in restrictStations: + stnSys = stn.system + yield Destination( + stnSys, stn, + [srcSys, stnSys], + srcDist(stnSys) + ) + else: + def _get_destinations(srcStation): + yield from tdb.getDestinations( + srcStation, + maxJumps=maxJumpsPer, + maxLyPer=maxLyPer, + avoidPlaces=avoidPlaces, + maxPadSize=maxPadSize, + maxLsFromStar=maxLsFromStar, + ) + + station_iterator = _get_destinations + prog = pbar.Progress(len(routes), 25) for route in routes: if tdenv.progress: @@ -829,14 +866,7 @@ def considerStation(dstStation, dest, multiplier): dstStation, route, trade, dest.via, dest.distLy, score ] - for dest in tdb.getDestinations( - srcStation, - maxJumps=maxJumpsPer, - maxLyPer=maxLyPer, - avoidPlaces=avoidPlaces, - maxPadSize=tdenv.padSize, - maxLsFromStar=tdenv.maxLs, - ): + for dest in station_iterator(srcStation): dstStation = dest.station if dstStation is srcStation: continue @@ -852,15 +882,6 @@ def considerStation(dstStation, dest, multiplier): if stnDataAge is None or stnDataAge > maxAge: continue - if tdenv.debug >= 1: - tdenv.DEBUG1( - "destSys {}, destStn {}, jumps {}, distLy {}", - dstStation.system.dbname, - dstStation.dbname, - "->".join([jump.str() for jump in dest.via]), - dest.distLy - ) - multiplier = 1.0 if restrictStations: if dstStation not in restricting: @@ -887,6 +908,15 @@ def considerStation(dstStation, dest, multiplier): # will be, so the larger the remainder. multiplier *= 1 + (srcGoalDist / dstGoalDist) + if tdenv.debug >= 1: + tdenv.DEBUG1( + "destSys {}, destStn {}, jumps {}, distLy {}", + dstStation.system.dbname, + dstStation.dbname, + "->".join([jump.str() for jump in dest.via]), + dest.distLy + ) + considerStation(dstStation, dest, multiplier) if restrictStations: