Refactor to narrow the scope within which I/O calls are made.
[super-star-trek.git] / sst.py
diff --git a/sst.py b/sst.py
index 691eff84dbb4e2f179528a3ecbce5becc9cc9795..34a2236dd3955f28eec9cb14a3a86957cee6fa68 100755 (executable)
--- a/sst.py
+++ b/sst.py
@@ -13,11 +13,12 @@ on how to modify (and how not to modify!) this code.
 """
 import os, sys, math, curses, time, readline, cPickle, random, copy, gettext, getpass
 
 """
 import os, sys, math, curses, time, readline, cPickle, random, copy, gettext, getpass
 
-version="2.1"
+version = "2.1"
 
 docpath        = (".", "../doc", "/usr/share/doc/sst")
 
 
 docpath        = (".", "../doc", "/usr/share/doc/sst")
 
-def _(str): return gettext.gettext(str)
+def _(st):
+    return gettext.gettext(st)
 
 GALSIZE        = 8             # Galaxy size in quadrants
 NINHAB         = (GALSIZE * GALSIZE / 2)       # Number of inhabited worlds
 
 GALSIZE        = 8             # Galaxy size in quadrants
 NINHAB         = (GALSIZE * GALSIZE / 2)       # Number of inhabited worlds
@@ -63,9 +64,9 @@ class Coord:
         self.i = x
         self.j = y
     def valid_quadrant(self):
         self.i = x
         self.j = y
     def valid_quadrant(self):
-        return self.i>=0 and self.i<GALSIZE and self.j>=0 and self.j<GALSIZE
+        return self.i >= 0 and self.i < GALSIZE and self.j >= 0 and self.j < GALSIZE
     def valid_sector(self):
     def valid_sector(self):
-       return self.i>=0 and self.i<QUADSIZE and self.j>=0 and self.j<QUADSIZE
+       return self.i >= 0 and self.i < QUADSIZE and self.j >= 0 and self.j < QUADSIZE
     def invalidate(self):
         self.i = self.j = None
     def is_valid(self):
     def invalidate(self):
         self.i = self.j = None
     def is_valid(self):
@@ -91,7 +92,8 @@ class Coord:
     def roundtogrid(self):
         return Coord(int(round(self.i)), int(round(self.j)))
     def distance(self, other=None):
     def roundtogrid(self):
         return Coord(int(round(self.i)), int(round(self.j)))
     def distance(self, other=None):
-        if not other: other = Coord(0, 0)
+        if not other:
+            other = Coord(0, 0)
         return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
     def bearing(self):
         return 1.90985*math.atan2(self.j, self.i)
         return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
     def bearing(self):
         return 1.90985*math.atan2(self.j, self.i)
@@ -122,6 +124,16 @@ class Coord:
         return "%s - %s" % (self.i+1, self.j+1)
     __repr__ = __str__
 
         return "%s - %s" % (self.i+1, self.j+1)
     __repr__ = __str__
 
+class Thingy(Coord):
+    "Do not anger the Space Thingy!"
+    def __init__(self):
+        Coord.__init__(self)
+        self.angered = False
+    def angry(self):
+        self.angered = True
+    def at(self, q):
+        return (q.i, q.j) == (self.i, self.j)
+
 class Planet:
     def __init__(self):
         self.name = None       # string-valued if inhabited
 class Planet:
     def __init__(self):
         self.name = None       # string-valued if inhabited
@@ -208,23 +220,23 @@ OPTION_ALMY       = 0x02000000    # user chose Almy variant
 OPTION_COLOR    = 0x04000000   # enable color display (experimental, ESR, 2010)
 
 # Define devices 
 OPTION_COLOR    = 0x04000000   # enable color display (experimental, ESR, 2010)
 
 # Define devices 
-DSRSENS        = 0
-DLRSENS        = 1
-DPHASER        = 2
-DPHOTON        = 3
-DLIFSUP        = 4
-DWARPEN        = 5
-DIMPULS        = 6
-DSHIELD        = 7
-DRADIO = 0
-DSHUTTL = 9
-DCOMPTR = 10
-DNAVSYS        = 11
-DTRANSP = 12
-DSHCTRL        = 13
-DDRAY  = 14
-DDSP   = 15
-NDEVICES= 16   # Number of devices
+DSRSENS         = 0
+DLRSENS         = 1
+DPHASER         = 2
+DPHOTON         = 3
+DLIFSUP         = 4
+DWARPEN         = 5
+DIMPULS         = 6
+DSHIELD         = 7
+DRADIO  = 0
+DSHUTTL  = 9
+DCOMPTR  = 10
+DNAVSYS         = 11
+DTRANSP  = 12
+DSHCTRL         = 13
+DDRAY   = 14
+DDSP    = 15
+NDEVICES = 16  # Number of devices
 
 SKILL_NONE     = 0
 SKILL_NOVICE   = 1
 
 SKILL_NONE     = 0
 SKILL_NOVICE   = 1
@@ -233,8 +245,10 @@ SKILL_GOOD = 3
 SKILL_EXPERT   = 4
 SKILL_EMERITUS = 5
 
 SKILL_EXPERT   = 4
 SKILL_EMERITUS = 5
 
-def damaged(dev):      return (game.damage[dev] != 0.0)
-def communicating():   return not damaged(DRADIO) or game.condition=="docked"
+def damaged(dev):
+    return (game.damage[dev] != 0.0)
+def communicating():
+    return not damaged(DRADIO) or game.condition=="docked"
 
 # Define future events 
 FSPY   = 0     # Spy event happens always (no future[] entry)
 
 # Define future events 
 FSPY   = 0     # Spy event happens always (no future[] entry)
@@ -254,12 +268,15 @@ NEVENTS   = 12
 
 # Abstract out the event handling -- underlying data structures will change
 # when we implement stateful events 
 
 # Abstract out the event handling -- underlying data structures will change
 # when we implement stateful events 
-def findevent(evtype): return game.future[evtype]
+def findevent(evtype):
+    return game.future[evtype]
 
 class Enemy:
 
 class Enemy:
-    def __init__(self, type=None, loc=None, power=None):
-        self.type = type
+    def __init__(self, etype=None, loc=None, power=None):
+        self.type = etype
         self.location = Coord()
         self.location = Coord()
+        self.kdist = None
+        self.kavgd = None
         if loc:
             self.move(loc)
         self.power = power     # enemy energy level
         if loc:
             self.move(loc)
         self.power = power     # enemy energy level
@@ -296,7 +313,7 @@ class Gamestate:
         while i > 0:
             i -= 1
             self.future.append(Event())
         while i > 0:
             i -= 1
             self.future.append(Event())
-        self.passwd  = None;           # Self Destruct password
+        self.passwd  = None    # Self Destruct password
         self.enemies = []
         self.quadrant = None   # where we are in the large
         self.sector = None     # where we are in the small
         self.enemies = []
         self.quadrant = None   # where we are in the large
         self.sector = None     # where we are in the small
@@ -366,6 +383,7 @@ class Gamestate:
         self.score = 0.0       # overall score
         self.perdate = 0.0     # rate of kills
         self.idebug = False    # Debugging instrumentation enabled?
         self.score = 0.0       # overall score
         self.perdate = 0.0     # rate of kills
         self.idebug = False    # Debugging instrumentation enabled?
+        self.statekscmdr = None # No SuperCommander coordinates yet.
     def recompute(self):
         # Stas thinks this should be (C expression): 
         # game.state.remkl + len(game.state.kcmdr) > 0 ?
     def recompute(self):
         # Stas thinks this should be (C expression): 
         # game.state.remkl + len(game.state.kcmdr) > 0 ?
@@ -373,7 +391,7 @@ class Gamestate:
         # He says the existing expression is prone to divide-by-zero errors
         # after killing the last klingon when score is shown -- perhaps also
         # if the only remaining klingon is SCOM.
         # He says the existing expression is prone to divide-by-zero errors
         # after killing the last klingon when score is shown -- perhaps also
         # if the only remaining klingon is SCOM.
-        game.state.remtime = game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr))
+        self.state.remtime = self.state.remres/(self.state.remkl + 4*len(self.state.kcmdr))
 
 FWON = 0
 FDEPLETE = 1
 
 FWON = 0
 FDEPLETE = 1
@@ -426,26 +444,21 @@ def tryexit(enemy, look, irun):
     iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
     if not welcoming(iq):
     iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
     if not welcoming(iq):
-       return False;
+       return False
     if enemy.type == 'R':
     if enemy.type == 'R':
-       return False; # Romulans cannot escape! 
+       return False # Romulans cannot escape! 
     if not irun:
        # avoid intruding on another commander's territory 
        if enemy.type == 'C':
             if iq in game.state.kcmdr:
     if not irun:
        # avoid intruding on another commander's territory 
        if enemy.type == 'C':
             if iq in game.state.kcmdr:
-                return False
+                return []
            # refuse to leave if currently attacking starbase 
            if game.battle == game.quadrant:
            # refuse to leave if currently attacking starbase 
            if game.battle == game.quadrant:
-               return False
+               return []
        # don't leave if over 1000 units of energy 
        if enemy.power > 1000.0:
        # don't leave if over 1000 units of energy 
        if enemy.power > 1000.0:
-           return False
-    # emit escape message and move out of quadrant.
-    # we know this if either short or long range sensors are working
-    if not damaged(DSRSENS) or not damaged(DLRSENS) or \
-       game.condition == "docked":
-       prout(crmena(True, enemy.type, "sector", enemy.location) + \
-              (_(" escapes to Quadrant %s (and regains strength).") % iq))
+           return []
+    oldloc = copy.copy(enemy.location)
     # handle local matters related to escape
     enemy.move(None)
     game.klhere -= 1
     # handle local matters related to escape
     enemy.move(None)
     game.klhere -= 1
@@ -454,19 +467,20 @@ def tryexit(enemy, look, irun):
     # Handle global matters related to escape 
     game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
     game.state.galaxy[iq.i][iq.j].klingons += 1
     # Handle global matters related to escape 
     game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
     game.state.galaxy[iq.i][iq.j].klingons += 1
-    if enemy.type=='S':
+    if enemy.type == 'S':
        game.iscate = False
        game.ientesc = False
        game.isatb = 0
        schedule(FSCMOVE, 0.2777)
        unschedule(FSCDBAS)
        game.iscate = False
        game.ientesc = False
        game.isatb = 0
        schedule(FSCMOVE, 0.2777)
        unschedule(FSCDBAS)
-       game.state.kscmdr=iq
+       game.state.kscmdr = iq
     else:
        for cmdr in game.state.kcmdr:
            if cmdr == game.quadrant:
                game.state.kcmdr.append(iq)
                break
     else:
        for cmdr in game.state.kcmdr:
            if cmdr == game.quadrant:
                game.state.kcmdr.append(iq)
                break
-    return True; # success 
+    # report move out of quadrant.
+    return [(True, enemy, oldloc, ibq)]
 
 # The bad-guy movement algorithm:
 # 
 
 # The bad-guy movement algorithm:
 # 
@@ -508,17 +522,18 @@ def tryexit(enemy, look, irun):
 
 def movebaddy(enemy):
     "Tactical movement for the bad guys."
 
 def movebaddy(enemy):
     "Tactical movement for the bad guys."
-    goto = Coord(); look = Coord()
+    goto = Coord()
+    look = Coord()
     irun = False
     # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant) 
     if game.skill >= SKILL_EXPERT:
        nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
     else:
        nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
     irun = False
     # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant) 
     if game.skill >= SKILL_EXPERT:
        nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
     else:
        nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
-    dist1 = enemy.kdist
-    mdist = int(dist1 + 0.5); # Nearest integer distance 
+    old_dist = enemy.kdist
+    mdist = int(old_dist + 0.5) # Nearest integer distance 
     # If SC, check with spy to see if should hi-tail it 
     # If SC, check with spy to see if should hi-tail it 
-    if enemy.type=='S' and \
+    if enemy.type == 'S' and \
        (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
        irun = True
        motion = -QUADSIZE
        (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
        irun = True
        motion = -QUADSIZE
@@ -526,7 +541,7 @@ def movebaddy(enemy):
        # decide whether to advance, retreat, or hold position 
        forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
        if not game.shldup:
        # decide whether to advance, retreat, or hold position 
        forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
        if not game.shldup:
-           forces += 1000; # Good for enemy if shield is down! 
+           forces += 1000 # Good for enemy if shield is down! 
        if not damaged(DPHASER) or not damaged(DPHOTON):
             if damaged(DPHASER): # phasers damaged 
                forces += 300.0
        if not damaged(DPHASER) or not damaged(DPHOTON):
             if damaged(DPHASER): # phasers damaged 
                forces += 300.0
@@ -544,14 +559,14 @@ def movebaddy(enemy):
            motion = ((forces + randreal(200))/150.0) - 5.0
        else:
             if forces > 1000.0: # Very strong -- move in for kill 
            motion = ((forces + randreal(200))/150.0) - 5.0
        else:
             if forces > 1000.0: # Very strong -- move in for kill 
-               motion = (1.0 - randreal())**2 * dist1 + 1.0
-           if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
+               motion = (1.0 - randreal())**2 * old_dist + 1.0
+           if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
                motion -= game.skill*(2.0-randreal()**2)
        if game.idebug:
            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
        # don't move if no motion 
                motion -= game.skill*(2.0-randreal()**2)
        if game.idebug:
            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
        # don't move if no motion 
-       if motion==0:
-           return
+       if motion == 0:
+           return []
        # Limit motion according to skill 
        if abs(motion) > game.skill:
             if motion < 0:
        # Limit motion according to skill 
        if abs(motion) > game.skill:
             if motion < 0:
@@ -561,11 +576,11 @@ def movebaddy(enemy):
     # calculate preferred number of steps 
     nsteps = abs(int(motion))
     if motion > 0 and nsteps > mdist:
     # calculate preferred number of steps 
     nsteps = abs(int(motion))
     if motion > 0 and nsteps > mdist:
-       nsteps = mdist; # don't overshoot 
+       nsteps = mdist # don't overshoot 
     if nsteps > QUADSIZE:
     if nsteps > QUADSIZE:
-       nsteps = QUADSIZE; # This shouldn't be necessary 
+       nsteps = QUADSIZE # This shouldn't be necessary 
     if nsteps < 1:
     if nsteps < 1:
-       nsteps = 1; # This shouldn't be necessary 
+       nsteps = 1 # This shouldn't be necessary 
     if game.idebug:
        proutn("NSTEPS = %d:" % nsteps)
     # Compute preferred values of delta X and Y 
     if game.idebug:
        proutn("NSTEPS = %d:" % nsteps)
     # Compute preferred values of delta X and Y 
@@ -591,19 +606,19 @@ def movebaddy(enemy):
         else:
             krawlj = -1
        success = False
         else:
             krawlj = -1
        success = False
-       attempts = 0; # Settle mysterious hang problem 
+       attempts = 0 # Settle mysterious hang problem 
        while attempts < 20 and not success:
             attempts += 1
            if look.i < 0 or look.i >= QUADSIZE:
        while attempts < 20 and not success:
             attempts += 1
            if look.i < 0 or look.i >= QUADSIZE:
-               if motion < 0 and tryexit(enemy, look, irun):
-                   return
+                if motion < 0:
+                   return tryexit(enemy, look, irun)
                if krawli == m.i or m.j == 0:
                    break
                look.i = goto.i + krawli
                krawli = -krawli
            elif look.j < 0 or look.j >= QUADSIZE:
                if krawli == m.i or m.j == 0:
                    break
                look.i = goto.i + krawli
                krawli = -krawli
            elif look.j < 0 or look.j >= QUADSIZE:
-               if motion < 0 and tryexit(enemy, look, irun):
-                   return
+               if motion < 0:
+                   return tryexit(enemy, look, irun)
                if krawlj == m.j or m.i == 0:
                    break
                look.j = goto.j + krawlj
                if krawlj == m.j or m.i == 0:
                    break
                look.j = goto.j + krawlj
@@ -613,7 +628,7 @@ def movebaddy(enemy):
                if game.quad[look.i][look.j] == game.ship and \
                    (enemy.type == 'C' or enemy.type == 'S'):
                    collision(rammed=True, enemy=enemy)
                if game.quad[look.i][look.j] == game.ship and \
                    (enemy.type == 'C' or enemy.type == 'S'):
                    collision(rammed=True, enemy=enemy)
-                   return
+                   return []
                if krawli != m.i and m.j != 0:
                    look.i = goto.i + krawli
                    krawli = -krawli
                if krawli != m.i and m.j != 0:
                    look.i = goto.i + krawli
                    krawli = -krawli
@@ -621,25 +636,19 @@ def movebaddy(enemy):
                    look.j = goto.j + krawlj
                    krawlj = -krawlj
                else:
                    look.j = goto.j + krawlj
                    krawlj = -krawlj
                else:
-                   break; # we have failed 
+                   break # we have failed 
            else:
                success = True
        if success:
            goto = look
            if game.idebug:
            else:
                success = True
        if success:
            goto = look
            if game.idebug:
-               proutn(`goto`)
+               proutn(repr(goto))
        else:
        else:
-           break; # done early 
+           break # done early 
     if game.idebug:
        skip(1)
     if game.idebug:
        skip(1)
-    if enemy.move(goto):
-       if not damaged(DSRSENS) or game.condition == "docked":
-           proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
-           if enemy.kdist < dist1:
-               proutn(_(" advances to "))
-           else:
-               proutn(_(" retreats to "))
-           prout("Sector %s." % goto)
+    # Enemy moved, but is still in sector
+    return [(False, enemy, old_dist, goto)]
 
 def moveklings():
     "Sequence Klingon tactical movement."
 
 def moveklings():
     "Sequence Klingon tactical movement."
@@ -647,14 +656,15 @@ def moveklings():
        prout("== MOVCOM")
     # Figure out which Klingon is the commander (or Supercommander)
     # and do move
        prout("== MOVCOM")
     # Figure out which Klingon is the commander (or Supercommander)
     # and do move
+    tacmoves = []
     if game.quadrant in game.state.kcmdr:
         for enemy in game.enemies:
            if enemy.type == 'C':
     if game.quadrant in game.state.kcmdr:
         for enemy in game.enemies:
            if enemy.type == 'C':
-               movebaddy(enemy)
-    if game.state.kscmdr==game.quadrant:
+               tacmoves += movebaddy(enemy)
+    if game.state.kscmdr == game.quadrant:
         for enemy in game.enemies:
            if enemy.type == 'S':
         for enemy in game.enemies:
            if enemy.type == 'S':
-               movebaddy(enemy)
+               tacmoves += movebaddy(enemy)
                break
     # If skill level is high, move other Klingons and Romulans too!
     # Move these last so they can base their actions on what the
                break
     # If skill level is high, move other Klingons and Romulans too!
     # Move these last so they can base their actions on what the
@@ -662,8 +672,8 @@ def moveklings():
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
         for enemy in game.enemies:
             if enemy.type in ('K', 'R'):
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
         for enemy in game.enemies:
             if enemy.type in ('K', 'R'):
-               movebaddy(enemy)
-    sortenemies()
+               tacmoves += movebaddy(enemy)
+    return tacmoves
 
 def movescom(iq, avoid):
     "Commander movement helper." 
 
 def movescom(iq, avoid):
     "Commander movement helper." 
@@ -676,16 +686,15 @@ def movescom(iq, avoid):
     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
     game.state.kscmdr = iq
     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
     game.state.kscmdr = iq
     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
-    if game.state.kscmdr==game.quadrant:
+    if game.state.kscmdr == game.quadrant:
        # SC has scooted, remove him from current quadrant 
        # SC has scooted, remove him from current quadrant 
-       game.iscate=False
-       game.isatb=0
+       game.iscate = False
+       game.isatb = 0
        game.ientesc = False
        unschedule(FSCDBAS)
        for enemy in game.enemies:
            if enemy.type == 'S':
        game.ientesc = False
        unschedule(FSCDBAS)
        for enemy in game.enemies:
            if enemy.type == 'S':
-               break
-       enemy.move(None)
+               enemy.move(None)
        game.klhere -= 1
        if game.condition != "docked":
            newcnd()
        game.klhere -= 1
        if game.condition != "docked":
            newcnd()
@@ -703,11 +712,14 @@ def movescom(iq, avoid):
                proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
                prout(_("   by the Super-commander.\""))
            break
                proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
                prout(_("   by the Super-commander.\""))
            break
-    return True; # looks good! 
+    return True # looks good! 
                        
 def supercommander():
     "Move the Super Commander." 
                        
 def supercommander():
     "Move the Super Commander." 
-    iq = Coord(); sc = Coord(); ibq = Coord(); idelta = Coord()
+    iq = Coord()
+    sc = Coord()
+    ibq = Coord()
+    idelta = Coord()
     basetbl = []
     if game.idebug:
        prout("== SUPERCOMMANDER")
     basetbl = []
     if game.idebug:
        prout("== SUPERCOMMANDER")
@@ -736,8 +748,8 @@ def supercommander():
         # without too many Klingons, and not already under attack. 
        ifindit = iwhichb = 0
        for (i2, base) in enumerate(game.state.baseq):
         # without too many Klingons, and not already under attack. 
        ifindit = iwhichb = 0
        for (i2, base) in enumerate(game.state.baseq):
-           i = basetbl[i2][0]; # bug in original had it not finding nearest
-           if base==game.quadrant or base==game.battle or not welcoming(base):
+           i = basetbl[i2][0]  # bug in original had it not finding nearest
+           if base == game.quadrant or base == game.battle or not welcoming(base):
                continue
            # if there is a commander, and no other base is appropriate,
            # we will take the one with the commander
                continue
            # if there is a commander, and no other base is appropriate,
            # we will take the one with the commander
@@ -750,7 +762,7 @@ def supercommander():
                ifindit = 1
                iwhichb = i
                break
                ifindit = 1
                iwhichb = i
                break
-       if ifindit==0:
+       if ifindit == 0:
            return # Nothing suitable -- wait until next time
        ibq = game.state.baseq[iwhichb]
        # decide how to move toward base 
            return # Nothing suitable -- wait until next time
        ibq = game.state.baseq[iwhichb]
        # decide how to move toward base 
@@ -763,7 +775,7 @@ def supercommander():
     iq = game.state.kscmdr + idelta
     if not movescom(iq, avoid):
        # failed -- try some other maneuvers 
     iq = game.state.kscmdr + idelta
     if not movescom(iq, avoid):
        # failed -- try some other maneuvers 
-       if idelta.i==0 or idelta.j==0:
+       if idelta.i == 0 or idelta.j == 0:
            # attempt angle move 
            if idelta.i != 0:
                iq.j = game.state.kscmdr.j + 1
            # attempt angle move 
            if idelta.i != 0:
                iq.j = game.state.kscmdr.j + 1
@@ -811,7 +823,7 @@ def supercommander():
                if not ja():
                    return
                game.resting = False
                if not ja():
                    return
                game.resting = False
-               game.optime = 0.0; # actually finished 
+               game.optime = 0.0 # actually finished 
                return
     # Check for intelligence report 
     if not game.idebug and \
                return
     # Check for intelligence report 
     if not game.idebug and \
@@ -830,13 +842,17 @@ def movetholian():
        return
     tid = Coord()
     if game.tholian.location.i == 0 and game.tholian.location.j == 0:
        return
     tid = Coord()
     if game.tholian.location.i == 0 and game.tholian.location.j == 0:
-       tid.i = 0; tid.j = QUADSIZE-1
+       tid.i = 0
+        tid.j = QUADSIZE-1
     elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
     elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
-       tid.i = QUADSIZE-1; tid.j = QUADSIZE-1
+       tid.i = QUADSIZE-1
+        tid.j = QUADSIZE-1
     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
-       tid.i = QUADSIZE-1; tid.j = 0
+       tid.i = QUADSIZE-1
+        tid.j = 0
     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
-       tid.i = 0; tid.j = 0
+       tid.i = 0
+        tid.j = 0
     else:
        # something is wrong! 
        game.tholian.move(None)
     else:
        # something is wrong! 
        game.tholian.move(None)
@@ -850,25 +866,25 @@ def movetholian():
     # move in x axis 
     while here.i != tid.i:
         here.i += delta.i
     # move in x axis 
     while here.i != tid.i:
         here.i += delta.i
-        if game.quad[here.i][here.j]=='.':
+        if game.quad[here.i][here.j] == '.':
             game.tholian.move(here)
     # move in y axis 
     while here.j != tid.j:
         here.j += delta.j
             game.tholian.move(here)
     # move in y axis 
     while here.j != tid.j:
         here.j += delta.j
-        if game.quad[here.i][here.j]=='.':
+        if game.quad[here.i][here.j] == '.':
             game.tholian.move(here)
     # check to see if all holes plugged 
     for i in range(QUADSIZE):
             game.tholian.move(here)
     # check to see if all holes plugged 
     for i in range(QUADSIZE):
-       if game.quad[0][i]!='#' and game.quad[0][i]!='T':
+       if game.quad[0][i] != '#' and game.quad[0][i] != 'T':
            return
            return
-       if game.quad[QUADSIZE-1][i]!='#' and game.quad[QUADSIZE-1][i]!='T':
+       if game.quad[QUADSIZE-1][i] != '#' and game.quad[QUADSIZE-1][i] != 'T':
            return
            return
-       if game.quad[i][0]!='#' and game.quad[i][0]!='T':
+       if game.quad[i][0] != '#' and game.quad[i][0] != 'T':
            return
            return
-       if game.quad[i][QUADSIZE-1]!='#' and game.quad[i][QUADSIZE-1]!='T':
+       if game.quad[i][QUADSIZE-1] != '#' and game.quad[i][QUADSIZE-1] != 'T':
            return
     # All plugged up -- Tholian splits 
            return
     # All plugged up -- Tholian splits 
-    game.quad[game.tholian.location.i][game.tholian.location.j]='#'
+    game.quad[game.tholian.location.i][game.tholian.location.j] = '#'
     dropin(' ')
     prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
     game.tholian.move(None)
     dropin(' ')
     prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
     game.tholian.move(None)
@@ -895,7 +911,7 @@ def doshield(shraise):
                    action = "SHUP"
                elif scanner.sees("down"):
                    action = "SHDN"
                    action = "SHUP"
                elif scanner.sees("down"):
                    action = "SHDN"
-       if action=="NONE":
+       if action == "NONE":
            proutn(_("Do you wish to change shield energy? "))
            if ja():
                action = "NRG"
            proutn(_("Do you wish to change shield energy? "))
            if ja():
                action = "NRG"
@@ -930,14 +946,14 @@ def doshield(shraise):
            prout(_("Shields raising uses up last of energy."))
            finish(FNRG)
            return
            prout(_("Shields raising uses up last of energy."))
            finish(FNRG)
            return
-       game.ididit=True
+       game.ididit = True
        return
     elif action == "SHDN":
        if not game.shldup:
            prout(_("Shields already down."))
            return
        return
     elif action == "SHDN":
        if not game.shldup:
            prout(_("Shields already down."))
            return
-       game.shldup=False
-       game.shldchg=True
+       game.shldup = False
+       game.shldchg = True
        prout(_("Shields lowered."))
        game.ididit = True
        return
        prout(_("Shields lowered."))
        game.ididit = True
        return
@@ -1009,7 +1025,7 @@ def randdevice():
        wsum += w
        if idx < wsum:
            return i
        wsum += w
        if idx < wsum:
            return i
-    return None;       # we should never get here
+    return None        # we should never get here
 
 def collision(rammed, enemy):
     "Collision handling fot rammong events."
 
 def collision(rammed, enemy):
     "Collision handling fot rammong events."
@@ -1057,7 +1073,7 @@ def collision(rammed, enemy):
 
 def torpedo(origin, bearing, dispersion, number, nburst):
     "Let a photon torpedo fly" 
 
 def torpedo(origin, bearing, dispersion, number, nburst):
     "Let a photon torpedo fly" 
-    if not damaged(DSRSENS) or game.condition=="docked":
+    if not damaged(DSRSENS) or game.condition == "docked":
        setwnd(srscan_window)
     else: 
        setwnd(message_window)
        setwnd(srscan_window)
     else: 
        setwnd(message_window)
@@ -1068,42 +1084,44 @@ def torpedo(origin, bearing, dispersion, number, nburst):
     # Loop to move a single torpedo 
     setwnd(message_window)
     for step in range(1, QUADSIZE*2):
     # Loop to move a single torpedo 
     setwnd(message_window)
     for step in range(1, QUADSIZE*2):
-        if not track.next(): break
+        if not track.next():
+            break
         w = track.sector()
        if not w.valid_sector():
            break
         w = track.sector()
        if not w.valid_sector():
            break
-       iquad=game.quad[w.i][w.j]
+       iquad = game.quad[w.i][w.j]
        tracktorpedo(w, step, number, nburst, iquad)
        tracktorpedo(w, step, number, nburst, iquad)
-       if iquad=='.':
+       if iquad == '.':
            continue
        # hit something 
            continue
        # hit something 
-       if not damaged(DSRSENS) or game.condition == "docked":
-           skip(1);    # start new line after text track 
+        setwnd(message_window)
+        if not damaged(DSRSENS) or game.condition == "docked":
+           skip(1)     # start new line after text track 
        if iquad in ('E', 'F'): # Hit our ship 
            skip(1)
            prout(_("Torpedo hits %s.") % crmshp())
            hit = 700.0 + randreal(100) - \
                1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
        if iquad in ('E', 'F'): # Hit our ship 
            skip(1)
            prout(_("Torpedo hits %s.") % crmshp())
            hit = 700.0 + randreal(100) - \
                1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
-           newcnd(); # we're blown out of dock 
-           if game.landed or game.condition=="docked":
+           newcnd() # we're blown out of dock 
+           if game.landed or game.condition == "docked":
                return hit # Cheat if on a planet 
             # In the C/FORTRAN version, dispersion was 2.5 radians, which
             # is 143 degrees, which is almost exactly 4.8 clockface units
                return hit # Cheat if on a planet 
             # 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+randreal(-2.4,2.4), distance=2**0.5)
+            displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
             displacement.next()
             bumpto = displacement.sector()
            if not bumpto.valid_sector():
                return hit
             displacement.next()
             bumpto = displacement.sector()
            if not bumpto.valid_sector():
                return hit
-           if game.quad[bumpto.i][bumpto.j]==' ':
+           if game.quad[bumpto.i][bumpto.j] == ' ':
                finish(FHOLE)
                return hit
                finish(FHOLE)
                return hit
-           if game.quad[bumpto.i][bumpto.j]!='.':
+           if game.quad[bumpto.i][bumpto.j] != '.':
                # can't move into object 
                return hit
            game.sector = bumpto
            proutn(crmshp())
                # can't move into object 
                return hit
            game.sector = bumpto
            proutn(crmshp())
-            game.quad[w.i][w.j]='.'
-            game.quad[bumpto.i][bumpto.j]=iquad
+            game.quad[w.i][w.j] = '.'
+            game.quad[bumpto.i][bumpto.j] = iquad
             prout(_(" displaced by blast to Sector %s ") % bumpto)
             for enemy in game.enemies:
                 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
             prout(_(" displaced by blast to Sector %s ") % bumpto)
             for enemy in game.enemies:
                 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
@@ -1117,46 +1135,49 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                return None
             for enemy in game.enemies:
                if w == enemy.location:
                return None
             for enemy in game.enemies:
                if w == enemy.location:
-                   break
-           kp = math.fabs(enemy.power)
-           h1 = 700.0 + randrange(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
-           h1 = math.fabs(h1)
-           if kp < h1:
-               h1 = kp
-            if enemy.power < 0:
-                enemy.power -= -h1
-            else:
-                enemy.power -= h1
-           if enemy.power == 0:
-               deadkl(w, iquad, w)
-               return None
-           proutn(crmena(True, iquad, "sector", w))
-            displacement = course(track.bearing+randreal(-2.4,2.4), distance=2**0.5)
-            displacement.next()
-            bumpto = displacement.sector()
-            if not bumpto.valid_sector():
-               prout(_(" damaged but not destroyed."))
-               return
-           if game.quad[bumpto.i][bumpto.j] == ' ':
-               prout(_(" buffeted into black hole."))
-               deadkl(w, iquad, bumpto)
-           if game.quad[bumpto.i][bumpto.j] != '.':
-               prout(_(" damaged but not destroyed."))
+                    kp = math.fabs(enemy.power)
+                    h1 = 700.0 + randrange(100) - \
+                        1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
+                    h1 = math.fabs(h1)
+                    if kp < h1:
+                        h1 = kp
+                    if enemy.power < 0:
+                        enemy.power -= -h1
+                    else:
+                        enemy.power -= h1
+                    if enemy.power == 0:
+                        deadkl(w, iquad, w)
+                        return None
+                    proutn(crmena(True, iquad, "sector", w))
+                    displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
+                    displacement.next()
+                    bumpto = displacement.sector()
+                    if not bumpto.valid_sector():
+                        prout(_(" damaged but not destroyed."))
+                        return
+                    if game.quad[bumpto.i][bumpto.j] == ' ':
+                        prout(_(" buffeted into black hole."))
+                        deadkl(w, iquad, bumpto)
+                    if game.quad[bumpto.i][bumpto.j] != '.':
+                        prout(_(" damaged but not destroyed."))
+                    else:
+                        prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
+                        enemy.location = bumpto
+                        game.quad[w.i][w.j] = '.'
+                        game.quad[bumpto.i][bumpto.j] = iquad
+                        for enemy in game.enemies:
+                            enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
+                        sortenemies()
+                    break
             else:
             else:
-                prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
-                enemy.location = bumpto
-                game.quad[w.i][w.j]='.'
-                game.quad[bumpto.i][bumpto.j]=iquad
-                for enemy in game.enemies:
-                    enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
-                sortenemies()
+                prout("Internal error, no enemy where expected!")
+                raise SystemExit, 1
             return None
        elif iquad == 'B': # Hit a base 
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
             game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
             return None
        elif iquad == 'B': # Hit a base 
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
             game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
-           game.quad[w.i][w.j]='.'
+           game.quad[w.i][w.j] = '.'
            game.base.invalidate()
            game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase -= 1
            game.state.chart[game.quadrant.i][game.quadrant.j].starbase -= 1
            game.base.invalidate()
            game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase -= 1
            game.state.chart[game.quadrant.i][game.quadrant.j].starbase -= 1
@@ -1209,7 +1230,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                # 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.
                # 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 = True
+               thing.angry()
            return None
        elif iquad == ' ': # Black hole 
            skip(1)
            return None
        elif iquad == ' ': # Black hole 
            skip(1)
@@ -1246,8 +1267,9 @@ def torpedo(origin, bearing, dispersion, number, nburst):
            return None
        break
     skip(1)
            return None
        break
     skip(1)
+    setwnd(message_window)
     prout(_("Torpedo missed."))
     prout(_("Torpedo missed."))
-    return None;
+    return None
 
 def fry(hit):
     "Critical-hit resolution." 
 
 def fry(hit):
     "Critical-hit resolution." 
@@ -1258,15 +1280,15 @@ def fry(hit):
     # Select devices and cause damage
     cdam = []
     while ncrit > 0:
     # Select devices and cause damage
     cdam = []
     while ncrit > 0:
-        ncrit -= 1
         while True:
            j = randdevice()
            # Cheat to prevent shuttle damage unless on ship 
         while True:
            j = randdevice()
            # Cheat to prevent shuttle damage unless on ship 
-            if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
+            if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship")):
                 break
        cdam.append(j)
        extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
        game.damage[j] += extradm
                 break
        cdam.append(j)
        extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
        game.damage[j] += extradm
+        ncrit -= 1
     skipcount = 0
     for (i, j) in enumerate(cdam):
        proutn(device[j])
     skipcount = 0
     for (i, j) in enumerate(cdam):
        proutn(device[j])
@@ -1278,7 +1300,7 @@ def fry(hit):
     prout(_(" damaged."))
     if damaged(DSHIELD) and game.shldup:
        prout(_("***Shields knocked down."))
     prout(_(" damaged."))
     if damaged(DSHIELD) and game.shldup:
        prout(_("***Shields knocked down."))
-       game.shldup=False
+       game.shldup = False
 
 def attack(torps_ok):
     # bad guy attacks us 
 
 def attack(torps_ok):
     # bad guy attacks us 
@@ -1286,8 +1308,11 @@ def attack(torps_ok):
     # game could be over at this point, check
     if game.alldone:
        return
     # game could be over at this point, check
     if game.alldone:
        return
-    attempt = False; ihurt = False;
-    hitmax=0.0; hittot=0.0; chgfac=1.0
+    attempt = False
+    ihurt = False
+    hitmax = 0.0
+    hittot = 0.0
+    chgfac = 1.0
     where = "neither"
     if game.idebug:
        prout("=== ATTACK!")
     where = "neither"
     if game.idebug:
        prout("=== ATTACK!")
@@ -1299,10 +1324,27 @@ def attack(torps_ok):
        game.neutz = False
        return
     # commanders get a chance to tac-move towards you 
        game.neutz = False
        return
     # commanders get a chance to tac-move towards you 
-    if (((game.quadrant in game.state.kcmdr or game.state.kscmdr==game.quadrant) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
-       moveklings()
+    if (((game.quadrant in game.state.kcmdr or game.state.kscmdr == game.quadrant) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
+        for (bugout, enemy, old, goto) in  moveklings():
+            if bugout:
+                # we know about this if either short or long range
+                # sensors are working
+                if damaged(DSRSENS) and damaged(DLRSENS) \
+                       and game.condition != "docked":
+                    prout(crmena(True, enemy.type, "sector", old) + \
+                          (_(" escapes to Quadrant %s (and regains strength).") % goto))
+            else: # Enemy still in-sector
+                if enemy.move(goto):
+                    if not damaged(DSRSENS) or game.condition == "docked":
+                        proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
+                        if enemy.kdist < old:
+                            proutn(_(" advances to "))
+                        else:
+                            proutn(_(" retreats to "))
+                        prout("Sector %s." % goto)
+        sortenemies()
     # if no enemies remain after movement, we're done 
     # if no enemies remain after movement, we're done 
-    if len(game.enemies)==0 or (len(game.enemies)==1 and thing == game.quadrant and not thing.angry):
+    if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
        return
     # set up partial hits if attack happens during shield status change 
     pfac = 1.0/game.inshld
        return
     # set up partial hits if attack happens during shield status change 
     pfac = 1.0/game.inshld
@@ -1314,29 +1356,29 @@ def attack(torps_ok):
        where = "sector"
     for enemy in game.enemies:
        if enemy.power < 0:
        where = "sector"
     for enemy in game.enemies:
        if enemy.power < 0:
-           continue;   # too weak to attack 
+           continue    # too weak to attack 
        # compute hit strength and diminish shield power 
        r = randreal()
        # Increase chance of photon torpedos if docked or enemy energy is low 
        if game.condition == "docked":
            r *= 0.25
        if enemy.power < 500:
        # compute hit strength and diminish shield power 
        r = randreal()
        # Increase chance of photon torpedos if docked or enemy energy is low 
        if game.condition == "docked":
            r *= 0.25
        if enemy.power < 500:
-           r *= 0.25; 
-       if enemy.type=='T' or (enemy.type=='?' and not thing.angry):
+           r *= 0.25 
+       if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
            continue
        # different enemies have different probabilities of throwing a torp 
        usephasers = not torps_ok or \
            (enemy.type == 'K' and r > 0.0005) or \
            continue
        # different enemies have different probabilities of throwing a torp 
        usephasers = not torps_ok or \
            (enemy.type == 'K' and r > 0.0005) or \
-           (enemy.type=='C' and r > 0.015) or \
-           (enemy.type=='R' and r > 0.3) or \
-           (enemy.type=='S' and r > 0.07) or \
-           (enemy.type=='?' and r > 0.05)
+           (enemy.type == 'C' and r > 0.015) or \
+           (enemy.type == 'R' and r > 0.3) or \
+           (enemy.type == 'S' and r > 0.07) or \
+           (enemy.type == '?' and r > 0.05)
        if usephasers:      # Enemy uses phasers 
            if game.condition == "docked":
        if usephasers:      # Enemy uses phasers 
            if game.condition == "docked":
-               continue; # Don't waste the effort! 
-           attempt = True; # Attempt to attack 
+               continue # Don't waste the effort! 
+           attempt = True # Attempt to attack 
            dustfac = randreal(0.8, 0.85)
            dustfac = randreal(0.8, 0.85)
-           hit = enemy.power*math.pow(dustfac,enemy.kavgd)
+           hit = enemy.power*math.pow(dustfac, enemy.kavgd)
            enemy.power *= 0.75
        else: # Enemy uses photon torpedo 
            # We should be able to make the bearing() method work here
            enemy.power *= 0.75
        else: # Enemy uses photon torpedo 
            # We should be able to make the bearing() method work here
@@ -1350,17 +1392,17 @@ def attack(torps_ok):
            dispersion = (randreal()+randreal())*0.5 - 0.5
            dispersion += 0.002*enemy.power*dispersion
            hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
            dispersion = (randreal()+randreal())*0.5 - 0.5
            dispersion += 0.002*enemy.power*dispersion
            hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
-           if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
-               finish(FWON); # Klingons did themselves in! 
+           if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
+               finish(FWON) # Klingons did themselves in! 
            if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
                return # Supernova or finished 
            if hit == None:
                continue
        # incoming phaser or torpedo, shields may dissipate it 
            if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
                return # Supernova or finished 
            if hit == None:
                continue
        # incoming phaser or torpedo, shields may dissipate it 
-       if game.shldup or game.shldchg or game.condition=="docked":
+       if game.shldup or game.shldchg or game.condition == "docked":
            # shields will take hits 
            propor = pfac * game.shield
            # shields will take hits 
            propor = pfac * game.shield
-            if game.condition =="docked":
+            if game.condition == "docked":
                 propor *= 2.1
            if propor < 0.1:
                propor = 0.1
                 propor *= 2.1
            if propor < 0.1:
                propor = 0.1
@@ -1426,20 +1468,20 @@ def attack(torps_ok):
     sortenemies()
     return
                
     sortenemies()
     return
                
-def deadkl(w, type, mv):
+def deadkl(w, etype, mv):
     "Kill a Klingon, Tholian, Romulan, or Thingy." 
     # Added mv to allow enemy to "move" before dying 
     "Kill a Klingon, Tholian, Romulan, or Thingy." 
     # Added mv to allow enemy to "move" before dying 
-    proutn(crmena(True, type, "sector", mv))
+    proutn(crmena(True, etype, "sector", mv))
     # Decide what kind of enemy it is and update appropriately 
     # Decide what kind of enemy it is and update appropriately 
-    if type == 'R':
+    if etype == 'R':
         # Chalk up a Romulan 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
         game.irhere -= 1
         game.state.nromrem -= 1
         # Chalk up a Romulan 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
         game.irhere -= 1
         game.state.nromrem -= 1
-    elif type == 'T':
+    elif etype == 'T':
         # Killed a Tholian 
         game.tholian = None
         # Killed a Tholian 
         game.tholian = None
-    elif type == '?':
+    elif etype == '?':
         # Killed a Thingy
         global thing
         thing = None
         # Killed a Thingy
         global thing
         thing = None
@@ -1465,7 +1507,7 @@ def deadkl(w, type, mv):
             unschedule(FSCDBAS)
     # For each kind of enemy, finish message to player 
     prout(_(" destroyed."))
             unschedule(FSCDBAS)
     # For each kind of enemy, finish message to player 
     prout(_(" destroyed."))
-    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
+    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
        return
     game.recompute()
     # Remove enemy ship from arrays describing local conditions
        return
     game.recompute()
     # Remove enemy ship from arrays describing local conditions
@@ -1481,9 +1523,9 @@ def targetcheck(w):
        huh()
        return None
     delta = Coord()
        huh()
        return None
     delta = Coord()
-    # FIXME: C code this was translated from is wacky -- why the sign reversal?
-    delta.j = (w.j - game.sector.j);
-    delta.i = (game.sector.i - w.i);
+    # C code this was translated from is wacky -- why the sign reversal?
+    delta.j = (w.j - game.sector.j)
+    delta.i = (game.sector.i - w.i)
     if delta == Coord(0, 0):
        skip(1)
        prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
     if delta == Coord(0, 0):
        skip(1)
        prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
@@ -1533,9 +1575,9 @@ def torps():
     target = []
     for i in range(n):
        key = scanner.next()
     target = []
     for i in range(n):
        key = scanner.next()
-       if i==0 and key == "IHEOL":
-           break;      # no coordinate waiting, we will try prompting 
-       if i==1 and key == "IHEOL":
+       if i == 0 and key == "IHEOL":
+           break       # no coordinate waiting, we will try prompting 
+       if i == 1 and key == "IHEOL":
            # direct all torpedoes at one target 
            while i < n:
                target.append(target[0])
            # direct all torpedoes at one target 
            while i < n:
                target.append(target[0])
@@ -1587,7 +1629,7 @@ def torps():
        if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
            return
     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
        if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
            return
     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
-       finish(FWON);
+       finish(FWON)
 
 def overheat(rpow):
     "Check for phasers overheating."
 
 def overheat(rpow):
     "Check for phasers overheating."
@@ -1631,18 +1673,18 @@ def checkshctrl(rpow):
     prout(_("Phaser energy dispersed by shields."))
     prout(_("Enemy unaffected."))
     overheat(rpow)
     prout(_("Phaser energy dispersed by shields."))
     prout(_("Enemy unaffected."))
     overheat(rpow)
-    return True;
+    return True
 
 def hittem(hits):
     "Register a phaser hit on Klingons and Romulans."
 
 def hittem(hits):
     "Register a phaser hit on Klingons and Romulans."
-    kk = 0
     w = Coord()
     skip(1)
     w = Coord()
     skip(1)
-    for (k, wham) in enumerate(hits):
-       if wham==0:
+    kk = 0
+    for wham in hits:
+       if wham == 0:
            continue
        dustfac = randreal(0.9, 1.0)
            continue
        dustfac = randreal(0.9, 1.0)
-       hit = wham*math.pow(dustfac,game.enemies[kk].kdist)
+       hit = wham*math.pow(dustfac, game.enemies[kk].kdist)
        kpini = game.enemies[kk].power
        kp = math.fabs(kpini)
        if PHASEFAC*hit < kp:
        kpini = game.enemies[kk].power
        kp = math.fabs(kpini)
        if PHASEFAC*hit < kp:
@@ -1660,20 +1702,20 @@ def hittem(hits):
        else:
            proutn(_("Very small hit on "))
        ienm = game.quad[w.i][w.j]
        else:
            proutn(_("Very small hit on "))
        ienm = game.quad[w.i][w.j]
-       if ienm=='?':
-           thing.angry = True
+       if ienm == '?':
+           thing.angry()
        proutn(crmena(False, ienm, "sector", w))
        skip(1)
        if kpow == 0:
            deadkl(w, ienm, w)
            if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
        proutn(crmena(False, ienm, "sector", w))
        skip(1)
        if kpow == 0:
            deadkl(w, ienm, w)
            if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
-               finish(FWON);           
+               finish(FWON)            
            if game.alldone:
                return
            kk -= 1     # don't do the increment
             continue
        else: # decide whether or not to emasculate klingon 
            if game.alldone:
                return
            kk -= 1     # don't do the increment
             continue
        else: # decide whether or not to emasculate klingon 
-           if kpow>0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
+           if kpow > 0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
                prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
                prout(_("   has just lost its firepower.\""))
                game.enemies[kk].power = -kpow
                prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
                prout(_("   has just lost its firepower.\""))
                game.enemies[kk].power = -kpow
@@ -1683,10 +1725,16 @@ def hittem(hits):
 def phasers():
     "Fire phasers at bad guys."
     hits = []
 def phasers():
     "Fire phasers at bad guys."
     hits = []
-    kz = 0; k = 1; irec=0 # Cheating inhibitor 
-    ifast = False; no = False; itarg = True; msgflag = True; rpow=0
+    kz = 0
+    k = 1
+    irec = 0 # Cheating inhibitor 
+    ifast = False
+    no = False
+    itarg = True
+    msgflag = True
+    rpow = 0
     automode = "NOTSET"
     automode = "NOTSET"
-    key=0
+    key = 0
     skip(1)
     # SR sensors and Computer are needed for automode 
     if damaged(DSRSENS) or damaged(DCOMPTR):
     skip(1)
     # SR sensors and Computer are needed for automode 
     if damaged(DSRSENS) or damaged(DCOMPTR):
@@ -1712,15 +1760,15 @@ def phasers():
        ifast = True
     # Original code so convoluted, I re-did it all
     # (That was Tom Almy talking about the C code, I think -- ESR)
        ifast = True
     # Original code so convoluted, I re-did it all
     # (That was Tom Almy talking about the C code, I think -- ESR)
-    while automode=="NOTSET":
-       key=scanner.next()
+    while automode == "NOTSET":
+       key = scanner.next()
        if key == "IHALPHA":
            if scanner.sees("manual"):
                if len(game.enemies)==0:
                    prout(_("There is no enemy present to select."))
                    scanner.chew()
                    key = "IHEOL"
        if key == "IHALPHA":
            if scanner.sees("manual"):
                if len(game.enemies)==0:
                    prout(_("There is no enemy present to select."))
                    scanner.chew()
                    key = "IHEOL"
-                   automode="AUTOMATIC"
+                   automode = "AUTOMATIC"
                else:
                    automode = "MANUAL"
                    key = scanner.next()
                else:
                    automode = "MANUAL"
                    key = scanner.next()
@@ -1764,18 +1812,18 @@ def phasers():
            key = scanner.next()
        if key != "IHREAL" and len(game.enemies) != 0:
            prout(_("Phasers locked on target. Energy available: %.2f")%avail)
            key = scanner.next()
        if key != "IHREAL" and len(game.enemies) != 0:
            prout(_("Phasers locked on target. Energy available: %.2f")%avail)
-       irec=0
+       irec = 0
         while True:
            scanner.chew()
            if not kz:
                for i in range(len(game.enemies)):
         while True:
            scanner.chew()
            if not kz:
                for i in range(len(game.enemies)):
-                   irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
-           kz=1
+                   irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
+           kz = 1
            proutn(_("%d units required. ") % irec)
            scanner.chew()
            proutn(_("Units to fire= "))
            key = scanner.next()
            proutn(_("%d units required. ") % irec)
            scanner.chew()
            proutn(_("Units to fire= "))
            key = scanner.next()
-           if key!="IHREAL":
+           if key != "IHREAL":
                return
            rpow = scanner.real
            if rpow > avail:
                return
            rpow = scanner.real
            if rpow > avail:
@@ -1784,15 +1832,15 @@ def phasers():
                key = "IHEOL"
             if not rpow > avail:
                 break
                key = "IHEOL"
             if not rpow > avail:
                 break
-       if rpow<=0:
+       if rpow <= 0:
            # chicken out 
            scanner.chew()
            return
            # chicken out 
            scanner.chew()
            return
-        key=scanner.next()
+        key = scanner.next()
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
        if ifast:
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
        if ifast:
-           game.energy -= 200; # Go and do it! 
+           game.energy -= 200 # Go and do it! 
            if checkshctrl(rpow):
                return
        scanner.chew()
            if checkshctrl(rpow):
                return
        scanner.chew()
@@ -1805,7 +1853,7 @@ def phasers():
                hits.append(0.0)
                if powrem <= 0:
                    continue
                hits.append(0.0)
                if powrem <= 0:
                    continue
-               hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))
+               hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))
                over = randreal(1.01, 1.06) * hits[i]
                temp = powrem
                powrem -= hits[i] + over
                over = randreal(1.01, 1.06) * hits[i]
                temp = powrem
                powrem -= hits[i] + over
@@ -1854,13 +1902,13 @@ def phasers():
                prout(cramen(ienm) + _(" can't be located without short range scan."))
                scanner.chew()
                key = "IHEOL"
                prout(cramen(ienm) + _(" can't be located without short range scan."))
                scanner.chew()
                key = "IHEOL"
-               hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko 
+               hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko 
                k += 1
                continue
            if key == "IHEOL":
                scanner.chew()
                if itarg and k > kz:
                k += 1
                continue
            if key == "IHEOL":
                scanner.chew()
                if itarg and k > kz:
-                   irec=(abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9,game.enemies[k].kdist))) *  randreal(1.01, 1.06) + 1.0
+                   irec = (abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9, game.enemies[k].kdist))) *       randreal(1.01, 1.06) + 1.0
                kz = k
                proutn("(")
                if not damaged(DCOMPTR):
                kz = k
                proutn("(")
                if not damaged(DCOMPTR):
@@ -1878,7 +1926,7 @@ def phasers():
                huh()
                return
            if key == "IHEOL":
                huh()
                return
            if key == "IHEOL":
-               if k==1: # Let me say I'm baffled by this 
+               if k == 1: # Let me say I'm baffled by this 
                    msgflag = True
                continue
            if scanner.real < 0:
                    msgflag = True
                continue
            if scanner.real < 0:
@@ -1892,7 +1940,7 @@ def phasers():
                prout(_("Available energy exceeded -- try again."))
                scanner.chew()
                return
                prout(_("Available energy exceeded -- try again."))
                scanner.chew()
                return
-           key = scanner.next(); # scan for next value 
+           key = scanner.next() # scan for next value 
            k += 1
        if rpow == 0.0:
            # zero energy -- abort 
            k += 1
        if rpow == 0.0:
            # zero energy -- abort 
@@ -1923,7 +1971,7 @@ def phasers():
                prout(_("Shields raised."))
        else:
            game.shldup = False
                prout(_("Shields raised."))
        else:
            game.shldup = False
-    overheat(rpow);
+    overheat(rpow)
 
 # Code from events,c begins here.
 
 
 # Code from events,c begins here.
 
@@ -1967,11 +2015,15 @@ def cancelrest():
 
 def events():
     "Run through the event queue looking for things to do."
 
 def events():
     "Run through the event queue looking for things to do."
-    i=0
-    fintim = game.state.date + game.optime; yank=0
-    ictbeam = False; istract = False
-    w = Coord(); hold = Coord()
-    ev = Event(); ev2 = Event()
+    i = 0
+    fintim = game.state.date + game.optime
+    yank = 0
+    ictbeam = False
+    istract = False
+    w = Coord()
+    hold = Coord()
+    ev = Event()
+    ev2 = Event()
 
     def tractorbeam(yank):
         "Tractor-beaming cases merge here." 
 
     def tractorbeam(yank):
         "Tractor-beaming cases merge here." 
@@ -2013,8 +2065,8 @@ def events():
             else:
                 prout(_("(Shields not currently useable.)"))
         newqad()
             else:
                 prout(_("(Shields not currently useable.)"))
         newqad()
-        # Adjust finish time to time of tractor beaming 
-        fintim = game.state.date+game.optime
+        # Adjust finish time to time of tractor beaming? 
+        fintim = game.state.date+game.optime
         attack(torps_ok=False)
         if not game.state.kcmdr:
             unschedule(FTBEAM)
         attack(torps_ok=False)
         if not game.state.kcmdr:
             unschedule(FTBEAM)
@@ -2089,11 +2141,11 @@ def events():
        # Decrement Federation resources and recompute remaining time 
        game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
         game.recompute()
        # Decrement Federation resources and recompute remaining time 
        game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
         game.recompute()
-       if game.state.remtime <=0:
+       if game.state.remtime <= 0:
            finish(FDEPLETE)
            return
        # Any crew left alive? 
            finish(FDEPLETE)
            return
        # Any crew left alive? 
-       if game.state.crew <=0:
+       if game.state.crew <= 0:
            finish(FCREW)
            return
        # Is life support adequate? 
            finish(FCREW)
            return
        # Is life support adequate? 
@@ -2137,7 +2189,7 @@ def events():
        elif evcode == FSPY: # Check with spy to see if SC should tractor beam 
            if game.state.nscrem == 0 or \
                ictbeam or istract or \
        elif evcode == FSPY: # Check with spy to see if SC should tractor beam 
            if game.state.nscrem == 0 or \
                ictbeam or istract or \
-                game.condition=="docked" or game.isatb==1 or game.iscate:
+                game.condition == "docked" or game.isatb == 1 or game.iscate:
                return
            if game.ientesc or \
                (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
                return
            if game.ientesc or \
                (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
@@ -2175,9 +2227,9 @@ def events():
                 continue
             try:
                 for ibq in game.state.baseq:
                 continue
             try:
                 for ibq in game.state.baseq:
-                   for cmdr in game.state.kcmdr: 
-                       if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
-                           raise JumpOut
+                    for cmdr in game.state.kcmdr: 
+                        if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
+                            raise JumpOut
                 else:
                     # no match found -- try later 
                     schedule(FBATTAK, expran(0.3*game.intime))
                 else:
                     # no match found -- try later 
                     schedule(FBATTAK, expran(0.3*game.intime))
@@ -2211,7 +2263,7 @@ def events():
            game.battle = game.state.kscmdr
            destroybase()
        elif evcode == FCDBAS: # Commander succeeds in destroying base 
            game.battle = game.state.kscmdr
            destroybase()
        elif evcode == FCDBAS: # Commander succeeds in destroying base 
-           if evcode==FCDBAS:
+           if evcode == FCDBAS:
                unschedule(FCDBAS)
                 if not game.state.baseq() \
                        or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
                unschedule(FCDBAS)
                 if not game.state.baseq() \
                        or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
@@ -2289,7 +2341,7 @@ def events():
            # tell the captain about it if we can 
            if communicating():
                prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
            # tell the captain about it if we can 
            if communicating():
                prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
-                        % (q.planet, `w`))
+                        % (q.planet, repr(w)))
                prout(_("by a Klingon invasion fleet."))
                if cancelrest():
                    return
                prout(_("by a Klingon invasion fleet."))
                if cancelrest():
                    return
@@ -2320,7 +2372,7 @@ def events():
            if q.klingons <= 0:
                q.status = "secure"
                continue
            if q.klingons <= 0:
                q.status = "secure"
                continue
-           if game.state.remkl >=MAXKLGAME:
+           if game.state.remkl >= MAXKLGAME:
                continue                # full right now 
            # reproduce one Klingon 
            w = ev.quadrant
                continue                # full right now 
            # reproduce one Klingon 
            w = ev.quadrant
@@ -2431,7 +2483,7 @@ def nova(nov):
         start = hits.pop()
         for offset.i in range(-1, 1+1):
             for offset.j in range(-1, 1+1):
         start = hits.pop()
         for offset.i in range(-1, 1+1):
             for offset.j in range(-1, 1+1):
-                if offset.j==0 and offset.i==0:
+                if offset.j == 0 and offset.i == 0:
                     continue
                 neighbor = start + offset
                 if not neighbor.valid_sector():
                     continue
                 neighbor = start + offset
                 if not neighbor.valid_sector():
@@ -2458,7 +2510,7 @@ def nova(nov):
                     if iquad == 'P':
                         game.state.nplankl += 1
                     else:
                     if iquad == 'P':
                         game.state.nplankl += 1
                     else:
-                        game.state.worldkl += 1
+                        game.state.nworldkl += 1
                     prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
                     game.iplnet.pclass = "destroyed"
                     game.iplnet = None
                     prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
                     game.iplnet.pclass = "destroyed"
                     game.iplnet = None
@@ -3090,16 +3142,16 @@ def iostart():
        curses.nonl()
        curses.cbreak()
         if game.options & OPTION_COLOR:
        curses.nonl()
        curses.cbreak()
         if game.options & OPTION_COLOR:
-            curses.start_color();
+            curses.start_color()
             curses.use_default_colors()
             curses.use_default_colors()
-            curses.init_pair(curses.COLOR_BLACK,   curses.COLOR_BLACK, -1);
-            curses.init_pair(curses.COLOR_GREEN,   curses.COLOR_GREEN, -1);
-            curses.init_pair(curses.COLOR_RED,     curses.COLOR_RED, -1);
-            curses.init_pair(curses.COLOR_CYAN,    curses.COLOR_CYAN, -1);
-            curses.init_pair(curses.COLOR_WHITE,   curses.COLOR_WHITE, -1);
-            curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1);
-            curses.init_pair(curses.COLOR_BLUE,    curses.COLOR_BLUE, -1);
-            curses.init_pair(curses.COLOR_YELLOW,  curses.COLOR_YELLOW, -1);
+            curses.init_pair(curses.COLOR_BLACK,   curses.COLOR_BLACK, -1)
+            curses.init_pair(curses.COLOR_GREEN,   curses.COLOR_GREEN, -1)
+            curses.init_pair(curses.COLOR_RED,     curses.COLOR_RED, -1)
+            curses.init_pair(curses.COLOR_CYAN,    curses.COLOR_CYAN, -1)
+            curses.init_pair(curses.COLOR_WHITE,   curses.COLOR_WHITE, -1)
+            curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1)
+            curses.init_pair(curses.COLOR_BLUE,    curses.COLOR_BLUE, -1)
+            curses.init_pair(curses.COLOR_YELLOW,  curses.COLOR_YELLOW, -1)
         global fullscreen_window, srscan_window, report_window, status_window
         global lrscan_window, message_window, prompt_window
         (rows, columns)   = stdscr.getmaxyx()
         global fullscreen_window, srscan_window, report_window, status_window
         global lrscan_window, message_window, prompt_window
         (rows, columns)   = stdscr.getmaxyx()
@@ -3150,7 +3202,8 @@ def pause_game():
         global linecount
         sys.stdout.write('\n')
         proutn(prompt)
         global linecount
         sys.stdout.write('\n')
         proutn(prompt)
-        raw_input()
+        if not replayfp:
+            raw_input()
         sys.stdout.write('\n' * rows)
         linecount = 0
 
         sys.stdout.write('\n' * rows)
         linecount = 0
 
@@ -3179,6 +3232,9 @@ def proutn(line):
        if curwnd == message_window and y >= my - 2:
            pause_game()
            clrscr()
        if curwnd == message_window and y >= my - 2:
            pause_game()
            clrscr()
+        # Uncomment this to debug curses problems
+        if logfp:
+            logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(line)))
        curwnd.addstr(line)
        curwnd.refresh()
     else:
        curwnd.addstr(line)
        curwnd.refresh()
     else:
@@ -3228,8 +3284,31 @@ def setwnd(wnd):
     "Change windows -- OK for this to be a no-op in tty mode."
     global curwnd
     if game.options & OPTION_CURSES:
     "Change windows -- OK for this to be a no-op in tty mode."
     global curwnd
     if game.options & OPTION_CURSES:
+        # Uncomment this to debug curses problems
+        if logfp:
+            if wnd == fullscreen_window:
+                legend = "fullscreen"
+            elif wnd == srscan_window:
+                legend = "srscan"
+            elif wnd == report_window:
+                legend = "report"
+            elif wnd == status_window:
+                legend = "status"
+            elif wnd == lrscan_window:
+                legend = "lrscan"
+            elif wnd == message_window:
+                legend = "message"
+            elif wnd == prompt_window:
+                legend = "prompt"
+            else:
+                legend = "unknown"
+            logfp.write("#curses: setwnd(%s)\n" % legend)
         curwnd = wnd
         curwnd = wnd
-        curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
+        # Some curses implementations get confused when you try this.
+        try:
+            curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
+        except curses.error:
+            pass
 
 def clreol():
     "Clear to end of line -- can be a no-op in tty mode" 
 
 def clreol():
     "Clear to end of line -- can be a no-op in tty mode" 
@@ -3241,47 +3320,47 @@ def clrscr():
     "Clear screen -- can be a no-op in tty mode."
     global linecount
     if game.options & OPTION_CURSES:
     "Clear screen -- can be a no-op in tty mode."
     global linecount
     if game.options & OPTION_CURSES:
-       curwnd.clear()
-       curwnd.move(0, 0)
-       curwnd.refresh()
+        curwnd.clear()
+        curwnd.move(0, 0)
+        curwnd.refresh()
     linecount = 0
 
 def textcolor(color=DEFAULT):
     if game.options & OPTION_COLOR:
        if color == DEFAULT: 
     linecount = 0
 
 def textcolor(color=DEFAULT):
     if game.options & OPTION_COLOR:
        if color == DEFAULT: 
-           curwnd.attrset(0);
+           curwnd.attrset(0)
        elif color ==  BLACK: 
        elif color ==  BLACK: 
-           curwnd.attron(curses.color_pair(curses.COLOR_BLACK));
+           curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
        elif color ==  BLUE: 
        elif color ==  BLUE: 
-           curwnd.attron(curses.color_pair(curses.COLOR_BLUE));
+           curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
        elif color ==  GREEN: 
        elif color ==  GREEN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_GREEN));
+           curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
        elif color ==  CYAN: 
        elif color ==  CYAN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_CYAN));
+           curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
        elif color ==  RED: 
        elif color ==  RED: 
-           curwnd.attron(curses.color_pair(curses.COLOR_RED));
+           curwnd.attron(curses.color_pair(curses.COLOR_RED))
        elif color ==  MAGENTA: 
        elif color ==  MAGENTA: 
-           curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA));
+           curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
        elif color ==  BROWN: 
        elif color ==  BROWN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_YELLOW));
+           curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
        elif color ==  LIGHTGRAY: 
        elif color ==  LIGHTGRAY: 
-           curwnd.attron(curses.color_pair(curses.COLOR_WHITE));
+           curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
        elif color ==  DARKGRAY: 
        elif color ==  DARKGRAY: 
-           curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD)
        elif color ==  LIGHTBLUE: 
        elif color ==  LIGHTBLUE: 
-           curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD)
        elif color ==  LIGHTGREEN: 
        elif color ==  LIGHTGREEN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
        elif color ==  LIGHTCYAN: 
        elif color ==  LIGHTCYAN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD)
        elif color ==  LIGHTRED: 
        elif color ==  LIGHTRED: 
-           curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD)
        elif color ==  LIGHTMAGENTA: 
        elif color ==  LIGHTMAGENTA: 
-           curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
        elif color ==  YELLOW: 
        elif color ==  YELLOW: 
-           curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
        elif color ==  WHITE:
        elif color ==  WHITE:
-           curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD);
+           curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
 
 def highvideo():
     if game.options & OPTION_COLOR:
 
 def highvideo():
     if game.options & OPTION_COLOR:
@@ -3794,7 +3873,7 @@ def impulse():
        scanner.chew()
        return
     # Make sure enough time is left for the trip 
        scanner.chew()
        return
     # Make sure enough time is left for the trip 
-    game.optime = course.dist/0.095
+    game.optime = course.distance/0.095
     if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
     if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
@@ -3806,9 +3885,9 @@ def impulse():
     game.ididit = True
     if game.alldone:
        return
     game.ididit = True
     if game.alldone:
        return
-    power = 20.0 + 100.0*course.dist
+    power = 20.0 + 100.0*course.distance
     game.energy -= power
     game.energy -= power
-    game.optime = course.dist/0.095
+    game.optime = course.distance/0.095
     if game.energy <= 0:
        finish(FNRG)
     return
     if game.energy <= 0:
        finish(FNRG)
     return
@@ -3844,7 +3923,7 @@ def warp(wcourse, involuntary):
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*wcourse.power(game.warpfac) > game.energy:
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*wcourse.power(game.warpfac) > game.energy:
-               iwarp = (game.energy/(wcourse.dist+0.05)) ** 0.333333333
+               iwarp = (game.energy/(wcourse.distance+0.05)) ** 0.333333333
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
@@ -4205,9 +4284,9 @@ def mayday():
        finish(FMATERIALIZE)
        return
     game.quad[game.sector.i][game.sector.j]=game.ship
        finish(FMATERIALIZE)
        return
     game.quad[game.sector.i][game.sector.j]=game.ship
-    textcolor(GREEN);
+    textcolor(GREEN)
     prout(_("succeeds."))
     prout(_("succeeds."))
-    textcolor(DEFAULT);
+    textcolor(DEFAULT)
     dock(False)
     skip(1)
     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
     dock(False)
     skip(1)
     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
@@ -4961,7 +5040,7 @@ def sectscan(goodScan, i, j):
                    "docked":CYAN,
                    "dead":BROWN}[game.condition]) 
         if game.quad[i][j] != game.ship: 
                    "docked":CYAN,
                    "dead":BROWN}[game.condition]) 
         if game.quad[i][j] != game.ship: 
-            highvideo();
+            highvideo()
        proutn("%c " % game.quad[i][j])
         textcolor(DEFAULT)
     else:
        proutn("%c " % game.quad[i][j])
         textcolor(DEFAULT)
     else:
@@ -5714,7 +5793,7 @@ def newqad():
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
     # Put in THING if needed
     if thing == game.quadrant:
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
     # Put in THING if needed
     if thing == game.quadrant:
-        Enemy(type='?', loc=dropin(),
+        Enemy(etype='?', loc=dropin(),
                   power=randreal(6000,6500.0)+250.0*game.skill)
         if not damaged(DSRSENS):
             skip(1)
                   power=randreal(6000,6500.0)+250.0*game.skill)
         if not damaged(DSRSENS):
             skip(1)
@@ -5731,7 +5810,7 @@ def newqad():
                w.j = withprob(0.5) * (QUADSIZE-1)
                 if game.quad[w.i][w.j] == '.':
                     break
                w.j = withprob(0.5) * (QUADSIZE-1)
                 if game.quad[w.i][w.j] == '.':
                     break
-            game.tholian = Enemy(type='T', loc=w,
+            game.tholian = Enemy(etype='T', loc=w,
                                  power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
            if game.quad[0][0]=='.':
                                  power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
            if game.quad[0][0]=='.':
@@ -5916,12 +5995,12 @@ def makemoves():
                    abandon_passed = True
                if cmd == scanner.token.upper() or (not abandon_passed \
                        and cmd.startswith(scanner.token.upper())):
                    abandon_passed = True
                if cmd == scanner.token.upper() or (not abandon_passed \
                        and cmd.startswith(scanner.token.upper())):
-                   break;
+                   break
            if cmd == "":
                 listCommands()
                 continue
             else:
            if cmd == "":
                 listCommands()
                 continue
             else:
-               break;
+               break
        if cmd == "SRSCAN":             # srscan
            srscan()
        elif cmd == "STATUS":           # status
        if cmd == "SRSCAN":             # srscan
            srscan()
        elif cmd == "STATUS":           # status
@@ -6275,8 +6354,7 @@ if __name__ == '__main__':
     try:
         global line, thing, game
         game = None
     try:
         global line, thing, game
         game = None
-        thing = Coord()
-        thing.angry = False
+        thing = Thingy()
         game = Gamestate()
         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
         if os.getenv("TERM"):
         game = Gamestate()
         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
         if os.getenv("TERM"):
@@ -6285,6 +6363,7 @@ if __name__ == '__main__':
             game.options |= OPTION_TTY
         seed = int(time.time())
         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:txV")
             game.options |= OPTION_TTY
         seed = int(time.time())
         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:txV")
+        replay = False
         for (switch, val) in options:
             if switch == '-r':
                 try:
         for (switch, val) in options:
             if switch == '-r':
                 try:
@@ -6294,11 +6373,12 @@ if __name__ == '__main__':
                     raise SystemExit, 1
                 try:
                     line = replayfp.readline().strip()
                     raise SystemExit, 1
                 try:
                     line = replayfp.readline().strip()
-                    (leader, key, seed) = line.split()
+                    (leader, __, seed) = line.split()
                     seed = eval(seed)
                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
                     line = replayfp.readline().strip()
                     arguments += line.split()[2:]
                     seed = eval(seed)
                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
                     line = replayfp.readline().strip()
                     arguments += line.split()[2:]
+                    replay = True
                 except ValueError:
                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
                     raise SystemExit(1)
                 except ValueError:
                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
                     raise SystemExit(1)
@@ -6330,6 +6410,7 @@ if __name__ == '__main__':
         if logfp:
             logfp.write("# seed %s\n" % seed)
             logfp.write("# options %s\n" % " ".join(arguments))
         if logfp:
             logfp.write("# seed %s\n" % seed)
             logfp.write("# options %s\n" % " ".join(arguments))
+            logfp.write("# SST2K version %s\n" % version)
             logfp.write("# recorded by %s@%s on %s\n" % \
                     (getpass.getuser(),socket.gethostname(),time.ctime()))
         random.seed(seed)
             logfp.write("# recorded by %s@%s on %s\n" % \
                     (getpass.getuser(),socket.gethostname(),time.ctime()))
         random.seed(seed)
@@ -6347,6 +6428,8 @@ if __name__ == '__main__':
                     game.alldone = False
                 else:
                     makemoves()
                     game.alldone = False
                 else:
                     makemoves()
+                if replay:
+                    break
                 skip(1)
                 stars()
                 skip(1)
                 skip(1)
                 stars()
                 skip(1)