New tests are working well eniugh that we can flush the old ones.
[super-star-trek.git] / sst.py
diff --git a/sst.py b/sst.py
index a9900528bda3b4edf03feb28b2a3decdaa8ce7ff..8466a481dbf7caec731003ee5aa64a52f0baa3b3 100755 (executable)
--- a/sst.py
+++ b/sst.py
@@ -36,43 +36,57 @@ docpath     = (".", "doc/", "/usr/share/doc/sst/")
 def _(st):
     return gettext.gettext(st)
 
-# This is all encapsulated not just for logging but because someday
-# we'll probably want to replace it with something like an LCG that
-# can be forward-ported off Python.
-
-import random
+# Rolling our own LCG because Python changed its incompatibly in 3.2.
+# Thus, we need to have our own to be 2/3 polyglot, which will also
+# be helpful when we forwrard-port.
 
 class randomizer:
+    # LCG PRNG parameters tested against
+    # Knuth vol. 2. by the authors of ADVENT 
+    LCG_A = 1093
+    LCG_C = 221587
+    LCG_M = 1048576
+
+    @staticmethod
+    def random():
+        old_x = game.lcg_x
+        game.lcg_x = (randomizer.LCG_A * game.lcg_x + randomizer.LCG_C) % randomizer.LCG_M
+        return old_x / randomizer.LCG_M;
+
     @staticmethod
     def withprob(p):
-        v = random.random()
-        if logfp:
-            logfp.write("#withprob(%.2f) -> %s\n" % (p, v < p))
+        v = randomizer.random()
+        #if logfp:
+        #    logfp.write("#withprob(%.2f) -> %s\n" % (p, v < p))
         return v < p
 
     @staticmethod
     def integer(*args):
-        s = random.randrange(*args)
-        if logfp:
-            logfp.write("#randrange%s -> %s\n" % (args, s))
-        return s
+        v = randomizer.random()
+        if len(args) == 1:
+            v = int(v * args[0])
+        else:
+            v = args[0] + int(v * (args[1] - args[0]))
+        #if logfp:
+        #    logfp.write("#integer%s -> %s\n" % (args, v))
+        return int(v)
 
     @staticmethod
     def real(*args):
-        v = random.random()
+        v = randomizer.random()
         if len(args) == 1:
             v *= args[0]                 # from [0, args[0])
         elif len(args) == 2:
             v = args[0] + v*(args[1]-args[0])        # from [args[0], args[1])
-        if logfp:
-            logfp.write("#real%s -> %f\n" % (args, v))
+        #if logfp:
+        #    logfp.write("#real%s -> %f\n" % (args, v))
         return v
 
     @staticmethod
     def seed(n):
-        if logfp:
-            logfp.write("#seed(%d)\n" % n)
-        random.seed(n)
+        #if logfp:
+        #    logfp.write("#seed(%d)\n" % n)
+        game.lcg_x = n % randomizer.LCG_M
 
 GALSIZE        = 8             # Galaxy size in quadrants
 NINHAB         = (GALSIZE * GALSIZE // 2)      # Number of inhabited worlds
@@ -118,8 +132,8 @@ class JumpOut(Exception):
 
 class Coord:
     def __init__(self, x=None, y=None):
-        self.i = x
-        self.j = y
+        self.i = x     # Row
+        self.j = y     # Column
     def valid_quadrant(self):
         return self.i >= 0 and self.i < GALSIZE and self.j >= 0 and self.j < GALSIZE
     def valid_sector(self):
@@ -162,13 +176,13 @@ class Coord:
         s = Coord()
         if self.i == 0:
             s.i = 0
-        elif s.i < 0:
-            s.i =-1
+        elif self.i < 0:
+            s.i = -1
         else:
             s.i = 1
         if self.j == 0:
             s.j = 0
-        elif s.j < 0:
+        elif self.j < 0:
             s.j = -1
         else:
             s.j = 1
@@ -469,6 +483,7 @@ class Gamestate:
         self.iscloaked = False  # Cloaking device on?
         self.ncviol = 0         # Algreon treaty violations
         self.isviolreported = False # We have been warned
+        self.lcg_x = 0         # LCG generator value
     def remkl(self):
         return sum([q.klingons for (_i, _j, q) in list(self.state.traverse())])
     def recompute(self):
@@ -935,6 +950,7 @@ def movetholian():
         game.tholian.move(None)
         prout("***Internal error: Tholian in a bad spot.")
         return
+    print("Tholian moving from %s to %s" % (game.tholian.location, tid))
     # do nothing if we are blocked
     if game.quad[tid.i][tid.j] not in ('.', '#'):
         return
@@ -1722,7 +1738,11 @@ def torps():
             proutn(_("Number of torpedoes to fire- "))
             continue        # Go back around to get a number
         else: # key == "IHREAL"
-            n = scanner.int()
+            try:
+                n = scanner.int()
+            except TypeError:
+                huh()
+                return
             if n <= 0: # abort command
                 scanner.chew()
                 return
@@ -2052,6 +2072,7 @@ def phasers():
             prout(_("Manual-fire-must-be-used"))
             skip(1)
     elif automode == "MANUAL":
+        print("****HERE WE ARE*** %d" % len(game.enemies))
         rpow = 0.0
         for k in range(len(game.enemies)):
             aim = game.enemies[k].location
@@ -2067,7 +2088,6 @@ def phasers():
                 scanner.chew()
                 key = "IHEOL"
                 hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko
-                k += 1
                 continue
             if key == "IHEOL":
                 scanner.chew()
@@ -2097,7 +2117,8 @@ def phasers():
                 # abort out
                 scanner.chew()
                 return
-            hits[k] = scanner.real
+            print("k is %d" % k)
+            hits.append(scanner.real)
             rpow += scanner.real
             # If total requested is too much, inform and start over
             if rpow > avail:
@@ -2105,7 +2126,6 @@ def phasers():
                 scanner.chew()
                 return
             key = scanner.nexttok() # scan for next value
-            k += 1
         if rpow == 0.0:
             # zero energy -- abort
             scanner.chew()