-#!/usr/bin/env python2
+#!/usr/bin/env python3
"""
sst.py -- Super Star Trek 2K
on how to modify (and how not to modify!) this code.
"""
from __future__ import print_function, division
+# Runs under Python 2 an Python 3. Preserve this property!
+# SPDX-License-Identifier: BSD-2-clause
+# pylint: disable=line-too-long,superfluous-parens,too-many-lines,invalid-name,missing-function-docstring,missing-class-docstring,multiple-statements,too-many-branches,too-many-statements,too-many-locals,too-many-nested-blocks,too-many-return-statements,too-many-instance-attributes,global-statement,no-else-break,no-else-return,no-else-continue,too-few-public-methods,too-many-boolean-expressions,consider-using-f-string,consider-using-enumerate,consider-using-with,unspecified-encoding
+
+# pylint: disable=multiple-imports
import os, sys, math, curses, time, pickle, copy, gettext, getpass
import getopt, socket, locale
+import codecs
# This import only works on Unixes. The intention is to enable
# Ctrl-P, Ctrl-N, and friends in Cmd.
try:
+ # pylint: disable=unused-import
import readline
except ImportError:
pass
except NameError:
my_input = input
-version = "2.4"
+version = "2.7"
docpath = (".", "doc/", "/usr/share/doc/sst/")
class randomizer:
# LCG PRNG parameters tested against
- # Knuth vol. 2. by the authors of ADVENT
+ # Knuth vol. 2. by the authors of ADVENT
LCG_A = 1093
LCG_C = 221587
LCG_M = 1048576
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;
+ return old_x / randomizer.LCG_M
@staticmethod
def withprob(p):
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
+ return (self.i is not None) and (self.j is not None) and (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
+ return (self.i is not None) and (self.j is not None) and (self.i >= 0) and (self.i < QUADSIZE) and (self.j >= 0) and (self.j < QUADSIZE)
def invalidate(self):
self.i = self.j = None
def __eq__(self, other):
return self.roundtogrid() % QUADSIZE
def scatter(self):
s = Coord()
- s.i = self.i + rnd.range(-1, 2)
- s.j = self.j + rnd.range(-1, 2)
+ 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:
OPTION_PLAIN = 0x01000000 # user chose plain game
OPTION_ALMY = 0x02000000 # user chose Almy variant
OPTION_COLOR = 0x04000000 # enable color display (ESR, 2010)
+OPTION_DOTFILL = 0x08000000 # fix dotfill glitch in chart (ESR, 2019)
# Define devices
DSRSENS = 0
iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))//QUADSIZE - 1
iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))//QUADSIZE - 1
if not welcoming(iq):
- return False
+ return []
if enemy.type == 'R':
- return False # Romulans cannot escape!
+ return [] # Romulans cannot escape!
if not irun:
# avoid intruding on another commander's territory
if enemy.type == 'C':
nsteps = abs(int(motion))
if motion > 0 and nsteps > mdist:
nsteps = mdist # don't overshoot
- if nsteps > QUADSIZE:
- nsteps = QUADSIZE # This shouldn't be necessary
- if nsteps < 1:
- nsteps = 1 # This shouldn't be necessary
+ 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)
# Compute preferred values of delta X and Y
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 \
- (enemy.type == 'C' or enemy.type == 'S'):
+ enemy.type in ('C', 'S'):
collision(rammed=True, enemy=enemy)
return []
if krawli != m.i and m.j != 0:
if communicating():
announce()
prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
- proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
+ prout(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
prout(_(" by the Super-commander.\""))
break
return True # looks good!
sc = game.state.kscmdr
for (i, base) in enumerate(game.state.baseq):
basetbl.append((i, (base - sc).distance()))
- if game.state.baseq > 1:
+ if len(game.state.baseq) > 1:
basetbl.sort(key=lambda x: x[1])
# look for nearest base without a commander, no Enterprise, and
# without too many Klingons, and not already under attack.
prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
% game.state.kscmdr)
prout(_(" reports that it is under attack from the Klingon Super-commander."))
- proutn(_(" It can survive until stardate %d.\"") \
+ prout(_(" It can survive until stardate %d.\"") \
% int(scheduled(FSCDBAS)))
if not game.resting:
return
return
announce()
prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
- proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
+ prout(_(" the Super-commander is in Quadrant %s.") % game.state.kscmdr)
return
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
damagereport()
else:
finish(FWON)
- return
def torpedo(origin, bearing, dispersion, number, nburst):
"Let a photon torpedo fly"
deadkl(w, iquad, w)
return None
proutn(crmena(True, iquad, "sector", w))
- displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5)
+ displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5, origin=w)
displacement.nexttok()
bumpto = displacement.sector()
if not bumpto.valid_sector():
prout(_(" damaged but not destroyed."))
- return
+ return None
if game.quad[bumpto.i][bumpto.j] == ' ':
prout(_(" buffeted into black hole."))
deadkl(w, iquad, bumpto)
propor = pfac * game.shield
if game.condition == "docked":
propor *= 2.1
- if propor < 0.1:
- propor = 0.1
+ propor = max(propor, 0.1)
hitsh = propor*chgfac*hit+1.0
absorb = 0.8*hitsh
if absorb > game.shield:
return
continue
else: # decide whether or not to emasculate klingon
+ # pylint: disable=chained-comparison
if kpow > 0 and rnd.withprob(0.9) and kpow <= rnd.real(0.4, 0.8)*kpini:
prout(_("***Mr. Spock- \"Captain, the vessel at Sector %s")%w)
prout(_(" has just lost its firepower.\""))
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
# abort out
scanner.chew()
return
- print("k is %d" % k)
hits.append(scanner.real)
rpow += scanner.real
# If total requested is too much, inform and start over
elif evcode == FCDBAS: # Commander succeeds in destroying base
if evcode == FCDBAS:
unschedule(FCDBAS)
- if not game.state.baseq() \
+ if not game.state.baseq \
or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
game.battle.invalidate()
continue
pdest.charted = True
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!
+ supernova(game.probe.quadrant()) # fire in the hole!
unschedule(FDSPROB)
if game.state.galaxy[pquad.i][pquad.j].supernova:
return
for nq.i in range(GALSIZE):
for nq.j in range(GALSIZE):
nstars += game.state.galaxy[nq.i][nq.j].stars
- if stars == 0:
+ if nstars == 0:
return # nothing to supernova exists
num = rnd.integer(nstars) + 1
for nq.i in range(GALSIZE):
proutn("=== Super nova here?")
if ja():
nq = game.quadrant
- if not nq == game.quadrant or game.justin:
+ if nq != game.quadrant or game.justin:
# it isn't here, or we just entered (treat as enroute)
if communicating():
skip(1)
if game.quadrant == nq or communicating():
game.state.galaxy[nq.i][nq.j].supernova = True
# If supernova destroys last Klingons give special message
- if game.unwon()==0 and not nq == game.quadrant:
+ if game.unwon()==0 and nq != game.quadrant:
skip(2)
if w is None:
prout(_("Lucky you!"))
def cgetline():
"Get a line of input."
if game.options & OPTION_CURSES:
- linein = curwnd.getstr() + "\n"
+ linein = codecs.decode(curwnd.getstr()) + "\n"
curwnd.refresh()
else:
if replayfp and not replayfp.closed:
else:
skip(1)
proutn(_("Torpedo track- "))
- elif step==4 or step==9:
+ elif step in {4, 9}:
skip(1)
proutn("%s " % w)
else:
if i != 0 and step == 1:
drawmaps(2)
time.sleep(0.4)
- if (iquad=='.') or (iquad==' '):
+ if iquad in {'.', ' '}:
put_srscan_sym(w, '+')
#sound(step*10)
#time.sleep(0.1)
if game.iscloaked:
# We can't be tractor beamed if cloaked,
# so move the event into the future
- postpone(FTBEAM, game.optime + expran(1.5*game.intime/len(game.kcmdr)))
+ postpone(FTBEAM, game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
else:
trbeam = True
game.condition = "red"
newquadrant(noattack)
break
elif check_collision(w):
- print("Collision detected")
+ prout(_("Collision detected"))
break
else:
game.sector = w
newcnd()
drawmaps(0)
setwnd(message_window)
- return
def dock(verbose):
"Dock our ship at a starbase."
if game.inorbit:
prout(_("You must first leave standard orbit."))
return
- if game.base is None or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
+ if game.base is None or not game.base.valid_sector():
+ prout(_("No starbase available for docking in this quadrant."))
+ return
+ if (abs(game.sector.i-game.base.i) > 1) or (abs(game.sector.j-game.base.j) > 1):
prout(crmshp() + _(" not adjacent to base."))
return
if game.iscloaked:
break
prout(_("fails."))
textcolor(DEFAULT)
- curses.delay_output(500)
+ if game.options & OPTION_CURSES:
+ curses.delay_output(500)
if m > 3:
game.quad[game.sector.i][game.sector.j]='?'
game.alive = False
def report():
# report on general game status
scanner.chew()
+ # pylint: disable=consider-using-ternary
s1 = (game.thawed and _("thawed ")) or ""
s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
s3 = (None, _("novice"), _("fair"),
show = ".1."
elif game.state.galaxy[i][j].charted:
show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
+ if (game.options & OPTION_DOTFILL):
+ show = show.replace(" ", ".")
else:
show = "..."
proutn(show)
'C':LIGHTRED,
'R':LIGHTRED,
'T':LIGHTRED,
+ '@':LIGHTGREEN,
+ 'P':LIGHTGREEN,
}.get(game.quad[i][j], DEFAULT))
proutn("%c " % game.quad[i][j])
textcolor(DEFAULT)
prout(_("We'll never make it, sir."))
scanner.chew()
return
- if twarp < 1.0:
- twarp = 1.0
+ twarp = max(twarp, 1.0)
break
scanner.chew()
proutn(_("Warp factor? "))
# Position ordinary Klingon Battle Cruisers
krem = game.inkling
klumper = 0.25*game.skill*(9.0-game.length)+1.0
- if klumper > MAXKLQUAD:
- klumper = MAXKLQUAD
+ klumper = min(klumper, MAXKLQUAD)
while True:
r = rnd.real()
klump = int((1.0 - r*r)*klumper)
- if klump > krem:
- klump = krem
+ klump = min(klump, krem)
krem -= klump
while True:
w = randplace(GALSIZE)
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)
+ 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)
+ 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
game.iplnet = None
game.neutz = game.inorbit = game.landed = False
game.ientesc = game.iseenit = game.isviolreported = False
+ game.tholian = None
# Create a blank quadrant
game.quad = fill2d(QUADSIZE, lambda i, j: '.')
if game.iscate:
proutn(legends[i])
if is_scheduled(i):
proutn("%.2f" % (scheduled(i)-game.state.date))
- if i == FENSLV or i == FREPRO:
+ if i in {FENSLV, FREPRO}:
ev = findevent(i)
proutn(" in %s" % ev.quadrant)
else:
scanner.chew()
elif key == "IHREAL":
ev = schedule(i, scanner.real)
- if i == FENSLV or i == FREPRO:
+ if i in {FENSLV, FREPRO}:
scanner.chew()
proutn("In quadrant- ")
key = scanner.nexttok()
replay = False
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:]
logfp.write("# options %s\n" % " ".join(arguments))
logfp.write("# SST2K version %s\n" % version)
logfp.write("# recorded by %s@%s on %s\n" % \
- (getpass.getuser(),socket.gethostname(),time.ctime()))
+ (getpass.getuser(),socket.getfqdn(),time.ctime()))
rnd.seed(seed)
scanner = sstscanner()
for arg in arguments: