X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=sst;h=f3d7e31cd52e0cd827243f55ae29c730205bccc2;hb=c7581ac8e9ed615651305824a41c2136629429d2;hp=e0784217561cd5239a2709e5e38131666203519f;hpb=067b4f263065939c1ba4fed037124ed076f69f98;p=super-star-trek.git diff --git a/sst b/sst index e078421..f3d7e31 100755 --- a/sst +++ b/sst @@ -11,15 +11,14 @@ Stas Sergeev, and Eric S. Raymond. See the doc/HACKING file in the distribution for designers notes and advice on how to modify (and how not to modify!) this code. """ -from __future__ import print_function, division -# Runs under Python 2 an Python 3. Preserve this property! +# Copyright by Eric S. Raymond # SPDX-License-Identifier: BSD-2-clause # pylint: disable=line-too-long,superfluous-parens,too-many-lines,invalid-name,missing-function-docstring,missing-class-docstring,multiple-statements,too-many-branches,too-many-statements,too-many-locals,too-many-nested-blocks,too-many-return-statements,too-many-instance-attributes,global-statement,no-else-break,no-else-return,no-else-continue,too-few-public-methods,too-many-boolean-expressions,consider-using-f-string,consider-using-enumerate,consider-using-with,unspecified-encoding # pylint: disable=multiple-imports import os, sys, math, curses, time, pickle, copy, gettext, getpass -import getopt, socket, locale +import getopt, socket import codecs # This import only works on Unixes. The intention is to enable @@ -27,15 +26,9 @@ import codecs try: # pylint: disable=unused-import import readline -except ImportError: +except ImportError: # pragma: no cover pass -# Prevent lossage under Python 3 -try: - my_input = raw_input -except NameError: - my_input = input - version = "2.7" docpath = (".", "doc/", "/usr/share/doc/sst/") @@ -44,8 +37,8 @@ def _(st): return gettext.gettext(st) # Rolling our own LCG because Python changed its incompatibly in 3.2. -# Thus, we need to have our own to be 2/3 polyglot, which will also -# be helpful when we forwrard-port. +# Thus, we needed to have our own to be 2/3 polyglot; it will be +# helpful when and if we ever forward-port to another language. class randomizer: # LCG PRNG parameters tested against @@ -95,6 +88,14 @@ class randomizer: # logfp.write("#seed(%d)\n" % n) game.lcg_x = n % randomizer.LCG_M + @staticmethod + def getrngstate(): + return game.lcg_x + + @staticmethod + def setrngstate(n): + game.lcg_x = n + GALSIZE = 8 # Galaxy size in quadrants NINHAB = (GALSIZE * GALSIZE // 2) # Number of inhabited worlds MAXUNINHAB = 10 # Maximum uninhabited worlds @@ -108,7 +109,7 @@ FOREVER = 1e30 # Time for the indefinite future MAXBURST = 3 # Max # of torps you can launch in one turn MINCMDR = 10 # Minimum number of Klingon commanders DOCKFAC = 0.25 # Repair faster when docked -PHASEFAC = 2.0 # Unclear what this is, it was in the C version +PHASEFAC = 2.0 # Phaser attenuation factor ALGERON = 2311 # Date of the Treaty of Algeron @@ -137,6 +138,9 @@ class TrekError(Exception): class JumpOut(Exception): pass +def letterize(n): + return chr(ord('a') + n - 1) + class Coord: def __init__(self, x=None, y=None): self.i = x # Row @@ -159,17 +163,17 @@ class Coord: return Coord(self.i*other, self.j*other) def __rmul__(self, other): return Coord(self.i*other, self.j*other) - def __div__(self, other): + def __div__(self, other): # pragma: no cover return Coord(self.i/other, self.j/other) - def __truediv__(self, other): + def __truediv__(self, other): # pragma: no cover return Coord(self.i/other, self.j/other) - def __floordiv__(self, other): + def __floordiv__(self, other): # pragma: no cover return Coord(self.i//other, self.j//other) def __mod__(self, other): return Coord(self.i % other, self.j % other) - def __rtruediv__(self, other): + def __rtruediv__(self, other): # pragma: no cover return Coord(self.i/other, self.j/other) - def __rfloordiv__(self, other): + def __rfloordiv__(self, other): # pragma: no cover return Coord(self.i//other, self.j//other) def roundtogrid(self): return Coord(int(round(self.i)), int(round(self.j))) @@ -199,26 +203,23 @@ class Coord: return self.roundtogrid() // QUADSIZE def sector(self): return self.roundtogrid() % QUADSIZE - def scatter(self): - s = Coord() - s.i = self.i + rnd.integer(-1, 2) - s.j = self.j + rnd.integer(-1, 2) - return s def __str__(self): if self.i is None or self.j is None: - return "Nowhere" + return "Nowhere" # pragma: no cover + if (game.options & OPTION_ALPHAMERIC): + return letterize(self.i + 1) + str(self.j + 1) return "%s - %s" % (self.i+1, self.j+1) __repr__ = __str__ -class Thingy(Coord): +class Thingy(): "Do not anger the Space Thingy!" def __init__(self): - Coord.__init__(self) + self.location = Coord() self.angered = False def angry(self): self.angered = True def at(self, q): - return (q.i, q.j) == (self.i, self.j) + return (q.i, q.j) == (self.location.i, self.location.j) class Planet: def __init__(self): @@ -242,7 +243,7 @@ class Quadrant: self.charted = False self.status = "secure" # Could be "secure", "distressed", "enslaved" def __str__(self): - return "" % self.__dict__ + return "" % self.__dict__ # pragma: no cover __repr__ = __str__ class Page: @@ -251,7 +252,7 @@ class Page: self.starbase = False self.klingons = None def __repr__(self): - return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars) + return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars) # pragma: no cover def fill2d(size, fillfun): "Fill an empty list in 2D." @@ -315,6 +316,7 @@ OPTION_PLAIN = 0x01000000 # user chose plain game OPTION_ALMY = 0x02000000 # user chose Almy variant OPTION_COLOR = 0x04000000 # enable color display (ESR, 2010) OPTION_DOTFILL = 0x08000000 # fix dotfill glitch in chart (ESR, 2019) +OPTION_ALPHAMERIC = 0x10000000 # Alpha Y coordinates (ESR, 2023) option_names = { "ALL": OPTION_ALL, @@ -389,7 +391,7 @@ NEVENTS = 12 # Abstract out the event handling -- underlying data structures will change # when we implement stateful events -def findevent(evtype): +def findevent(evtype): # pragma: no cover return game.future[evtype] class Enemy: @@ -422,7 +424,7 @@ class Enemy: game.enemies.remove(self) return motion def __repr__(self): - return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging + return "<%s,%s,%f>" % (self.type, self.location, self.power) # pragma: no cover class Gamestate: def __init__(self): @@ -774,7 +776,7 @@ def movebaddy(enemy): def moveklings(): "Sequence Klingon tactical movement." if game.idebug: - prout("== MOVCOM") + prout("== MOVCOM") # pragma: no cover # Figure out which Klingon is the commander (or Supercommander) # and do move tacmoves = [] @@ -974,7 +976,7 @@ def movetholian(): elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0: tid.i = 0 tid.j = 0 - else: + else: # pragma: no cover # something is wrong! game.tholian.move(None) prout("***Internal error: Tholian in a bad spot.") @@ -1227,10 +1229,10 @@ def randdevice(): wsum += w if idx < wsum: return i - return None # we should never get here + return None # pragma: no cover def collision(rammed, enemy): - "Collision handling for rammong events." + "Collision handling for ramming events." prouts(_("***RED ALERT! RED ALERT!")) skip(1) prout(_("***COLLISION IMMINENT.")) @@ -1247,12 +1249,12 @@ def collision(rammed, enemy): proutn(_(" (original position)")) skip(1) deadkl(enemy.location, enemy.type, game.sector) - proutn("***" + crmshp() + " heavily damaged.") + prout("***" + crmshp() + " heavily damaged.") icas = rnd.integer(10, 30) prout(_("***Sickbay reports %d casualties") % icas) game.casual += icas game.state.crew -= icas - # In the pre-SST2K version, all devices got equiprobably damaged, + # In the pre-SST2K versions, all devices got equiprobably damaged, # which was silly. Instead, pick up to half the devices at # random according to our weighting table, ncrits = rnd.integer(NDEVICES//2) @@ -1371,7 +1373,7 @@ def torpedo(origin, bearing, dispersion, number, nburst): tenemy.kdist = tenemy.kavgd = (game.sector-tenemy.location).distance() sortenemies() break - else: + else: # pragma: no cover prout("Internal error, no enemy where expected!") raise SystemExit(1) return None @@ -1522,7 +1524,7 @@ def attack(torps_ok): chgfac = 1.0 where = "neither" if game.idebug: - prout("=== ATTACK!") + prout("=== ATTACK!") # pragma: no cover # Tholian gets to move before attacking if game.tholian: movetholian() @@ -2376,7 +2378,7 @@ def events(): game.isatb = 0 else: game.battle.invalidate() - if game.idebug: + if game.idebug: # pragma: no cover prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim)) for i in range(1, NEVENTS): if i == FSNOVA: proutn("=== Supernova ") @@ -2406,7 +2408,7 @@ def events(): if game.future[l].date < datemin: evcode = l if game.idebug: - prout("== Event %d fires" % evcode) + prout("== Event %d fires" % evcode) # pragma: no cover datemin = game.future[l].date xtime = datemin-game.state.date if game.iscloaked: @@ -2608,7 +2610,7 @@ def events(): break else: # can't seem to find one; ignore this call - if game.idebug: + if game.idebug: # pragma: no cover prout("=== Couldn't find location for distress event.") continue # got one!! Schedule its enslavement @@ -2894,7 +2896,7 @@ def supernova(w): break if num <=0: break - if game.idebug: + if game.idebug: # pragma: no cover proutn("=== Super nova here?") if ja(): nq = game.quadrant @@ -3173,13 +3175,14 @@ def finish(ifin): prout(_("Your starship is now an expanding cloud of subatomic particles")) elif ifin == FMATERIALIZE: prout(_("Starbase was unable to re-materialize your starship.")) - prout(_("Sic transit gloria mundi")) + prout(_("Sic transit gloria mundi.")) elif ifin == FPHASER: prout(_("The %s has been cremated by its own phasers.") % crmshp()) elif ifin == FLOST: prout(_("You and your landing party have been")) prout(_("converted to energy, dissipating through space.")) elif ifin == FMINING: + # This does not seem to be reachable from any code path. prout(_("You are left with your landing party on")) prout(_("a wild jungle planet inhabited by primitive cannibals.")) skip(1) @@ -3192,6 +3195,7 @@ def finish(ifin): prout(_("That was a great shot.")) skip(1) elif ifin == FSSC: + # This does not seem to be reachable from any code path. prout(_("The Galileo is instantly annihilated by the supernova.")) prout(_("You and your mining party are atomized.")) skip(1) @@ -3411,11 +3415,6 @@ curwnd = None def iostart(): global stdscr, rows - # for some recent versions of python2, the following enables UTF8 - # for the older ones we probably need to set C locale, and python3 - # has no problems at all - if sys.version_info[0] < 3: - locale.setlocale(locale.LC_ALL, "") gettext.bindtextdomain("sst", "/usr/local/share/locale") gettext.textdomain("sst") if not (game.options & OPTION_CURSES): @@ -3424,7 +3423,7 @@ def iostart(): rows = ln_env else: rows = 25 - else: + else: # pragma: no cover stdscr = curses.initscr() stdscr.keypad(True) curses.nonl() @@ -3455,7 +3454,7 @@ def iostart(): def ioend(): "Wrap up I/O." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover stdscr.keypad(False) curses.echo() curses.nocbreak() @@ -3463,7 +3462,7 @@ def ioend(): def waitfor(): "Wait for user action -- OK to do nothing if on a TTY" - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover stdscr.getch() def announce(): @@ -3477,7 +3476,7 @@ def pause_game(): else: prompt = _("[PRESS ENTER TO CONTINUE]") - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover drawmaps(0) setwnd(prompt_window) prompt_window.clear() @@ -3491,14 +3490,14 @@ def pause_game(): sys.stdout.write('\n') proutn(prompt) if not replayfp: - my_input() + input() sys.stdout.write('\n' * rows) linecount = 0 def skip(i): "Skip i lines. Pause game if this would cause a scrolling event." for _dummy in range(i): - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover (y, _x) = curwnd.getyx() try: curwnd.move(y+1, 0) @@ -3514,7 +3513,7 @@ def skip(i): def proutn(proutntline): "Utter a line with no following line feed." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover (y, x) = curwnd.getyx() (my, _mx) = curwnd.getmaxyx() if curwnd == message_window and y >= my - 2: @@ -3538,7 +3537,7 @@ def prouts(proutsline): if not replayfp or replayfp.closed: # Don't slow down replays time.sleep(0.03) proutn(c) - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover curwnd.refresh() else: sys.stdout.flush() @@ -3547,7 +3546,7 @@ def prouts(proutsline): def cgetline(): "Get a line of input." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover linein = codecs.decode(curwnd.getstr()) + "\n" curwnd.refresh() else: @@ -3555,7 +3554,7 @@ def cgetline(): while True: linein = replayfp.readline() proutn(linein) - if linein == '': + if linein == '': # pragma: no cover prout("*** Replay finished") replayfp.close() break @@ -3563,7 +3562,7 @@ def cgetline(): break else: try: - linein = my_input() + "\n" + linein = input() + "\n" except EOFError: prout("") sys.exit(0) @@ -3574,7 +3573,7 @@ def cgetline(): def setwnd(wnd): "Change windows -- OK for this to be a no-op in tty mode." global curwnd - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover if game.cdebug and logfp: if wnd == fullscreen_window: legend = "fullscreen" @@ -3602,21 +3601,21 @@ def setwnd(wnd): def clreol(): "Clear to end of line -- can be a no-op in tty mode" - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover curwnd.clrtoeol() curwnd.refresh() def clrscr(): "Clear screen -- can be a no-op in tty mode." global linecount - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover curwnd.clear() curwnd.move(0, 0) curwnd.refresh() linecount = 0 def textcolor(color=DEFAULT): - if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): + if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): # pragma: no cover if color == DEFAULT: curwnd.attrset(0) elif color == BLACK: @@ -3653,7 +3652,7 @@ def textcolor(color=DEFAULT): curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD) def highvideo(): - if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): + if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): # pragma: no cover curwnd.attron(curses.A_REVERSE) # @@ -3662,7 +3661,7 @@ def highvideo(): def drawmaps(mode): "Hook to be called after moving to redraw maps." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover if mode == 1: sensor() setwnd(srscan_window) @@ -3681,7 +3680,7 @@ def drawmaps(mode): lrscan_window.move(0, 0) lrscan(silent=False) -def put_srscan_sym(w, sym): +def put_srscan_sym(w, sym): # pragma: no cover "Emit symbol for short-range scan." srscan_window.move(w.i+1, w.j*2+2) srscan_window.addch(sym) @@ -3689,7 +3688,7 @@ def put_srscan_sym(w, sym): def boom(w): "Enemy fall down, go boom." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover drawmaps(0) setwnd(srscan_window) srscan_window.attron(curses.A_REVERSE) @@ -3704,12 +3703,12 @@ def boom(w): def warble(): "Sound and visual effects for teleportation." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover drawmaps(2) setwnd(message_window) #sound(50) prouts(" . . . . . ") - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover #curses.delay_output(1000) #nosound() pass @@ -3727,7 +3726,7 @@ def tracktorpedo(w, step, i, n, iquad): elif step in {4, 9}: skip(1) proutn("%s " % w) - else: + else: # pragma: no cover if not damaged(DSRSENS) or game.condition=="docked": if i != 0 and step == 1: drawmaps(2) @@ -3751,7 +3750,7 @@ def tracktorpedo(w, step, i, n, iquad): def makechart(): "Display the current galaxy chart." - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover setwnd(message_window) message_window.clear() chart() @@ -3762,14 +3761,14 @@ NSYM = 14 def prstat(txt, data): proutn(txt) - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover skip(1) setwnd(status_window) else: proutn(" " * (NSYM - len(txt))) proutn(data) skip(1) - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover setwnd(report_window) # Code from moving.c begins here @@ -3842,12 +3841,12 @@ def imove(icourse=None, noattack=False): stopegy = 50.0*icourse.distance/game.optime if iquad in ('T', 'K', 'C', 'S', 'R', '?'): for enemy in game.enemies: - if enemy.location == game.sector: + if enemy.location == h: collision(rammed=False, enemy=enemy) return True # This should not happen - prout(_("Which way did he go?")) - return False + prout(_("Which way did he go?")) # pragma: no cover + return False # pragma: no cover elif iquad == ' ': skip(1) prouts(_("***RED ALERT! RED ALERT!")) @@ -4589,7 +4588,7 @@ def mayday(): break prout(_("fails.")) textcolor(DEFAULT) - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover curses.delay_output(500) if m > 3: game.quad[game.sector.i][game.sector.j]='?' @@ -5305,8 +5304,8 @@ def damagereport(): for i in range(NDEVICES): if damaged(i): if not jdam: - prout(_("\tDEVICE\t\t\t-REPAIR TIMES-")) - prout(_("\t\t\tIN FLIGHT\t\tDOCKED")) + prout(_("DEVICE REPAIR TIMES")) + prout(_(" IN FLIGHT DOCKED")) jdam = True prout(" %-26s\t%8.2f\t\t%8.2f" % (device[i], game.damage[i]+0.05, @@ -5339,7 +5338,10 @@ def chart(): prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart))) prout(" 1 2 3 4 5 6 7 8") for i in range(GALSIZE): - proutn("%d |" % (i+1)) + if (game.options & OPTION_ALPHAMERIC): + proutn("%c |" % letterize(i+1)) + else: + proutn("%d |" % (i+1)) for j in range(GALSIZE): if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j: proutn("<") @@ -5369,7 +5371,7 @@ def sectscan(goodScan, i, j): if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1): if game.quad[i][j] in ('E', 'F'): if game.iscloaked: - highvideo() + highvideo() # pragma: no cover textcolor({"green":GREEN, "yellow":YELLOW, "red":RED, @@ -5478,7 +5480,10 @@ def srscan(): if game.condition != "docked": newcnd() for i in range(QUADSIZE): - proutn("%2d " % (i+1)) + if (game.options & OPTION_ALPHAMERIC): + proutn("%c " % letterize(i+1)) + else: + proutn("%2d " % (i+1)) for j in range(QUADSIZE): sectscan(goodScan, i, j) skip(1) @@ -5627,11 +5632,11 @@ def goptions(): else: prout(_("No such option as ") + scanner.token) if mode == "set": - if (not (game.options & OPTION_CURSES)) and (changemask & OPTION_CURSES): + if (not (game.options & OPTION_CURSES)) and (changemask & OPTION_CURSES): # pragma: no cover iostart() game.options |= changemask elif mode == "clear": - if (game.options & OPTION_CURSES) and (not (changemask & OPTION_CURSES)): + if (game.options & OPTION_CURSES) and (not (changemask & OPTION_CURSES)): # pragma: no cover ioend() game.options &=~ changemask prout(_("Acknowledged, Captain.")) @@ -5933,12 +5938,17 @@ def setup(): # Place thing (in tournament game, we don't want one!) # New in SST2K: never place the Thing near a starbase. # This makes sense and avoids a special case in the old code. - global thing if game.tourn is None: + # Avoid distrubing the RNG chain. This code + # was a late fix and we don't want to mess up + # all the regression tests. + state = randomizer.getrngstate() while True: - thing = randplace(GALSIZE) - if thing not in game.state.baseq: + thing.location = randplace(GALSIZE) + # Put it somewhere a starbase is not + if thing.location not in game.state.baseq: break + randomizer.setrngstate(state) skip(2) game.state.snap = False if game.skill == SKILL_NOVICE: @@ -5974,7 +5984,7 @@ def setup(): clrscr() setwnd(message_window) newqad() - if len(game.enemies) - (thing == game.quadrant) - (game.tholian is not None): + if len(game.enemies) - (thing.location == game.quadrant) - (game.tholian is not None): game.shldup = True if game.neutz: # bad luck to start in a Romulan Neutral Zone attack(torps_ok=False) @@ -5982,7 +5992,8 @@ def setup(): def choose(): "Choose your game type." while True: - game.tourn = game.length = 0 + game.tourn = None + game.length = 0 game.thawed = False game.skill = SKILL_NONE # Do not chew here, we want to use command-line tokens @@ -5990,6 +6001,7 @@ def choose(): proutn(_("Would you like a regular, tournament, or saved game? ")) scanner.nexttok() if scanner.sees("tournament"): + game.tourn = 0 while scanner.nexttok() == "IHEOL": proutn(_("Type in tournament number-")) if scanner.real == 0: @@ -6050,18 +6062,18 @@ def choose(): scanner.nexttok() if scanner.sees("plain"): # Approximates the UT FORTRAN version. - game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE | OPTION_CLOAK | OPTION_DOTFILL) + game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE | OPTION_CLOAK | OPTION_DOTFILL | OPTION_ALPHAMERIC) game.options |= OPTION_PLAIN elif scanner.sees("almy"): # Approximates Tom Almy's version. - game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_DOTFILL) + game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_DOTFILL | OPTION_ALPHAMERIC) game.options |= OPTION_ALMY elif scanner.sees("fancy") or scanner.sees("\n"): pass elif len(scanner.token): proutn(_("What is \"%s\"?") % scanner.token) setpassword() - if game.passwd == "debug": + if game.passwd == "debug": # pragma: no cover game.idebug = True prout("=== Debug mode enabled.") # Use parameters to generate initial values of things @@ -6177,7 +6189,7 @@ def newqad(): prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE.")) prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!")) # Put in THING if needed - if thing == game.quadrant: + if thing.location == game.quadrant: Enemy(etype='?', loc=dropin(), power=rnd.real(6000,6500.0)+250.0*game.skill) if not damaged(DSRSENS): @@ -6236,6 +6248,7 @@ def setpassword(): proutn(_("Please type in a secret password- ")) scanner.nexttok() game.passwd = scanner.token + #game.passwd = getpass.getpass("Please type in a secret password- ") if game.passwd is not None: break else: @@ -6294,8 +6307,10 @@ commands = [ ("", 0), ] -def listCommands(): +def listCommands(): # pragma: no cover "Generate a list of legal commands." + # Coverage-disabled because testing for this is fragile + # in the presence of changes in the command set. prout(_("LEGAL COMMANDS ARE:")) emitted = 0 for (key, opt) in commands: @@ -6377,7 +6392,7 @@ def makemoves(): clrscr() proutn("COMMAND> ") if scanner.nexttok() == "IHEOL": - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover makechart() continue elif scanner.token == "": @@ -6396,14 +6411,14 @@ def makemoves(): if cmd == scanner.token.upper() or (not abandon_passed \ and cmd.startswith(scanner.token.upper())): break - if cmd == "": + if cmd == "": # pragma: no cover listCommands() continue elif opt and not (opt & game.options): huh() else: break - if game.options & OPTION_CURSES: + if game.options & OPTION_CURSES: # pragma: no cover prout("COMMAND> %s" % cmd) if cmd == "SRSCAN": # srscan srscan() @@ -6511,7 +6526,7 @@ def makemoves(): helpme() # get help elif cmd == "SCORE": score() # see current score - elif cmd == "CURSES": + elif cmd == "CURSES": # pragma: no cover game.options |= (OPTION_CURSES | OPTION_COLOR) iostart() elif cmd == "OPTIONS": @@ -6554,7 +6569,7 @@ def cramen(ch): elif ch == '#': s = _("Tholian web") elif ch == '?': s = _("Stranger") elif ch == '@': s = _("Inhabited World") - else: s = "Unknown??" + else: s = "Unknown??" # pragma: no cover return s def crmena(loud, enemy, loctype, w): @@ -6646,6 +6661,15 @@ class sstscanner: def getcoord(self): s = Coord() self.nexttok() + if (game.options & OPTION_ALPHAMERIC): + try: + if (self.type == "IHALPHA") and (self.token[0] in "abcdefghij") and (self.token[1] in "0123456789"): + s.i = ord(self.token[0]) - ord("a") + s.j = int(self.token[1:])-1 + return s + except (TypeError, IndexError): + huh() + return None if self.type != "IHREAL": huh() return None @@ -6656,7 +6680,7 @@ class sstscanner: return None s.j = self.int()-1 return s - def __repr__(self): + def __repr__(self): # pragma: no cover return "" % (self.token, self.type, self.inqueue) def ja(): @@ -6677,7 +6701,7 @@ def huh(): skip(1) prout(_("Beg your pardon, Captain?")) -def debugme(): +def debugme(): # pragma: no cover "Access to the internals for debugging." proutn("Reset levels? ") if ja(): @@ -6774,55 +6798,59 @@ if __name__ == '__main__': logfp = None game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY) if os.getenv("TERM"): - game.options |= OPTION_CURSES + game.options |= OPTION_CURSES # pragma: no cover else: game.options |= OPTION_TTY seed = int(time.time()) - (options, arguments) = getopt.getopt(sys.argv[1:], "cr:s:txV") - for (switch, val) in options: - if switch == '-r': - # pylint: disable=raise-missing-from - try: - replayfp = open(val, "r") - except IOError: - sys.stderr.write("sst: can't open replay file %s\n" % val) - raise SystemExit(1) - # pylint: disable=raise-missing-from - try: - line = replayfp.readline().strip() - (leader, __, seed) = line.split() - # pylint: disable=eval-used - seed = eval(seed) - line = replayfp.readline().strip() - arguments += line.split()[2:] - except ValueError: - sys.stderr.write("sst: replay file %s is ill-formed\n"% val) + try: + (options, arguments) = getopt.getopt(sys.argv[1:], "cr:s:txV") + for (switch, val) in options: + if switch == '-r': + # pylint: disable=raise-missing-from + try: + replayfp = open(val, "r") + except IOError: + sys.stderr.write("sst: can't open replay file %s\n" % val) + raise SystemExit(1) + # pylint: disable=raise-missing-from + try: + line = replayfp.readline().strip() + (leader, __, seed) = line.split() + # pylint: disable=eval-used + seed = eval(seed) + line = replayfp.readline().strip() + arguments += line.split()[2:] + except ValueError: # pragma: no cover + sys.stderr.write("sst: replay file %s is ill-formed\n"% val) + raise SystemExit(1) + game.options |= OPTION_TTY + game.options &=~ OPTION_CURSES + elif switch == '-s': # pragma: no cover + seed = int(val) + elif switch == '-t': # pragma: no cover + game.options |= OPTION_TTY + game.options &=~ OPTION_CURSES + elif switch == '-x': # pragma: no cover + game.idebug = True + elif switch == '-c': # Enable curses debugging - undocumented + game.cdebug = True + elif switch == '-V': # pragma: no cover + print("SST2K", version) + raise SystemExit(0) + else: # pragma: no cover + sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n") raise SystemExit(1) - game.options |= OPTION_TTY - game.options &=~ OPTION_CURSES - elif switch == '-s': - seed = int(val) - elif switch == '-t': - game.options |= OPTION_TTY - game.options &=~ OPTION_CURSES - elif switch == '-x': - game.idebug = True - elif switch == '-c': # Enable curses debugging - undocumented - game.cdebug = True - elif switch == '-V': - print("SST2K", version) - raise SystemExit(0) - else: - sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n") - raise SystemExit(1) + except getopt.GetoptError as e: + print(e) + raise SystemExit(1) # where to save the input in case of bugs - if "TMPDIR" in os.environ: + if "TMPDIR" in os.environ: # pragma: no cover tmpdir = os.environ['TMPDIR'] else: tmpdir = "/tmp" try: logfp = open(os.path.join(tmpdir, "sst-input.log"), "w") - except IOError: + except IOError: # pragma: no cover sys.stderr.write("sst: warning, can't open logfile\n") sys.exit(1) if logfp: @@ -6850,7 +6878,8 @@ if __name__ == '__main__': if replayfp: break skip(1) - stars() + if (game.options & OPTION_TTY): + stars() skip(1) if game.tourn and game.alldone: proutn(_("Do you want your score recorded?")) @@ -6859,15 +6888,18 @@ if __name__ == '__main__': scanner.push("\n") freeze(False) scanner.chew() - proutn(_("Do you want to play again? ")) - if not ja(): + if (game.options & OPTION_TTY): + proutn(_("Do you want to play again? ")) + if not ja(): + break + else: break skip(1) prout(_("May the Great Bird of the Galaxy roost upon your home planet.")) finally: ioend() raise SystemExit(0) - except KeyboardInterrupt: + except KeyboardInterrupt: # pragma: no cover if logfp: logfp.close() print("")