Equates for display character names made sense when the values were
[super-star-trek.git] / src / sst.py
index 94a7184d417628d66659dc6be2a2bf526a34d6af..637a65db0033cbc66e1be7d5caee8881cd37ee4f 100644 (file)
 #!/usr/bin/env python
 """
 #!/usr/bin/env python
 """
-sst.py =-- Super Star Trek in Python
-
-This code is a Python translation of a C translation of a FORTRAN
-original dating back to 1973.  Beautiful Python it is not.  But it
-works.
-
-Dave Matuszek says:
-
-SRSCAN, MOVE, PHASERS, CALL, STATUS, IMPULSE, PHOTONS, ABANDON,
-LRSCAN, WARP, SHIELDS, DESTRUCT, CHART, REST, DOCK, QUIT, and DAMAGE
-were in the original non-"super" version of UT FORTRAN Star Trek.
-
-Tholians were not in the original. Dave is dubious about their merits.
-(They are now controlled by OPTION_THOLIAN and turned off if the game
-type is "plain".)
-
-Planets and dilithium crystals were not in the original.  Dave is OK
-with this idea. (It's now controlled by OPTION_PLANETS and turned 
-off if the game type is "plain".)
+sst.py -- Super Star Trek 2K
 
 
-Dave says the bit about the Galileo getting turned into a
-McDonald's is "consistant with our original vision".  (This has been
-left permanently enabled, as it can only happen if OPTION_PLANETS
-is on.)
+SST2K is a Python translation of a C translation of a FORTRAN
+original dating back to 1973.  Beautiful Python it is not, but it
+works.  Translation by Eric S. Raymond; original game by David Matuszek
+and Paul Reynolds, with modifications by Don Smith, Tom Almy,
+Stas Sergeev, and Eric S. Raymond.
 
 
-Dave also says the Space Thingy should not be preserved across saved
-games, so you can't prove to others that you've seen it.  He says it
-shouldn't fire back, either.  It should do nothing except scream and
-disappear when hit by photon torpedos.  It's OK that it may move
-when attacked, but it didn't in the original.  (Whether the Thingy
-can fire back is now controlled by OPTION_THINGY and turned off if the
-game type is "plain" or "almy".  The no-save behavior has been restored.)
-
-The Faerie Queen, black holes, and time warping were in the original.
-
-Here are Tom Almy's changes:
-
-In early 1997, I got the bright idea to look for references to
-"Super Star Trek" on the World Wide Web. There weren't many hits,
-but there was one that came up with 1979 Fortran sources! This
-version had a few additional features that mine didn't have,
-however mine had some feature it didn't have. So I merged its
-features that I liked. I also took a peek at the DECUS version (a
-port, less sources, to the PDP-10), and some other variations.
-
-1, Compared to the original UT version, I've changed the "help"
-command to "call" and the "terminate" command to "quit" to better
-match user expectations. The DECUS version apparently made those
-changes as well as changing "freeze" to "save". However I like
-"freeze".  (Both "freeze" and "save" work in SST2K.)
-
-2. The experimental deathray originally had only a 5% chance of
-success, but could be used repeatedly. I guess after a couple
-years of use, it was less "experimental" because the 1979
-version had a 70% success rate. However it was prone to breaking
-after use. I upgraded the deathray, but kept the original set of
-failure modes (great humor!).  (Now controlled by OPTION_DEATHRAY
-and turned off if game type is "plain".)
-
-3. The 1979 version also mentions srscan and lrscan working when
-docked (using the starbase's scanners), so I made some changes here
-to do this (and indicating that fact to the player), and then realized
-the base would have a subspace radio as well -- doing a Chart when docked
-updates the star chart, and all radio reports will be heard. The Dock
-command will also give a report if a base is under attack.
-
-4. Tholian Web from the 1979 version.  (Now controlled by
-OPTION_THOLIAN and turned off if game type is "plain".)
-
-5. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
-and turned off if game type is "plain".)
-
-6. Regular Klingons and Romulans can move in Expert and Emeritus games. 
-This code could use improvement. (Now controlled by OPTION_MVBADDY
-and turned off if game type is "plain".)
-
-7. The deep-space probe feature from the DECUS version.  (Now controlled
-by OPTION_PROBE and turned off if game type is "plain").
-
-8. 'emexit' command from the 1979 version.
-
-9. Bugfix: Klingon commander movements are no longer reported if long-range 
-sensors are damaged.
-
-10. Bugfix: Better base positioning at startup (more spread out).
-That made sense to add because most people abort games with 
-bad base placement.
-
-In June 2002, I fixed two known bugs and a documentation typo.
-In June 2004 I fixed a number of bugs involving: 1) parsing invalid
-numbers, 2) manual phasers when SR scan is damaged and commander is
-present, 3) time warping into the future, 4) hang when moving
-klingons in crowded quadrants.  (These fixes are in SST2K.)
-
-Here are Stas Sergeev's changes:
-
-1. The Space Thingy can be shoved, if you ram it, and can fire back if 
-fired upon. (Now controlled by OPTION_THINGY and turned off if game 
-type is "plain" or "almy".)
-
-2. When you are docked, base covers you with an almost invincible shield. 
-(A commander can still ram you, or a Romulan can destroy the base,
-or a SCom can even succeed with direct attack IIRC, but this rarely 
-happens.)  (Now controlled by OPTION_BASE and turned off if game 
-type is "plain" or "almy".)
-
-3. Ramming a black hole is no longer instant death.  There is a
-chance you might get timewarped instead. (Now controlled by 
-OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
-
-4. The Tholian can be hit with phasers.
-
-5. SCom can't escape from you if no more enemies remain 
-(without this, chasing SCom can take an eternity).
-
-6. Probe target you enter is now the destination quadrant. Before I don't 
-remember what it was, but it was something I had difficulty using.
-
-7. Secret password is now autogenerated.
-
-8. "Plaque" is adjusted for A4 paper :-)
-
-9. Phasers now tells you how much energy needed, but only if the computer 
-is alive.
-
-10. Planets are auto-scanned when you enter the quadrant.
-
-11. Mining or using crystals in presense of enemy now yields an attack.
-There are other minor adjustments to what yields an attack
-and what does not.
-
-12. "freeze" command reverts to "save", most people will understand this
-better anyway. (SST2K recognizes both.)
-
-13. Screen-oriented interface, with sensor scans always up.  (SST2K
-supports both screen-oriented and TTY modes.)
-
-Eric Raymond's changes:
-
-Mainly, I translated this C code out of FORTRAN into C -- created #defines
-for a lot of magic numbers and refactored the heck out of it.
-
-1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
-
-2. Status report now indicates when dilithium crystals are on board.
-
-3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
-
-4. Added game option selection so you can play a close (but not bug-for-
-bug identical) approximation of older versions.
-
-5. Half the quadrants now have inhabited planets, from which one 
-cannot mine dilithium (there will still be the same additional number
-of dilithium-bearing planets).  Torpedoing an inhabited world is *bad*.
-There is BSD-Trek-like logic for Klingons to attack and enslave 
-inhabited worlds, producing more ships (only is skill is 'good' or 
-better). (Controlled by OPTION_WORLDS and turned off if game 
-type is "plain" or "almy".)
-
-6. User input is now logged so we can do regression testing.
-
-7. More BSD-Trek features: You can now lose if your entire crew
-dies in battle.  When abandoning ship in a game with inhabited
-worlds enabled, they must have one in the quadrant to beam down
-to; otherwise they die in space and this counts heavily against
-your score.  Docking at a starbase replenishes your crew.
-
-8. Still more BSD-Trek: we now have a weighted damage table.
-Also, the nav subsystem (enabling automatic course
-setting) can be damaged separately from the main computer (which
-handles weapons targeting, ETA calculation, and self-destruct).
-
-After these features were added, I translated this into Python and added
-more:
-
-9. A long-range scan is done silently whenever you call CHART; thus
-the LRSCAN command is no longer needed.  (Controlled by OPTION_AUTOSCAN
-and turned off if game type is "plain" or "almy".)
+See the doc/HACKING file in the distribution for designers notes and advice
+ion how to modify (and how not to modify!) this code.
 """
 import os, sys, math, curses, time, readline, cPickle, random, copy, gettext
 
 """
 import os, sys, math, curses, time, readline, cPickle, random, copy, gettext
 
@@ -201,40 +33,25 @@ FOREVER    = 1e30
 MAXBURST       = 3
 MINCMDR        = 10
 
 MAXBURST       = 3
 MINCMDR        = 10
 
-# 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)
-
-# How to represent features
-IHR = 'R',
-IHK = 'K',
-IHC = 'C',
-IHS = 'S',
-IHSTAR = '*',
-IHP = 'P',
-IHW = '@',
-IHB = 'B',
-IHBLANK = ' ',
-IHDOT = '.',
-IHQUEST = '?',
-IHE = 'E',
-IHF = 'F',
-IHT = 'T',
-IHWEB = '#',
-IHMATER0 = '-',
-IHMATER1 = 'o',
-IHMATER2 = '0'
+class TrekError:
+    pass
 
 class coord:
     def __init__(self, x=None, y=None):
         self.i = x
         self.j = y
 
 class coord:
     def __init__(self, x=None, y=None):
         self.i = x
         self.j = y
+    def valid_quadrant(self):
+        return self.i>=0 and self.i<GALSIZE and self.j>=0 and self.j<GALSIZE
+    def valid_sector(self):
+       return self.i>=0 and self.i<QUADSIZE and self.j>=0 and self.j<QUADSIZE
     def invalidate(self):
         self.i = self.j = None
     def is_valid(self):
         return self.i != None and self.j != None
     def __eq__(self, other):
         return other != None and self.i == other.i and self.j == other.j
     def invalidate(self):
         self.i = self.j = None
     def is_valid(self):
         return self.i != None and self.j != None
     def __eq__(self, other):
         return other != None and self.i == other.i and self.j == other.j
+    def __ne__(self, other):
+        return other == None or self.i != other.i or self.j != other.j
     def __add__(self, other):
         return coord(self.i+other.i, self.j+other.j)
     def __sub__(self, other):
     def __add__(self, other):
         return coord(self.i+other.i, self.j+other.j)
     def __sub__(self, other):
@@ -245,16 +62,17 @@ class coord:
         return coord(self.i*other, self.j*other)
     def __div__(self, other):
         return coord(self.i/other, self.j/other)
         return coord(self.i*other, self.j*other)
     def __div__(self, other):
         return coord(self.i/other, self.j/other)
+    def __mod__(self, other):
+        return coord(self.i % other, self.j % other)
     def __rdiv__(self, other):
         return coord(self.i/other, self.j/other)
     def __rdiv__(self, other):
         return coord(self.i/other, self.j/other)
-    def snaptogrid(self):
+    def roundtogrid(self):
         return coord(int(round(self.i)), int(round(self.j)))
     def distance(self, other=None):
         if not other: other = coord(0, 0)
         return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
         return coord(int(round(self.i)), int(round(self.j)))
     def distance(self, other=None):
         if not other: other = coord(0, 0)
         return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
-    def bearing(self, other=None):
-        if not other: other = coord(0, 0)
-        return 1.90985*math.atan2(self.j-other.j, self.i-other.i)
+    def bearing(self):
+        return 1.90985*math.atan2(self.j, self.i)
     def sgn(self):
         s = coord()
         if self.i == 0:
     def sgn(self):
         s = coord()
         if self.i == 0:
@@ -266,13 +84,16 @@ class coord:
         else:
             s.j = self.j / abs(self.j)
         return s
         else:
             s.j = self.j / abs(self.j)
         return s
+    def quadrant(self):
+        #print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
+        return self.roundtogrid() / QUADSIZE
+    def sector(self):
+        return self.roundtogrid() % QUADSIZE
     def scatter(self):
         s = coord()
         s.i = self.i + randrange(-1, 2)
         s.j = self.j + randrange(-1, 2)
         return s
     def scatter(self):
         s = coord()
         s.i = self.i + randrange(-1, 2)
         s.j = self.j + randrange(-1, 2)
         return s
-    def __hash__(self):
-        return hash((x, y))
     def __str__(self):
         if self.i == None or self.j == None:
             return "Nowhere"
     def __str__(self):
         if self.i == None or self.j == None:
             return "Nowhere"
@@ -426,10 +247,10 @@ class enemy:
         motion = (loc != self.kloc)
         if self.kloc.i is not None and self.kloc.j is not None:
             if motion:
         motion = (loc != self.kloc)
         if self.kloc.i is not None and self.kloc.j is not None:
             if motion:
-                if self.type == IHT:
-                    game.quad[self.kloc.i][self.kloc.j] = IHWEB
+                if self.type == 'T':
+                    game.quad[self.kloc.i][self.kloc.j] = '#'
                 else:
                 else:
-                    game.quad[self.kloc.i][self.kloc.j] = IHDOT
+                    game.quad[self.kloc.i][self.kloc.j] = '.'
         if loc:
             self.kloc = copy.copy(loc)
             game.quad[self.kloc.i][self.kloc.j] = self.type
         if loc:
             self.kloc = copy.copy(loc)
             game.quad[self.kloc.i][self.kloc.j] = self.type
@@ -460,7 +281,6 @@ class gamestate:
         self.base = None       # position of base in current quadrant
         self.battle = None     # base coordinates being attacked
         self.plnet = None      # location of planet in quadrant
         self.base = None       # position of base in current quadrant
         self.battle = None     # base coordinates being attacked
         self.plnet = None      # location of planet in quadrant
-        self.probec = None     # current probe quadrant
         self.gamewon = False   # Finished!
         self.ididit = False    # action taken -- allows enemy to attack
         self.alive = False     # we are alive (not killed)
         self.gamewon = False   # Finished!
         self.ididit = False    # action taken -- allows enemy to attack
         self.alive = False     # we are alive (not killed)
@@ -503,7 +323,6 @@ class gamestate:
         self.irhere = 0                # Romulans in quadrant
         self.isatb = 0         # =1 if super commander is attacking base
         self.tourn = None      # tournament number
         self.irhere = 0                # Romulans in quadrant
         self.isatb = 0         # =1 if super commander is attacking base
         self.tourn = None      # tournament number
-        self.proben = 0                # number of moves for probe
         self.nprobes = 0       # number of probes available
         self.inresor = 0.0     # initial resources
         self.intime = 0.0      # initial time
         self.nprobes = 0       # number of probes available
         self.inresor = 0.0     # initial resources
         self.intime = 0.0      # initial time
@@ -516,15 +335,12 @@ class gamestate:
         self.warpfac = 0.0     # warp speed
         self.wfacsq = 0.0      # squared warp factor
         self.lsupres = 0.0     # life support reserves
         self.warpfac = 0.0     # warp speed
         self.wfacsq = 0.0      # squared warp factor
         self.lsupres = 0.0     # life support reserves
-        self.dist = 0.0                # movement distance
-        self.direc = 0.0       # movement direction
         self.optime = 0.0      # time taken by current operation
         self.docfac = 0.0      # repair factor when docking (constant?)
         self.damfac = 0.0      # damage factor
         self.lastchart = 0.0   # time star chart was last updated
         self.cryprob = 0.0     # probability that crystal will work
         self.optime = 0.0      # time taken by current operation
         self.docfac = 0.0      # repair factor when docking (constant?)
         self.damfac = 0.0      # damage factor
         self.lastchart = 0.0   # time star chart was last updated
         self.cryprob = 0.0     # probability that crystal will work
-        self.probe = None      # location of probe
-        self.probein = None    # probe i,j increment
+        self.probe = None      # object holding probe course info
         self.height = 0.0      # height of orbit around planet
     def recompute(self):
         # Stas thinks this should be (C expression): 
         self.height = 0.0      # height of orbit around planet
     def recompute(self):
         # Stas thinks this should be (C expression): 
@@ -535,25 +351,6 @@ class gamestate:
         # if the only remaining klingon is SCOM.
         game.state.remtime = game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr))
 
         # if the only remaining klingon is SCOM.
         game.state.remtime = game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr))
 
-IHR = 'R'
-IHK = 'K'
-IHC = 'C'
-IHS = 'S'
-IHSTAR = '*'
-IHP = 'P'
-IHW = '@'
-IHB = 'B'
-IHBLANK = ' '
-IHDOT = '.'
-IHQUEST = '?'
-IHE = 'E'
-IHF = 'F'
-IHT = 'T'
-IHWEB = '#'
-IHMATER0 = '-'
-IHMATER1 = 'o'
-IHMATER2 = '0'
-
 FWON = 0
 FDEPLETE = 1
 FLIFESUP = 2
 FWON = 0
 FDEPLETE = 1
 FLIFESUP = 2
@@ -600,7 +397,7 @@ def randreal(*args):
 
 def welcoming(iq):
     "Would this quadrant welcome another Klingon?"
 
 def welcoming(iq):
     "Would this quadrant welcome another Klingon?"
-    return VALID_QUADRANT(iq.i,iq.j) and \
+    return iq.valid_quadrant() and \
        not game.state.galaxy[iq.i][iq.j].supernova and \
        game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
 
        not game.state.galaxy[iq.i][iq.j].supernova and \
        game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
 
@@ -611,11 +408,11 @@ def tryexit(enemy, look, irun):
     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
     if not welcoming(iq):
        return False;
     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
     if not welcoming(iq):
        return False;
-    if enemy.type == IHR:
+    if enemy.type == 'R':
        return False; # Romulans cannot escape! 
     if not irun:
        # avoid intruding on another commander's territory 
        return False; # Romulans cannot escape! 
     if not irun:
        # avoid intruding on another commander's territory 
-       if enemy.type == IHC:
+       if enemy.type == 'C':
             if iq in game.state.kcmdr:
                 return False
            # refuse to leave if currently attacking starbase 
             if iq in game.state.kcmdr:
                 return False
            # refuse to leave if currently attacking starbase 
@@ -638,7 +435,7 @@ def tryexit(enemy, look, irun):
     # Handle global matters related to escape 
     game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
     game.state.galaxy[iq.i][iq.j].klingons += 1
     # Handle global matters related to escape 
     game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
     game.state.galaxy[iq.i][iq.j].klingons += 1
-    if enemy.type==IHS:
+    if enemy.type=='S':
        game.iscate = False
        game.ientesc = False
        game.isatb = 0
        game.iscate = False
        game.ientesc = False
        game.isatb = 0
@@ -702,7 +499,7 @@ def movebaddy(enemy):
     dist1 = enemy.kdist
     mdist = int(dist1 + 0.5); # Nearest integer distance 
     # If SC, check with spy to see if should hi-tail it 
     dist1 = enemy.kdist
     mdist = int(dist1 + 0.5); # Nearest integer distance 
     # If SC, check with spy to see if should hi-tail it 
-    if enemy.type==IHS and \
+    if enemy.type=='S' and \
        (enemy.kpower <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
        irun = True
        motion = -QUADSIZE
        (enemy.kpower <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
        irun = True
        motion = -QUADSIZE
@@ -792,10 +589,10 @@ def movebaddy(enemy):
                    break
                look.j = next.j + krawlj
                krawlj = -krawlj
                    break
                look.j = next.j + krawlj
                krawlj = -krawlj
-           elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != IHDOT:
+           elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
                # See if enemy should ram ship 
                if game.quad[look.i][look.j] == game.ship and \
                # See if enemy should ram ship 
                if game.quad[look.i][look.j] == game.ship and \
-                   (enemy.type == IHC or enemy.type == IHS):
+                   (enemy.type == 'C' or enemy.type == 'S'):
                    collision(rammed=True, enemy=enemy)
                    return
                if krawli != m.i and m.j != 0:
                    collision(rammed=True, enemy=enemy)
                    return
                if krawli != m.i and m.j != 0:
@@ -833,11 +630,11 @@ def moveklings():
     # and do move
     if game.quadrant in game.state.kcmdr:
         for enemy in game.enemies:
     # and do move
     if game.quadrant in game.state.kcmdr:
         for enemy in game.enemies:
-           if enemy.type == IHC:
+           if enemy.type == 'C':
                movebaddy(enemy)
     if game.state.kscmdr==game.quadrant:
         for enemy in game.enemies:
                movebaddy(enemy)
     if game.state.kscmdr==game.quadrant:
         for enemy in game.enemies:
-           if enemy.type == IHS:
+           if enemy.type == 'S':
                movebaddy(enemy)
                break
     # If skill level is high, move other Klingons and Romulans too!
                movebaddy(enemy)
                break
     # If skill level is high, move other Klingons and Romulans too!
@@ -845,7 +642,7 @@ def moveklings():
     # commander(s) do.
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
         for enemy in game.enemies:
     # commander(s) do.
     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
         for enemy in game.enemies:
-            if enemy.type in (IHK, IHR):
+            if enemy.type in ('K', 'R'):
                movebaddy(enemy)
     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
 
                movebaddy(enemy)
     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
 
@@ -867,7 +664,7 @@ def movescom(iq, avoid):
        game.ientesc = False
        unschedule(FSCDBAS)
        for enemy in game.enemies:
        game.ientesc = False
        unschedule(FSCDBAS)
        for enemy in game.enemies:
-           if enemy.type == IHS:
+           if enemy.type == 'S':
                break
        enemy.move(None)
        game.klhere -= 1
                break
        enemy.move(None)
        game.klhere -= 1
@@ -1027,34 +824,34 @@ def movetholian():
         prout("***Internal error: Tholian in a bad spot.")
        return
     # do nothing if we are blocked 
         prout("***Internal error: Tholian in a bad spot.")
        return
     # do nothing if we are blocked 
-    if game.quad[id.i][id.j] not in (IHDOT, IHWEB):
+    if game.quad[id.i][id.j] not in ('.', '#'):
        return
     here = copy.copy(game.tholian.kloc)
     delta = (id - game.tholian.kloc).sgn()
     # move in x axis 
     while here.i != id.i:
         here.i += delta.i
        return
     here = copy.copy(game.tholian.kloc)
     delta = (id - game.tholian.kloc).sgn()
     # move in x axis 
     while here.i != id.i:
         here.i += delta.i
-        if game.quad[here.i][here.j]==IHDOT:
+        if game.quad[here.i][here.j]=='.':
             game.tholian.move(here)
     # move in y axis 
     while here.j != id.j:
         here.j += delta.j
             game.tholian.move(here)
     # move in y axis 
     while here.j != id.j:
         here.j += delta.j
-        if game.quad[here.i][here.j]==IHDOT:
+        if game.quad[here.i][here.j]=='.':
             game.tholian.move(here)
     # check to see if all holes plugged 
     for i in range(QUADSIZE):
             game.tholian.move(here)
     # check to see if all holes plugged 
     for i in range(QUADSIZE):
-       if game.quad[0][i]!=IHWEB and game.quad[0][i]!=IHT:
+       if game.quad[0][i]!='#' and game.quad[0][i]!='T':
            return
            return
-       if game.quad[QUADSIZE-1][i]!=IHWEB and game.quad[QUADSIZE-1][i]!=IHT:
+       if game.quad[QUADSIZE-1][i]!='#' and game.quad[QUADSIZE-1][i]!='T':
            return
            return
-       if game.quad[i][0]!=IHWEB and game.quad[i][0]!=IHT:
+       if game.quad[i][0]!='#' and game.quad[i][0]!='T':
            return
            return
-       if game.quad[i][QUADSIZE-1]!=IHWEB and game.quad[i][QUADSIZE-1]!=IHT:
+       if game.quad[i][QUADSIZE-1]!='#' and game.quad[i][QUADSIZE-1]!='T':
            return
     # All plugged up -- Tholian splits 
            return
     # All plugged up -- Tholian splits 
-    game.quad[game.tholian.kloc.i][game.tholian.kloc.j]=IHWEB
-    dropin(IHBLANK)
-    prout(crmena(True, IHT, "sector", game.tholian) + _(" completes web."))
+    game.quad[game.tholian.kloc.i][game.tholian.kloc.j]='#'
+    dropin(' ')
+    prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
     game.tholian.move(None)
     return
 
     game.tholian.move(None)
     return
 
@@ -1224,7 +1021,7 @@ def collision(rammed, enemy):
     skip(2)
     proutn("***")
     proutn(crmshp())
     skip(2)
     proutn("***")
     proutn(crmshp())
-    hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(enemy.type, 1.0)
+    hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
     if rammed:
         proutn(_(" rammed by "))
     else:
     if rammed:
         proutn(_(" rammed by "))
     else:
@@ -1259,66 +1056,63 @@ def collision(rammed, enemy):
        finish(FWON)
     return
 
        finish(FWON)
     return
 
-def torpedo(origin, course, dispersion, number, nburst):
+def torpedo(origin, bearing, dispersion, number, nburst):
     "Let a photon torpedo fly" 
     if not damaged(DSRSENS) or game.condition=="docked":
        setwnd(srscan_window)
     else: 
        setwnd(message_window)
     "Let a photon torpedo fly" 
     if not damaged(DSRSENS) or game.condition=="docked":
        setwnd(srscan_window)
     else: 
        setwnd(message_window)
-    shoved = False
-    ac = course + 0.25*dispersion
-    angle = (15.0-ac)*0.5235988
-    bullseye = (15.0 - course)*0.5235988
-    delta = coord(-math.sin(angle), math.cos(angle))          
-    bigger = max(abs(delta.i), abs(delta.j))
-    delta /= bigger
-    w = coord(0, 0); jw = coord(0, 0)
-    ungridded = copy.copy(origin)
+    ac = bearing + 0.25*dispersion     # dispersion is a random variable
+    bullseye = (15.0 - bearing)*0.5235988
+    track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin)) 
+    bumpto = coord(0, 0)
     # Loop to move a single torpedo 
     # Loop to move a single torpedo 
+    setwnd(message_window)
     for step in range(1, QUADSIZE*2):
     for step in range(1, QUADSIZE*2):
-       ungridded += delta
-       w = ungridded.snaptogrid()
-       if not VALID_SECTOR(w.i, w.j):
+        if not track.next(): break
+        w = track.sector()
+       if not w.valid_sector():
            break
        iquad=game.quad[w.i][w.j]
        tracktorpedo(origin, w, step, number, nburst, iquad)
            break
        iquad=game.quad[w.i][w.j]
        tracktorpedo(origin, w, step, number, nburst, iquad)
-       if iquad==IHDOT:
+       if iquad=='.':
            continue
        # hit something 
            continue
        # hit something 
-       setwnd(message_window)
-       if damaged(DSRSENS) and not game.condition=="docked":
+       if not damaged(DSRSENS) or game.condition == "docked":
            skip(1);    # start new line after text track 
            skip(1);    # start new line after text track 
-       if iquad in (IHE, IHF): # Hit our ship 
+       if iquad in ('E', 'F'): # Hit our ship 
            skip(1)
            prout(_("Torpedo hits %s.") % crmshp())
            hit = 700.0 + randreal(100) - \
            skip(1)
            prout(_("Torpedo hits %s.") % crmshp())
            hit = 700.0 + randreal(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            newcnd(); # we're blown out of dock 
            newcnd(); # we're blown out of dock 
-           # We may be displaced. 
            if game.landed or game.condition=="docked":
                return hit # Cheat if on a planet 
            if game.landed or game.condition=="docked":
                return hit # Cheat if on a planet 
-           ang = angle + 2.5*(randreal()-0.5)
-           temp = math.fabs(math.sin(ang))
-           if math.fabs(math.cos(ang)) > temp:
-               temp = math.fabs(math.cos(ang))
-           xx = -math.sin(ang)/temp
-           yy = math.cos(ang)/temp
-           jw.i = int(w.i+xx+0.5)
-           jw.j = int(w.j+yy+0.5)
-           if not VALID_SECTOR(jw.i, jw.j):
+            # 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()
+            bumpto = displacement.sector()
+           if not bumpto.valid_sector():
                return hit
                return hit
-           if game.quad[jw.i][jw.j]==IHBLANK:
+           if game.quad[bumpto.i][bumpto.j]==' ':
                finish(FHOLE)
                return hit
                finish(FHOLE)
                return hit
-           if game.quad[jw.i][jw.j]!=IHDOT:
+           if game.quad[bumpto.i][bumpto.j]!='.':
                # can't move into object 
                return hit
                # can't move into object 
                return hit
-           game.sector = jw
+           game.sector = bumpto
            proutn(crmshp())
            proutn(crmshp())
-           shoved = True
-       elif iquad in (IHC, IHS, IHR, IHK): # Hit a regular enemy 
+            game.quad[w.i][w.j]='.'
+            game.quad[bumpto.i][bumpto.j]=iquad
+            prout(_(" displaced by blast to Sector %s ") % bumpto)
+            for enemy in game.enemies:
+                enemy.kdist = enemy.kavgd = (game.sector-enemy.kloc).distance()
+            game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+            return None
+       elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy 
            # find the enemy 
            # find the enemy 
-           if iquad in (IHC, IHS) and withprob(0.05):
+           if iquad in ('C', 'S') and withprob(0.05):
                prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
                prout(_("   torpedo neutralized."))
                return None
                prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
                prout(_("   torpedo neutralized."))
                return None
@@ -1327,7 +1121,7 @@ def torpedo(origin, course, dispersion, number, nburst):
                    break
            kp = math.fabs(enemy.kpower)
            h1 = 700.0 + randrange(100) - \
                    break
            kp = math.fabs(enemy.kpower)
            h1 = 700.0 + randrange(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            h1 = math.fabs(h1)
            if kp < h1:
                h1 = kp
            h1 = math.fabs(h1)
            if kp < h1:
                h1 = kp
@@ -1339,74 +1133,69 @@ def torpedo(origin, course, dispersion, number, nburst):
                deadkl(w, iquad, w)
                return None
            proutn(crmena(True, iquad, "sector", w))
                deadkl(w, iquad, w)
                return None
            proutn(crmena(True, iquad, "sector", w))
-           # If enemy damaged but not destroyed, try to displace 
-           ang = angle + 2.5*(randreal()-0.5)
-           temp = math.fabs(math.sin(ang))
-           if math.fabs(math.cos(ang)) > temp:
-               temp = math.fabs(math.cos(ang))
-           xx = -math.sin(ang)/temp
-           yy = math.cos(ang)/temp
-           jw.i = int(w.i+xx+0.5)
-           jw.j = int(w.j+yy+0.5)
-           if not VALID_SECTOR(jw.i, jw.j):
+            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
                prout(_(" damaged but not destroyed."))
                return
-           if game.quad[jw.i][jw.j]==IHBLANK:
+           if game.quad[bumpto.i][bumpto.j] == ' ':
                prout(_(" buffeted into black hole."))
                prout(_(" buffeted into black hole."))
-               deadkl(w, iquad, jw)
-               return None
-           if game.quad[jw.i][jw.j]!=IHDOT:
-               # can't move into object 
+               deadkl(w, iquad, bumpto)
+           if game.quad[bumpto.i][bumpto.j] != '.':
                prout(_(" damaged but not destroyed."))
                prout(_(" damaged but not destroyed."))
-               return None
-           proutn(_(" damaged--"))
-           enemy.kloc = jw
-           shoved = True
-           break
-       elif iquad == IHB: # Hit a base 
+            else:
+                prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
+                enemy.kloc = 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.kloc).distance()
+                game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+            return None
+       elif iquad == 'B': # Hit a base 
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
             game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
             game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
-           game.quad[w.i][w.j]=IHDOT
+           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.basekl += 1
            newcnd()
            return None
            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.basekl += 1
            newcnd()
            return None
-       elif iquad == IHP: # Hit a planet 
+       elif iquad == 'P': # Hit a planet 
            prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
            game.state.nplankl += 1
            game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
            game.plnet.invalidate()
            prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
            game.state.nplankl += 1
            game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
            game.plnet.invalidate()
-           game.quad[w.i][w.j] = IHDOT
+           game.quad[w.i][w.j] = '.'
            if game.landed:
                # captain perishes on planet 
                finish(FDPLANET)
            return None
            if game.landed:
                # captain perishes on planet 
                finish(FDPLANET)
            return None
-       elif iquad == IHW: # Hit an inhabited world -- very bad! 
+       elif iquad == '@': # Hit an inhabited world -- very bad! 
            prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
            game.state.nworldkl += 1
            game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
            game.plnet.invalidate()
            prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
            game.state.nworldkl += 1
            game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
            game.plnet.invalidate()
-           game.quad[w.i][w.j] = IHDOT
+           game.quad[w.i][w.j] = '.'
            if game.landed:
                # captain perishes on planet 
                finish(FDPLANET)
            if game.landed:
                # captain perishes on planet 
                finish(FDPLANET)
-           prout(_("You have just destroyed an inhabited planet."))
-           prout(_("Celebratory rallies are being held on the Klingon homeworld."))
+           prout(_("The torpedo destroyed an inhabited planet."))
            return None
            return None
-       elif iquad == IHSTAR: # Hit a star 
+       elif iquad == '*': # Hit a star 
            if withprob(0.9):
                nova(w)
             else:
            if withprob(0.9):
                nova(w)
             else:
-                prout(crmena(True, IHSTAR, "sector", w) + _(" unaffected by photon blast."))
+                prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
            return None
            return None
-       elif iquad == IHQUEST: # Hit a thingy 
+       elif iquad == '?': # Hit a thingy 
            if not (game.options & OPTION_THINGY) or withprob(0.3):
                skip(1)
                prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
            if not (game.options & OPTION_THINGY) or withprob(0.3):
                skip(1)
                prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
@@ -1424,32 +1213,32 @@ def torpedo(origin, course, dispersion, number, nburst):
                thing.angry = True
                shoved = True
            return None
                thing.angry = True
                shoved = True
            return None
-       elif iquad == IHBLANK: # Black hole 
+       elif iquad == ' ': # Black hole 
            skip(1)
            skip(1)
-           prout(crmena(True, IHBLANK, "sector", w) + _(" swallows torpedo."))
+           prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
            return None
            return None
-       elif iquad == IHWEB: # hit the web 
+       elif iquad == '#': # hit the web 
            skip(1)
            prout(_("***Torpedo absorbed by Tholian web."))
            return None
            skip(1)
            prout(_("***Torpedo absorbed by Tholian web."))
            return None
-       elif iquad == IHT:  # Hit a Tholian 
+       elif iquad == 'T':  # Hit a Tholian 
            h1 = 700.0 + randrange(100) - \
                1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
            h1 = math.fabs(h1)
            if h1 >= 600:
            h1 = 700.0 + randrange(100) - \
                1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
            h1 = math.fabs(h1)
            if h1 >= 600:
-               game.quad[w.i][w.j] = IHDOT
+               game.quad[w.i][w.j] = '.'
                deadkl(w, iquad, w)
                game.tholian = None
                return None
            skip(1)
                deadkl(w, iquad, w)
                game.tholian = None
                return None
            skip(1)
-           proutn(crmena(True, IHT, "sector", w))
+           proutn(crmena(True, 'T', "sector", w))
            if withprob(0.05):
                prout(_(" survives photon blast."))
                return None
            prout(_(" disappears."))
            game.tholian.move(None)
            if withprob(0.05):
                prout(_(" survives photon blast."))
                return None
            prout(_(" disappears."))
            game.tholian.move(None)
-           game.quad[w.i][w.j] = IHWEB
-           dropin(IHBLANK)
+           game.quad[w.i][w.j] = '#'
+           dropin(' ')
            return None
         else: # Problem!
            skip(1)
            return None
         else: # Problem!
            skip(1)
@@ -1458,16 +1247,6 @@ def torpedo(origin, course, dispersion, number, nburst):
            skip(1)
            return None
        break
            skip(1)
            return None
        break
-    if curwnd!=message_window:
-       setwnd(message_window)
-    if shoved:
-       game.quad[w.i][w.j]=IHDOT
-       game.quad[jw.i][jw.j]=iquad
-       prout(_(" displaced by blast to Sector %s ") % jw)
-       for ll in range(len(game.enemies)):
-           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)
     prout(_("Torpedo missed."))
     return None;
     skip(1)
     prout(_("Torpedo missed."))
     return None;
@@ -1544,15 +1323,15 @@ def attack(torps_ok):
            r *= 0.25
        if enemy.kpower < 500:
            r *= 0.25; 
            r *= 0.25
        if enemy.kpower < 500:
            r *= 0.25; 
-       if enemy.type==IHT or (enemy.type==IHQUEST and not thing.angry):
+       if enemy.type=='T' or (enemy.type=='?' and not thing.angry):
            continue
        # different enemies have different probabilities of throwing a torp 
        usephasers = not torps_ok or \
            continue
        # different enemies have different probabilities of throwing a torp 
        usephasers = not torps_ok or \
-           (enemy.type == IHK and r > 0.0005) or \
-           (enemy.type==IHC and r > 0.015) or \
-           (enemy.type==IHR and r > 0.3) or \
-           (enemy.type==IHS and r > 0.07) or \
-           (enemy.type==IHQUEST and r > 0.05)
+           (enemy.type == 'K' and r > 0.0005) or \
+           (enemy.type=='C' and r > 0.015) or \
+           (enemy.type=='R' and r > 0.3) or \
+           (enemy.type=='S' and r > 0.07) or \
+           (enemy.type=='?' and r > 0.05)
        if usephasers:      # Enemy uses phasers 
            if game.condition == "docked":
                continue; # Don't waste the effort! 
        if usephasers:      # Enemy uses phasers 
            if game.condition == "docked":
                continue; # Don't waste the effort! 
@@ -1653,15 +1432,15 @@ def deadkl(w, type, mv):
     # Added mv to allow enemy to "move" before dying 
     proutn(crmena(True, type, "sector", mv))
     # Decide what kind of enemy it is and update appropriately 
     # Added mv to allow enemy to "move" before dying 
     proutn(crmena(True, type, "sector", mv))
     # Decide what kind of enemy it is and update appropriately 
-    if type == IHR:
+    if type == 'R':
         # Chalk up a Romulan 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
         game.irhere -= 1
         game.state.nromrem -= 1
         # Chalk up a Romulan 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
         game.irhere -= 1
         game.state.nromrem -= 1
-    elif type == IHT:
+    elif type == 'T':
         # Killed a Tholian 
         game.tholian = None
         # Killed a Tholian 
         game.tholian = None
-    elif type == IHQUEST:
+    elif type == '?':
         # Killed a Thingy
         global thing
         thing = None
         # Killed a Thingy
         global thing
         thing = None
@@ -1669,16 +1448,16 @@ def deadkl(w, type, mv):
         # Killed some type of Klingon 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
         game.klhere -= 1
         # Killed some type of Klingon 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
         game.klhere -= 1
-        if type == IHC:
+        if type == 'C':
             game.state.kcmdr.remove(game.quadrant)
             unschedule(FTBEAM)
             if game.state.kcmdr:
                 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
             if is_scheduled(FCDBAS) and game.battle == game.quadrant:
                 unschedule(FCDBAS)    
             game.state.kcmdr.remove(game.quadrant)
             unschedule(FTBEAM)
             if game.state.kcmdr:
                 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
             if is_scheduled(FCDBAS) and game.battle == game.quadrant:
                 unschedule(FCDBAS)    
-        elif type ==  IHK:
+        elif type ==  'K':
             game.state.remkl -= 1
             game.state.remkl -= 1
-        elif type ==  IHS:
+        elif type ==  'S':
             game.state.nscrem -= 1
             game.state.kscmdr.invalidate()
             game.isatb = 0
             game.state.nscrem -= 1
             game.state.kscmdr.invalidate()
             game.isatb = 0
@@ -1699,7 +1478,7 @@ def deadkl(w, type, mv):
 
 def targetcheck(w):
     "Return None if target is invalid, otherwise return a course angle."
 
 def targetcheck(w):
     "Return None if target is invalid, otherwise return a course angle."
-    if not VALID_SECTOR(w.i, w.j):
+    if not w.valid_sector():
        huh()
        return None
     delta = coord()
        huh()
        return None
     delta = coord()
@@ -1882,7 +1661,7 @@ def hittem(hits):
        else:
            proutn(_("Very small hit on "))
        ienm = game.quad[w.i][w.j]
        else:
            proutn(_("Very small hit on "))
        ienm = game.quad[w.i][w.j]
-       if ienm==IHQUEST:
+       if ienm=='?':
            thing.angry = True
        proutn(crmena(False, ienm, "sector", w))
        skip(1)
            thing.angry = True
        proutn(crmena(False, ienm, "sector", w))
        skip(1)
@@ -2072,7 +1851,7 @@ def phasers():
                msgflag = False
                rpow = 0.0
            if damaged(DSRSENS) and \
                msgflag = False
                rpow = 0.0
            if damaged(DSRSENS) and \
-               not game.sector.distance(aim)<2**0.5 and ienm in (IHC, IHS):
+               not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
                prout(cramen(ienm) + _(" can't be located without short range scan."))
                scanner.chew()
                key = "IHEOL"
                prout(cramen(ienm) + _(" can't be located without short range scan."))
                scanner.chew()
                key = "IHEOL"
@@ -2136,7 +1915,7 @@ def phasers():
     if ifast:
        skip(1)
        if no == 0:
     if ifast:
        skip(1)
        if no == 0:
-           if withprob(0.99):
+           if withprob(0.01):
                prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
                prouts(_("         CLICK   CLICK   POP  . . ."))
                prout(_(" No response, sir!"))
                prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
                prouts(_("         CLICK   CLICK   POP  . . ."))
                prout(_(" No response, sir!"))
@@ -2249,7 +2028,7 @@ def events():
         # Handle case where base is in same quadrant as starship 
         if game.battle == game.quadrant:
             game.state.chart[game.battle.i][game.battle.j].starbase = False
         # Handle case where base is in same quadrant as starship 
         if game.battle == game.quadrant:
             game.state.chart[game.battle.i][game.battle.j].starbase = False
-            game.quad[game.base.i][game.base.j] = IHDOT
+            game.quad[game.base.i][game.base.j] = '.'
             game.base.invalidate()
             newcnd()
             skip(1)
             game.base.invalidate()
             newcnd()
             skip(1)
@@ -2454,40 +2233,36 @@ def events():
                supercommander()
        elif evcode == FDSPROB: # Move deep space probe 
            schedule(FDSPROB, 0.01)
                supercommander()
        elif evcode == FDSPROB: # Move deep space probe 
            schedule(FDSPROB, 0.01)
-           game.probe += game.probein
-            newloc = (game.probe / float(QUADSIZE)).snaptogrid()
-            if newloc != game.probec:
-                game.probec = newloc
-               if not VALID_QUADRANT(game.probec.i, game.probec.j) or \
-                   game.state.galaxy[game.probec.i][game.probec.j].supernova:
+            if not game.probe.next():
+               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
                     if communicating():
                        announce()
                        skip(1)
                        proutn(_("Lt. Uhura-  \"The deep space probe "))
                    # Left galaxy or ran into supernova
                     if communicating():
                        announce()
                        skip(1)
                        proutn(_("Lt. Uhura-  \"The deep space probe "))
-                       if not VALID_QUADRANT(game.probec.i, game.probec.j):
-                           proutn(_("has left the galaxy.\""))
+                       if not game.probe.quadrant().valid_quadrant():
+                           prout(_("has left the galaxy.\""))
                        else:
                        else:
-                           proutn(_("is no longer transmitting.\""))
+                           prout(_("is no longer transmitting.\""))
                    unschedule(FDSPROB)
                    continue
                 if communicating():
                    unschedule(FDSPROB)
                    continue
                 if communicating():
-                   announce()
+                   #announce()
                    skip(1)
                    skip(1)
-                   proutn(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probec)
-           pdest = game.state.galaxy[game.probec.i][game.probec.j]
-           # Update star chart if Radio is working or have access to radio
+                   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]
            if communicating():
            if communicating():
-               chp = game.state.chart[game.probec.i][game.probec.j]
+               chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
                chp.klingons = pdest.klingons
                chp.starbase = pdest.starbase
                chp.stars = pdest.stars
                pdest.charted = True
                chp.klingons = pdest.klingons
                chp.starbase = pdest.starbase
                chp.stars = pdest.stars
                pdest.charted = True
-           game.proben -= 1 # One less to travel
-           if game.proben == 0 and game.isarmed and pdest.stars:
-               supernova(game.probec)          # fire in the hole!
+           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)
                unschedule(FDSPROB)
-               if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova: 
+               if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova: 
                    return
        elif evcode == FDISTR: # inhabited system issues distress call 
            unschedule(FDISTR)
                    return
        elif evcode == FDISTR: # inhabited system issues distress call 
            unschedule(FDISTR)
@@ -2550,14 +2325,15 @@ def events():
                continue                # full right now 
            # reproduce one Klingon 
            w = ev.quadrant
                continue                # full right now 
            # reproduce one Klingon 
            w = ev.quadrant
+            m = coord()
            if game.klhere >= MAXKLQUAD:
                 try:
                     # this quadrant not ok, pick an adjacent one 
            if game.klhere >= MAXKLQUAD:
                 try:
                     # this quadrant not ok, pick an adjacent one 
-                    for i in range(w.i - 1, w.i + 2):
-                        for j in range(w.j - 1, w.j + 2):
-                            if not VALID_QUADRANT(i, j):
+                    for m.i in range(w.i - 1, w.i + 2):
+                        for m.j in range(w.j - 1, w.j + 2):
+                            if not m.valid_quadrant():
                                 continue
                                 continue
-                            q = game.state.galaxy[w.i][w.j]
+                            q = game.state.galaxy[m.i][m.j]
                             # check for this quad ok (not full & no snova) 
                             if q.klingons >= MAXKLQUAD or q.supernova:
                                 continue
                             # check for this quad ok (not full & no snova) 
                             if q.klingons >= MAXKLQUAD or q.supernova:
                                 continue
@@ -2565,7 +2341,7 @@ def events():
                     else:
                         continue       # search for eligible quadrant failed
                 except "FOUNDIT":
                     else:
                         continue       # search for eligible quadrant failed
                 except "FOUNDIT":
-                    w.i = i; w.j = j
+                    w = m
            # deliver the child 
            game.state.remkl += 1
            q.klingons += 1
            # deliver the child 
            game.state.remkl += 1
            q.klingons += 1
@@ -2574,7 +2350,6 @@ def events():
                game.enemies.append(newkling())
            # recompute time left
             game.recompute()
                game.enemies.append(newkling())
            # recompute time left
             game.recompute()
-           # report the disaster if we can 
            if communicating():
                if game.quadrant == w:
                    prout(_("Spock- sensors indicate the Klingons have"))
            if communicating():
                if game.quadrant == w:
                    prout(_("Spock- sensors indicate the Klingons have"))
@@ -2645,8 +2420,8 @@ def nova(nov):
        supernova(game.quadrant)
        return
     # handle initial nova 
        supernova(game.quadrant)
        return
     # handle initial nova 
-    game.quad[nov.i][nov.j] = IHDOT
-    prout(crmena(False, IHSTAR, "sector", nov) + _(" novas."))
+    game.quad[nov.i][nov.j] = '.'
+    prout(crmena(False, '*', "sector", nov) + _(" novas."))
     game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
     game.state.starkl += 1
     # Set up queue to recursively trigger adjacent stars 
     game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
     game.state.starkl += 1
     # Set up queue to recursively trigger adjacent stars 
@@ -2660,13 +2435,13 @@ def nova(nov):
                 if offset.j==0 and offset.i==0:
                     continue
                 neighbor = start + offset
                 if offset.j==0 and offset.i==0:
                     continue
                 neighbor = start + offset
-                if not VALID_SECTOR(neighbor.j, neighbor.i):
+                if not neighbor.valid_sector():
                     continue
                 iquad = game.quad[neighbor.i][neighbor.j]
                 # Empty space ends reaction
                     continue
                 iquad = game.quad[neighbor.i][neighbor.j]
                 # Empty space ends reaction
-                if iquad in (IHDOT, IHQUEST, IHBLANK, IHT, IHWEB):
+                if iquad in ('.', '?', ' ', 'T', '#'):
                     pass
                     pass
-                elif iquad == IHSTAR: # Affect another star 
+                elif iquad == '*': # Affect another star 
                     if withprob(0.05):
                         # This star supernovas 
                         supernova(game.quadrant)
                     if withprob(0.05):
                         # This star supernovas 
                         supernova(game.quadrant)
@@ -2675,33 +2450,33 @@ def nova(nov):
                         hits.append(neighbor)
                        game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
                        game.state.starkl += 1
                         hits.append(neighbor)
                        game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
                        game.state.starkl += 1
-                       proutn(crmena(True, IHSTAR, "sector", neighbor))
+                       proutn(crmena(True, '*', "sector", neighbor))
                        prout(_(" novas."))
                        prout(_(" novas."))
-                        game.quad[neighbor.i][neighbor.j] = IHDOT
+                        game.quad[neighbor.i][neighbor.j] = '.'
                         kount += 1
                         kount += 1
-                elif iquad in (IHP, IHW): # Destroy planet 
+                elif iquad in ('P', '@'): # Destroy planet 
                     game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
                     game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
-                    if iquad == IHP:
+                    if iquad == 'P':
                         game.state.nplankl += 1
                     else:
                         game.state.worldkl += 1
                         game.state.nplankl += 1
                     else:
                         game.state.worldkl += 1
-                    prout(crmena(True, IHB, "sector", neighbor) + _(" destroyed."))
+                    prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
                     game.iplnet.pclass = "destroyed"
                     game.iplnet = None
                     game.plnet.invalidate()
                     if game.landed:
                         finish(FPNOVA)
                         return
                     game.iplnet.pclass = "destroyed"
                     game.iplnet = None
                     game.plnet.invalidate()
                     if game.landed:
                         finish(FPNOVA)
                         return
-                    game.quad[neighbor.i][neighbor.j] = IHDOT
-                elif iquad == IHB: # Destroy base 
+                    game.quad[neighbor.i][neighbor.j] = '.'
+                elif iquad == 'B': # Destroy base 
                     game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
                     game.state.baseq = filter(lambda x: x!= game.quadrant, game.state.baseq)
                     game.base.invalidate()
                     game.state.basekl += 1
                     newcnd()
                     game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
                     game.state.baseq = filter(lambda x: x!= game.quadrant, game.state.baseq)
                     game.base.invalidate()
                     game.state.basekl += 1
                     newcnd()
-                    prout(crmena(True, IHB, "sector", neighbor) + _(" destroyed."))
-                    game.quad[neighbor.i][neighbor.j] = IHDOT
-                elif iquad in (IHE, IHF): # Buffet ship 
+                    prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
+                    game.quad[neighbor.i][neighbor.j] = '.'
+                elif iquad in ('E', 'F'): # Buffet ship 
                     prout(_("***Starship buffeted by nova."))
                     if game.shldup:
                         if game.shield >= 2000.0:
                     prout(_("***Starship buffeted by nova."))
                     if game.shldup:
                         if game.shield >= 2000.0:
@@ -2720,9 +2495,9 @@ def nova(nov):
                         return
                     # add in course nova contributes to kicking starship
                     bump += (game.sector-hits[mm]).sgn()
                         return
                     # add in course nova contributes to kicking starship
                     bump += (game.sector-hits[mm]).sgn()
-                elif iquad == IHK: # kill klingon 
+                elif iquad == 'K': # kill klingon 
                     deadkl(neighbor, iquad, neighbor)
                     deadkl(neighbor, iquad, neighbor)
-                elif iquad in (IHC,IHS,IHR): # Damage/destroy big enemies 
+                elif iquad in ('C','S','R'): # Damage/destroy big enemies 
                     for ll in range(len(game.enemies)):
                         if game.enemies[ll].kloc == neighbor:
                             break
                     for ll in range(len(game.enemies)):
                         if game.enemies[ll].kloc == neighbor:
                             break
@@ -2732,36 +2507,37 @@ def nova(nov):
                         break
                     newc = neighbor + neighbor - hits[mm]
                     proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
                         break
                     newc = neighbor + neighbor - hits[mm]
                     proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
-                    if not VALID_SECTOR(newc.i, newc.j):
+                    if not newc.valid_sector():
                         # can't leave quadrant 
                         skip(1)
                         break
                     iquad1 = game.quad[newc.i][newc.j]
                         # can't leave quadrant 
                         skip(1)
                         break
                     iquad1 = game.quad[newc.i][newc.j]
-                    if iquad1 == IHBLANK:
-                        proutn(_(", blasted into ") + crmena(False, IHBLANK, "sector", newc))
+                    if iquad1 == ' ':
+                        proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
                         skip(1)
                         deadkl(neighbor, iquad, newc)
                         break
                         skip(1)
                         deadkl(neighbor, iquad, newc)
                         break
-                    if iquad1 != IHDOT:
+                    if iquad1 != '.':
                         # can't move into something else 
                         skip(1)
                         break
                     proutn(_(", buffeted to Sector %s") % newc)
                         # can't move into something else 
                         skip(1)
                         break
                     proutn(_(", buffeted to Sector %s") % newc)
-                    game.quad[neighbor.i][neighbor.j] = IHDOT
+                    game.quad[neighbor.i][neighbor.j] = '.'
                     game.quad[newc.i][newc.j] = iquad
                     game.enemies[ll].move(newc)
     # Starship affected by nova -- kick it away. 
                     game.quad[newc.i][newc.j] = iquad
                     game.enemies[ll].move(newc)
     # Starship affected by nova -- kick it away. 
-    game.dist = kount*0.1
-    game.direc = course[3*(bump.i+1)+bump.j+2]
-    if game.direc == 0.0:
-       game.dist = 0.0
-    if game.dist == 0.0:
+    dist = kount*0.1
+    direc = course[3*(bump.i+1)+bump.j+2]
+    if direc == 0.0:
+       dist = 0.0
+    if dist == 0.0:
        return
        return
-    game.optime = 10.0*game.dist/16.0
+    course = course(bearing=direc, distance=dist)
+    game.optime = course.time(warp=4)
     skip(1)
     prout(_("Force of nova displaces starship."))
     skip(1)
     prout(_("Force of nova displaces starship."))
-    imove(novapush=True)
-    game.optime = 10.0*game.dist/16.0
+    imove(course, noattack=True)
+    game.optime = course.time(warp=4)
     return
        
 def supernova(w):
     return
        
 def supernova(w):
@@ -2802,7 +2578,7 @@ def supernova(w):
        num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
        for ns.i in range(QUADSIZE):
            for ns.j in range(QUADSIZE):
        num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
        for ns.i in range(QUADSIZE):
            for ns.j in range(QUADSIZE):
-               if game.quad[ns.i][ns.j]==IHSTAR:
+               if game.quad[ns.i][ns.j]=='*':
                    num -= 1
                    if num==0:
                        break
                    num -= 1
                    if num==0:
                        break
@@ -2909,7 +2685,7 @@ def selfdestruct():
 
 def kaboom():
     stars()
 
 def kaboom():
     stars()
-    if game.ship==IHE:
+    if game.ship=='E':
        prouts("***")
     prouts(_("********* Entropy of %s maximized *********") % crmshp())
     skip(1)
        prouts("***")
     prouts(_("********* Entropy of %s maximized *********") % crmshp())
     skip(1)
@@ -2943,7 +2719,7 @@ def badpoints():
             45.0*game.nhelp +\
             100.0*game.state.basekl +\
             3.0*game.abandoned
             45.0*game.nhelp +\
             100.0*game.state.basekl +\
             3.0*game.abandoned
-    if game.ship == IHF:
+    if game.ship == 'F':
         badpt += 100.0
     elif game.ship == None:
         badpt += 200.0
         badpt += 100.0
     elif game.ship == None:
         badpt += 200.0
@@ -3115,10 +2891,10 @@ def finish(ifin):
        prout(_("You are crushed into extremely dense matter."))
     elif ifin == FCREW:
        prout(_("Your last crew member has died."))
        prout(_("You are crushed into extremely dense matter."))
     elif ifin == FCREW:
        prout(_("Your last crew member has died."))
-    if game.ship == IHF:
+    if game.ship == 'F':
        game.ship = None
        game.ship = None
-    elif game.ship == IHE:
-       game.ship = IHF
+    elif game.ship == 'E':
+       game.ship = 'F'
     game.alive = False
     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
        goodies = game.state.remres/game.inresor
     game.alive = False
     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
        goodies = game.state.remres/game.inresor
@@ -3153,9 +2929,9 @@ def score():
     iwon = 0
     if game.gamewon:
        iwon = 100*game.skill
     iwon = 0
     if game.gamewon:
        iwon = 100*game.skill
-    if game.ship == IHE
+    if game.ship == 'E'
        klship = 0
        klship = 0
-    elif game.ship == IHF
+    elif game.ship == 'F'
        klship = 1
     else:
        klship = 2
        klship = 1
     else:
        klship = 2
@@ -3278,7 +3054,7 @@ def plaque():
         fp.write(_("Emeritus level\n\n"))
     else:
         fp.write(_(" Cheat level\n\n"))
         fp.write(_("Emeritus level\n\n"))
     else:
         fp.write(_(" Cheat level\n\n"))
-    timestring = ctime()
+    timestring = time.ctime()
     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
                     (timestring+4, timestring+20, timestring+11))
     fp.write(_("                                                        Your score:  %d\n\n") % iscore)
     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
                     (timestring+4, timestring+20, timestring+11))
     fp.write(_("                                                        Your score:  %d\n\n") % iscore)
@@ -3342,7 +3118,7 @@ def waitfor():
 
 def announce():
     skip(1)
 
 def announce():
     skip(1)
-    prouts(_("[ANOUNCEMENT ARRIVING...]"))
+    prouts(_("[ANNOUNCEMENT ARRIVING...]"))
     skip(1)
 
 def pause_game():
     skip(1)
 
 def pause_game():
@@ -3461,11 +3237,6 @@ def clrscr():
        curwnd.refresh()
     linecount = 0
     
        curwnd.refresh()
     linecount = 0
     
-def highvideo():
-    "Set highlight video, if this is reasonable."
-    if game.options & OPTION_CURSES:
-       curwnd.attron(curses.A_REVERSE)
 #
 # Things past this point have policy implications.
 # 
 #
 # Things past this point have policy implications.
 # 
@@ -3530,7 +3301,7 @@ def tracktorpedo(origin, w, step, i, n, iquad):
        if step == 1:
            if n != 1:
                skip(1)
        if step == 1:
            if n != 1:
                skip(1)
-               proutn(_("Track for %s torpedo number %d-  ") % (game.quad[origin.i][origin.j],i+1))
+               proutn(_("Track for torpedo number %d-  ") % (i+1))
            else:
                skip(1)
                proutn(_("Torpedo track- "))
            else:
                skip(1)
                proutn(_("Torpedo track- "))
@@ -3542,7 +3313,7 @@ def tracktorpedo(origin, w, step, i, n, iquad):
            if i != 0 and step == 1:
                drawmaps(2)
                time.sleep(0.4)
            if i != 0 and step == 1:
                drawmaps(2)
                time.sleep(0.4)
-           if (iquad==IHDOT) or (iquad==IHBLANK):
+           if (iquad=='.') or (iquad==' '):
                put_srscan_sym(w, '+')
                #sound(step*10)
                #time.sleep(0.1)
                put_srscan_sym(w, '+')
                #sound(step*10)
                #time.sleep(0.1)
@@ -3584,180 +3355,151 @@ def prstat(txt, data):
 
 # Code from moving.c begins here
 
 
 # Code from moving.c begins here
 
-def imove(novapush):
+def imove(course=None, noattack=False):
     "Movement execution for warp, impulse, supernova, and tractor-beam events."
     "Movement execution for warp, impulse, supernova, and tractor-beam events."
-    w = coord(); final = coord()
-    trbeam = False
+    w = coord()
 
 
-    def no_quad_change():
-        # No quadrant change -- compute new average enemy distances 
-        game.quad[game.sector.i][game.sector.j] = game.ship
-        if game.enemies:
+    def newquadrant(noattack):
+        # Leaving quadrant -- allow final enemy attack 
+        # Don't do it if being pushed by Nova 
+        if len(game.enemies) != 0 and not noattack:
+            newcnd()
             for enemy in game.enemies:
             for enemy in game.enemies:
-                finald = (w-enemy.kloc).distance()
+                finald = (w - enemy.kloc).distance()
                 enemy.kavgd = 0.5 * (finald + enemy.kdist)
                 enemy.kavgd = 0.5 * (finald + enemy.kdist)
-                enemy.kdist = finald
-            game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
-            if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
+            # 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.i][game.quadrant.j].supernova:
                 attack(torps_ok=False)
                 attack(torps_ok=False)
-            for enemy in game.enemies:
-                enemy.kavgd = enemy.kdist
-        newcnd()
-        drawmaps(0)
-        setwnd(message_window)
-    w.i = w.j = 0
+            if game.alldone:
+                return
+        # check for edge of galaxy 
+        kinks = 0
+        while True:
+            kink = False
+            if course.final.i < 0:
+                course.final.i = -course.final.i
+                kink = True
+            if course.final.j < 0:
+                course.final.j = -course.final.j
+                kink = True
+            if course.final.i >= GALSIZE*QUADSIZE:
+                course.final.i = (GALSIZE*QUADSIZE*2) - course.final.i
+                kink = True
+            if course.final.j >= GALSIZE*QUADSIZE:
+                course.final.j = (GALSIZE*QUADSIZE*2) - course.final.j
+                kink = True
+            if kink:
+                kinks += 1
+            else:
+                break
+        if kinks:
+            game.nkinks += 1
+            if game.nkinks == 3:
+                # Three strikes -- you're out! 
+                finish(FNEG3)
+                return
+            skip(1)
+            prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
+            prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
+            prout(_("YOU WILL BE DESTROYED."))
+        # Compute final position in new quadrant 
+        if trbeam: # Don't bother if we are to be beamed 
+            return
+        game.quadrant = course.final.quadrant()
+        game.sector = course.final.sector()
+        skip(1)
+        prout(_("Entering Quadrant %s.") % game.quadrant)
+        game.quad[game.sector.i][game.sector.j] = game.ship
+        newqad()
+        if game.skill>SKILL_NOVICE:
+            attack(torps_ok=False)  
+
+    def check_collision(h):
+        iquad = game.quad[h.i][h.j]
+        if iquad != '.':
+            # object encountered in flight path 
+            stopegy = 50.0*course.distance/game.optime
+            if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
+                for enemy in game.enemies:
+                    if enemy.kloc == game.sector:
+                        break
+                collision(rammed=False, enemy=enemy)
+                return True
+            elif iquad == ' ':
+                skip(1)
+                prouts(_("***RED ALERT!  RED ALERT!"))
+                skip(1)
+                proutn("***" + crmshp())
+                proutn(_(" pulled into black hole at Sector %s") % h)
+                # Getting pulled into a black hole was certain
+                # death in Almy's original.  Stas Sergeev added a
+                # possibility that you'll get timewarped instead.
+                n=0
+                for m in range(NDEVICES):
+                    if game.damage[m]>0: 
+                        n += 1
+                probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
+                if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
+                    timwrp()
+                else: 
+                    finish(FHOLE)
+                return True
+            else:
+                # something else 
+                skip(1)
+                proutn(crmshp())
+                if iquad == '#':
+                    prout(_(" encounters Tholian web at %s;") % h)
+                else:
+                    prout(_(" blocked by object at %s;") % h)
+                proutn(_("Emergency stop required "))
+                prout(_("%2d units of energy.") % int(stopegy))
+                game.energy -= stopegy
+                if game.energy <= 0:
+                    finish(FNRG)
+                return True
+        return False
+
+    trbeam = False
     if game.inorbit:
        prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
        game.inorbit = False
     if game.inorbit:
        prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
        game.inorbit = False
-    angle = ((15.0 - game.direc) * 0.5235988)
-    deltax = -math.sin(angle)
-    deltay = math.cos(angle)
-    if math.fabs(deltax) > math.fabs(deltay):
-       bigger = math.fabs(deltax)
-    else:
-       bigger = math.fabs(deltay)
-    deltay /= bigger
-    deltax /= bigger
     # If tractor beam is to occur, don't move full distance 
     if game.state.date+game.optime >= scheduled(FTBEAM):
        trbeam = True
        game.condition = "red"
     # If tractor beam is to occur, don't move full distance 
     if game.state.date+game.optime >= scheduled(FTBEAM):
        trbeam = True
        game.condition = "red"
-       game.dist = game.dist*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
+       course.distance = course.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
        game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
        game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
-    # Move within the quadrant 
-    game.quad[game.sector.i][game.sector.j] = IHDOT
-    x = game.sector.i
-    y = game.sector.j
-    n = int(10.0*game.dist*bigger+0.5)
-    if n > 0:
-       for m in range(1, n+1):
-            x += deltax
-            y += deltay
-           w.i = int(round(x))
-           w.j = int(round(y))
-           if not VALID_SECTOR(w.i, w.j):
-               # Leaving quadrant -- allow final enemy attack 
-               # Don't do it if being pushed by Nova 
-               if len(game.enemies) != 0 and not novapush:
-                   newcnd()
-                   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.i][game.quadrant.j].supernova:
-                       attack(torps_ok=False)
-                   if game.alldone:
-                       return
-               # compute final position -- new quadrant and sector 
-               x = (QUADSIZE*game.quadrant.i)+game.sector.i
-               y = (QUADSIZE*game.quadrant.j)+game.sector.j
-               w.i = int(round(x+10.0*game.dist*bigger*deltax))
-               w.j = int(round(y+10.0*game.dist*bigger*deltay))
-               # check for edge of galaxy 
-               kinks = 0
-                while True:
-                   kink = False
-                   if w.i < 0:
-                       w.i = -w.i
-                       kink = True
-                   if w.j < 0:
-                       w.j = -w.j
-                       kink = True
-                   if w.i >= GALSIZE*QUADSIZE:
-                       w.i = (GALSIZE*QUADSIZE*2) - w.i
-                       kink = True
-                   if w.j >= GALSIZE*QUADSIZE:
-                       w.j = (GALSIZE*QUADSIZE*2) - w.j
-                       kink = True
-                   if kink:
-                       kinks += 1
-                    else:
-                        break
-               if kinks:
-                   game.nkinks += 1
-                   if game.nkinks == 3:
-                       # Three strikes -- you're out! 
-                       finish(FNEG3)
-                       return
-                   skip(1)
-                   prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
-                   prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
-                   prout(_("YOU WILL BE DESTROYED."))
-               # Compute final position in new quadrant 
-               if trbeam: # Don't bother if we are to be beamed 
-                   return
-               game.quadrant.i = w.i/QUADSIZE
-               game.quadrant.j = w.j/QUADSIZE
-               game.sector.i = w.i - (QUADSIZE*game.quadrant.i)
-               game.sector.j = w.j - (QUADSIZE*game.quadrant.j)
-               skip(1)
-               prout(_("Entering Quadrant %s.") % game.quadrant)
-               game.quad[game.sector.i][game.sector.j] = game.ship
-               newqad()
-               if game.skill>SKILL_NOVICE:
-                   attack(torps_ok=False)  
-               return
-           iquad = game.quad[w.i][w.j]
-           if iquad != IHDOT:
-               # object encountered in flight path 
-               stopegy = 50.0*game.dist/game.optime
-               game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
-                if iquad in (IHT, IHK, IHC, IHS, IHR, IHQUEST):
-                   game.sector = w
-                    for enemy in game.enemies:
-                        if enemy.kloc == game.sector:
-                            break
-                   collision(rammed=False, enemy=enemy)
-                   final = game.sector
-               elif iquad == IHBLANK:
-                   skip(1)
-                   prouts(_("***RED ALERT!  RED ALERT!"))
-                   skip(1)
-                   proutn("***" + crmshp())
-                   proutn(_(" pulled into black hole at Sector %s") % w)
-                   #
-                   # Getting pulled into a black hole was certain
-                   # death in Almy's original.  Stas Sergeev added a
-                   # possibility that you'll get timewarped instead.
-                   # 
-                   n=0
-                   for m in range(NDEVICES):
-                       if game.damage[m]>0: 
-                           n += 1
-                   probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
-                   if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
-                       timwrp()
-                   else: 
-                       finish(FHOLE)
-                   return
-               else:
-                   # something else 
-                   skip(1)
-                   proutn(crmshp())
-                   if iquad == IHWEB:
-                       prout(_(" encounters Tholian web at %s;") % w)
-                   else:
-                       prout(_(" blocked by object at %s;") % w)
-                   proutn(_("Emergency stop required "))
-                   prout(_("%2d units of energy.") % int(stopegy))
-                   game.energy -= stopegy
-                   final.i = int(round(deltax))
-                   final.j = int(round(deltay))
-                   game.sector = final
-                   if game.energy <= 0:
-                       finish(FNRG)
-                       return
-                # We're here!
-               no_quad_change()
-                return
-       game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
-       game.sector = w
-    final = game.sector
-    no_quad_change()
+    # Move out
+    game.quad[game.sector.i][game.sector.j] = '.'
+    for m in range(course.moves):
+        course.next()
+        w = course.sector()
+        if course.origin.quadrant() != course.location.quadrant():
+            newquadrant(noattack)
+            break
+        elif check_collision(w):
+            print "Collision detected"
+            break
+        else:
+            game.sector = w
+    # We're in destination quadrant -- compute new average enemy distances
+    game.quad[game.sector.i][game.sector.j] = game.ship
+    if game.enemies:
+        for enemy in game.enemies:
+            finald = (w-enemy.kloc).distance()
+            enemy.kavgd = 0.5 * (finald + enemy.kdist)
+            enemy.kdist = finald
+        game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+        if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
+            attack(torps_ok=False)
+        for enemy in game.enemies:
+            enemy.kavgd = enemy.kdist
+    newcnd()
+    drawmaps(0)
+    setwnd(message_window)
     return
 
 def dock(verbose):
     return
 
 def dock(verbose):
@@ -3797,6 +3539,14 @@ def dock(verbose):
 # because it involves giving x and y motions, yet the coordinates
 # are always displayed y - x, where +y is downward!
 
 # because it involves giving x and y motions, yet the coordinates
 # are always displayed y - x, where +y is downward!
 
+def cartesian(loc1=None, loc2=None):
+    if loc1 is None:
+        return game.quadrant * QUADSIZE + game.sector
+    elif loc2 is None:
+        return game.quadrant * QUADSIZE + loc1
+    else:
+        return loc1 * QUADSIZE + loc2
+
 def getcourse(isprobe):
     "Get a course and distance from the user."
     key = 0
 def getcourse(isprobe):
     "Get a course and distance from the user."
     key = 0
@@ -3809,7 +3559,7 @@ def getcourse(isprobe):
        prout(_("Dummy! You can't leave standard orbit until you"))
        proutn(_("are back aboard the ship."))
        scanner.chew()
        prout(_("Dummy! You can't leave standard orbit until you"))
        proutn(_("are back aboard the ship."))
        scanner.chew()
-       return False
+       raise TrekError
     while navmode == "unspecified":
        if damaged(DNAVSYS):
            if isprobe:
     while navmode == "unspecified":
        if damaged(DNAVSYS):
            if isprobe:
@@ -3837,7 +3587,7 @@ def getcourse(isprobe):
            else:
                huh()
                scanner.chew()
            else:
                huh()
                scanner.chew()
-               return False
+               raise TrekError
        else: # numeric 
            if isprobe:
                prout(_("(Manual navigation assumed.)"))
        else: # numeric 
            if isprobe:
                prout(_("(Manual navigation assumed.)"))
@@ -3845,6 +3595,7 @@ def getcourse(isprobe):
                prout(_("(Manual movement assumed.)"))
            navmode = "manual"
            break
                prout(_("(Manual movement assumed.)"))
            navmode = "manual"
            break
+    delta = coord()
     if navmode == "automatic":
        while key == "IHEOL":
            if isprobe:
     if navmode == "automatic":
        while key == "IHEOL":
            if isprobe:
@@ -3856,12 +3607,12 @@ def getcourse(isprobe):
            key = scanner.next()
        if key != "IHREAL":
            huh()
            key = scanner.next()
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        xi = int(round(scanner.real))-1
        key = scanner.next()
        if key != "IHREAL":
            huh()
        xi = int(round(scanner.real))-1
        key = scanner.next()
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        xj = int(round(scanner.real))-1
        key = scanner.next()
        if key == "IHREAL":
        xj = int(round(scanner.real))-1
        key = scanner.next()
        if key == "IHREAL":
@@ -3870,7 +3621,7 @@ def getcourse(isprobe):
            key = scanner.next()
            if key != "IHREAL":
                huh()
            key = scanner.next()
            if key != "IHREAL":
                huh()
-               return False
+               raise TrekError
            xl = int(round(scanner.real))-1
            dquad.i = xi
            dquad.j = xj
            xl = int(round(scanner.real))-1
            dquad.i = xi
            dquad.j = xj
@@ -3888,9 +3639,9 @@ def getcourse(isprobe):
                dsect.i = xi
                dsect.j = xj
            itemp = "normal"
                dsect.i = xi
                dsect.j = xj
            itemp = "normal"
-       if not VALID_QUADRANT(dquad.i,dquad.j) or not VALID_SECTOR(dsect.i,dsect.j):
+       if not dquad.valid_quadrant() or not dsect.valid_sector():
            huh()
            huh()
-           return False
+           raise TrekError
        skip(1)
        if not isprobe:
            if itemp > "curt":
        skip(1)
        if not isprobe:
            if itemp > "curt":
@@ -3899,7 +3650,6 @@ def getcourse(isprobe):
            else:
                prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
         # the actual deltas get computed here
            else:
                prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
         # the actual deltas get computed here
-        delta = coord()
        delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
        delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
     else: # manual 
        delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
        delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
     else: # manual 
@@ -3911,27 +3661,66 @@ def getcourse(isprobe):
        itemp = "verbose"
        if key != "IHREAL":
            huh()
        itemp = "verbose"
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        delta.j = scanner.real
        key = scanner.next()
        if key != "IHREAL":
            huh()
        delta.j = scanner.real
        key = scanner.next()
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        delta.i = scanner.real
     # Check for zero movement 
     if delta.i == 0 and delta.j == 0:
        scanner.chew()
        delta.i = scanner.real
     # Check for zero movement 
     if delta.i == 0 and delta.j == 0:
        scanner.chew()
-       return False
+       raise TrekError
     if itemp == "verbose" and not isprobe:
        skip(1)
        prout(_("Helmsman Sulu- \"Aye, Sir.\""))
     if itemp == "verbose" and not isprobe:
        skip(1)
        prout(_("Helmsman Sulu- \"Aye, Sir.\""))
-    # Course actually laid in.
-    game.dist = delta.distance()
-    game.direc = delta.bearing()
-    if game.direc < 0.0:
-       game.direc += 12.0
     scanner.chew()
     scanner.chew()
-    return True
+    return course(bearing=delta.bearing(), distance=delta.distance())
+
+class course:
+    def __init__(self, bearing, distance, origin=None): 
+        self.distance = distance
+        self.bearing = bearing
+        if origin is None:
+            self.origin = cartesian(game.quadrant, game.sector)
+        else:
+            self.origin = origin
+        # The bearing() code we inherited from FORTRAN is actually computing
+        # clockface directions!
+        if self.bearing < 0.0:
+            self.bearing += 12.0
+        self.angle = ((15.0 - self.bearing) * 0.5235988)
+        if origin is None:
+            self.origin = cartesian(game.quadrant, game.sector)
+        else:
+            self.origin = cartesian(game.quadrant, origin)
+        self.increment = coord(-math.sin(self.angle), math.cos(self.angle))
+        bigger = max(abs(self.increment.i), abs(self.increment.j))
+        self.increment /= bigger
+        self.moves = int(round(10*self.distance*bigger))
+        self.reset()
+        self.final = (self.location + self.moves*self.increment).roundtogrid()
+    def reset(self):
+        self.location = self.origin
+        self.step = 0
+    def arrived(self):
+        return self.location.roundtogrid() == self.final
+    def next(self):
+        "Next step on course."
+        self.step += 1
+        self.nextlocation = self.location + self.increment
+        samequad = (self.location.quadrant() == self.nextlocation.quadrant())
+        self.location = self.nextlocation
+        return samequad
+    def quadrant(self):
+        return self.location.quadrant()
+    def sector(self):
+        return self.location.sector()
+    def power(self, warp):
+       return self.distance*(warp**3)*(game.shldup+1)
+    def time(self, warp):
+        return 10.0*self.distance/warp**2
 
 def impulse():
     "Move under impulse power."
 
 def impulse():
     "Move under impulse power."
@@ -3942,9 +3731,11 @@ def impulse():
        prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
        return
     if game.energy > 30.0:
        prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
        return
     if game.energy > 30.0:
-        if not getcourse(isprobe=False):
+        try:
+            course = getcourse(isprobe=False)
+        except TrekError:
            return
            return
-       power = 20.0 + 100.0*game.dist
+       power = 20.0 + 100.0*course.distance
     else:
        power = 30.0
     if power >= game.energy:
     else:
        power = 30.0
     if power >= game.energy:
@@ -3961,7 +3752,7 @@ def impulse():
        scanner.chew()
        return
     # Make sure enough time is left for the trip 
        scanner.chew()
        return
     # Make sure enough time is left for the trip 
-    game.optime = game.dist/0.095
+    game.optime = course.dist/0.095
     if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
     if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
@@ -3969,26 +3760,26 @@ def impulse():
        if ja() == False:
            return
     # Activate impulse engines and pay the cost 
        if ja() == False:
            return
     # Activate impulse engines and pay the cost 
-    imove(novapush=False)
+    imove(course, noattack=False)
     game.ididit = True
     if game.alldone:
        return
     game.ididit = True
     if game.alldone:
        return
-    power = 20.0 + 100.0*game.dist
+    power = 20.0 + 100.0*course.dist
     game.energy -= power
     game.energy -= power
-    game.optime = game.dist/0.095
+    game.optime = course.dist/0.095
     if game.energy <= 0:
        finish(FNRG)
     return
 
     if game.energy <= 0:
        finish(FNRG)
     return
 
-def warp(timewarp):
+def warp(course, involuntary):
     "ove under warp drive."
     blooey = False; twarp = False
     "ove under warp drive."
     blooey = False; twarp = False
-    if not timewarp: # Not WARPX entry 
+    if not involuntary: # Not WARPX entry 
        game.ididit = False
        if game.damage[DWARPEN] > 10.0:
            scanner.chew()
            skip(1)
        game.ididit = False
        if game.damage[DWARPEN] > 10.0:
            scanner.chew()
            skip(1)
-           prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
+           prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
            return
        if damaged(DWARPEN) and game.warpfac > 4.0:
            scanner.chew()
            return
        if damaged(DWARPEN) and game.warpfac > 4.0:
            scanner.chew()
@@ -3996,18 +3787,22 @@ def warp(timewarp):
            prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
            prout(_("  is repaired, I can only give you warp 4.\""))
            return
            prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
            prout(_("  is repaired, I can only give you warp 4.\""))
            return
-               # Read in course and distance 
-        if not getcourse(isprobe=False):
-           return
-       # Make sure starship has enough energy for the trip 
-       power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
-       if power >= game.energy:
+               # Read in course and distance
+        if course==None:
+            try:
+                course = getcourse(isprobe=False)
+            except TrekError:
+                return
+       # Make sure starship has enough energy for the trip
+        # Note: this formula is slightly different from the C version,
+        # and lets you skate a bit closer to the edge.
+       if course.power(game.warpfac) >= game.energy:
            # Insufficient power for trip 
            game.ididit = False
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*power > game.energy:
            # Insufficient power for trip 
            game.ididit = False
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*power > game.energy:
-               iwarp = math.pow((game.energy/(game.dist+0.05)), 0.333333333)
+               iwarp = (game.energy/(course.dist+0.05)) ** 0.333333333
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
@@ -4019,10 +3814,9 @@ def warp(timewarp):
                        prout(".")
            else:
                prout(_("We haven't the energy to go that far with the shields up."))
                        prout(".")
            else:
                prout(_("We haven't the energy to go that far with the shields up."))
-           return
-                                               
+           return                              
        # Make sure enough time is left for the trip 
        # Make sure enough time is left for the trip 
-       game.optime = 10.0*game.dist/game.warpfac**2
+       game.optime = course.time(game.warpfac)
        if game.optime >= 0.8*game.state.remtime:
            skip(1)
            prout(_("First Officer Spock- \"Captain, I compute that such"))
        if game.optime >= 0.8*game.state.remtime:
            skip(1)
            prout(_("First Officer Spock- \"Captain, I compute that such"))
@@ -4038,12 +3832,12 @@ def warp(timewarp):
     if game.warpfac > 6.0:
        # Decide if engine damage will occur
         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
     if game.warpfac > 6.0:
        # Decide if engine damage will occur
         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
-       prob = game.dist*(6.0-game.warpfac)**2/66.666666666
+       prob = course.distance*(6.0-game.warpfac)**2/66.666666666
        if prob > randreal():
            blooey = True
        if prob > randreal():
            blooey = True
-           game.dist = randreal(game.dist)
+           course.distance = randreal(course.distance)
        # Decide if time warp will occur 
        # Decide if time warp will occur 
-       if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > randreal():
+       if 0.5*course.distance*math.pow(7.0,game.warpfac-10.0) > randreal():
            twarp = True
        if idebug and game.warpfac==10 and not twarp:
            blooey = False
            twarp = True
        if idebug and game.warpfac==10 and not twarp:
            blooey = False
@@ -4053,36 +3847,23 @@ def warp(timewarp):
        if blooey or twarp:
            # If time warp or engine damage, check path 
            # If it is obstructed, don't do warp or damage 
        if blooey or twarp:
            # If time warp or engine damage, check path 
            # If it is obstructed, don't do warp or damage 
-           angle = ((15.0-game.direc)*0.5235998)
-           deltax = -math.sin(angle)
-           deltay = math.cos(angle)
-           if math.fabs(deltax) > math.fabs(deltay):
-               bigger = math.fabs(deltax)
-           else:
-               bigger = math.fabs(deltay)
-           deltax /= bigger
-           deltay /= bigger
-           n = 10.0 * game.dist * bigger +0.5
-           x = game.sector.i
-           y = game.sector.j
-           for l in range(1, n+1):
-               x += deltax
-               ix = x + 0.5
-               y += deltay
-               iy = y +0.5
-               if not VALID_SECTOR(ix, iy):
-                   break
-               if game.quad[ix][iy] != IHDOT:
+            for m in range(course.moves):
+                course.next()
+                w = course.sector()
+                if not w.valid_sector():
+                    break
+               if game.quad[w.i][w.j] != '.':
                    blooey = False
                    twarp = False
                    blooey = False
                    twarp = False
+            course.reset()
     # Activate Warp Engines and pay the cost 
     # Activate Warp Engines and pay the cost 
-    imove(novapush=False)
+    imove(course, noattack=False)
     if game.alldone:
        return
     if game.alldone:
        return
-    game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
+    game.energy -= course.power(game.warpfac)
     if game.energy <= 0:
        finish(FNRG)
     if game.energy <= 0:
        finish(FNRG)
-    game.optime = 10.0*game.dist/game.warpfac**2
+    game.optime = course.time(game.warpfac)
     if twarp:
        timwrp()
     if blooey:
     if twarp:
        timwrp()
     if blooey:
@@ -4176,7 +3957,7 @@ def atover(igrab):
            proutn(_("The %s has stopped in a quadrant containing") % crmshp())
            prouts(_("   a supernova."))
            skip(2)
            proutn(_("The %s has stopped in a quadrant containing") % crmshp())
            prouts(_("   a supernova."))
            skip(2)
-       proutn(_("***Emergency automatic override attempts to hurl ")+crmshp())
+       prout(_("***Emergency automatic override attempts to hurl ")+crmshp())
        prout(_("safely out of quadrant."))
        if not damaged(DRADIO):
            game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
        prout(_("safely out of quadrant."))
        if not damaged(DRADIO):
            game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
@@ -4189,15 +3970,13 @@ def atover(igrab):
        game.warpfac = randreal(6.0, 8.0)
        prout(_("Warp factor set to %d") % int(game.warpfac))
        power = 0.75*game.energy
        game.warpfac = randreal(6.0, 8.0)
        prout(_("Warp factor set to %d") % int(game.warpfac))
        power = 0.75*game.energy
-       game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
-       distreq = randreal(math.sqrt(2))
-       if distreq < game.dist:
-           game.dist = distreq
-       game.optime = 10.0*game.dist/game.warpfac**2
-       game.direc = randreal(12)       # How dumb! 
+       dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
+       dist = max(dist, randreal(math.sqrt(2)))
+        bugout = course(bearing=randreal(12), distance=dist)   # How dumb!
+       game.optime = bugout.time(game.warpfac)
        game.justin = False
        game.inorbit = False
        game.justin = False
        game.inorbit = False
-       warp(True)
+       warp(bugout, involuntary=True)
        if not game.justin:
            # This is bad news, we didn't leave quadrant. 
            if game.alldone:
        if not game.justin:
            # This is bad news, we didn't leave quadrant. 
            if game.alldone:
@@ -4234,14 +4013,13 @@ def timwrp():
        unschedule(FCDBAS)
        unschedule(FSCDBAS)
        game.battle.invalidate()
        unschedule(FCDBAS)
        unschedule(FSCDBAS)
        game.battle.invalidate()
-
        # Make sure Galileo is consistant -- Snapshot may have been taken
         # when on planet, which would give us two Galileos! 
        gotit = False
        for l in range(game.inplan):
            if game.state.planets[l].known == "shuttle_down":
                gotit = True
        # Make sure Galileo is consistant -- Snapshot may have been taken
         # when on planet, which would give us two Galileos! 
        gotit = False
        for l in range(game.inplan):
            if game.state.planets[l].known == "shuttle_down":
                gotit = True
-               if game.iscraft == "onship" and game.ship==IHE:
+               if game.iscraft == "onship" and game.ship=='E':
                    prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
                    game.iscraft = "offship"
        # Likewise, if in the original time the Galileo was abandoned, but
                    prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
                    game.iscraft = "offship"
        # Likewise, if in the original time the Galileo was abandoned, but
@@ -4268,7 +4046,7 @@ def probe():
     if game.nprobes == 0:
        scanner.chew()
        skip(1)
     if game.nprobes == 0:
        scanner.chew()
        skip(1)
-       if game.ship == IHE
+       if game.ship == 'E'
            prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
        else:
            prout(_("Ye Faerie Queene has no deep space probes."))
            prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
        else:
            prout(_("Ye Faerie Queene has no deep space probes."))
@@ -4305,17 +4083,11 @@ def probe():
        game.isarmed = ja()
     elif key == "IHREAL":              # first element of course
         scanner.push(scanner.token)
        game.isarmed = ja()
     elif key == "IHREAL":              # first element of course
         scanner.push(scanner.token)
-    if not getcourse(isprobe=True):
-       return
+    try:
+        game.probe = getcourse(isprobe=True)
+    except TrekError:
+        return
     game.nprobes -= 1
     game.nprobes -= 1
-    angle = ((15.0 - game.direc) * 0.5235988)
-    game.probein = coord(-math.sin(angle), math.cos(angle))
-    bigger = max(abs(game.probein.i), abs(game.probein.j))
-    game.probein /= bigger
-    game.proben = 10.0*game.dist*bigger +0.5
-    game.probe = coord(game.quadrant.i*QUADSIZE + game.sector.i, 
-                       game.quadrant.j*QUADSIZE + game.sector.j)
-    game.probec = copy.copy(game.quadrant)
     schedule(FDSPROB, 0.01) # Time to move one sector
     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
     game.ididit = True
     schedule(FDSPROB, 0.01) # Time to move one sector
     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
     game.ididit = True
@@ -4353,13 +4125,13 @@ def mayday():
        game.quadrant = ibq
        newqad()
     # dematerialize starship 
        game.quadrant = ibq
        newqad()
     # dematerialize starship 
-    game.quad[game.sector.i][game.sector.j]=IHDOT
+    game.quad[game.sector.i][game.sector.j]='.'
     proutn(_("Starbase in Quadrant %s responds--%s dematerializes") \
            % (game.quadrant, crmshp()))
     game.sector.invalidate()
     for m in range(1, 5+1):
         w = game.base.scatter() 
     proutn(_("Starbase in Quadrant %s responds--%s dematerializes") \
            % (game.quadrant, crmshp()))
     game.sector.invalidate()
     for m in range(1, 5+1):
         w = game.base.scatter() 
-       if VALID_SECTOR(w.i,w.j) and game.quad[w.i][w.j]==IHDOT:
+       if w.valid_sector() and game.quad[w.i][w.j]=='.':
            # found one -- finish up 
             game.sector = w
            break
            # found one -- finish up 
             game.sector = w
            break
@@ -4374,25 +4146,21 @@ def mayday():
        elif m == 2: proutn(_("2nd"))
        elif m == 3: proutn(_("3rd"))
        proutn(_(" attempt to re-materialize ") + crmshp())
        elif m == 2: proutn(_("2nd"))
        elif m == 3: proutn(_("3rd"))
        proutn(_(" attempt to re-materialize ") + crmshp())
-       game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
-       #textcolor("red")
+       game.quad[ix][iy]=('-','o','O')[m-1]
        warble()
        if randreal() > probf:
            break
        prout(_("fails."))
        curses.delay_output(500)
        warble()
        if randreal() > probf:
            break
        prout(_("fails."))
        curses.delay_output(500)
-       #textcolor(None)
     if m > 3:
     if m > 3:
-       game.quad[ix][iy]=IHQUEST
+       game.quad[ix][iy]='?'
        game.alive = False
        drawmaps(1)
        setwnd(message_window)
        finish(FMATERIALIZE)
        return
     game.quad[ix][iy]=game.ship
        game.alive = False
        drawmaps(1)
        setwnd(message_window)
        finish(FMATERIALIZE)
        return
     game.quad[ix][iy]=game.ship
-    #textcolor("green")
     prout(_("succeeds."))
     prout(_("succeeds."))
-    #textcolor(None)
     dock(False)
     skip(1)
     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
     dock(False)
     skip(1)
     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
@@ -4401,7 +4169,7 @@ def abandon():
     "Abandon ship."
     scanner.chew()
     if game.condition=="docked":
     "Abandon ship."
     scanner.chew()
     if game.condition=="docked":
-       if game.ship!=IHE:
+       if game.ship!='E':
            prout(_("You cannot abandon Ye Faerie Queene."))
            return
     else:
            prout(_("You cannot abandon Ye Faerie Queene."))
            return
     else:
@@ -4459,11 +4227,11 @@ def abandon():
            newqad()
        while True:
            # position next to base by trial and error 
            newqad()
        while True:
            # position next to base by trial and error 
-           game.quad[game.sector.i][game.sector.j] = IHDOT
+           game.quad[game.sector.i][game.sector.j] = '.'
            for l in range(QUADSIZE):
                game.sector = game.base.scatter()
            for l in range(QUADSIZE):
                game.sector = game.base.scatter()
-               if VALID_SECTOR(game.sector.i, game.sector.j) and \
-                       game.quad[game.sector.i][game.sector.j] == IHDOT:
+               if game.sector.valid_sector() and \
+                       game.quad[game.sector.i][game.sector.j] == '.':
                     break
            if l < QUADSIZE+1:
                break # found a spot 
                     break
            if l < QUADSIZE+1:
                break # found a spot 
@@ -4471,7 +4239,7 @@ def abandon():
            game.sector.j=QUADSIZE/2
            newqad()
     # Get new commission 
            game.sector.j=QUADSIZE/2
            newqad()
     # Get new commission 
-    game.quad[game.sector.i][game.sector.j] = game.ship = IHF
+    game.quad[game.sector.i][game.sector.j] = game.ship = 'F'
     game.state.crew = FULLCREW
     prout(_("Starfleet puts you in command of another ship,"))
     prout(_("the Faerie Queene, which is antiquated but,"))
     game.state.crew = FULLCREW
     prout(_("Starfleet puts you in command of another ship,"))
     prout(_("the Faerie Queene, which is antiquated but,"))
@@ -4854,7 +4622,7 @@ def deathray():
     game.ididit = False
     skip(1)
     scanner.chew()
     game.ididit = False
     skip(1)
     scanner.chew()
-    if game.ship != IHE:
+    if game.ship != 'E':
        prout(_("Ye Faerie Queene has no death ray."))
        return
     if len(game.enemies)==0:
        prout(_("Ye Faerie Queene has no death ray."))
        return
     if len(game.enemies)==0:
@@ -4938,8 +4706,8 @@ def deathray():
        prout(_(" Mr. Sulu."))
        for i in range(QUADSIZE):
            for j in range(QUADSIZE):
        prout(_(" Mr. Sulu."))
        for i in range(QUADSIZE):
            for j in range(QUADSIZE):
-               if game.quad[i][j] == IHDOT:
-                   game.quad[i][j] = IHQUEST
+               if game.quad[i][j] == '.':
+                   game.quad[i][j] = '?'
        prout(_("  Captain, our quadrant is now infested with"))
        prouts(_(" - - - - - -  *THINGS*."))
        skip(1)
        prout(_("  Captain, our quadrant is now infested with"))
        prouts(_(" - - - - - -  *THINGS*."))
        skip(1)
@@ -5013,7 +4781,7 @@ def report():
        prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
     if game.nhelp:
        prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
        prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
     if game.nhelp:
        prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
-    if game.ship == IHE:
+    if game.ship == 'E':
        proutn(_("You have "))
        if game.nprobes:
            proutn("%d" % (game.nprobes))
        proutn(_("You have "))
        if game.nprobes:
            proutn("%d" % (game.nprobes))
@@ -5058,7 +4826,7 @@ def lrscan(silent):
         if not silent:
             proutn(" ")
         for y in range(game.quadrant.j-1, game.quadrant.j+2):
         if not silent:
             proutn(" ")
         for y in range(game.quadrant.j-1, game.quadrant.j+2):
-           if not VALID_QUADRANT(x, y):
+           if not coord(x, y).valid_quadrant():
                 if not silent:
                     proutn("  -1")
            else:
                 if not silent:
                     proutn("  -1")
            else:
@@ -5077,7 +4845,6 @@ def damagereport():
     "Damage report."
     jdam = False
     scanner.chew()
     "Damage report."
     jdam = False
     scanner.chew()
-
     for i in range(NDEVICES):
        if damaged(i):
            if not jdam:
     for i in range(NDEVICES):
        if damaged(i):
            if not jdam:
@@ -5141,16 +4908,7 @@ def chart():
 def sectscan(goodScan, i, j):
     "Light up an individual dot in a sector."
     if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
 def sectscan(goodScan, i, j):
     "Light up an individual dot in a sector."
     if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 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.quad[i][j] != game.ship: 
-               highvideo()
        proutn("%c " % game.quad[i][j])
        proutn("%c " % game.quad[i][j])
-       #textcolor(None)
     else:
        proutn("- ")
 
     else:
        proutn("- ")
 
@@ -5162,11 +4920,8 @@ def status(req=0):
     if not req or req == 2:
        if game.condition != "docked":
            newcnd()
     if not req or req == 2:
        if game.condition != "docked":
            newcnd()
-        dam = 0
-       for t in range(NDEVICES):
-           if game.damage[t]>0: 
-               dam += 1
-       prstat(_("Condition"), _("%s, %i DAMAGES") % (game.condition.upper(), dam))
+       prstat(_("Condition"), _("%s, %i DAMAGES") % \
+               (game.condition.upper(), sum(map(lambda x: x > 0, game.damage))))
     if not req or req == 3:
        prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
     if not req or req == 4:
     if not req or req == 3:
        prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
     if not req or req == 4:
@@ -5199,7 +4954,7 @@ def status(req=0):
        prstat(_("Shields"), s+data)
     if not req or req == 9:
         prstat(_("Klingons Left"), "%d" \
        prstat(_("Shields"), s+data)
     if not req or req == 9:
         prstat(_("Klingons Left"), "%d" \
-               % (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem))
+               % (game.state.remkl+len(game.state.kcmdr)+game.state.nscrem))
     if not req or req == 10:
        if game.options & OPTION_WORLDS:
            plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
     if not req or req == 10:
        if game.options & OPTION_WORLDS:
            plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
@@ -5284,10 +5039,10 @@ def eta():
            w2.j = 0
        else:
            w2.j=QUADSIZE-1
            w2.j = 0
        else:
            w2.j=QUADSIZE-1
-    if not VALID_QUADRANT(w1.i, w1.j) or not VALID_SECTOR(w2.i, w2.j):
+    if not w1.valid_quadrant() or not w2.valid_sector():
        huh()
        return
        huh()
        return
-    game.dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
+    dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
                (w1.i-game.quadrant.i+(w2.i-game.sector.i)/(QUADSIZE*1.0))**2)
     wfl = False
     if prompt:
                (w1.i-game.quadrant.i+(w2.i-game.sector.i)/(QUADSIZE*1.0))**2)
     wfl = False
     if prompt:
@@ -5299,7 +5054,7 @@ def eta():
            ttime = scanner.real
            if ttime > game.state.date:
                ttime -= game.state.date # Actually a star date
            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
+            twarp=(math.floor(math.sqrt((10.0*dist)/ttime)*10.0)+1.0)/10.0
             if ttime <= 1e-10 or twarp > 10:
                prout(_("We'll never make it, sir."))
                scanner.chew()
             if ttime <= 1e-10 or twarp > 10:
                prout(_("We'll never make it, sir."))
                scanner.chew()
@@ -5319,8 +5074,8 @@ def eta():
        prout(_("Captain, certainly you can give me one of these."))
     while True:
        scanner.chew()
        prout(_("Captain, certainly you can give me one of these."))
     while True:
        scanner.chew()
-       ttime = (10.0*game.dist)/twarp**2
-       tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
+       ttime = (10.0*dist)/twarp**2
+       tpower = dist*twarp*twarp*twarp*(game.shldup+1)
        if tpower >= game.energy:
            prout(_("Insufficient energy, sir."))
            if not game.shldup or tpower > game.energy*2.0:
        if tpower >= game.energy:
            prout(_("Insufficient energy, sir."))
            if not game.shldup or tpower > game.energy*2.0:
@@ -5505,7 +5260,7 @@ def setup():
        return # frozen game
     # Prepare the Enterprise
     game.alldone = game.gamewon = game.shldchg = game.shldup = False
        return # frozen game
     # Prepare the Enterprise
     game.alldone = game.gamewon = game.shldchg = game.shldup = False
-    game.ship = IHE
+    game.ship = 'E'
     game.state.crew = FULLCREW
     game.energy = game.inenrg = 5000.0
     game.shield = game.inshld = 2500.0
     game.state.crew = FULLCREW
     game.energy = game.inenrg = 5000.0
     game.shield = game.inshld = 2500.0
@@ -5802,7 +5557,7 @@ def dropin(iquad=None):
     "Drop a feature on a random dot in the current quadrant."
     while True:
         w = randplace(QUADSIZE)
     "Drop a feature on a random dot in the current quadrant."
     while True:
         w = randplace(QUADSIZE)
-        if game.quad[w.i][w.j] == IHDOT:
+        if game.quad[w.i][w.j] == '.':
             break
     if iquad is not None:
         game.quad[w.i][w.j] = iquad
             break
     if iquad is not None:
         game.quad[w.i][w.j] = iquad
@@ -5820,7 +5575,7 @@ def newcnd():
 
 def newkling():
     "Drop new Klingon into current quadrant."
 
 def newkling():
     "Drop new Klingon into current quadrant."
-    return enemy(IHK, loc=dropin(), power=randreal(300,450)+25.0*game.skill)
+    return enemy('K', loc=dropin(), power=randreal(300,450)+25.0*game.skill)
 
 def newqad():
     "Set up a new state of quadrant, for when we enter or re-enter it."
 
 def newqad():
     "Set up a new state of quadrant, for when we enter or re-enter it."
@@ -5829,7 +5584,7 @@ def newqad():
     game.neutz = game.inorbit = game.landed = False
     game.ientesc = game.iseenit = False
     # Create a blank quadrant
     game.neutz = game.inorbit = game.landed = False
     game.ientesc = game.iseenit = False
     # Create a blank quadrant
-    game.quad = fill2d(QUADSIZE, lambda i, j: IHDOT)
+    game.quad = fill2d(QUADSIZE, lambda i, j: '.')
     if game.iscate:
        # Attempt to escape Super-commander, so tbeam back!
        game.iscate = False
     if game.iscate:
        # Attempt to escape Super-commander, so tbeam back!
        game.iscate = False
@@ -5851,28 +5606,28 @@ def newqad():
         for cmdr in game.state.kcmdr:
            if cmdr == game.quadrant:
                 e = game.enemies[game.klhere-1]
         for cmdr in game.state.kcmdr:
            if cmdr == game.quadrant:
                 e = game.enemies[game.klhere-1]
-                game.quad[e.kloc.i][e.kloc.j] = IHC
+                game.quad[e.kloc.i][e.kloc.j] = 'C'
                 e.kpower = randreal(950,1350) + 50.0*game.skill
                break   
        # If we need a super-commander, promote a Klingon
        if game.quadrant == game.state.kscmdr:
             e = game.enemies[0]
                 e.kpower = randreal(950,1350) + 50.0*game.skill
                break   
        # If we need a super-commander, promote a Klingon
        if game.quadrant == game.state.kscmdr:
             e = game.enemies[0]
-           game.quad[e.kloc.i][e.kloc.j] = IHS
+           game.quad[e.kloc.i][e.kloc.j] = 'S'
            e.kpower = randreal(1175.0,  1575.0) + 125.0*game.skill
            game.iscate = (game.state.remkl > 1)
     # Put in Romulans if needed
     for i in range(q.romulans):
            e.kpower = randreal(1175.0,  1575.0) + 125.0*game.skill
            game.iscate = (game.state.remkl > 1)
     # Put in Romulans if needed
     for i in range(q.romulans):
-        enemy(IHR, loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
+        enemy('R', loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
     # If quadrant needs a starbase, put it in
     if q.starbase:
     # If quadrant needs a starbase, put it in
     if q.starbase:
-       game.base = dropin(IHB)
+       game.base = dropin('B')
     # If quadrant needs a planet, put it in
     if q.planet:
        game.iplnet = q.planet
        if not q.planet.inhabited:
     # If quadrant needs a planet, put it in
     if q.planet:
        game.iplnet = q.planet
        if not q.planet.inhabited:
-           game.plnet = dropin(IHP)
+           game.plnet = dropin('P')
        else:
        else:
-           game.plnet = dropin(IHW)
+           game.plnet = dropin('@')
     # Check for condition
     newcnd()
     # Check for RNZ
     # Check for condition
     newcnd()
     # Check for RNZ
@@ -5887,7 +5642,7 @@ def newqad():
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
     # Put in THING if needed
     if thing == game.quadrant:
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
     # Put in THING if needed
     if thing == game.quadrant:
-        enemy(type=IHQUEST, loc=dropin(),
+        enemy(type='?', loc=dropin(),
                   power=randreal(6000,6500.0)+250.0*game.skill)
         if not damaged(DSRSENS):
             skip(1)
                   power=randreal(6000,6500.0)+250.0*game.skill)
         if not damaged(DSRSENS):
             skip(1)
@@ -5902,37 +5657,37 @@ def newqad():
             while True:
                w.i = withprob(0.5) * (QUADSIZE-1)
                w.j = withprob(0.5) * (QUADSIZE-1)
             while True:
                w.i = withprob(0.5) * (QUADSIZE-1)
                w.j = withprob(0.5) * (QUADSIZE-1)
-                if game.quad[w.i][w.j] == IHDOT:
+                if game.quad[w.i][w.j] == '.':
                     break
                     break
-            game.tholian = enemy(type=IHT, loc=w,
+            game.tholian = enemy(type='T', loc=w,
                                  power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
                                  power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners 
-           if game.quad[0][0]==IHDOT:
+           if game.quad[0][0]=='.':
                game.quad[0][0] = 'X'
                game.quad[0][0] = 'X'
-           if game.quad[0][QUADSIZE-1]==IHDOT:
+           if game.quad[0][QUADSIZE-1]=='.':
                game.quad[0][QUADSIZE-1] = 'X'
                game.quad[0][QUADSIZE-1] = 'X'
-           if game.quad[QUADSIZE-1][0]==IHDOT:
+           if game.quad[QUADSIZE-1][0]=='.':
                game.quad[QUADSIZE-1][0] = 'X'
                game.quad[QUADSIZE-1][0] = 'X'
-           if game.quad[QUADSIZE-1][QUADSIZE-1]==IHDOT:
+           if game.quad[QUADSIZE-1][QUADSIZE-1]=='.':
                game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
     # And finally the stars
     for i in range(q.stars):
                game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
     # And finally the stars
     for i in range(q.stars):
-       dropin(IHSTAR)
+       dropin('*')
     # Put in a few black holes
     for i in range(1, 3+1):
        if withprob(0.5): 
     # Put in a few black holes
     for i in range(1, 3+1):
        if withprob(0.5): 
-           dropin(IHBLANK)
+           dropin(' ')
     # Take out X's in corners if Tholian present
     if game.tholian:
        if game.quad[0][0]=='X':
     # Take out X's in corners if Tholian present
     if game.tholian:
        if game.quad[0][0]=='X':
-           game.quad[0][0] = IHDOT
+           game.quad[0][0] = '.'
        if game.quad[0][QUADSIZE-1]=='X':
        if game.quad[0][QUADSIZE-1]=='X':
-           game.quad[0][QUADSIZE-1] = IHDOT
+           game.quad[0][QUADSIZE-1] = '.'
        if game.quad[QUADSIZE-1][0]=='X':
        if game.quad[QUADSIZE-1][0]=='X':
-           game.quad[QUADSIZE-1][0] = IHDOT
+           game.quad[QUADSIZE-1][0] = '.'
        if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
        if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
-           game.quad[QUADSIZE-1][QUADSIZE-1] = IHDOT
+           game.quad[QUADSIZE-1][QUADSIZE-1] = '.'
 
 def setpassword():
     "Set the self-destruct password."
 
 def setpassword():
     "Set the self-destruct password."
@@ -6037,10 +5792,8 @@ def helpme():
             proutn(_("   current directory or to "))
             proutn(SSTDOC)
             prout(".\"")
             proutn(_("   current directory or to "))
             proutn(SSTDOC)
             prout(".\"")
-            #
             # This used to continue: "You need to find SST.DOC and put 
             # it in the current directory."
             # This used to continue: "You need to find SST.DOC and put 
             # it in the current directory."
-            # 
             return
     while True:
         linebuf = fp.readline()
             return
     while True:
         linebuf = fp.readline()
@@ -6112,7 +5865,7 @@ def makemoves():
            if game.ididit:
                hitme = True
        elif cmd == "MOVE":             # move under warp
            if game.ididit:
                hitme = True
        elif cmd == "MOVE":             # move under warp
-           warp(False)
+           warp(course=None, involuntary=False)
        elif cmd == "SHIELDS":          # shields
            doshield(shraise=False)
            if game.ididit:
        elif cmd == "SHIELDS":          # shields
            doshield(shraise=False)
            if game.ididit:
@@ -6221,18 +5974,18 @@ def makemoves():
 
 def cramen(type):
     "Emit the name of an enemy or feature." 
 
 def cramen(type):
     "Emit the name of an enemy or feature." 
-    if   type == IHR: s = _("Romulan")
-    elif type == IHK: s = _("Klingon")
-    elif type == IHC: s = _("Commander")
-    elif type == IHS: s = _("Super-commander")
-    elif type == IHSTAR: s = _("Star")
-    elif type == IHP: s = _("Planet")
-    elif type == IHB: s = _("Starbase")
-    elif type == IHBLANK: s = _("Black hole")
-    elif type == IHT: s = _("Tholian")
-    elif type == IHWEB: s = _("Tholian web")
-    elif type == IHQUEST: s = _("Stranger")
-    elif type == IHW: s = _("Inhabited World")
+    if   type == 'R': s = _("Romulan")
+    elif type == 'K': s = _("Klingon")
+    elif type == 'C': s = _("Commander")
+    elif type == 'S': s = _("Super-commander")
+    elif type == '*': s = _("Star")
+    elif type == 'P': s = _("Planet")
+    elif type == 'B': s = _("Starbase")
+    elif type == ' ': s = _("Black hole")
+    elif type == 'T': s = _("Tholian")
+    elif type == '#': s = _("Tholian web")
+    elif type == '?': s = _("Stranger")
+    elif type == '@': s = _("Inhabited World")
     else: s = "Unknown??"
     return s
 
     else: s = "Unknown??"
     return s
 
@@ -6250,7 +6003,7 @@ def crmena(stars, enemy, loctype, w):
 
 def crmshp():
     "Emit our ship name." 
 
 def crmshp():
     "Emit our ship name." 
-    return{IHE:_("Enterprise"),IHF:_("Faerie Queene")}.get(game.ship,"Ship???")
+    return{'E':_("Enterprise"),'F':_("Faerie Queene")}.get(game.ship,"Ship???")
 
 def stars():
     "Emit a line of stars" 
 
 def stars():
     "Emit a line of stars"