Various minor port fixes.
[super-star-trek.git] / src / sst.py
index 1aa671bfbd1656821bef2d2b7c0b80cbc840e823..7eb5c938da4879ef6dda8ec2f86da88b6f4089ac 100644 (file)
@@ -200,16 +200,12 @@ MAXKLGAME = 127
 MAXKLQUAD      = 9
 FULLCREW       = 428   # BSD Trek was 387, that's wrong 
 FOREVER        = 1e30
+MAXBURST       = 3
 
 # These functions hide the difference between 0-origin and 1-origin addressing.
 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))
-def invalidate(w):     w.x = w.y = 0
-def is_valid(w):       return (w.x != 0 and w.y != 0)
-
 # How to represent features
 IHR = 'R',
 IHK = 'K',
@@ -247,8 +243,13 @@ class coord:
     def __add__(self, other):
         return coord(self.x+self.x, self.y+self.y)
     def __sub__(self, other):
-        return coord(self.x-self.x, self.y-self.y)
-    def distance(self, other):
+        return coord(self.x-other.x, self.y-other.y)
+    def __mul__(self, other):
+        return coord(self.x*other, self.y*other)
+    def __rmul__(self, other):
+        return coord(self.x*other, self.y*other)
+    def distance(self, other=None):
+        if not other: other = coord(0, 0)
         return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
     def sgn(self):
         s = coord()
@@ -261,6 +262,8 @@ class coord:
         else:
             s.y = self.y / abs(self.y)
         return s
+    def course(self):
+        return 1.90985*math.atan2(self.y, self.x)
     def scatter(self):
         s = coord()
         s.x = self.x + randrange(-1, 2)
@@ -424,16 +427,22 @@ class enemy:
         self.kpower = power    # enemy energy level
         game.enemies.append(self)
     def move(self, loc):
+        motion = (loc != self.kloc)
         if self.kloc.x is not None and self.kloc.y is not None:
-            game.quad[self.kloc.x][self.kloc.y] = IHDOT
+            if motion:
+                if self.type == IHT:
+                    game.quad[self.kloc.x][self.kloc.y] = IHWEB
+                else:
+                    game.quad[self.kloc.x][self.kloc.y] = IHDOT
         if loc:
-            self.kloc = loc
+            self.kloc = copy.copy(loc)
             game.quad[self.kloc.x][self.kloc.y] = self.type
-            self.kdist = self.kavgd = distance(game.sector, loc)
+            self.kdist = self.kavgd = (game.sector - loc).distance()
         else:
-            self.kloc = coord()        # enemy sector location
+            self.kloc = coord()
             self.kdist = self.kavgd = None
             game.enemies.remove(self)
+        return motion
     def __repr__(self):
         return "<%s=%f>" % (self.kloc, self.kpower)    # For debugging
 
@@ -442,7 +451,7 @@ class gamestate:
         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.quad = None       # contents of our quadrant
         self.damage = [0.0] * NDEVICES # damage encountered
         self.future = []               # future events
         for i in range(NEVENTS):
@@ -578,25 +587,6 @@ FTRIBBLE = 19
 FHOLE = 20
 FCREW = 21
 
-# From enumerated type 'COLORS'
-DEFAULT = 0
-BLACK = 1
-BLUE = 2
-GREEN = 3
-CYAN = 4
-RED = 5
-MAGENTA = 6
-BROWN = 7
-LIGHTGRAY = 8
-DARKGRAY = 9
-LIGHTBLUE = 10
-LIGHTGREEN = 11
-LIGHTCYAN = 12
-LIGHTRED = 13
-LIGHTMAGENTA = 14
-YELLOW = 15
-WHITE = 16
-
 # Log the results of pulling random numbers so we can check determinism.
 
 import traceback
@@ -614,15 +604,15 @@ def randrange(*args):
 def randreal(*args):
     v = random.random()
     if len(args) == 1:
-        v *= args[0]           # returns from [0, a1)
+        v *= args[0]           # returns from [0, args[0])
     elif len(args) == 2:
-        v = args[0] + v*args[1]        # returns from [a1, a2)
+        v = args[0] + v*(args[1]-args[0])      # returns from [args[0], args[1])
     #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
@@ -631,11 +621,11 @@ 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:
+       if enemy.type == IHC:
            for n in range(game.state.remcom):
                if game.state.kcmdr[n] == iq:
                    return False
@@ -643,23 +633,23 @@ def tryexit(look, ienm, loccom, irun):
            if game.battle == game.quadrant:
                return False
        # don't leave if over 1000 units of energy 
-       if game.enemies[loccom].kpower > 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.enemies[loccom].kloc)
+       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.enemies[loccom].move(None)
+    enemy.move(None)
     game.klhere -= 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
@@ -715,7 +705,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
@@ -724,17 +714,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.enemies[loccom].kdist
+    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.enemies[loccom].kpower <= 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.enemies[loccom].kpower+100.0*len(game.enemies)+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):
@@ -754,9 +743,9 @@ def movebaddy(com, loccom, ienm):
            motion = ((forces + randreal(200))/150.0) - 5.0
        else:
             if forces > 1000.0: # Very strong -- move in for kill 
-               motion = (1.0-square(randreal()))*dist1 + 1.0
+               motion = (1.0 - randreal())**2 * dist1 + 1.0
            if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
-               motion -= game.skill*(2.0-square(randreal()))
+               motion -= game.skill*(2.0-randreal()**2)
        if idebug:
            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
        # don't move if no motion 
@@ -782,35 +771,33 @@ 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
-    if 2.0 * abs(mx) < abs(my):
-       mx = 0
-    if 2.0 * abs(my) < abs(game.sector.x-com.x):
-       my = 0
-    if mx != 0:
-        if mx*motion < 0:
-            mx = -1
+    m = game.sector - enemy.kloc
+    if 2.0 * abs(m.x) < abs(m.y):
+       m.x = 0
+    if 2.0 * abs(m.y) < abs(game.sector.x-enemy.kloc.x):
+       m.y = 0
+    if m.x != 0:
+        if m.x*motion < 0:
+            m.x = -1
         else:
-            mx = 1
-    if my != 0:
-        if my*motion < 0:
-            my = -1
+            m.x = 1
+    if m.y != 0:
+        if m.y*motion < 0:
+            m.y = -1
         else:
-            my = 1
-    next = com
+            m.y = 1
+    next = enemy.kloc
     # main move loop 
     for ll in range(nsteps):
        if idebug:
            proutn(" %d" % (ll+1))
        # Check if preferred position available 
-       look.x = next.x + mx
-       look.y = next.y + my
-        if mx < 0:
+       look = next + m
+        if m.x < 0:
             krawlx = 1
         else:
             krawlx = -1
-        if my < 0:
+        if m.y < 0:
             krawly = 1
         else:
             krawly = -1
@@ -819,29 +806,29 @@ def movebaddy(com, loccom, ienm):
        while attempts < 20 and not success:
             attempts += 1
            if look.x < 0 or look.x >= QUADSIZE:
-               if motion < 0 and tryexit(look, ienm, loccom, irun):
+               if motion < 0 and tryexit(enemy, look, irun):
                    return
-               if krawlx == mx or my == 0:
+               if krawlx == m.x or m.y == 0:
                    break
                look.x = next.x + krawlx
                krawlx = -krawlx
            elif look.y < 0 or look.y >= QUADSIZE:
-               if motion < 0 and tryexit(look, ienm, loccom, irun):
+               if motion < 0 and tryexit(enemy, look, irun):
                    return
-               if krawly == my or mx == 0:
+               if krawly == m.y or m.x == 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:
+               if krawlx != m.x and m.y != 0:
                    look.x = next.x + krawlx
                    krawlx = -krawlx
-               elif krawly != my and mx != 0:
+               elif krawly != m.y and m.x != 0:
                    look.y = next.y + krawly
                    krawly = -krawly
                else:
@@ -854,21 +841,14 @@ 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:
-       # it moved 
-       game.enemies[loccom].kloc = next
-       game.enemies[loccom].kdist = game.enemies[loccom].kavgd = distance(game.sector, next)
-       if not damaged(DSRSENS) or game.condition == docked:
+    if enemy.move(next):
+       if not damaged(DSRSENS) or game.condition == "docked":
            proutn("***")
-           cramen(ienm)
-           proutn(_(" from Sector %s") % com)
-           if game.enemies[loccom].kdist < dist1:
+           cramen(enemy.type)
+           proutn(_(" from Sector %s") % enemy.kloc)
+           if enemy.kdist < dist1:
                proutn(_(" advances to "))
            else:
                proutn(_(" retreats to "))
@@ -881,22 +861,21 @@ def moveklings():
     # Figure out which Klingon is the commander (or Supercommander)
     # and do move
     if game.comhere:
-        for (i, e) in enumerate(game.enemies):
-           if game.quad[e.kloc.x][e.kloc.y] == IHC:
-               movebaddy(e.kloc, i, IHC)
-               break
+        for enemy in game.enemies:
+           if enemy.type == IHC:
+               movebaddy(enemy)
     if game.ishere:
-        for (i, e) in enumerate(game.enemies):
-           if game.quad[e.kloc.x][e.kloc.y] == IHS:
-               movebaddy(e.kloc, 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, e) in enumerate(game.enemies):
-            if game.quad[e.kloc.x][e.kloc.y] in (IHK, IHR):
-               movebaddy(e.kloc, i, game.quad[e.kloc.x][e.kloc.y])
+        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):
@@ -923,12 +902,12 @@ def movescom(iq, avoid):
        game.ishere = False
        game.ientesc = False
        unschedule(FSCDBAS)
-       for e in game.enemies:
-           if game.quad[e.kloc.x][e.kloc.y] == IHS:
+       for enemy in game.enemies:
+           if enemy.type == IHS:
                break
-       game.enemies[i].move(None)
+       enemy.move(None)
        game.klhere -= 1
-       if game.condition!=docked:
+       if game.condition != "docked":
            newcnd()
         game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
     # check for a helpful planet 
@@ -970,7 +949,7 @@ def supercommander():
            return
        sc = game.state.kscmdr
        for i in range(game.state.rembase):
-           basetbl.append((i, distance(game.state.baseq[i], sc)))
+           basetbl.append((i, (game.state.baseq[i] - sc).distance()))
        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
@@ -1070,41 +1049,40 @@ def supercommander():
     return
 
 def movetholian():
-    # move the Tholian 
+    # move the Tholian
     if not game.tholian or game.justin:
        return
+    id = coord()
     if game.tholian.kloc.x == 0 and game.tholian.kloc.y == 0:
-       idx = 0; idy = QUADSIZE-1
+       id.x = 0; id.y = QUADSIZE-1
     elif game.tholian.kloc.x == 0 and game.tholian.kloc.y == QUADSIZE-1:
-       idx = QUADSIZE-1; idy = QUADSIZE-1
+       id.x = QUADSIZE-1; id.y = QUADSIZE-1
     elif game.tholian.kloc.x == QUADSIZE-1 and game.tholian.kloc.y == QUADSIZE-1:
-       idx = QUADSIZE-1; idy = 0
+       id.x = QUADSIZE-1; id.y = 0
     elif game.tholian.kloc.x == QUADSIZE-1 and game.tholian.kloc.y == 0:
-       idx = 0; idy = 0
+       id.x = 0; id.y = 0
     else:
        # something is wrong! 
-       game.tholian = None
+       game.tholian.move(None)
+        prout("***Internal error: Tholian in a bad spot.")
        return
     # do nothing if we are blocked 
-    if game.quad[idx][idy]!= IHDOT and game.quad[idx][idy]!= IHWEB:
+    if game.quad[id.x][id.y] not in (IHDOT, IHWEB):
        return
-    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.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.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
+    here = copy.copy(game.tholian.kloc)
+    delta = (id - game.tholian.kloc).sgn()
+    # move in x axis 
+    while here.x != id.x:
+        #print "Moving in X", delta
+        here.x += delta.x
+        if game.quad[here.x][here.y]==IHDOT:
+            game.tholian.move(here)
+    # move in y axis 
+    while here.y != id.y:
+        #print "Moving in Y", delta
+        here.y += delta.y
+        if game.quad[here.x][here.y]==IHDOT:
+            game.tholian.move(here)
     # check to see if all holes plugged 
     for i in range(QUADSIZE):
        if game.quad[0][i]!=IHWEB and game.quad[0][i]!=IHT:
@@ -1121,6 +1099,7 @@ def movetholian():
     crmena(True, IHT, "sector", game.tholian)
     prout(_(" completes web."))
     game.tholian.move(None)
+    print "Tholian movement ends"
     return
 
 # Code from battle.c begins here
@@ -1132,18 +1111,17 @@ def doshield(shraise):
     if shraise:
        action = "SHUP"
     else:
-       key = scan()
+       key = scanner.next()
        if key == IHALPHA:
-           if isit("transfer"):
+           if scanner.sees("transfer"):
                action = "NRG"
            else:
-               chew()
                if damaged(DSHIELD):
                    prout(_("Shields damaged and down."))
                    return
-               if isit("up"):
+               if scanner.sees("up"):
                    action = "SHUP"
-               elif isit("down"):
+               elif scanner.sees("down"):
                    action = "SHDN"
        if action=="NONE":
            proutn(_("Do you wish to change shield energy? "))
@@ -1158,14 +1136,14 @@ def doshield(shraise):
                if ja() == True:
                    action = "SHDN"
                else:
-                   chew()
+                   scanner.chew()
                    return
            else:
                proutn(_("Shields are down. Do you want them up? "))
                if ja() == True:
                    action = "SHUP"
                else:
-                   chew()
+                   scanner.chew()
                    return    
     if action == "SHUP": # raise shields 
        if game.shldup:
@@ -1193,24 +1171,24 @@ def doshield(shraise):
        game.ididit = True
        return
     elif action == "NRG":
-       while scan() != IHREAL:
-           chew()
+       while scanner.next() != IHREAL:
+           scanner.chew()
            proutn(_("Energy to transfer to shields- "))
-       chew()
-       if aaitem == 0:
+       scanner.chew()
+       if scanner.real == 0:
            return
-       if aaitem > game.energy:
+       if scanner.real > game.energy:
            prout(_("Insufficient ship energy."))
            return
        game.ididit = True
-       if game.shield+aaitem >= game.inshld:
+       if game.shield+scanner.real >= game.inshld:
            prout(_("Shield energy maximized."))
-           if game.shield+aaitem > game.inshld:
+           if game.shield+scanner.real > game.inshld:
                prout(_("Excess energy requested returned to ship energy"))
            game.energy -= game.inshld-game.shield
            game.shield = game.inshld
            return
-       if aaitem < 0.0 and game.energy-aaitem > game.inenrg:
+       if scanner.real < 0.0 and game.energy-scanner.real > game.inenrg:
            # Prevent shield drain loophole 
            skip(1)
            prout(_("Engineering to bridge--"))
@@ -1218,18 +1196,18 @@ def doshield(shraise):
            prout(_("  I can't drain the shields."))
            game.ididit = False
            return
-       if game.shield+aaitem < 0:
+       if game.shield+scanner.real < 0:
            prout(_("All shield energy transferred to ship."))
            game.energy += game.shield
            game.shield = 0.0
            return
        proutn(_("Scotty- \""))
-       if aaitem > 0:
+       if scanner.real > 0:
            prout(_("Transferring energy to shields.\""))
        else:
            prout(_("Draining energy from shields.\""))
-       game.shield += aaitem
-       game.energy -= aaitem
+       game.shield += scanner.real
+       game.energy -= scanner.real
        return
 
 def randdevice():
@@ -1284,24 +1262,24 @@ 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."))
@@ -1331,16 +1309,16 @@ def ram(ibumpd, ienm, w):
        finish(FWON)
     return
 
-def torpedo(course, r, incoming, i, n):
+def torpedo(course, dispersion, origin, number, nburst):
     # let a photon torpedo fly 
     iquad = 0
     shoved = False
-    ac = course + 0.25*r
+    ac = course + 0.25*dispersion
     angle = (15.0-ac)*0.5235988
     bullseye = (15.0 - course)*0.5235988
     deltax = -math.sin(angle);
     deltay = math.cos(angle);
-    x = incoming.x; y = incoming.y
+    x = origin.x; y = origin.y
     w = coord(); jw = coord()
     w.x = w.y = jw.x = jw.y = 0
     bigger = max(math.fabs(deltax), math.fabs(deltay))
@@ -1351,7 +1329,7 @@ def torpedo(course, r, incoming, i, n):
     else: 
        setwnd(message_window)
     # Loop to move a single torpedo 
-    for l in range(1, 15+1):
+    for step in range(1, 15+1):
        x += deltax
        w.x = int(x + 0.5)
        y += deltay
@@ -1359,7 +1337,7 @@ def torpedo(course, r, incoming, i, n):
        if not VALID_SECTOR(w.x, w.y):
            break
        iquad=game.quad[w.x][w.y]
-       tracktorpedo(w, l, i, n, iquad)
+       tracktorpedo(origin, w, step, number, nburst, iquad)
        if iquad==IHDOT:
            continue
        # hit something 
@@ -1372,7 +1350,7 @@ def torpedo(course, r, incoming, i, n):
            crmshp()
            prout(".")
            hit = 700.0 + randreal(100) - \
-               1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
            newcnd(); # we're blown out of dock 
            # We may be displaced. 
            if game.landed or game.condition=="docked":
@@ -1409,7 +1387,7 @@ def torpedo(course, r, incoming, i, n):
                    break
            kp = math.fabs(e.kpower)
            h1 = 700.0 + randrange(100) - \
-               1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
            h1 = math.fabs(h1)
            if kp < h1:
                h1 = kp
@@ -1428,8 +1406,8 @@ def torpedo(course, r, incoming, i, n):
                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):
                prout(_(" damaged but not destroyed."))
                return
@@ -1467,7 +1445,7 @@ def torpedo(course, r, incoming, i, n):
            game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
-           invalidate(game.plnet)
+           game.plnet.invalidate()
            game.quad[w.x][w.y] = IHDOT
            if game.landed:
                # captain perishes on planet 
@@ -1480,7 +1458,7 @@ def torpedo(course, r, incoming, i, n):
            game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
-           invalidate(game.plnet)
+           game.plnet.invalidate()
            game.quad[w.x][w.y] = IHDOT
            if game.landed:
                # captain perishes on planet 
@@ -1526,7 +1504,7 @@ def torpedo(course, r, incoming, i, n):
            return None
        elif iquad == IHT:  # Hit a Tholian 
            h1 = 700.0 + randrange(100) - \
-               1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
            h1 = math.fabs(h1)
            if h1 >= 600:
                game.quad[w.x][w.y] = IHDOT
@@ -1557,7 +1535,7 @@ def torpedo(course, r, incoming, i, n):
        game.quad[jw.x][jw.y]=iquad
        prout(_(" displaced by blast to Sector %s ") % jw)
        for ll in range(len(game.enemies)):
-           game.enemies[ll].kdist = game.enemies[ll].kavgd = distance(game.sector,game.enemies[ll].kloc)
+           game.enemies[ll].kdist = game.enemies[ll].kavgd = (game.sector-game.enemies[ll].kloc).distance()
         game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
        return None
     skip(1)
@@ -1585,7 +1563,7 @@ def fry(hit):
     for (i, j) in enumerate(cdam):
        proutn(device[j])
         if skipcount % 3 == 2 and i < len(cdam)-1:
-            skip()
+            skip(1)
         skipcount += 1
         if i < len(cdam)-1:
             proutn(_(" and "))
@@ -1649,7 +1627,7 @@ def attack(torps_ok):
            if game.condition == "docked":
                continue; # Don't waste the effort! 
            attempt = True; # Attempt to attack 
-           dustfac = 0.8 + randreal(0.5)
+           dustfac = randreal(0.8, 0.85)
            hit = enemy.kpower*math.pow(dustfac,enemy.kavgd)
            enemy.kpower *= 0.75
        else: # Enemy uses photon torpedo 
@@ -1661,9 +1639,9 @@ def attack(torps_ok):
                crmena(False, enemy.type, where, enemy.kloc)
            attempt = True
            prout("  ")
-           r = (randreal()+randreal())*0.5 - 0.5
-           r += 0.002*enemy.kpower*r
-           hit = torpedo(course, r, enemy.kloc, 1, 1)
+           dispersion = (randreal()+randreal())*0.5 - 0.5
+           dispersion += 0.002*enemy.kpower*dispersion
+           hit = torpedo(course, dispersion, origin=enemy.kloc, number=1, nburst=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:
@@ -1729,7 +1707,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 = randrange(hittot * 0.015)
+       icas = randrange(int(hittot * 0.015))
        if icas >= 2:
            skip(1)
            prout(_("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties") % icas)
@@ -1798,97 +1776,101 @@ def deadkl(w, type, mv):
            break
     return
 
-def targetcheck(x, y):
+def targetcheck(w):
     # Return None if target is invalid 
-    if not VALID_SECTOR(x, y):
+    if not VALID_SECTOR(w.x, w.y):
        huh()
        return None
-    deltx = 0.1*(y - game.sector.y)
-    delty = 0.1*(x - game.sector.x)
+    deltx = 0.1*(w.y - game.sector.y)
+    delty = 0.1*(w.x - game.sector.x)
     if deltx==0 and delty== 0:
        skip(1)
        prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
        prout(_("  I recommend an immediate review of"))
        prout(_("  the Captain's psychological profile.\""))
-       chew()
+       scanner.chew()
        return None
     return 1.90985932*math.atan2(deltx, delty)
 
 def photon():
-    # launch photon torpedo 
+    # launch photon torpedo
+    course = [0.0] * MAXBURST
     game.ididit = False
     if damaged(DPHOTON):
        prout(_("Photon tubes damaged."))
-       chew()
+       scanner.chew()
        return
     if game.torps == 0:
        prout(_("No torpedoes left."))
-       chew()
+       scanner.chew()
        return
-    key = scan()
+    key = scanner.next()
     while True:
        if key == IHALPHA:
            huh()
            return
        elif key == IHEOL:
            prout(_("%d torpedoes left.") % game.torps)
+            scanner.chew()
            proutn(_("Number of torpedoes to fire- "))
-           key = scan()
-       else: # key == IHREAL  {
-           n = aaitem + 0.5
+           key = scanner.next()
+       else: # key == IHREAL
+           n = scanner.int()
            if n <= 0: # abort command 
-               chew()
+               scanner.chew()
                return
-           if n > 3:
-               chew()
-               prout(_("Maximum of 3 torpedoes per burst."))
+           if n > MAXBURST:
+               scanner.chew()
+               prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
                key = IHEOL
                return
            if n <= game.torps:
                break
-           chew()
+           scanner.chew()
            key = IHEOL
-    for i in range(1, n+1):
-       key = scan()
-       if i==1 and key == IHEOL:
+    targ = []
+    for i in range(MAXBURST):
+        targ.append(coord())
+    for i in range(n):
+       key = scanner.next()
+       if i==0 and key == IHEOL:
            break;      # we will try prompting 
-       if i==2 and key == IHEOL:
+       if i==1 and key == IHEOL:
            # direct all torpedoes at one target 
            while i <= n:
-               targ[i][1] = targ[1][1]
-               targ[i][2] = targ[1][2]
-               course[i] = course[1]
+               targ[i] = targ[0]
+               course[i] = course[0]
                i += 1
            break
        if key != IHREAL:
            huh()
            return
-       targ[i][1] = aaitem
-       key = scan()
+       targ[i].x = scanner.int()
+       key = scanner.next()
        if key != IHREAL:
            huh()
            return
-       targ[i][2] = aaitem
-       course[i] = targetcheck(targ[i][1], targ[i][2])
+       targ[i].y = scanner.int()
+       course[i] = targetcheck(targ[i])
         if course[i] == None:
            return
-    chew()
-    if i == 1 and key == IHEOL:
+    scanner.chew()
+    if i == 0 and key == IHEOL:
        # prompt for each one 
-       for i in range(1, n+1):
-           proutn(_("Target sector for torpedo number %d- ") % i)
-           key = scan()
+       for i in range(n):
+           proutn(_("Target sector for torpedo number %d- ") % (i+1))
+           key = scanner.next()
            if key != IHREAL:
                huh()
                return
-           targ[i][1] = int(aaitem-0.5)
-           key = scan()
+           targ[i].x = scanner.int()
+           key = scanner.next()
            if key != IHREAL:
                huh()
                return
-           targ[i][2] = int(aaitem-0.5)
-           chew()
-            course[i] = targetcheck(targ[i][1], targ[i][2])
+           targ[i].y = scanner.int()
+           scanner.chew()
+            course[i] = targetcheck(targ[i])
             if course[i] == None:
                 return
     game.ididit = True
@@ -1896,10 +1878,10 @@ def photon():
     for i in range(n):
        if game.condition != "docked":
            game.torps -= 1
-       r = (randreal()+randreal())*0.5 -0.5
-       if math.fabs(r) >= 0.47:
+       dispersion = (randreal()+randreal())*0.5 -0.5
+       if math.fabs(dispersion) >= 0.47:
            # misfire! 
-           r *= randreal(1.2, 2.2)
+           dispersion *= randreal(1.2, 2.2)
            if n > 0:
                prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
            else:
@@ -1912,8 +1894,8 @@ def photon():
                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
-       torpedo(course[i], r, game.sector, i, n)
+           dispersion *= 1.0 + 0.0001*game.shield
+       torpedo(course[i], dispersion, origin=game.sector, number=i, nburst=n)
        if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
            return
     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
@@ -1948,7 +1930,7 @@ def checkshctrl(rpow):
     prouts(_("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\""))
     skip(2)
     prout(_("Lt. Uhura-  \"Sir, all decks reporting damage.\""))
-    icas = randrange(hit*0.012)
+    icas = randrange(int(hit*0.012))
     skip(1)
     fry(0.8*hit)
     if icas:
@@ -1965,9 +1947,10 @@ def checkshctrl(rpow):
 
 def hittem(hits):
     # register a phaser hit on Klingons and Romulans
-    nenhr2 = game.nenhere; kk=0
+    nenhr2 = len(game.enemies); kk=0
     w = coord()
     skip(1)
+    print "Hits are:", hits
     for (k, wham) in enumerate(hits):
        if wham==0:
            continue
@@ -2018,57 +2001,57 @@ def phasers():
     automode = "NOTSET"
     key=0
     skip(1)
-    # SR sensors and Computer are needed fopr automode 
+    # SR sensors and Computer are needed for automode 
     if damaged(DSRSENS) or damaged(DCOMPTR):
        itarg = False
     if game.condition == "docked":
        prout(_("Phasers can't be fired through base shields."))
-       chew()
+       scanner.chew()
        return
     if damaged(DPHASER):
        prout(_("Phaser control damaged."))
-       chew()
+       scanner.chew()
        return
     if game.shldup:
        if damaged(DSHCTRL):
            prout(_("High speed shield control damaged."))
-           chew()
+           scanner.chew()
            return
        if game.energy <= 200.0:
            prout(_("Insufficient energy to activate high-speed shield control."))
-           chew()
+           scanner.chew()
            return
        prout(_("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\""))
        ifast = True
     # Original code so convoluted, I re-did it all
     # (That was Tom Almy talking about the C code, I think -- ESR)
     while automode=="NOTSET":
-       key=scan()
+       key=scanner.next()
        if key == IHALPHA:
-           if isit("manual"):
-               if game.nenhere==0:
+           if scanner.sees("manual"):
+               if len(game.enemies)==0:
                    prout(_("There is no enemy present to select."))
-                   chew()
+                   scanner.chew()
                    key = IHEOL
                    automode="AUTOMATIC"
                else:
                    automode = "MANUAL"
-                   key = scan()
-           elif isit("automatic"):
-               if (not itarg) and game.nenhere != 0:
+                   key = scanner.next()
+           elif scanner.sees("automatic"):
+               if (not itarg) and len(game.enemies) != 0:
                    automode = "FORCEMAN"
                else:
-                   if game.nenhere==0:
+                   if len(game.enemies)==0:
                        prout(_("Energy will be expended into space."))
                    automode = "AUTOMATIC"
-                   key = scan()
-           elif isit("no"):
+                   key = scanner.next()
+           elif scanner.sees("no"):
                no = True
            else:
                huh()
                return
        elif key == IHREAL:
-           if game.nenhere==0:
+           if len(game.enemies)==0:
                prout(_("Energy will be expended into space."))
                automode = "AUTOMATIC"
            elif not itarg:
@@ -2077,37 +2060,37 @@ def phasers():
                automode = "AUTOMATIC"
        else:
            # IHEOL 
-           if game.nenhere==0:
+           if len(game.enemies)==0:
                prout(_("Energy will be expended into space."))
                automode = "AUTOMATIC"
            elif not itarg:
                automode = "FORCEMAN"
            else: 
                proutn(_("Manual or automatic? "))
-                chew()
+                scanner.chew()
     avail = game.energy
     if ifast:
         avail -= 200.0
     if automode == "AUTOMATIC":
-       if key == IHALPHA and isit("no"):
+       if key == IHALPHA and scanner.sees("no"):
            no = True
-           key = scan()
-       if key != IHREAL and game.nenhere != 0:
+           key = scanner.next()
+       if key != IHREAL and len(game.enemies) != 0:
            prout(_("Phasers locked on target. Energy available: %.2f")%avail)
        irec=0
         while True:
-           chew()
+           scanner.chew()
            if not kz:
-               for i in range(game.nenhere):
+               for i in range(len(game.enemies)):
                    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()
+           scanner.chew()
            proutn(_("Units to fire= "))
-           key = scan()
+           key = scanner.next()
            if key!=IHREAL:
                return
-           rpow = aaitem
+           rpow = scanner.real
            if rpow > avail:
                proutn(_("Energy available= %.2f") % avail)
                skip(1)
@@ -2116,22 +2099,22 @@ def phasers():
                 break
        if rpow<=0:
            # chicken out 
-           chew()
+           scanner.chew()
            return
-        key=scan()
-       if key == IHALPHA and isit("no"):
+        key=scanner.next()
+       if key == IHALPHA and scanner.sees("no"):
            no = True
        if ifast:
            game.energy -= 200; # Go and do it! 
            if checkshctrl(rpow):
                return
-       chew()
+       scanner.chew()
        game.energy -= rpow
        extra = rpow
-       if game.nenhere:
+       if len(game.enemies):
            extra = 0.0
            powrem = rpow
-           for i in range(game.nenhere):
+           for i in range(len(game.enemies)):
                hits.append(0.0)
                if powrem <= 0:
                    continue
@@ -2151,13 +2134,13 @@ def phasers():
        if extra > 0 and not game.alldone:
            if game.tholian:
                proutn(_("*** Tholian web absorbs "))
-               if game.nenhere>0:
+               if len(game.enemies)>0:
                    proutn(_("excess "))
                prout(_("phaser energy."))
            else:
                prout(_("%d expended on empty space.") % int(extra))
     elif automode == "FORCEMAN":
-       chew()
+       scanner.chew()
        key = IHEOL
        if damaged(DCOMPTR):
            prout(_("Battle computer damaged, manual fire only."))
@@ -2171,7 +2154,7 @@ def phasers():
            skip(1)
     elif automode == "MANUAL":
        rpow = 0.0
-        for k in range(game.nenhere):
+        for k in range(len(game.enemies)):
            aim = game.enemies[k].kloc
            ienm = game.quad[aim.x][aim.y]
            if msgflag:
@@ -2179,17 +2162,17 @@ def phasers():
                skip(1)
                msgflag = False
                rpow = 0.0
-           if damaged(DSRSENS) and not (abs(game.sector.x-aim.x) < 2 and abs(game.sector.y-aim.y) < 2) and \
-               (ienm == IHC or ienm == IHS):
+           if damaged(DSRSENS) and \
+               not game.sector.distance(aim)<2**0.5 and ienm in (IHC, IHS):
                cramen(ienm)
                prout(_(" can't be located without short range scan."))
-               chew()
+               scanner.chew()
                key = IHEOL
                hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko 
                k += 1
                continue
            if key == IHEOL:
-               chew()
+               scanner.chew()
                if itarg and k > kz:
                    irec=(abs(game.enemies[k].kpower)/(PHASEFAC*math.pow(0.9,game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
                kz = k
@@ -2202,10 +2185,10 @@ def phasers():
                proutn(_("units to fire at "))
                crmena(False, ienm, "sector", aim)
                proutn("-  ")
-               key = scan()
-           if key == IHALPHA and isit("no"):
+               key = scanner.next()
+           if key == IHALPHA and scanner.sees("no"):
                no = True
-               key = scan()
+               key = scanner.next()
                continue
            if key == IHALPHA:
                huh()
@@ -2214,27 +2197,27 @@ def phasers():
                if k==1: # Let me say I'm baffled by this 
                    msgflag = True
                continue
-           if aaitem < 0:
+           if scanner.real < 0:
                # abort out 
-               chew()
+               scanner.chew()
                return
-           hits[k] = aaitem
-           rpow += aaitem
+           hits[k] = scanner.real
+           rpow += scanner.real
            # If total requested is too much, inform and start over 
             if rpow > avail:
                prout(_("Available energy exceeded -- try again."))
-               chew()
+               scanner.chew()
                return
-           key = scan(); # scan for next value 
+           key = scanner.next(); # scan for next value 
            k += 1
        if rpow == 0.0:
            # zero energy -- abort 
-           chew()
+           scanner.chew()
            return
-       if key == IHALPHA and isit("no"):
+       if key == IHALPHA and scanner.sees("no"):
            no = True
        game.energy -= rpow
-       chew()
+       scanner.chew()
        if ifast:
            game.energy -= 200.0
            if checkshctrl(rpow):
@@ -2351,7 +2334,7 @@ def events():
         newqad(False)
         # Adjust finish time to time of tractor beaming 
         fintim = game.state.date+game.optime
-        attack(False)
+        attack(torps_ok=False)
         if game.state.remcom <= 0:
             unschedule(FTBEAM)
         else: 
@@ -2390,7 +2373,7 @@ def events():
             game.battle = hold
             game.isatb = 0
         else:
-            invalidate(game.battle)
+            game.battle.invalidate()
 
     if idebug:
        prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
@@ -2487,7 +2470,7 @@ def events():
                  (game.torps < 5 or damaged(DPHOTON))):
                # Tractor-beam her! 
                istract = ictbeam = True
-                tractorbeam(distance(game.state.kscmdr, game.quadrant))
+                tractorbeam((game.state.kscmdr-game.quadrant).distance())
            else:
                return
        elif evcode == FTBEAM: # Tractor beam 
@@ -2495,7 +2478,7 @@ def events():
                 unschedule(FTBEAM)
                 continue
             i = randrange(game.state.remcom)
-            yank = distance(game.state.kcmdr[i], game.quadrant)
+            yank = (game.state.kcmdr[i]-game.quadrant).distance()
             if istract or game.condition == "docked" or yank == 0:
                 # Drats! Have to reschedule 
                 schedule(FTBEAM, 
@@ -2562,7 +2545,7 @@ def events():
                if i > game.state.remcom or game.state.rembase == 0 or \
                    not game.state.galaxy[game.battle.x][game.battle.y].starbase:
                    # No action to take after all 
-                   invalidate(game.battle)
+                   game.battle.invalidate()
                    continue
             destroybase()
        elif evcode == FSCMOVE: # Supercommander moves 
@@ -2714,18 +2697,18 @@ def wait():
     # wait on events 
     game.ididit = False
     while True:
-       key = scan()
+       key = scanner.next()
        if key  != IHEOL:
            break
        proutn(_("How long? "))
-    chew()
+    scanner.chew()
     if key != IHREAL:
        huh()
        return
-    origTime = delay = aaitem
+    origTime = delay = scanner.real
     if delay <= 0.0:
        return
-    if delay >= game.state.remtime or game.nenhere != 0:
+    if delay >= game.state.remtime or len(game.enemies) != 0:
        proutn(_("Are you sure? "))
        if ja() == False:
            return
@@ -2738,13 +2721,13 @@ def wait():
            prout(_("%d stardates left.") % int(game.state.remtime))
            return
        temp = game.optime = delay
-       if game.nenhere:
+       if len(game.enemies):
            rtime = randreal(1.0, 2.0)
            if rtime < temp:
                temp = rtime
            game.optime = temp
        if game.optime < delay:
-           attack(False)
+           attack(torps_ok=False)
        if game.alldone:
            return
        events()
@@ -2822,7 +2805,7 @@ def nova(nov):
                     prout(_(" destroyed."))
                     game.iplnet.pclass = "destroyed"
                     game.iplnet = None
-                    invalidate(game.plnet)
+                    game.plnet.invalidate()
                     if game.landed:
                         finish(FPNOVA)
                         return
@@ -2834,7 +2817,7 @@ def nova(nov):
                             break
                     game.state.baseq[i] = game.state.baseq[game.state.rembase]
                     game.state.rembase -= 1
-                    invalidate(game.base)
+                    game.base.invalidate()
                     game.state.basekl += 1
                     newcnd()
                     crmena(True, IHB, "sector", neighbor)
@@ -2862,7 +2845,7 @@ def nova(nov):
                 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):
+                    for ll in range(len(game.enemies)):
                         if game.enemies[ll].kloc == neighbor:
                             break
                     game.enemies[ll].kpower -= 800.0 # If firepower is lost, die 
@@ -2955,13 +2938,12 @@ def supernova(induced, w=None):
        prouts(_("***RED ALERT!  RED ALERT!"))
        skip(1)
        prout(_("***Incipient supernova detected at Sector %s") % ns)
-       if square(ns.x-game.sector.x) + square(ns.y-game.sector.y) <= 2.1:
+       if (ns.x-game.sector.x)**2 + (ns.y-game.sector.y)**2 <= 2.1:
            proutn(_("Emergency override attempts t"))
            prouts("***************")
            skip(1)
            stars()
            game.alldone = True
-
     # destroy any Klingons in supernovaed quadrant 
     kldead = game.state.galaxy[nq.x][nq.y].klingons
     game.state.galaxy[nq.x][nq.y].klingons = 0
@@ -2971,17 +2953,13 @@ def supernova(induced, w=None):
        game.iscate = False
        unschedule(FSCMOVE)
        unschedule(FSCDBAS)
-    if game.state.remcom:
-       maxloop = game.state.remcom
-       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])
-               game.state.remcom -= 1
-               kldead -= 1
-               if game.state.remcom==0:
-                   unschedule(FTBEAM)
-               break
+    survivors = filter(lambda w: w != nq, game.state.kcmdr)
+    comkills = len(game.state.kcmdr) - len(survivors)
+    game.state.kcmdr = survivors
+    kldead -= comkills
+    game.state.remcom -= comkills
+    if game.state.remcom==0:
+        unschedule(FTBEAM)
     game.state.remkl -= kldead
     # destroy Romulans and planets in supernovaed quadrant 
     nrmdead = game.state.galaxy[nq.x][nq.y].romulans
@@ -2992,15 +2970,9 @@ def supernova(induced, w=None):
        if game.state.planets[loop].w == nq:
            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(maxloop):
-           if game.state.baseq[loop] == nq:
-               game.state.baseq[loop] = game.state.baseq[game.state.rembase]
-               invalidate(game.state.baseq[game.state.rembase])
-               game.state.rembase -= 1
-               break
+    # Destroy any base in supernovaed quadrant
+    game.state.baseq = filter(lambda x: x != nq, game.state.baseq)
+    game.state.rembase = len(game.state.baseq)
     # If starship caused supernova, tally up destruction 
     if induced:
        game.state.starkl += game.state.galaxy[nq.x][nq.y].stars
@@ -3027,7 +2999,7 @@ def supernova(induced, w=None):
 def selfdestruct():
     # self-destruct maneuver 
     # Finish with a BANG! 
-    chew()
+    scanner.chew()
     if damaged(DCOMPTR):
        prout(_("Computer damaged; cannot execute destruct sequence."))
        return
@@ -3045,9 +3017,9 @@ def selfdestruct():
     skip(1)
     prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
     skip(1)
-    scan()
-    chew()
-    if game.passwd != citem:
+    scanner.next()
+    scanner.chew()
+    if game.passwd != scanner.token:
        prouts(_("PASSWORD-REJECTED;"))
        skip(1)
        prouts(_("CONTINUITY-EFFECTED"))
@@ -3074,10 +3046,10 @@ def kaboom():
     skip(1)
     stars()
     skip(1)
-    if game.nenhere != 0:
+    if len(game.enemies) != 0:
        whammo = 25.0 * game.energy
        l=1
-       while l <= game.nenhere:
+       while l <= len(game.enemies):
            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
@@ -3165,7 +3137,7 @@ def finish(ifin):
                        prout(_("You cannot get a citation, so..."))
                    else:
                        proutn(_("Do you want your Commodore Emeritus Citation printed? "))
-                       chew()
+                       scanner.chew()
                        if ja() == True:
                            igotit = True
            # Only grant long life if alive (original didn't!)
@@ -3517,6 +3489,7 @@ def iostart():
        #noecho()
         global fullscreen_window, srscan_window, report_window, status_window
         global lrscan_window, message_window, prompt_window
+        (rows, columns)   = stdscr.getmaxyx()
        fullscreen_window = stdscr
        srscan_window     = curses.newwin(12, 25, 0,       0)
        report_window     = curses.newwin(11, 0,  1,       25)
@@ -3528,8 +3501,16 @@ def iostart():
        setwnd(fullscreen_window)
        textcolor(DEFAULT)
 
+def textcolor(color):
+    "Set text foreground color.  Presently a stub."
+    pass       # FIXME
+
+def ioend():
+    "Wrap up I/O.  Presently a stub."
+    pass
+
 def waitfor():
-    "wait for user action -- OK to do nothing if on a TTY"
+    "Wait for user action -- OK to do nothing if on a TTY"
     if game.options & OPTION_CURSES:
        stsdcr.getch()
 
@@ -3627,9 +3608,9 @@ def cgetline():
                 elif line[0] != "#":
                     break
        else:
-           line = raw_input()
+           line = raw_input() + "\n"
     if logfp:
-       logfp.write("$" + line + "\n")
+       logfp.write(line)
     return line
 
 def setwnd(wnd):
@@ -3653,45 +3634,7 @@ def clrscr():
        curwnd.move(0, 0)
        curwnd.refresh()
     linecount = 0
-
-def textcolor(color):
-    "Set the current text color"
-    if game.options & OPTION_CURSES:
-       if color == DEFAULT: 
-           curwnd.attrset(0)
-       elif color == BLACK: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLACK))
-       elif color == BLUE: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLUE))
-       elif color == GREEN: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_GREEN))
-       elif color == CYAN: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_CYAN))
-       elif color == RED: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_RED))
-       elif color == MAGENTA: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_MAGENTA))
-       elif color == BROWN: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_YELLOW))
-       elif color == LIGHTGRAY: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_WHITE))
-       elif color == DARKGRAY: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLACK) | curses.A_BOLD)
-       elif color == LIGHTBLUE: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLUE) | curses.A_BOLD)
-       elif color == LIGHTGREEN: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_GREEN) | curses.A_BOLD)
-       elif color == LIGHTCYAN: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_CYAN) | curses.A_BOLD)
-       elif color == LIGHTRED: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_RED) | curses.A_BOLD)
-       elif color == LIGHTMAGENTA: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_MAGENTA) | curses.A_BOLD)
-       elif color == YELLOW: 
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_YELLOW) | curses.A_BOLD)
-       elif color == WHITE:
-           curwnd.attron(curses.COLOR_PAIR(curses.COLOR_WHITE) | curses.A_BOLD)
-
+    
 def highvideo():
     "Set highlight video, if this is reasonable."
     if game.options & OPTION_CURSES:
@@ -3755,27 +3698,27 @@ def warble():
        #nosound()
         pass
 
-def tracktorpedo(w, l, i, n, iquad):
+def tracktorpedo(origin, w, step, i, n, iquad):
     "Torpedo-track animation." 
     if not game.options & OPTION_CURSES:
-       if l == 1:
+       if step == 1:
            if n != 1:
                skip(1)
-               proutn(_("Track for torpedo number %d-  ") % i)
+               proutn(_("Track for %s torpedo number %d-  ") % (game.quad[origin.x][origin.y],i+1))
            else:
                skip(1)
                proutn(_("Torpedo track- "))
-       elif l==4 or l==9: 
+       elif step==4 or step==9: 
            skip(1)
-       proutn("%d - %d   " % (w.x, w.y))
+       proutn("%s   " % w)
     else:
        if not damaged(DSRSENS) or game.condition=="docked":
-           if i != 1 and l == 1:
+           if i != 0 and step == 1:
                drawmaps(2)
                time.sleep(0.4)
            if (iquad==IHDOT) or (iquad==IHBLANK):
                put_srscan_sym(w, '+')
-               #sound(l*10)
+               #sound(step*10)
                #time.sleep(0.1)
                #nosound()
                put_srscan_sym(w, iquad)
@@ -3788,7 +3731,7 @@ def tracktorpedo(w, l, i, n, iquad):
                curwnd.attroff(curses.A_REVERSE)
                put_srscan_sym(w, iquad)
        else:
-           proutn("%d - %d   " % (w.x, w.y))
+           proutn("%s   " % w)
 
 def makechart():
     "Display the current galaxy chart."
@@ -3823,16 +3766,16 @@ def imove(novapush):
     def no_quad_change():
         # 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(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
+        if game.enemies:
+            for enemy in game.enemies:
+                finald = (w-game.enemy.kloc).distance()
+                enemy.kavgd = 0.5 * (finald + ememy.kdist)
+                enemy.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(game.nenhere):
-                game.enemies[m].kavgd = game.enemies[m].kdist
+                attack(torps_ok=False)
+            for enemy in game.enemies:
+                enemy.kavgd = enemy.kdist
         newcnd()
         drawmaps(0)
         setwnd(message_window)
@@ -3869,18 +3812,18 @@ def imove(novapush):
            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:
+               if len(game.enemies) != 0 and not novapush:
                    newcnd()
-                   for m in range(game.nenhere):
-                       finald = distance(w, game.enemies[m].kloc)
-                       game.enemies[m].kavgd = 0.5 * (finald + game.enemies[m].kdist)
+                   for enemy in game.enemies:
+                       finald = (w - enemy.kloc).distance()
+                       enemy.kavgd = 0.5 * (finald + enemy.kdist)
                    #
                    # Stas Sergeev added the condition
                    # that attacks only happen if Klingons
                    # are present and your skill is good.
                    # 
                    if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
-                       attack(False)
+                       attack(torps_ok=False)
                    if game.alldone:
                        return
                # compute final position -- new quadrant and sector 
@@ -3930,16 +3873,19 @@ def imove(novapush):
                game.quad[game.sector.x][game.sector.y] = game.ship
                newqad(False)
                if game.skill>SKILL_NOVICE:
-                   attack(False)  
+                   attack(torps_ok=False)  
                return
            iquad = game.quad[w.x][w.y]
            if iquad != IHDOT:
                # object encountered in flight path 
                stopegy = 50.0*game.dist/game.optime
-               game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
+               game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
                 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)
@@ -3983,7 +3929,7 @@ def imove(novapush):
                 # We're here!
                no_quad_change()
                 return
-       game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
+       game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
        game.sector = w
     final = game.sector
     no_quad_change()
@@ -3991,14 +3937,14 @@ def imove(novapush):
 
 def dock(verbose):
     # dock our ship at a starbase 
-    chew()
+    scanner.chew()
     if game.condition == "docked" and verbose:
        prout(_("Already docked."))
        return
     if game.inorbit:
        prout(_("You must first leave standard orbit."))
        return
-    if not is_valid(game.base) or abs(game.sector.x-game.base.x) > 1 or abs(game.sector.y-game.base.y) > 1:
+    if not game.base.is_valid() or abs(game.sector.x-game.base.x) > 1 or abs(game.sector.y-game.base.y) > 1:
        crmshp()
        prout(_(" not adjacent to base."))
        return
@@ -4038,7 +3984,7 @@ def getcourse(isprobe, akey):
     if game.landed and not isprobe:
        prout(_("Dummy! You can't leave standard orbit until you"))
        proutn(_("are back aboard the ship."))
-       chew()
+       scanner.chew()
        return False
     while navmode == "unspecified":
        if damaged(DNAVSYS):
@@ -4046,7 +3992,7 @@ def getcourse(isprobe, akey):
                prout(_("Computer damaged; manual navigation only"))
            else:
                prout(_("Computer damaged; manual movement only"))
-           chew()
+           scanner.chew()
            navmode = "manual"
            key = IHEOL
            break
@@ -4055,23 +4001,23 @@ def getcourse(isprobe, akey):
            key = akey
            akey = -1
        else: 
-           key = scan()
+           key = scanner.next()
        if key == IHEOL:
            proutn(_("Manual or automatic- "))
            iprompt = True
-           chew()
+           scanner.chew()
        elif key == IHALPHA:
-            if isit("manual"):
+            if scanner.sees("manual"):
                navmode = "manual"
-               key = scan()
+               key = scanner.next()
                break
-            elif isit("automatic"):
+            elif scanner.sees("automatic"):
                navmode = "automatic"
-               key = scan()
+               key = scanner.next()
                break
            else:
                huh()
-               chew()
+               scanner.chew()
                return False
        else: # numeric 
            if isprobe:
@@ -4086,27 +4032,27 @@ def getcourse(isprobe, akey):
                proutn(_("Target quadrant or quadrant&sector- "))
            else:
                proutn(_("Destination sector or quadrant&sector- "))
-           chew()
+           scanner.chew()
            iprompt = True
-           key = scan()
+           key = scanner.next()
        if key != IHREAL:
            huh()
            return False
-       xi = int(round(aaitem))-1
-       key = scan()
+       xi = int(round(scanner.real))-1
+       key = scanner.next()
        if key != IHREAL:
            huh()
            return False
-       xj = int(round(aaitem))-1
-       key = scan()
+       xj = int(round(scanner.real))-1
+       key = scanner.next()
        if key == IHREAL:
            # both quadrant and sector specified 
-           xk = int(round(aaitem))-1
-           key = scan()
+           xk = int(round(scanner.real))-1
+           key = scanner.next()
            if key != IHREAL:
                huh()
                return False
-           xl = int(round(aaitem))-1
+           xl = int(round(scanner.real))-1
            dquad.x = xi
            dquad.y = xj
            dsect.y = xk
@@ -4139,22 +4085,22 @@ def getcourse(isprobe, akey):
     else: # manual 
        while key == IHEOL:
            proutn(_("X and Y displacements- "))
-           chew()
+           scanner.chew()
            iprompt = True
-           key = scan()
+           key = scanner.next()
        itemp = "verbose"
        if key != IHREAL:
            huh()
            return False
-       deltax = aaitem
-       key = scan()
+       deltax = scanner.real
+       key = scanner.next()
        if key != IHREAL:
            huh()
            return False
-       deltay = aaitem
+       deltay = scanner.real
     # Check for zero movement 
     if deltax == 0 and deltay == 0:
-       chew()
+       scanner.chew()
        return False
     if itemp == "verbose" and not isprobe:
        skip(1)
@@ -4164,14 +4110,14 @@ def getcourse(isprobe, akey):
     game.direc = math.atan2(deltax, deltay)*1.90985932
     if game.direc < 0.0:
        game.direc += 12.0
-    chew()
+    scanner.chew()
     return True
 
 def impulse():
     # move under impulse power 
     game.ididit = False
     if damaged(DIMPULS):
-       chew()
+       scanner.chew()
        skip(1)
        prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
        return
@@ -4192,7 +4138,7 @@ def impulse():
            prout(_(" quadrants.\""))
        else:
            prout(_("quadrant.  They are, therefore, useless.\""))
-       chew()
+       scanner.chew()
        return
     # Make sure enough time is left for the trip 
     game.optime = game.dist/0.095
@@ -4220,12 +4166,12 @@ def warp(timewarp):
     if not timewarp: # Not WARPX entry 
        game.ididit = False
        if game.damage[DWARPEN] > 10.0:
-           chew()
+           scanner.chew()
            skip(1)
            prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
            return
        if damaged(DWARPEN) and game.warpfac > 4.0:
-           chew()
+           scanner.chew()
            skip(1)
            prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
            prout(_("  is repaired, I can only give you warp 4.\""))
@@ -4272,7 +4218,7 @@ def warp(timewarp):
     if game.warpfac > 6.0:
        # 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
+       prob = game.dist*(6.0-game.warpfac)**2/66.666666666
        if prob > randreal():
            blooey = True
            game.dist = randreal(game.dist)
@@ -4332,30 +4278,30 @@ def warp(timewarp):
 def setwarp():
     # change the warp factor   
     while True:
-        key=scan()
+        key=scanner.next()
         if key != IHEOL:
             break
-       chew()
+       scanner.chew()
        proutn(_("Warp factor- "))
-    chew()
+    scanner.chew()
     if key != IHREAL:
        huh()
        return
     if game.damage[DWARPEN] > 10.0:
        prout(_("Warp engines inoperative."))
        return
-    if damaged(DWARPEN) and aaitem > 4.0:
+    if damaged(DWARPEN) and scanner.real > 4.0:
        prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
        prout(_("  but right now we can only go warp 4.\""))
        return
-    if aaitem > 10.0:
+    if scanner.real > 10.0:
        prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
        return
-    if aaitem < 1.0:
+    if scanner.real < 1.0:
        prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
        return
     oldfac = game.warpfac
-    game.warpfac = aaitem
+    game.warpfac = scanner.real
     game.wfacsq=game.warpfac*game.warpfac
     if game.warpfac <= oldfac or game.warpfac <= 6.0:
        prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
@@ -4372,7 +4318,7 @@ def setwarp():
 
 def atover(igrab):
     # cope with being tossed out of quadrant by supernova or yanked by beam 
-    chew()
+    scanner.chew()
     # is captain on planet? 
     if game.landed:
        if damaged(DTRANSP):
@@ -4474,7 +4420,7 @@ def timwrp():
        game.isatb = 0
        unschedule(FCDBAS)
        unschedule(FSCDBAS)
-       invalidate(game.battle)
+       game.battle.invalidate()
 
        # Make sure Galileo is consistant -- Snapshot may have been taken
         # when on planet, which would give us two Galileos! 
@@ -4509,7 +4455,7 @@ def probe():
     # launch deep-space probe 
     # New code to launch a deep space probe 
     if game.nprobes == 0:
-       chew()
+       scanner.chew()
        skip(1)
        if game.ship == IHE: 
            prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
@@ -4517,12 +4463,12 @@ def probe():
            prout(_("Ye Faerie Queene has no deep space probes."))
        return
     if damaged(DDSP):
-       chew()
+       scanner.chew()
        skip(1)
        prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
        return
     if is_scheduled(FDSPROB):
-       chew()
+       scanner.chew()
        skip(1)
        if damaged(DRADIO) and game.condition != "docked":
            prout(_("Spock-  \"Records show the previous probe has not yet"))
@@ -4530,7 +4476,7 @@ def probe():
        else:
            prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
        return
-    key = scan()
+    key = scanner.next()
     if key == IHEOL:
        # slow mode, so let Kirk know how many probes there are left
         if game.nprobes == 1:
@@ -4541,9 +4487,9 @@ def probe():
        if ja() == False:
            return
     game.isarmed = False
-    if key == IHALPHA and citem == "armed":
+    if key == IHALPHA and scanner.token == "armed":
        game.isarmed = True
-       key = scan()
+       key = scanner.next()
     elif key == IHEOL:
        proutn(_("Arm NOVAMAX warhead? "))
        game.isarmed = ja()
@@ -4589,7 +4535,7 @@ def mayday():
     # yell for help from nearest starbase 
     # There's more than one way to move in this game! 
     line = 0
-    chew()
+    scanner.chew()
     # Test for conditions which prevent calling for help 
     if game.condition == "docked":
        prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
@@ -4609,11 +4555,11 @@ def mayday():
     game.nhelp += 1
     if game.base.x!=0:
        # There's one in this quadrant 
-       ddist = distance(game.base, game.sector)
+       ddist = (game.base - game.sector).distance()
     else:
        ddist = FOREVER
        for m in range(game.state.rembase):
-           xdist = QUADSIZE * distance(game.state.baseq[m], game.quadrant)
+           xdist = QUADSIZE * (game.state.baseq[m] - game.quadrant).distance()
            if xdist < ddist:
                ddist = xdist
                line = m
@@ -4632,7 +4578,7 @@ def mayday():
            # found one -- finish up 
             game.sector = w
            break
-    if not is_valid(game.sector):
+    if not game.sector.is_valid():
        prout(_("You have been lost in space..."))
        finish(FMATERIALIZE)
        return
@@ -4645,13 +4591,13 @@ def mayday():
        proutn(_(" attempt to re-materialize "))
        crmshp()
        game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
-       textcolor(RED)
+       textcolor("red")
        warble()
        if randreal() > probf:
            break
        prout(_("fails."))
        curses.delay_output(500)
-       textcolor(DEFAULT)
+       textcolor(None)
     if m > 3:
        game.quad[ix][iy]=IHQUEST
        game.alive = False
@@ -4660,9 +4606,9 @@ def mayday():
        finish(FMATERIALIZE)
        return
     game.quad[ix][iy]=game.ship
-    textcolor(GREEN)
+    textcolor("green")
     prout(_("succeeds."))
-    textcolor(DEFAULT)
+    textcolor(None)
     dock(False)
     skip(1)
     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
@@ -4687,7 +4633,7 @@ def mayday():
 
 def abandon():
     # abandon ship 
-    chew()
+    scanner.chew()
     if game.condition=="docked":
        if game.ship!=IHE:
            prout(_("You cannot abandon Ye Faerie Queene."))
@@ -4797,7 +4743,7 @@ def survey():
     # report on (uninhabited) planets in the galaxy 
     iknow = False
     skip(1)
-    chew()
+    scanner.chew()
     prout(_("Spock-  \"Planet report follows, Captain.\""))
     skip(1)
     for i in range(game.inplan):
@@ -4824,14 +4770,14 @@ def survey():
 def orbit():
     # enter standard orbit 
     skip(1)
-    chew()
+    scanner.chew()
     if game.inorbit:
        prout(_("Already in standard orbit."))
        return
     if damaged(DWARPEN) and damaged(DIMPULS):
        prout(_("Both warp and impulse engines damaged."))
        return
-    if not is_valid(game.plnet) or abs(game.sector.x-game.plnet.x) > 1 or abs(game.sector.y-game.plnet.y) > 1:
+    if not game.plnet.is_valid() or abs(game.sector.x-game.plnet.x) > 1 or abs(game.sector.y-game.plnet.y) > 1:
        crmshp()
        prout(_(" not adjacent to planet."))
        skip(1)
@@ -4876,7 +4822,7 @@ def sensor():
 def beam():
     # use the transporter 
     nrgneed = 0
-    chew()
+    scanner.chew()
     skip(1)
     if damaged(DTRANSP):
        prout(_("Transporter damaged."))
@@ -4903,7 +4849,7 @@ def beam():
        prout(_("  exploring a planet with no dilithium crystals."))
        proutn(_("  Are you sure this is wise?\" "))
        if ja() == False:
-           chew()
+           scanner.chew()
            return
     if not (game.options & OPTION_PLAIN):
        nrgneed = 50 * game.skill + game.height / 100.0
@@ -4919,14 +4865,14 @@ def beam():
                prout(_("  Although the Galileo shuttle craft may still be on a surface."))
            proutn(_("  Are you sure this is wise?\" "))
            if ja() == False:
-               chew()
+               scanner.chew()
                return
     if game.landed:
        # Coming from planet 
        if game.iplnet.known=="shuttle_down":
            proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
            if ja() == True:
-               chew()
+               scanner.chew()
                return
            prout(_("Your crew hides the Galileo to prevent capture by aliens."))
        prout(_("Landing party assembled, ready to beam up."))
@@ -4968,7 +4914,7 @@ def beam():
 def mine():
     # strip-mine a world for dilithium 
     skip(1)
-    chew()
+    scanner.chew()
     if not game.landed:
        prout(_("Mining party not on planet."))
        return
@@ -4998,7 +4944,7 @@ def usecrystals():
     # use dilithium crystals 
     game.ididit = False
     skip(1)
-    chew()
+    scanner.chew()
     if not game.icrystl:
        prout(_("No dilithium crystals available."))
        return
@@ -5011,7 +4957,7 @@ def usecrystals():
     prout(_("  system may risk a severe explosion."))
     proutn(_("  Are you sure this is wise?\" "))
     if ja() == False:
-       chew()
+       scanner.chew()
        return
     skip(1)
     prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
@@ -5042,7 +4988,7 @@ def usecrystals():
 
 def shuttle():
     # use shuttlecraft for planetary jaunt 
-    chew()
+    scanner.chew()
     skip(1)
     if damaged(DSHUTTL):
        if game.damage[DSHUTTL] == -1.0:
@@ -5146,11 +5092,11 @@ def deathray():
     # use the big zapper 
     game.ididit = False
     skip(1)
-    chew()
+    scanner.chew()
     if game.ship != IHE:
        prout(_("Ye Faerie Queene has no death ray."))
        return
-    if game.nenhere==0:
+    if len(game.enemies)==0:
        prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
        return
     if damaged(DDRAY):
@@ -5182,7 +5128,7 @@ def deathray():
     if r > dprob:
        prouts(_("Sulu- \"Captain!  It's working!\""))
        skip(2)
-       while game.nenhere > 0:
+       while len(game.enemies) > 0:
            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:
@@ -5267,7 +5213,7 @@ def attackreport(curt):
 
 def report():
     # report on general game status 
-    chew()
+    scanner.chew()
     s1 = "" and game.thawed and _("thawed ")
     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
     s3 = (None, _("novice"). _("fair"),
@@ -5369,7 +5315,7 @@ def lrscan(silent):
 def damagereport():
     # damage report 
     jdam = False
-    chew()
+    scanner.chew()
 
     for i in range(NDEVICES):
        if damaged(i):
@@ -5395,7 +5341,7 @@ def rechart():
 
 def chart():
     # display the star chart  
-    chew()
+    scanner.chew()
     if (game.options & OPTION_AUTOSCAN):
         lrscan(silent=True)
     if not damaged(DRADIO):
@@ -5436,15 +5382,15 @@ def sectscan(goodScan, i, j):
     # light up an individual dot in a sector 
     if goodScan or (abs(i-game.sector.x)<= 1 and abs(j-game.sector.y) <= 1):
        if (game.quad[i][j]==IHMATER0) or (game.quad[i][j]==IHMATER1) or (game.quad[i][j]==IHMATER2) or (game.quad[i][j]==IHE) or (game.quad[i][j]==IHF):
-           if game.condition   == "red": textcolor(RED)
-           elif game.condition == "green": textcolor(GREEN)
-           elif game.condition == "yellow": textcolor(YELLOW)
-           elif game.condition == "docked": textcolor(CYAN)
-           elif game.condition == "dead": textcolor(BROWN)
+           if game.condition   == "red": textcolor("red")
+           elif game.condition == "green": textcolor("green")
+           elif game.condition == "yellow": textcolor("yellow")
+           elif game.condition == "docked": textcolor("cyan")
+           elif game.condition == "dead": textcolor("brown")
            if game.quad[i][j] != game.ship: 
                highvideo()
        proutn("%c " % game.quad[i][j])
-       textcolor(DEFAULT)
+       textcolor(None)
     else:
        proutn("- ")
 
@@ -5507,11 +5453,11 @@ def status(req=0):
 
 def request():
     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
-    while scan() == IHEOL:
+    while scanner.next() == IHEOL:
        proutn(_("Information desired? "))
-    chew()
-    if citem in requests:
-        status(requests.index(citem))
+    scanner.chew()
+    if scanner.token in requests:
+        status(requests.index(scanner.token))
     else:
        prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
        prout(("  date, condition, position, lsupport, warpfactor,"))
@@ -5543,7 +5489,6 @@ def srscan():
            sectscan(goodScan, i, j)
        skip(1)
                        
-                       
 def eta():
     # use computer to get estimated time of arrival for a warp jump 
     w1 = coord(); w2 = coord()
@@ -5552,24 +5497,24 @@ def eta():
        prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
        skip(1)
        return
-    if scan() != IHREAL:
+    if scanner.next() != IHREAL:
        prompt = True
-       chew()
+       scanner.chew()
        proutn(_("Destination quadrant and/or sector? "))
-       if scan()!=IHREAL:
+       if scanner.next()!=IHREAL:
            huh()
            return
-    w1.y = int(aaitem-0.5)
-    if scan() != IHREAL:
+    w1.y = int(scanner.real-0.5)
+    if scanner.next() != IHREAL:
        huh()
        return
-    w1.x = int(aaitem-0.5)
-    if scan() == IHREAL:
-       w2.y = int(aaitem-0.5)
-       if scan() != IHREAL:
+    w1.x = int(scanner.real-0.5)
+    if scanner.next() == IHREAL:
+       w2.y = int(scanner.real-0.5)
+       if scanner.next() != IHREAL:
            huh()
            return
-       w2.x = int(aaitem-0.5)
+       w2.x = int(scanner.real-0.5)
     else:
        if game.quadrant.y>w1.x:
            w2.x = 0
@@ -5582,39 +5527,39 @@ def eta():
     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)))
+    game.dist = math.sqrt((w1.y-game.quadrant.y+0.1*(w2.y-game.sector.y))**2+
+               (w1.x-game.quadrant.x+0.1*(w2.x-game.sector.x))**2)
     wfl = False
     if prompt:
        prout(_("Answer \"no\" if you don't know the value:"))
     while True:
-       chew()
+       scanner.chew()
        proutn(_("Time or arrival date? "))
-       if scan()==IHREAL:
-           ttime = aaitem
+       if scanner.next()==IHREAL:
+           ttime = scanner.real
            if ttime > game.state.date:
                ttime -= game.state.date # Actually a star date
             twarp=(math.floor(math.sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0
             if ttime <= 1e-10 or twarp > 10:
                prout(_("We'll never make it, sir."))
-               chew()
+               scanner.chew()
                return
            if twarp < 1.0:
                twarp = 1.0
            break
-       chew()
+       scanner.chew()
        proutn(_("Warp factor? "))
-       if scan()== IHREAL:
+       if scanner.next()== IHREAL:
            wfl = True
-           twarp = aaitem
+           twarp = scanner.real
            if twarp<1.0 or twarp > 10.0:
                huh()
                return
            break
        prout(_("Captain, certainly you can give me one of these."))
     while True:
-       chew()
-       ttime = (10.0*game.dist)/square(twarp)
+       scanner.chew()
+       ttime = (10.0*game.dist)/twarp**2
        tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
        if tpower >= game.energy:
            prout(_("Insufficient energy, sir."))
@@ -5622,15 +5567,15 @@ def eta():
                if not wfl:
                    return
                proutn(_("New warp factor to try? "))
-               if scan() == IHREAL:
+               if scanner.next() == IHREAL:
                    wfl = True
-                   twarp = aaitem
+                   twarp = scanner.real
                    if twarp<1.0 or twarp > 10.0:
                        huh()
                        return
                    continue
                else:
-                   chew()
+                   scanner.chew()
                    skip(1)
                    return
            prout(_("But if you lower your shields,"))
@@ -5655,14 +5600,14 @@ def eta():
            (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
            prout(_("The starbase there will be destroyed by then."))
        proutn(_("New warp factor to try? "))
-       if scan() == IHREAL:
+       if scanner.next() == IHREAL:
            wfl = True
-           twarp = aaitem
+           twarp = scanner.real
            if twarp<1.0 or twarp > 10.0:
                huh()
                return
        else:
-           chew()
+           scanner.chew()
            skip(1)
            return
                        
@@ -5681,22 +5626,22 @@ def prelim():
 def freeze(boss):
     # save game 
     if boss:
-       citem = "emsave.trk"
+       scanner.token = "emsave.trk"
     else:
-        key = scan()
+        key = scanner.next()
        if key == IHEOL:
            proutn(_("File name: "))
-           key = scan()
+           key = scanner.next()
        if key != IHALPHA:
            huh()
            return
-       chew()
-        if '.' not in citem:
-           citem += ".trk"
+       scanner.chew()
+        if '.' not in scanner.token:
+           scanner.token += ".trk"
     try:
-        fp = open(citem, "wb")
+        fp = open(scanner.token, "wb")
     except IOError:
-       prout(_("Can't freeze game as file %s") % citem)
+       prout(_("Can't freeze game as file %s") % scanner.token)
        return
     cPickle.dump(game, fp)
     fp.close()
@@ -5704,20 +5649,20 @@ def freeze(boss):
 def thaw():
     # retrieve saved game 
     game.passwd[0] = '\0'
-    key = scan()
+    key = scanner.next()
     if key == IHEOL:
        proutn(_("File name: "))
-       key = scan()
+       key = scanner.next()
     if key != IHALPHA:
        huh()
        return True
-    chew()
-    if '.' not in citem:
-        citem += ".trk"
+    scanner.chew()
+    if '.' not in scanner.token:
+        scanner.token += ".trk"
     try:
-        fp = open(citem, "rb")
+        fp = open(scanner.token, "rb")
     except IOError:
-       prout(_("Can't thaw game in %s") % citem)
+       prout(_("Can't thaw game in %s") % scanner.token)
        return
     game = cPickle.load(fp)
     fp.close()
@@ -5794,11 +5739,11 @@ device = (
        _("D. S. Probe"), \
 )
 
-def setup(needprompt):
+def setup():
     # prepare to play, set up cosmos 
     w = coord()
     #  Decide how many of everything
-    if choose(needprompt):
+    if choose():
        return # frozen game
     # Prepare the Enterprise
     game.alldone = game.gamewon = False
@@ -5878,7 +5823,7 @@ def setup(needprompt):
             # so it did them in the opposite order.
             for j in range(1, i):
                # Improved placement algorithm to spread out bases
-               distq = w.distance(game.state.baseq[j])
+               distq = (w - game.state.baseq[j]).distance()
                if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
                    contflag = True
                    if idebug:
@@ -5912,7 +5857,7 @@ def setup(needprompt):
         if krem <= 0:
             break
     # Position Klingon Commander Ships
-    for i in range(1, game.incom+1):
+    for i in range(game.incom):
         while True:
             w = randplace(GALSIZE)
            if (game.state.galaxy[w.x][w.y].klingons or withprob(0.25)) and \
@@ -5994,12 +5939,12 @@ def setup(needprompt):
        prout(_("  YOU'LL NEED IT."))
     waitfor()
     newqad(False)
-    if game.nenhere - (thing == game.quadrant) - (game.tholian != None):
+    if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
        game.shldup = True
     if game.neutz:     # bad luck to start in a Romulan Neutral Zone
-       attack(False)
+       attack(torps_ok=False)
 
-def choose(needprompt):
+def choose():
     # choose your game type
     global thing
     while True:
@@ -6007,26 +5952,26 @@ def choose(needprompt):
        game.thawed = False
        game.skill = SKILL_NONE
        game.length = 0
-       if needprompt: # Can start with command line options 
+       if not scanner.inqueue: # Can start with command line options 
            proutn(_("Would you like a regular, tournament, or saved game? "))
-       scan()
-       if len(citem)==0: # Try again
+        scanner.next()
+       if len(scanner.token)==0: # Try again
            continue
-        if isit("tournament"):
-           while scan() == IHEOL:
+        if scanner.sees("tournament"):
+           while scanner.next() == IHEOL:
                proutn(_("Type in tournament number-"))
-           if aaitem == 0:
-               chew()
+           if scanner.real == 0:
+               scanner.chew()
                continue # We don't want a blank entry
-           game.tourn = int(round(aaitem))
-           random.seed(aaitem)
+           game.tourn = int(round(scanner.real))
+           random.seed(scanner.real)
             if logfp:
-                logfp.write("# random.seed(%d)\n" % aaitem)
+                logfp.write("# random.seed(%d)\n" % scanner.real)
            break
-        if isit("saved") or isit("frozen"):
+        if scanner.sees("saved") or scanner.sees("frozen"):
            if thaw():
                continue
-           chew()
+           scanner.chew()
            if game.passwd == None:
                continue
            if not game.alldone:
@@ -6034,55 +5979,55 @@ def choose(needprompt):
            report()
            waitfor()
            return True
-        if isit("regular"):
+        if scanner.sees("regular"):
            break
-       proutn(_("What is \"%s\"?"), citem)
-       chew()
+       proutn(_("What is \"%s\"?") % scanner.token)
+       scanner.chew()
     while game.length==0 or game.skill==SKILL_NONE:
-       if scan() == IHALPHA:
-            if isit("short"):
+       if scanner.next() == IHALPHA:
+            if scanner.sees("short"):
                game.length = 1
-           elif isit("medium"):
+           elif scanner.sees("medium"):
                game.length = 2
-           elif isit("long"):
+           elif scanner.sees("long"):
                game.length = 4
-           elif isit("novice"):
+           elif scanner.sees("novice"):
                game.skill = SKILL_NOVICE
-           elif isit("fair"):
+           elif scanner.sees("fair"):
                game.skill = SKILL_FAIR
-           elif isit("good"):
+           elif scanner.sees("good"):
                game.skill = SKILL_GOOD
-           elif isit("expert"):
+           elif scanner.sees("expert"):
                game.skill = SKILL_EXPERT
-           elif isit("emeritus"):
+           elif scanner.sees("emeritus"):
                game.skill = SKILL_EMERITUS
            else:
                proutn(_("What is \""))
-               proutn(citem)
+               proutn(scanner.token)
                prout("\"?")
        else:
-           chew()
+           scanner.chew()
            if game.length==0:
                proutn(_("Would you like a Short, Medium, or Long game? "))
            elif game.skill == SKILL_NONE:
                proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
     # Choose game options -- added by ESR for SST2K
-    if scan() != IHALPHA:
-       chew()
+    if scanner.next() != IHALPHA:
+       scanner.chew()
        proutn(_("Choose your game style (or just press enter): "))
-       scan()
-    if isit("plain"):
+       scanner.next()
+    if scanner.sees("plain"):
        # Approximates the UT FORTRAN version.
        game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
        game.options |= OPTION_PLAIN
-    elif isit("almy"):
+    elif scanner.sees("almy"):
        # Approximates Tom Almy's version.
        game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
        game.options |= OPTION_ALMY
-    elif isit("fancy"):
+    elif scanner.sees("fancy"):
        pass
-    elif len(citem):
-        proutn(_("What is \"%s\"?") % citem)
+    elif len(scanner.token):
+        proutn(_("What is \"%s\"?") % scanner.token)
     setpassword()
     if game.passwd == "debug":
        idebug = True
@@ -6146,12 +6091,13 @@ def newqad(shutup):
     game.ishere = False
     game.irhere = 0
     game.iplnet = 0
-    game.nenhere = 0
     game.neutz = False
     game.inorbit = False
     game.landed = False
     game.ientesc = False
     game.iseenit = False
+    # Create a blank quadrant
+    game.quad = fill2d(QUADSIZE, lambda i, j: IHDOT)
     if game.iscate:
        # Attempt to escape Super-commander, so tbeam back!
        game.iscate = False
@@ -6162,7 +6108,6 @@ def newqad(shutup):
        return
     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 = []
@@ -6186,7 +6131,7 @@ def newqad(shutup):
            game.iscate = (game.state.remkl > 1)
            game.ishere = True
     # Put in Romulans if needed
-    for i in range(game.klhere, game.nenhere):
+    for i in range(game.klhere, len(game.enemies)):
         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:
@@ -6213,7 +6158,7 @@ def newqad(shutup):
     if shutup==0:
        # Put in THING if needed
        if thing == game.quadrant:
-           enemy(symbol=IHQUEST, loc=dropin(),
+           enemy(type=IHQUEST, loc=dropin(),
                       power=randreal(6000,6500.0)+250.0*game.skill)
            if not damaged(DSRSENS):
                skip(1)
@@ -6230,7 +6175,7 @@ def newqad(shutup):
                w.y = withprob(0.5) * (QUADSIZE-1)
                 if game.quad[w.x][w.y] == IHDOT:
                     break
-            game.tholian = enemy(symbol=IHT, loc=w,
+            game.tholian = enemy(type=IHT, loc=w,
                                  power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
            if game.quad[0][0]==IHDOT:
@@ -6264,10 +6209,10 @@ def setpassword():
     # set the self-destruct password 
     if game.options & OPTION_PLAIN:
        while True:
-           chew()
+           scanner.chew()
            proutn(_("Please type in a secret password- "))
-           scan()
-           game.passwd = citem
+           scanner.next()
+           game.passwd = scanner.token
            if game.passwd != None:
                break
     else:
@@ -6338,23 +6283,23 @@ def listCommands():
 def helpme():
     # browse on-line help 
     # Give help on commands 
-    key = scan()
+    key = scanner.next()
     while True:
        if key == IHEOL:
            setwnd(prompt_window)
            proutn(_("Help on what command? "))
-           key = scan()
+           key = scanner.next()
        setwnd(message_window)
        if key == IHEOL:
            return
-        if citem in commands or citem == "ABBREV":
+        if scanner.token in commands or scanner.token == "ABBREV":
            break
        skip(1)
        listCommands()
        key = IHEOL
-       chew()
+       scanner.chew()
        skip(1)
-    cmd = citem.upper()
+    cmd = scanner.token.upper()
     try:
         fp = open(SSTDOC, "r")
     except IOError:
@@ -6403,25 +6348,27 @@ def makemoves():
            hitme = False
            game.justin = False
            game.optime = 0.0
-           chew()
+           scanner.chew()
            setwnd(prompt_window)
            clrscr()
            proutn("COMMAND> ")
-           if scan() == IHEOL:
+           if scanner.next() == IHEOL:
                if game.options & OPTION_CURSES:
                    makechart()
                continue
+            elif scanner.token == "":
+                continue
            game.ididit = False
            clrscr()
            setwnd(message_window)
            clrscr()
-            candidates = filter(lambda x: x.startswith(citem.upper()),
+            candidates = filter(lambda x: x.startswith(scanner.token.upper()),
                                 commands)
             if len(candidates) == 1:
                 cmd = candidates[0]
                 break
             elif candidates and not (game.options & OPTION_PLAIN):
-                prout("Commands with that prefix: " + " ".join(candidates))
+                prout("Commands with prefix '%s': %s" % (scanner.token, " ".join(candidates)))
             else:
                 listCommands()
                 continue
@@ -6451,7 +6398,7 @@ def makemoves():
        elif cmd == "DOCK":             # dock at starbase
            dock(True)
            if game.ididit:
-               attack(False)           
+               attack(torps_ok=False)          
        elif cmd == "DAMAGES":          # damage reports
            damagereport()
        elif cmd == "CHART":            # chart
@@ -6459,7 +6406,7 @@ def makemoves():
        elif cmd == "IMPULSE":          # impulse
            impulse()
        elif cmd == "REST":             # rest
-           os.wait()
+           wait()
            if game.ididit:
                hitme = True
        elif cmd == "WARP":             # warp
@@ -6536,7 +6483,7 @@ def makemoves():
                atover(False)
                continue
            if hitme and not game.justin:
-               attack(True)
+               attack(torps_ok=True)
                if game.alldone:
                    break
                if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
@@ -6604,72 +6551,82 @@ def randplace(size):
     w.y = randrange(size)
     return w
 
-def chew():
-    # Demand input for next scan
-    global inqueue
-    inqueue = None
-
-def chew2():
-    # return IHEOL next time 
-    global inqueue
-    inqueue = []
-
-def scan():
-    # Get a token from the user
-    global inqueue, line, citem, aaitem
-    aaitem = 0.0
-    citem = ''
-
-    # Read a line if nothing here
-    if inqueue == None:
-       line = cgetline()
-       if curwnd==prompt_window:
-           clrscr()
-           setwnd(message_window)
-           clrscr()
-        # Skip leading white space
-        line = line.lstrip()
-        if line:
-            inqueue = line.split()
-        else:
-            inqueue = []
+class sstscanner:
+    def __init__(self):
+        self.type = None
+        self.token = None
+        self.real = 0.0
+        self.inqueue = []
+    def next(self):
+        # Get a token from the user
+        self.real = 0.0
+        self.token = ''
+        # Fill the token quue if nothing here
+        while not self.inqueue:
+            line = cgetline()
+            if curwnd==prompt_window:
+                clrscr()
+                setwnd(message_window)
+                clrscr()
+            if line == '':
+                return None
+            # Skip leading white space
+            line = line.lstrip()
+            if not line:
+                continue
+            else:
+                self.inqueue = line.lstrip().split() + [IHEOL] 
+        # From here on in it's all looking at the queue
+        self.token = self.inqueue.pop(0)
+        if self.token == IHEOL:
+            self.type = IHEOL
             return IHEOL
-    elif not inqueue:
-        return IHEOL
-    # From here on in it's all looking at the queue
-    citem = inqueue.pop(0)
-    if citem == IHEOL:
-        return IHEOL
-    try:
-        aaitem = float(citem)
-        return IHREAL
-    except ValueError:
-        pass
-    # Treat as alpha
-    citem = citem.lower()
-    return IHALPHA
+        try:
+            self.real = float(self.token)
+            self.type = IHREAL
+            return IHREAL
+        except ValueError:
+            pass
+        # Treat as alpha
+        self.token = self.token.lower()
+        self.type = IHALPHA
+        self.real = None
+        return IHALPHA
+    def push(self, toklist):
+        self.inqueue += toklist
+    def chew(self):
+        # Demand input for next scan
+        self.inqueue = None
+        self.real = self.token = None
+    def chew2(self):
+        # return IHEOL next time 
+        self.inqueue = []
+        self.real = self.token = None
+    def sees(self, s):
+        # compares s to item and returns true if it matches to the length of s
+        return s.startswith(self.token)
+    def int(self):
+        # Round token value to nearest integer
+        return int(round(scanner.real))
 
 def ja():
     # yes-or-no confirmation 
-    chew()
+    scanner.chew()
     while True:
-       scan()
-       chew()
-       if citem == 'y':
+       scanner.next()
+       scanner.chew()
+       if scanner.token == 'y':
            return True
-       if citem == 'n':
+       if scanner.token == 'n':
            return False
        proutn(_("Please answer with \"y\" or \"n\": "))
 
 def huh():
     # complain about unparseable input 
-    chew()
+    scanner.chew()
     skip(1)
     prout(_("Beg your pardon, Captain?"))
 
-def isit(s):
-    # compares s to citem and returns true if it matches to the length of s
-    return s.startswith(citem)
 
 def debugme():
     # access to the internals for debugging 
@@ -6698,9 +6655,9 @@ def debugme():
            proutn("Kill ")
            proutn(device[i])
            proutn("? ")
-           chew()
-           key = scan()
-            if key == IHALPHA and isit("y"):
+           scanner.chew()
+           key = scanner.next()
+            if key == IHALPHA and scanner.sees("y"):
                game.damage[i] = 10.0
     proutn("Examine/change events? ")
     if ja() == True:
@@ -6729,32 +6686,32 @@ def debugme():
            else:
                proutn("never")
            proutn("? ")
-           chew()
-           key = scan()
+           scanner.chew()
+           key = scanner.next()
            if key == 'n':
                unschedule(i)
-               chew()
+               scanner.chew()
            elif key == IHREAL:
-               ev = schedule(i, aaitem)
+               ev = schedule(i, scanner.real)
                if i == FENSLV or i == FREPRO:
-                   chew()
+                   scanner.chew()
                    proutn("In quadrant- ")
-                   key = scan()
+                   key = scanner.next()
                    # IHEOL says to leave coordinates as they are 
                    if key != IHEOL:
                        if key != IHREAL:
                            prout("Event %d canceled, no x coordinate." % (i))
                            unschedule(i)
                            continue
-                       w.x = int(round(aaitem))
-                       key = scan()
+                       w.x = int(round(scanner.real))
+                       key = scanner.next()
                        if key != IHREAL:
                            prout("Event %d canceled, no y coordinate." % (i))
                            unschedule(i)
                            continue
-                       w.y = int(round(aaitem))
+                       w.y = int(round(scanner.real))
                        ev.quadrant = w
-       chew()
+       scanner.chew()
     proutn("Induce supernova here? ")
     if ja() == True:
        game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = True
@@ -6763,8 +6720,7 @@ def debugme():
 if __name__ == '__main__':
     try:
         global line, thing, game, idebug
-        game = citem = aaitem = inqueue = None
-        line = ''
+        game = None
         thing = coord()
         thing.angry = False
         game = gamestate()
@@ -6813,35 +6769,36 @@ if __name__ == '__main__':
             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()
+        scanner = sstscanner()
+        scanner.push(arguments)
+        try:
+            iostart()
+            while True: # Play a game 
+                setwnd(fullscreen_window)
+                clrscr()
+                prelim()
+                setup()
+                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:
+                        scanner.chew2()
+                        freeze(False)
+                scanner.chew()
+                proutn(_("Do you want to play again? "))
+                if not ja():
+                    break
             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."))
+            prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
+        finally:
+            ioend()
         raise SystemExit, 0
     except KeyboardInterrupt:
         print""