Make sure probe findings get charted.
[super-star-trek.git] / sst.py
diff --git a/sst.py b/sst.py
old mode 100644 (file)
new mode 100755 (executable)
index 5bc4bac..d037ff4
--- 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
 
-version="2.0"
+version = "2.1"
 
 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
@@ -52,17 +53,20 @@ LIGHTMAGENTA = 13
 YELLOW       = 14
 WHITE        = 15
 
-class TrekError:
+class TrekError(Exception):
     pass
 
-class coord:
+class JumpOut(Exception):
+    pass 
+
+class Coord:
     def __init__(self, x=None, y=None):
         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):
-       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):
@@ -72,28 +76,29 @@ class coord:
     def __ne__(self, other):
         return other == None or self.i != other.i or self.j != other.j
     def __add__(self, other):
-        return coord(self.i+other.i, self.j+other.j)
+        return Coord(self.i+other.i, self.j+other.j)
     def __sub__(self, other):
-        return coord(self.i-other.i, self.j-other.j)
+        return Coord(self.i-other.i, self.j-other.j)
     def __mul__(self, other):
-        return coord(self.i*other, self.j*other)
+        return Coord(self.i*other, self.j*other)
     def __rmul__(self, other):
-        return coord(self.i*other, self.j*other)
+        return Coord(self.i*other, self.j*other)
     def __div__(self, other):
-        return coord(self.i/other, self.j/other)
+        return Coord(self.i/other, self.j/other)
     def __mod__(self, other):
-        return coord(self.i % other, self.j % other)
+        return Coord(self.i % other, self.j % other)
     def __rdiv__(self, other):
-        return coord(self.i/other, self.j/other)
+        return Coord(self.i/other, self.j/other)
     def roundtogrid(self):
-        return coord(int(round(self.i)), int(round(self.j)))
+        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)
     def sgn(self):
-        s = coord()
+        s = Coord()
         if self.i == 0:
             s.i = 0
         else:
@@ -109,7 +114,7 @@ class coord:
     def sector(self):
         return self.roundtogrid() % QUADSIZE
     def scatter(self):
-        s = coord()
+        s = Coord()
         s.i = self.i + randrange(-1, 2)
         s.j = self.j + randrange(-1, 2)
         return s
@@ -119,18 +124,28 @@ class coord:
         return "%s - %s" % (self.i+1, self.j+1)
     __repr__ = __str__
 
-class planet:
+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
-        self.quadrant = coord()        # quadrant located
+        self.quadrant = Coord()        # quadrant located
         self.pclass = None     # could be ""M", "N", "O", or "destroyed"
         self.crystals = "absent"# could be "mined", "present", "absent"
         self.known = "unknown" # could be "unknown", "known", "shuttle_down"
-        self.inhabited = False # is it inhabites?
+        self.inhabited = False # is it inhabited?
     def __str__(self):
         return self.name
 
-class quadrant:
+class Quadrant:
     def __init__(self):
         self.stars = 0
         self.planet = None
@@ -141,11 +156,13 @@ class quadrant:
        self.charted = False
         self.status = "secure" # Could be "secure", "distressed", "enslaved"
 
-class page:
+class Page:
     def __init__(self):
        self.stars = None
-       self.starbase = None
+       self.starbase = False
        self.klingons = None
+    def __repr__(self):
+        return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars)
 
 def fill2d(size, fillfun):
     "Fill an empty list in 2D."
@@ -156,7 +173,7 @@ def fill2d(size, fillfun):
             lst[i].append(fillfun(i, j))
     return lst
 
-class snapshot:
+class Snapshot:
     def __init__(self):
         self.snap = False      # snapshot taken
         self.crew = 0          # crew complement
@@ -173,13 +190,13 @@ class snapshot:
        self.remtime = 0        # remaining time
         self.baseq = []        # Base quadrant coordinates
         self.kcmdr = []        # Commander quadrant coordinates
-       self.kscmdr = coord()   # Supercommander quadrant coordinates
+       self.kscmdr = Coord()   # Supercommander quadrant coordinates
         # the galaxy
-        self.galaxy = fill2d(GALSIZE, lambda i, j: quadrant())
+        self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
         # the starchart
-       self.chart = fill2d(GALSIZE, lambda i, j: page())
+       self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
 
-class event:
+class Event:
     def __init__(self):
         self.date = None       # A real number
         self.quadrant = None   # A coord structure
@@ -205,23 +222,23 @@ OPTION_ALMY       = 0x02000000    # user chose Almy variant
 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
@@ -230,8 +247,10 @@ SKILL_GOOD = 3
 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)
@@ -251,12 +270,15 @@ NEVENTS   = 12
 
 # 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:
-    def __init__(self, type=None, loc=None, power=None):
-        self.type = type
-        self.location = coord()
+class Enemy:
+    def __init__(self, etype=None, loc=None, power=None):
+        self.type = etype
+        self.location = Coord()
+        self.kdist = None
+        self.kavgd = None
         if loc:
             self.move(loc)
         self.power = power     # enemy energy level
@@ -274,24 +296,26 @@ class enemy:
             game.quad[self.location.i][self.location.j] = self.type
             self.kdist = self.kavgd = (game.sector - loc).distance()
         else:
-            self.location = coord()
+            self.location = Coord()
             self.kdist = self.kavgd = None
             game.enemies.remove(self)
         return motion
     def __repr__(self):
         return "<%s,%s.%f>" % (self.type, self.location, self.power)   # For debugging
 
-class gamestate:
+class Gamestate:
     def __init__(self):
         self.options = None    # Game options
-        self.state = snapshot()        # A snapshot structure
-        self.snapsht = snapshot()      # Last snapshot taken for time-travel purposes
+        self.state = Snapshot()        # A snapshot structure
+        self.snapsht = Snapshot()      # Last snapshot taken for time-travel purposes
         self.quad = None       # contents of our quadrant
         self.damage = [0.0] * NDEVICES # damage encountered
-        self.future = []               # future events
-        for i in range(NEVENTS):
-            self.future.append(event())
-        self.passwd  = None;           # Self Destruct password
+        self.future = []       # future events
+        i = NEVENTS
+        while i > 0:
+            i -= 1
+            self.future.append(Event())
+        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
@@ -358,6 +382,10 @@ class gamestate:
         self.cryprob = 0.0     # probability that crystal will work
         self.probe = None      # object holding probe course info
         self.height = 0.0      # height of orbit around planet
+        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 ?
@@ -365,7 +393,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.
-        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
@@ -414,30 +442,25 @@ def welcoming(iq):
 
 def tryexit(enemy, look, irun):
     "A bad guy attempts to bug out."
-    iq = coord()
+    iq = Coord()
     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':
-       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:
-                return False
+                return []
            # 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:
-           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).") % q))
+           return []
+    oldloc = copy.copy(enemy.location)
     # handle local matters related to escape
     enemy.move(None)
     game.klhere -= 1
@@ -446,19 +469,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
-    if enemy.type=='S':
+    if enemy.type == 'S':
        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[n] = iq
+               game.state.kcmdr.append(iq)
                break
-    return True; # success 
+    # report move out of quadrant.
+    return [(True, enemy, oldloc, ibq)]
 
 # The bad-guy movement algorithm:
 # 
@@ -500,17 +524,18 @@ def tryexit(enemy, look, irun):
 
 def movebaddy(enemy):
     "Tactical movement for the bad guys."
-    next = 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)
-    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 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
@@ -518,7 +543,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:
-           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
@@ -536,14 +561,14 @@ def movebaddy(enemy):
            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 idebug:
+       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:
@@ -553,12 +578,12 @@ def movebaddy(enemy):
     # 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:
-       nsteps = QUADSIZE; # This shouldn't be necessary 
+       nsteps = QUADSIZE # This shouldn't be necessary 
     if nsteps < 1:
-       nsteps = 1; # This shouldn't be necessary 
-    if idebug:
+       nsteps = 1 # This shouldn't be necessary 
+    if game.idebug:
        proutn("NSTEPS = %d:" % nsteps)
     # Compute preferred values of delta X and Y 
     m = game.sector - enemy.location
@@ -567,13 +592,13 @@ def movebaddy(enemy):
     if 2.0 * abs(m.j) < abs(game.sector.i-enemy.location.i):
        m.j = 0
     m = (motion * m).sgn()
-    next = enemy.location
+    goto = enemy.location
     # main move loop 
     for ll in range(nsteps):
-       if idebug:
+       if game.idebug:
            proutn(" %d" % (ll+1))
        # Check if preferred position available 
-       look = next + m
+       look = goto + m
         if m.i < 0:
             krawli = 1
         else:
@@ -583,70 +608,65 @@ def movebaddy(enemy):
         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:
-               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 = next.i + krawli
+               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 = next.j + krawlj
+               look.j = goto.j + krawlj
                krawlj = -krawlj
            elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
                # See if enemy should ram ship 
                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 = next.i + krawli
+                   look.i = goto.i + krawli
                    krawli = -krawli
                elif krawlj != m.j and m.i != 0:
-                   look.j = next.j + krawlj
+                   look.j = goto.j + krawlj
                    krawlj = -krawlj
                else:
-                   break; # we have failed 
+                   break # we have failed 
            else:
                success = True
        if success:
-           next = look
-           if idebug:
-               proutn(`next`)
+           goto = look
+           if game.idebug:
+               proutn(repr(goto))
        else:
-           break; # done early 
-    if idebug:
+           break # done early 
+    if game.idebug:
        skip(1)
-    if enemy.move(next):
-       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." % next)
+    # Enemy moved, but is still in sector
+    return [(False, enemy, old_dist, goto)]
 
 def moveklings():
     "Sequence Klingon tactical movement."
-    if idebug:
+    if game.idebug:
        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':
-               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':
-               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
@@ -654,8 +674,8 @@ def moveklings():
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
         for enemy in game.enemies:
             if enemy.type in ('K', 'R'):
-               movebaddy(enemy)
-    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+               tacmoves += movebaddy(enemy)
+    return tacmoves
 
 def movescom(iq, avoid):
     "Commander movement helper." 
@@ -668,20 +688,19 @@ 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
-    if game.state.kscmdr==game.quadrant:
+    if game.state.kscmdr == game.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':
-               break
-       enemy.move(None)
+               enemy.move(None)
        game.klhere -= 1
        if game.condition != "docked":
            newcnd()
-        game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+        sortenemies()
     # check for a helpful planet 
     for i in range(game.inplan):
        if game.state.planets[i].quadrant == game.state.kscmdr and \
@@ -695,13 +714,16 @@ def movescom(iq, avoid):
                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." 
-    iq = coord(); sc = coord(); ibq = coord(); idelta = coord()
+    iq = Coord()
+    sc = Coord()
+    ibq = Coord()
+    idelta = Coord()
     basetbl = []
-    if idebug:
+    if game.idebug:
        prout("== SUPERCOMMANDER")
     # Decide on being active or passive 
     avoid = ((game.incom - len(game.state.kcmdr) + game.inkling - game.state.remkl)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
@@ -720,16 +742,16 @@ def supercommander():
            unschedule(FSCMOVE)
            return
        sc = game.state.kscmdr
-        for base in game.state.baseq:
+        for (i, base) in enumerate(game.state.baseq):
            basetbl.append((i, (base - sc).distance()))
        if game.state.baseq > 1:
-            basetbl.sort(lambda x, y: cmp(x[1]. y[1]))
+            basetbl.sort(lambda x, y: cmp(x[1], y[1]))
        # look for nearest base without a commander, no Enterprise, and
         # 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
@@ -742,7 +764,7 @@ def supercommander():
                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 
@@ -755,7 +777,7 @@ def supercommander():
     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
@@ -800,13 +822,13 @@ def supercommander():
                if not game.resting:
                    return
                prout(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
-               if ja() == 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 idebug and \
+    if not game.idebug and \
        (withprob(0.8) or \
         (not communicating()) or \
         not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
@@ -820,47 +842,51 @@ def movetholian():
     "Move the Tholian."
     if not game.tholian or game.justin:
        return
-    id = coord()
+    tid = Coord()
     if game.tholian.location.i == 0 and game.tholian.location.j == 0:
-       id.i = 0; id.j = QUADSIZE-1
+       tid.i = 0
+        tid.j = QUADSIZE-1
     elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
-       id.i = QUADSIZE-1; id.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:
-       id.i = QUADSIZE-1; id.j = 0
+       tid.i = QUADSIZE-1
+        tid.j = 0
     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
-       id.i = 0; id.j = 0
+       tid.i = 0
+        tid.j = 0
     else:
        # something is wrong! 
        game.tholian.move(None)
         prout("***Internal error: Tholian in a bad spot.")
        return
     # do nothing if we are blocked 
-    if game.quad[id.i][id.j] not in ('.', '#'):
+    if game.quad[tid.i][tid.j] not in ('.', '#'):
        return
     here = copy.copy(game.tholian.location)
-    delta = (id - game.tholian.location).sgn()
+    delta = (tid - game.tholian.location).sgn()
     # move in x axis 
-    while here.i != id.i:
+    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 != id.j:
+    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):
-       if game.quad[0][i]!='#' and game.quad[0][i]!='T':
+       if game.quad[0][i] != '#' and game.quad[0][i] != 'T':
            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
-       if game.quad[i][0]!='#' and game.quad[i][0]!='T':
+       if game.quad[i][0] != '#' and game.quad[i][0] != 'T':
            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 
-    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)
@@ -887,27 +913,27 @@ def doshield(shraise):
                    action = "SHUP"
                elif scanner.sees("down"):
                    action = "SHDN"
-       if action=="NONE":
+       if action == "NONE":
            proutn(_("Do you wish to change shield energy? "))
-           if ja() == True:
+           if ja():
                action = "NRG"
            elif damaged(DSHIELD):
                prout(_("Shields damaged and down."))
                return
            elif game.shldup:
                proutn(_("Shields are up. Do you want them down? "))
-               if ja() == True:
+               if ja():
                    action = "SHDN"
                else:
                    scanner.chew()
                    return
            else:
                proutn(_("Shields are down. Do you want them up? "))
-               if ja() == True:
+               if ja():
                    action = "SHUP"
                else:
                    scanner.chew()
-                   return    
+                   return
     if action == "SHUP": # raise shields 
        if game.shldup:
            prout(_("Shields already up."))
@@ -922,14 +948,14 @@ def doshield(shraise):
            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
-       game.shldup=False
-       game.shldchg=True
+       game.shldup = False
+       game.shldchg = True
        prout(_("Shields lowered."))
        game.ididit = True
        return
@@ -996,15 +1022,15 @@ def randdevice():
     )
     assert(sum(weights) == 1000)
     idx = randrange(1000)
-    sum = 0
+    wsum = 0
     for (i, w) in enumerate(weights):
-       sum += w
-       if idx < sum:
+       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."
+    "Collision handling for rammong events."
     prouts(_("***RED ALERT!  RED ALERT!"))
     skip(1)
     prout(_("***COLLISION IMMINENT."))
@@ -1021,16 +1047,17 @@ def collision(rammed, enemy):
        proutn(_(" (original position)"))
     skip(1)
     deadkl(enemy.location, enemy.type, game.sector)
-    proutn("***" + crmship() + " heavily damaged.")
+    proutn("***" + crmshp() + " heavily damaged.")
     icas = randrange(10, 30)
-    prout(_("***Sickbay reports %d casualties"), icas)
+    prout(_("***Sickbay reports %d casualties") % icas)
     game.casual += icas
     game.state.crew -= icas
     # In the pre-SST2K version, all devices got equiprobably damaged,
     # which was silly.  Instead, pick up to half the devices at
     # random according to our weighting table,
     ncrits = randrange(NDEVICES/2)
-    for m in range(ncrits):
+    while ncrits > 0:
+        ncrits -= 1
        dev = randdevice()
        if game.damage[dev] < 0:
            continue
@@ -1048,57 +1075,59 @@ def collision(rammed, enemy):
 
 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)
     ac = bearing + 0.25*dispersion     # dispersion is a random variable
     bullseye = (15.0 - bearing)*0.5235988
     track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin)) 
-    bumpto = coord(0, 0)
+    bumpto = Coord(0, 0)
     # 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
-       iquad=game.quad[w.i][w.j]
-       tracktorpedo(origin, w, step, number, nburst, iquad)
-       if iquad=='.':
+       iquad = game.quad[w.i][w.j]
+       tracktorpedo(w, step, number, nburst, iquad)
+       if iquad == '.':
            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))
-           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
-            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
-           if game.quad[bumpto.i][bumpto.j]==' ':
+           if game.quad[bumpto.i][bumpto.j] == ' ':
                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())
-            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()
-            game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+            sortenemies()
             return None
        elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy 
            # find the enemy 
@@ -1108,49 +1137,52 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                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:
-                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()
-                game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+                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)
-           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.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
+           game.state.chart[game.quadrant.i][game.quadrant.j].starbase = False
            game.state.basekl += 1
            newcnd()
            return None
@@ -1200,8 +1232,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.
-               thing.angry = True
-               shoved = True
+               thing.angry()
            return None
        elif iquad == ' ': # Black hole 
            skip(1)
@@ -1213,7 +1244,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
            return None
        elif iquad == 'T':  # Hit a Tholian 
            h1 = 700.0 + randrange(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            h1 = math.fabs(h1)
            if h1 >= 600:
                game.quad[w.i][w.j] = '.'
@@ -1238,8 +1269,9 @@ def torpedo(origin, bearing, dispersion, number, nburst):
            return None
        break
     skip(1)
+    setwnd(message_window)
     prout(_("Torpedo missed."))
-    return None;
+    return None
 
 def fry(hit):
     "Critical-hit resolution." 
@@ -1249,15 +1281,16 @@ def fry(hit):
     proutn(_("***CRITICAL HIT--"))
     # Select devices and cause damage
     cdam = []
-    for loop1 in range(ncrit):
+    while ncrit > 0:
         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
+        ncrit -= 1
     skipcount = 0
     for (i, j) in enumerate(cdam):
        proutn(device[j])
@@ -1269,7 +1302,7 @@ def fry(hit):
     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 
@@ -1277,10 +1310,13 @@ def attack(torps_ok):
     # 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 idebug:
+    if game.idebug:
        prout("=== ATTACK!")
     # Tholian gets to move before attacking 
     if game.tholian:
@@ -1290,10 +1326,27 @@ def attack(torps_ok):
        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 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
@@ -1305,33 +1358,33 @@ def attack(torps_ok):
        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:
-           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 \
-           (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":
-               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)
-           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
-           course = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
+           pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
            hit = 0
            proutn(_("***TORPEDO INCOMING"))
            if not damaged(DSRSENS):
@@ -1340,19 +1393,19 @@ def attack(torps_ok):
            prout("  ")
            dispersion = (randreal()+randreal())*0.5 - 0.5
            dispersion += 0.002*enemy.power*dispersion
-           hit = torpedo(enemy.location, course, dispersion, number=1, nburst=1)
-           if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
-               finish(FWON); # Klingons did themselves in! 
+           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.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
-            if game.condition =="docked":
-                propr *= 2.1
+            if game.condition == "docked":
+                propor *= 2.1
            if propor < 0.1:
                propor = 0.1
            hitsh = propor*chgfac*hit+1.0
@@ -1414,23 +1467,23 @@ def attack(torps_ok):
     # After attack, reset average distance to enemies 
     for enemy in game.enemies:
        enemy.kavgd = enemy.kdist
-    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+    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 
-    proutn(crmena(True, type, "sector", mv))
+    proutn(crmena(True, etype, "sector", mv))
     # 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
-    elif type == 'T':
+    elif etype == 'T':
         # Killed a Tholian 
         game.tholian = None
-    elif type == '?':
+    elif etype == '?':
         # Killed a Thingy
         global thing
         thing = None
@@ -1456,7 +1509,7 @@ def deadkl(w, type, mv):
             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
@@ -1471,11 +1524,11 @@ def targetcheck(w):
     if not w.valid_sector():
        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);
-    if delta == coord(0, 0):
+    delta = Coord()
+    # 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,"))
        prout(_("  I recommend an immediate review of"))
@@ -1486,7 +1539,7 @@ def targetcheck(w):
 
 def torps():
     "Launch photon torpedo salvo."
-    course = []
+    tcourse = []
     game.ididit = False
     if damaged(DPHOTON):
        prout(_("Photon tubes damaged."))
@@ -1524,21 +1577,21 @@ def torps():
     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])
-               course.append(course[0])
+               tcourse.append(tcourse[0])
                i += 1
            break
         scanner.push(scanner.token)
         target.append(scanner.getcoord())
         if target[-1] == None:
             return
-        course.append(targetcheck(target[-1]))
-        if course[-1] == None:
+        tcourse.append(targetcheck(target[-1]))
+        if tcourse[-1] == None:
            return
     scanner.chew()
     if len(target) == 0:
@@ -1549,8 +1602,8 @@ def torps():
             target.append(scanner.getcoord())
             if target[-1] == None:
                 return
-            course.append(targetcheck(target[-1]))
-            if course[-1] == None:
+            tcourse.append(targetcheck(target[-1]))
+            if tcourse[-1] == None:
                 return
     game.ididit = True
     # Loop for moving <n> torpedoes 
@@ -1574,11 +1627,11 @@ def torps():
            break
        if game.shldup or game.condition == "docked":
            dispersion *= 1.0 + 0.0001*game.shield
-       torpedo(game.sector, course[i], dispersion, number=i, nburst=n)
+       torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
        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);
+    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
+       finish(FWON)
 
 def overheat(rpow):
     "Check for phasers overheating."
@@ -1622,18 +1675,18 @@ def checkshctrl(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."
-    nenhr2 = len(game.enemies); kk=0
-    w = coord()
+    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)
-       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:
@@ -1651,20 +1704,20 @@ def hittem(hits):
        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:
-               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 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
@@ -1674,10 +1727,16 @@ def hittem(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"
-    key=0
+    key = 0
     skip(1)
     # SR sensors and Computer are needed for automode 
     if damaged(DSRSENS) or damaged(DCOMPTR):
@@ -1703,15 +1762,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)
-    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"
-                   automode="AUTOMATIC"
+                   automode = "AUTOMATIC"
                else:
                    automode = "MANUAL"
                    key = scanner.next()
@@ -1755,18 +1814,18 @@ def phasers():
            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)):
-                   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()
-           if key!="IHREAL":
+           if key != "IHREAL":
                return
            rpow = scanner.real
            if rpow > avail:
@@ -1775,15 +1834,15 @@ def phasers():
                key = "IHEOL"
             if not rpow > avail:
                 break
-       if rpow<=0:
+       if rpow <= 0:
            # chicken out 
            scanner.chew()
            return
-        key=scanner.next()
+        key = scanner.next()
        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()
@@ -1796,7 +1855,7 @@ def phasers():
                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
@@ -1845,13 +1904,13 @@ def phasers():
                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:
-                   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):
@@ -1869,7 +1928,7 @@ def phasers():
                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:
@@ -1883,7 +1942,7 @@ def phasers():
                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 
@@ -1914,7 +1973,7 @@ def phasers():
                prout(_("Shields raised."))
        else:
            game.shldup = False
-    overheat(rpow);
+    overheat(rpow)
 
 # Code from events,c begins here.
 
@@ -1950,7 +2009,7 @@ def cancelrest():
     if game.resting:
        skip(1)
        proutn(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
-       if ja() == True:
+       if ja():
            game.resting = False
            game.optime = 0.0
            return True
@@ -1958,11 +2017,15 @@ def cancelrest():
 
 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." 
@@ -2004,8 +2067,8 @@ def events():
             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)
@@ -2043,7 +2106,7 @@ def events():
             game.isatb = 0
         else:
             game.battle.invalidate()
-    if idebug:
+    if game.idebug:
        prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
        for i in range(1, NEVENTS):
            if   i == FSNOVA:  proutn("=== Supernova       ")
@@ -2072,7 +2135,7 @@ def events():
        for l in range(1, NEVENTS):
            if game.future[l].date < datemin:
                evcode = l
-               if idebug:
+               if game.idebug:
                    prout("== Event %d fires" % evcode)
                datemin = game.future[l].date
        xtime = datemin-game.state.date
@@ -2080,11 +2143,11 @@ def events():
        # 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? 
-       if game.state.crew <=0:
+       if game.state.crew <= 0:
            finish(FCREW)
            return
        # Is life support adequate? 
@@ -2128,7 +2191,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 \
-                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 \
@@ -2166,15 +2229,15 @@ def events():
                 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 ibq
+                    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))
                     unschedule(FCDBAS)
                     continue
-            except coord:
+            except JumpOut:
                 pass
            # commander + starbase combination found -- launch attack 
            game.battle = ibq
@@ -2202,7 +2265,7 @@ def events():
            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:
@@ -2241,18 +2304,18 @@ def events():
                    #announce()
                    skip(1)
                    prout(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
-           pdest = game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j]
+            pquad = game.probe.quadrant()
+           pdest = game.state.galaxy[pquad.i][pquad.j]
            if communicating():
-               chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
-               chp.klingons = pdest.klingons
-               chp.starbase = pdest.starbase
-               chp.stars = pdest.stars
+               game.state.chart[pquad.i][pquad.j].klingons = pdest.klingons
+               game.state.chart[pquad.i][pquad.j].starbase = pdest.starbase
+               game.state.chart[pquad.i][pquad.j].stars = pdest.stars
                pdest.charted = True
            game.probe.moves -= 1 # One less to travel
            if game.probe.arrived() and game.isarmed and pdest.stars:
                supernova(game.probe)           # fire in the hole!
                unschedule(FDSPROB)
-               if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova: 
+               if game.state.galaxy[pquad.i][pquad.j].supernova: 
                    return
        elif evcode == FDISTR: # inhabited system issues distress call 
            unschedule(FDISTR)
@@ -2270,7 +2333,7 @@ def events():
                     break
             else:
                # can't seem to find one; ignore this call 
-               if idebug:
+               if game.idebug:
                    prout("=== Couldn't find location for distress event.")
                continue
            # got one!!  Schedule its enslavement 
@@ -2280,7 +2343,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") \
-                        % (q.planet, `w`))
+                        % (q.planet, repr(w)))
                prout(_("by a Klingon invasion fleet."))
                if cancelrest():
                    return
@@ -2311,11 +2374,11 @@ def events():
            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
-            m = coord()
+            m = Coord()
            if game.klhere >= MAXKLQUAD:
                 try:
                     # this quadrant not ok, pick an adjacent one 
@@ -2327,10 +2390,10 @@ def events():
                             # check for this quad ok (not full & no snova) 
                             if q.klingons >= MAXKLQUAD or q.supernova:
                                 continue
-                            raise "FOUNDIT"
+                            raise JumpOut
                     else:
                         continue       # search for eligible quadrant failed
-                except "FOUNDIT":
+                except JumpOut:
                     w = m
            # deliver the child 
            game.state.remkl += 1
@@ -2367,7 +2430,7 @@ def wait():
        return
     if delay >= game.state.remtime or len(game.enemies) != 0:
        proutn(_("Are you sure? "))
-       if ja() == False:
+       if not ja():
            return
     # Alternate resting periods (events) with attacks 
     game.resting = True
@@ -2403,8 +2466,8 @@ def wait():
 
 def nova(nov):
     "Star goes nova." 
-    course = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
-    newc = coord(); neighbor = coord(); bump = coord(0, 0)
+    ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
+    newc = Coord(); neighbor = Coord(); bump = Coord(0, 0)
     if withprob(0.05):
        # Wow! We've supernova'ed 
        supernova(game.quadrant)
@@ -2418,11 +2481,11 @@ def nova(nov):
     hits = [nov]
     kount = 0
     while hits:
-        offset = coord()
+        offset = Coord()
         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():
@@ -2449,7 +2512,7 @@ def nova(nov):
                     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
@@ -2484,7 +2547,7 @@ def nova(nov):
                         finish(FNOVA)
                         return
                     # add in course nova contributes to kicking starship
-                    bump += (game.sector-hits[mm]).sgn()
+                    bump += (game.sector-hits[-1]).sgn()
                 elif iquad == 'K': # kill klingon 
                     deadkl(neighbor, iquad, neighbor)
                 elif iquad in ('C','S','R'): # Damage/destroy big enemies 
@@ -2495,7 +2558,7 @@ def nova(nov):
                     if game.enemies[ll].power <= 0.0:
                         deadkl(neighbor, iquad, neighbor)
                         break
-                    newc = neighbor + neighbor - hits[mm]
+                    newc = neighbor + neighbor - hits[-1]
                     proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
                     if not newc.valid_sector():
                         # can't leave quadrant 
@@ -2517,17 +2580,17 @@ def nova(nov):
                     game.enemies[ll].move(newc)
     # Starship affected by nova -- kick it away. 
     dist = kount*0.1
-    direc = course[3*(bump.i+1)+bump.j+2]
+    direc = ncourse[3*(bump.i+1)+bump.j+2]
     if direc == 0.0:
        dist = 0.0
     if dist == 0.0:
        return
-    course = course(bearing=direc, distance=dist)
-    game.optime = course.time(warp=4)
+    scourse = course(bearing=direc, distance=dist)
+    game.optime = scourse.time(warp=4)
     skip(1)
     prout(_("Force of nova displaces starship."))
-    imove(course, noattack=True)
-    game.optime = course.time(warp=4)
+    imove(scourse, noattack=True)
+    game.optime = scourse.time(warp=4)
     return
        
 def supernova(w):
@@ -2538,7 +2601,7 @@ def supernova(w):
     else:
        # Scheduled supernova -- select star at random. 
        stars = 0
-        nq = coord()
+        nq = Coord()
        for nq.i in range(GALSIZE):
            for nq.j in range(GALSIZE):
                stars += game.state.galaxy[nq.i][nq.j].stars
@@ -2552,9 +2615,9 @@ def supernova(w):
                    break
            if num <=0:
                break
-       if idebug:
+       if game.idebug:
            proutn("=== Super nova here?")
-           if ja() == True:
+           if ja():
                nq = game.quadrant
     if not nq == game.quadrant or game.justin:
        # it isn't here, or we just entered (treat as enroute) 
@@ -2563,7 +2626,7 @@ def supernova(w):
            prout(_("Message from Starfleet Command       Stardate %.2f") % game.state.date)
            prout(_("     Supernova in Quadrant %s; caution advised.") % nq)
     else:
-       ns = coord()
+       ns = Coord()
        # we are in the quadrant! 
        num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
        for ns.i in range(QUADSIZE):
@@ -2655,7 +2718,6 @@ def selfdestruct():
     prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
     skip(1)
     scanner.next()
-    scanner.chew()
     if game.passwd != scanner.token:
        prouts(_("PASSWORD-REJECTED;"))
        skip(1)
@@ -2683,11 +2745,9 @@ def kaboom():
     skip(1)
     if len(game.enemies) != 0:
        whammo = 25.0 * game.energy
-       l=1
-       while l <= len(game.enemies):
+       for l in range(len(game.enemies)):
            if game.enemies[l].power*game.enemies[l].kdist <= whammo: 
                deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
-           l += 1
     finish(FDILITHIUM)
                                
 def killrate():
@@ -2768,12 +2828,12 @@ def finish(ifin):
                    prout(_("Now you can retire and write your own Star Trek game!"))
                    skip(1)
                elif game.skill >= SKILL_EXPERT:
-                   if game.thawed and not idebug:
+                   if game.thawed and not game.idebug:
                        prout(_("You cannot get a citation, so..."))
                    else:
                        proutn(_("Do you want your Commodore Emeritus Citation printed? "))
                        scanner.chew()
-                       if ja() == True:
+                       if ja():
                            igotit = True
            # Only grant long life if alive (original didn't!)
            skip(1)
@@ -2911,11 +2971,10 @@ def finish(ifin):
 def score():
     "Compute player's score."
     timused = game.state.date - game.indate
-    iskill = game.skill
     if (timused == 0 or (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0) and timused < 5.0:
        timused = 5.0
-    perdate = killrate()
-    ithperd = 500*perdate + 0.5
+    game.perdate = killrate()
+    ithperd = 500*game.perdate + 0.5
     iwon = 0
     if game.gamewon:
        iwon = 100*game.skill
@@ -2925,7 +2984,7 @@ def score():
        klship = 1
     else:
        klship = 2
-    iscore = 10*(game.inkling - game.state.remkl) \
+    game.score = 10*(game.inkling - game.state.remkl) \
              + 50*(game.incom - len(game.state.kcmdr)) \
              + ithperd + iwon \
              + 20*(game.inrom - game.state.nromrem) \
@@ -2933,7 +2992,7 @@ def score():
             - game.state.nromrem \
              - badpoints()
     if not game.alive:
-       iscore -= 200
+       game.score -= 200
     skip(2)
     prout(_("Your score --"))
     if game.inrom - game.state.nromrem:
@@ -2953,7 +3012,7 @@ def score():
              (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
     if ithperd:
        prout(_("%6.2f Klingons per stardate              %5d") %
-             (perdate, ithperd))
+             (game.perdate, ithperd))
     if game.state.starkl:
        prout(_("%6d stars destroyed by your action     %5d") %
              (game.state.starkl, -5*game.state.starkl))
@@ -2989,7 +3048,7 @@ def score():
        elif game.skill ==  SKILL_EMERITUS:     proutn(_("Emeritus game"))
        prout("           %5d" % iwon)
     skip(1)
-    prout(_("TOTAL SCORE                               %5d") % iscore)
+    prout(_("TOTAL SCORE                               %5d") % game.score)
 
 def plaque():
     "Emit winner's commemmorative plaque." 
@@ -3045,8 +3104,8 @@ def plaque():
     timestring = time.ctime()
     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
                     (timestring+4, timestring+20, timestring+11))
-    fp.write(_("                                                        Your score:  %d\n\n") % iscore)
-    fp.write(_("                                                    Klingons per stardate:  %.2f\n") % perdate)
+    fp.write(_("                                                        Your score:  %d\n\n") % game.score)
+    fp.write(_("                                                    Klingons per stardate:  %.2f\n") % game.perdate)
     fp.close()
 
 # Code from io.c begins here
@@ -3085,16 +3144,16 @@ def iostart():
        curses.nonl()
        curses.cbreak()
         if game.options & OPTION_COLOR:
-            curses.start_color();
+            curses.start_color()
             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()
@@ -3145,25 +3204,20 @@ def pause_game():
         global linecount
         sys.stdout.write('\n')
         proutn(prompt)
-        raw_input()
-        for j in range(rows):
-            sys.stdout.write('\n')
+        if not replayfp:
+            raw_input()
+        sys.stdout.write('\n' * rows)
         linecount = 0
 
 def skip(i):
     "Skip i lines.  Pause game if this would cause a scrolling event."
     for dummy in range(i):
        if game.options & OPTION_CURSES:
-            (y, x) = curwnd.getyx()
-            (my, mx) = curwnd.getmaxyx()
-           if curwnd == message_window and y >= my - 2:
-               pause_game()
-               clrscr()
-           else:
-                try:
-                    curwnd.move(y+1, 0)
-                except curses.error:
-                    pass
+           (y, x) = curwnd.getyx()
+           try:
+               curwnd.move(y+1, 0)
+           except curses.error:
+               pass
        else:
             global linecount
            linecount += 1
@@ -3175,6 +3229,14 @@ def skip(i):
 def proutn(line):
     "Utter a line with no following line feed."
     if game.options & OPTION_CURSES:
+       (y, x) = curwnd.getyx()
+       (my, mx) = curwnd.getmaxyx()
+       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:
@@ -3224,8 +3286,31 @@ def setwnd(wnd):
     "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
-        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" 
@@ -3237,47 +3322,47 @@ def clrscr():
     "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: 
-           curwnd.attrset(0);
+           curwnd.attrset(0)
        elif color ==  BLACK: 
-           curwnd.attron(curses.color_pair(curses.COLOR_BLACK));
+           curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
        elif color ==  BLUE: 
-           curwnd.attron(curses.color_pair(curses.COLOR_BLUE));
+           curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
        elif color ==  GREEN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_GREEN));
+           curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
        elif color ==  CYAN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_CYAN));
+           curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
        elif color ==  RED: 
-           curwnd.attron(curses.color_pair(curses.COLOR_RED));
+           curwnd.attron(curses.color_pair(curses.COLOR_RED))
        elif color ==  MAGENTA: 
-           curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA));
+           curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
        elif color ==  BROWN: 
-           curwnd.attron(curses.color_pair(curses.COLOR_YELLOW));
+           curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
        elif color ==  LIGHTGRAY: 
-           curwnd.attron(curses.color_pair(curses.COLOR_WHITE));
+           curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
        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: 
-           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: 
-           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: 
-           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: 
-           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: 
-           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: 
-           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:
-           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:
@@ -3341,7 +3426,7 @@ def warble():
        #nosound()
         pass
 
-def tracktorpedo(origin, w, step, i, n, iquad):
+def tracktorpedo(w, step, i, n, iquad):
     "Torpedo-track animation." 
     if not game.options & OPTION_CURSES:
        if step == 1:
@@ -3401,9 +3486,9 @@ def prstat(txt, data):
 
 # Code from moving.c begins here
 
-def imove(course=None, noattack=False):
+def imove(icourse=None, noattack=False):
     "Movement execution for warp, impulse, supernova, and tractor-beam events."
-    w = coord()
+    w = Coord()
 
     def newquadrant(noattack):
         # Leaving quadrant -- allow final enemy attack 
@@ -3424,17 +3509,17 @@ def imove(course=None, noattack=False):
         kinks = 0
         while True:
             kink = False
-            if course.final.i < 0:
-                course.final.i = -course.final.i
+            if icourse.final.i < 0:
+                icourse.final.i = -icourse.final.i
                 kink = True
-            if course.final.j < 0:
-                course.final.j = -course.final.j
+            if icourse.final.j < 0:
+                icourse.final.j = -icourse.final.j
                 kink = True
-            if course.final.i >= GALSIZE*QUADSIZE:
-                course.final.i = (GALSIZE*QUADSIZE*2) - course.final.i
+            if icourse.final.i >= GALSIZE*QUADSIZE:
+                icourse.final.i = (GALSIZE*QUADSIZE*2) - icourse.final.i
                 kink = True
-            if course.final.j >= GALSIZE*QUADSIZE:
-                course.final.j = (GALSIZE*QUADSIZE*2) - course.final.j
+            if icourse.final.j >= GALSIZE*QUADSIZE:
+                icourse.final.j = (GALSIZE*QUADSIZE*2) - icourse.final.j
                 kink = True
             if kink:
                 kinks += 1
@@ -3453,8 +3538,8 @@ def imove(course=None, noattack=False):
         # Compute final position in new quadrant 
         if trbeam: # Don't bother if we are to be beamed 
             return
-        game.quadrant = course.final.quadrant()
-        game.sector = course.final.sector()
+        game.quadrant = icourse.final.quadrant()
+        game.sector = icourse.final.sector()
         skip(1)
         prout(_("Entering Quadrant %s.") % game.quadrant)
         game.quad[game.sector.i][game.sector.j] = game.ship
@@ -3466,7 +3551,7 @@ def imove(course=None, noattack=False):
         iquad = game.quad[h.i][h.j]
         if iquad != '.':
             # object encountered in flight path 
-            stopegy = 50.0*course.distance/game.optime
+            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:
@@ -3516,14 +3601,14 @@ def imove(course=None, noattack=False):
     if game.state.date+game.optime >= scheduled(FTBEAM):
        trbeam = True
        game.condition = "red"
-       course.distance = course.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
+       icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
        game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
     # Move out
     game.quad[game.sector.i][game.sector.j] = '.'
-    for m in range(course.moves):
-        course.next()
-        w = course.sector()
-        if course.origin.quadrant() != course.location.quadrant():
+    for m in range(icourse.moves):
+        icourse.next()
+        w = icourse.sector()
+        if icourse.origin.quadrant() != icourse.location.quadrant():
             newquadrant(noattack)
             break
         elif check_collision(w):
@@ -3538,7 +3623,7 @@ def imove(course=None, noattack=False):
             finald = (w-enemy.location).distance()
             enemy.kavgd = 0.5 * (finald + enemy.kdist)
             enemy.kdist = finald
-        game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+        sortenemies()
         if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
             attack(torps_ok=False)
         for enemy in game.enemies:
@@ -3591,7 +3676,7 @@ def getcourse(isprobe):
     dquad = copy.copy(game.quadrant)
     navmode = "unspecified"
     itemp = "curt"
-    dsect = coord()
+    dsect = Coord()
     iprompt = False
     if game.landed and not isprobe:
        prout(_("Dummy! You can't leave standard orbit until you"))
@@ -3633,7 +3718,7 @@ def getcourse(isprobe):
                prout(_("(Manual movement assumed.)"))
            navmode = "manual"
            break
-    delta = coord()
+    delta = Coord()
     if navmode == "automatic":
        while key == "IHEOL":
            if isprobe:
@@ -3733,7 +3818,7 @@ class course:
             self.origin = cartesian(game.quadrant, game.sector)
         else:
             self.origin = cartesian(game.quadrant, origin)
-        self.increment = coord(-math.sin(self.angle), math.cos(self.angle))
+        self.increment = Coord(-math.sin(self.angle), math.cos(self.angle))
         bigger = max(abs(self.increment.i), abs(self.increment.j))
         self.increment /= bigger
         self.moves = int(round(10*self.distance*bigger))
@@ -3790,26 +3875,26 @@ def impulse():
        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"))
        proutn(_("we dare spend the time?\" "))
-       if ja() == False:
+       if not ja():
            return
     # Activate impulse engines and pay the cost 
     imove(course, noattack=False)
     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.optime = course.dist/0.095
+    game.optime = course.distance/0.095
     if game.energy <= 0:
        finish(FNRG)
     return
 
-def warp(course, involuntary):
+def warp(wcourse, involuntary):
     "ove under warp drive."
     blooey = False; twarp = False
     if not involuntary: # Not WARPX entry 
@@ -3826,21 +3911,21 @@ def warp(course, involuntary):
            prout(_("  is repaired, I can only give you warp 4.\""))
            return
                # Read in course and distance
-        if course==None:
+        if wcourse==None:
             try:
-                course = getcourse(isprobe=False)
+                wcourse = getcourse(isprobe=False)
             except TrekError:
                 return
        # Make sure starship has enough energy for the trip
         # Note: this formula is slightly different from the C version,
         # and lets you skate a bit closer to the edge.
-       if course.power(game.warpfac) >= game.energy:
+       if wcourse.power(game.warpfac) >= game.energy:
            # Insufficient power for trip 
            game.ididit = False
            skip(1)
            prout(_("Engineering to bridge--"))
-           if not game.shldup or 0.5*power > game.energy:
-               iwarp = (game.energy/(course.dist+0.05)) ** 0.333333333
+           if not game.shldup or 0.5*wcourse.power(game.warpfac) > game.energy:
+               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:
@@ -3854,7 +3939,7 @@ def warp(course, involuntary):
                prout(_("We haven't the energy to go that far with the shields up."))
            return                              
        # Make sure enough time is left for the trip 
-       game.optime = course.time(game.warpfac)
+       game.optime = wcourse.time(game.warpfac)
        if game.optime >= 0.8*game.state.remtime:
            skip(1)
            prout(_("First Officer Spock- \"Captain, I compute that such"))
@@ -3862,7 +3947,7 @@ def warp(course, involuntary):
                   (100.0*game.optime/game.state.remtime))
            prout(_(" percent of our"))
            proutn(_("  remaining time.  Are you sure this is wise?\" "))
-           if ja() == False:
+           if not ja():
                game.ididit = False
                game.optime=0 
                return
@@ -3870,38 +3955,40 @@ def warp(course, involuntary):
     if game.warpfac > 6.0:
        # Decide if engine damage will occur
         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
-       prob = course.distance*(6.0-game.warpfac)**2/66.666666666
+       prob = wcourse.distance*(6.0-game.warpfac)**2/66.666666666
        if prob > randreal():
            blooey = True
-           course.distance = randreal(course.distance)
+           wcourse.distance = randreal(wcourse.distance)
        # Decide if time warp will occur 
-       if 0.5*course.distance*math.pow(7.0,game.warpfac-10.0) > randreal():
+       if 0.5*wcourse.distance*math.pow(7.0,game.warpfac-10.0) > randreal():
            twarp = True
-       if idebug and game.warpfac==10 and not twarp:
+       if game.idebug and game.warpfac==10 and not twarp:
            blooey = False
            proutn("=== Force time warp? ")
-           if ja() == True:
+           if ja():
                twarp = True
        if blooey or twarp:
            # If time warp or engine damage, check path 
-           # If it is obstructed, don't do warp or damage 
-            for m in range(course.moves):
-                course.next()
-                w = course.sector()
+           # If it is obstructed, don't do warp or damage
+            look = wcourse.moves
+            while look > 0:
+                look -= 1
+                wcourse.next()
+                w = wcourse.sector()
                 if not w.valid_sector():
                     break
                if game.quad[w.i][w.j] != '.':
                    blooey = False
                    twarp = False
-            course.reset()
+            wcourse.reset()
     # Activate Warp Engines and pay the cost 
-    imove(course, noattack=False)
+    imove(wcourse, noattack=False)
     if game.alldone:
        return
-    game.energy -= course.power(game.warpfac)
+    game.energy -= wcourse.power(game.warpfac)
     if game.energy <= 0:
        finish(FNRG)
-    game.optime = course.time(game.warpfac)
+    game.optime = wcourse.time(game.warpfac)
     if twarp:
        timwrp()
     if blooey:
@@ -4109,7 +4196,7 @@ def probe():
         else:
             prout(_("%d probes left") % game.nprobes)
        proutn(_("Are you sure you want to fire a probe? "))
-       if ja() == False:
+       if not ja():
            return
     game.isarmed = False
     if key == "IHALPHA" and scanner.token == "armed":
@@ -4183,7 +4270,7 @@ def mayday():
        elif m == 2: proutn(_("2nd"))
        elif m == 3: proutn(_("3rd"))
        proutn(_(" attempt to re-materialize ") + crmshp())
-       game.quad[ix][iy]=('-','o','O')[m-1]
+       game.quad[game.sector.i][game.sector.j]=('-','o','O')[m-1]
         textcolor(RED)
        warble()
        if randreal() > probf:
@@ -4192,16 +4279,16 @@ def mayday():
         textcolor(DEFAULT)
        curses.delay_output(500)
     if m > 3:
-       game.quad[ix][iy]='?'
+       game.quad[game.sector.i][game.sector.j]='?'
        game.alive = False
        drawmaps(1)
        setwnd(message_window)
        finish(FMATERIALIZE)
        return
-    game.quad[ix][iy]=game.ship
-    textcolor(GREEN);
+    game.quad[game.sector.i][game.sector.j]=game.ship
+    textcolor(GREEN)
     prout(_("succeeds."))
-    textcolor(DEFAULT);
+    textcolor(DEFAULT)
     dock(False)
     skip(1)
     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
@@ -4324,15 +4411,15 @@ def survey():
            continue
        if (game.state.planets[i].known != "unknown" \
             and not game.state.planets[i].inhabited) \
-            or idebug:
+            or game.idebug:
            iknow = True
-           if idebug and game.state.planets[i].known=="unknown":
+           if game.idebug and game.state.planets[i].known=="unknown":
                proutn("(Unknown) ")
            proutn(_("Quadrant %s") % game.state.planets[i].quadrant)
            proutn(_("   class "))
            proutn(game.state.planets[i].pclass)
            proutn("   ")
-           if game.state.planets[i].crystals != present:
+           if game.state.planets[i].crystals != "present":
                proutn(_("no "))
            prout(_("dilithium crystals present."))
            if game.state.planets[i].known=="shuttle_down": 
@@ -4404,7 +4491,7 @@ def beam():
        if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
            skip(1)
            proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
-           if ja() == True:
+            if ja():
                shuttle()
        return
     if not game.inorbit:
@@ -4422,7 +4509,7 @@ def beam():
        prout(_("Spock-  \"Captain, I fail to see the logic in"))
        prout(_("  exploring a planet with no dilithium crystals."))
        proutn(_("  Are you sure this is wise?\" "))
-       if ja() == False:
+       if not ja():
            scanner.chew()
            return
     if not (game.options & OPTION_PLAIN):
@@ -4438,14 +4525,14 @@ def beam():
            if game.iplnet.known == "shuttle_down":
                prout(_("  Although the Galileo shuttle craft may still be on a surface."))
            proutn(_("  Are you sure this is wise?\" "))
-           if ja() == False:
+           if not ja():
                scanner.chew()
                return
     if game.landed:
        # Coming from planet 
        if game.iplnet.known=="shuttle_down":
            proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
-           if ja() == True:
+           if ja():
                scanner.chew()
                return
            prout(_("Your crew hides the Galileo to prevent capture by aliens."))
@@ -4528,7 +4615,7 @@ def usecrystals():
     prout(_("  raw dilithium crystals into the ship's power"))
     prout(_("  system may risk a severe explosion."))
     proutn(_("  Are you sure this is wise?\" "))
-    if ja() == False:
+    if not ja():
        scanner.chew()
        return
     skip(1)
@@ -4597,7 +4684,7 @@ def shuttle():
               int(100*game.optime/game.state.remtime))
        prout(_("remaining time."))
        proutn(_("Are you sure this is wise?\" "))
-       if ja() == False:
+       if not ja():
            game.optime = 0.0
            return
     if game.landed:
@@ -4606,7 +4693,7 @@ def shuttle():
            # Galileo on ship! 
            if not damaged(DTRANSP):
                proutn(_("Spock-  \"Would you rather use the transporter?\" "))
-               if ja() == True:
+               if ja():
                    beam()
                    return
                proutn(_("Shuttle crew"))
@@ -4675,7 +4762,7 @@ def deathray():
     prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
     prout(_("  is highly unpredictible.  Considering the alternatives,"))
     proutn(_("  are you sure this is wise?\" "))
-    if ja() == False:
+    if not ja():
        return
     prout(_("Spock-  \"Acknowledged.\""))
     skip(1)
@@ -4739,7 +4826,6 @@ def deathray():
        finish(FDRAY)
        return
     if r <= 0.75:
-       intj
        prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
        skip(2)
        proutn(_("Spock-  \"I believe the word is"))
@@ -4784,7 +4870,7 @@ def attackreport(curt):
 def report():
     # report on general game status 
     scanner.chew()
-    s1 = "" and game.thawed and _("thawed ")
+    s1 = (game.thawed and _("thawed ")) or ""
     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
     s3 = (None, _("novice"), _("fair"),
           _("good"), _("expert"), _("emeritus"))[game.skill]
@@ -4867,7 +4953,7 @@ def lrscan(silent):
         if not silent:
             proutn(" ")
         for y in range(game.quadrant.j-1, game.quadrant.j+2):
-           if not coord(x, y).valid_quadrant():
+           if not Coord(x, y).valid_quadrant():
                 if not silent:
                     proutn("  -1")
            else:
@@ -4956,7 +5042,7 @@ def sectscan(goodScan, i, j):
                    "docked":CYAN,
                    "dead":BROWN}[game.condition]) 
         if game.quad[i][j] != game.ship: 
-            highvideo();
+            highvideo()
        proutn("%c " % game.quad[i][j])
         textcolor(DEFAULT)
     else:
@@ -5056,7 +5142,7 @@ def srscan():
                
 def eta():
     "Use computer to get estimated time of arrival for a warp jump."
-    w1 = coord(); w2 = coord()
+    w1 = Coord(); w2 = Coord()
     prompt = False
     if damaged(DCOMPTR):
        prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
@@ -5198,7 +5284,6 @@ def freeze(boss):
     if key != "IHALPHA":
         huh()
         return
-    scanner.chew()
     if '.' not in scanner.token:
         scanner.token += ".trk"
     try:
@@ -5208,10 +5293,12 @@ def freeze(boss):
        return
     cPickle.dump(game, fp)
     fp.close()
+    scanner.chew()
 
 def thaw():
-    "Retrieve saved game." 
-    game.passwd[0] = '\0'
+    "Retrieve saved game."
+    global game
+    game.passwd = None
     key = scanner.next()
     if key == "IHEOL":
        proutn(_("File name: "))
@@ -5219,7 +5306,6 @@ def thaw():
     if key != "IHALPHA":
        huh()
        return True
-    scanner.chew()
     if '.' not in scanner.token:
         scanner.token += ".trk"
     try:
@@ -5229,6 +5315,7 @@ def thaw():
        return
     game = cPickle.load(fp)
     fp.close()
+    scanner.chew()
     return False
 
 # I used <http://www.memory-alpha.org> to find planets
@@ -5304,7 +5391,7 @@ device = (
 
 def setup():
     "Prepare to play, set up cosmos."
-    w = coord()
+    w = Coord()
     #  Decide how many of everything
     if choose():
        return # frozen game
@@ -5324,25 +5411,40 @@ def setup():
     for i in range(NDEVICES): 
        game.damage[i] = 0.0
     # Set up assorted game parameters
-    game.battle = coord()
+    game.battle = Coord()
     game.state.date = game.indate = 100.0 * randreal(20, 51)
     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
     game.isatb = game.state.nplankl = 0
-    game.state.starkl = game.state.basekl = 0
+    game.state.starkl = game.state.basekl = game.state.nworldkl = 0
     game.iscraft = "onship"
     game.landed = False
     game.alive = True
+
+    # the galaxy
+    game.state.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
+    # the starchart
+    game.state.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
+
+    game.state.planets = []      # Planet information
+    game.state.baseq = []      # Base quadrant coordinates
+    game.state.kcmdr = []      # Commander quadrant coordinates
+    game.statekscmdr = Coord() # Supercommander quadrant coordinates
+
     # Starchart is functional but we've never seen it
     game.lastchart = FOREVER
     # Put stars in the galaxy
     game.instar = 0
     for i in range(GALSIZE):
        for j in range(GALSIZE):
-           k = randrange(1, QUADSIZE**2/10+1)
+            # Can't have more stars per quadrant than fit in one decimal digit,
+            # if we do the chart representation will break. 
+           k = randrange(1, min(10, QUADSIZE**2/10))
            game.instar += k
            game.state.galaxy[i][j].stars = k
     # Locate star bases in galaxy
+    if game.idebug:
+        prout("=== Allocating %d bases" % game.inbase)
     for i in range(game.inbase):
         while True:
             while True:
@@ -5357,14 +5459,16 @@ def setup():
                distq = (w - game.state.baseq[j]).distance()
                if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
                    contflag = True
-                   if idebug:
+                   if game.idebug:
                        prout("=== Abandoning base #%d at %s" % (i, w))
                    break
                elif distq < 6.0 * (BASEMAX+1-game.inbase):
-                   if idebug:
+                   if game.idebug:
                        prout("=== Saving base #%d, close to #%d" % (i, j))
             if not contflag:
                 break
+        if game.idebug:
+            prout("=== Placing base #%d in quadrant %s" % (i, w))
        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
@@ -5402,7 +5506,7 @@ def setup():
             w = randplace(GALSIZE) 
             if game.state.galaxy[w.i][w.j].planet == None:
                 break
-        new = planet()
+        new = Planet()
        new.quadrant = w
         new.crystals = "absent"
        if (game.options & OPTION_WORLDS) and i < NINHAB:
@@ -5490,6 +5594,8 @@ def setup():
     if game.state.nscrem:
        prout(_("  YOU'LL NEED IT."))
     waitfor()
+    clrscr()
+    setwnd(message_window)
     newqad()
     if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
        game.shldup = True
@@ -5502,8 +5608,9 @@ def choose():
        game.tourn = game.length = 0
        game.thawed = False
        game.skill = SKILL_NONE
-       if not scanner.inqueue: # Can start with command line options 
-           proutn(_("Would you like a regular, tournament, or saved game? "))
+       scanner.chew()
+#      if not scanner.inqueue: # Can start with command line options 
+       proutn(_("Would you like a regular, tournament, or saved game? "))
         scanner.next()
         if scanner.sees("tournament"):
            while scanner.next() == "IHEOL":
@@ -5529,7 +5636,7 @@ def choose():
            return True
         if scanner.sees("regular"):
            break
-       proutn(_("What is \"%s\"?") % scanner.token)
+       proutn(_("What is \"%s\"? ") % scanner.token)
        scanner.chew()
     while game.length==0 or game.skill==SKILL_NONE:
        if scanner.next() == "IHALPHA":
@@ -5579,7 +5686,7 @@ def choose():
     game.options &=~ OPTION_COLOR
     setpassword()
     if game.passwd == "debug":
-       idebug = True
+       game.idebug = True
        prout("=== Debug mode enabled.")
     # Use parameters to generate initial values of things
     game.damfac = 0.5 * game.skill
@@ -5623,7 +5730,11 @@ def newcnd():
 
 def newkling():
     "Drop new Klingon into current quadrant."
-    return enemy('K', loc=dropin(), power=randreal(300,450)+25.0*game.skill)
+    return Enemy('K', loc=dropin(), power=randreal(300,450)+25.0*game.skill)
+
+def sortenemies():
+    "Sort enemies by distance so 'nearest' is meaningful."
+    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
 
 def newqad():
     "Set up a new state of quadrant, for when we enter or re-enter it."
@@ -5665,7 +5776,7 @@ def newqad():
            game.iscate = (game.state.remkl > 1)
     # Put in Romulans if needed
     for i in range(q.romulans):
-        enemy('R', loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
+        Enemy('R', loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
     # If quadrant needs a starbase, put it in
     if q.starbase:
        game.base = dropin('B')
@@ -5690,7 +5801,7 @@ def newqad():
            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)
@@ -5701,13 +5812,13 @@ def newqad():
        if (game.skill < SKILL_GOOD and withprob(0.02)) or \
            (game.skill == SKILL_GOOD and withprob(0.05)) or \
             (game.skill > SKILL_GOOD and withprob(0.08)):
-            w = coord()
+            w = Coord()
             while True:
                w.i = withprob(0.5) * (QUADSIZE-1)
                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]=='.':
@@ -5718,7 +5829,7 @@ def newqad():
                game.quad[QUADSIZE-1][0] = 'X'
            if game.quad[QUADSIZE-1][QUADSIZE-1]=='.':
                game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
-    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+    sortenemies()
     # And finally the stars
     for i in range(q.stars):
        dropin('*')
@@ -5749,59 +5860,61 @@ def setpassword():
                break
     else:
         game.passwd = ""
-        for i in range(3):
-           game.passwd += chr(ord('a')+randrange(26))
+        game.passwd += chr(ord('a')+randrange(26))
+        game.passwd += chr(ord('a')+randrange(26))
+        game.passwd += chr(ord('a')+randrange(26))
 
 # Code from sst.c begins here
 
-commands = {
-    "SRSCAN":          OPTION_TTY,
-    "STATUS":          OPTION_TTY,
-    "REQUEST":         OPTION_TTY,
-    "LRSCAN":          OPTION_TTY,
-    "PHASERS":         0,
-    "TORPEDO":         0,
-    "PHOTONS":         0,
-    "MOVE":            0,
-    "SHIELDS":         0,
-    "DOCK":            0,
-    "DAMAGES":         0,
-    "CHART":           0,
-    "IMPULSE":         0,
-    "REST":            0,
-    "WARP":            0,
-    "SCORE":           0,
-    "SENSORS":         OPTION_PLANETS,
-    "ORBIT":           OPTION_PLANETS,
-    "TRANSPORT":       OPTION_PLANETS,
-    "MINE":            OPTION_PLANETS,
-    "CRYSTALS":        OPTION_PLANETS,
-    "SHUTTLE":         OPTION_PLANETS,
-    "PLANETS":         OPTION_PLANETS,
-    "REPORT":          0,
-    "COMPUTER":        0,
-    "COMMANDS":        0,
-    "EMEXIT":          0,
-    "PROBE":           OPTION_PROBE,
-    "SAVE":            0,
-    "FREEZE":          0,      # Synonym for SAVE
-    "ABANDON":         0,
-    "DESTRUCT":        0,
-    "DEATHRAY":        0,
-    "DEBUG":           0,
-    "MAYDAY":          0,
-    "SOS":             0,      # Synonym for MAYDAY
-    "CALL":            0,      # Synonym for MAYDAY
-    "QUIT":            0,
-    "HELP":            0,
-}
+commands = [
+    ("SRSCAN",         OPTION_TTY),
+    ("STATUS",         OPTION_TTY),
+    ("REQUEST",        OPTION_TTY),
+    ("LRSCAN",         OPTION_TTY),
+    ("PHASERS",        0),
+    ("TORPEDO",        0),
+    ("PHOTONS",        0),
+    ("MOVE",           0),
+    ("SHIELDS",        0),
+    ("DOCK",           0),
+    ("DAMAGES",        0),
+    ("CHART",          0),
+    ("IMPULSE",        0),
+    ("REST",           0),
+    ("WARP",           0),
+    ("SCORE",          0),
+    ("SENSORS",        OPTION_PLANETS),
+    ("ORBIT",          OPTION_PLANETS),
+    ("TRANSPORT",      OPTION_PLANETS),
+    ("MINE",           OPTION_PLANETS),
+    ("CRYSTALS",       OPTION_PLANETS),
+    ("SHUTTLE",        OPTION_PLANETS),
+    ("PLANETS",        OPTION_PLANETS),
+    ("REPORT",         0),
+    ("COMPUTER",       0),
+    ("COMMANDS",       0),
+    ("EMEXIT",         0),
+    ("PROBE",          OPTION_PROBE),
+    ("SAVE",           0),
+    ("FREEZE",         0),     # Synonym for SAVE
+    ("ABANDON",        0),
+    ("DESTRUCT",       0),
+    ("DEATHRAY",       0),
+    ("DEBUG",          0),
+    ("MAYDAY",         0),
+    ("SOS",            0),     # Synonym for MAYDAY
+    ("CALL",           0),     # Synonym for MAYDAY
+    ("QUIT",           0),
+    ("HELP",           0),
+    ("",               0),
+]
 
 def listCommands():
     "Generate a list of legal commands."
     prout(_("LEGAL COMMANDS ARE:"))
     emitted = 0
-    for key in commands:
-       if not commands[key] or (commands[key] & game.options):
+    for (key, opt) in commands:
+       if not opt or (opt & game.options):
             proutn("%-12s " % key)
             emitted += 1
             if emitted % 5 == 4:
@@ -5819,7 +5932,8 @@ def helpme():
        setwnd(message_window)
        if key == "IHEOL":
            return
-        if scanner.token.upper() in commands or scanner.token == "ABBREV":
+       cmds = map(lambda x: x[0], commands)
+       if scanner.token.upper() in cmds or scanner.token.upper() == "ABBREV":
            break
        skip(1)
        listCommands()
@@ -5863,8 +5977,6 @@ def helpme():
 
 def makemoves():
     "Command-interpretation loop."
-    clrscr()
-    setwnd(message_window)
     while True:        # command loop 
        drawmaps(1)
         while True:    # get a command 
@@ -5884,16 +5996,19 @@ def makemoves():
            clrscr()
            setwnd(message_window)
            clrscr()
-            candidates = filter(lambda x: x.startswith(scanner.token.upper()),
-                                commands)
-            if len(candidates) == 1:
-                cmd = candidates[0]
-                break
-            elif candidates and not (game.options & OPTION_PLAIN):
-                prout("Commands with prefix '%s': %s" % (scanner.token, " ".join(candidates)))
-            else:
+           abandon_passed = False
+           for (cmd, opt) in commands:
+               # commands after ABANDON cannot be abbreviated
+               if cmd == "ABANDON":
+                   abandon_passed = True
+               if cmd == scanner.token.upper() or (not abandon_passed \
+                       and cmd.startswith(scanner.token.upper())):
+                   break
+           if cmd == "":
                 listCommands()
                 continue
+            else:
+               break
        if cmd == "SRSCAN":             # srscan
            srscan()
        elif cmd == "STATUS":           # status
@@ -5911,7 +6026,7 @@ def makemoves():
            if game.ididit:
                hitme = True
        elif cmd == "MOVE":             # move under warp
-           warp(course=None, involuntary=False)
+           warp(wcourse=None, involuntary=False)
        elif cmd == "SHIELDS":          # shields
            doshield(shraise=False)
            if game.ididit:
@@ -5966,7 +6081,7 @@ def makemoves():
        elif cmd == "EMEXIT":           # Emergency exit
            clrscr()                    # Hide screen
            freeze(True)                # forced save
-           raise SysExit,1                     # And quick exit
+           raise SystemExit,1          # And quick exit
        elif cmd == "PROBE":
            probe()                     # Launch probe
            if game.ididit:
@@ -6015,7 +6130,7 @@ def makemoves():
            break
        if game.alldone:
            break
-    if idebug:
+    if game.idebug:
        prout("=== Ending")
 
 def cramen(type):
@@ -6061,7 +6176,7 @@ def expran(avrage):
 
 def randplace(size):
     "Choose a random location."
-    w = coord()
+    w = Coord()
     w.i = randrange(size) 
     w.j = randrange(size)
     return w
@@ -6122,7 +6237,7 @@ class sstscanner:
         # Round token value to nearest integer
         return int(round(scanner.real))
     def getcoord(self):
-        s = coord()
+        s = Coord()
         scanner.next()
        if scanner.type != "IHREAL":
            huh()
@@ -6134,7 +6249,7 @@ class sstscanner:
            return None
        s.j = scanner.int()-1
         return s
-    def __repr__(str):
+    def __repr__(self):
         return "<sstcanner: token=%s, type=%s, queue=%s>" % (scanner.token, scanner.type, scanner.inqueue)
 
 def ja():
@@ -6158,26 +6273,26 @@ def huh():
 def debugme():
     "Access to the internals for debugging."
     proutn("Reset levels? ")
-    if ja() == True:
+    if ja():
        if game.energy < game.inenrg:
            game.energy = game.inenrg
        game.shield = game.inshld
        game.torps = game.intorps
        game.lsupres = game.inlsr
     proutn("Reset damage? ")
-    if ja() == True:
+    if ja():
        for i in range(NDEVICES): 
            if game.damage[i] > 0.0: 
                game.damage[i] = 0.0
     proutn("Toggle debug flag? ")
-    if ja() == True:
-       idebug = not idebug
-       if idebug:
+    if ja():
+       game.idebug = not game.idebug
+       if game.idebug:
            prout("Debug output ON")        
        else:
            prout("Debug output OFF")
     proutn("Cause selective damage? ")
-    if ja() == True:
+    if ja():
        for i in range(NDEVICES):
            proutn("Kill %s?" % device[i])
            scanner.chew()
@@ -6185,9 +6300,9 @@ def debugme():
             if key == "IHALPHA" and scanner.sees("y"):
                game.damage[i] = 10.0
     proutn("Examine/change events? ")
-    if ja() == True:
-       ev = event()
-       w = coord()
+    if ja():
+       ev = Event()
+       w = Coord()
         legends = {
             FSNOVA:  "Supernova       ",
             FTBEAM:  "T Beam          ",
@@ -6238,19 +6353,17 @@ def debugme():
                        ev.quadrant = w
        scanner.chew()
     proutn("Induce supernova here? ")
-    if ja() == True:
+    if ja():
        game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
        atover(True)
 
 if __name__ == '__main__':
     import getopt, socket
     try:
-        global line, thing, game, idebug
+        global line, thing, game
         game = None
-        thing = coord()
-        thing.angry = False
-        game = gamestate()
-        idebug = 0
+        thing = Thingy()
+        game = Gamestate()
         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
         if os.getenv("TERM"):
             game.options |= OPTION_CURSES
@@ -6258,6 +6371,7 @@ if __name__ == '__main__':
             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:
@@ -6267,11 +6381,12 @@ if __name__ == '__main__':
                     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:]
+                    replay = True
                 except ValueError:
                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
                     raise SystemExit(1)
@@ -6283,7 +6398,7 @@ if __name__ == '__main__':
                 game.options |= OPTION_TTY
                 game.options &=~ OPTION_CURSES
             elif switch == '-x':
-                idebug = True
+                game.idebug = True
             elif switch == '-V':
                 print "SST2K", version
                 raise SystemExit, 0 
@@ -6303,6 +6418,7 @@ if __name__ == '__main__':
         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)
@@ -6320,12 +6436,14 @@ if __name__ == '__main__':
                     game.alldone = False
                 else:
                     makemoves()
+                if replay:
+                    break
                 skip(1)
                 stars()
                 skip(1)
                 if game.tourn and game.alldone:
                     proutn(_("Do you want your score recorded?"))
-                    if ja() == True:
+                    if ja():
                         scanner.chew()
                         scanner.push("\n")
                         freeze(False)
@@ -6342,3 +6460,5 @@ if __name__ == '__main__':
         if logfp:
             logfp.close()
         print ""
+
+# End.