next -> nxttok in preparation for 2to3 translation.
[super-star-trek.git] / sst.py
diff --git a/sst.py b/sst.py
index c42171ea645df8d280aef91dfd851d401605417c..be11f15c853c298f27afbc93229ccf3bef7191ac 100755 (executable)
--- a/sst.py
+++ b/sst.py
@@ -131,6 +131,8 @@ class Thingy(Coord):
         self.angered = False
     def angry(self):
         self.angered = True
+    def at(self, q):
+        return (q.i, q.j) == (self.i, self.j)
 
 class Planet:
     def __init__(self):
@@ -139,7 +141,7 @@ class Planet:
         self.pclass = None     # could be ""M", "N", "O", or "destroyed"
         self.crystals = "absent"# could be "mined", "present", "absent"
         self.known = "unknown" # could be "unknown", "known", "shuttle_down"
-        self.inhabited = False # is it inhabites?
+        self.inhabited = False # is it inhabited?
     def __str__(self):
         return self.name
 
@@ -157,8 +159,10 @@ class Quadrant:
 class Page:
     def __init__(self):
        self.stars = None
-       self.starbase = None
+       self.starbase = False
        self.klingons = None
+    def __repr__(self):
+        return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars)
 
 def fill2d(size, fillfun):
     "Fill an empty list in 2D."
@@ -273,6 +277,8 @@ class Enemy:
     def __init__(self, etype=None, loc=None, power=None):
         self.type = etype
         self.location = Coord()
+        self.kdist = None
+        self.kavgd = None
         if loc:
             self.move(loc)
         self.power = power     # enemy energy level
@@ -379,6 +385,7 @@ class Gamestate:
         self.score = 0.0       # overall score
         self.perdate = 0.0     # rate of kills
         self.idebug = False    # Debugging instrumentation enabled?
+        self.statekscmdr = None # No SuperCommander coordinates yet.
     def recompute(self):
         # Stas thinks this should be (C expression): 
         # game.state.remkl + len(game.state.kcmdr) > 0 ?
@@ -446,19 +453,14 @@ def tryexit(enemy, look, irun):
        # avoid intruding on another commander's territory 
        if enemy.type == 'C':
             if iq in game.state.kcmdr:
-                return False
+                return []
            # refuse to leave if currently attacking starbase 
            if game.battle == game.quadrant:
-               return False
+               return []
        # don't leave if over 1000 units of energy 
        if enemy.power > 1000.0:
-           return False
-    # emit escape message and move out of quadrant.
-    # we know this if either short or long range sensors are working
-    if not damaged(DSRSENS) or not damaged(DLRSENS) or \
-       game.condition == "docked":
-       prout(crmena(True, enemy.type, "sector", enemy.location) + \
-              (_(" escapes to Quadrant %s (and regains strength).") % iq))
+           return []
+    oldloc = copy.copy(enemy.location)
     # handle local matters related to escape
     enemy.move(None)
     game.klhere -= 1
@@ -479,7 +481,8 @@ def tryexit(enemy, look, irun):
            if cmdr == game.quadrant:
                game.state.kcmdr.append(iq)
                break
-    return True # success 
+    # report move out of quadrant.
+    return [(True, enemy, oldloc, ibq)]
 
 # The bad-guy movement algorithm:
 # 
@@ -529,8 +532,8 @@ def movebaddy(enemy):
        nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
     else:
        nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
-    dist1 = enemy.kdist
-    mdist = int(dist1 + 0.5) # Nearest integer distance 
+    old_dist = enemy.kdist
+    mdist = int(old_dist + 0.5) # Nearest integer distance 
     # If SC, check with spy to see if should hi-tail it 
     if enemy.type == 'S' and \
        (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
@@ -558,14 +561,14 @@ def movebaddy(enemy):
            motion = ((forces + randreal(200))/150.0) - 5.0
        else:
             if forces > 1000.0: # Very strong -- move in for kill 
-               motion = (1.0 - randreal())**2 * dist1 + 1.0
+               motion = (1.0 - randreal())**2 * old_dist + 1.0
            if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
                motion -= game.skill*(2.0-randreal()**2)
        if game.idebug:
            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
        # don't move if no motion 
        if motion == 0:
-           return
+           return []
        # Limit motion according to skill 
        if abs(motion) > game.skill:
             if motion < 0:
@@ -609,15 +612,15 @@ def movebaddy(enemy):
        while attempts < 20 and not success:
             attempts += 1
            if look.i < 0 or look.i >= QUADSIZE:
-               if motion < 0 and tryexit(enemy, look, irun):
-                   return
+                if motion < 0:
+                   return tryexit(enemy, look, irun)
                if krawli == m.i or m.j == 0:
                    break
                look.i = goto.i + krawli
                krawli = -krawli
            elif look.j < 0 or look.j >= QUADSIZE:
-               if motion < 0 and tryexit(enemy, look, irun):
-                   return
+               if motion < 0:
+                   return tryexit(enemy, look, irun)
                if krawlj == m.j or m.i == 0:
                    break
                look.j = goto.j + krawlj
@@ -627,7 +630,7 @@ def movebaddy(enemy):
                if game.quad[look.i][look.j] == game.ship and \
                    (enemy.type == 'C' or enemy.type == 'S'):
                    collision(rammed=True, enemy=enemy)
-                   return
+                   return []
                if krawli != m.i and m.j != 0:
                    look.i = goto.i + krawli
                    krawli = -krawli
@@ -646,14 +649,8 @@ def movebaddy(enemy):
            break # done early 
     if game.idebug:
        skip(1)
-    if enemy.move(goto):
-       if not damaged(DSRSENS) or game.condition == "docked":
-           proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
-           if enemy.kdist < dist1:
-               proutn(_(" advances to "))
-           else:
-               proutn(_(" retreats to "))
-           prout("Sector %s." % goto)
+    # Enemy moved, but is still in sector
+    return [(False, enemy, old_dist, goto)]
 
 def moveklings():
     "Sequence Klingon tactical movement."
@@ -661,14 +658,15 @@ def moveklings():
        prout("== MOVCOM")
     # Figure out which Klingon is the commander (or Supercommander)
     # and do move
+    tacmoves = []
     if game.quadrant in game.state.kcmdr:
         for enemy in game.enemies:
            if enemy.type == 'C':
-               movebaddy(enemy)
+               tacmoves += movebaddy(enemy)
     if game.state.kscmdr == game.quadrant:
         for enemy in game.enemies:
            if enemy.type == 'S':
-               movebaddy(enemy)
+               tacmoves += movebaddy(enemy)
                break
     # If skill level is high, move other Klingons and Romulans too!
     # Move these last so they can base their actions on what the
@@ -676,8 +674,8 @@ def moveklings():
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
         for enemy in game.enemies:
             if enemy.type in ('K', 'R'):
-               movebaddy(enemy)
-    sortenemies()
+               tacmoves += movebaddy(enemy)
+    return tacmoves
 
 def movescom(iq, avoid):
     "Commander movement helper." 
@@ -698,8 +696,7 @@ def movescom(iq, avoid):
        unschedule(FSCDBAS)
        for enemy in game.enemies:
            if enemy.type == 'S':
-               break
-       enemy.move(None)
+               enemy.move(None)
        game.klhere -= 1
        if game.condition != "docked":
            newcnd()
@@ -904,7 +901,7 @@ def doshield(shraise):
     if shraise:
        action = "SHUP"
     else:
-       key = scanner.next()
+       key = scanner.nexttok()
        if key == "IHALPHA":
            if scanner.sees("transfer"):
                action = "NRG"
@@ -963,7 +960,7 @@ def doshield(shraise):
        game.ididit = True
        return
     elif action == "NRG":
-       while scanner.next() != "IHREAL":
+       while scanner.nexttok() != "IHREAL":
            scanner.chew()
            proutn(_("Energy to transfer to shields- "))
         nrg = scanner.real
@@ -1033,7 +1030,7 @@ def randdevice():
     return None        # we should never get here
 
 def collision(rammed, enemy):
-    "Collision handling fot rammong events."
+    "Collision handling for rammong events."
     prouts(_("***RED ALERT!  RED ALERT!"))
     skip(1)
     prout(_("***COLLISION IMMINENT."))
@@ -1089,7 +1086,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
     # Loop to move a single torpedo 
     setwnd(message_window)
     for step in range(1, QUADSIZE*2):
-        if not track.next():
+        if not track.nexttok():
             break
         w = track.sector()
        if not w.valid_sector():
@@ -1099,7 +1096,8 @@ def torpedo(origin, bearing, dispersion, number, nburst):
        if iquad == '.':
            continue
        # hit something 
-       if not damaged(DSRSENS) or game.condition == "docked":
+        setwnd(message_window)
+        if not damaged(DSRSENS) or game.condition == "docked":
            skip(1)     # start new line after text track 
        if iquad in ('E', 'F'): # Hit our ship 
            skip(1)
@@ -1112,7 +1110,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
             # In the C/FORTRAN version, dispersion was 2.5 radians, which
             # is 143 degrees, which is almost exactly 4.8 clockface units
             displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
-            displacement.next()
+            displacement.nexttok()
             bumpto = displacement.sector()
            if not bumpto.valid_sector():
                return hit
@@ -1139,40 +1137,43 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                return None
             for enemy in game.enemies:
                if w == enemy.location:
-                   break
-           kp = math.fabs(enemy.power)
-           h1 = 700.0 + randrange(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
-           h1 = math.fabs(h1)
-           if kp < h1:
-               h1 = kp
-            if enemy.power < 0:
-                enemy.power -= -h1
-            else:
-                enemy.power -= h1
-           if enemy.power == 0:
-               deadkl(w, iquad, w)
-               return None
-           proutn(crmena(True, iquad, "sector", w))
-            displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
-            displacement.next()
-            bumpto = displacement.sector()
-            if not bumpto.valid_sector():
-               prout(_(" damaged but not destroyed."))
-               return
-           if game.quad[bumpto.i][bumpto.j] == ' ':
-               prout(_(" buffeted into black hole."))
-               deadkl(w, iquad, bumpto)
-           if game.quad[bumpto.i][bumpto.j] != '.':
-               prout(_(" damaged but not destroyed."))
+                    kp = math.fabs(enemy.power)
+                    h1 = 700.0 + randrange(100) - \
+                        1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
+                    h1 = math.fabs(h1)
+                    if kp < h1:
+                        h1 = kp
+                    if enemy.power < 0:
+                        enemy.power -= -h1
+                    else:
+                        enemy.power -= h1
+                    if enemy.power == 0:
+                        deadkl(w, iquad, w)
+                        return None
+                    proutn(crmena(True, iquad, "sector", w))
+                    displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
+                    displacement.nexttok()
+                    bumpto = displacement.sector()
+                    if not bumpto.valid_sector():
+                        prout(_(" damaged but not destroyed."))
+                        return
+                    if game.quad[bumpto.i][bumpto.j] == ' ':
+                        prout(_(" buffeted into black hole."))
+                        deadkl(w, iquad, bumpto)
+                    if game.quad[bumpto.i][bumpto.j] != '.':
+                        prout(_(" damaged but not destroyed."))
+                    else:
+                        prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
+                        enemy.location = bumpto
+                        game.quad[w.i][w.j] = '.'
+                        game.quad[bumpto.i][bumpto.j] = iquad
+                        for enemy in game.enemies:
+                            enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
+                        sortenemies()
+                    break
             else:
-                prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
-                enemy.location = bumpto
-                game.quad[w.i][w.j] = '.'
-                game.quad[bumpto.i][bumpto.j] = iquad
-                for enemy in game.enemies:
-                    enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
-                sortenemies()
+                prout("Internal error, no enemy where expected!")
+                raise SystemExit, 1
             return None
        elif iquad == 'B': # Hit a base 
            skip(1)
@@ -1180,8 +1181,8 @@ def torpedo(origin, bearing, dispersion, number, nburst):
             game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
            game.quad[w.i][w.j] = '.'
            game.base.invalidate()
-           game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase -= 1
-           game.state.chart[game.quadrant.i][game.quadrant.j].starbase -= 1
+           game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
+           game.state.chart[game.quadrant.i][game.quadrant.j].starbase = False
            game.state.basekl += 1
            newcnd()
            return None
@@ -1268,6 +1269,7 @@ def torpedo(origin, bearing, dispersion, number, nburst):
            return None
        break
     skip(1)
+    setwnd(message_window)
     prout(_("Torpedo missed."))
     return None
 
@@ -1280,7 +1282,6 @@ def fry(hit):
     # Select devices and cause damage
     cdam = []
     while ncrit > 0:
-        ncrit -= 1
         while True:
            j = randdevice()
            # Cheat to prevent shuttle damage unless on ship 
@@ -1289,6 +1290,7 @@ def fry(hit):
        cdam.append(j)
        extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
        game.damage[j] += extradm
+        ncrit -= 1
     skipcount = 0
     for (i, j) in enumerate(cdam):
        proutn(device[j])
@@ -1325,9 +1327,26 @@ def attack(torps_ok):
        return
     # commanders get a chance to tac-move towards you 
     if (((game.quadrant in game.state.kcmdr or game.state.kscmdr == game.quadrant) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
-       moveklings()
+        for (bugout, enemy, old, goto) in  moveklings():
+            if bugout:
+                # we know about this if either short or long range
+                # sensors are working
+                if damaged(DSRSENS) and damaged(DLRSENS) \
+                       and game.condition != "docked":
+                    prout(crmena(True, enemy.type, "sector", old) + \
+                          (_(" escapes to Quadrant %s (and regains strength).") % goto))
+            else: # Enemy still in-sector
+                if enemy.move(goto):
+                    if not damaged(DSRSENS) or game.condition == "docked":
+                        proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
+                        if enemy.kdist < old:
+                            proutn(_(" advances to "))
+                        else:
+                            proutn(_(" retreats to "))
+                        prout("Sector %s." % goto)
+        sortenemies()
     # if no enemies remain after movement, we're done 
-    if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing == game.quadrant and not thing.angered):
+    if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
        return
     # set up partial hits if attack happens during shield status change 
     pfac = 1.0/game.inshld
@@ -1532,7 +1551,7 @@ def torps():
        return
     # First, get torpedo count
     while True:
-        scanner.next()
+        scanner.nexttok()
        if scanner.token == "IHALPHA":
            huh()
            return
@@ -1557,7 +1576,7 @@ def torps():
     # Next, get targets
     target = []
     for i in range(n):
-       key = scanner.next()
+       key = scanner.nexttok()
        if i == 0 and key == "IHEOL":
            break       # no coordinate waiting, we will try prompting 
        if i == 1 and key == "IHEOL":
@@ -1662,7 +1681,8 @@ def hittem(hits):
     "Register a phaser hit on Klingons and Romulans."
     w = Coord()
     skip(1)
-    for (kk, wham) in enumerate(hits):
+    kk = 0
+    for wham in hits:
        if wham == 0:
            continue
        dustfac = randreal(0.9, 1.0)
@@ -1743,7 +1763,7 @@ def phasers():
     # Original code so convoluted, I re-did it all
     # (That was Tom Almy talking about the C code, I think -- ESR)
     while automode == "NOTSET":
-       key = scanner.next()
+       key = scanner.nexttok()
        if key == "IHALPHA":
            if scanner.sees("manual"):
                if len(game.enemies)==0:
@@ -1753,7 +1773,7 @@ def phasers():
                    automode = "AUTOMATIC"
                else:
                    automode = "MANUAL"
-                   key = scanner.next()
+                   key = scanner.nexttok()
            elif scanner.sees("automatic"):
                if (not itarg) and len(game.enemies) != 0:
                    automode = "FORCEMAN"
@@ -1761,7 +1781,7 @@ def phasers():
                    if len(game.enemies)==0:
                        prout(_("Energy will be expended into space."))
                    automode = "AUTOMATIC"
-                   key = scanner.next()
+                   key = scanner.nexttok()
            elif scanner.sees("no"):
                no = True
            else:
@@ -1791,7 +1811,7 @@ def phasers():
     if automode == "AUTOMATIC":
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
-           key = scanner.next()
+           key = scanner.nexttok()
        if key != "IHREAL" and len(game.enemies) != 0:
            prout(_("Phasers locked on target. Energy available: %.2f")%avail)
        irec = 0
@@ -1804,7 +1824,7 @@ def phasers():
            proutn(_("%d units required. ") % irec)
            scanner.chew()
            proutn(_("Units to fire= "))
-           key = scanner.next()
+           key = scanner.nexttok()
            if key != "IHREAL":
                return
            rpow = scanner.real
@@ -1818,7 +1838,7 @@ def phasers():
            # chicken out 
            scanner.chew()
            return
-        key = scanner.next()
+        key = scanner.nexttok()
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
        if ifast:
@@ -1899,10 +1919,10 @@ def phasers():
                    proutn("??")
                proutn(")  ")
                proutn(_("units to fire at %s-  ") % crmena(False, ienm, "sector", aim))                
-               key = scanner.next()
+               key = scanner.nexttok()
            if key == "IHALPHA" and scanner.sees("no"):
                no = True
-               key = scanner.next()
+               key = scanner.nexttok()
                continue
            if key == "IHALPHA":
                huh()
@@ -1922,7 +1942,7 @@ def phasers():
                prout(_("Available energy exceeded -- try again."))
                scanner.chew()
                return
-           key = scanner.next() # scan for next value 
+           key = scanner.nexttok() # scan for next value 
            k += 1
        if rpow == 0.0:
            # zero energy -- abort 
@@ -2047,8 +2067,8 @@ def events():
             else:
                 prout(_("(Shields not currently useable.)"))
         newqad()
-        # Adjust finish time to time of tractor beaming 
-        fintim = game.state.date+game.optime
+        # Adjust finish time to time of tractor beaming? 
+        fintim = game.state.date+game.optime
         attack(torps_ok=False)
         if not game.state.kcmdr:
             unschedule(FTBEAM)
@@ -2266,7 +2286,7 @@ def events():
                supercommander()
        elif evcode == FDSPROB: # Move deep space probe 
            schedule(FDSPROB, 0.01)
-            if not game.probe.next():
+            if not game.probe.nexttok():
                if not game.probe.quadrant().valid_quadrant() or \
                    game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
                    # Left galaxy or ran into supernova
@@ -2284,18 +2304,18 @@ def events():
                    #announce()
                    skip(1)
                    prout(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
-           pdest = game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j]
+            pquad = game.probe.quadrant()
+           pdest = game.state.galaxy[pquad.i][pquad.j]
            if communicating():
-               chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
-               chp.klingons = pdest.klingons
-               chp.starbase = pdest.starbase
-               chp.stars = pdest.stars
+               game.state.chart[pquad.i][pquad.j].klingons = pdest.klingons
+               game.state.chart[pquad.i][pquad.j].starbase = pdest.starbase
+               game.state.chart[pquad.i][pquad.j].stars = pdest.stars
                pdest.charted = True
            game.probe.moves -= 1 # One less to travel
            if game.probe.arrived() and game.isarmed and pdest.stars:
                supernova(game.probe)           # fire in the hole!
                unschedule(FDSPROB)
-               if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova: 
+               if game.state.galaxy[pquad.i][pquad.j].supernova: 
                    return
        elif evcode == FDISTR: # inhabited system issues distress call 
            unschedule(FDISTR)
@@ -2397,7 +2417,7 @@ def wait():
     "Wait on events."
     game.ididit = False
     while True:
-       key = scanner.next()
+       key = scanner.nexttok()
        if key  != "IHEOL":
            break
        proutn(_("How long? "))
@@ -2697,7 +2717,7 @@ def selfdestruct():
     skip(1)
     prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
     skip(1)
-    scanner.next()
+    scanner.nexttok()
     if game.passwd != scanner.token:
        prouts(_("PASSWORD-REJECTED;"))
        skip(1)
@@ -3184,7 +3204,8 @@ def pause_game():
         global linecount
         sys.stdout.write('\n')
         proutn(prompt)
-        raw_input()
+        if not replayfp:
+            raw_input()
         sys.stdout.write('\n' * rows)
         linecount = 0
 
@@ -3213,6 +3234,9 @@ def proutn(line):
        if curwnd == message_window and y >= my - 2:
            pause_game()
            clrscr()
+        # Uncomment this to debug curses problems
+        if logfp:
+            logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(line)))
        curwnd.addstr(line)
        curwnd.refresh()
     else:
@@ -3262,8 +3286,31 @@ def setwnd(wnd):
     "Change windows -- OK for this to be a no-op in tty mode."
     global curwnd
     if game.options & OPTION_CURSES:
+        # Uncomment this to debug curses problems
+        if logfp:
+            if wnd == fullscreen_window:
+                legend = "fullscreen"
+            elif wnd == srscan_window:
+                legend = "srscan"
+            elif wnd == report_window:
+                legend = "report"
+            elif wnd == status_window:
+                legend = "status"
+            elif wnd == lrscan_window:
+                legend = "lrscan"
+            elif wnd == message_window:
+                legend = "message"
+            elif wnd == prompt_window:
+                legend = "prompt"
+            else:
+                legend = "unknown"
+            logfp.write("#curses: setwnd(%s)\n" % legend)
         curwnd = wnd
-        curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
+        # Some curses implementations get confused when you try this.
+        try:
+            curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
+        except curses.error:
+            pass
 
 def clreol():
     "Clear to end of line -- can be a no-op in tty mode" 
@@ -3559,7 +3606,7 @@ def imove(icourse=None, noattack=False):
     # Move out
     game.quad[game.sector.i][game.sector.j] = '.'
     for m in range(icourse.moves):
-        icourse.next()
+        icourse.nexttok()
         w = icourse.sector()
         if icourse.origin.quadrant() != icourse.location.quadrant():
             newquadrant(noattack)
@@ -3646,7 +3693,7 @@ def getcourse(isprobe):
            navmode = "manual"
            key = "IHEOL"
            break
-        key = scanner.next()
+        key = scanner.nexttok()
        if key == "IHEOL":
            proutn(_("Manual or automatic- "))
            iprompt = True
@@ -3654,11 +3701,11 @@ def getcourse(isprobe):
        elif key == "IHALPHA":
             if scanner.sees("manual"):
                navmode = "manual"
-               key = scanner.next()
+               key = scanner.nexttok()
                break
             elif scanner.sees("automatic"):
                navmode = "automatic"
-               key = scanner.next()
+               key = scanner.nexttok()
                break
            else:
                huh()
@@ -3680,21 +3727,21 @@ def getcourse(isprobe):
                proutn(_("Destination sector or quadrant&sector- "))
            scanner.chew()
            iprompt = True
-           key = scanner.next()
+           key = scanner.nexttok()
        if key != "IHREAL":
            huh()
            raise TrekError
        xi = int(round(scanner.real))-1
-       key = scanner.next()
+       key = scanner.nexttok()
        if key != "IHREAL":
            huh()
            raise TrekError
        xj = int(round(scanner.real))-1
-       key = scanner.next()
+       key = scanner.nexttok()
        if key == "IHREAL":
            # both quadrant and sector specified 
            xk = int(round(scanner.real))-1
-           key = scanner.next()
+           key = scanner.nexttok()
            if key != "IHREAL":
                huh()
                raise TrekError
@@ -3733,13 +3780,13 @@ def getcourse(isprobe):
            proutn(_("X and Y displacements- "))
            scanner.chew()
            iprompt = True
-           key = scanner.next()
+           key = scanner.nexttok()
        itemp = "verbose"
        if key != "IHREAL":
            huh()
            raise TrekError
        delta.j = scanner.real
-       key = scanner.next()
+       key = scanner.nexttok()
        if key != "IHREAL":
            huh()
            raise TrekError
@@ -3782,7 +3829,7 @@ class course:
         self.step = 0
     def arrived(self):
         return self.location.roundtogrid() == self.final
-    def next(self):
+    def nexttok(self):
         "Next step on course."
         self.step += 1
         self.nextlocation = self.location + self.increment
@@ -3828,7 +3875,7 @@ def impulse():
        scanner.chew()
        return
     # Make sure enough time is left for the trip 
-    game.optime = course.dist/0.095
+    game.optime = course.distance/0.095
     if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
@@ -3840,9 +3887,9 @@ def impulse():
     game.ididit = True
     if game.alldone:
        return
-    power = 20.0 + 100.0*course.dist
+    power = 20.0 + 100.0*course.distance
     game.energy -= power
-    game.optime = course.dist/0.095
+    game.optime = course.distance/0.095
     if game.energy <= 0:
        finish(FNRG)
     return
@@ -3878,7 +3925,7 @@ def warp(wcourse, involuntary):
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*wcourse.power(game.warpfac) > game.energy:
-               iwarp = (game.energy/(wcourse.dist+0.05)) ** 0.333333333
+               iwarp = (game.energy/(wcourse.distance+0.05)) ** 0.333333333
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
@@ -3926,7 +3973,7 @@ def warp(wcourse, involuntary):
             look = wcourse.moves
             while look > 0:
                 look -= 1
-                wcourse.next()
+                wcourse.nexttok()
                 w = wcourse.sector()
                 if not w.valid_sector():
                     break
@@ -3956,7 +4003,7 @@ def warp(wcourse, involuntary):
 def setwarp():
     "Change the warp factor."
     while True:
-        key=scanner.next()
+        key=scanner.nexttok()
         if key != "IHEOL":
             break
        scanner.chew()
@@ -4142,7 +4189,7 @@ def probe():
        else:
            prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
        return
-    key = scanner.next()
+    key = scanner.nexttok()
     if key == "IHEOL":
         if game.nprobes == 1:
             prout(_("1 probe left."))
@@ -4154,7 +4201,7 @@ def probe():
     game.isarmed = False
     if key == "IHALPHA" and scanner.token == "armed":
        game.isarmed = True
-       key = scanner.next()
+       key = scanner.nexttok()
     elif key == "IHEOL":
        proutn(_("Arm NOVAMAX warhead? "))
        game.isarmed = ja()
@@ -4506,7 +4553,7 @@ def beam():
     skip(1)
     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
     skip(2)
-    if withprob(0.98):
+    if not withprob(0.98):
        prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
        skip(2)
        prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
@@ -5057,7 +5104,7 @@ def status(req=0):
 def request():
     "Request specified status data, a historical relic from slow TTYs."
     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
-    while scanner.next() == "IHEOL":
+    while scanner.nexttok() == "IHEOL":
        proutn(_("Information desired? "))
     scanner.chew()
     if scanner.token in requests:
@@ -5101,21 +5148,21 @@ def eta():
        prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
        skip(1)
        return
-    if scanner.next() != "IHREAL":
+    if scanner.nexttok() != "IHREAL":
        prompt = True
        scanner.chew()
        proutn(_("Destination quadrant and/or sector? "))
-       if scanner.next()!="IHREAL":
+       if scanner.nexttok()!="IHREAL":
            huh()
            return
     w1.j = int(scanner.real-0.5)
-    if scanner.next() != "IHREAL":
+    if scanner.nexttok() != "IHREAL":
        huh()
        return
     w1.i = int(scanner.real-0.5)
-    if scanner.next() == "IHREAL":
+    if scanner.nexttok() == "IHREAL":
        w2.j = int(scanner.real-0.5)
-       if scanner.next() != "IHREAL":
+       if scanner.nexttok() != "IHREAL":
            huh()
            return
        w2.i = int(scanner.real-0.5)
@@ -5139,7 +5186,7 @@ def eta():
     while True:
        scanner.chew()
        proutn(_("Time or arrival date? "))
-       if scanner.next()=="IHREAL":
+       if scanner.nexttok()=="IHREAL":
            ttime = scanner.real
            if ttime > game.state.date:
                ttime -= game.state.date # Actually a star date
@@ -5153,7 +5200,7 @@ def eta():
            break
        scanner.chew()
        proutn(_("Warp factor? "))
-       if scanner.next()== "IHREAL":
+       if scanner.nexttok()== "IHREAL":
            wfl = True
            twarp = scanner.real
            if twarp<1.0 or twarp > 10.0:
@@ -5171,7 +5218,7 @@ def eta():
                if not wfl:
                    return
                proutn(_("New warp factor to try? "))
-               if scanner.next() == "IHREAL":
+               if scanner.nexttok() == "IHREAL":
                    wfl = True
                    twarp = scanner.real
                    if twarp<1.0 or twarp > 10.0:
@@ -5204,7 +5251,7 @@ 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 scanner.next() == "IHREAL":
+       if scanner.nexttok() == "IHREAL":
            wfl = True
            twarp = scanner.real
            if twarp<1.0 or twarp > 10.0:
@@ -5230,10 +5277,10 @@ def freeze(boss):
     "Save game."
     if boss:
        scanner.push("emsave.trk")
-    key = scanner.next()
+    key = scanner.nexttok()
     if key == "IHEOL":
         proutn(_("File name: "))
-        key = scanner.next()
+        key = scanner.nexttok()
     if key != "IHALPHA":
         huh()
         return
@@ -5252,10 +5299,10 @@ def thaw():
     "Retrieve saved game."
     global game
     game.passwd = None
-    key = scanner.next()
+    key = scanner.nexttok()
     if key == "IHEOL":
        proutn(_("File name: "))
-       key = scanner.next()
+       key = scanner.nexttok()
     if key != "IHALPHA":
        huh()
        return True
@@ -5390,10 +5437,14 @@ def setup():
     game.instar = 0
     for i in range(GALSIZE):
        for j in range(GALSIZE):
-           k = randrange(1, QUADSIZE**2/10+1)
+            # Can't have more stars per quadrant than fit in one decimal digit,
+            # if we do the chart representation will break. 
+           k = randrange(1, min(10, QUADSIZE**2/10))
            game.instar += k
            game.state.galaxy[i][j].stars = k
     # Locate star bases in galaxy
+    if game.idebug:
+        prout("=== Allocating %d bases" % game.inbase)
     for i in range(game.inbase):
         while True:
             while True:
@@ -5416,6 +5467,8 @@ def setup():
                        prout("=== Saving base #%d, close to #%d" % (i, j))
             if not contflag:
                 break
+        if game.idebug:
+            prout("=== Placing base #%d in quadrant %s" % (i, w))
        game.state.baseq.append(w)
        game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
     # Position ordinary Klingon Battle Cruisers
@@ -5558,9 +5611,9 @@ def choose():
        scanner.chew()
 #      if not scanner.inqueue: # Can start with command line options 
        proutn(_("Would you like a regular, tournament, or saved game? "))
-        scanner.next()
+        scanner.nexttok()
         if scanner.sees("tournament"):
-           while scanner.next() == "IHEOL":
+           while scanner.nexttok() == "IHEOL":
                proutn(_("Type in tournament number-"))
            if scanner.real == 0:
                scanner.chew()
@@ -5586,7 +5639,7 @@ def choose():
        proutn(_("What is \"%s\"? ") % scanner.token)
        scanner.chew()
     while game.length==0 or game.skill==SKILL_NONE:
-       if scanner.next() == "IHALPHA":
+       if scanner.nexttok() == "IHALPHA":
             if scanner.sees("short"):
                game.length = 1
            elif scanner.sees("medium"):
@@ -5614,10 +5667,10 @@ def choose():
            elif game.skill == SKILL_NONE:
                proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
     # Choose game options -- added by ESR for SST2K
-    if scanner.next() != "IHALPHA":
+    if scanner.nexttok() != "IHALPHA":
        scanner.chew()
        proutn(_("Choose your game style (plain, almy, fancy or just press enter): "))
-       scanner.next()
+       scanner.nexttok()
     if scanner.sees("plain"):
        # Approximates the UT FORTRAN version.
        game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
@@ -5748,7 +5801,7 @@ def newqad():
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
     # Put in THING if needed
     if thing == game.quadrant:
-        Enemy(type='?', loc=dropin(),
+        Enemy(etype='?', loc=dropin(),
                   power=randreal(6000,6500.0)+250.0*game.skill)
         if not damaged(DSRSENS):
             skip(1)
@@ -5765,7 +5818,7 @@ def newqad():
                w.j = withprob(0.5) * (QUADSIZE-1)
                 if game.quad[w.i][w.j] == '.':
                     break
-            game.tholian = Enemy(type='T', loc=w,
+            game.tholian = Enemy(etype='T', loc=w,
                                  power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
            if game.quad[0][0]=='.':
@@ -5801,7 +5854,7 @@ def setpassword():
        while True:
            scanner.chew()
            proutn(_("Please type in a secret password- "))
-           scanner.next()
+           scanner.nexttok()
            game.passwd = scanner.token
            if game.passwd != None:
                break
@@ -5870,12 +5923,12 @@ def listCommands():
 
 def helpme():
     "Browse on-line help."
-    key = scanner.next()
+    key = scanner.nexttok()
     while True:
        if key == "IHEOL":
            setwnd(prompt_window)
            proutn(_("Help on what command? "))
-           key = scanner.next()
+           key = scanner.nexttok()
        setwnd(message_window)
        if key == "IHEOL":
            return
@@ -5933,7 +5986,7 @@ def makemoves():
            setwnd(prompt_window)
            clrscr()
            proutn("COMMAND> ")
-           if scanner.next() == "IHEOL":
+           if scanner.nexttok() == "IHEOL":
                if game.options & OPTION_CURSES:
                    makechart()
                continue
@@ -6134,7 +6187,7 @@ class sstscanner:
         self.token = None
         self.real = 0.0
         self.inqueue = []
-    def next(self):
+    def nexttok(self):
         # Get a token from the user
         self.real = 0.0
         self.token = ''
@@ -6185,12 +6238,12 @@ class sstscanner:
         return int(round(scanner.real))
     def getcoord(self):
         s = Coord()
-        scanner.next()
+        scanner.nexttok()
        if scanner.type != "IHREAL":
            huh()
            return None
        s.i = scanner.int()-1
-        scanner.next()
+        scanner.nexttok()
        if scanner.type != "IHREAL":
            huh()
            return None
@@ -6203,7 +6256,7 @@ def ja():
     "Yes-or-no confirmation."
     scanner.chew()
     while True:
-       scanner.next()
+       scanner.nexttok()
        if scanner.token == 'y':
            return True
        if scanner.token == 'n':
@@ -6243,7 +6296,7 @@ def debugme():
        for i in range(NDEVICES):
            proutn("Kill %s?" % device[i])
            scanner.chew()
-           key = scanner.next()
+           key = scanner.nexttok()
             if key == "IHALPHA" and scanner.sees("y"):
                game.damage[i] = 10.0
     proutn("Examine/change events? ")
@@ -6274,7 +6327,7 @@ def debugme():
                proutn("never")
            proutn("? ")
            scanner.chew()
-           key = scanner.next()
+           key = scanner.nexttok()
            if key == 'n':
                unschedule(i)
                scanner.chew()
@@ -6283,7 +6336,7 @@ def debugme():
                if i == FENSLV or i == FREPRO:
                    scanner.chew()
                    proutn("In quadrant- ")
-                   key = scanner.next()
+                   key = scanner.nexttok()
                    # "IHEOL" says to leave coordinates as they are 
                    if key != "IHEOL":
                        if key != "IHREAL":
@@ -6291,7 +6344,7 @@ def debugme():
                            unschedule(i)
                            continue
                        w.i = int(round(scanner.real))
-                       key = scanner.next()
+                       key = scanner.nexttok()
                        if key != "IHREAL":
                            prout("Event %d canceled, no y coordinate." % (i))
                            unschedule(i)
@@ -6318,6 +6371,7 @@ if __name__ == '__main__':
             game.options |= OPTION_TTY
         seed = int(time.time())
         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:txV")
+        replay = False
         for (switch, val) in options:
             if switch == '-r':
                 try:
@@ -6332,6 +6386,7 @@ if __name__ == '__main__':
                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
                     line = replayfp.readline().strip()
                     arguments += line.split()[2:]
+                    replay = True
                 except ValueError:
                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
                     raise SystemExit(1)
@@ -6363,6 +6418,7 @@ if __name__ == '__main__':
         if logfp:
             logfp.write("# seed %s\n" % seed)
             logfp.write("# options %s\n" % " ".join(arguments))
+            logfp.write("# SST2K version %s\n" % version)
             logfp.write("# recorded by %s@%s on %s\n" % \
                     (getpass.getuser(),socket.gethostname(),time.ctime()))
         random.seed(seed)
@@ -6380,6 +6436,8 @@ if __name__ == '__main__':
                     game.alldone = False
                 else:
                     makemoves()
+                if replay:
+                    break
                 skip(1)
                 stars()
                 skip(1)