Get rid of obnoxious visible "have we paused?" state.
[super-star-trek.git] / src / sst.py
index 8e3842f045b30132845080f2486ee6301c5b8593..ace9641c7da650a38076089c0f05ba4c97016f96 100644 (file)
@@ -1,5 +1,15 @@
 """
 sst.py =-- Super Star Trek in Python
+
+Control flow of this translation is pretty much identical to the C version
+(and thus like the ancestral FORTRAN) but the data structures are
+radically different -- the Python code makes heavy use of objects.
+
+Note that the game.quad, game.snap.galaxy and game.snap.chart members
+are not actually arrays but dictioaries indixed by coord tuples.  Be setting
+the hash of a coord equal to the hash of a literal tuple containing its
+coordinate data, we ensure these can be indexed both ways.
+
 """
 import math
 
@@ -15,6 +25,11 @@ MAXKLGAME    = 127
 MAXKLQUAD      = 9
 FOREVER        = 1e30
 
+# These macros hide the difference between 0-origin and 1-origin addressing.
+# They're a step towards de-FORTRANizing the code.
+def VALID_QUADRANT(x,y): ((x)>=1 and (x)<=GALSIZE and (y)>=1 and (y)<=GALSIZE)
+def VALID_SECTOR(x, y):        ((x)>=1 and (x)<=QUADSIZE and (y)>=1 and (y)<=QUADSIZE)
+
 # These types have not been dealt with yet 
 IHQUEST = '?',
 IHWEB = '#',
@@ -46,24 +61,30 @@ class coord:
         return "%d - %d" % (self.x, self.y)
 
 class feature:
-    "A feature in the current quadrant (ship, star, black hole, etc)." 
+    "A feature in the current quadrant (ship, star, black hole, base, etc)." 
     def __init__(self):
         self.type = None       # name of feature type
-        self.location = None   # location
+        self.sector = None     # sector location
     def distance(self):
-        return self.location.distance(game.sector)
+        return self.sector.distance(game.sector)
     def __str__(self):
+        "This will be overridden by subclasses."
         return self.name[0]
-
-empty = None   # Value of empty space in game.quad
+    def sectormove(self, dest):
+        "Move this feature within the current quadrant." 
+        if self.sector:
+            game.quad[self.sector] = None
+        game.quad[dest] = self
+        self.sector = dest
 
 class ship(feature):
-    "An enemy ship in the current quadrant." 
-    def __init__(self):
+    "A starship, frindly or enemy." 
+    def __init__(self, type, power):
         feature.__init__(self)
-        self.type = None       # klingon, romulan, commander,
-                               # supercommander, tholian
-        self.power = None      # power
+        self.type = type       # klingon, romulan, commander,
+                               # supercommander, tholian,
+                                # enterprise, faerie queene.
+        self.power = power     # power
         if self.type in ("Klingon", "Commander", "Super-Commander"):
             game.remkl += 1
         elif self.type == "Romulan":
@@ -73,12 +94,16 @@ class ship(feature):
             game.remkl -= 1
         elif self.type == "Romulan":
             game.romrem -= 1
-    def sectormove(self, dest):
-        "Move this ship within the current quadrant." 
-        if self.location:
-            game.quad[self.location] = None
-        game.quad[dest] = self
-        self.location = dest
+
+class space(feature):
+    "Empty space.  Has no state, just knows how to identify iself."
+    def __str__(self):
+        return '*'
+
+class star(feature):
+    "A star.  Has no state, just knows how to identify iself."
+    def __str__(self):
+        return '*'
 
 class planet(feature):
     "A planet.  May be inhabited or not, may hold dilithium crystals or not."
@@ -88,39 +113,33 @@ class planet(feature):
         self.crystals = None   # "absent", "present", or "mined"
         self.inhabited = False
         self.known = "unknown" # Other values: "known" and "shuttle down"
+        game.state.planets.append(self)
+    def __del__(self):
+        game.state.planets.remove(self)
     def __str__(self):
         if self.inhabited:
             return '@'
         else:
             return 'P'
 
-class star(feature):
-    "A star.  Has no state, just knows how to identify iself."
-    def __init(self):
-        feature.__init__(self)
-    def __str__(self):
-        return '*'
-
 class web(feature):
     "A bit of Tholian web.  Has no state, just knows how to identify iself."
-    def __init(self):
-        feature.__init__(self)
     def __str__(self):
         return '*'
 
 class blackhole(feature):
     "A black hole.  Has no hair, just knows how to identify iself."
-    def __init(self):
-        feature.__init__(self)
     def __str__(self):
-        return '*'
+        return ' '
 
 class starbase(feature):
-    "Starbases also have no features."
-    def __init(self):
+    "Starbases also have no features, just a location."
+    def __init(self, quadrant):
         feature.__init__(self)
+        self.quadrant = quadrant
+        game.state.bases.append(self)
     def __del__(self):
-        game.state.bases.remove(self.location)
+        game.state.bases.remove(self)
     def __str__(self):
         return 'B'
 
@@ -158,13 +177,11 @@ class snapshot:
        self.remkl = None       # remaining klingons
        self.remcom = None      # remaining commanders
        self.nscrem = None      # remaining super commanders
-       self.rembase = None     # remaining bases
        self.starkl = None      # destroyed stars
        self.basekl = None      # destroyed bases
        self.nromrem = None     # Romulans remaining
-       self.nplankl = None     # destroyed uninhabited planets
-       self.nworldkl = None    # destroyed inhabited planets
-        self.plnets = [];      # List of planets known
+       self.nplankl = None     # destroyed uninhabited planets self.nworldkl = None    # destroyed inhabited planets
+        self.planets = [];     # List of planets known
         self.date = None       # stardate
        self.remres = None      # remaining resources
        self. remtime = None    # remaining time
@@ -280,7 +297,7 @@ class game:
 
 def communicating():
     "Are we in communication with Starfleet Command?"
-    return (not damaged("DRADIO")) or game.condition == docked
+    return (not damaged("DRADIO")) or game.condition == "docked"
 
 # Code corresponding to ai.c begins here
 
@@ -291,8 +308,8 @@ def tryexit(look, ship, irun):
     iq.y = game.quadrant.y+(look.y+(QUADSIZE-1))/QUADSIZE - 1
     if not valid_quadrant(iq) or \
        game.state.galaxy[iq].supernova or \
-        game.state.galaxy[iq].klingons > 8:
-       return False;   # no can do -- neg energy, supernovae, or >8 Klingons
+        game.state.galaxy[iq].klingons > MAXKLQUAD-1:
+       return False;   # no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons
     if ship.type == "Romulan":
         return False   # Romulans cannot escape
     if not irun:
@@ -506,7 +523,7 @@ def movebaddy(ship):
                 prout(" retreats to sector %s" % ship.location)
         ship.sectormove(next)
 
-def movcom(): 
+def moveklings(): 
     "Allow enemies to move."
     for enemy in self.quad.enemies():
         if enemy.type == "Commander":
@@ -527,10 +544,9 @@ def movcom():
 
 def movescom(ship, avoid):
     # commander movement helper
-    global ipage
     if game.state.kscmdr == game.quadrant or \
        game.state.galaxy[iq].supernova or \
-        game.state.galaxy[iq].klingons > 8
+        game.state.galaxy[iq].klingons > MAXKLQUAD-1
        return True
     if avoid:
        # Avoid quadrants with bases if we want to avoid Enterprise
@@ -553,14 +569,12 @@ def movescom(ship, avoid):
     game.state.kscmdr = iq
     game.state.galaxy[game.state.kscmdr].klingons += 1
     # check for a helpful planet in the destination quadrant
-    for planet in game.state.plnets:
+    for planet in game.state.planets:
        if planet.location == game.state.kscmdr and planet.crystals=="present":
            # destroy the planet
-           game.state.plnets.remove(planet)
+           del planet
             if communicating():
-                if not ipage:
                     pause_game(True)
-               ipage = true
                prout("Lt. Uhura-  \"Captain, Starfleet Intelligence reports")
                proutn(_("   a planet in "))
                proutn(cramlc(quadrant, game.state.kscmdr))
@@ -578,12 +592,12 @@ def scom():
     passive = ((NKILLC+NKILLK)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) \
                or (game.state.date-game.indate) < 3.0)
     if not game.iscate and passive:
-       # compute move away from Enterprise
-       idelta = game.state.kscmdr - game.quadrant
+       # coxmpute move away from Enterprise
+       delta = game.state.kscmdr - game.quadrant
         if distance(game.state.kscmdr) > 2.0:
            # circulate in space
-           idelta,x = game.state.kscmdr.y-game.quadrant.y
-           idelta,y = game.quadrant.x-game.state.kscmdr.x
+           delta.x = game.state.kscmdr.y-game.quadrant.y
+           delta.y = game.quadrant.x-game.state.kscmdr.x
     else:
         if len(game.state.bases):
             unschedule("FSCMOVE")
@@ -595,7 +609,7 @@ def scom():
        # without too many Klingons, and not already under attack.
         nearest = filter(game.starbases,
                          lambda x: game.state.galaxy[x].supernova \
-                         and game.state.galaxy[x].klingons <= 8)
+                         and game.state.galaxy[x].klingons <= MAXKLQUAD-1)
         if game.quadrant in nearest:
             nearest.remove(game.quadrant)
         if game.battle in nearest:
@@ -609,16 +623,16 @@ def scom():
         if len(nearest) == 0:
             return     # Nothing suitable -- wait until next time
        # decide how to move toward base
-       idelta = ibq - game.state.kscmdr
+       delta = ibq - game.state.kscmdr
     # maximum movement is 1 quadrant in either or both axis
     delta = delta.sgn()
     # try moving in both x and y directions
-    iq = game.state.kscmdr + idelta
+    iq = game.state.kscmdr + delta
     if movescom(iq, passive):
        # failed -- try some other maneuvers
-        if ideltax==0 or ideltay==0:
+        if delta.x==0 or delta.y==0:
            # attempt angle move
-            if ideltax != 0:
+            if delta.x != 0:
                iq.y = game.state.kscmdr.y + 1
                 if movescom(iq, passive):
                    iq.y = game.state.kscmdr.y - 1
@@ -632,11 +646,11 @@ def scom():
            # try moving just in x or y
            iq.y = game.state.kscmdr.y
             if movescom(iq, passive):
-               iq.y = game.state.kscmdr.y + ideltay
+               iq.y = game.state.kscmdr.y + delta.y
                iq.x = game.state.kscmdr.x
                movescom(iq, passive)
     # check for a base
-    if game.state.rembase == 0:
+    if len(game.state.bases) == 0:
        unschedule("FSCMOVE")
     else:
         for ibq in game.bases:
@@ -652,9 +666,7 @@ def scom():
                 if not communicating():
                     return # no warning
                 game.iseenit = True
-                if not ipage:
-                    pause_game(true)
-                ipage = True
+                pause_game(true)
                 proutn(_("Lt. Uhura-  \"Captain, the starbase in "))
                 proutn(cramlc(quadrant, game.state.kscmdr))
                 skip(1)
@@ -673,9 +685,7 @@ def scom():
     if (Rand() > 0.2 or not communicating() or
         not game.state.galaxy[game.state.kscmdr].charted):
        return
-    if ipage:
-        pause_game(true)
-        ipage = true
+    pause_game(true)
     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
     proutn(_("   the Super-commander is in "))
     proutn(cramlc(quadrant, game.state.kscmdr))
@@ -702,7 +712,7 @@ def movetho(void):
        game.tholian = None
        return
     # Do nothing if we are blocked
-    if game.quad[next] != empty and not isinstance(game.quad[next]. web):
+    if not (isinstance(game.quad[next], space) or isinstance(game.quad[next], web)):
         return
     # Now place some web
     im = (next - game.tholian.location).sgn()
@@ -710,13 +720,13 @@ def movetho(void):
        # move in x axis
        while game.tholian.location.x != next.x:
            game.tholian.location.x += im.x
-            if game.quad[game.tholian.location] == empty:
+            if isinstance(game.quad[game.tholian.location], space):
                 game.quad[game.tholian.location] = web()
     elif game.tholian.y != next.y:
        # move in y axis
        while game.tholian.y != next.y:
            game.tholian.y += im.y
-            if game.quad[game.tholian.location] == empty:
+            if isinstance(game.quad[game.tholian.location], space):
                 game.quad[game.tholian.location] = web()
     # web is done, move ship
     game.tholian.movesector(next)