Refactor code to refer to enemy objects.
[super-star-trek.git] / src / sst.py
index 14a1f27429c2885fd9174a7a6aa8ba10cd30f72a..76fa31a6a911303b02f8141927cdd6907b4730fd 100644 (file)
@@ -3,9 +3,9 @@
 sst.py =-- Super Star Trek in Python
 
 This code is a Python translation of a C translation of a FORTRAN original.
-The FORTRANness still shows in many ways, notably the use of 1-origin index
-an a lot of parallel arrays where a more modern language would use structures
-or objects.
+The FORTRANness still shows in many ways, notably the use of a lot of
+parallel arrays where a more modern language would use structures
+or objects.  (However, 1-origin array indexing was fixed.)
 
 Dave Matuszek says:
 
@@ -172,8 +172,15 @@ your score.  Docking at a starbase replenishes your crew.
 Also, the nav subsystem (enabling automatic course
 setting) can be damaged separately from the main computer (which
 handles weapons targeting, ETA calculation, and self-destruct).
+
+After these features were added, I translated this into Python and added
+more:
+
+9. A long-range scan is done silently whenever you call CHART; thus
+the LRSCAN command is no longer needed.  (Controlled by OPTION_AUTOSCAN
+and turned off if game type is "plain" or "almy".)
 """
-import os, sys, math, curses, time, atexit, readline, cPickle, random, getopt
+import os,sys,math,curses,time,atexit,readline,cPickle,random,getopt,copy
 
 SSTDOC         = "/usr/share/doc/sst/sst.doc"
 DOC_NAME       = "sst.doc"
@@ -195,8 +202,8 @@ FULLCREW    = 428   # BSD Trek was 387, that's wrong
 FOREVER        = 1e30
 
 # These functions hide the difference between 0-origin and 1-origin addressing.
-def VALID_QUADRANT(x, y):      return ((x)>=1 and (x)<=GALSIZE and (y)>=1 and (y)<=GALSIZE)
-def VALID_SECTOR(x, y):        return ((x)>=1 and (x)<=QUADSIZE and (y)>=1 and (y)<=QUADSIZE)
+def VALID_QUADRANT(x, y):      return ((x)>=0 and (x)<GALSIZE and (y)>=0 and (y)<GALSIZE)
+def VALID_SECTOR(x, y):        return ((x)>=0 and (x)<QUADSIZE and (y)>=0 and (y)<QUADSIZE)
 
 def square(i):         return ((i)*(i))
 def distance(c1, c2):  return math.sqrt(square(c1.x - c2.x) + square(c1.y - c2.y))
@@ -244,11 +251,28 @@ class coord:
     def distance(self, other):
         return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
     def sgn(self):
-        return coord(self.x / abs(x), self.y / abs(y));
+        s = coord()
+        if self.x == 0:
+            s.x = 0
+        else:
+            s.x = self.x / abs(self.x)
+        if self.y == 0:
+            s.y = 0
+        else:
+            s.y = self.y / abs(self.y)
+        return s
+    def scatter(self):
+        s = coord()
+        s.x = self.x + randrange(-1, 2)
+        s.y = self.y + randrange(-1, 2)
+        return s
     def __hash__(self):
         return hash((x, y))
     def __str__(self):
-        return "%s - %s" % (self.x, self.y)
+        if self.x == None or self.y == None:
+            return "Nowhere"
+        return "%s - %s" % (self.x+1, self.y+1)
+    __repr__ = __str__
 
 class planet:
     def __init__(self):
@@ -261,7 +285,6 @@ class planet:
     def __str__(self):
         return self.name
 
-NOPLANET = None
 class quadrant:
     def __init__(self):
         self.stars = None
@@ -282,10 +305,10 @@ class page:
 def fill2d(size, fillfun):
     "Fill an empty list in 2D."
     lst = []
-    for i in range(size+1):
+    for i in range(size):
         lst.append([]) 
-        for j in range(size+1):
-            lst[i][j] = fillfun(i, j)
+        for j in range(size):
+            lst[i].append(fillfun(i, j))
     return lst
 
 class snapshot:
@@ -306,10 +329,10 @@ class snapshot:
        self.remres = 0         # remaining resources
        self.remtime = 0        # remaining time
         self.baseq = []        # Base quadrant coordinates
-        for i in range(BASEMAX+1):
+        for i in range(BASEMAX):
             self.baseq.append(coord())
         self.kcmdr = []        # Commander quadrant coordinates
-        for i in range(QUADSIZE+1):
+        for i in range(QUADSIZE):
             self.kcmdr.append(coord())
        self.kscmdr = coord()   # Supercommander quadrant coordinates
         # the galaxy (subscript 0 not used)
@@ -328,15 +351,16 @@ OPTION_TTY        = 0x00000001    # old interface
 OPTION_CURSES  = 0x00000002    # new interface 
 OPTION_IOMODES = 0x00000003    # cover both interfaces 
 OPTION_PLANETS = 0x00000004    # planets and mining 
-OPTION_THOLIAN = 0x00000008    # Tholians and their webs 
-OPTION_THINGY  = 0x00000010    # Space Thingy can shoot back 
-OPTION_PROBE   = 0x00000020    # deep-space probes 
+OPTION_THOLIAN = 0x00000008    # Tholians and their webs (UT 1979 version)
+OPTION_THINGY  = 0x00000010    # Space Thingy can shoot back (Stas, 2005)
+OPTION_PROBE   = 0x00000020    # deep-space probes (DECUS version, 1980)
 OPTION_SHOWME  = 0x00000040    # bracket Enterprise in chart 
-OPTION_RAMMING = 0x00000080    # enemies may ram Enterprise 
-OPTION_MVBADDY = 0x00000100    # more enemies can move 
-OPTION_BLKHOLE = 0x00000200    # black hole may timewarp you 
-OPTION_BASE    = 0x00000400    # bases have good shields 
-OPTION_WORLDS  = 0x00000800    # logic for inhabited worlds 
+OPTION_RAMMING = 0x00000080    # enemies may ram Enterprise (Almy)
+OPTION_MVBADDY = 0x00000100    # more enemies can move (Almy)
+OPTION_BLKHOLE = 0x00000200    # black hole may timewarp you (Stas, 2005) 
+OPTION_BASE    = 0x00000400    # bases have good shields (Stas, 2005)
+OPTION_WORLDS  = 0x00000800    # logic for inhabited worlds (ESR, 2006)
+OPTION_AUTOSCAN        = 0x00001000    # automatic LRSCAN before CHART (ESR, 2006)
 OPTION_PLAIN   = 0x01000000    # user chose plain game 
 OPTION_ALMY    = 0x02000000    # user chose Almy variant 
 
@@ -391,24 +415,43 @@ NEVENTS   = 12
 # 
 def findevent(evtype): return game.future[evtype]
 
+class enemy:
+    def __init__(self, type=None, loc=None, power=None):
+        self.type = type
+        self.kloc = coord()
+        if loc:
+            self.move(loc)
+        self.kpower = power    # enemy energy level
+        game.enemies.append(self)
+    def move(self, loc):
+        if self.kloc.x is not None and self.kloc.y is not None:
+            game.quad[self.kloc.x][self.kloc.y] = IHDOT
+        if loc:
+            self.kloc = loc
+            game.quad[self.kloc.x][self.kloc.y] = self.type
+            self.kdist = self.kavgd = distance(game.sector, loc)
+        else:
+            self.kloc = coord()        # enemy sector location
+            self.kdist = self.kavgd = None
+            game.enemies.remove(self)
+    def __repr__(self):
+        return "<%s=%f>" % (self.kloc, self.kpower)    # For debugging
+
 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.quad = fill2d(QUADSIZE, lambda i, j: IHDOT)       # contents of our quadrant
-        self.kpower = fill2d(QUADSIZE, lambda i, j: 0.0)       # enemy energy levels
-        self.kdist = fill2d(QUADSIZE, lambda i, j: 0.0)                # enemy distances
-        self.kavgd = fill2d(QUADSIZE, lambda i, j: 0.0)        # average distances
         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.ks = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)]  # enemy sector locations
+        self.enemies = []
         self.quadrant = None   # where we are in the large
         self.sector = None     # where we are in the small
-        self.tholian = None    # coordinates of Tholian
+        self.tholian = None    # Tholian enemy object
         self.base = None       # position of base in current quadrant
         self.battle = None     # base coordinates being attacked
         self.plnet = None      # location of planet in quadrant
@@ -423,7 +466,6 @@ class gamestate:
         self.ishere = False    # super-commander in quadrant
         self.iscate = False    # super commander is here
         self.ientesc = False   # attempted escape from supercommander
-        self.ithere = False    # Tholian is here 
         self.resting = False   # rest time
         self.icraft = False    # Kirk in Galileo
         self.landed = False    # party on planet (true), on ship (false)
@@ -453,12 +495,11 @@ class gamestate:
         self.casual = 0                # causalties
         self.nhelp = 0         # calls for help
         self.nkinks = 0                # count of energy-barrier crossings
-        self.iplnet = 0                # planet # in quadrant
+        self.iplnet = None     # planet # in quadrant
         self.inplan = 0                # initial planets
-        self.nenhere = 0       # number of enemies in quadrant
         self.irhere = 0                # Romulans in quadrant
         self.isatb = 0         # =1 if super commander is attacking base
-        self.tourn = 0         # tournament number
+        self.tourn = None      # tournament number
         self.proben = 0                # number of moves for probe
         self.nprobes = 0       # number of probes available
         self.inresor = 0.0     # initial resources
@@ -556,9 +597,32 @@ LIGHTMAGENTA = 14
 YELLOW = 15
 WHITE = 16
 
+# Log the results of pulling random numbers so we can check determinism.
+
+import traceback
+
+def withprob(p):
+    v = random.random()
+    #logfp.write("# withprob(%s) -> %f (%s) at %s\n" % (p, v, v<p, traceback.extract_stack()[-2][1:]))
+    return v < p
+
+def randrange(*args):
+    v = random.randrange(*args)
+    #logfp.write("# randrange%s -> %s at %s\n" % (args, v, traceback.extract_stack()[-2][1:]))
+    return v
+
+def randreal(*args):
+    v = random.random()
+    if len(args) == 1:
+        v *= args[0]           # returns from [0, a1)
+    elif len(args) == 2:
+        v = args[0] + v*args[1]        # returns from [a1, a2)
+    #logfp.write("# randreal%s -> %s at %s\n" % (args, v, traceback.extract_stack()[-2][1:]))
+    return v
+
 # Code from ai.c begins here
 
-def tryexit(look, ienm, loccom, irun):
+def tryexit(enemy, look, irun):
     # a bad guy attempts to bug out 
     iq = coord()
     iq.x = game.quadrant.x+(look.x+(QUADSIZE-1))/QUADSIZE - 1
@@ -567,40 +631,35 @@ def tryexit(look, ienm, loccom, irun):
        game.state.galaxy[iq.x][iq.y].supernova or \
        game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
        return False; # no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons 
-    if ienm == IHR:
+    if enemy.type == IHR:
        return False; # Romulans cannot escape! 
     if not irun:
        # avoid intruding on another commander's territory 
-       if ienm == IHC:
-           for n in range(1, game.state.remcom+1):
+       if enemy.type == IHC:
+           for n in range(game.state.remcom):
                if game.state.kcmdr[n] == iq:
                    return False
            # refuse to leave if currently attacking starbase 
            if game.battle == game.quadrant:
                return False
        # don't leave if over 1000 units of energy 
-       if game.kpower[loccom] > 1000.0:
+       if enemy.kpower > 1000.0:
            return False
     # print 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:
-       crmena(True, ienm, "sector", game.ks[loccom])
+       game.condition == "docked":
+       crmena(True, enemy.type, "sector", enemy.kloc)
        prout(_(" escapes to Quadrant %s (and regains strength).") % q)
-    # handle local matters related to escape 
-    game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT
-    game.ks[loccom] = game.ks[game.nenhere]
-    game.kavgd[loccom] = game.kavgd[game.nenhere]
-    game.kpower[loccom] = game.kpower[game.nenhere]
-    game.kdist[loccom] = game.kdist[game.nenhere]
+    # handle local matters related to escape
+    enemy.move(None)
     game.klhere -= 1
-    game.nenhere -= 1
-    if game.condition != docked:
+    if game.condition != "docked":
        newcnd()
     # Handle global matters related to escape 
     game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
     game.state.galaxy[iq.x][iq.y].klingons += 1
-    if ienm==IHS:
+    if enemy.type==IHS:
        game.ishere = False
        game.iscate = False
        game.ientesc = False
@@ -609,7 +668,7 @@ def tryexit(look, ienm, loccom, irun):
        unschedule(FSCDBAS)
        game.state.kscmdr=iq
     else:
-       for n in range(1, game.state.remcom+1):
+       for n in range(game.state.remcom):
            if game.state.kcmdr[n] == game.quadrant:
                game.state.kcmdr[n]=iq
                break
@@ -656,7 +715,7 @@ def tryexit(look, ienm, loccom, irun):
 # 5.  Motion is limited to skill level, except for SC hi-tailing it out.
 # 
 
-def movebaddy(com, loccom, ienm):
+def movebaddy(enemy):
     # tactical movement for the bad guys 
     next = coord(); look = coord()
     irun = False
@@ -665,18 +724,16 @@ def movebaddy(com, loccom, ienm):
        nbaddys = ((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0)
     else:
        nbaddys = game.comhere + game.ishere
-
-    dist1 = game.kdist[loccom]
+    dist1 = enemy.kdist
     mdist = int(dist1 + 0.5); # Nearest integer distance 
-
     # If SC, check with spy to see if should hi-tail it 
-    if ienm==IHS and \
-       (game.kpower[loccom] <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
+    if enemy.type==IHS and \
+       (enemy.kpower <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
        irun = True
        motion = -QUADSIZE
     else:
        # decide whether to advance, retreat, or hold position 
-       forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1)
+       forces = enemy.kpower+100.0*len(game.enemies)+400*(nbaddys-1)
        if not game.shldup:
            forces += 1000; # Good for enemy if shield is down! 
        if not damaged(DPHASER) or not damaged(DPHOTON):
@@ -693,12 +750,12 @@ def movebaddy(com, loccom, ienm):
            forces += 1000.0
        motion = 0
         if forces <= 1000.0 and game.condition != "docked": # Typical situation 
-           motion = ((forces+200.0*random.random())/150.0) - 5.0
+           motion = ((forces + randreal(200))/150.0) - 5.0
        else:
             if forces > 1000.0: # Very strong -- move in for kill 
-               motion = (1.0-square(random.random()))*dist1 + 1.0
+               motion = (1.0-square(randreal()))*dist1 + 1.0
            if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
-               motion -= game.skill*(2.0-square(random.random()))
+               motion -= game.skill*(2.0-square(randreal()))
        if idebug:
            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
        # don't move if no motion 
@@ -712,9 +769,9 @@ def movebaddy(com, loccom, ienm):
                 motion = game.skill
     # calculate preferred number of steps 
     if motion < 0:
-        msteps = -motion
+        nsteps = -motion
     else:
-        msteps = motion
+        nsteps = motion
     if motion > 0 and nsteps > mdist:
        nsteps = mdist; # don't overshoot 
     if nsteps > QUADSIZE:
@@ -724,11 +781,11 @@ def movebaddy(com, loccom, ienm):
     if idebug:
        proutn("NSTEPS = %d:" % nsteps)
     # Compute preferred values of delta X and Y 
-    mx = game.sector.x - com.x
-    my = game.sector.y - com.y
+    mx = game.sector.x - enemy.kloc.x
+    my = game.sector.y - enemy.kloc.y
     if 2.0 * abs(mx) < abs(my):
        mx = 0
-    if 2.0 * abs(my) < abs(game.sector.x-com.x):
+    if 2.0 * abs(my) < abs(game.sector.x-enemy.kloc.x):
        my = 0
     if mx != 0:
         if mx*motion < 0:
@@ -740,7 +797,7 @@ def movebaddy(com, loccom, ienm):
             my = -1
         else:
             my = 1
-    next = com
+    next = enemy.kloc
     # main move loop 
     for ll in range(nsteps):
        if idebug:
@@ -760,25 +817,25 @@ def movebaddy(com, loccom, ienm):
        attempts = 0; # Settle mysterious hang problem 
        while attempts < 20 and not success:
             attempts += 1
-           if look.x < 1 or look.x > QUADSIZE:
-               if motion < 0 and tryexit(look, ienm, loccom, irun):
+           if look.x < 0 or look.x >= QUADSIZE:
+               if motion < 0 and tryexit(enemy, look, irun):
                    return
                if krawlx == mx or my == 0:
                    break
                look.x = next.x + krawlx
                krawlx = -krawlx
-           elif look.y < 1 or look.y > QUADSIZE:
-               if motion < 0 and tryexit(look, ienm, loccom, irun):
+           elif look.y < 0 or look.y >= QUADSIZE:
+               if motion < 0 and tryexit(enemy, look, irun):
                    return
                if krawly == my or mx == 0:
                    break
                look.y = next.y + krawly
                krawly = -krawly
            elif (game.options & OPTION_RAMMING) and game.quad[look.x][look.y] != IHDOT:
-               # See if we should ram ship 
+               # See if enemy should ram ship 
                if game.quad[look.x][look.y] == game.ship and \
-                   (ienm == IHC or ienm == IHS):
-                   ram(True, ienm, com)
+                   (enemy.type == IHC or enemy.type == IHS):
+                   collision(rammed=True, enemy=enemy)
                    return
                if krawlx != mx and my != 0:
                    look.x = next.x + krawlx
@@ -796,21 +853,20 @@ def movebaddy(com, loccom, ienm):
                proutn(`next`)
        else:
            break; # done early 
-       
     if idebug:
        skip(1)
     # Put commander in place within same quadrant 
-    game.quad[com.x][com.y] = IHDOT
-    game.quad[next.x][next.y] = ienm
-    if next != com:
+    game.quad[enemy.kloc.x][enemy.kloc.y] = IHDOT
+    game.quad[next.x][next.y] = enemy.type
+    if next != enemy.kloc:
        # it moved 
-       game.ks[loccom] = next
-       game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next)
-       if not damaged(DSRSENS) or game.condition == docked:
+       enemy.kloc = next
+       enemy.kdist = enemy.kavgd = distance(game.sector, next)
+       if not damaged(DSRSENS) or game.condition == "docked":
            proutn("***")
-           cramen(ienm)
-           proutn(_(" from Sector %s") % com)
-           if game.kdist[loccom] < dist1:
+           cramen(enemy.type)
+           proutn(_(" from Sector %s") % enemy.kloc)
+           if enemy.kdist < dist1:
                proutn(_(" advances to "))
            else:
                proutn(_(" retreats to "))
@@ -823,26 +879,22 @@ def moveklings():
     # Figure out which Klingon is the commander (or Supercommander)
     # and do move
     if game.comhere:
-       for i in range(1, game.nenhere+1):
-           w = game.ks[i]
-           if game.quad[w.x][w.y] == IHC:
-               movebaddy(w, i, IHC)
-               break
+        for enemy in game.enemies:
+           if enemy.type == IHC:
+               movebaddy(enemy)
     if game.ishere:
-       for i in range(1, game.nenhere+1):
-           w = game.ks[i]
-           if game.quad[w.x][w.y] == IHS:
-               movebaddy(w, i, IHS)
+        for enemy in game.enemies:
+           if enemy.type == IHS:
+               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
     # commander(s) do.
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
-       for i in range(1, game.nenhere+1):
-           w = game.ks[i]
-           if game.quad[w.x][w.y] == IHK or game.quad[w.x][w.y] == IHR:
-               movebaddy(w, i, game.quad[w.x][w.y])
-    sortklings();
+        for enemy in game.enemies:
+            if enemy.type in (IHK, IHR):
+               movebaddy(enemy)
+    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
 
 def movescom(iq, avoid):
     # commander movement helper 
@@ -852,7 +904,7 @@ def movescom(iq, avoid):
        return 1
     if avoid:
        # Avoid quadrants with bases if we want to avoid Enterprise 
-       for i in range(1, game.state.rembase+1):
+       for i in range(game.state.rembase):
            if game.state.baseq[i] == iq:
                return True
     if game.justin and not game.iscate:
@@ -868,26 +920,21 @@ def movescom(iq, avoid):
        game.ishere = False
        game.ientesc = False
        unschedule(FSCDBAS)
-       for i in range(1, game.nenhere+1):
-           if game.quad[game.ks[i].x][game.ks[i].y] == IHS:
+       for enemy in game.enemies:
+           if enemy.type == IHS:
                break
-       game.quad[game.ks[i].x][game.ks[i].y] = IHDOT
-       game.ks[i] = game.ks[game.nenhere]
-       game.kdist[i] = game.kdist[game.nenhere]
-       game.kavgd[i] = game.kavgd[game.nenhere]
-       game.kpower[i] = game.kpower[game.nenhere]
+       enemy.move(None)
        game.klhere -= 1
-       game.nenhere -= 1
-       if game.condition!=docked:
+       if game.condition != "docked":
            newcnd()
-       sortklings()
+        game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
     # check for a helpful planet 
     for i in range(game.inplan):
        if game.state.planets[i].w == game.state.kscmdr and \
            game.state.planets[i].crystals == "present":
            # destroy the planet 
            game.state.planets[i].pclass = "destroyed"
-           game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET
+           game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = None
            if communicating():
                announce()
                prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
@@ -919,14 +966,14 @@ def supercommander():
            unschedule(FSCMOVE)
            return
        sc = game.state.kscmdr
-       for i in range(1, game.state.rembase+1):
+       for i in range(game.state.rembase):
            basetbl.append((i, distance(game.state.baseq[i], sc)))
        if game.state.rembase > 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 in range(1, game.state.rembase+1):
+       for i2 in range(game.state.rembase):
            i = basetbl[i2][0]; # bug in original had it not finding nearest
            ibq = game.state.baseq[i]
            if ibq == game.quadrant or ibq == game.battle or \
@@ -935,7 +982,7 @@ def supercommander():
                continue
            # if there is a commander, and no other base is appropriate,
            #   we will take the one with the commander
-           for j in range(1, game.state.remcom+1):
+           for j in range(game.state.remcom):
                if ibq == game.state.kcmdr[j] and ifindit!= 2:
                    ifindit = 2
                    iwhichb = i
@@ -945,7 +992,7 @@ def supercommander():
                iwhichb = i
                break
        if ifindit==0:
-           return; # Nothing suitable -- wait until next time
+           return # Nothing suitable -- wait until next time
        ibq = game.state.baseq[iwhichb]
        # decide how to move toward base 
        idelta = ibq - game.state.kscmdr
@@ -980,19 +1027,19 @@ def supercommander():
     if game.state.rembase == 0:
        unschedule(FSCMOVE)
     else:
-       for i in range(1, game.state.rembase+1):
+       for i in range(game.state.rembase):
            ibq = game.state.baseq[i]
            if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
                # attack the base 
                if avoid:
-                   return; # no, don't attack base! 
+                   return # no, don't attack base! 
                game.iseenit = False
                game.isatb = 1
-               schedule(FSCDBAS, 1.0 +2.0*random.random())
+               schedule(FSCDBAS, randreal(1.0, 3.0))
                if is_scheduled(FCDBAS):
                    postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
                if not communicating():
-                   return; # no warning 
+                   return # no warning 
                game.iseenit = True
                announce()
                prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") \
@@ -1010,72 +1057,67 @@ def supercommander():
                return
     # Check for intelligence report 
     if not idebug and \
-       (random.random() > 0.2 or \
+       (withprob(0.8) or \
         (not communicating()) or \
         not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted):
        return
     announce()
     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
     proutn(_("   the Super-commander is in Quadrant %s,") % game.state.kscmdr)
-    return;
+    return
 
 def movetholian():
     # move the Tholian 
-    if not game.ithere or game.justin:
+    if not game.tholian or game.justin:
        return
-
-    if game.tholian.x == 1 and game.tholian.y == 1:
-       idx = 1; idy = QUADSIZE
-    elif game.tholian.x == 1 and game.tholian.y == QUADSIZE:
-       idx = QUADSIZE; idy = QUADSIZE
-    elif game.tholian.x == QUADSIZE and game.tholian.y == QUADSIZE:
-       idx = QUADSIZE; idy = 1
-    elif game.tholian.x == QUADSIZE and game.tholian.y == 1:
-       idx = 1; idy = 1
+    if game.tholian.kloc.x == 0 and game.tholian.kloc.y == 0:
+       idx = 0; idy = QUADSIZE-1
+    elif game.tholian.kloc.x == 0 and game.tholian.kloc.y == QUADSIZE-1:
+       idx = QUADSIZE-1; idy = QUADSIZE-1
+    elif game.tholian.kloc.x == QUADSIZE-1 and game.tholian.kloc.y == QUADSIZE-1:
+       idx = QUADSIZE-1; idy = 0
+    elif game.tholian.kloc.x == QUADSIZE-1 and game.tholian.kloc.y == 0:
+       idx = 0; idy = 0
     else:
        # something is wrong! 
-       game.ithere = False
+       game.tholian = None
        return
-
     # do nothing if we are blocked 
     if game.quad[idx][idy]!= IHDOT and game.quad[idx][idy]!= IHWEB:
        return
-    game.quad[game.tholian.x][game.tholian.y] = IHWEB
-
-    if game.tholian.x != idx:
+    game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHWEB
+    if game.tholian.kloc.x != idx:
        # move in x axis 
-       im = math.fabs(idx - game.tholian.x)*1.0/(idx - game.tholian.x)
-       while game.tholian.x != idx:
-           game.tholian.x += im
-           if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
-               game.quad[game.tholian.x][game.tholian.y] = IHWEB
-    elif game.tholian.y != idy:
+       im = math.fabs(idx - game.tholian.kloc.x)*1.0/(idx - game.tholian.kloc.x)
+       while game.tholian.kloc.x != idx:
+           game.tholian.kloc.x += im
+           if game.quad[game.tholian.kloc.x][game.tholian.kloc.y]==IHDOT:
+               game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHWEB
+    elif game.tholian.kloc.y != idy:
        # move in y axis 
-       im = math.fabs(idy - game.tholian.y)*1.0/(idy - game.tholian.y)
-       while game.tholian.y != idy:
-           game.tholian.y += im
-           if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
-               game.quad[game.tholian.x][game.tholian.y] = IHWEB
-    game.quad[game.tholian.x][game.tholian.y] = IHT
-    game.ks[game.nenhere] = game.tholian
-
+       im = math.fabs(idy - game.tholian.kloc.y)*1.0/(idy - game.tholian.kloc.y)
+       while game.tholian.kloc.y != idy:
+           game.tholian.kloc.y += im
+           if game.quad[game.tholian.kloc.x][game.tholian.kloc.y]==IHDOT:
+               game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHWEB
+    game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHT
+    #game.enemies[-1].kloc = game.tholian      #FIXME
     # check to see if all holes plugged 
-    for i in range(1, QUADSIZE+1):
-       if game.quad[1][i]!=IHWEB and game.quad[1][i]!=IHT:
+    for i in range(QUADSIZE):
+       if game.quad[0][i]!=IHWEB and game.quad[0][i]!=IHT:
            return
        if game.quad[QUADSIZE][i]!=IHWEB and game.quad[QUADSIZE][i]!=IHT:
            return
-       if game.quad[i][1]!=IHWEB and game.quad[i][1]!=IHT:
+       if game.quad[i][0]!=IHWEB and game.quad[i][0]!=IHT:
            return
        if game.quad[i][QUADSIZE]!=IHWEB and game.quad[i][QUADSIZE]!=IHT:
            return
     # All plugged up -- Tholian splits 
-    game.quad[game.tholian.x][game.tholian.y]=IHWEB
+    game.quad[game.tholian.kloc.x][game.tholian.kloc.y]=IHWEB
     dropin(IHBLANK)
     crmena(True, IHT, "sector", game.tholian)
     prout(_(" completes web."))
-    game.ithere = False
-    game.nenhere -= 1
+    game.tholian.move(None)
     return
 
 # Code from battle.c begins here
@@ -1203,7 +1245,7 @@ def randdevice():
     # 
     # This is one place where OPTION_PLAIN does not restore the
     # original behavior, which was equiprobable damage across
-    # all devices.  If we wanted that, we'd return NDEVICES*random.random()
+    # all devices.  If we wanted that, we'd return randrange(NDEVICES)
     # and have done with it.  Also, in the original game, DNAVYS
     # and DCOMPTR were the same device. 
     # 
@@ -1231,7 +1273,7 @@ def randdevice():
        10,     # DDRAY: death ray                       1.0% 
        30,     # DDSP: deep-space probes                3.0% 
     )
-    idx = random.random() * 1000.0     # weights must sum to 1000 
+    idx = randrange(1000)      # weights must sum to 1000 
     sum = 0
     for (i, w) in enumerate(weights):
        sum += w
@@ -1239,28 +1281,28 @@ def randdevice():
            return i
     return None;       # we should never get here
 
-def ram(ibumpd, ienm, w):
-    # make our ship ram something 
+def collision(rammed, enemy):
+    # collision handling
     prouts(_("***RED ALERT!  RED ALERT!"))
     skip(1)
     prout(_("***COLLISION IMMINENT."))
     skip(2)
     proutn("***")
     crmshp()
-    hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(ienm, 1.0)
-    if ibumpd:
+    hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(enemy.type, 1.0)
+    if rammed:
         proutn(_(" rammed by "))
     else:
         proutn(_(" rams "))
-    crmena(False, ienm, sector, w)
-    if ibumpd:
+    crmena(False, enemy.type, "sector", enemy.kloc)
+    if rammed:
        proutn(_(" (original position)"))
     skip(1)
-    deadkl(w, ienm, game.sector)
+    deadkl(enemy.kloc, enemy.type, game.sector)
     proutn("***")
     crmshp()
     prout(_(" heavily damaged."))
-    icas = 10.0+20.0*random.random()
+    icas = randrange(10, 30)
     prout(_("***Sickbay reports %d casualties"), icas)
     game.casual += icas
     game.state.crew -= icas
@@ -1269,12 +1311,12 @@ def ram(ibumpd, ienm, w):
     # which was silly.  Instead, pick up to half the devices at
     # random according to our weighting table,
     # 
-    ncrits = random.randrange(NDEVICES/2)
+    ncrits = randrange(NDEVICES/2)
     for m in range(ncrits):
        dev = randdevice()
        if game.damage[dev] < 0:
            continue
-       extradm = (10.0*hardness*random.random()+1.0)*game.damfac
+       extradm = (10.0*hardness*randreal()+1.0)*game.damfac
        # Damage for at least time of travel! 
        game.damage[dev] += game.optime + extradm
     game.shldup = False
@@ -1284,7 +1326,7 @@ def ram(ibumpd, ienm, w):
        damagereport()
     else:
        finish(FWON)
-    return;
+    return
 
 def torpedo(course, r, incoming, i, n):
     # let a photon torpedo fly 
@@ -1308,9 +1350,9 @@ def torpedo(course, r, incoming, i, n):
     # Loop to move a single torpedo 
     for l in range(1, 15+1):
        x += deltax
-       w.x = x + 0.5
+       w.x = int(x + 0.5)
        y += deltay
-       w.y = y + 0.5
+       w.y = int(y + 0.5)
        if not VALID_SECTOR(w.x, w.y):
            break
        iquad=game.quad[w.x][w.y]
@@ -1326,20 +1368,20 @@ def torpedo(course, r, incoming, i, n):
            proutn(_("Torpedo hits "))
            crmshp()
            prout(".")
-           hit = 700.0 + 100.0*random.random() - \
+           hit = 700.0 + randreal(100) - \
                1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
            newcnd(); # we're blown out of dock 
            # We may be displaced. 
            if game.landed or game.condition=="docked":
                return hit # Cheat if on a planet 
-           ang = angle + 2.5*(random.random()-0.5)
+           ang = angle + 2.5*(randreal()-0.5)
            temp = math.fabs(math.sin(ang))
            if math.fabs(math.cos(ang)) > temp:
                temp = math.fabs(math.cos(ang))
            xx = -math.sin(ang)/temp
            yy = math.cos(ang)/temp
-           jw.x=w.x+xx+0.5
-           jw.y=w.y+yy+0.5
+           jw.x = int(w.x+xx+0.5)
+           jw.y = int(w.y+yy+0.5)
            if not VALID_SECTOR(jw.x, jw.y):
                return hit
            if game.quad[jw.x][jw.y]==IHBLANK:
@@ -1352,32 +1394,32 @@ def torpedo(course, r, incoming, i, n):
            crmshp()
            shoved = True
        elif iquad in (IHC, IHS): # Hit a commander 
-           if random.random() <= 0.05:
-               crmena(True, iquad, sector, w)
+           if withprob(0.05):
+               crmena(True, iquad, "sector", w)
                prout(_(" uses anti-photon device;"))
                prout(_("   torpedo neutralized."))
                return None
        elif iquad in (IHR, IHK): # Hit a regular enemy 
            # find the enemy 
-           for ll in range(1, game.nenhere+1):
-               if w == game.ks[ll]:
+            for enemy in game.enemies:
+               if w == game.enemies[ll].kloc:
                    break
-           kp = math.fabs(game.kpower[ll])
-           h1 = 700.0 + 100.0*random.random() - \
+           kp = math.fabs(e.kpower)
+           h1 = 700.0 + randrange(100) - \
                1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
            h1 = math.fabs(h1)
            if kp < h1:
                h1 = kp
-            if game.kpower[ll] < 0:
-                game.kpower[ll] -= -h1
+            if enemy.kpower < 0:
+                enemy.kpower -= -h1
             else:
-                game.kpower[ll] -= h1
-           if game.kpower[ll] == 0:
+                enemy.kpower -= h1
+           if enemy.kpower == 0:
                deadkl(w, iquad, w)
                return None
            crmena(True, iquad, "sector", w)
            # If enemy damaged but not destroyed, try to displace 
-           ang = angle + 2.5*(random.random()-0.5)
+           ang = angle + 2.5*(randreal()-0.5)
            temp = math.fabs(math.sin(ang))
            if math.fabs(math.cos(ang)) > temp:
                temp = math.fabs(math.cos(ang))
@@ -1397,13 +1439,13 @@ def torpedo(course, r, incoming, i, n):
                prout(_(" damaged but not destroyed."))
                return None
            proutn(_(" damaged--"))
-           game.ks[ll] = jw
+           enemy.kloc = jw
            shoved = True
            break
        elif iquad == IHB: # Hit a base 
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
-           for ll in range(1, game.state.rembase+1):
+           for ll in range(game.state.rembase):
                if game.state.baseq[ll] == game.quadrant:
                    game.state.baseq[ll]=game.state.baseq[game.state.rembase]
                    break
@@ -1416,12 +1458,12 @@ def torpedo(course, r, incoming, i, n):
            newcnd()
            return None
        elif iquad == IHP: # Hit a planet 
-           crmena(True, iquad, sector, w)
+           crmena(True, iquad, "sector", w)
            prout(_(" destroyed."))
            game.state.nplankl += 1
-           game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
-           game.state.planets[game.iplnet].pclass = destroyed
-           game.iplnet = 0
+           game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
+           game.iplnet.pclass = "destroyed"
+           game.iplnet = None
            invalidate(game.plnet)
            game.quad[w.x][w.y] = IHDOT
            if game.landed:
@@ -1429,12 +1471,12 @@ def torpedo(course, r, incoming, i, n):
                finish(FDPLANET)
            return None
        elif iquad == IHW: # Hit an inhabited world -- very bad! 
-           crmena(True, iquad, sector, w)
+           crmena(True, iquad, "sector", w)
            prout(_(" destroyed."))
            game.state.nworldkl += 1
-           game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
-           game.state.planets[game.iplnet].pclass = destroyed
-           game.iplnet = 0
+           game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
+           game.iplnet.pclass = "destroyed"
+           game.iplnet = None
            invalidate(game.plnet)
            game.quad[w.x][w.y] = IHDOT
            if game.landed:
@@ -1444,14 +1486,14 @@ def torpedo(course, r, incoming, i, n):
            prout(_("Celebratory rallies are being held on the Klingon homeworld."))
            return None
        elif iquad == IHSTAR: # Hit a star 
-           if random.random() > 0.10:
+           if withprob(0.9):
                nova(w)
-               return None
-           crmena(True, IHSTAR, sector, w)
-           prout(_(" unaffected by photon blast."))
+            else:
+                crmena(True, IHSTAR, "sector", w)
+                prout(_(" unaffected by photon blast."))
            return None
        elif iquad == IHQUEST: # Hit a thingy 
-           if not (game.options & OPTION_THINGY) or random.random()>0.7:
+           if not (game.options & OPTION_THINGY) or withprob(0.3):
                skip(1)
                prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
                skip(1)
@@ -1466,13 +1508,13 @@ def torpedo(course, r, incoming, i, n):
                # 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.
-               # 
-               iqengry = True
+               #
+               thing.angry = True
                shoved = True
            return None
        elif iquad == IHBLANK: # Black hole 
            skip(1)
-           crmena(True, IHBLANK, sector, w)
+           crmena(True, IHBLANK, "sector", w)
            prout(_(" swallows torpedo."))
            return None
        elif iquad == IHWEB: # hit the web 
@@ -1480,29 +1522,28 @@ def torpedo(course, r, incoming, i, n):
            prout(_("***Torpedo absorbed by Tholian web."))
            return None
        elif iquad == IHT:  # Hit a Tholian 
-           h1 = 700.0 + 100.0*random.random() - \
+           h1 = 700.0 + randrange(100) - \
                1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
            h1 = math.fabs(h1)
            if h1 >= 600:
                game.quad[w.x][w.y] = IHDOT
-               game.ithere = False
                deadkl(w, iquad, w)
+               game.tholian = None
                return None
            skip(1)
-           crmena(True, IHT, sector, w)
-           if random.random() > 0.05:
+           crmena(True, IHT, "sector", w)
+           if withprob(0.05):
                prout(_(" survives photon blast."))
                return None
            prout(_(" disappears."))
+           game.tholian.move(None)
            game.quad[w.x][w.y] = IHWEB
-           game.ithere = False
-           game.nenhere -= 1
            dropin(IHBLANK)
            return None
         else: # Problem!
            skip(1)
            proutn("Don't know how to handle torpedo collision with ")
-           crmena(True, iquad, sector, w)
+           crmena(True, iquad, "sector", w)
            skip(1)
            return None
        break
@@ -1512,9 +1553,9 @@ def torpedo(course, r, incoming, i, n):
        game.quad[w.x][w.y]=IHDOT
        game.quad[jw.x][jw.y]=iquad
        prout(_(" displaced by blast to Sector %s ") % jw)
-       for ll in range(1, game.nenhere+1):
-           game.kdist[ll] = game.kavgd[ll] = distance(game.sector,game.ks[ll])
-       sortklings()
+       for ll in range(len(game.enemies)):
+           game.enemies[ll].kdist = game.enemies[ll].kavgd = distance(game.sector,game.enemies[ll].kloc)
+        game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
        return None
     skip(1)
     prout(_("Torpedo missed."))
@@ -1522,12 +1563,9 @@ def torpedo(course, r, incoming, i, n):
 
 def fry(hit):
     # critical-hit resolution 
-    ktr=1
-    # a critical hit occured 
-    if hit < (275.0-25.0*game.skill)*(1.0+0.5*random.random()):
+    if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
        return
-
-    ncrit = 1.0 + hit/(500.0+100.0*random.random())
+    ncrit = int(1.0 + hit/(500.0+randreal(100)))
     proutn(_("***CRITICAL HIT--"))
     # Select devices and cause damage
     cdam = []
@@ -1538,19 +1576,16 @@ def fry(hit):
             if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
                 break
        cdam.append(j)
-       extradm = (hit*game.damfac)/(ncrit*(75.0+25.0*random.random()))
+       extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
        game.damage[j] += extradm
-       if loop1 > 0:
-            for loop2 in range(loop1):
-                if j == cdam[loop2]:
-                    break
-           if loop2 < loop1:
-               continue
-           ktr += 1
-           if ktr==3:
-               skip(1)
-           proutn(_(" and "))
+    skipcount = 0
+    for (i, j) in enumerate(cdam):
        proutn(device[j])
+        if skipcount % 3 == 2 and i < len(cdam)-1:
+            skip()
+        skipcount += 1
+        if i < len(cdam)-1:
+            proutn(_(" and "))
     prout(_(" damaged."))
     if damaged(DSHIELD) and game.shldup:
        prout(_("***Shields knocked down."))
@@ -1558,91 +1593,78 @@ def fry(hit):
 
 def attack(torps_ok):
     # bad guy attacks us 
-    # torps_ok == false forces use of phasers in an attack 
-    atackd = False; attempt = False; ihurt = False;
+    # torps_ok == False forces use of phasers in an attack 
+    attempt = False; ihurt = False;
     hitmax=0.0; hittot=0.0; chgfac=1.0
-    jay = coord()
     where = "neither"
-
     # game could be over at this point, check 
     if game.alldone:
        return
-
     if idebug:
        prout("=== ATTACK!")
-
-    # Tholian gewts to move before attacking 
-    if game.ithere:
+    # Tholian gets to move before attacking 
+    if game.tholian:
        movetholian()
-
     # if you have just entered the RNZ, you'll get a warning 
     if game.neutz: # The one chance not to be attacked 
        game.neutz = False
        return
-
     # commanders get a chance to tac-move towards you 
     if (((game.comhere or game.ishere) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
        moveklings()
-
     # if no enemies remain after movement, we're done 
-    if game.nenhere==0 or (game.nenhere==1 and iqhere and not iqengry):
+    if len(game.enemies)==0 or (len(game.enemies)==1 and thing == game.quadrant and not thing.angry):
        return
-
     # set up partial hits if attack happens during shield status change 
     pfac = 1.0/game.inshld
     if game.shldchg:
-       chgfac = 0.25+0.5*random.random()
-
+       chgfac = 0.25 + randreal(0.5)
     skip(1)
-
     # message verbosity control 
     if game.skill <= SKILL_FAIR:
        where = "sector"
-
-    for loop in range(1, game.nenhere+1):
-       if game.kpower[loop] < 0:
+    for enemy in game.enemies:
+       if enemy.kpower < 0:
            continue;   # too weak to attack 
        # compute hit strength and diminish shield power 
-       r = random.random()
-       # Increase chance of photon torpedos if docked or enemy energy low 
+       r = randreal()
+       # Increase chance of photon torpedos if docked or enemy energy is low 
        if game.condition == "docked":
            r *= 0.25
-       if game.kpower[loop] < 500:
+       if enemy.kpower < 500:
            r *= 0.25; 
-       jay = game.ks[loop]
-       iquad = game.quad[jay.x][jay.y]
-       if iquad==IHT or (iquad==IHQUEST and not iqengry):
+       if enemy.type==IHT or (enemy.type==IHQUEST and not thing.angry):
            continue
        # different enemies have different probabilities of throwing a torp 
        usephasers = not torps_ok or \
-           (iquad == IHK and r > 0.0005) or \
-           (iquad==IHC and r > 0.015) or \
-           (iquad==IHR and r > 0.3) or \
-           (iquad==IHS and r > 0.07) or \
-           (iquad==IHQUEST and r > 0.05)
+           (enemy.type == IHK and r > 0.0005) or \
+           (enemy.type==IHC and r > 0.015) or \
+           (enemy.type==IHR and r > 0.3) or \
+           (enemy.type==IHS and r > 0.07) or \
+           (enemy.type==IHQUEST and r > 0.05)
        if usephasers:      # Enemy uses phasers 
            if game.condition == "docked":
                continue; # Don't waste the effort! 
            attempt = True; # Attempt to attack 
-           dustfac = 0.8+0.05*random.random()
-           hit = game.kpower[loop]*math.pow(dustfac,game.kavgd[loop])
-           game.kpower[loop] *= 0.75
+           dustfac = 0.8 + randreal(0.5)
+           hit = enemy.kpower*math.pow(dustfac,enemy.kavgd)
+           enemy.kpower *= 0.75
        else: # Enemy uses photon torpedo 
-           course = 1.90985*math.atan2(game.sector.y-jay.y, jay.x-game.sector.x)
+           course = 1.90985*math.atan2(game.sector.y-enemy.kloc.y, enemy.kloc.x-game.sector.x)
            hit = 0
            proutn(_("***TORPEDO INCOMING"))
            if not damaged(DSRSENS):
                proutn(_(" From "))
-               crmena(False, iquad, where, jay)
+               crmena(False, enemy.type, where, enemy.kloc)
            attempt = True
            prout("  ")
-           r = (random.random()+random.random())*0.5 -0.5
-           r += 0.002*game.kpower[loop]*r
-           hit = torpedo(course, r, jay, 1, 1)
+           r = (randreal()+randreal())*0.5 - 0.5
+           r += 0.002*enemy.kpower*r
+           hit = torpedo(course, r, enemy.kloc, 1, 1)
            if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
                finish(FWON); # Klingons did themselves in! 
            if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.alldone:
-               return; # Supernova or finished 
+               return # Supernova or finished 
            if hit == None:
                continue
        # incoming phaser or torpedo, shields may dissipate it 
@@ -1673,7 +1695,7 @@ def attack(torps_ok):
            crmshp()
        if not damaged(DSRSENS) and usephasers:
            proutn(_(" from "))
-           crmena(False, iquad, where, jay)
+           crmena(False, enemy.type, where, enemy.kloc)
        skip(1)
        # Decide if hit is critical 
        if hit > hitmax:
@@ -1687,8 +1709,6 @@ def attack(torps_ok):
        return
     if not attempt and game.condition == "docked":
        prout(_("***Enemies decide against attacking your ship."))
-    if not atackd:
-       return
     percent = 100.0*pfac*game.shield+0.5
     if not ihurt:
        # Shields fully protect ship 
@@ -1706,7 +1726,7 @@ def attack(torps_ok):
     prout(_("%d%%,   torpedoes left %d") % (percent, game.torps))
     # Check if anyone was hurt 
     if hitmax >= 200 or hittot >= 500:
-       icas= hittot*random.random()*0.015
+       icas = randrange(hittot * 0.015)
        if icas >= 2:
            skip(1)
            prout(_("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties") % icas)
@@ -1714,81 +1734,66 @@ def attack(torps_ok):
            game.casual += icas
            game.state.crew -= icas
     # After attack, reset average distance to enemies 
-    for loop in range(1, game.nenhere+1):
-       game.kavgd[loop] = game.kdist[loop]
-    sortklings()
-    return;
+    for enemy in game.enemies:
+       enemy.kavgd = enemy.kdist
+    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+    return
                
 def deadkl(w, type, mv):
     # kill a Klingon, Tholian, Romulan, or Thingy 
     # Added mv to allow enemy to "move" before dying 
-
-    crmena(True, type, sector, mv)
+    crmena(True, type, "sector", mv)
     # Decide what kind of enemy it is and update appropriately 
     if type == IHR:
-       # chalk up a Romulan 
-       game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans -= 1
-       game.irhere -= 1
-       game.state.nromrem -= 1
+        # chalk up a Romulan 
+        game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans -= 1
+        game.irhere -= 1
+        game.state.nromrem -= 1
     elif type == IHT:
-       # Killed a Tholian 
-       game.ithere = False
+        # Killed a Tholian 
+        game.tholian = None
     elif type == IHQUEST:
-       # Killed a Thingy 
-       iqhere = iqengry = False
-       invalidate(thing)
+        # Killed a Thingy
+        global thing
+        thing = None
     else:
-       # Some type of a Klingon 
-       game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
-       game.klhere -= 1
-       if type == IHC:
-           game.comhere = False
-           for i in range(1, game.state.remcom+1):
-               if game.state.kcmdr[i] == game.quadrant:
-                   break
-           game.state.kcmdr[i] = game.state.kcmdr[game.state.remcom]
-           game.state.kcmdr[game.state.remcom].x = 0
-           game.state.kcmdr[game.state.remcom].y = 0
-           game.state.remcom -= 1
-           unschedule(FTBEAM)
-           if game.state.remcom != 0:
-               schedule(FTBEAM, expran(1.0*game.incom/game.state.remcom))
-       elif type ==  IHK:
-           game.state.remkl -= 1
-       elif type ==  IHS:
-           game.state.nscrem -= 1
-           game.ishere = False
-           game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
-           game.iscate = False
-           unschedule(FSCMOVE)
-           unschedule(FSCDBAS)
-       else:
-           prout("*** Internal error, deadkl() called on %s\n" % type)
-
+        # Some type of a Klingon 
+        game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
+        game.klhere -= 1
+        if type == IHC:
+            game.comhere = False
+            for i in range(game.state.remcom):
+                if game.state.kcmdr[i] == game.quadrant:
+                    break
+            game.state.kcmdr[i] = game.state.kcmdr[game.state.remcom]
+            game.state.kcmdr[game.state.remcom].x = 0
+            game.state.kcmdr[game.state.remcom].y = 0
+            game.state.remcom -= 1
+            unschedule(FTBEAM)
+            if game.state.remcom != 0:
+                schedule(FTBEAM, expran(1.0*game.incom/game.state.remcom))
+            if is_scheduled(FCDBAS) and game.battle == game.quadrant:
+                unschedule(FCDBAS)    
+        elif type ==  IHK:
+            game.state.remkl -= 1
+        elif type ==  IHS:
+            game.state.nscrem -= 1
+            game.ishere = False
+            game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
+            game.iscate = False
+            unschedule(FSCMOVE)
+            unschedule(FSCDBAS)
     # For each kind of enemy, finish message to player 
     prout(_(" destroyed."))
-    game.quad[w.x][w.y] = IHDOT
     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
        return
     game.recompute()
-    # Remove enemy ship from arrays describing local conditions 
-    if is_scheduled(FCDBAS) and game.battle == game.quadrant and type==IHC:
-       unschedule(FCDBAS)
-    for i in range(1, game.nenhere+1):
-       if game.ks[i] == w:
+    # Remove enemy ship from arrays describing local conditions
+    for e in game.enemies:
+       if e.kloc == w:
+            e.move(None)
            break
-    game.nenhere -= 1
-    if i <= game.nenhere:
-        for j in range(i, game.nenhere+1):
-           game.ks[j] = game.ks[j+1]
-           game.kpower[j] = game.kpower[j+1]
-           game.kavgd[j] = game.kdist[j] = game.kdist[j+1]
-    game.ks[game.nenhere+1].x = 0
-    game.ks[game.nenhere+1].x = 0
-    game.kdist[game.nenhere+1] = 0
-    game.kavgd[game.nenhere+1] = 0
-    game.kpower[game.nenhere+1] = 0
-    return;
+    return
 
 def targetcheck(x, y):
     # Return None if target is invalid 
@@ -1873,35 +1878,35 @@ def photon():
            if key != IHREAL:
                huh()
                return
-           targ[i][1] = aaitem
+           targ[i][1] = int(aaitem-0.5)
            key = scan()
            if key != IHREAL:
                huh()
                return
-           targ[i][2] = aaitem
+           targ[i][2] = int(aaitem-0.5)
            chew()
             course[i] = targetcheck(targ[i][1], targ[i][2])
             if course[i] == None:
                 return
     game.ididit = True
     # Loop for moving <n> torpedoes 
-    for i in range(1, n+1):
+    for i in range(n):
        if game.condition != "docked":
            game.torps -= 1
-       r = (random.random()+random.random())*0.5 -0.5
+       r = (randreal()+randreal())*0.5 -0.5
        if math.fabs(r) >= 0.47:
            # misfire! 
-           r = (random.random()+1.2) * r
-           if n>1:
-               prouts(_("***TORPEDO NUMBER %d MISFIRES") % i)
+           r *= randreal(1.2, 2.2)
+           if n > 0:
+               prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
            else:
                prouts(_("***TORPEDO MISFIRES."))
            skip(1)
            if i < n:
                prout(_("  Remainder of burst aborted."))
-           if random.random() <= 0.2:
+           if withprob(0.2):
                prout(_("***Photon tubes damaged by misfire."))
-               game.damage[DPHOTON] = game.damfac*(1.0+2.0*random.random())
+               game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
            break
        if game.shldup or game.condition == "docked":
            r *= 1.0 + 0.0001*game.shield
@@ -1914,16 +1919,15 @@ def photon():
 def overheat(rpow):
     # check for phasers overheating 
     if rpow > 1500:
-       chekbrn = (rpow-1500.)*0.00038
-       if random.random() <= chekbrn:
+        checkburn = (rpow-1500.0)*0.00038
+        if withprob(checkburn):
            prout(_("Weapons officer Sulu-  \"Phasers overheated, sir.\""))
-           game.damage[DPHASER] = game.damfac*(1.0 + random.random()) * (1.0+chekbrn)
+           game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
 
 def checkshctrl(rpow):
     # check shield control 
-       
     skip(1)
-    if random.random() < 0.998:
+    if withprob(0.998):
        prout(_("Shields lowered."))
        return False
     # Something bad has happened 
@@ -1941,7 +1945,7 @@ def checkshctrl(rpow):
     prouts(_("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\""))
     skip(2)
     prout(_("Lt. Uhura-  \"Sir, all decks reporting damage.\""))
-    icas = hit*random.random()*0.012
+    icas = randrange(hit*0.012)
     skip(1)
     fry(0.8*hit)
     if icas:
@@ -1956,27 +1960,26 @@ def checkshctrl(rpow):
     overheat(rpow)
     return True;
 
-def hittem(doublehits):
-    # register a phaser hit on Klingons and Romulans 
-    nenhr2=game.nenhere; kk=1
+def hittem(hits):
+    # register a phaser hit on Klingons and Romulans
+    nenhr2 = game.nenhere; kk=0
     w = coord()
     skip(1)
-    for k in range(1, nenhr2+1):
-        wham = hits[k]
+    for (k, wham) in enumerate(hits):
        if wham==0:
            continue
-       dustfac = 0.9 + 0.01*random.random()
-       hit = wham*math.pow(dustfac,game.kdist[kk])
-       kpini = game.kpower[kk]
+       dustfac = randreal(0.9, 1.0)
+       hit = wham*math.pow(dustfac,game.enemies[kk].kdist)
+       kpini = game.enemies[kk].kpower
        kp = math.fabs(kpini)
        if PHASEFAC*hit < kp:
            kp = PHASEFAC*hit
-        if game.kpower[kk] < 0:
-            game.kpower[kk] -= -kp
+        if game.enemies[kk].kpower < 0:
+            game.enemies[kk].kpower -= -kp
         else:
-            game.kpower[kk] -= kp
-       kpow = game.kpower[kk]
-       w = game.ks[kk]
+            game.enemies[kk].kpower -= kp
+       kpow = game.enemies[kk].kpower
+       w = game.enemies[kk].kloc
        if hit > 0.005:
            if not damaged(DSRSENS):
                boom(w)
@@ -1985,7 +1988,7 @@ def hittem(doublehits):
            proutn(_("Very small hit on "))
        ienm = game.quad[w.x][w.y]
        if ienm==IHQUEST:
-           iqengry = True
+           thing.angry = True
        crmena(False, ienm, "sector", w)
        skip(1)
        if kpow == 0:
@@ -1994,24 +1997,23 @@ def hittem(doublehits):
                finish(FWON);           
            if game.alldone:
                return
-           kk -= 1; # don't do the increment 
+           kk -= 1     # don't do the increment
+            continue
        else: # decide whether or not to emasculate klingon 
-           if kpow > 0 and random.random() >= 0.9 and \
-               kpow <= ((0.4 + 0.4*random.random())*kpini):
-               prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s"), w)
+           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.kpower[kk] = -kpow
+               game.enemies[kk].kpower = -kpow
         kk += 1
-    return;
+    return
 
 def phasers():
     # fire phasers 
-    hits = []; rpow=0
+    hits = []
     kz = 0; k = 1; irec=0 # Cheating inhibitor 
-    ifast = False; no = False; itarg = True; msgflag = True
+    ifast = False; no = False; itarg = True; msgflag = True; rpow=0
     automode = "NOTSET"
     key=0
-
     skip(1)
     # SR sensors and Computer are needed fopr automode 
     if damaged(DSRSENS) or damaged(DCOMPTR):
@@ -2035,8 +2037,8 @@ def phasers():
            return
        prout(_("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\""))
        ifast = True
-               
-    # Original code so convoluted, I re-did it all 
+    # 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=scan()
        if key == IHALPHA:
@@ -2078,7 +2080,8 @@ def phasers():
            elif not itarg:
                automode = "FORCEMAN"
            else: 
-               proutn(_("Manual or automatic? "))                      
+               proutn(_("Manual or automatic? "))
+                chew()
     avail = game.energy
     if ifast:
         avail -= 200.0
@@ -2092,8 +2095,8 @@ def phasers():
         while True:
            chew()
            if not kz:
-               for i in range(1, game.nenhere+1):
-                   irec += math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))*(1.01+0.05*random.random()) + 1.0
+               for i in range(game.nenhere):
+                   irec += math.fabs(game.enemies[i].kpower)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
            kz=1
            proutn(_("%d units required. ") % irec)
            chew()
@@ -2125,12 +2128,12 @@ def phasers():
        if game.nenhere:
            extra = 0.0
            powrem = rpow
-           for i in range(1, game.nenhere+1):
-               hits[i] = 0.0
+           for i in range(game.nenhere):
+               hits.append(0.0)
                if powrem <= 0:
                    continue
-               hits[i] = math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))
-               over = (0.01 + 0.05*random.random())*hits[i]
+               hits[i] = math.fabs(game.enemies[i].kpower)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))
+               over = randreal(1.01, 1.06) * hits[i]
                temp = powrem
                powrem -= hits[i] + over
                if powrem <= 0 and temp < hits[i]:
@@ -2143,7 +2146,7 @@ def phasers():
            hittem(hits)
            game.ididit = True
        if extra > 0 and not game.alldone:
-           if game.ithere:
+           if game.tholian:
                proutn(_("*** Tholian web absorbs "))
                if game.nenhere>0:
                    proutn(_("excess "))
@@ -2165,8 +2168,8 @@ def phasers():
            skip(1)
     elif automode == "MANUAL":
        rpow = 0.0
-        for k in range(1, game.nenhere+1):
-           aim = game.ks[k]
+        for k in range(game.nenhere):
+           aim = game.enemies[k].kloc
            ienm = game.quad[aim.x][aim.y]
            if msgflag:
                proutn(_("Energy available= %.2f") % (avail-0.006))
@@ -2185,7 +2188,7 @@ def phasers():
            if key == IHEOL:
                chew()
                if itarg and k > kz:
-                   irec=(abs(game.kpower[k])/(PHASEFAC*math.pow(0.9,game.kdist[k]))) * (1.01+0.05*random.random()) + 1.0
+                   irec=(abs(game.enemies[k].kpower)/(PHASEFAC*math.pow(0.9,game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
                kz = k
                proutn("(")
                if not damaged(DCOMPTR):
@@ -2194,7 +2197,7 @@ def phasers():
                    proutn("??")
                proutn(")  ")
                proutn(_("units to fire at "))
-               crmena(False, ienm, sector, aim)
+               crmena(False, ienm, "sector", aim)
                proutn("-  ")
                key = scan()
            if key == IHALPHA and isit("no"):
@@ -2241,7 +2244,7 @@ def phasers():
     if ifast:
        skip(1)
        if no == 0:
-           if random.random() >= 0.99:
+           if withprob(0.99):
                prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
                prouts(_("         CLICK   CLICK   POP  . . ."))
                prout(_(" No response, sir!"))
@@ -2259,8 +2262,6 @@ def phasers():
 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
 # BSD Trek, from which we swiped the idea, can have up to 5.
 
-import math
-
 def unschedule(evtype):
     # remove an event from the schedule 
     game.future[evtype].date = FOREVER
@@ -2292,7 +2293,6 @@ def cancelrest():
            game.resting = False
            game.optime = 0.0
            return True
-
     return False
 
 def events():
@@ -2303,9 +2303,8 @@ def events():
     w = coord(); hold = coord()
     ev = event(); ev2 = event()
 
-    def tractorbeam():
+    def tractorbeam(yank):
         # tractor beaming cases merge here 
-        yank = math.sqrt(yank)
         announce()
         game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5) 
         skip(1)
@@ -2322,14 +2321,14 @@ def events():
         # Check to see if shuttle is aboard 
         if game.iscraft == "offship":
             skip(1)
-            if random.random() > 0.5:
+            if withprob(0.5):
                 prout(_("Galileo, left on the planet surface, is captured"))
                 prout(_("by aliens and made into a flying McDonald's."))
                 game.damage[DSHUTTL] = -10
                 game.iscraft = "removed"
             else:
                 prout(_("Galileo, left on the planet surface, is well hidden."))
-        if evcode==0:
+        if evcode == FSPY:
             game.quadrant = game.state.kscmdr
         else:
             game.quadrant = game.state.kcmdr[i]
@@ -2342,8 +2341,8 @@ def events():
             game.resting = False
         if not game.shldup:
             if not damaged(DSHIELD) and game.shield > 0:
-                doshield(True) # raise shields 
-                game.shldchg=False
+                doshield(shraise=True) # raise shields 
+                game.shldchg = False
             else:
                 prout(_("(Shields not currently useable.)"))
         newqad(False)
@@ -2447,7 +2446,7 @@ def events():
        if game.condition == "docked":
            repair /= game.docfac
        # Don't fix Deathray here 
-       for l in range(0, NDEVICES):
+       for l in range(NDEVICES):
            if game.damage[l] > 0.0 and l != DDRAY:
                 if game.damage[l]-repair > 0.0:
                     game.damage[l] -= repair
@@ -2484,27 +2483,25 @@ def events():
                 (game.energy < 2500 or damaged(DPHASER)) and \
                  (game.torps < 5 or damaged(DPHOTON))):
                # Tractor-beam her! 
-               istract = True
-               yank = distance(game.state.kscmdr, game.quadrant)
-                ictbeam = True
-                tractorbeam()
+               istract = ictbeam = True
+                tractorbeam(distance(game.state.kscmdr, game.quadrant))
            else:
                return
        elif evcode == FTBEAM: # Tractor beam 
             if game.state.remcom == 0:
                 unschedule(FTBEAM)
                 continue
-            i = random.random()*game.state.remcom+1.0
-            yank = square(game.state.kcmdr[i].x-game.quadrant.x) + square(game.state.kcmdr[i].y-game.quadrant.y)
+            i = randrange(game.state.remcom)
+            yank = distance(game.state.kcmdr[i], game.quadrant)
             if istract or game.condition == "docked" or yank == 0:
                 # Drats! Have to reschedule 
                 schedule(FTBEAM, 
                          game.optime + expran(1.5*game.intime/game.state.remcom))
                 continue
             ictbeam = True
-            tractorbeam()
+            tractorbeam(yank)
        elif evcode == FSNAP: # Snapshot of the universe (for time warp) 
-           game.snapsht = game.state
+           game.snapsht = copy.deepcopy(game.state)
            game.state.snap = True
            schedule(FSNAP, expran(0.5 * game.intime))
        elif evcode == FBATTAK: # Commander attacks starbase 
@@ -2514,8 +2511,8 @@ def events():
                unschedule(FCDBAS)
                 continue
            i = 0
-           for j in range(1, game.state.rembase+1):
-               for k in range(1, game.state.remcom+1):
+           for j in range(game.state.rembase):
+               for k in range(game.state.remcom):
                    if game.state.baseq[j] == game.state.kcmdr[k] and \
                        not game.state.baseq[j] == game.quadrant and \
                         not game.state.baseq[j] == game.state.kscmdr:
@@ -2529,7 +2526,7 @@ def events():
                continue
            # commander + starbase combination found -- launch attack 
            game.battle = game.state.baseq[j]
-           schedule(FCDBAS, 1.0+3.0*random.random())
+           schedule(FCDBAS, randreal(1.0, 4.0))
            if game.isatb: # extra time if SC already attacking 
                postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
            game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
@@ -2539,10 +2536,9 @@ def events():
            game.iseenit = True
            announce()
            skip(1)
-           proutn(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") % game.battle)
+           prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") % game.battle)
            prout(_("   reports that it is under attack and that it can"))
-           proutn(_("   hold out only until stardate %d") % (int(scheduled(FCDBAS))))
-            prout(".\"")
+           prout(_("   hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
            if cancelrest():
                 return
        elif evcode == FSCDBAS: # Supercommander destroys base 
@@ -2557,7 +2553,7 @@ def events():
            if evcode==FCDBAS:
                unschedule(FCDBAS)
                # find the lucky pair 
-               for i in range(1, game.state.remcom+1):
+               for i in range(game.state.remcom):
                    if game.state.kcmdr[i] == game.battle: 
                        break
                if i > game.state.remcom or game.state.rembase == 0 or \
@@ -2623,9 +2619,9 @@ def events():
                # supernova'ed, and which has some Klingons in it
                w = randplace(GALSIZE)
                q = game.state.galaxy[w.x][w.y]
-                if not (game.quadrant == w or q.planet == NOPLANET or \
-                     not game.state.planets[q.planet].inhabited or \
-                     q.supernova or q.status!=secure or q.klingons<=0):
+                if not (game.quadrant == w or q.planet == None or \
+                     not q.planet.inhabited or \
+                     q.supernova or q.status!="secure" or q.klingons<=0):
                     break
             else:
                # can't seem to find one; ignore this call 
@@ -2697,7 +2693,7 @@ def events():
            q.klingons += 1
            if game.quadrant == w:
                 game.klhere += 1
-               newkling(game.klhere)
+               game.enemies.append(newkling())
            # recompute time left
             game.recompute()
            # report the disaster if we can 
@@ -2707,7 +2703,7 @@ def events():
                    prout(_("launched a warship from %s.") % q.planet)
                else:
                    prout(_("Uhura- Starfleet reports increased Klingon activity"))
-                   if q.planet != NOPLANET:
+                   if q.planet != None:
                        proutn(_("near %s") % q.planet)
                    prout(_("in Quadrant %s.") % w)
                                
@@ -2730,9 +2726,7 @@ def wait():
        proutn(_("Are you sure? "))
        if ja() == False:
            return
-
     # Alternate resting periods (events) with attacks 
-
     game.resting = True
     while True:
        if delay <= 0:
@@ -2742,7 +2736,7 @@ def wait():
            return
        temp = game.optime = delay
        if game.nenhere:
-           rtime = 1.0 + random.random()
+           rtime = randreal(1.0, 2.0)
            if rtime < temp:
                temp = rtime
            game.optime = temp
@@ -2774,146 +2768,129 @@ 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(); scratch = coord()
-
-    if random.random() < 0.05:
+    newc = coord(); neighbor = coord(); bump = coord(0, 0)
+    if withprob(0.05):
        # Wow! We've supernova'ed 
        supernova(False, nov)
        return
-
     # handle initial nova 
     game.quad[nov.x][nov.y] = IHDOT
-    crmena(False, IHSTAR, sector, nov)
+    crmena(False, IHSTAR, "sector", nov)
     prout(_(" novas."))
     game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
     game.state.starkl += 1
-       
-    # Set up stack to recursively trigger adjacent stars 
-    bot = top = top2 = 1
+    # Set up queue to recursively trigger adjacent stars 
+    hits = [nov]
     kount = 0
-    icx = icy = 0
-    hits[1][1] = nov.x
-    hits[1][2] = nov.y
-    while True:
-       for mm in range(bot, top+1): 
-           for nn in range(1, 3+1):  # nn,j represents coordinates around current 
-               for j in range(1, 3+1):
-                   if j==2 and nn== 2:
-                       continue
-                   scratch.x = hits[mm][1]+nn-2
-                   scratch.y = hits[mm][2]+j-2
-                   if not VALID_SECTOR(scratch.y, scratch.x):
-                       continue
-                   iquad = game.quad[scratch.x][scratch.y]
-                    # Empty space ends reaction
-                    if iquad in (IHDOT, IHQUEST, IHBLANK, IHT, IHWEB):
-                       break
-                   elif iquad == IHSTAR: # Affect another star 
-                       if random.random() < 0.05:
-                           # This star supernovas 
-                           scratch = supernova(False)
-                           return
-                       top2 += 1
-                       hits[top2][1]=scratch.x
-                       hits[top2][2]=scratch.y
+    while hits:
+        offset = coord()
+        start = hits.pop()
+        for offset.x in range(-1, 1+1):
+            for offset.y in range(-1, 1+1):
+                if offset.y==0 and offset.x==0:
+                    continue
+                neighbor = start + offset
+                if not VALID_SECTOR(neighbor.y, neighbor.x):
+                    continue
+                iquad = game.quad[neighbor.x][neighbor.y]
+                # Empty space ends reaction
+                if iquad in (IHDOT, IHQUEST, IHBLANK, IHT, IHWEB):
+                    pass
+                elif iquad == IHSTAR: # Affect another star 
+                    if withprob(0.05):
+                        # This star supernovas 
+                        supernova(False)
+                        return
+                    else:
+                        hits.append(neighbor)
                        game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
                        game.state.starkl += 1
-                       crmena(True, IHSTAR, sector, scratch)
+                       crmena(True, IHSTAR, "sector", neighbor)
                        prout(_(" novas."))
-                       game.quad[scratch.x][scratch.y] = IHDOT
-                   elif iquad == IHP: # Destroy planet 
-                       game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
-                       game.state.nplankl += 1
-                       crmena(True, IHP, sector, scratch)
-                       prout(_(" destroyed."))
-                       game.state.planets[game.iplnet].pclass = destroyed
-                       game.iplnet = 0
-                       invalidate(game.plnet)
-                       if game.landed:
-                           finish(FPNOVA)
-                           return
-                       game.quad[scratch.x][scratch.y] = IHDOT
-                   elif iquad == IHB: # Destroy base 
-                       game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = False
-                       for i in range(1, game.state.rembase+1):
-                           if game.state.baseq[i] == game.quadrant: 
-                               break
-                       game.state.baseq[i] = game.state.baseq[game.state.rembase]
-                       game.state.rembase -= 1
-                       invalidate(game.base)
-                       game.state.basekl += 1
-                       newcnd()
-                       crmena(True, IHB, sector, scratch)
-                       prout(_(" destroyed."))
-                       game.quad[scratch.x][scratch.y] = IHDOT
-                   elif iquad in (IHE, IHF): # Buffet ship 
-                       prout(_("***Starship buffeted by nova."))
-                       if game.shldup:
-                           if game.shield >= 2000.0:
-                               game.shield -= 2000.0
-                           else:
-                               diff = 2000.0 - game.shield
-                               game.energy -= diff
-                               game.shield = 0.0
-                               game.shldup = False
-                               prout(_("***Shields knocked out."))
-                               game.damage[DSHIELD] += 0.005*game.damfac*random.random()*diff
-                       else:
-                           game.energy -= 2000.0
-                       if game.energy <= 0:
-                           finish(FNOVA)
-                           return
-                       # add in course nova contributes to kicking starship
-                       icx += game.sector.x-hits[mm][1]
-                       icy += game.sector.y-hits[mm][2]
-                       kount += 1
-                   elif iquad == IHK: # kill klingon 
-                       deadkl(scratch,iquad, scratch)
-                    elif iquad in (IHC,IHS,IHR): # Damage/destroy big enemies 
-                       for ll in range(1, game.nenhere+1):
-                           if game.ks[ll] == scratch:
-                               break
-                       game.kpower[ll] -= 800.0 # If firepower is lost, die 
-                       if game.kpower[ll] <= 0.0:
-                           deadkl(scratch, iquad, scratch)
-                           break
-                       newc.x = scratch.x + scratch.x - hits[mm][1]
-                       newc.y = scratch.y + scratch.y - hits[mm][2]
-                       crmena(True, iquad, sector, scratch)
-                       proutn(_(" damaged"))
-                       if not VALID_SECTOR(newc.x, newc.y):
-                           # can't leave quadrant 
-                           skip(1)
-                           break
-                       iquad1 = game.quad[newc.x][newc.y]
-                       if iquad1 == IHBLANK:
-                           proutn(_(", blasted into "))
-                           crmena(False, IHBLANK, sector, newc)
-                           skip(1)
-                           deadkl(scratch, iquad, newc)
-                           break
-                       if iquad1 != IHDOT:
-                           # can't move into something else 
-                           skip(1)
-                           break
-                       proutn(_(", buffeted to Sector %s") % newc)
-                       game.quad[scratch.x][scratch.y] = IHDOT
-                       game.quad[newc.x][newc.y] = iquad
-                       game.ks[ll] = newc
-                       game.kdist[ll] = game.kavgd[ll] = distance(game.sector, newc)
-                       skip(1)
-       if top == top2: 
-           break
-       bot = top + 1
-       top = top2
-    if kount==0: 
-       return
-
+                        game.quad[neighbor.x][neighbor.y] = IHDOT
+                        kount += 1
+                elif iquad in (IHP, IHW): # Destroy planet 
+                    game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
+                    if iquad == IHP:
+                        game.state.nplankl += 1
+                    else:
+                        game.state.worldkl += 1
+                    crmena(True, iquad, "sector", neighbor)
+                    prout(_(" destroyed."))
+                    game.iplnet.pclass = "destroyed"
+                    game.iplnet = None
+                    invalidate(game.plnet)
+                    if game.landed:
+                        finish(FPNOVA)
+                        return
+                    game.quad[neighbor.x][neighbor.y] = IHDOT
+                elif iquad == IHB: # Destroy base 
+                    game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = False
+                    for i in range(game.state.rembase):
+                        if game.state.baseq[i] == game.quadrant: 
+                            break
+                    game.state.baseq[i] = game.state.baseq[game.state.rembase]
+                    game.state.rembase -= 1
+                    invalidate(game.base)
+                    game.state.basekl += 1
+                    newcnd()
+                    crmena(True, IHB, "sector", neighbor)
+                    prout(_(" destroyed."))
+                    game.quad[neighbor.x][neighbor.y] = IHDOT
+                elif iquad in (IHE, IHF): # Buffet ship 
+                    prout(_("***Starship buffeted by nova."))
+                    if game.shldup:
+                        if game.shield >= 2000.0:
+                            game.shield -= 2000.0
+                        else:
+                            diff = 2000.0 - game.shield
+                            game.energy -= diff
+                            game.shield = 0.0
+                            game.shldup = False
+                            prout(_("***Shields knocked out."))
+                            game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
+                    else:
+                        game.energy -= 2000.0
+                    if game.energy <= 0:
+                        finish(FNOVA)
+                        return
+                    # add in course nova contributes to kicking starship
+                    bump += (game.sector-hits[mm]).sgn()
+                elif iquad == IHK: # kill klingon 
+                    deadkl(neighbor, iquad, neighbor)
+                elif iquad in (IHC,IHS,IHR): # Damage/destroy big enemies 
+                    for ll in range(game.nenhere):
+                        if game.enemies[ll].kloc == neighbor:
+                            break
+                    game.enemies[ll].kpower -= 800.0 # If firepower is lost, die 
+                    if game.enemies[ll].kpower <= 0.0:
+                        deadkl(neighbor, iquad, neighbor)
+                        break
+                    newc = neighbor + neighbor - hits[mm]
+                    crmena(True, iquad, "sector", neighbor)
+                    proutn(_(" damaged"))
+                    if not VALID_SECTOR(newc.x, newc.y):
+                        # can't leave quadrant 
+                        skip(1)
+                        break
+                    iquad1 = game.quad[newc.x][newc.y]
+                    if iquad1 == IHBLANK:
+                        proutn(_(", blasted into "))
+                        crmena(False, IHBLANK, "sector", newc)
+                        skip(1)
+                        deadkl(neighbor, iquad, newc)
+                        break
+                    if iquad1 != IHDOT:
+                        # can't move into something else 
+                        skip(1)
+                        break
+                    proutn(_(", buffeted to Sector %s") % newc)
+                    game.quad[neighbor.x][neighbor.y] = IHDOT
+                    game.quad[newc.x][newc.y] = iquad
+                    game.enemies[ll].move(newc)
     # Starship affected by nova -- kick it away. 
     game.dist = kount*0.1
-    icx = sgn(icx)
-    icy = sgn(icy)
-    game.direc = course[3*(icx+1)+icy+2]
+    game.direc = course[3*(bump.x+1)+bump.y+2]
     if game.direc == 0.0:
        game.dist = 0.0
     if game.dist == 0.0:
@@ -2921,7 +2898,7 @@ def nova(nov):
     game.optime = 10.0*game.dist/16.0
     skip(1)
     prout(_("Force of nova displaces starship."))
-    imove(True)
+    imove(novapush=True)
     game.optime = 10.0*game.dist/16.0
     return
        
@@ -2929,7 +2906,6 @@ def supernova(induced, w=None):
     # star goes supernova 
     num = 0; npdead = 0
     nq = coord()
-
     if w != None: 
        nq = w
     else:
@@ -2937,14 +2913,14 @@ def supernova(induced, w=None):
        # Scheduled supernova -- select star 
        # logic changed here so that we won't favor quadrants in top
         # left of universe 
-       for nq.x in range(1, GALSIZE+1):
-           for nq.y in range(1, GALSIZE+1):
+       for nq.x in range(GALSIZE):
+           for nq.y in range(GALSIZE):
                stars += game.state.galaxy[nq.x][nq.y].stars
        if stars == 0:
            return # nothing to supernova exists 
-       num = random.random()*stars + 1
-       for nq.x in range(1, GALSIZE+1):
-           for nq.y in range(1, GALSIZE+1):
+       num = randrange(stars) + 1
+       for nq.x in range(GALSIZE):
+           for nq.y in range(GALSIZE):
                num -= game.state.galaxy[nq.x][nq.y].stars
                if num <= 0:
                    break
@@ -2954,7 +2930,6 @@ def supernova(induced, w=None):
            proutn("=== Super nova here?")
            if ja() == True:
                nq = game.quadrant
-
     if not nq == game.quadrant or game.justin:
        # it isn't here, or we just entered (treat as enroute) 
        if communicating():
@@ -2964,16 +2939,15 @@ def supernova(induced, w=None):
     else:
        ns = coord()
        # we are in the quadrant! 
-       num = random.random()* game.state.galaxy[nq.x][nq.y].stars + 1
-       for ns.x in range(1, QUADSIZE+1):
-           for ns.y in range(1, QUADSIZE+1):
+       num = randrange(game.state.galaxy[nq.x][nq.y].stars) + 1
+       for ns.x in range(QUADSIZE):
+           for ns.y in range(QUADSIZE):
                if game.quad[ns.x][ns.y]==IHSTAR:
                    num -= 1
                    if num==0:
                        break
            if num==0:
                break
-
        skip(1)
        prouts(_("***RED ALERT!  RED ALERT!"))
        skip(1)
@@ -2996,7 +2970,7 @@ def supernova(induced, w=None):
        unschedule(FSCDBAS)
     if game.state.remcom:
        maxloop = game.state.remcom
-       for l in range(1, maxloop+1):
+       for l in range(maxloop):
            if game.state.kcmdr[l] == nq:
                game.state.kcmdr[l] = game.state.kcmdr[game.state.remcom]
                invalidate(game.state.kcmdr[game.state.remcom])
@@ -3013,12 +2987,12 @@ def supernova(induced, w=None):
     # Destroy planets 
     for loop in range(game.inplan):
        if game.state.planets[loop].w == nq:
-           game.state.planets[loop].pclass = destroyed
+           game.state.planets[loop].pclass = "destroyed"
            npdead += 1
     # Destroy any base in supernovaed quadrant 
     if game.state.rembase:
        maxloop = game.state.rembase
-       for loop in range(1, maxloop+1):
+       for loop in range(maxloop):
            if game.state.baseq[loop] == nq:
                game.state.baseq[loop] = game.state.baseq[game.state.rembase]
                invalidate(game.state.baseq[game.state.rembase])
@@ -3082,7 +3056,7 @@ def selfdestruct():
     prouts("                         3"); skip(1)
     prouts("                            2"); skip(1)
     prouts("                              1"); skip(1)
-    if random.random() < 0.15:
+    if withprob(0.15):
        prouts(_("GOODBYE-CRUEL-WORLD"))
        skip(1)
     kaboom()
@@ -3101,14 +3075,20 @@ def kaboom():
        whammo = 25.0 * game.energy
        l=1
        while l <= game.nenhere:
-           if game.kpower[l]*game.kdist[l] <= whammo: 
-               deadkl(game.ks[l], game.quad[game.ks[l].x][game.ks[l].y], game.ks[l])
+           if game.enemies[l].kpower*game.enemies[l].kdist <= whammo: 
+               deadkl(game.enemies[l].kloc, game.quad[game.enemies[l].kloc.x][game.enemies[l].kloc.y], game.enemies[l].kloc)
            l += 1
     finish(FDILITHIUM)
                                
 def killrate():
     "Compute our rate of kils over time."
-    return ((game.inkling + game.incom + game.inscom) - (game.state.remkl + game.state.remcom + game.state.nscrem))/(game.state.date-game.indate)
+    elapsed = game.state.date - game.indate
+    if elapsed == 0:   # Avoid divide-by-zero error if calculated on turn 0
+        return 0
+    else:
+        starting = (game.inkling + game.incom + game.inscom)
+        remaining = (game.state.remkl + game.state.remcom + game.state.nscrem)
+        return (starting - remaining)/elapsed
 
 def badpoints():
     "Compute demerits."
@@ -3125,7 +3105,6 @@ def badpoints():
         badpt += 200.0
     return badpt
 
-
 def finish(ifin):
     # end the game, with appropriate notfications 
     igotit = False
@@ -3219,7 +3198,7 @@ def finish(ifin):
     elif ifin == FBATTLE:
        proutn(_("The "))
        crmshp()
-       prout(_("has been destroyed in battle."))
+       prout(_(" has been destroyed in battle."))
        skip(1)
        prout(_("Dulce et decorum est pro patria mori."))
     elif ifin == FNEG3:
@@ -3314,10 +3293,10 @@ def finish(ifin):
     if (game.state.remkl + game.state.remcom + game.state.nscrem) != 0:
        goodies = game.state.remres/game.inresor
        baddies = (game.state.remkl + 2.0*game.state.remcom)/(game.inkling+2.0*game.incom)
-       if goodies/baddies >= 1.0+0.5*random.random():
+       if goodies/baddies >= randreal(1.0, 1.5):
            prout(_("As a result of your actions, a treaty with the Klingon"))
            prout(_("Empire has been signed. The terms of the treaty are"))
-           if goodies/baddies >= 3.0+random.random():
+           if goodies/baddies >= randreal(3.0):
                prout(_("favorable to the Federation."))
                skip(1)
                prout(_("Congratulations!"))
@@ -3336,7 +3315,6 @@ 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 + game.state.remcom + game.state.nscrem) != 0) and timused < 5.0:
        timused = 5.0
@@ -3435,7 +3413,6 @@ def plaque():
     winner = cgetline()
     # The 38 below must be 64 for 132-column paper 
     nskip = 38 - len(winner)/2
-
     fp.write("\n\n\n\n")
     # --------DRAW ENTERPRISE PICTURE. 
     fp.write("                                       EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
@@ -3512,7 +3489,7 @@ def iostart():
     #textdomain(PACKAGE)
     if atexit.register(outro):
        sys.stderr.write("Unable to register outro(), exiting...\n")
-       os.exit(1)
+       raise SysExit,1
     if not (game.options & OPTION_CURSES):
        ln_env = os.getenv("LINES")
         if ln_env:
@@ -3548,7 +3525,6 @@ def iostart():
        setwnd(fullscreen_window)
        textcolor(DEFAULT)
 
-
 def waitfor():
     "wait for user action -- OK to do nothing if on a TTY"
     if game.options & OPTION_CURSES:
@@ -3582,7 +3558,7 @@ def pause_game():
         sys.stdout.write('\n')
         proutn(prompt)
         raw_input()
-        for j in range(0, rows):
+        for j in range(rows):
             sys.stdout.write('\n')
         linecount = 0
 
@@ -3621,13 +3597,15 @@ def prout(line):
 def prouts(line):
     "print slowly!" 
     for c in line:
-       curses.delay_output(30)
+        if not replayfp or replayfp.closed:    # Don't slow down replays
+            time.sleep(0.03)
        proutn(c)
        if game.options & OPTION_CURSES:
            wrefresh(curwnd)
        else:
            sys.stdout.flush()
-    curses.delay_output(300)
+    if not replayfp or replayfp.closed:
+        time.sleep(0.03)
 
 def cgetline():
     "Get a line of input."
@@ -3636,9 +3614,17 @@ def cgetline():
        curwnd.refresh()
     else:
        if replayfp and not replayfp.closed:
-           line = replayfp.readline()
+            while True:
+                line = replayfp.readline()
+                proutn(line)
+                if line == '':
+                    prout("*** Replay finished")
+                    replayfp.close()
+                    break
+                elif line[0] != "#":
+                    break
        else:
-           line = raw_input()
+           line = raw_input() + "\n"
     if logfp:
        logfp.write(line)
     return line
@@ -3708,9 +3694,6 @@ def highvideo():
     if game.options & OPTION_CURSES:
        curwnd.attron(curses.A_REVERSE)
  
-def commandhook(cmd, before):
-    pass
-
 #
 # Things past this point have policy implications.
 # 
@@ -3730,11 +3713,11 @@ def drawmaps(mode):
            setwnd(report_window)
            report_window.clear()
            report_window.move(0, 0)
-           status(0)
+           status()
            setwnd(lrscan_window)
            lrscan_window.clear()
            lrscan_window.move(0, 0)
-           lrscan()
+           lrscan(silent=False)
 
 def put_srscan_sym(w, sym):
     "Emit symbol for short-range scan."
@@ -3750,7 +3733,7 @@ def boom(w):
        srscan_window.attron(curses.A_REVERSE)
        put_srscan_sym(w, game.quad[w.x][w.y])
        #sound(500)
-       #delay(1000)
+       #time.sleep(1.0)
        #nosound()
        srscan_window.attroff(curses.A_REVERSE)
        put_srscan_sym(w, game.quad[w.x][w.y])
@@ -3786,18 +3769,18 @@ def tracktorpedo(w, l, i, n, iquad):
        if not damaged(DSRSENS) or game.condition=="docked":
            if i != 1 and l == 1:
                drawmaps(2)
-               curses.delay_output(400)
+               time.sleep(0.4)
            if (iquad==IHDOT) or (iquad==IHBLANK):
                put_srscan_sym(w, '+')
                #sound(l*10)
-               #curses.delay_output(100)
+               #time.sleep(0.1)
                #nosound()
                put_srscan_sym(w, iquad)
            else:
                curwnd.attron(curses.A_REVERSE)
                put_srscan_sym(w, iquad)
                #sound(500)
-               #curses.delay_output(1000)
+               #time.sleep(1.0)
                #nosound()
                curwnd.attroff(curses.A_REVERSE)
                put_srscan_sym(w, iquad)
@@ -3821,8 +3804,8 @@ def prstat(txt, data):
        skip(1)
        setwnd(status_window)
     else:
-        proutn(" " * NSYM - len(tx))
-    vproutn(data)
+        proutn(" " * (NSYM - len(txt)))
+    proutn(data)
     skip(1)
     if game.options & OPTION_CURSES:
        setwnd(report_window)
@@ -3835,27 +3818,25 @@ def imove(novapush):
     trbeam = False
 
     def no_quad_change():
-        # No quadrant change -- compute new avg enemy distances 
+        # No quadrant change -- compute new average enemy distances 
         game.quad[game.sector.x][game.sector.y] = game.ship
         if game.nenhere:
-            for m in range(1, game.nenhere+1):
-                finald = distance(w, game.ks[m])
-                game.kavgd[m] = 0.5 * (finald+game.kdist[m])
-                game.kdist[m] = finald
-            sortklings()
+            for m in range(game.nenhere):
+                finald = distance(w, game.enemies[m].kloc)
+                game.enemies[m].kavgd = 0.5 * (finald+game.enemies[m].kdist)
+                game.enemies[m].kdist = finald
+            game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
             if not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
                 attack(False)
-            for m in range(1, game.nenhere+1):
-                game.kavgd[m] = game.kdist[m]
+            for m in range(game.nenhere):
+                game.enemies[m].kavgd = game.enemies[m].kdist
         newcnd()
         drawmaps(0)
         setwnd(message_window)
-
     w.x = w.y = 0
     if game.inorbit:
        prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
        game.inorbit = False
-
     angle = ((15.0 - game.direc) * 0.5235988)
     deltax = -math.sin(angle)
     deltay = math.cos(angle)
@@ -3863,10 +3844,8 @@ def imove(novapush):
        bigger = math.fabs(deltax)
     else:
        bigger = math.fabs(deltay)
-               
     deltay /= bigger
     deltax /= bigger
-
     # If tractor beam is to occur, don't move full distance 
     if game.state.date+game.optime >= scheduled(FTBEAM):
        trbeam = True
@@ -3877,22 +3856,21 @@ def imove(novapush):
     game.quad[game.sector.x][game.sector.y] = IHDOT
     x = game.sector.x
     y = game.sector.y
-    n = 10.0*game.dist*bigger+0.5
-
+    n = int(10.0*game.dist*bigger+0.5)
     if n > 0:
        for m in range(1, n+1):
             x += deltax
             y += deltay
-           w.x = x + 0.5
-           w.y = y + 0.5
+           w.x = int(round(x))
+           w.y = int(round(y))
            if not VALID_SECTOR(w.x, w.y):
                # Leaving quadrant -- allow final enemy attack 
                # Don't do it if being pushed by Nova 
                if game.nenhere != 0 and not novapush:
                    newcnd()
-                   for m in range(1, game.nenhere+1):
-                       finald = distance(w, game.ks[m])
-                       game.kavgd[m] = 0.5 * (finald + game.kdist[m])
+                   for m in range(game.nenhere):
+                       finald = distance(w, game.enemies[m].kloc)
+                       game.enemies[m].kavgd = 0.5 * (finald + game.enemies[m].kdist)
                    #
                    # Stas Sergeev added the condition
                    # that attacks only happen if Klingons
@@ -3903,31 +3881,30 @@ def imove(novapush):
                    if game.alldone:
                        return
                # compute final position -- new quadrant and sector 
-               x = QUADSIZE*(game.quadrant.x-1)+game.sector.x
-               y = QUADSIZE*(game.quadrant.y-1)+game.sector.y
-               w.x = x+10.0*game.dist*bigger*deltax+0.5
-               w.y = y+10.0*game.dist*bigger*deltay+0.5
+               x = (QUADSIZE*game.quadrant.x)+game.sector.x
+               y = (QUADSIZE*game.quadrant.y)+game.sector.y
+               w.x = int(round(x+10.0*game.dist*bigger*deltax))
+               w.y = int(round(y+10.0*game.dist*bigger*deltay))
                # check for edge of galaxy 
                kinks = 0
                 while True:
-                   kink = 0
-                   if w.x <= 0:
-                       w.x = -w.x + 1
-                       kink = 1
-                   if w.y <= 0:
-                       w.y = -w.y + 1
-                       kink = 1
-                   if w.x > GALSIZE*QUADSIZE:
-                       w.x = (GALSIZE*QUADSIZE*2)+1 - w.x
-                       kink = 1
-                   if w.y > GALSIZE*QUADSIZE:
-                       w.y = (GALSIZE*QUADSIZE*2)+1 - w.y
-                       kink = 1
+                   kink = False
+                   if w.x < 0:
+                       w.x = -w.x
+                       kink = True
+                   if w.y < 0:
+                       w.y = -w.y
+                       kink = True
+                   if w.x >= GALSIZE*QUADSIZE:
+                       w.x = (GALSIZE*QUADSIZE*2) - w.x
+                       kink = True
+                   if w.y >= GALSIZE*QUADSIZE:
+                       w.y = (GALSIZE*QUADSIZE*2) - w.y
+                       kink = True
                    if kink:
-                       kinks = 1
-               if not kink:
-                    break
-
+                       kinks += 1
+                    else:
+                        break
                if kinks:
                    game.nkinks += 1
                    if game.nkinks == 3:
@@ -3941,10 +3918,10 @@ def imove(novapush):
                # Compute final position in new quadrant 
                if trbeam: # Don't bother if we are to be beamed 
                    return
-               game.quadrant.x = (w.x+(QUADSIZE-1))/QUADSIZE
-               game.quadrant.y = (w.y+(QUADSIZE-1))/QUADSIZE
-               game.sector.x = w.x - QUADSIZE*(game.quadrant.x-1)
-               game.sector.y = w.y - QUADSIZE*(game.quadrant.y-1)
+               game.quadrant.x = w.x/QUADSIZE
+               game.quadrant.y = w.y/QUADSIZE
+               game.sector.x = w.x - (QUADSIZE*game.quadrant.x)
+               game.sector.y = w.y - (QUADSIZE*game.quadrant.y)
                skip(1)
                prout(_("Entering Quadrant %s.") % game.quadrant)
                game.quad[game.sector.x][game.sector.y] = game.ship
@@ -3957,9 +3934,12 @@ def imove(novapush):
                # object encountered in flight path 
                stopegy = 50.0*game.dist/game.optime
                game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
-                if iquad in (IHT. IHK, OHC, IHS, IHR, IHQUEST):
+                if iquad in (IHT, IHK, IHC, IHS, IHR, IHQUEST):
                    game.sector = w
-                   ram(False, iquad, game.sector)
+                    for enemy in game.enemies:
+                        if enemy.kloc == game.sector:
+                            break
+                   collision(rammed=False, enemy=enemy)
                    final = game.sector
                elif iquad == IHBLANK:
                    skip(1)
@@ -3974,11 +3954,11 @@ def imove(novapush):
                    # possibility that you'll get timewarped instead.
                    # 
                    n=0
-                   for m in range(0, NDEVICES):
+                   for m in range(NDEVICES):
                        if game.damage[m]>0: 
                            n += 1
                    probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
-                   if (game.options & OPTION_BLKHOLE) and random.random()>probf
+                   if (game.options & OPTION_BLKHOLE) and withprob(1-probf)
                        timwrp()
                    else: 
                        finish(FHOLE)
@@ -4038,8 +4018,7 @@ def dock(verbose):
        prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
        attackreport(False)
        game.iseenit = True
-
-# 
 # This program originally required input in terms of a (clock)
 # direction and distance. Somewhere in history, it was changed to
 # cartesian coordinates. So we need to convert.  Probably
@@ -4047,25 +4026,20 @@ def dock(verbose):
 # pain if the computer isn't working! Manual mode is still confusing
 # because it involves giving x and y motions, yet the coordinates
 # are always displayed y - x, where +y is downward!
-# 
 
-def getcd(isprobe, akey):
-    # get course and distance 
-    irowq=game.quadrant.x; icolq=game.quadrant.y; key=0
+def getcourse(isprobe, akey):
+    # get course and distance
+    key = 0
+    dquad = copy.copy(game.quadrant)
     navmode = "unspecified"
     itemp = "curt"
-    incr = coord()
+    dsect = coord()
     iprompt = False
-
-    # Get course direction and distance. If user types bad values, return
-    # with DIREC = -1.0.
-    game.direc = -1.0
-       
     if game.landed and not isprobe:
        prout(_("Dummy! You can't leave standard orbit until you"))
        proutn(_("are back aboard the ship."))
        chew()
-       return
+       return False
     while navmode == "unspecified":
        if damaged(DNAVSYS):
            if isprobe:
@@ -4082,7 +4056,6 @@ def getcd(isprobe, akey):
            akey = -1
        else: 
            key = scan()
-
        if key == IHEOL:
            proutn(_("Manual or automatic- "))
            iprompt = True
@@ -4099,7 +4072,7 @@ def getcd(isprobe, akey):
            else:
                huh()
                chew()
-               return
+               return False
        else: # numeric 
            if isprobe:
                prout(_("(Manual navigation assumed.)"))
@@ -4107,7 +4080,6 @@ def getcd(isprobe, akey):
                prout(_("(Manual movement assumed.)"))
            navmode = "manual"
            break
-
     if navmode == "automatic":
        while key == IHEOL:
            if isprobe:
@@ -4117,52 +4089,53 @@ def getcd(isprobe, akey):
            chew()
            iprompt = True
            key = scan()
-
        if key != IHREAL:
            huh()
-           return
-       xi = aaitem
+           return False
+       xi = int(round(aaitem))-1
        key = scan()
        if key != IHREAL:
            huh()
-           return
-       xj = aaitem
+           return False
+       xj = int(round(aaitem))-1
        key = scan()
        if key == IHREAL:
            # both quadrant and sector specified 
-           xk = aaitem
+           xk = int(round(aaitem))-1
            key = scan()
            if key != IHREAL:
                huh()
-               return
-           xl = aaitem
-
-           irowq = xi + 0.5
-           icolq = xj + 0.5
-           incr.y = xk + 0.5
-           incr.x = xl + 0.5
+               return False
+           xl = int(round(aaitem))-1
+           dquad.x = xi
+           dquad.y = xj
+           dsect.y = xk
+           dsect.x = xl
        else:
+            # only one pair of numbers was specified
            if isprobe:
                # only quadrant specified -- go to center of dest quad 
-               irowq = xi + 0.5
-               icolq = xj + 0.5
-               incr.y = incr.x = 5
+               dquad.x = xi
+               dquad.y = xj
+               dsect.y = dsect.x = 4   # preserves 1-origin behavior
            else:
-               incr.y = xi + 0.5
-               incr.x = xj + 0.5
+                # only sector specified
+               dsect.y = xi
+               dsect.x = xj
            itemp = "normal"
-       if not VALID_QUADRANT(icolq,irowq) or not VALID_SECTOR(incr.x,incr.y):
+       if not VALID_QUADRANT(dquad.y,dquad.x) or not VALID_SECTOR(dsect.x,dsect.y):
            huh()
-           return
+           return False
        skip(1)
        if not isprobe:
            if itemp > "curt":
                if iprompt:
-                   prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % incr)
+                   prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
            else:
                prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
-       deltax = icolq - game.quadrant.y + 0.1*(incr.x-game.sector.y)
-       deltay = game.quadrant.x - irowq + 0.1*(game.sector.x-incr.y)
+        # the actual deltas get computed here
+       deltax = dquad.y-game.quadrant.y + 0.1*(dsect.x-game.sector.y)
+       deltay = game.quadrant.x-dquad.x + 0.1*(game.sector.x-dsect.y)
     else: # manual 
        while key == IHEOL:
            proutn(_("X and Y displacements- "))
@@ -4172,26 +4145,27 @@ def getcd(isprobe, akey):
        itemp = "verbose"
        if key != IHREAL:
            huh()
-           return
+           return False
        deltax = aaitem
        key = scan()
        if key != IHREAL:
            huh()
-           return
+           return False
        deltay = aaitem
     # Check for zero movement 
     if deltax == 0 and deltay == 0:
        chew()
-       return
+       return False
     if itemp == "verbose" and not isprobe:
        skip(1)
        prout(_("Helmsman Sulu- \"Aye, Sir.\""))
+    # Course actually laid in.
     game.dist = math.sqrt(deltax*deltax + deltay*deltay)
     game.direc = math.atan2(deltax, deltay)*1.90985932
     if game.direc < 0.0:
        game.direc += 12.0
     chew()
-    return
+    return True
 
 def impulse():
     # move under impulse power 
@@ -4202,13 +4176,11 @@ def impulse():
        prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
        return
     if game.energy > 30.0:
-       getcd(False, 0)
-       if game.direc == -1.0:
+        if not getcourse(isprobe=False, akey=0):
            return
        power = 20.0 + 100.0*game.dist
     else:
        power = 30.0
-
     if power >= game.energy:
        # Insufficient power for trip 
        skip(1)
@@ -4231,7 +4203,7 @@ def impulse():
        if ja() == False:
            return
     # Activate impulse engines and pay the cost 
-    imove(False)
+    imove(novapush=False)
     game.ididit = True
     if game.alldone:
        return
@@ -4258,12 +4230,9 @@ def warp(timewarp):
            prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
            prout(_("  is repaired, I can only give you warp 4.\""))
            return
-                       
-       # Read in course and distance 
-       getcd(False, 0)
-       if game.direc == -1.0:
+               # Read in course and distance 
+        if not getcourse(isprobe=False, akey=0):
            return
-
        # Make sure starship has enough energy for the trip 
        power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
        if power >= game.energy:
@@ -4301,13 +4270,14 @@ def warp(timewarp):
                return
     # Entry WARPX 
     if game.warpfac > 6.0:
-       # Decide if engine damage will occur 
-       prob = game.dist*(6.0-game.warpfac)*(6.0-game.warpfac)/66.666666666
-       if prob > random.random():
+       # Decide if engine damage will occur
+        # ESR: Seems wrong. Probability of damage goes *down* with distance? 
+       prob = game.dist*square(6.0-game.warpfac)/66.666666666
+       if prob > randreal():
            blooey = True
-           game.dist = random.random()*game.dist
+           game.dist = randreal(game.dist)
        # Decide if time warp will occur 
-       if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > random.random():
+       if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > randreal():
            twarp = True
        if idebug and game.warpfac==10 and not twarp:
            blooey = False
@@ -4340,10 +4310,8 @@ def warp(timewarp):
                if game.quad[ix][iy] != IHDOT:
                    blooey = False
                    twarp = False
-                               
-
     # Activate Warp Engines and pay the cost 
-    imove(False)
+    imove(novapush=False)
     if game.alldone:
        return
     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
@@ -4353,7 +4321,7 @@ def warp(timewarp):
     if twarp:
        timwrp()
     if blooey:
-       game.damage[DWARPEN] = game.damfac*(3.0*random.random()+1.0)
+       game.damage[DWARPEN] = game.damfac * randreal(1.0, 4.0)
        skip(1)
        prout(_("Engineering to bridge--"))
        prout(_("  Scott here.  The warp engines are damaged."))
@@ -4404,7 +4372,6 @@ def setwarp():
 
 def atover(igrab):
     # cope with being tossed out of quadrant by supernova or yanked by beam 
-
     chew()
     # is captain on planet? 
     if game.landed:
@@ -4416,7 +4383,7 @@ def atover(igrab):
            prout(_("But with the shields up it's hopeless."))
            finish(FPNOVA)
        prouts(_("His desperate attempt to rescue you . . ."))
-       if random.random() <= 0.5:
+       if withprob(0.5):
            prout(_("fails."))
            finish(FPNOVA)
            return
@@ -4424,20 +4391,18 @@ def atover(igrab):
        if game.imine:
            game.imine = False
            proutn(_("The crystals mined were "))
-           if random.random() <= 0.25:
+           if withprob(0.25):
                prout(_("lost."))
            else:
                prout(_("saved."))
                game.icrystl = True
     if igrab:
        return
-
     # Check to see if captain in shuttle craft 
     if game.icraft:
        finish(FSTRACTOR)
     if game.alldone:
        return
-
     # Inform captain of attempt to reach safety 
     skip(1)
     while True:
@@ -4461,16 +4426,16 @@ def atover(igrab):
            prout(_("Warp engines damaged."))
            finish(FSNOVAED)
            return
-       game.warpfac = 6.0+2.0*random.random()
+       game.warpfac = randreal(6.0, 8.0)
        game.wfacsq = game.warpfac * game.warpfac
        prout(_("Warp factor set to %d") % int(game.warpfac))
        power = 0.75*game.energy
        game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
-       distreq = 1.4142+random.random()
+       distreq = randreal(math.sqrt(2))
        if distreq < game.dist:
            game.dist = distreq
        game.optime = 10.0*game.dist/game.wfacsq
-       game.direc = 12.0*random.random()       # How dumb! 
+       game.direc = randreal(12)       # How dumb! 
        game.justin = False
        game.inorbit = False
        warp(True)
@@ -4491,7 +4456,7 @@ def atover(igrab):
 def timwrp():
     # let's do the time warp again 
     prout(_("***TIME WARP ENTERED."))
-    if game.state.snap and random.random() < 0.5:
+    if game.state.snap and withprob(0.5):
        # Go back in time 
        prout(_("You are traveling backwards in time %d stardates.") %
              int(game.state.date-game.snapsht.date))
@@ -4532,7 +4497,7 @@ def timwrp():
        prout(_("Spock has reconstructed a correct star chart from memory"))
     else:
        # Go forward in time 
-       game.optime = -0.5*game.intime*math.log(random.random())
+       game.optime = -0.5*game.intime*math.log(randreal())
        prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
        # cheat to make sure no tractor beams occur during time warp 
        postpone(FTBEAM, game.optime)
@@ -4566,7 +4531,6 @@ def probe():
            prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
        return
     key = scan()
-
     if key == IHEOL:
        # slow mode, so let Kirk know how many probes there are left
         if game.nprobes == 1:
@@ -4576,7 +4540,6 @@ def probe():
        proutn(_("Are you sure you want to fire a probe? "))
        if ja() == False:
            return
-
     game.isarmed = False
     if key == IHALPHA and citem == "armed":
        game.isarmed = True
@@ -4584,8 +4547,7 @@ def probe():
     elif key == IHEOL:
        proutn(_("Arm NOVAMAX warhead? "))
        game.isarmed = ja()
-    getcd(True, key)
-    if game.direc == -1.0:
+    if not getcourse(isprobe=True, akey=key):
        return
     game.nprobes -= 1
     angle = ((15.0 - game.direc) * 0.5235988)
@@ -4595,7 +4557,6 @@ def probe():
        bigger = math.fabs(game.probeinx)
     else:
        bigger = math.fabs(game.probeiny)
-               
     game.probeiny /= bigger
     game.probeinx /= bigger
     game.proben = 10.0*game.dist*bigger +0.5
@@ -4628,7 +4589,6 @@ def mayday():
     # yell for help from nearest starbase 
     # There's more than one way to move in this game! 
     line = 0
-
     chew()
     # Test for conditions which prevent calling for help 
     if game.condition == "docked":
@@ -4652,7 +4612,7 @@ def mayday():
        ddist = distance(game.base, game.sector)
     else:
        ddist = FOREVER
-       for m in range(1, game.state.rembase+1):
+       for m in range(game.state.rembase):
            xdist = QUADSIZE * distance(game.state.baseq[m], game.quadrant)
            if xdist < ddist:
                ddist = xdist
@@ -4667,12 +4627,10 @@ def mayday():
     prout(_(" dematerializes."))
     game.sector.x=0
     for m in range(1, 5+1):
-       ix = game.base.x+3.0*random.random()-1
-       iy = game.base.y+3.0*random.random()-1
+        w = game.base.scatter() 
        if VALID_SECTOR(ix,iy) and game.quad[ix][iy]==IHDOT:
            # found one -- finish up 
-           game.sector.x=ix
-           game.sector.y=iy
+            game.sector = w
            break
     if not is_valid(game.sector):
        prout(_("You have been lost in space..."))
@@ -4689,7 +4647,7 @@ def mayday():
        game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
        textcolor(RED)
        warble()
-       if random.random() > probf:
+       if randreal() > probf:
            break
        prout(_("fails."))
        curses.delay_output(500)
@@ -4767,7 +4725,7 @@ def abandon():
        if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
            prout(_("Remainder of ship's complement beam down"))
            prout(_("to nearest habitable planet."))
-       elif q.planet != NOPLANET and not damaged(DTRANSP):
+       elif q.planet != None and not damaged(DTRANSP):
            prout(_("Remainder of ship's complement beam down to %s.") %
                    q.planet)
        else:
@@ -4782,7 +4740,7 @@ def abandon():
        game.nprobes = 0 # No probes 
        prout(_("You are captured by Klingons and released to"))
        prout(_("the Federation in a prisoner-of-war exchange."))
-       nb = random.random()*game.state.rembase+1
+       nb = randrange(game.state.rembase)
        # Set up quadrant and position FQ adjacient to base 
        if not game.quadrant == game.state.baseq[nb]:
            game.quadrant = game.state.baseq[nb]
@@ -4791,9 +4749,8 @@ def abandon():
        while True:
            # position next to base by trial and error 
            game.quad[game.sector.x][game.sector.y] = IHDOT
-           for l in range(1, QUADSIZE+1):
-               game.sector.x = 3.0*random.random() - 1.0 + game.base.x
-               game.sector.y = 3.0*random.random() - 1.0 + game.base.y
+           for l in range(QUADSIZE):
+               game.sector = game.base.scatter()
                if VALID_SECTOR(game.sector.x, game.sector.y) and \
                        game.quad[game.sector.x][game.sector.y] == IHDOT:
                     break
@@ -4814,7 +4771,7 @@ def abandon():
     game.iscraft = "offship" # Galileo disappears 
     # Resupply ship 
     game.condition="docked"
-    for l in range(0, NDEVICES): 
+    for l in range(NDEVICES): 
        game.damage[l] = 0.0
     game.damage[DSHUTTL] = -1
     game.energy = game.inenrg = 3000.0
@@ -4844,7 +4801,7 @@ def survey():
     prout(_("Spock-  \"Planet report follows, Captain.\""))
     skip(1)
     for i in range(game.inplan):
-       if game.state.planets[i].pclass == destroyed:
+       if game.state.planets[i].pclass == "destroyed":
            continue
        if (game.state.planets[i].known != "unknown" \
             and not game.state.planets[i].inhabited) \
@@ -4879,12 +4836,12 @@ def orbit():
        prout(_(" not adjacent to planet."))
        skip(1)
        return
-    game.optime = 0.02+0.03*random.random()
+    game.optime = randreal(0.02, 0.05)
     prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
     newcnd()
     if consumeTime():
        return
-    game.height = (1400.0+7200.0*random.random())
+    game.height = randreal(1400, 8600)
     prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
     game.inorbit = True
     game.ididit = True
@@ -4895,23 +4852,26 @@ def sensor():
        if game.options & OPTION_TTY:
            prout(_("Short range sensors damaged."))
        return
-    if not is_valid(game.plnet):
+    if game.iplnet == None:
        if game.options & OPTION_TTY:
            prout(_("Spock- \"No planet in this quadrant, Captain.\""))
        return
-    if game.state.planets[game.iplnet].known == "unknown":
+    if game.iplnet.known == "unknown":
        prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
        skip(1)
        prout(_("         Planet at Sector %s is of class %s.") %
-             (sector,game.plnet, game.state.planets[game.iplnet]))
-       if game.state.planets[game.iplnet].known=="shuttle_down": 
+             (game.plnet, game.iplnet.pclass))
+       if game.iplnet.known=="shuttle_down": 
            prout(_("         Sensors show Galileo still on surface."))
        proutn(_("         Readings indicate"))
-       if game.state.planets[game.iplnet].crystals != present:
+       if game.iplnet.crystals != "present":
            proutn(_(" no"))
        prout(_(" dilithium crystals present.\""))
-       if game.state.planets[game.iplnet].known == "unknown":
-           game.state.planets[game.iplnet].known = "known"
+       if game.iplnet.known == "unknown":
+           game.iplnet.known = "known"
+    elif game.iplnet.inhabited:
+        prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
+        prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
 
 def beam():
     # use the transporter 
@@ -4920,7 +4880,7 @@ def beam():
     skip(1)
     if damaged(DTRANSP):
        prout(_("Transporter damaged."))
-       if not damaged(DSHUTTL) and (game.state.planets[game.iplnet].known=="shuttle_down" or game.iscraft == "onship"):
+       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:
@@ -4933,12 +4893,12 @@ def beam():
     if game.shldup:
        prout(_("Impossible to transport through shields."))
        return
-    if game.state.planets[game.iplnet].known=="unknown":
+    if game.iplnet.known=="unknown":
        prout(_("Spock-  \"Captain, we have no information on this planet"))
        prout(_("  and Starfleet Regulations clearly state that in this situation"))
        prout(_("  you may not go down.\""))
        return
-    if not game.landed and game.state.planets[game.iplnet].crystals==absent:
+    if not game.landed and game.iplnet.crystals=="absent":
        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?\" "))
@@ -4955,7 +4915,7 @@ def beam():
            prout(_("Engineering to bridge--"))
            prout(_("  Captain, we have enough energy only to transport you down to"))
            prout(_("  the planet, but there wouldn't be an energy for the trip back."))
-           if game.state.planets[game.iplnet].known == "shuttle_down":
+           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:
@@ -4963,7 +4923,7 @@ def beam():
                return
     if game.landed:
        # Coming from planet 
-       if game.state.planets[game.iplnet].known=="shuttle_down":
+       if game.iplnet.known=="shuttle_down":
            proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
            if ja() == True:
                chew()
@@ -4986,7 +4946,7 @@ def beam():
     skip(1)
     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
     skip(2)
-    if random.random() > 0.98:
+    if withprob(0.98):
        prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
        skip(2)
        prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
@@ -4997,7 +4957,7 @@ def beam():
     game.energy -= nrgneed
     skip(2)
     prout(_("Transport complete."))
-    if game.landed and game.state.planets[game.iplnet].known=="shuttle_down":
+    if game.landed and game.iplnet.known=="shuttle_down":
        prout(_("The shuttle craft Galileo is here!"))
     if not game.landed and game.imine:
        game.icrystl = True
@@ -5012,10 +4972,10 @@ def mine():
     if not game.landed:
        prout(_("Mining party not on planet."))
        return
-    if game.state.planets[game.iplnet].crystals == mined:
+    if game.iplnet.crystals == "mined":
        prout(_("This planet has already been strip-mined for dilithium."))
        return
-    elif game.state.planets[game.iplnet].crystals == absent:
+    elif game.iplnet.crystals == "absent":
        prout(_("No dilithium crystals on this planet."))
        return
     if game.imine:
@@ -5027,11 +4987,11 @@ def mine():
        skip(1)
        prout(_("there's no reason to mine more at this time."))
        return
-    game.optime = (0.1+0.2*random.random())*game.state.planets[game.iplnet].pclass
+    game.optime = randreal(0.1, 0.3)*(ord(game.iplnet.pclass)-ord("L"))
     if consumeTime():
        return
     prout(_("Mining operation complete."))
-    game.state.planets[game.iplnet].crystals = mined
+    game.iplnet.crystals = "mined"
     game.imine = game.ididit = True
 
 def usecrystals():
@@ -5062,7 +5022,7 @@ def usecrystals():
     skip(1)
     prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
     skip(1)
-    if random.random() <= game.cryprob:
+    if with(game.cryprob):
        prouts(_("  \"Activating now! - - No good!  It's***"))
        skip(2)
        prouts(_("***RED ALERT!  RED A*L********************************"))
@@ -5072,7 +5032,7 @@ def usecrystals():
        skip(1)
        kaboom()
        return
-    game.energy += 5000.0*(1.0 + 0.9*random.random())
+    game.energy += randreal(5000.0, 5500.0)
     prouts(_("  \"Activating now! - - "))
     prout(_("The instruments"))
     prout(_("   are going crazy, but I think it's"))
@@ -5086,7 +5046,7 @@ def shuttle():
     skip(1)
     if damaged(DSHUTTL):
        if game.damage[DSHUTTL] == -1.0:
-           if game.inorbit and game.state.planets[game.iplnet].known == "shuttle_down":
+           if game.inorbit and game.iplnet.known == "shuttle_down":
                prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
            else:
                prout(_("Ye Faerie Queene had no shuttle craft."))
@@ -5099,16 +5059,16 @@ def shuttle():
        crmshp()
        prout(_(" not in standard orbit."))
        return
-    if (game.state.planets[game.iplnet].known != "shuttle_down") and game.iscraft != "onship":
+    if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
        prout(_("Shuttle craft not currently available."))
        return
-    if not game.landed and game.state.planets[game.iplnet].known=="shuttle_down":
+    if not game.landed and game.iplnet.known=="shuttle_down":
        prout(_("You will have to beam down to retrieve the shuttle craft."))
        return
     if game.shldup or game.condition == "docked":
        prout(_("Shuttle craft cannot pass through shields."))
        return
-    if game.state.planets[game.iplnet].known=="unknown":
+    if game.iplnet.known=="unknown":
        prout(_("Spock-  \"Captain, we have no information on this planet"))
        prout(_("  and Starfleet Regulations clearly state that in this situation"))
        prout(_("  you may not fly down.\""))
@@ -5140,7 +5100,7 @@ def shuttle():
            skip(1)
            if consumeTime():
                return
-           game.state.planets[game.iplnet].known="shuttle_down"
+           game.iplnet.known="shuttle_down"
            prout(_("Trip complete."))
            return
        else:
@@ -5150,7 +5110,7 @@ def shuttle():
            skip(1)
            prouts(_("The short hop begins . . ."))
            skip(1)
-           game.state.planets[game.iplnet].known="known"
+           game.iplnet.known="known"
            game.icraft = True
            skip(1)
            game.landed = False
@@ -5176,7 +5136,7 @@ def shuttle():
        game.iscraft = "offship"
        if consumeTime():
            return
-       game.state.planets[game.iplnet].known = "shuttle_down"
+       game.iplnet.known = "shuttle_down"
        game.landed = True
        game.icraft = False
        prout(_("Trip complete."))
@@ -5184,8 +5144,6 @@ def shuttle():
 
 def deathray():
     # use the big zapper 
-    r = random.random()
-       
     game.ididit = False
     skip(1)
     chew()
@@ -5217,27 +5175,28 @@ def deathray():
     skip(1)
     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
     skip(1)
-    dprob = .30
+    dprob = 0.30
     if game.options & OPTION_PLAIN:
-       dprob = .5
+       dprob = 0.5
+    r = randreal()
     if r > dprob:
        prouts(_("Sulu- \"Captain!  It's working!\""))
        skip(2)
        while game.nenhere > 0:
-           deadkl(game.ks[1], game.quad[game.ks[1].x][game.ks[1].y],game.ks[1])
+           deadkl(game.enemies[1].kloc, game.quad[game.enemies[1].kloc.x][game.enemies[1].kloc.y],game.enemies[1].kloc)
        prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
        if (game.state.remkl + game.state.remcom + game.state.nscrem) == 0:
            finish(FWON)    
        if (game.options & OPTION_PLAIN) == 0:
            prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
-           if random.random() <= 0.05:
+           if withprob(0.05):
                prout(_("   is still operational.\""))
            else:
                prout(_("   has been rendered nonfunctional.\""))
                game.damage[DDRAY] = 39.95
        return
-    r = random.random()        # Pick failure method 
-    if r <= .30:
+    r = randreal()     # Pick failure method 
+    if r <= 0.30:
        prouts(_("Sulu- \"Captain!  It's working!\""))
        skip(1)
        prouts(_("***RED ALERT!  RED ALERT!"))
@@ -5251,7 +5210,7 @@ def deathray():
        skip(1)
        kaboom()
        return
-    if r <= .55:
+    if r <= 0.55:
        prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
        skip(1)
        prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
@@ -5270,8 +5229,8 @@ def deathray():
        proutn(_("Spock-  \"I believe the word is"))
        prouts(_(" *ASTONISHING*"))
        prout(_(" Mr. Sulu."))
-       for i in range(1, QUADSIZE+1):
-           for j in range(1, QUADSIZE+1):
+       for i in range(QUADSIZE):
+           for j in range(QUADSIZE):
                if game.quad[i][j] == IHDOT:
                    game.quad[i][j] = IHQUEST
        prout(_("  Captain, our quadrant is now infested with"))
@@ -5376,30 +5335,34 @@ def report():
                   (i, (_("s"), "")[i==1]))
     skip(1)
        
-def lrscan():
+def lrscan(silent):
     # long-range sensor scan 
     if damaged(DLRSENS):
        # Now allow base's sensors if docked 
        if game.condition != "docked":
-           prout(_("LONG-RANGE SENSORS DAMAGED."))
+            if not silent:
+                prout(_("LONG-RANGE SENSORS DAMAGED."))
            return
-       prout(_("Starbase's long-range scan"))
-    else:
+        if not silent:
+            prout(_("Starbase's long-range scan"))
+    elif not silent:
        prout(_("Long-range scan"))
     for x in range(game.quadrant.x-1, game.quadrant.x+2):
-        proutn(" ")
+        if not silent:
+            proutn(" ")
         for y in range(game.quadrant.y-1, game.quadrant.y+2):
            if not VALID_QUADRANT(x, y):
-               proutn("  -1")
+                if not silent:
+                    proutn("  -1")
            else:
                if not damaged(DRADIO):
                    game.state.galaxy[x][y].charted = True
                game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
                game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
                game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
-               if game.state.galaxy[x][y].supernova: 
+               if not silent and game.state.galaxy[x][y].supernova: 
                    proutn(" ***")
-               else:
+               elif not silent:
                    proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
        prout(" ")
 
@@ -5423,8 +5386,8 @@ def damagereport():
 def rechart():
     # update the chart in the Enterprise's computer from galaxy data 
     game.lastchart = game.state.date
-    for i in range(1, GALSIZE+1):
-       for j in range(1, GALSIZE+1):
+    for i in range(GALSIZE):
+       for j in range(GALSIZE):
            if game.state.galaxy[i][j].charted:
                game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
                game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
@@ -5433,6 +5396,8 @@ def rechart():
 def chart():
     # display the star chart  
     chew()
+    if (game.options & OPTION_AUTOSCAN):
+        lrscan(silent=True)
     if not damaged(DRADIO):
        rechart()
     if game.lastchart < game.state.date and game.condition == "docked":
@@ -5443,22 +5408,22 @@ def chart():
     if game.state.date > game.lastchart:
        prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
     prout("      1    2    3    4    5    6    7    8")
-    for i in range(1, GALSIZE+1):
-       proutn("%d |" % (i))
-       for j in range(1, GALSIZE+1):
+    for i in range(GALSIZE):
+       proutn("%d |" % (i+1))
+       for j in range(GALSIZE):
            if (game.options & OPTION_SHOWME) and i == game.quadrant.x and j == game.quadrant.y:
                proutn("<")
            else:
                proutn(" ")
            if game.state.galaxy[i][j].supernova:
-               strcpy(buf, "***")
+               show = "***"
            elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
-               strcpy(buf, ".1.")
+               show = ".1."
            elif game.state.galaxy[i][j].charted:
-               sprintf(buf, "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars))
+               show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
            else:
-               strcpy(buf, "...")
-           proutn(buf)
+               show = "..."
+           proutn(show)
            if (game.options & OPTION_SHOWME) and i == game.quadrant.x and j == game.quadrant.y:
                proutn(">")
            else:
@@ -5483,57 +5448,57 @@ def sectscan(goodScan, i, j):
     else:
        proutn("- ")
 
-def status(req):
+def status(req=0):
     # print status report lines 
 
     if not req or req == 1:
        prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
                % (game.state.date, game.state.remtime))
-    elif not req or req == 2:
+    if not req or req == 2:
        if game.condition != "docked":
            newcnd()
         dam = 0
-       for t in range(0, NDEVICES):
+       for t in range(NDEVICES):
            if game.damage[t]>0: 
                dam += 1
        prstat(_("Condition"), _("%s, %i DAMAGES") % (game.condition.upper(), dam))
-    elif not req or req == 3:
+    if not req or req == 3:
        prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
-    elif not req or req == 4:
+    if not req or req == 4:
        if damaged(DLIFSUP):
            if game.condition == "docked":
-               sprintf(s, _("DAMAGED, Base provides"))
+               s = _("DAMAGED, Base provides")
            else:
-               sprintf(s, _("DAMAGED, reserves=%4.2f") % game.lsupres)
+               s = _("DAMAGED, reserves=%4.2f") % game.lsupres
        else:
-           sprintf(s, _("ACTIVE"))
+           s = _("ACTIVE")
        prstat(_("Life Support"), s)
-    elif not req or req == 5:
-       prstat(_("Warp Factor"), "%.1f" % (game.warpfac))
-    elif not req or req == 6:
+    if not req or req == 5:
+       prstat(_("Warp Factor"), "%.1f" % game.warpfac)
+    if not req or req == 6:
         extra = ""
         if game.icrystl and (game.options & OPTION_SHOWME):
             extra = _(" (have crystals)")
-       prstat(_("Energy"), "%.2f%s" % game.energy, extra)
-    elif not req or req == 7:
+       prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
+    if not req or req == 7:
        prstat(_("Torpedoes"), "%d" % (game.torps))
-    elif not req or req == 8:
+    if not req or req == 8:
        if damaged(DSHIELD):
-           strcpy(s, _("DAMAGED,"))
+           s = _("DAMAGED,")
        elif game.shldup:
-           strcpy(s, _("UP,"))
+           s = _("UP,")
        else:
-           strcpy(s, _("DOWN,"))
+           s = _("DOWN,")
        data = _(" %d%% %.1f units") \
                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
-       prstat(_("Shields"), s)
-    elif not req or req == 9:
+       prstat(_("Shields"), s+data)
+    if not req or req == 9:
         prstat(_("Klingons Left"), "%d" \
                % (game.state.remkl + game.state.remcom + game.state.nscrem))
-    elif not req or req == 10:
+    if not req or req == 10:
        if game.options & OPTION_WORLDS:
            plnet = game.state.galaxy[game.quadrant.x][game.quadrant.y].planet
-           if plnet != NOPLANET and game.state.planets[plnet].inhabited:
+           if plnet and plnet.inhabited:
                prstat(_("Major system"), plnet.name)
            else:
                prout(_("Sector is uninhabited"))
@@ -5572,9 +5537,9 @@ def srscan():
     prout("    1 2 3 4 5 6 7 8 9 10")
     if game.condition != "docked":
        newcnd()
-    for i in range(1, QUADSIZE+1):
-       proutn("%2d  " % (i))
-       for j in range(1, QUADSIZE+1):
+    for i in range(QUADSIZE):
+       proutn("%2d  " % (i+1))
+       for j in range(QUADSIZE):
            sectscan(goodScan, i, j)
        skip(1)
                        
@@ -5594,34 +5559,32 @@ def eta():
        if scan()!=IHREAL:
            huh()
            return
-    w1.y = aaitem +0.5
+    w1.y = int(aaitem-0.5)
     if scan() != IHREAL:
        huh()
        return
-    w1.x = aaitem + 0.5
+    w1.x = int(aaitem-0.5)
     if scan() == IHREAL:
-       w2.y = aaitem + 0.5
+       w2.y = int(aaitem-0.5)
        if scan() != IHREAL:
            huh()
            return
-       w2.x = aaitem + 0.5
+       w2.x = int(aaitem-0.5)
     else:
        if game.quadrant.y>w1.x:
-           w2.x = 1
+           w2.x = 0
        else:
-           w2.x=QUADSIZE
+           w2.x=QUADSIZE-1
        if game.quadrant.x>w1.y:
-           w2.y = 1
+           w2.y = 0
        else:
-           w2.y=QUADSIZE
-
+           w2.y=QUADSIZE-1
     if not VALID_QUADRANT(w1.x, w1.y) or not VALID_SECTOR(w2.x, w2.y):
        huh()
        return
     game.dist = math.sqrt(square(w1.y-game.quadrant.y+0.1*(w2.y-game.sector.y))+
                square(w1.x-game.quadrant.x+0.1*(w2.x-game.sector.x)))
     wfl = False
-
     if prompt:
        prout(_("Answer \"no\" if you don't know the value:"))
     while True:
@@ -5704,83 +5667,16 @@ def eta():
            return
                        
 
-#ifdef BSD_BUG_FOR_BUG
-# A visual scan is made in a particular direction of three sectors
-# in the general direction specified.  This takes time, and
-# Klingons can attack you, so it should be done only when sensors
-# are out.  Code swiped from BSD-Trek.  Not presently used, as we
-# automatically display all adjacent sectors on the short-range
-# scan even when short-range sensors are out.
-
-# This struct[] has the delta x, delta y for particular directions
-
-visdelta = (
-    (-1,-1),
-    (-1, 0),
-    (-1, 1),
-    (0,         1),
-    (1,         1),
-    (1,         0),
-    (1,        -1),
-    (0,        -1),
-    (-1,-1),
-    (-1, 0),
-    (-1, 1),
-)
-
-def visual():
-    v = coord()
-    if scan() != IHREAL:
-       chew()
-       proutn(_("Direction? "))
-       if scan()!=IHREAL:
-           huh()
-           return
-    if aaitem < 0.0 or aaitem > 360.0:
-       return
-    co = (aaitem + 22) / 45
-    v = visdelta[co]
-    ix = game.sector.x + v.x
-    iy = game.sector.y + v.y
-    if ix < 0 or ix >= QUADSIZE or iy < 0 or iy >= QUADSIZE:
-       co = '?'
-    else:
-       co = game.quad[ix][iy]
-    printf("%d,%d %c " % (ix, iy, co))
-    v += 1
-    ix = game.sector.x + v.x
-    iy = game.sector.y + v.y
-    if ix < 0 or ix >= QUADSIZE or iy < 0 or iy >= QUADSIZE:
-       co = '?'
-    else:
-       co = game.quad[ix][iy]
-    printf("%c " % (co))
-    v += 1
-    ix = game.sector.x + v.x
-    iy = game.sector.y + v.y
-    if ix < 0 or ix >= QUADSIZE or iy < 0 or iy >= QUADSIZE:
-       co = '?'
-    else:
-       co = game.quad[ix][iy]
-    prout("%c %d,%d\n" % (co, ix, iy))
-    game.optime = 0.5
-    game.ididit = True
-#endif
-
 # Code from setup.c begins here
 
-def filelength(fd):
-    return os.fstat(fd).st_size
-
 def prelim():
     # issue a historically correct banner 
     skip(2)
     prout(_("-SUPER- STAR TREK"))
     skip(1)
-#ifdef __HISTORICAL__
+# From the FORTRAN original
 #    prout(_("Latest update-21 Sept 78"))
 #    skip(1)
-#endif __HISTORICAL__ 
 
 def freeze(boss):
     # save game 
@@ -5901,7 +5797,6 @@ device = (
 def setup(needprompt):
     # prepare to play, set up cosmos 
     w = coord()
-
     #  Decide how many of everything
     if choose(needprompt):
        return # frozen game
@@ -5918,14 +5813,14 @@ def setup(needprompt):
     game.quadrant = randplace(GALSIZE)
     game.sector = randplace(QUADSIZE)
     game.torps = game.intorps = 10
-    game.nprobes = int(3.0*random.random() + 2.0)      # Give them 2-4 of these
+    game.nprobes = randrange(2, 5)
     game.warpfac = 5.0
     game.wfacsq = game.warpfac * game.warpfac
-    for i in range(0, NDEVICES): 
+    for i in range(NDEVICES): 
        game.damage[i] = 0.0
     # Set up assorted game parameters
     game.battle = coord()
-    game.state.date = game.indate = 100.0*int(31.0*random.random()+20.0)
+    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
@@ -5934,11 +5829,11 @@ def setup(needprompt):
     game.landed = False
     game.alive = True
     game.docfac = 0.25
-    for i in range(1, GALSIZE+1):
-       for j in range(1, GALSIZE+1):
+    for i in range(GALSIZE):
+       for j in range(GALSIZE):
            quad = game.state.galaxy[i][j]
            quad.charted = 0
-           quad.planet = NOPLANET
+           quad.planet = None
            quad.romulans = 0
            quad.klingons = 0
            quad.starbase = False
@@ -5947,7 +5842,7 @@ def setup(needprompt):
     # Initialize times for extraneous events
     schedule(FSNOVA, expran(0.5 * game.intime))
     schedule(FTBEAM, expran(1.5 * (game.intime / game.state.remcom)))
-    schedule(FSNAP, 1.0 + random.random()) # Force an early snapshot
+    schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
     schedule(FBATTAK, expran(0.3*game.intime))
     unschedule(FCDBAS)
     if game.state.nscrem:
@@ -5966,13 +5861,13 @@ def setup(needprompt):
     game.lastchart = FOREVER
     # Put stars in the galaxy
     game.instar = 0
-    for i in range(1, GALSIZE+1):
-       for j in range(1, GALSIZE+1):
-           k = random.random()*9.0 + 1.0
+    for i in range(GALSIZE):
+       for j in range(GALSIZE):
+           k = randrange(1, QUADSIZE**2/10+1)
            game.instar += k
            game.state.galaxy[i][j].stars = k
     # Locate star bases in galaxy
-    for i in range(1, game.inbase+1):
+    for i in range(game.inbase):
         while True:
             while True:
                 w = randplace(GALSIZE)
@@ -5984,7 +5879,7 @@ def setup(needprompt):
             for j in range(1, i):
                # Improved placement algorithm to spread out bases
                distq = w.distance(game.state.baseq[j])
-               if distq < 6.0*(BASEMAX+1-game.inbase) and random.random() < 0.75:
+               if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
                    contflag = True
                    if idebug:
                        prout("=== Abandoning base #%d at %s" % (i, w))
@@ -6003,7 +5898,7 @@ def setup(needprompt):
     if klumper > MAXKLQUAD: 
        klumper = MAXKLQUAD
     while True:
-       r = random.random()
+       r = randreal()
        klump = (1.0 - r*r)*klumper
        if klump > krem:
            klump = krem
@@ -6013,14 +5908,14 @@ def setup(needprompt):
             if not game.state.galaxy[w.x][w.y].supernova and \
                game.state.galaxy[w.x][w.y].klingons + klump <= MAXKLQUAD:
                 break
-       game.state.galaxy[w.x][w.y].klingons += klump
+       game.state.galaxy[w.x][w.y].klingons += int(klump)
         if krem <= 0:
             break
     # Position Klingon Commander Ships
     for i in range(1, game.incom+1):
         while True:
             w = randplace(GALSIZE)
-           if (game.state.galaxy[w.x][w.y].klingons or random.random()>=0.75) and \
+           if (game.state.galaxy[w.x][w.y].klingons or withprob(0.25)) and \
                   not game.state.galaxy[w.x][w.y].supernova and \
                   game.state.galaxy[w.x][w.y].klingons <= MAXKLQUAD-1 and \
                    not w in game.state.kcmdr[:i]:
@@ -6031,7 +5926,7 @@ def setup(needprompt):
     for i in range(game.inplan):
         while True:
             w = randplace(GALSIZE) 
-            if game.state.galaxy[w.x][w.y].planet == NOPLANET:
+            if game.state.galaxy[w.x][w.y].planet == None:
                 break
         new = planet()
        new.w = w
@@ -6043,15 +5938,15 @@ def setup(needprompt):
             new.name = systnames[i]
            new.inhabited = True
        else:
-           new.pclass = ("M", "N", "O")[random.randint(0, 2)]
-            if random.random()*1.5:            # 1 in 3 chance of crystals
+           new.pclass = ("M", "N", "O")[randrange(0, 3)]
+            if withprob(0.33):
                 new.crystals = "present"
            new.known = "unknown"
            new.inhabited = False
        game.state.galaxy[w.x][w.y].planet = new
         game.state.planets.append(new)
     # Locate Romulans
-    for i in range(1, game.state.nromrem+1):
+    for i in range(game.state.nromrem):
        w = randplace(GALSIZE)
        game.state.galaxy[w.x][w.y].romulans += 1
     # Locate the Super Commander
@@ -6062,10 +5957,10 @@ def setup(needprompt):
                 break
        game.state.kscmdr = w
        game.state.galaxy[w.x][w.y].klingons += 1
-    # Place thing (in tournament game, thingx == -1, don't want one!)
+    # Place thing (in tournament game, we don't want one!)
     global thing
-    if thing == None:
-       thing = randplace(GALSIZE)
+    if game.tourn is None:
+        thing = randplace(GALSIZE)
     skip(2)
     game.state.snap = False
     if game.skill == SKILL_NOVICE:
@@ -6087,7 +5982,7 @@ def setup(needprompt):
            prout(_("And one (GULP) Super-Commander."))
        prout(_("%d stardates.") % int(game.intime))
        proutn(_("%d starbases in ") % game.inbase)
-    for i in range(1, game.inbase+1):
+    for i in range(game.inbase):
        proutn(`game.state.baseq[i]`)
        proutn("  ")
     skip(2)
@@ -6099,7 +5994,7 @@ def setup(needprompt):
        prout(_("  YOU'LL NEED IT."))
     waitfor()
     newqad(False)
-    if game.nenhere - iqhere-game.ithere:
+    if game.nenhere - (thing == game.quadrant) - (game.tholian != None):
        game.shldup = True
     if game.neutz:     # bad luck to start in a Romulan Neutral Zone
        attack(False)
@@ -6123,8 +6018,10 @@ def choose(needprompt):
            if aaitem == 0:
                chew()
                continue # We don't want a blank entry
-           game.tourn = int(aaitem)
+           game.tourn = int(round(aaitem))
            random.seed(aaitem)
+            if logfp:
+                logfp.write("# random.seed(%d)\n" % aaitem)
            break
         if isit("saved") or isit("frozen"):
            if thaw():
@@ -6193,19 +6090,19 @@ def choose(needprompt):
 
     # Use parameters to generate initial values of things
     game.damfac = 0.5 * game.skill
-    game.state.rembase = random.randint(BASEMIN, BASEMAX)
+    game.state.rembase = randrange(BASEMIN, BASEMAX+1)
     game.inbase = game.state.rembase
     game.inplan = 0
     if game.options & OPTION_PLANETS:
-       game.inplan += (MAXUNINHAB/2) + (MAXUNINHAB/2+1)*random.random()
+       game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
     if game.options & OPTION_WORLDS:
-       game.inplan += NINHAB
-    game.state.nromrem = game.inrom = (2.0+random.random())*game.skill
+       game.inplan += int(NINHAB)
+    game.state.nromrem = game.inrom = randrange(2 *game.skill)
     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
     game.state.remtime = 7.0 * game.length
     game.intime = game.state.remtime
-    game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*random.random())*game.skill*0.1+.15)
-    game.incom = game.skill + 0.0625*game.inkling*random.random()
+    game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
+    game.incom = int(game.skill + 0.0625*game.inkling*randreal())
     game.state.remcom = min(10, game.incom)
     game.incom = game.state.remcom
     game.state.remres = (game.inkling+4*game.incom)*game.intime
@@ -6215,14 +6112,15 @@ def choose(needprompt):
        game.inbase = game.state.rembase
     return False
 
-def dropin(iquad):
+def dropin(iquad=None):
     # drop a feature on a random dot in the current quadrant 
     w = coord()
     while True:
         w = randplace(QUADSIZE)
         if game.quad[w.x][w.y] == IHDOT:
             break
-    game.quad[w.x][w.y] = iquad
+    if iquad is not None:
+        game.quad[w.x][w.y] = iquad
     return w
 
 def newcnd():
@@ -6235,13 +6133,9 @@ def newcnd():
     if not game.alive:
        game.condition="dead"
 
-def newkling(i):
-    # drop new Klingon into current quadrant 
-    pi = dropin(IHK)
-    game.ks[i] = pi
-    game.kdist[i] = game.kavgd[i] = distance(game.sector, pi)
-    game.kpower[i] = random.random()*150.0 +300.0 +25.0*game.skill
-    return pi
+def newkling():
+    # drop new Klingon into current quadrant
+    return enemy(IHK, loc=dropin(), power=randreal(300,450)+25.0*game.skill)
 
 def newqad(shutup):
     # set up a new state of quadrant, for when we enter or re-enter it 
@@ -6257,9 +6151,6 @@ def newqad(shutup):
     game.inorbit = False
     game.landed = False
     game.ientesc = False
-    game.ithere = False
-    iqhere = False
-    iqengry = False
     game.iseenit = False
     if game.iscate:
        # Attempt to escape Super-commander, so tbeam back!
@@ -6272,54 +6163,43 @@ def newqad(shutup):
     game.klhere = q.klingons
     game.irhere = q.romulans
     game.nenhere = game.klhere + game.irhere
-
     # Position Starship
     game.quad[game.sector.x][game.sector.y] = game.ship
-
+    game.enemies = []
     if q.klingons:
-       w.x = w.y = 0   # quiet a gcc warning 
        # Position ordinary Klingons
-       for i in range(1, game.klhere+1):
-           w = newkling(i)
+       for i in range(game.klhere):
+            newkling()
        # If we need a commander, promote a Klingon
-       for i in range(1, game.state.remcom+1):
+       for i in range(game.state.remcom):
            if game.state.kcmdr[i] == game.quadrant:
-               break
-                       
-       if i <= game.state.remcom:
-           game.quad[w.x][w.y] = IHC
-           game.kpower[game.klhere] = 950.0+400.0*random.random()+50.0*game.skill
-           game.comhere = True
-
+                e = game.enemies[game.klhere-1]
+                game.quad[e.kloc.x][e.kloc.y] = IHC
+                e.kpower = randreal(950,1350) + 50.0*game.skill
+                game.comhere = True
+               break   
        # If we need a super-commander, promote a Klingon
-       if same(game.quadrant, game.state.kscmdr):
-           game.quad[game.ks[1].x][game.ks[1].y] = IHS
-           game.kpower[1] = 1175.0 + 400.0*random.random() + 125.0*game.skill
+       if game.quadrant == game.state.kscmdr:
+            e = game.enemies[0]
+           game.quad[e.kloc.x][e.kloc.y] = IHS
+           e.kpower = randreal(1175.0,  1575.0) + 125.0*game.skill
            game.iscate = (game.state.remkl > 1)
            game.ishere = True
     # Put in Romulans if needed
-    for i in range(game.klhere+1, game.nenhere+1):
-       w = dropin(IHR)
-       game.ks[i] = w
-       game.kdist[i] = game.kavgd[i] = distance(game.sector, w)
-       game.kpower[i] = random.random()*400.0 + 450.0 + 50.0*game.skill
+    for i in range(game.klhere, game.nenhere):
+        enemy(IHR, 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(IHB)
-       
     # If quadrant needs a planet, put it in
-    if q.planet != NOPLANET:
+    if q.planet:
        game.iplnet = q.planet
-       if game.state.planets[q.planet].inhabited == UNINHABITED:
+       if not q.planet.inhabited:
            game.plnet = dropin(IHP)
        else:
            game.plnet = dropin(IHW)
     # Check for condition
     newcnd()
-    # And finally the stars
-    for i in range(1, q.stars+1): 
-       dropin(IHSTAR)
-
     # Check for RNZ
     if game.irhere > 0 and game.klhere == 0:
        game.neutz = True
@@ -6330,94 +6210,55 @@ def newqad(shutup):
            skip(1)
            prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
-
     if shutup==0:
        # Put in THING if needed
-       if same(thing, game.quadrant):
-           w = dropin(IHQUEST)
-           thing = randplace(GALSIZE)
-           game.nenhere += 1
-           iqhere = True
-           game.ks[game.nenhere] = w
-           game.kdist[game.nenhere] = game.kavgd[game.nenhere] = \
-               distance(game.sector, w)
-           game.kpower[game.nenhere] = random.random()*6000.0 +500.0 +250.0*game.skill
+       if thing == game.quadrant:
+           enemy(symbol=IHQUEST, loc=dropin(),
+                      power=randreal(6000,6500.0)+250.0*game.skill)
            if not damaged(DSRSENS):
                skip(1)
                prout(_("Mr. Spock- \"Captain, this is most unusual."))
                prout(_("    Please examine your short-range scan.\""))
-
     # Decide if quadrant needs a Tholian; lighten up if skill is low 
     if game.options & OPTION_THOLIAN:
-       if (game.skill < SKILL_GOOD and random.random() <= 0.02) or \
-           (game.skill == SKILL_GOOD and random.random() <= 0.05) or \
-            (game.skill > SKILL_GOOD and random.random() <= 0.08):
+       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()
             while True:
-               game.tholian.x = random.choice((1, QUADSIZE))
-               game.tholian.y = random.choice((1, QUADSIZE))
-                if game.quad[game.tholian.x][game.tholian.y] == IHDOT:
+               w.x = withprob(0.5) * (QUADSIZE-1)
+               w.y = withprob(0.5) * (QUADSIZE-1)
+                if game.quad[w.x][w.y] == IHDOT:
                     break
-           game.quad[game.tholian.x][game.tholian.y] = IHT
-           game.ithere = True
-           game.nenhere += 1
-           game.ks[game.nenhere] = game.tholian
-           game.kdist[game.nenhere] = game.kavgd[game.nenhere] = \
-               distance(game.sector, game.tholian)
-           game.kpower[game.nenhere] = random.random()*400.0 +100.0 +25.0*game.skill
+            game.tholian = enemy(symbol=IHT, loc=w,
+                                 power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
-           if game.quad[1][1]==IHDOT:
-               game.quad[1][1] = 'X'
-           if game.quad[1][QUADSIZE]==IHDOT:
-               game.quad[1][QUADSIZE] = 'X'
-           if game.quad[QUADSIZE][1]==IHDOT:
-               game.quad[QUADSIZE][1] = 'X'
-           if game.quad[QUADSIZE][QUADSIZE]==IHDOT:
-               game.quad[QUADSIZE][QUADSIZE] = 'X'
-    sortklings()
-
+           if game.quad[0][0]==IHDOT:
+               game.quad[0][0] = 'X'
+           if game.quad[0][QUADSIZE-1]==IHDOT:
+               game.quad[0][QUADSIZE-1] = 'X'
+           if game.quad[QUADSIZE-1][0]==IHDOT:
+               game.quad[QUADSIZE-1][0] = 'X'
+           if game.quad[QUADSIZE-1][QUADSIZE-1]==IHDOT:
+               game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
+    game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+    # And finally the stars
+    for i in range(q.stars):
+       dropin(IHSTAR)
     # Put in a few black holes
     for i in range(1, 3+1):
-       if random.random() > 0.5
+       if withprob(0.5)
            dropin(IHBLANK)
-
     # Take out X's in corners if Tholian present
-    if game.ithere:
-       if game.quad[1][1]=='X':
-           game.quad[1][1] = IHDOT
-       if game.quad[1][QUADSIZE]=='X':
-           game.quad[1][QUADSIZE] = IHDOT
-       if game.quad[QUADSIZE][1]=='X':
-           game.quad[QUADSIZE][1] = IHDOT
-       if game.quad[QUADSIZE][QUADSIZE]=='X':
-           game.quad[QUADSIZE][QUADSIZE] = IHDOT
-
-def sortklings():
-    # sort Klingons by distance from us 
-    # The author liked bubble sort. So we will use it. :-(
-    if game.nenhere-iqhere-game.ithere < 2:
-       return
-    while True:
-       sw = False
-       for j in range(1, game.nenhere):
-           if game.kdist[j] > game.kdist[j+1]:
-               sw = True
-               t = game.kdist[j]
-               game.kdist[j] = game.kdist[j+1]
-               game.kdist[j+1] = t
-               t = game.kavgd[j]
-               game.kavgd[j] = game.kavgd[j+1]
-               game.kavgd[j+1] = t
-               k = game.ks[j].x
-               game.ks[j].x = game.ks[j+1].x
-               game.ks[j+1].x = k
-               k = game.ks[j].y
-               game.ks[j].y = game.ks[j+1].y
-               game.ks[j+1].y = k
-               t = game.kpower[j]
-               game.kpower[j] = game.kpower[j+1]
-               game.kpower[j+1] = t
-        if not sw:
-            break
+    if game.tholian:
+       if game.quad[0][0]=='X':
+           game.quad[0][0] = IHDOT
+       if game.quad[0][QUADSIZE-1]=='X':
+           game.quad[0][QUADSIZE-1] = IHDOT
+       if game.quad[QUADSIZE-1][0]=='X':
+           game.quad[QUADSIZE-1][0] = IHDOT
+       if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
+           game.quad[QUADSIZE-1][QUADSIZE-1] = IHDOT
 
 def setpassword():
     # set the self-destruct password 
@@ -6432,7 +6273,7 @@ def setpassword():
     else:
         game.passwd = ""
         for i in range(3):
-           game.passwd += chr(97+int(random.random()*25))
+           game.passwd += chr(ord('a')+randrange(26))
 
 # Code from sst.c begins here
 
@@ -6487,7 +6328,7 @@ def listCommands():
     k = 0
     proutn(_("LEGAL COMMANDS ARE:"))
     for key in commands:
-       if ACCEPT(cmd):
+       if ACCEPT(key):
             if k % 5 == 0:
                 skip(1)
             proutn("%-12s " % key) 
@@ -6562,7 +6403,6 @@ def makemoves():
            hitme = False
            game.justin = False
            game.optime = 0.0
-           i = -1
            chew()
            setwnd(prompt_window)
            clrscr()
@@ -6575,19 +6415,24 @@ def makemoves():
            clrscr()
            setwnd(message_window)
            clrscr()
-            cmd = citem.upoper
-            if cmd not in commands:
+            candidates = filter(lambda x: x.startswith(citem.upper()),
+                                commands)
+            if len(candidates) == 1:
+                cmd = candidates[0]
+                break
+            elif candidates and not (game.options & OPTION_PLAIN):
+                prout("Commands with that prefix: " + " ".join(candidates))
+            else:
                 listCommands()
                 continue
-       commandhook(commands[i].name, True)
        if cmd == "SRSCAN":             # srscan
            srscan()
        elif cmd == "STATUS":           # status
-           status(0)
+           status()
        elif cmd == "REQUEST":          # status request 
            request()
        elif cmd == "LRSCAN":           # long range scan
-           lrscan()
+           lrscan(silent=False)
        elif cmd == "PHASERS":          # phasers
            phasers()
            if game.ididit:
@@ -6599,7 +6444,7 @@ def makemoves():
        elif cmd == "MOVE":             # move under warp
            warp(False)
        elif cmd == "SHIELDS":          # shields
-           doshield(False)
+           doshield(shraise=False)
            if game.ididit:
                hitme = True
                game.shldchg = False
@@ -6652,7 +6497,7 @@ def makemoves():
        elif cmd == "EMEXIT":           # Emergency exit
            clrscr()                    # Hide screen
            freeze(True)                # forced save
-           os.exit(1)                  # And quick exit
+           raise SysExit,1                     # And quick exit
        elif cmd == "PROBE":
            probe()                     # Launch probe
            if game.ididit:
@@ -6680,15 +6525,6 @@ def makemoves():
            game.alldone = True         # quit the game
        elif cmd == "HELP":
            helpme()                    # get help
-       elif cmd == "SEED":             # set random-number seed
-           key = scan()
-           if key == IHREAL:
-               seed = int(aaitem)
-#ifdef BSD_BUG_FOR_BUG
-#      elif cmd == "VISUAL":
-#          visual()                    # perform visual scan
-#endif
-       commandhook(commands[i].name, False)
        while True:
            if game.alldone:
                break           # Game has ended
@@ -6730,22 +6566,18 @@ def cramen(cmd):
     else: s = "Unknown??"
     proutn(s)
 
-def cramlc(loctype, w):
-    # name a location 
-    if loctype == "quadrant":
-       buf = _("Quadrant ")
-    elif loctype == "sector":
-       buf = _("Sector ")
-    buf += ("%d - %d" % (w.x, w.y))
-    return buf
-
 def crmena(stars, enemy, loctype, w):
     # print an enemy and his location 
     if stars:
        proutn("***")
     cramen(enemy)
     proutn(_(" at "))
-    proutn(cramlc(loctype, w))
+    buf = ""
+    if loctype == "quadrant":
+       buf = _("Quadrant ")
+    elif loctype == "sector":
+       buf = _("Sector ")
+    proutn(buf + `w`)
 
 def crmshp():
     # print our ship name 
@@ -6763,13 +6595,13 @@ def stars():
     skip(1)
 
 def expran(avrage):
-    return -avrage*math.log(1e-7 + random.random())
+    return -avrage*math.log(1e-7 + randreal())
 
 def randplace(size):
     # choose a random location  
     w = coord()
-    w.x = random.randint(1, size) 
-    w.y = random.randint(1, size)
+    w.x = randrange(size) 
+    w.y = randrange(size)
     return w
 
 def chew():
@@ -6784,7 +6616,7 @@ def chew2():
 
 def scan():
     # Get a token from the user
-    global inqueue, line, citem
+    global inqueue, line, citem, aaitem
     aaitem = 0.0
     citem = ''
 
@@ -6850,7 +6682,7 @@ def debugme():
        game.lsupres = game.inlsr
     proutn("Reset damage? ")
     if ja() == True:
-       for i in range(0, NDEVICES): 
+       for i in range(NDEVICES): 
            if game.damage[i] > 0.0: 
                game.damage[i] = 0.0
     proutn("Toggle debug flag? ")
@@ -6914,13 +6746,13 @@ def debugme():
                            prout("Event %d canceled, no x coordinate." % (i))
                            unschedule(i)
                            continue
-                       w.x = int(aaitem)
+                       w.x = int(round(aaitem))
                        key = scan()
                        if key != IHREAL:
                            prout("Event %d canceled, no y coordinate." % (i))
                            unschedule(i)
                            continue
-                       w.y = int(aaitem)
+                       w.y = int(round(aaitem))
                        ev.quadrant = w
        chew()
     proutn("Induce supernova here? ")
@@ -6929,85 +6761,88 @@ def debugme():
        atover(True)
 
 if __name__ == '__main__':
-    global line, thing, game
-    game = citem = aaitem = inqueue = None
-    line = ''
-    thing = coord()
-    game = gamestate()
-    idebug = 0
-
-    game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY)
-    # Disable curses mode until the game logic is working.
-    #    if os.getenv("TERM"):
-    #  game.options |= OPTION_CURSES | OPTION_SHOWME
-    #    else:
-    game.options |= OPTION_TTY
-
-    seed = time.time()
-    (options, arguments) = getopt.getopt(sys.argv[1:], "r:tx")
-    for (switch, val) in options:
-        if switch == '-r':
-            try:
-                replayfp = open(optarg, "r")
-            except IOError:
-               sys.stderr.write("sst: can't open replay file %s\n" % optarg)
-               os.exit(1)
-            line = replayfp.readline().strip()
-            try:
-                (key, seed) = line.split()
-                seed = int(seed)
-            except ValueError:
-               sys.stderr.write("sst: replay file %s is ill-formed\n"%optarg)
-               os.exit(1)
-           game.options |= OPTION_TTY
-           game.options &=~ OPTION_CURSES
-       elif switch == '-t':
-           game.options |= OPTION_TTY
-           game.options &=~ OPTION_CURSES
-       elif switch == '-x':
-           idebug = True
-       else:
-           sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
-           os.exit(0)
-    # where to save the input in case of bugs
     try:
-        logfp = open("/usr/tmp/sst-input.log", "w")
-    except IOError:
-        sys.stderr.write("sst: warning, can't open logfile\n")
-    if logfp:
-       #setlinebuf(logfp)
-       logfp.write("seed %d\n" % (seed))
-    random.seed(seed)
-
-    iostart()
-    if arguments:
-        inqueue = arguments
-    else:
-        inqueue = None
-
-    while True: # Play a game 
-       setwnd(fullscreen_window)
-       clrscr()
-       prelim()
-       setup(needprompt=not line)
-       if game.alldone:
-           score()
-           game.alldone = False
-       else:
-           makemoves()
-       skip(1)
-       stars()
-       skip(1)
-
-       if game.tourn and game.alldone:
-           proutn(_("Do you want your score recorded?"))
-           if ja() == True:
-               chew2()
-               freeze(False)
-       proutn(_("Do you want to play again? "))
-       if not ja():
-           break
-    skip(1)
-    prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
-    raise SysExit, 0
-
+        global line, thing, game, idebug
+        game = citem = aaitem = inqueue = None
+        line = ''
+        thing = coord()
+        thing.angry = False
+        game = gamestate()
+        idebug = 0
+        game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
+        # Disable curses mode until the game logic is working.
+        #    if os.getenv("TERM"):
+        #      game.options |= OPTION_CURSES | OPTION_SHOWME
+        #    else:
+        game.options |= OPTION_TTY
+        seed = int(time.time())
+        (options, arguments) = getopt.getopt(sys.argv[1:], "r:tx")
+        for (switch, val) in options:
+            if switch == '-r':
+                try:
+                    replayfp = open(val, "r")
+                except IOError:
+                    sys.stderr.write("sst: can't open replay file %s\n" % val)
+                    raise SystemExit, 1
+                try:
+                    line = replayfp.readline().strip()
+                    (leader, key, seed) = line.split()
+                    seed = eval(seed)
+                    sys.stderr.write("sst2k: seed set to %s\n" % seed)
+                    line = replayfp.readline().strip()
+                    arguments += line.split()[2:]
+                except ValueError:
+                    sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
+                    raise SystemExit(1)
+                game.options |= OPTION_TTY
+                game.options &=~ OPTION_CURSES
+            elif switch == '-t':
+                game.options |= OPTION_TTY
+                game.options &=~ OPTION_CURSES
+            elif switch == '-x':
+                idebug = True
+            else:
+                sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
+                raise SystemExit, 1
+        # where to save the input in case of bugs
+        try:
+            logfp = open("/usr/tmp/sst-input.log", "w")
+        except IOError:
+            sys.stderr.write("sst: warning, can't open logfile\n")
+        if logfp:
+            logfp.write("# seed %s\n" % seed)
+            logfp.write("# options %s\n" % " ".join(arguments))
+        random.seed(seed)
+        iostart()
+        if arguments:
+            inqueue = arguments
+        else:
+            inqueue = None
+        while True: # Play a game 
+            setwnd(fullscreen_window)
+            clrscr()
+            prelim()
+            setup(needprompt=not inqueue)
+            if game.alldone:
+                score()
+                game.alldone = False
+            else:
+                makemoves()
+            skip(1)
+            stars()
+            skip(1)
+            if game.tourn and game.alldone:
+                proutn(_("Do you want your score recorded?"))
+                if ja() == True:
+                    chew2()
+                    freeze(False)
+            chew()
+            proutn(_("Do you want to play again? "))
+            if not ja():
+                break
+        skip(1)
+        prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
+        raise SystemExit, 0
+    except KeyboardInterrupt:
+        print""
+        pass