# pylint: disable=multiple-imports
import os, sys, math, curses, time, pickle, copy, gettext, getpass
-import getopt, socket, locale
+import getopt, socket
import codecs
# This import only works on Unixes. The intention is to enable
try:
# pylint: disable=unused-import
import readline
-except ImportError:
+except ImportError: # pragma: no cover
pass
version = "2.7"
# logfp.write("#seed(%d)\n" % n)
game.lcg_x = n % randomizer.LCG_M
+ @staticmethod
+ def getrngstate():
+ return game.lcg_x
+
+ @staticmethod
+ def setrngstate(n):
+ game.lcg_x = n
+
GALSIZE = 8 # Galaxy size in quadrants
NINHAB = (GALSIZE * GALSIZE // 2) # Number of inhabited worlds
MAXUNINHAB = 10 # Maximum uninhabited worlds
MAXBURST = 3 # Max # of torps you can launch in one turn
MINCMDR = 10 # Minimum number of Klingon commanders
DOCKFAC = 0.25 # Repair faster when docked
-PHASEFAC = 2.0 # Unclear what this is, it was in the C version
+PHASEFAC = 2.0 # Phaser attenuation factor
ALGERON = 2311 # Date of the Treaty of Algeron
class JumpOut(Exception):
pass
+def letterize(n):
+ return chr(ord('a') + n - 1)
+
class Coord:
def __init__(self, x=None, y=None):
self.i = x # Row
return Coord(self.i*other, self.j*other)
def __rmul__(self, other):
return Coord(self.i*other, self.j*other)
- def __div__(self, other):
+ def __div__(self, other): # pragma: no cover
return Coord(self.i/other, self.j/other)
- def __truediv__(self, other):
+ def __truediv__(self, other): # pragma: no cover
return Coord(self.i/other, self.j/other)
- def __floordiv__(self, other):
+ def __floordiv__(self, other): # pragma: no cover
return Coord(self.i//other, self.j//other)
def __mod__(self, other):
return Coord(self.i % other, self.j % other)
- def __rtruediv__(self, other):
+ def __rtruediv__(self, other): # pragma: no cover
return Coord(self.i/other, self.j/other)
- def __rfloordiv__(self, other):
+ def __rfloordiv__(self, other): # pragma: no cover
return Coord(self.i//other, self.j//other)
def roundtogrid(self):
return Coord(int(round(self.i)), int(round(self.j)))
return self.roundtogrid() // QUADSIZE
def sector(self):
return self.roundtogrid() % QUADSIZE
- def scatter(self):
- s = Coord()
- s.i = self.i + rnd.integer(-1, 2)
- s.j = self.j + rnd.integer(-1, 2)
- return s
def __str__(self):
if self.i is None or self.j is None:
- return "Nowhere"
+ return "Nowhere" # pragma: no cover
+ if (game.options & OPTION_ALPHAMERIC):
+ return letterize(self.i + 1) + str(self.j + 1)
return "%s - %s" % (self.i+1, self.j+1)
__repr__ = __str__
-class Thingy(Coord):
+class Thingy():
"Do not anger the Space Thingy!"
def __init__(self):
- Coord.__init__(self)
- self.angered = False
- def angry(self):
- self.angered = True
+ self.location = Coord()
def at(self, q):
- return (q.i, q.j) == (self.i, self.j)
+ return (q.i, q.j) == (self.location.i, self.location.j)
class Planet:
def __init__(self):
self.charted = False
self.status = "secure" # Could be "secure", "distressed", "enslaved"
def __str__(self):
- return "<Quadrant: %(klingons)d>" % self.__dict__
+ return "<Quadrant: %(klingons)d>" % self.__dict__ # pragma: no cover
__repr__ = __str__
class Page:
self.starbase = False
self.klingons = None
def __repr__(self):
- return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars)
+ return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars) # pragma: no cover
def fill2d(size, fillfun):
"Fill an empty list in 2D."
OPTION_TTY = 0x00000001 # old interface
OPTION_CURSES = 0x00000002 # new interface
OPTION_IOMODES = 0x00000003 # cover both interfaces
-OPTION_PLANETS = 0x00000004 # planets and mining
+OPTION_PLANETS = 0x00000004 # planets and mining (> 1974)
OPTION_THOLIAN = 0x00000008 # Tholians and their webs (UT 1979 version)
-OPTION_THINGY = 0x00000010 # Space Thingy can shoot back (Stas, 2005)
OPTION_PROBE = 0x00000020 # deep-space probes (DECUS version, 1980)
-OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart
-OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy)
-OPTION_MVBADDY = 0x00000100 # more enemies can move (Almy)
-OPTION_BLKHOLE = 0x00000200 # black hole may timewarp you (Stas, 2005)
-OPTION_BASE = 0x00000400 # bases have good shields (Stas, 2005)
-OPTION_WORLDS = 0x00000800 # logic for inhabited worlds (ESR, 2006)
-OPTION_AUTOSCAN = 0x00001000 # automatic LRSCAN before CHART (ESR, 2006)
-OPTION_CAPTURE = 0x00002000 # Enable BSD-Trek capture (Almy, 2013).
-OPTION_CLOAK = 0x80004000 # Enable BSD-Trek capture (Almy, 2013).
-OPTION_PLAIN = 0x01000000 # user chose plain game
-OPTION_ALMY = 0x02000000 # user chose Almy variant
+OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart (ESR, 2005)
+OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy, 1979)
+OPTION_MVBADDY = 0x00000100 # more enemies can move (Almy, 1979?)
+OPTION_AUTOPASS = 0x00000200 # Autogenerate password (Almy, 1997?)
+OPTION_BLKHOLE = 0x00000400 # black hole may timewarp you (Stas, 2005)
+OPTION_BASE = 0x00000800 # bases have good shields (Stas, 2005)
+OPTION_WORLDS = 0x00001000 # logic for inhabited worlds (ESR, 2006)
+OPTION_AUTOSCAN = 0x00002000 # automatic LRSCAN before CHART (ESR, 2006)
+OPTION_CAPTURE = 0x00004000 # Enable BSD-Trek capture (Almy, 2013).
+OPTION_CLOAK = 0x80008000 # Enable BSD-Trek capture (Almy, 2013).
+OPTION_ALMY = 0x01000000 # Almy's death ray upgrade (1997?)
OPTION_COLOR = 0x04000000 # enable color display (ESR, 2010)
OPTION_DOTFILL = 0x08000000 # fix dotfill glitch in chart (ESR, 2019)
+OPTION_ALPHAMERIC = 0x10000000 # Alpha Y coordinates (ESR, 2023)
option_names = {
"ALL": OPTION_ALL,
"TTY": OPTION_TTY,
"IOMODES": OPTION_IOMODES,
"PLANETS": OPTION_PLANETS,
- "THOLIAN": OPTION_THOLIAN,
- "THINGY": OPTION_THINGY,
"PROBE": OPTION_PROBE,
"SHOWME": OPTION_SHOWME,
"RAMMING": OPTION_RAMMING,
"MVBADDY": OPTION_MVBADDY,
+ "AUTOPASS": OPTION_AUTOPASS,
"BLKHOLE": OPTION_BLKHOLE,
"BASE": OPTION_BASE,
"WORLDS": OPTION_WORLDS,
"AUTOSCAN": OPTION_AUTOSCAN,
"CAPTURE": OPTION_CAPTURE,
"CLOAK": OPTION_CLOAK,
- "PLAIN": OPTION_PLAIN,
"ALMY": OPTION_ALMY,
"COLOR": OPTION_COLOR,
"DOTFILL": OPTION_DOTFILL,
- }
+ "ALPHAMERIC": OPTION_ALPHAMERIC,
+}
+
+option_years = {
+ OPTION_PLANETS: 1979,
+ OPTION_PROBE: 1980,
+ OPTION_SHOWME: 2005,
+ OPTION_RAMMING: 1979,
+ OPTION_MVBADDY: 1979,
+ OPTION_AUTOPASS: 1997,
+ OPTION_BLKHOLE: 2005,
+ OPTION_BASE: 2005,
+ OPTION_WORLDS: 2996,
+ OPTION_AUTOSCAN: 2006,
+ OPTION_CAPTURE: 2013,
+ OPTION_CLOAK: 2013,
+ OPTION_ALMY: 1997,
+ OPTION_COLOR: 2010,
+ OPTION_DOTFILL: 2019,
+ OPTION_ALPHAMERIC: 2023,
+}
# Define devices
DSRSENS = 0
# Abstract out the event handling -- underlying data structures will change
# when we implement stateful events
-def findevent(evtype):
+def findevent(evtype): # pragma: no cover
return game.future[evtype]
class Enemy:
game.enemies.remove(self)
return motion
def __repr__(self):
- return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging
+ return "<%s,%s,%f>" % (self.type, self.location, self.power) # pragma: no cover
class Gamestate:
def __init__(self):
if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off !
motion -= game.skill*(2.0-rnd.real()**2)
if game.idebug:
- proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
+ proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces)) # pragma: no cover
# don't move if no motion
if motion == 0:
return []
nsteps = min(nsteps, QUADSIZE) # This shouldn't be necessary
nsteps = max(nsteps, 1) # This shouldn't be necessary
if game.idebug:
- proutn("NSTEPS = %d:" % nsteps)
+ proutn("NSTEPS = %d:" % nsteps) # pragma: no cover
# Compute preferred values of delta X and Y
m = game.sector - enemy.location
if 2.0 * abs(m.i) < abs(m.j):
# main move loop
for ll in range(nsteps):
if game.idebug:
- proutn(" %d" % (ll+1))
+ proutn(" %d" % (ll+1)) # pragma: no cover
# Check if preferred position available
look = goto + m
if m.i < 0:
if success:
goto = look
if game.idebug:
- proutn(repr(goto))
+ proutn(repr(goto)) # pragma: no cover
else:
break # done early
if game.idebug:
- skip(1)
+ skip(1) # pragma: no cover
# Enemy moved, but is still in sector
return [(False, enemy, old_dist, goto)]
def moveklings():
"Sequence Klingon tactical movement."
if game.idebug:
- prout("== MOVCOM")
+ prout("== MOVCOM") # pragma: no cover
# Figure out which Klingon is the commander (or Supercommander)
# and do move
tacmoves = []
idelta = Coord()
basetbl = []
if game.idebug:
- prout("== SUPERCOMMANDER")
+ prout("== SUPERCOMMANDER") # pragma: no cover
# Decide on being active or passive
avoid = ((game.incom - len(game.state.kcmdr) + game.inkling - game.remkl())/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
(game.state.date-game.indate) < 3.0)
elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
tid.i = 0
tid.j = 0
- else:
+ else: # pragma: no cover
# something is wrong!
game.tholian.move(None)
prout("***Internal error: Tholian in a bad spot.")
wsum += w
if idx < wsum:
return i
- return None # we should never get here
+ return None # pragma: no cover
def collision(rammed, enemy):
- "Collision handling for rammong events."
+ "Collision handling for ramming events."
prouts(_("***RED ALERT! RED ALERT!"))
skip(1)
prout(_("***COLLISION IMMINENT."))
proutn(_(" (original position)"))
skip(1)
deadkl(enemy.location, enemy.type, game.sector)
- proutn("***" + crmshp() + " heavily damaged.")
+ prout("***" + crmshp() + " heavily damaged.")
icas = rnd.integer(10, 30)
prout(_("***Sickbay reports %d casualties") % icas)
game.casual += icas
game.state.crew -= icas
- # In the pre-SST2K version, all devices got equiprobably damaged,
+ # In the pre-SST2K versions, all devices got equiprobably damaged,
# which was silly. Instead, pick up to half the devices at
# random according to our weighting table,
ncrits = rnd.integer(NDEVICES//2)
# Loop to move a single torpedo
setwnd(message_window)
for step in range(1, QUADSIZE*2):
- if not track.nexttok():
+ if not track.nextstep():
break
w = track.sector()
if not w.valid_sector():
# 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+rnd.real(-2.4, 2.4), distance=2**0.5)
- displacement.nexttok()
+ displacement.nextstep()
bumpto = displacement.sector()
if not bumpto.valid_sector():
return hit
return None
proutn(crmena(True, iquad, "sector", w))
displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5, origin=w)
- displacement.nexttok()
+ displacement.nextstep()
bumpto = displacement.sector()
if game.quad[bumpto.i][bumpto.j] == ' ':
prout(_(" buffeted into black hole."))
tenemy.kdist = tenemy.kavgd = (game.sector-tenemy.location).distance()
sortenemies()
break
- else:
+ else: # pragma: no cover
prout("Internal error, no enemy where expected!")
raise SystemExit(1)
return None
prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
return None
elif iquad == '?': # Hit a thingy
- if not (game.options & OPTION_THINGY) or rnd.withprob(0.3):
- skip(1)
- prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
- skip(1)
- prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
- skip(1)
- proutn(_("Mr. Spock-"))
- prouts(_(" \"Fascinating!\""))
- skip(1)
- deadkl(w, iquad, w)
- else:
- # Stas Sergeev added the possibility that
- # you can shove the Thingy and piss it off.
- # It then becomes an enemy and may fire at you.
- thing.angry()
+ skip(1)
+ prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
+ skip(1)
+ prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
+ skip(1)
+ proutn(_("Mr. Spock-"))
+ prouts(_(" \"Fascinating!\""))
+ skip(1)
+ deadkl(w, iquad, w)
return None
elif iquad == ' ': # Black hole
skip(1)
chgfac = 1.0
where = "neither"
if game.idebug:
- prout("=== ATTACK!")
+ prout("=== ATTACK!") # pragma: no cover
# Tholian gets to move before attacking
if game.tholian:
movetholian()
prout("Sector %s." % goto)
sortenemies()
# if no enemies remain after movement, we're done
- if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
+ if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant)):
return
# set up partial hits if attack happens during shield status change
pfac = 1.0/game.inshld
r *= 0.25
if enemy.power < 500:
r *= 0.25
- if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
+ if enemy.type in ('T', '?'):
continue
# different enemies have different probabilities of throwing a torp
usephasers = not torps_ok or \
else:
proutn(_("Very small hit on "))
ienm = game.quad[w.i][w.j]
- if ienm == '?':
- thing.angry()
proutn(crmena(False, ienm, "sector", w))
skip(1)
if kpow == 0:
game.isatb = 0
else:
game.battle.invalidate()
- if game.idebug:
+ if game.idebug: # pragma: no cover
prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
for i in range(1, NEVENTS):
if i == FSNOVA: proutn("=== Supernova ")
if game.future[l].date < datemin:
evcode = l
if game.idebug:
- prout("== Event %d fires" % evcode)
+ prout("== Event %d fires" % evcode) # pragma: no cover
datemin = game.future[l].date
xtime = datemin-game.state.date
if game.iscloaked:
supercommander()
elif evcode == FDSPROB: # Move deep space probe
schedule(FDSPROB, 0.01)
- if not game.probe.nexttok():
+ if not game.probe.nextstep():
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
else:
# can't seem to find one; ignore this call
if game.idebug:
- prout("=== Couldn't find location for distress event.")
+ prout("=== Couldn't find location for distress event.") # pragma: no cover
continue
# got one!! Schedule its enslavement
ev = schedule(FENSLV, expran(game.intime))
break
if num <=0:
break
- if game.idebug:
+ if game.idebug: # pragma: no cover
proutn("=== Super nova here?")
if ja():
nq = game.quadrant
prout(_("Your starship is now an expanding cloud of subatomic particles"))
elif ifin == FMATERIALIZE:
prout(_("Starbase was unable to re-materialize your starship."))
- prout(_("Sic transit gloria mundi"))
+ prout(_("Sic transit gloria mundi."))
elif ifin == FPHASER:
prout(_("The %s has been cremated by its own phasers.") % crmshp())
elif ifin == FLOST:
prout(_("You and your landing party have been"))
prout(_("converted to energy, dissipating through space."))
elif ifin == FMINING:
+ # This does not seem to be reachable from any code path.
prout(_("You are left with your landing party on"))
prout(_("a wild jungle planet inhabited by primitive cannibals."))
skip(1)
prout(_("That was a great shot."))
skip(1)
elif ifin == FSSC:
+ # This does not seem to be reachable from any code path.
prout(_("The Galileo is instantly annihilated by the supernova."))
prout(_("You and your mining party are atomized."))
skip(1)
def iostart():
global stdscr, rows
- # for some recent versions of python2, the following enables UTF8
- # for the older ones we probably need to set C locale, and python3
- # has no problems at all
- if sys.version_info[0] < 3:
- locale.setlocale(locale.LC_ALL, "")
gettext.bindtextdomain("sst", "/usr/local/share/locale")
gettext.textdomain("sst")
if not (game.options & OPTION_CURSES):
rows = ln_env
else:
rows = 25
- else:
+ else: # pragma: no cover
stdscr = curses.initscr()
stdscr.keypad(True)
curses.nonl()
def ioend():
"Wrap up I/O."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
stdscr.keypad(False)
curses.echo()
curses.nocbreak()
def waitfor():
"Wait for user action -- OK to do nothing if on a TTY"
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
stdscr.getch()
def announce():
else:
prompt = _("[PRESS ENTER TO CONTINUE]")
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
drawmaps(0)
setwnd(prompt_window)
prompt_window.clear()
def skip(i):
"Skip i lines. Pause game if this would cause a scrolling event."
for _dummy in range(i):
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
(y, _x) = curwnd.getyx()
try:
curwnd.move(y+1, 0)
def proutn(proutntline):
"Utter a line with no following line feed."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
(y, x) = curwnd.getyx()
(my, _mx) = curwnd.getmaxyx()
if curwnd == message_window and y >= my - 2:
if not replayfp or replayfp.closed: # Don't slow down replays
time.sleep(0.03)
proutn(c)
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
curwnd.refresh()
else:
sys.stdout.flush()
def cgetline():
"Get a line of input."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
linein = codecs.decode(curwnd.getstr()) + "\n"
curwnd.refresh()
else:
while True:
linein = replayfp.readline()
proutn(linein)
- if linein == '':
+ if linein == '': # pragma: no cover
prout("*** Replay finished")
replayfp.close()
break
def setwnd(wnd):
"Change windows -- OK for this to be a no-op in tty mode."
global curwnd
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
if game.cdebug and logfp:
if wnd == fullscreen_window:
legend = "fullscreen"
def clreol():
"Clear to end of line -- can be a no-op in tty mode"
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
curwnd.clrtoeol()
curwnd.refresh()
def clrscr():
"Clear screen -- can be a no-op in tty mode."
global linecount
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
curwnd.clear()
curwnd.move(0, 0)
curwnd.refresh()
linecount = 0
def textcolor(color=DEFAULT):
- if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES):
+ if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): # pragma: no cover
if color == DEFAULT:
curwnd.attrset(0)
elif color == BLACK:
curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
def highvideo():
- if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES):
+ if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): # pragma: no cover
curwnd.attron(curses.A_REVERSE)
#
def drawmaps(mode):
"Hook to be called after moving to redraw maps."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
if mode == 1:
sensor()
setwnd(srscan_window)
lrscan_window.move(0, 0)
lrscan(silent=False)
-def put_srscan_sym(w, sym):
+def put_srscan_sym(w, sym): # pragma: no cover
"Emit symbol for short-range scan."
srscan_window.move(w.i+1, w.j*2+2)
srscan_window.addch(sym)
def boom(w):
"Enemy fall down, go boom."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
drawmaps(0)
setwnd(srscan_window)
srscan_window.attron(curses.A_REVERSE)
def warble():
"Sound and visual effects for teleportation."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
drawmaps(2)
setwnd(message_window)
#sound(50)
prouts(" . . . . . ")
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
#curses.delay_output(1000)
#nosound()
pass
elif step in {4, 9}:
skip(1)
proutn("%s " % w)
- else:
+ else: # pragma: no cover
if not damaged(DSRSENS) or game.condition=="docked":
if i != 0 and step == 1:
drawmaps(2)
def makechart():
"Display the current galaxy chart."
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
setwnd(message_window)
message_window.clear()
chart()
def prstat(txt, data):
proutn(txt)
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
skip(1)
setwnd(status_window)
else:
proutn(" " * (NSYM - len(txt)))
proutn(data)
skip(1)
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
setwnd(report_window)
# Code from moving.c begins here
stopegy = 50.0*icourse.distance/game.optime
if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
for enemy in game.enemies:
- if enemy.location == game.sector:
+ if enemy.location == h:
collision(rammed=False, enemy=enemy)
return True
# This should not happen
- prout(_("Which way did he go?"))
- return False
+ prout(_("Which way did he go?")) # pragma: no cover
+ return False # pragma: no cover
elif iquad == ' ':
skip(1)
prouts(_("***RED ALERT! RED ALERT!"))
# Move out
game.quad[game.sector.i][game.sector.j] = '.'
for _m in range(icourse.moves):
- icourse.nexttok()
+ icourse.nextstep()
w = icourse.sector()
if icourse.origin.quadrant() != icourse.location.quadrant():
newquadrant(noattack)
self.step = 0
def arrived(self):
return self.location.roundtogrid() == self.final
- def nexttok(self):
+ def nextstep(self):
"Next step on course."
self.step += 1
self.nextlocation = self.location + self.increment
return
def warp(wcourse, involuntary):
- "ove under warp drive."
+ "Move under warp drive."
blooey = False; twarp = False
if not involuntary: # Not WARPX entry
game.ididit = False
# Decide if time warp will occur
if 0.5*wcourse.distance*math.pow(7.0,game.warpfac-10.0) > rnd.real():
twarp = True
- if game.idebug and game.warpfac==10 and not twarp:
+ if game.idebug and game.warpfac==10 and not twarp: # pragma: no cover
blooey = False
proutn("=== Force time warp? ")
if ja():
look = wcourse.moves
while look > 0:
look -= 1
- wcourse.nexttok()
+ wcourse.nextstep()
w = wcourse.sector()
if not w.valid_sector():
break
break
prout(_("fails."))
textcolor(DEFAULT)
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
curses.delay_output(500)
if m > 3:
game.quad[game.sector.i][game.sector.j]='?'
if not ja():
scanner.chew()
return
- if not (game.options & OPTION_PLAIN):
+ if (game.options & OPTION_ALMY):
nrgneed = 50 * game.skill + game.height / 100.0
if nrgneed > game.energy:
prout(_("Engineering to bridge--"))
prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
skip(1)
dprob = 0.30
- if game.options & OPTION_PLAIN:
+ # Ugh. This test (For Tom Almy's death-ray upgrade) was inverted for a long time.
+ # Furthermore, somebody (ESR or Stas?) changed Tom Almy's 0.7 upgraded chance of
+ # working to 0.5.
+ if game.options & OPTION_ALMY:
dprob = 0.5
r = rnd.real()
if r > dprob:
prout(_("Ensign Chekov- \"Congratulations, Captain!\""))
if game.unwon() == 0:
finish(FWON)
- if (game.options & OPTION_PLAIN) == 0:
+ if (game.options & OPTION_ALMY):
prout(_("Spock- \"Captain, I believe the `Experimental Death Ray'"))
if rnd.withprob(0.05):
prout(_(" is still operational.\""))
for i in range(NDEVICES):
if damaged(i):
if not jdam:
- prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
- prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
+ prout(_("DEVICE REPAIR TIMES"))
+ prout(_(" IN FLIGHT DOCKED"))
jdam = True
prout(" %-26s\t%8.2f\t\t%8.2f" % (device[i],
game.damage[i]+0.05,
prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
prout(" 1 2 3 4 5 6 7 8")
for i in range(GALSIZE):
- proutn("%d |" % (i+1))
+ if (game.options & OPTION_ALPHAMERIC):
+ proutn("%c |" % letterize(i+1))
+ else:
+ proutn("%d |" % (i+1))
for j in range(GALSIZE):
if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
proutn("<")
if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
if game.quad[i][j] in ('E', 'F'):
if game.iscloaked:
- highvideo()
+ highvideo() # pragma: no cover
textcolor({"green":GREEN,
"yellow":YELLOW,
"red":RED,
if game.condition != "docked":
newcnd()
for i in range(QUADSIZE):
- proutn("%2d " % (i+1))
+ if (game.options & OPTION_ALPHAMERIC):
+ proutn("%c " % letterize(i+1))
+ else:
+ proutn("%2d " % (i+1))
for j in range(QUADSIZE):
sectscan(goodScan, i, j)
skip(1)
else:
prout(_("No such option as ") + scanner.token)
if mode == "set":
- if (not (game.options & OPTION_CURSES)) and (changemask & OPTION_CURSES):
+ if (not (game.options & OPTION_CURSES)) and (changemask & OPTION_CURSES): # pragma: no cover
iostart()
game.options |= changemask
elif mode == "clear":
- if (game.options & OPTION_CURSES) and (not (changemask & OPTION_CURSES)):
+ if (game.options & OPTION_CURSES) and (not (changemask & OPTION_CURSES)): # pragma: no cover
ioend()
game.options &=~ changemask
prout(_("Acknowledged, Captain."))
game.state.galaxy[i][j].stars = k
# Locate star bases in galaxy
if game.idebug:
- prout("=== Allocating %d bases" % game.inbase)
+ prout("=== Allocating %d bases" % game.inbase) # pragma: no cover
for i in range(game.inbase):
while True:
while True:
if distq < 6.0*(BASEMAX+1-game.inbase) and rnd.withprob(0.75):
contflag = True
if game.idebug:
- prout("=== Abandoning base #%d at %s" % (i, w))
+ prout("=== Abandoning base #%d at %s" % (i, w)) # pragma: no cover
break
elif distq < 6.0 * (BASEMAX+1-game.inbase):
if game.idebug:
- prout("=== Saving base #%d, close to #%d" % (i, j))
+ prout("=== Saving base #%d, close to #%d" % (i, j)) # pragma: no cover
if not contflag:
break
if game.idebug:
- prout("=== Placing base #%d in quadrant %s" % (i, w))
+ prout("=== Placing base #%d in quadrant %s" % (i, w)) # pragma: no cover
game.state.baseq.append(w)
game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
# Position ordinary Klingon Battle Cruisers
# Place thing (in tournament game, we don't want one!)
# New in SST2K: never place the Thing near a starbase.
# This makes sense and avoids a special case in the old code.
- global thing
if game.tourn is None:
+ # Avoid distrubing the RNG chain. This code
+ # was a late fix and we don't want to mess up
+ # all the regression tests.
+ state = randomizer.getrngstate()
while True:
- thing = randplace(GALSIZE)
- if thing not in game.state.baseq:
+ thing.location = randplace(GALSIZE)
+ # Put it somewhere a starbase is not
+ if thing.location not in game.state.baseq:
break
+ randomizer.setrngstate(state)
skip(2)
game.state.snap = False
if game.skill == SKILL_NOVICE:
clrscr()
setwnd(message_window)
newqad()
- if len(game.enemies) - (thing == game.quadrant) - (game.tholian is not None):
+ if len(game.enemies) - (thing.location == game.quadrant) - (game.tholian is not None):
game.shldup = True
if game.neutz: # bad luck to start in a Romulan Neutral Zone
attack(torps_ok=False)
def choose():
"Choose your game type."
- while True:
- game.tourn = game.length = 0
- game.thawed = False
- game.skill = SKILL_NONE
- # Do not chew here, we want to use command-line tokens
- if not scanner.inqueue: # Can start with command line options
- proutn(_("Would you like a regular, tournament, or saved game? "))
+ game.tourn = None
+ game.length = 0
+ game.thawed = False
+ game.skill = SKILL_NONE
+ gametype = None
+ wayback = 0
+ while gametype is None or game.length == 0 or game.skill == SKILL_NONE or wayback == 0:
+ eol_is_fancy = False
+ if not scanner.inqueue or scanner.token == "IHEOL": # Can start with command line options
+ if gametype is None:
+ proutn(_("Would you like a regular, tournament, or saved game? "))
+ elif game.length==0:
+ proutn(_("Would you like a Short, Medium, or Long game? "))
+ elif game.skill == SKILL_NONE:
+ proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
+ elif wayback == 0:
+ proutn(_("Wayback setting (press enter for current year): "))
+ eol_is_fancy = True
scanner.nexttok()
- if scanner.sees("tournament"):
+ if game.idebug:
+ prout("-- Token: %s=%s" % (scanner.type, repr(scanner.token)))
+ if scanner.token == "":
+ raise SystemExit(0) # Early end of replay
+ if scanner.token.startswith("r"): # regular
+ gametype = "regular"
+ elif scanner.token.startswith("t"):
+ gametype = "tournament"
+ proutn(_("Type in tournament number-"))
+ game.tourn = 0
while scanner.nexttok() == "IHEOL":
- proutn(_("Type in tournament number-"))
- if scanner.real == 0:
- scanner.chew()
- continue # We don't want a blank entry
+ if scanner.real == 0:
+ scanner.chew()
+ continue # We don't want a blank entry
game.tourn = int(round(scanner.real))
rnd.seed(scanner.real)
if logfp:
logfp.write("# rnd.seed(%d)\n" % scanner.real)
- break
- if scanner.sees("saved") or scanner.sees("frozen"):
+ elif scanner.token.startswith("sa") or scanner.token.startswith("fr"): # saved or frozen
+ gametype = "saved"
if thaw():
continue
scanner.chew()
report()
waitfor()
return True
- if scanner.sees("regular"):
- break
- proutn(_("What is \"%s\"? ") % scanner.token)
- scanner.chew()
- while game.length==0 or game.skill==SKILL_NONE:
- if scanner.nexttok() == "IHALPHA":
- if scanner.sees("short"):
- game.length = 1
- elif scanner.sees("medium"):
- game.length = 2
- elif scanner.sees("long"):
- game.length = 4
- elif scanner.sees("novice"):
- game.skill = SKILL_NOVICE
- elif scanner.sees("fair"):
- game.skill = SKILL_FAIR
- elif scanner.sees("good"):
- game.skill = SKILL_GOOD
- elif scanner.sees("expert"):
- game.skill = SKILL_EXPERT
- elif scanner.sees("emeritus"):
- game.skill = SKILL_EMERITUS
- else:
- proutn(_("What is \""))
- proutn(scanner.token)
- prout("\"?")
+ elif scanner.token.startswith("s"): # short
+ game.length = 1
+ elif scanner.token.startswith("m"): # medium
+ game.length = 2
+ elif scanner.token.startswith("l"): # long
+ game.length = 4
+ elif scanner.token.startswith("n"): # novice
+ game.skill = SKILL_NOVICE
+ elif (game.skill is None) and scanner.token.startswith("f"): # fair
+ game.skill = SKILL_FAIR
+ elif scanner.token.startswith("g"): # good
+ game.skill = SKILL_GOOD
+ elif scanner.token.startswith("e"): # expert
+ game.skill = SKILL_EXPERT
+ elif scanner.token.startswith("em"): # emeritus
+ game.skill = SKILL_EMERITUS
+ elif scanner.type == "IHREAL":
+ wayback = scanner.int()
+ if wayback <= 1973:
+ # Approximates the UT FORTRAN version.
+ game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE | OPTION_CLOAK | OPTION_ALMY | OPTION_AUTOPASS | OPTION_DOTFILL | OPTION_ALPHAMERIC)
+ elif scanner.int() < 1980:
+ # Approximates Tom Almy's 1979 version.
+ game.options &=~ (OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_DOTFILL | OPTION_ALPHAMERIC)
+ elif (eol_is_fancy and scanner.token.startswith("\n")):
+ wayback = time.localtime().tm_year
+ elif scanner.token.startswith("\n"):
+ continue
+ elif scanner.token.startswith("idebug"):
+ game.idebug = True
else:
- scanner.chew()
- if game.length==0:
- proutn(_("Would you like a Short, Medium, or Long game? "))
- elif game.skill == SKILL_NONE:
- proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
- # Choose game options -- added by ESR for SST2K
- if scanner.nexttok() != "IHALPHA":
- scanner.chew()
- proutn(_("Choose your game style (plain, almy, fancy or just press enter): "))
- scanner.nexttok()
- if scanner.sees("plain"):
- # Approximates the UT FORTRAN version.
- game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE | OPTION_CLOAK | OPTION_DOTFILL)
- game.options |= OPTION_PLAIN
- elif scanner.sees("almy"):
- # Approximates Tom Almy's version.
- game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_DOTFILL)
- game.options |= OPTION_ALMY
- elif scanner.sees("fancy") or scanner.sees("\n"):
- pass
- elif len(scanner.token):
- proutn(_("What is \"%s\"?") % scanner.token)
+ # Unrecognized token
+ prout(_("Can't interpret %s") % repr(scanner.token))
setpassword()
- if game.passwd == "debug":
+ if game.passwd == "debug": # pragma: no cover
game.idebug = True
prout("=== Debug mode enabled.")
+ if game.idebug:
+ prout("--- Setup: type=%s length=%s skill=%s wayback=%s" % (gametype, game.length, game.skill, wayback))
# Use parameters to generate initial values of things
game.damfac = 0.5 * game.skill
game.inbase = rnd.integer(BASEMIN, BASEMAX+1)
prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
# Put in THING if needed
- if thing == game.quadrant:
+ if thing.location == game.quadrant:
Enemy(etype='?', loc=dropin(),
power=rnd.real(6000,6500.0)+250.0*game.skill)
if not damaged(DSRSENS):
def setpassword():
"Set the self-destruct password."
- if game.options & OPTION_PLAIN:
+ if game.options & OPTION_AUTOPASS:
+ game.passwd = ""
+ game.passwd += chr(ord('a')+rnd.integer(26))
+ game.passwd += chr(ord('a')+rnd.integer(26))
+ game.passwd += chr(ord('a')+rnd.integer(26))
+ else:
while True:
scanner.chew()
proutn(_("Please type in a secret password- "))
scanner.nexttok()
game.passwd = scanner.token
+ #game.passwd = getpass.getpass("Please type in a secret password- ")
if game.passwd is not None:
break
- else:
- game.passwd = ""
- game.passwd += chr(ord('a')+rnd.integer(26))
- game.passwd += chr(ord('a')+rnd.integer(26))
- game.passwd += chr(ord('a')+rnd.integer(26))
# Code from sst.c begins here
("", 0),
]
-def listCommands():
+def listCommands(): # pragma: no cover
"Generate a list of legal commands."
+ # Coverage-disabled because testing for this is fragile
+ # in the presence of changes in the command set.
prout(_("LEGAL COMMANDS ARE:"))
emitted = 0
for (key, opt) in commands:
clrscr()
proutn("COMMAND> ")
if scanner.nexttok() == "IHEOL":
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
makechart()
continue
elif scanner.token == "":
if cmd == scanner.token.upper() or (not abandon_passed \
and cmd.startswith(scanner.token.upper())):
break
- if cmd == "":
+ if cmd == "": # pragma: no cover
listCommands()
continue
elif opt and not (opt & game.options):
huh()
else:
break
- if game.options & OPTION_CURSES:
+ if game.options & OPTION_CURSES: # pragma: no cover
prout("COMMAND> %s" % cmd)
if cmd == "SRSCAN": # srscan
srscan()
helpme() # get help
elif cmd == "SCORE":
score() # see current score
- elif cmd == "CURSES":
+ elif cmd == "CURSES": # pragma: no cover
game.options |= (OPTION_CURSES | OPTION_COLOR)
iostart()
elif cmd == "OPTIONS":
if game.alldone:
break
if game.idebug:
- prout("=== Ending")
+ prout("=== Ending") # pragma: no cover
def cramen(ch):
"Emit the name of an enemy or feature."
elif ch == '#': s = _("Tholian web")
elif ch == '?': s = _("Stranger")
elif ch == '@': s = _("Inhabited World")
- else: s = "Unknown??"
+ else: s = "Unknown??" # pragma: no cover
return s
def crmena(loud, enemy, loctype, w):
def getcoord(self):
s = Coord()
self.nexttok()
+ if (game.options & OPTION_ALPHAMERIC):
+ try:
+ if (self.type == "IHALPHA") and (self.token[0] in "abcdefghij") and (self.token[1] in "0123456789"):
+ s.i = ord(self.token[0]) - ord("a")
+ s.j = int(self.token[1:])-1
+ return s
+ except (TypeError, IndexError):
+ huh()
+ return None
if self.type != "IHREAL":
huh()
return None
return None
s.j = self.int()-1
return s
- def __repr__(self):
+ def __repr__(self): # pragma: no cover
return "<sstcanner: token=%s, type=%s, queue=%s>" % (self.token, self.type, self.inqueue)
def ja():
skip(1)
prout(_("Beg your pardon, Captain?"))
-def debugme():
+def debugme(): # pragma: no cover
"Access to the internals for debugging."
proutn("Reset levels? ")
if ja():
proutn("Toggle debug flag? ")
if ja():
game.idebug = not game.idebug
- if game.idebug:
+ if game.idebug: # pragma: no cover
prout("Debug output ON")
else:
prout("Debug output OFF")
game = Gamestate()
rnd = randomizer()
logfp = None
- game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
+ game.options = OPTION_ALL &~ OPTION_IOMODES
if os.getenv("TERM"):
- game.options |= OPTION_CURSES
+ game.options |= OPTION_CURSES # pragma: no cover
else:
game.options |= OPTION_TTY
seed = int(time.time())
- (options, arguments) = getopt.getopt(sys.argv[1:], "cr:s:txV")
- for (switch, val) in options:
- if switch == '-r':
- # pylint: disable=raise-missing-from
- try:
- replayfp = open(val, "r")
- except IOError:
- sys.stderr.write("sst: can't open replay file %s\n" % val)
- raise SystemExit(1)
- # pylint: disable=raise-missing-from
- try:
- line = replayfp.readline().strip()
- (leader, __, seed) = line.split()
- # pylint: disable=eval-used
- seed = eval(seed)
- line = replayfp.readline().strip()
- arguments += line.split()[2:]
- except ValueError:
- sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
+ try:
+ (options, arguments) = getopt.getopt(sys.argv[1:], "cr:s:txV")
+ for (switch, val) in options:
+ if switch == '-r':
+ # pylint: disable=raise-missing-from
+ try:
+ replayfp = open(val, "r")
+ except IOError:
+ sys.stderr.write("sst: can't open replay file %s\n" % val)
+ raise SystemExit(1)
+ # pylint: disable=raise-missing-from
+ try:
+ while True:
+ line = replayfp.readline().strip()
+ print(line)
+ if line == "#":
+ break
+ if line.startswith("# seed"):
+ (__, __, seed) = line.split()
+ # pylint: disable=eval-used
+ seed = eval(seed)
+ elif line.startswith("# arguments"):
+ arguments += line.split()[2:]
+ except ValueError: # pragma: no cover
+ sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
+ raise SystemExit(1)
+ game.options |= OPTION_TTY
+ game.options &=~ OPTION_CURSES
+ elif switch == '-s': # pragma: no cover
+ seed = int(val)
+ elif switch == '-t': # pragma: no cover
+ game.options |= OPTION_TTY
+ game.options &=~ OPTION_CURSES
+ elif switch == '-x': # pragma: no cover
+ game.idebug = True
+ elif switch == '-c': # Enable curses debugging - undocumented
+ game.cdebug = True
+ elif switch == '-V': # pragma: no cover
+ print("SST2K", version)
+ raise SystemExit(0)
+ else: # pragma: no cover
+ sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
raise SystemExit(1)
- game.options |= OPTION_TTY
- game.options &=~ OPTION_CURSES
- elif switch == '-s':
- seed = int(val)
- elif switch == '-t':
- game.options |= OPTION_TTY
- game.options &=~ OPTION_CURSES
- elif switch == '-x':
- game.idebug = True
- elif switch == '-c': # Enable curses debugging - undocumented
- game.cdebug = True
- elif switch == '-V':
- print("SST2K", version)
- raise SystemExit(0)
- else:
- sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
- raise SystemExit(1)
+ except getopt.GetoptError as err:
+ print(err)
+ raise SystemExit(1) from err
# where to save the input in case of bugs
- if "TMPDIR" in os.environ:
+ if "TMPDIR" in os.environ: # pragma: no cover
tmpdir = os.environ['TMPDIR']
else:
tmpdir = "/tmp"
try:
logfp = open(os.path.join(tmpdir, "sst-input.log"), "w")
- except IOError:
+ except IOError: # pragma: no cover
sys.stderr.write("sst: warning, can't open logfile\n")
sys.exit(1)
if logfp:
logfp.write("# SST2K version %s\n" % version)
logfp.write("# recorded by %s@%s on %s\n" % \
(getpass.getuser(),socket.getfqdn(),time.ctime()))
+ logfp.write("#\n")
rnd.seed(seed)
scanner = sstscanner()
for arg in arguments:
finally:
ioend()
raise SystemExit(0)
- except KeyboardInterrupt:
+ except KeyboardInterrupt: # pragma: no cover
if logfp:
logfp.close()
print("")