X-Git-Url: https://jxself.org/git/?p=super-star-trek.git;a=blobdiff_plain;f=src%2Fsst.py;h=14a1f27429c2885fd9174a7a6aa8ba10cd30f72a;hp=5b41777bf0d5dc83cdcb995c6405ead793a7c399;hb=6059a6d14ea95686e2dad1f6251580ee9951e2f1;hpb=25c1a316d0dd3ab77642521c359db6cfc660615d diff --git a/src/sst.py b/src/sst.py index 5b41777..14a1f27 100644 --- a/src/sst.py +++ b/src/sst.py @@ -1,4 +1,5 @@ -''' +#!/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. @@ -171,7 +172,7 @@ your score. Docking at a starbase replenishes your crew. 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). -''' +""" import os, sys, math, curses, time, atexit, readline, cPickle, random, getopt SSTDOC = "/usr/share/doc/sst/sst.doc" @@ -186,6 +187,7 @@ NINHAB = (GALSIZE * GALSIZE / 2) MAXUNINHAB = 10 PLNETMAX = (NINHAB + MAXUNINHAB) QUADSIZE = 10 +BASEMIN = 2 BASEMAX = (GALSIZE * GALSIZE / 12) MAXKLGAME = 127 MAXKLQUAD = 9 @@ -226,7 +228,7 @@ IHREAL = 0.0 IHALPHA = " " class coord: - def __init(self, x=None, y=None): + def __init__(self, x=None, y=None): self.x = x self.y = y def invalidate(self): @@ -234,7 +236,7 @@ class coord: def is_valid(self): return self.x != None and self.y != None def __eq__(self, other): - return self.x == other.y and self.x == other.y + return other != None and self.x == other.y and self.x == other.y def __add__(self, other): return coord(self.x+self.x, self.y+self.y) def __sub__(self, other): @@ -246,22 +248,22 @@ class coord: def __hash__(self): return hash((x, y)) def __str__(self): - return "%d - %d" % (self.x, self.y) + return "%s - %s" % (self.x, self.y) class planet: - def __init(self): + def __init__(self): self.name = None # string-valued if inhabited self.w = coord() # quadrant located self.pclass = None # could be ""M", "N", "O", or "destroyed" - self.crystals = None # could be "mined", "present", "absent" - self.known = None # could be "unknown", "known", "shuttle_down" - self.inhabited # is it inhabites? + self.crystals = "absent"# could be "mined", "present", "absent" + self.known = "unknown" # could be "unknown", "known", "shuttle_down" + self.inhabited = False # is it inhabites? def __str__(self): return self.name NOPLANET = None class quadrant: - def __init(self): + def __init__(self): self.stars = None self.planet = None self.starbase = None @@ -272,28 +274,37 @@ class quadrant: self.status = None # Could be "secure", "distressed", "enslaved" class page: - def __init(self): + def __init__(self): self.stars = None self.starbase = None self.klingons = None +def fill2d(size, fillfun): + "Fill an empty list in 2D." + lst = [] + for i in range(size+1): + lst.append([]) + for j in range(size+1): + lst[i][j] = fillfun(i, j) + return lst + class snapshot: - def __init(self): + def __init__(self): self.snap = False # snapshot taken - self.crew = None # crew complement - 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.crew = 0 # crew complement + self.remkl = 0 # remaining klingons + self.remcom = 0 # remaining commanders + self.nscrem = 0 # remaining super commanders + self.rembase = 0 # remaining bases + self.starkl = 0 # destroyed stars + self.basekl = 0 # destroyed bases + self.nromrem = 0 # Romulans remaining + self.nplankl = 0 # destroyed uninhabited planets + self.nworldkl = 0 # destroyed inhabited planets self.planets = [] # Planet information - self.date = None # stardate - self.remres = None # remaining resources - self.remtime = None # remaining time + self.date = 0.0 # stardate + self.remres = 0 # remaining resources + self.remtime = 0 # remaining time self.baseq = [] # Base quadrant coordinates for i in range(BASEMAX+1): self.baseq.append(coord()) @@ -301,16 +312,10 @@ class snapshot: for i in range(QUADSIZE+1): self.kcmdr.append(coord()) self.kscmdr = coord() # Supercommander quadrant coordinates - self.galaxy = [] # The Galaxy (subscript 0 not used) - for i in range(GALSIZE+1): - self.chart.append([]) - for j in range(GALSIZE+1): - self.galaxy[i].append(quadrant()) - self.chart = [] # the starchart (subscript 0 not used) - for i in range(GALSIZE+1): - self.chart.append([]) - for j in range(GALSIZE+1): - self.chart[i].append(page()) + # the galaxy (subscript 0 not used) + self.galaxy = fill2d(GALSIZE, lambda i, j: quadrant()) + # the starchart (subscript 0 not used) + self.chart = fill2d(GALSIZE, lambda i, j: page()) class event: def __init__(self): @@ -389,14 +394,14 @@ def findevent(evtype): return game.future[evtype] class gamestate: def __init__(self): self.options = None # Game options - self.state = None # A snapshot structure - self.snapsht = None # Last snapshot taken for time-travel purposes - self.quad = [[IHDOT * (QUADSIZE+1)] * (QUADSIZE+1)] # contents of our quadrant - self.kpower = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)] # enemy energy levels - self.kdist = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)] # enemy distances - self.kavgd = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)] # average distances + self.state = snapshot() # A snapshot structure + self.snapsht = snapshot() # Last snapshot taken for time-travel purposes + self.quad = fill2d(QUADSIZE, lambda i, j: IHDOT) # contents of our quadrant + self.kpower = fill2d(QUADSIZE, lambda i, j: 0.0) # enemy energy levels + self.kdist = fill2d(QUADSIZE, lambda i, j: 0.0) # enemy distances + self.kavgd = fill2d(QUADSIZE, lambda i, j: 0.0) # average distances self.damage = [0.0] * NDEVICES # damage encountered - self.future = [0.0] * NEVENTS # future events + self.future = [] # future events for i in range(NEVENTS): self.future.append(event()) self.passwd = None; # Self Destruct password @@ -2270,7 +2275,7 @@ def scheduled(evtype): return game.future[evtype].date def schedule(evtype, offset): - # schedule an event of specified type + # schedule an event of specified type game.future[evtype].date = game.state.date + offset return game.future[evtype] @@ -3477,6 +3482,7 @@ def plaque(): rows = linecount = 0 # for paging stdscr = None +replayfp = None fullscreen_window = None srscan_window = None report_window = None @@ -3484,6 +3490,7 @@ status_window = None lrscan_window = None message_window = None prompt_window = None +curwnd = None def outro(): "wrap up, either normally or due to signal" @@ -3494,12 +3501,12 @@ def outro(): #resetterm() #echo() curses.endwin() - stdout.write('\n') + sys.stdout.write('\n') if logfp: logfp.close() def iostart(): - global stdscr + global stdscr, rows #setlocale(LC_ALL, "") #bindtextdomain(PACKAGE, LOCALEDIR) #textdomain(PACKAGE) @@ -3572,16 +3579,16 @@ def pause_game(): setwnd(message_window) else: global linecount - stdout.write('\n') + sys.stdout.write('\n') proutn(prompt) raw_input() for j in range(0, rows): - stdout.write('\n') + sys.stdout.write('\n') linecount = 0 def skip(i): "Skip i lines. Pause game if this would cause a scrolling event." - while dummy in range(i): + for dummy in range(i): if game.options & OPTION_CURSES: (y, x) = curwnd.getyx() (my, mx) = curwnd.getmaxyx() @@ -3593,10 +3600,10 @@ def skip(i): else: global linecount linecount += 1 - if linecount >= rows: + if rows and linecount >= rows: pause_game() else: - stdout.write('\n') + sys.stdout.write('\n') def proutn(line): "Utter a line with no following line feed." @@ -3604,7 +3611,8 @@ def proutn(line): curwnd.addstr(line) curwnd.refresh() else: - stdout.write(line) + sys.stdout.write(line) + sys.stdout.flush() def prout(line): proutn(line) @@ -3630,12 +3638,14 @@ def cgetline(): if replayfp and not replayfp.closed: line = replayfp.readline() else: - sys.stdin.readline() + line = raw_input() if logfp: logfp.write(line) + return line def setwnd(wnd): - "Change windows -- OK for this to be a no-op in tty mode." + "Change windows -- OK for this to be a no-op in tty mode." + global curwnd if game.options & OPTION_CURSES: curwnd = wnd curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window) @@ -5890,7 +5900,6 @@ device = ( def setup(needprompt): # prepare to play, set up cosmos - intj, krem, klumper w = coord() # Decide how many of everything @@ -5915,7 +5924,7 @@ def setup(needprompt): for i in range(0, NDEVICES): game.damage[i] = 0.0 # Set up assorted game parameters - invalidate(game.battle) + game.battle = coord() game.state.date = game.indate = 100.0*int(31.0*random.random()+20.0) game.nkinks = game.nhelp = game.casual = game.abandoned = 0 game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False @@ -5972,9 +5981,9 @@ def setup(needprompt): contflag = False # C version: for (j = i-1; j > 0; j--) # so it did them in the opposite order. - for j in range(i): - # Improved placement algorithm to spread out bases - distq = w.distance(baseq[j]) + for j in range(1, i): + # Improved placement algorithm to spread out bases + distq = w.distance(game.state.baseq[j]) if distq < 6.0*(BASEMAX+1-game.inbase) and random.random() < 0.75: contflag = True if idebug: @@ -6034,13 +6043,13 @@ def setup(needprompt): new.name = systnames[i] new.inhabited = True else: - new.pclass = ("M", "N", "O")[random.random()*3.0] + new.pclass = ("M", "N", "O")[random.randint(0, 2)] if random.random()*1.5: # 1 in 3 chance of crystals new.crystals = "present" new.known = "unknown" new.inhabited = False game.state.galaxy[w.x][w.y].planet = new - game.state.plnets.append(new) + game.state.planets.append(new) # Locate Romulans for i in range(1, game.state.nromrem+1): w = randplace(GALSIZE) @@ -6049,15 +6058,14 @@ def setup(needprompt): if game.state.nscrem > 0: while True: w = randplace(GALSIZE) - if not game.state.galaxy[w.x][w.y].supernova and game.state.galaxy[w.x][w.y].klingons <= MXKLQUAD: + if not game.state.galaxy[w.x][w.y].supernova and game.state.galaxy[w.x][w.y].klingons <= MAXKLQUAD: break game.state.kscmdr = w game.state.galaxy[w.x][w.y].klingons += 1 # Place thing (in tournament game, thingx == -1, don't want one!) - if thing.x != -1: + global thing + if thing == None: thing = randplace(GALSIZE) - else: - invalidate(thing) skip(2) game.state.snap = False if game.skill == SKILL_NOVICE: @@ -6097,7 +6105,8 @@ def setup(needprompt): attack(False) def choose(needprompt): - # choose your game type + # choose your game type + global thing while True: game.tourn = 0 game.thawed = False @@ -6115,7 +6124,6 @@ def choose(needprompt): chew() continue # We don't want a blank entry game.tourn = int(aaitem) - thing.x = -1 random.seed(aaitem) break if isit("saved") or isit("frozen"): @@ -6131,9 +6139,7 @@ def choose(needprompt): return True if isit("regular"): break - proutn(_("What is \"")) - proutn(citem) - prout("\"?") + proutn(_("What is \"%s\"?"), citem) chew() while game.length==0 or game.skill==SKILL_NONE: if scan() == IHALPHA: @@ -6187,7 +6193,7 @@ def choose(needprompt): # Use parameters to generate initial values of things game.damfac = 0.5 * game.skill - game.state.rembase = 2.0 + random.random()*(BASEMAX-2.0) + game.state.rembase = random.randint(BASEMIN, BASEMAX) game.inbase = game.state.rembase game.inplan = 0 if game.options & OPTION_PLANETS: @@ -6241,10 +6247,8 @@ def newqad(shutup): # set up a new state of quadrant, for when we enter or re-enter it w = coord() game.justin = True - invalidate(game.base) game.klhere = 0 game.comhere = False - invalidate(game.plnet) game.ishere = False game.irhere = 0 game.iplnet = 0 @@ -6261,10 +6265,6 @@ def newqad(shutup): # Attempt to escape Super-commander, so tbeam back! game.iscate = False game.ientesc = True - # Clear quadrant - for i in range(1, QUADSIZE+1): - for j in range(1, QUADSIZE+1): - game.quad[i][j] = IHDOT q = game.state.galaxy[game.quadrant.x][game.quadrant.y] # cope with supernova if q.supernova: @@ -6283,7 +6283,7 @@ def newqad(shutup): w = newkling(i) # If we need a commander, promote a Klingon for i in range(1, game.state.remcom+1): - if same(game.state.kcmdr[i], game.quadrant): + if game.state.kcmdr[i] == game.quadrant: break if i <= game.state.remcom: @@ -6432,7 +6432,7 @@ def setpassword(): else: game.passwd = "" for i in range(3): - game.passwd[i] += chr(97+int(random.random()*25)) + game.passwd += chr(97+int(random.random()*25)) # Code from sst.c begins here @@ -6773,37 +6773,37 @@ def randplace(size): return w def chew(): + # Demand input for next scan global inqueue - inqueue = [] + inqueue = None def chew2(): # return IHEOL next time global inqueue - inqueue = ["\n"] + inqueue = [] def scan(): # Get a token from the user - global inqueue + global inqueue, line, citem aaitem = 0.0 citem = '' # Read a line if nothing here - if line == '\n': - line = '' - return IHEOL - elif line == '': + if inqueue == None: line = cgetline() if curwnd==prompt_window: clrscr() setwnd(message_window) clrscr() - # Skip leading white space - line = line.lstrip() - # Nothing left - if not line: - return IHEOL - else: - inqueue += line.split() + # Skip leading white space + line = line.lstrip() + if line: + inqueue = line.split() + else: + inqueue = [] + return IHEOL + elif not inqueue: + return IHEOL # From here on in it's all looking at the queue citem = inqueue.pop(0) if citem == IHEOL: @@ -6811,7 +6811,7 @@ def scan(): try: aaitem = float(citem) return IHREAL - except ValuError: + except ValueError: pass # Treat as alpha citem = citem.lower() @@ -6836,8 +6836,8 @@ def huh(): prout(_("Beg your pardon, Captain?")) def isit(s): - # compares s to citem and returns true if it matches to the length of s - return citem.startswith(s) + # compares s to citem and returns true if it matches to the length of s + return s.startswith(citem) def debugme(): # access to the internals for debugging @@ -6929,20 +6929,24 @@ def debugme(): atover(True) if __name__ == '__main__': - line = "" + global line, thing, game + game = citem = aaitem = inqueue = None + line = '' thing = coord() game = gamestate() + idebug = 0 game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY) - if os.getenv("TERM"): - game.options |= OPTION_CURSES | OPTION_SHOWME - else: - game.options |= OPTION_TTY + # Disable curses mode until the game logic is working. + # if os.getenv("TERM"): + # game.options |= OPTION_CURSES | OPTION_SHOWME + # else: + game.options |= OPTION_TTY seed = time.time() (options, arguments) = getopt.getopt(sys.argv[1:], "r:tx") for (switch, val) in options: - if switch == 'r': + if switch == '-r': try: replayfp = open(optarg, "r") except IOError: @@ -6957,10 +6961,10 @@ if __name__ == '__main__': os.exit(1) game.options |= OPTION_TTY game.options &=~ OPTION_CURSES - elif switch == 't': + elif switch == '-t': game.options |= OPTION_TTY game.options &=~ OPTION_CURSES - elif switch == 'x': + elif switch == '-x': idebug = True else: sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n") @@ -6976,16 +6980,16 @@ if __name__ == '__main__': random.seed(seed) iostart() - - for i in range(optind, argc): - line += sys.argv[i] - line += " " + if arguments: + inqueue = arguments + else: + inqueue = None while True: # Play a game setwnd(fullscreen_window) clrscr() prelim() - setup(line[0] == '\0') + setup(needprompt=not line) if game.alldone: score() game.alldone = False