Suppress Supercommander creation befor 1979 versiom.
[super-star-trek.git] / sst
diff --git a/sst b/sst
index 45d60977dcd45e7743498cc07da649d2683631e9..3d30875c8c47e9b5dbfb62fe08c4b6ae4055d5d4 100755 (executable)
--- a/sst
+++ b/sst
@@ -18,7 +18,7 @@ on how to modify (and how not to modify!) this code.
 
 # 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
@@ -26,7 +26,7 @@ import codecs
 try:
     # pylint: disable=unused-import
     import readline
-except ImportError:
+except ImportError:  # pragma: no cover
     pass
 
 version = "2.7"
@@ -88,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
@@ -155,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)))
@@ -195,28 +203,20 @@ 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.angered = False
-    def angry(self):
-        self.angered = True
+        self.location = Coord()
     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):
@@ -240,7 +240,7 @@ class Quadrant:
         self.charted = False
         self.status = "secure"        # Could be "secure", "distressed", "enslaved"
     def __str__(self):
-        return "<Quadrant: %(klingons)d>" % self.__dict__
+        return "<Quadrant: %(klingons)d>" % self.__dict__  # pragma: no cover
     __repr__ = __str__
 
 class Page:
@@ -249,7 +249,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."
@@ -296,47 +296,48 @@ OPTION_ALL        = 0xffffffff
 OPTION_TTY        = 0x00000001        # old interface
 OPTION_CURSES     = 0x00000002        # new interface
 OPTION_IOMODES    = 0x00000003        # cover both interfaces
-OPTION_PLANETS    = 0x00000004        # planets and mining
+OPTION_PLANETS    = 0x00000004        # planets and mining (> 1974)
 OPTION_THOLIAN    = 0x00000008        # Tholians and their webs (UT 1979 version)
-OPTION_THINGY     = 0x00000010        # Space Thingy can shoot back (Stas, 2005)
+OPTION_SUPERCMDR  = 0x00000010        # Supercommanders (UT 1979 version)
 OPTION_PROBE      = 0x00000020        # deep-space probes (DECUS version, 1980)
-OPTION_SHOWME     = 0x00000040        # bracket Enterprise in chart
-OPTION_RAMMING    = 0x00000080        # enemies may ram Enterprise (Almy)
-OPTION_MVBADDY    = 0x00000100        # more enemies can move (Almy)
-OPTION_BLKHOLE    = 0x00000200        # black hole may timewarp you (Stas, 2005)
+OPTION_MVBADDY    = 0x00000040        # more enemies can move (Almy, 1979?)
+OPTION_RAMMING    = 0x00000080        # enemies may ram Enterprise (Almy, 1979?)
+OPTION_ALMY       = 0x00000100        # Almy's death ray upgrade (1997?)
+OPTION_AUTOPASS   = 0x00000200        # Autogenerate password (Almy, 1997?)
 OPTION_BASE       = 0x00000400        # bases have good shields (Stas, 2005)
-OPTION_WORLDS     = 0x00000800        # logic for inhabited worlds (ESR, 2006)
-OPTION_AUTOSCAN   = 0x00001000        # automatic LRSCAN before CHART (ESR, 2006)
-OPTION_CAPTURE    = 0x00002000        # Enable BSD-Trek capture (Almy, 2013).
-OPTION_CLOAK      = 0x80004000        # Enable BSD-Trek capture (Almy, 2013).
-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_BLKHOLE    = 0x00000800        # black hole may timewarp you (Stas, 2005)
+OPTION_SHOWME     = 0x00001000        # bracket Enterprise in chart (ESR, 2005)
+OPTION_WORLDS     = 0x00002000        # logic for inhabited worlds (ESR, 2006)
+OPTION_AUTOSCAN   = 0x00004000        # automatic LRSCAN before CHART (ESR, 2006)
+OPTION_COLOR      = 0x00008000        # enable color display (ESR, 2010)
+OPTION_CAPTURE    = 0x00010000        # Enable BSD-Trek capture (Almy, 2013).
+OPTION_CLOAK      = 0x10020000        # Enable BSD-Trek capture (Almy, 2013).
+OPTION_DOTFILL    = 0x01040000        # fix dotfill glitch in chart (ESR, 2019)
+OPTION_ALPHAMERIC = 0x00080000        # Alpha Y coordinates (ESR, 2023)
 
 option_names = {
-    "ALL": OPTION_ALL,
-    "TTY": OPTION_TTY,
-    "IOMODES": OPTION_IOMODES,
-    "PLANETS": OPTION_PLANETS,
-    "THOLIAN": OPTION_THOLIAN,
-    "THINGY": OPTION_THINGY,
-    "PROBE": OPTION_PROBE,
-    "SHOWME": OPTION_SHOWME,
-    "RAMMING": OPTION_RAMMING,
-    "MVBADDY": OPTION_MVBADDY,
-    "BLKHOLE": OPTION_BLKHOLE,
-    "BASE": OPTION_BASE,
-    "WORLDS": OPTION_WORLDS,
-    "AUTOSCAN": OPTION_AUTOSCAN,
-    "CAPTURE": OPTION_CAPTURE,
-    "CLOAK": OPTION_CLOAK,
-    "PLAIN": OPTION_PLAIN,
-    "ALMY": OPTION_ALMY,
-    "COLOR": OPTION_COLOR,
-    "DOTFILL": OPTION_DOTFILL,
-    }
+    "ALL": (OPTION_ALL, 0),
+    "TTY": (OPTION_TTY, 0),
+    "IOMODES": (OPTION_IOMODES, 0),
+    "PLANETS": (OPTION_PLANETS, 1974),
+    "THOLIAN": (OPTION_THOLIAN, 1979),
+    "SUPERCMDR": (OPTION_SUPERCMDR, 1979),
+    "PROBE": (OPTION_PROBE, 1980),
+    "MVBADDY": (OPTION_MVBADDY, 1981),   # year bumped to make it distinct
+    "RAMMING": (OPTION_RAMMING, 1982),   # year bumped to make it distinct
+    "ALMY": (OPTION_ALMY, 1997),
+    "AUTOPASS": (OPTION_AUTOPASS, 1998), # year bumped to make it distinct
+    "BASE": (OPTION_BASE, 2004),        # year bumped to make it distinct
+    "BLKHOLE": (OPTION_BLKHOLE, 2004),   # year bumped to make it distinct
+    "SHOWME": (OPTION_SHOWME, 2005),
+    "WORLDS": (OPTION_WORLDS, 2006),
+    "AUTOSCAN": (OPTION_AUTOSCAN, 2007),  # year bumped to make it distinct
+    "COLOR": (OPTION_COLOR, 2010),
+    "CAPTURE": (OPTION_CAPTURE, 2013),
+    "CLOAK": (OPTION_CLOAK, 2014),        # year bumped to make it distinct
+    "DOTFILL": (OPTION_DOTFILL, 2019),
+    "ALPHAMERIC": (OPTION_ALPHAMERIC, 2023)
+}
 
 # Define devices
 DSRSENS         = 0
@@ -388,7 +389,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:
@@ -421,7 +422,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):
@@ -685,7 +686,7 @@ def movebaddy(enemy):
             if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off !
                 motion -= game.skill*(2.0-rnd.real()**2)
         if game.idebug:
-            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
+            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))     # pragma: no cover
         # don't move if no motion
         if motion == 0:
             return []
@@ -702,7 +703,7 @@ def movebaddy(enemy):
     nsteps = min(nsteps, QUADSIZE) # This shouldn't be necessary
     nsteps = max(nsteps, 1) # This shouldn't be necessary
     if game.idebug:
-        proutn("NSTEPS = %d:" % nsteps)
+        proutn("NSTEPS = %d:" % nsteps)        # pragma: no cover
     # Compute preferred values of delta X and Y
     m = game.sector - enemy.location
     if 2.0 * abs(m.i) < abs(m.j):
@@ -714,7 +715,7 @@ def movebaddy(enemy):
     # main move loop
     for ll in range(nsteps):
         if game.idebug:
-            proutn(" %d" % (ll+1))
+            proutn(" %d" % (ll+1))     # pragma: no cover
         # Check if preferred position available
         look = goto + m
         if m.i < 0:
@@ -762,18 +763,18 @@ def movebaddy(enemy):
         if success:
             goto = look
             if game.idebug:
-                proutn(repr(goto))
+                proutn(repr(goto))     # pragma: no cover
         else:
             break # done early
     if game.idebug:
-        skip(1)
+        skip(1)        # pragma: no cover
     # Enemy moved, but is still in sector
     return [(False, enemy, old_dist, goto)]
 
 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 = []
@@ -842,7 +843,7 @@ def supercommander():
     idelta = Coord()
     basetbl = []
     if game.idebug:
-        prout("== SUPERCOMMANDER")
+        prout("== SUPERCOMMANDER")     # pragma: no cover
     # Decide on being active or passive
     avoid = ((game.incom - len(game.state.kcmdr) + game.inkling - game.remkl())/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
             (game.state.date-game.indate) < 3.0)
@@ -973,7 +974,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.")
@@ -1226,10 +1227,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."))
@@ -1246,12 +1247,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)
@@ -1284,7 +1285,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
     # Loop to move a single torpedo
     setwnd(message_window)
     for step in range(1, QUADSIZE*2):
-        if not track.nexttok():
+        if not track.nextstep():
             break
         w = track.sector()
         if not w.valid_sector():
@@ -1308,7 +1309,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
             # In the C/FORTRAN version, dispersion was 2.5 radians, which
             # is 143 degrees, which is almost exactly 4.8 clockface units
             displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5)
-            displacement.nexttok()
+            displacement.nextstep()
             bumpto = displacement.sector()
             if not bumpto.valid_sector():
                 return hit
@@ -1350,7 +1351,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                         return None
                     proutn(crmena(True, iquad, "sector", w))
                     displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5, origin=w)
-                    displacement.nexttok()
+                    displacement.nextstep()
                     bumpto = displacement.sector()
                     if game.quad[bumpto.i][bumpto.j] == ' ':
                         prout(_(" buffeted into black hole."))
@@ -1370,7 +1371,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
@@ -1417,21 +1418,15 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                 prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
             return None
         elif iquad == '?': # Hit a thingy
-            if not (game.options & OPTION_THINGY) or rnd.withprob(0.3):
-                skip(1)
-                prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
-                skip(1)
-                prouts(_("    HACK!     HACK!    HACK!        *CHOKE!*  "))
-                skip(1)
-                proutn(_("Mr. Spock-"))
-                prouts(_("  \"Fascinating!\""))
-                skip(1)
-                deadkl(w, iquad, w)
-            else:
-                # Stas Sergeev added the possibility that
-                # you can shove the Thingy and piss it off.
-                # It then becomes an enemy and may fire at you.
-                thing.angry()
+            skip(1)
+            prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
+            skip(1)
+            prouts(_("    HACK!     HACK!    HACK!        *CHOKE!*  "))
+            skip(1)
+            proutn(_("Mr. Spock-"))
+            prouts(_("  \"Fascinating!\""))
+            skip(1)
+            deadkl(w, iquad, w)
             return None
         elif iquad == ' ': # Black hole
             skip(1)
@@ -1521,7 +1516,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()
@@ -1550,7 +1545,7 @@ def attack(torps_ok):
                         prout("Sector %s." % goto)
         sortenemies()
     # if no enemies remain after movement, we're done
-    if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
+    if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant)):
         return
     # set up partial hits if attack happens during shield status change
     pfac = 1.0/game.inshld
@@ -1570,7 +1565,7 @@ def attack(torps_ok):
             r *= 0.25
         if enemy.power < 500:
             r *= 0.25
-        if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
+        if enemy.type in ('T', '?'):
             continue
         # different enemies have different probabilities of throwing a torp
         usephasers = not torps_ok or \
@@ -1913,8 +1908,6 @@ def hittem(hits):
         else:
             proutn(_("Very small hit on "))
         ienm = game.quad[w.i][w.j]
-        if ienm == '?':
-            thing.angry()
         proutn(crmena(False, ienm, "sector", w))
         skip(1)
         if kpow == 0:
@@ -2375,7 +2368,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       ")
@@ -2405,7 +2398,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:
@@ -2560,7 +2553,7 @@ def events():
                 supercommander()
         elif evcode == FDSPROB: # Move deep space probe
             schedule(FDSPROB, 0.01)
-            if not game.probe.nexttok():
+            if not game.probe.nextstep():
                 if not game.probe.quadrant().valid_quadrant() or \
                     game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
                     # Left galaxy or ran into supernova
@@ -2608,7 +2601,7 @@ def events():
             else:
                 # can't seem to find one; ignore this call
                 if game.idebug:
-                    prout("=== Couldn't find location for distress event.")
+                    prout("=== Couldn't find location for distress event.")    # pragma: no cover
                 continue
             # got one!!  Schedule its enslavement
             ev = schedule(FENSLV, expran(game.intime))
@@ -2893,7 +2886,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
@@ -3192,6 +3185,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 +3405,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 +3413,7 @@ def iostart():
             rows = ln_env
         else:
             rows = 25
-    else:
+    else:      # pragma: no cover
         stdscr = curses.initscr()
         stdscr.keypad(True)
         curses.nonl()
@@ -3455,7 +3444,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 +3452,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 +3466,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()
@@ -3498,7 +3487,7 @@ def pause_game():
 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 +3503,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 +3527,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 +3536,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 +3544,7 @@ def cgetline():
             while True:
                 linein = replayfp.readline()
                 proutn(linein)
-                if linein == '':
+                if linein == '':    # pragma: no cover
                     prout("*** Replay finished")
                     replayfp.close()
                     break
@@ -3574,7 +3563,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 +3591,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 +3642,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 +3651,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 +3670,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 +3678,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 +3693,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 +3716,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 +3740,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 +3751,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 +3831,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!"))
@@ -3901,7 +3890,7 @@ def imove(icourse=None, noattack=False):
     # Move out
     game.quad[game.sector.i][game.sector.j] = '.'
     for _m in range(icourse.moves):
-        icourse.nexttok()
+        icourse.nextstep()
         w = icourse.sector()
         if icourse.origin.quadrant() != icourse.location.quadrant():
             newquadrant(noattack)
@@ -4132,7 +4121,7 @@ class course:
         self.step = 0
     def arrived(self):
         return self.location.roundtogrid() == self.final
-    def nexttok(self):
+    def nextstep(self):
         "Next step on course."
         self.step += 1
         self.nextlocation = self.location + self.increment
@@ -4198,7 +4187,7 @@ def impulse():
     return
 
 def warp(wcourse, involuntary):
-    "ove under warp drive."
+    "Move under warp drive."
     blooey = False; twarp = False
     if not involuntary: # Not WARPX entry
         game.ididit = False
@@ -4270,7 +4259,7 @@ def warp(wcourse, involuntary):
         # Decide if time warp will occur
         if 0.5*wcourse.distance*math.pow(7.0,game.warpfac-10.0) > rnd.real():
             twarp = True
-        if game.idebug and game.warpfac==10 and not twarp:
+        if game.idebug and game.warpfac==10 and not twarp:                # pragma: no cover
             blooey = False
             proutn("=== Force time warp? ")
             if ja():
@@ -4281,7 +4270,7 @@ def warp(wcourse, involuntary):
             look = wcourse.moves
             while look > 0:
                 look -= 1
-                wcourse.nexttok()
+                wcourse.nextstep()
                 w = wcourse.sector()
                 if not w.valid_sector():
                     break
@@ -4589,7 +4578,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]='?'
@@ -4827,7 +4816,7 @@ def beam():
         if not ja():
             scanner.chew()
             return
-    if not (game.options & OPTION_PLAIN):
+    if (game.options & OPTION_ALMY):
         nrgneed = 50 * game.skill + game.height / 100.0
         if nrgneed > game.energy:
             prout(_("Engineering to bridge--"))
@@ -5094,7 +5083,10 @@ def deathray():
     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
     skip(1)
     dprob = 0.30
-    if game.options & OPTION_PLAIN:
+    # Ugh. This test (For Tom Almy's death-ray upgrade) was inverted for a long time.
+    # Furthermore, somebody (ESR or Stas?) changed Tom Almy's 0.7 upgraded chance of
+    # working to 0.5.
+    if game.options & OPTION_ALMY:
         dprob = 0.5
     r = rnd.real()
     if r > dprob:
@@ -5105,7 +5097,7 @@ def deathray():
         prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
         if game.unwon() == 0:
             finish(FWON)
-        if (game.options & OPTION_PLAIN) == 0:
+        if (game.options & OPTION_ALMY):
             prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
             if rnd.withprob(0.05):
                 prout(_("   is still operational.\""))
@@ -5305,8 +5297,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,
@@ -5372,7 +5364,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,
@@ -5617,7 +5609,7 @@ def goptions():
     if mode == "IHEOL":
         active = []
         for k, v in option_names.items():
-            if (v & game.options) and k != "ALL":
+            if (v[0] & game.options) and k != "ALL":
                 active.append(k)
         active.sort()
         prout(str(" ".join(active)))
@@ -5629,15 +5621,15 @@ def goptions():
             if scanner.type == "IHEOL":
                 break
             if scanner.token.upper() in option_names:
-                changemask |= option_names[scanner.token.upper()]
+                changemask |= option_names[scanner.token.upper()][0]
             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."))
@@ -5829,7 +5821,7 @@ def setup():
             game.state.galaxy[i][j].stars = k
     # Locate star bases in galaxy
     if game.idebug:
-        prout("=== Allocating %d bases" % game.inbase)
+        prout("=== Allocating %d bases" % game.inbase)                # pragma: no cover
     for i in range(game.inbase):
         while True:
             while True:
@@ -5845,15 +5837,15 @@ def setup():
                 if distq < 6.0*(BASEMAX+1-game.inbase) and rnd.withprob(0.75):
                     contflag = True
                     if game.idebug:
-                        prout("=== Abandoning base #%d at %s" % (i, w))
+                        prout("=== Abandoning base #%d at %s" % (i, w))                # pragma: no cover
                     break
                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
                     if game.idebug:
-                        prout("=== Saving base #%d, close to #%d" % (i, j))
+                        prout("=== Saving base #%d, close to #%d" % (i, j))                # pragma: no cover
             if not contflag:
                 break
         if game.idebug:
-            prout("=== Placing base #%d in quadrant %s" % (i, w))
+            prout("=== Placing base #%d in quadrant %s" % (i, w))                # pragma: no cover
         game.state.baseq.append(w)
         game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
     # Position ordinary Klingon Battle Cruisers
@@ -5939,12 +5931,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:
@@ -5980,33 +5977,52 @@ 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)
 
 def choose():
     "Choose your game type."
-    while True:
-        game.tourn = game.length = 0
-        game.thawed = False
-        game.skill = SKILL_NONE
-        # Do not chew here, we want to use command-line tokens
-        if not scanner.inqueue: # Can start with command line options
-            proutn(_("Would you like a regular, tournament, or saved game? "))
+    game.tourn = None
+    game.length = 0
+    game.thawed = False
+    game.skill = SKILL_NONE
+    gametype = None
+    wayback = 0
+    while gametype is None or game.length == 0 or game.skill == SKILL_NONE or wayback == 0:
+        eol_is_fancy = False
+        if not scanner.inqueue or scanner.token == "IHEOL": # Can start with command line options
+            if gametype is None:
+                proutn(_("Would you like a regular, tournament, or saved game? "))
+            elif game.length==0:
+                proutn(_("Would you like a Short, Medium, or Long game? "))
+            elif game.skill == SKILL_NONE:
+                proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
+            elif wayback == 0:
+                proutn(_("Wayback setting (press enter for current year): "))
+                eol_is_fancy = True
         scanner.nexttok()
-        if scanner.sees("tournament"):
+        if game.idebug:
+            prout("-- Token: %s=%s" % (scanner.type, repr(scanner.token)))
+        if scanner.token == "":
+            raise SystemExit(0)        # Early end of replay
+        if scanner.token.startswith("r"):      # regular
+            gametype = "regular"
+        elif scanner.token.startswith("t"):
+            gametype = "tournament"
+            proutn(_("Type in tournament number-"))
+            game.tourn = 0
             while scanner.nexttok() == "IHEOL":
-                proutn(_("Type in tournament number-"))
-            if scanner.real == 0:
-                scanner.chew()
-                continue # We don't want a blank entry
+                if scanner.real == 0:
+                    scanner.chew()
+                    continue # We don't want a blank entry
             game.tourn = int(round(scanner.real))
             rnd.seed(scanner.real)
             if logfp:
                 logfp.write("# rnd.seed(%d)\n" % scanner.real)
-            break
-        if scanner.sees("saved") or scanner.sees("frozen"):
+        elif scanner.token.startswith("sa") or scanner.token.startswith("fr"): # saved or frozen
+            gametype = "saved"
             if thaw():
                 continue
             scanner.chew()
@@ -6017,59 +6033,42 @@ def choose():
             report()
             waitfor()
             return True
-        if scanner.sees("regular"):
-            break
-        proutn(_("What is \"%s\"? ") % scanner.token)
-        scanner.chew()
-    while game.length==0 or game.skill==SKILL_NONE:
-        if scanner.nexttok() == "IHALPHA":
-            if scanner.sees("short"):
-                game.length = 1
-            elif scanner.sees("medium"):
-                game.length = 2
-            elif scanner.sees("long"):
-                game.length = 4
-            elif scanner.sees("novice"):
-                game.skill = SKILL_NOVICE
-            elif scanner.sees("fair"):
-                game.skill = SKILL_FAIR
-            elif scanner.sees("good"):
-                game.skill = SKILL_GOOD
-            elif scanner.sees("expert"):
-                game.skill = SKILL_EXPERT
-            elif scanner.sees("emeritus"):
-                game.skill = SKILL_EMERITUS
-            else:
-                proutn(_("What is \""))
-                proutn(scanner.token)
-                prout("\"?")
+        elif scanner.token.startswith("s"):            # short
+            game.length = 1
+        elif scanner.token.startswith("m"):            # medium
+            game.length = 2
+        elif scanner.token.startswith("l"):            # long
+            game.length = 4
+        elif scanner.token.startswith("n"):            # novice
+            game.skill = SKILL_NOVICE
+        elif (game.skill is None) and scanner.token.startswith("f"):           # fair
+            game.skill = SKILL_FAIR
+        elif scanner.token.startswith("g"):            # good
+            game.skill = SKILL_GOOD
+        elif scanner.token.startswith("e"):            # expert
+            game.skill = SKILL_EXPERT
+        elif scanner.token.startswith("em"):   # emeritus
+            game.skill = SKILL_EMERITUS
+        elif scanner.type == "IHREAL":
+            wayback = scanner.int()
+        elif (eol_is_fancy and scanner.token.startswith("\n")):
+            wayback = time.localtime().tm_year
+        elif scanner.token.startswith("\n"):
+            continue
+        elif scanner.token.startswith("idebug"):
+            game.idebug = True
         else:
-            scanner.chew()
-            if game.length==0:
-                proutn(_("Would you like a Short, Medium, or Long game? "))
-            elif game.skill == SKILL_NONE:
-                proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
-    # Choose game options -- added by ESR for SST2K
-    if scanner.nexttok() != "IHALPHA":
-        scanner.chew()
-        proutn(_("Choose your game style (plain, almy, fancy or just press enter): "))
-        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 | 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 | 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)
+            # Unrecognized token
+            prout(_("Can't interpret %s") % repr(scanner.token))
+    for (name, (option, year)) in option_names.items():
+        if wayback < year:
+            game.options &=~ option
     setpassword()
-    if game.passwd == "debug":
+    if game.passwd == "debug":                # pragma: no cover
         game.idebug = True
         prout("=== Debug mode enabled.")
+    if game.idebug:
+        prout("--- Setup: type=%s length=%s skill=%s wayback=%s" % (gametype, game.length, game.skill, wayback))
     # Use parameters to generate initial values of things
     game.damfac = 0.5 * game.skill
     game.inbase = rnd.integer(BASEMIN, BASEMAX+1)
@@ -6079,7 +6078,7 @@ def choose():
     if game.options & OPTION_WORLDS:
         game.inplan += int(NINHAB)
     game.state.nromrem = game.inrom = rnd.integer(2 * game.skill)
-    game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
+    game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR) and ((game.options & OPTION_SUPERCMDR) != 0)
     game.state.remtime = 7.0 * game.length
     game.intime = game.state.remtime
     game.inkling = int(2.0*game.intime*((game.skill+1 - 2*rnd.real())*game.skill*0.1+.15))
@@ -6183,7 +6182,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,7 +6235,12 @@ def newqad():
 
 def setpassword():
     "Set the self-destruct password."
-    if game.options & OPTION_PLAIN:
+    if game.options & OPTION_AUTOPASS:
+        game.passwd = ""
+        game.passwd += chr(ord('a')+rnd.integer(26))
+        game.passwd += chr(ord('a')+rnd.integer(26))
+        game.passwd += chr(ord('a')+rnd.integer(26))
+    else:
         while True:
             scanner.chew()
             proutn(_("Please type in a secret password- "))
@@ -6245,11 +6249,6 @@ def setpassword():
             #game.passwd = getpass.getpass("Please type in a secret password- ")
             if game.passwd is not None:
                 break
-    else:
-        game.passwd = ""
-        game.passwd += chr(ord('a')+rnd.integer(26))
-        game.passwd += chr(ord('a')+rnd.integer(26))
-        game.passwd += chr(ord('a')+rnd.integer(26))
 
 # Code from sst.c begins here
 
@@ -6301,8 +6300,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:
@@ -6384,7 +6385,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 == "":
@@ -6403,14 +6404,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()
@@ -6518,7 +6519,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":
@@ -6545,7 +6546,7 @@ def makemoves():
         if game.alldone:
             break
     if game.idebug:
-        prout("=== Ending")
+        prout("=== Ending")                # pragma: no cover
 
 def cramen(ch):
     "Emit the name of an enemy or feature."
@@ -6561,7 +6562,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):
@@ -6672,7 +6673,7 @@ class sstscanner:
             return None
         s.j = self.int()-1
         return s
-    def __repr__(self):
+    def __repr__(self):                # pragma: no cover
         return "<sstcanner: token=%s, type=%s, queue=%s>" % (self.token, self.type, self.inqueue)
 
 def ja():
@@ -6693,7 +6694,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():
@@ -6710,7 +6711,7 @@ def debugme():
     proutn("Toggle debug flag? ")
     if ja():
         game.idebug = not game.idebug
-        if game.idebug:
+        if game.idebug:                # pragma: no cover
             prout("Debug output ON")
         else:
             prout("Debug output OFF")
@@ -6788,57 +6789,66 @@ if __name__ == '__main__':
         game = Gamestate()
         rnd = randomizer()
         logfp = None
-        game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
+        game.options = OPTION_ALL &~ OPTION_IOMODES
         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:
+                        while True:
+                            line = replayfp.readline().strip()
+                            print(line)
+                            if line == "#":
+                                break
+                            if line.startswith("# seed"):
+                                (__, __, seed) = line.split()
+                                # pylint: disable=eval-used
+                                seed = eval(seed)
+                            elif line.startswith("# arguments"):
+                                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 err:
+            print(err)
+            raise SystemExit(1) from err
         # 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:
@@ -6847,6 +6857,7 @@ if __name__ == '__main__':
             logfp.write("# SST2K version %s\n" % version)
             logfp.write("# recorded by %s@%s on %s\n" % \
                     (getpass.getuser(),socket.getfqdn(),time.ctime()))
+            logfp.write("#\n")
         rnd.seed(seed)
         scanner = sstscanner()
         for arg in arguments:
@@ -6887,7 +6898,7 @@ if __name__ == '__main__':
         finally:
             ioend()
         raise SystemExit(0)
-    except KeyboardInterrupt:
+    except KeyboardInterrupt:                # pragma: no cover
         if logfp:
             logfp.close()
         print("")