mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
989 lines
29 KiB
Python
989 lines
29 KiB
Python
# **** **** STAR TREK **** ****
|
|
# **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE,
|
|
# **** AS SEEN ON THE STAR TREK TV SHOW.
|
|
# **** ORIGINAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION
|
|
# **** PUBLISHED IN DEC'S "101 BASIC GAMES", BY DAVE AHL.
|
|
# **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB
|
|
# **** LEEDOM - APRIL & DECEMBER 1974,
|
|
# **** WITH A LITTLE HELP FROM HIS FRIENDS . . .
|
|
#
|
|
# Python translation by Jack Boyce - February 2021
|
|
# Output is identical to BASIC version except for a few
|
|
# fixes (as noted, search `bug`) and minor cleanup.
|
|
|
|
|
|
import random
|
|
from math import sqrt
|
|
from typing import Any, List
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Utility functions
|
|
# -------------------------------------------------------------------------
|
|
|
|
|
|
def fnr():
|
|
# Generate a random integer from 0 to 7 inclusive.
|
|
return random.randint(0, 7)
|
|
|
|
|
|
def quadrant_name(row, col, region_only=False):
|
|
# Return quadrant name visible on scans, etc.
|
|
region1 = [
|
|
"ANTARES",
|
|
"RIGEL",
|
|
"PROCYON",
|
|
"VEGA",
|
|
"CANOPUS",
|
|
"ALTAIR",
|
|
"SAGITTARIUS",
|
|
"POLLUX",
|
|
]
|
|
region2 = [
|
|
"SIRIUS",
|
|
"DENEB",
|
|
"CAPELLA",
|
|
"BETELGEUSE",
|
|
"ALDEBARAN",
|
|
"REGULUS",
|
|
"ARCTURUS",
|
|
"SPICA",
|
|
]
|
|
modifier = ["I", "II", "III", "IV"]
|
|
|
|
quadrant = region1[row] if col < 4 else region2[row]
|
|
|
|
if not region_only:
|
|
quadrant += " " + modifier[col % 4]
|
|
|
|
return quadrant
|
|
|
|
|
|
def insert_marker(row, col, marker):
|
|
# Insert a marker into a given position in the quadrant string `qs`. The
|
|
# contents of a quadrant (Enterprise, stars, etc.) are stored in `qs`.
|
|
global qs
|
|
|
|
if len(marker) != 3:
|
|
print("ERROR")
|
|
exit()
|
|
|
|
pos = round(col) * 3 + round(row) * 24
|
|
qs = qs[0:pos] + marker + qs[(pos + 3) : 192]
|
|
|
|
|
|
def compare_marker(row, col, test_marker):
|
|
# Check whether the position in the current quadrant is occupied with a
|
|
# given marker.
|
|
pos = round(col) * 3 + round(row) * 24
|
|
return qs[pos : (pos + 3)] == test_marker
|
|
|
|
|
|
def find_empty_place():
|
|
# Find an empty location in the current quadrant.
|
|
while True:
|
|
row, col = fnr(), fnr()
|
|
if compare_marker(row, col, " "):
|
|
return row, col
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Functions for individual player commands
|
|
# -------------------------------------------------------------------------
|
|
|
|
|
|
def navigation():
|
|
# Take navigation input and move the Enterprise.
|
|
global d, s, e, k, qs, t, q1, q2, s1, s2
|
|
|
|
while True:
|
|
c1s = input("COURSE (1-9)? ")
|
|
if len(c1s) > 0:
|
|
c1 = float(c1s)
|
|
break
|
|
if c1 == 9:
|
|
c1 = 1
|
|
if c1 < 1 or c1 >= 9:
|
|
print(" LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'")
|
|
return
|
|
|
|
while True:
|
|
warps = input(f"WARP FACTOR (0-{'0.2' if d[0] < 0 else '8'})? ")
|
|
if len(warps) > 0:
|
|
warp = float(warps)
|
|
break
|
|
if d[0] < 0 and warp > 0.2:
|
|
print("WARP ENGINES ARE DAMAGED. MAXIMUM SPEED = WARP 0.2")
|
|
return
|
|
if warp == 0:
|
|
return
|
|
if warp < 0 or warp > 8:
|
|
print(
|
|
" CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE " f"WARP {warp}!'"
|
|
)
|
|
return
|
|
|
|
n = round(warp * 8)
|
|
if e < n:
|
|
print("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE")
|
|
print(f" FOR MANEUVERING AT WARP {warp}!'")
|
|
if s >= n - e and d[6] >= 0:
|
|
print(f"DEFLECTOR CONTROL ROOM ACKNOWLEDGES {s} UNITS OF ENERGY")
|
|
print(" PRESENTLY DEPLOYED TO SHIELDS.")
|
|
return
|
|
|
|
# klingons move and fire
|
|
for i in range(3):
|
|
if k[i][2] != 0:
|
|
insert_marker(k[i][0], k[i][1], " ")
|
|
k[i][0], k[i][1] = find_empty_place()
|
|
insert_marker(k[i][0], k[i][1], "+K+")
|
|
|
|
klingons_fire()
|
|
|
|
# repair damaged devices and print damage report
|
|
line = ""
|
|
for i in range(8):
|
|
if d[i] < 0:
|
|
d[i] += min(warp, 1)
|
|
if -0.1 < d[i] < 0:
|
|
d[i] = -0.1
|
|
elif d[i] >= 0:
|
|
if len(line) == 0:
|
|
line = "DAMAGE CONTROL REPORT:"
|
|
line += " " + devices[i] + " REPAIR COMPLETED\n"
|
|
if len(line) > 0:
|
|
print(line)
|
|
if random.random() <= 0.2:
|
|
r1 = fnr()
|
|
if random.random() < 0.6:
|
|
d[r1] -= random.random() * 5 + 1
|
|
print(f"DAMAGE CONTROL REPORT: {devices[r1]} DAMAGED\n")
|
|
else:
|
|
d[r1] += random.random() * 3 + 1
|
|
print(
|
|
f"DAMAGE CONTROL REPORT: {devices[r1]} STATE OF " "REPAIR IMPROVED\n"
|
|
)
|
|
|
|
# begin moving starship
|
|
insert_marker(int(s1), int(s2), " ")
|
|
ic1 = int(c1)
|
|
x1 = c[ic1 - 1][0] + (c[ic1][0] - c[ic1 - 1][0]) * (c1 - ic1)
|
|
x2 = c[ic1 - 1][1] + (c[ic1][1] - c[ic1 - 1][1]) * (c1 - ic1)
|
|
q1_start, q2_start = q1, q2
|
|
x, y = s1, s2
|
|
|
|
for _ in range(n):
|
|
s1 += x1
|
|
s2 += x2
|
|
|
|
if s1 < 0 or s1 > 7 or s2 < 0 or s2 > 7:
|
|
# exceeded quadrant limits; calculate final position
|
|
x += 8 * q1 + n * x1
|
|
y += 8 * q2 + n * x2
|
|
q1, q2 = int(x / 8), int(y / 8)
|
|
s1, s2 = int(x - q1 * 8), int(y - q2 * 8)
|
|
if s1 < 0:
|
|
q1 -= 1
|
|
s1 = 7
|
|
if s2 < 0:
|
|
q2 -= 1
|
|
s2 = 7
|
|
|
|
hit_edge = False
|
|
if q1 < 0:
|
|
hit_edge = True
|
|
q1 = s1 = 0
|
|
if q1 > 7:
|
|
hit_edge = True
|
|
q1 = s1 = 7
|
|
if q2 < 0:
|
|
hit_edge = True
|
|
q2 = s2 = 0
|
|
if q2 > 7:
|
|
hit_edge = True
|
|
q2 = s2 = 7
|
|
if hit_edge:
|
|
print("LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:")
|
|
print(" 'PERMISSION TO ATTEMPT CROSSING OF GALACTIC " "PERIMETER")
|
|
print(" IS HEREBY *DENIED*. SHUT DOWN YOUR ENGINES.'")
|
|
print("CHIEF ENGINEER SCOTT REPORTS 'WARP ENGINES SHUT DOWN")
|
|
print(
|
|
f" AT SECTOR {s1 + 1} , {s2 + 1} OF QUADRANT "
|
|
f"{q1 + 1} , {q2 + 1}.'"
|
|
)
|
|
if t > t0 + t9:
|
|
end_game(won=False, quit=False)
|
|
return
|
|
|
|
if q1 == q1_start and q2 == q2_start:
|
|
break
|
|
t += 1
|
|
maneuver_energy(n)
|
|
new_quadrant()
|
|
return
|
|
else:
|
|
pos = int(s1) * 24 + int(s2) * 3
|
|
if qs[pos : (pos + 2)] != " ":
|
|
s1, s2 = int(s1 - x1), int(s2 - x2)
|
|
print(
|
|
"WARP ENGINES SHUT DOWN AT SECTOR "
|
|
f"{s1 + 1} , {s2 + 1} DUE TO BAD NAVAGATION"
|
|
)
|
|
break
|
|
else:
|
|
s1, s2 = int(s1), int(s2)
|
|
|
|
insert_marker(int(s1), int(s2), "<*>")
|
|
maneuver_energy(n)
|
|
|
|
t += 0.1 * int(10 * warp) if warp < 1 else 1
|
|
if t > t0 + t9:
|
|
end_game(won=False, quit=False)
|
|
return
|
|
|
|
short_range_scan()
|
|
|
|
|
|
def maneuver_energy(n):
|
|
# Deduct the energy for navigation from energy/shields.
|
|
global e, s
|
|
|
|
e -= n + 10
|
|
|
|
if e <= 0:
|
|
print("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER.")
|
|
s += e
|
|
e = 0
|
|
s = max(0, s)
|
|
|
|
|
|
def short_range_scan():
|
|
# Print a short range scan.
|
|
global docked, e, p, s
|
|
|
|
docked = False
|
|
for i in (s1 - 1, s1, s1 + 1):
|
|
for j in (s2 - 1, s2, s2 + 1):
|
|
if 0 <= i <= 7 and 0 <= j <= 7:
|
|
if compare_marker(i, j, ">!<"):
|
|
docked = True
|
|
cs = "DOCKED"
|
|
e, p = e0, p0
|
|
print("SHIELDS DROPPED FOR DOCKING PURPOSES")
|
|
s = 0
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
else:
|
|
if k3 > 0:
|
|
cs = "*RED*"
|
|
elif e < e0 * 0.1:
|
|
cs = "YELLOW"
|
|
else:
|
|
cs = "GREEN"
|
|
|
|
if d[1] < 0:
|
|
print("\n*** SHORT RANGE SENSORS ARE OUT ***\n")
|
|
return
|
|
|
|
sep = "---------------------------------"
|
|
print(sep)
|
|
for i in range(8):
|
|
line = ""
|
|
for j in range(8):
|
|
pos = i * 24 + j * 3
|
|
line = line + " " + qs[pos : (pos + 3)]
|
|
|
|
if i == 0:
|
|
line += f" STARDATE {round(int(t * 10) * 0.1, 1)}"
|
|
elif i == 1:
|
|
line += f" CONDITION {cs}"
|
|
elif i == 2:
|
|
line += f" QUADRANT {q1 + 1} , {q2 + 1}"
|
|
elif i == 3:
|
|
line += f" SECTOR {s1 + 1} , {s2 + 1}"
|
|
elif i == 4:
|
|
line += f" PHOTON TORPEDOES {int(p)}"
|
|
elif i == 5:
|
|
line += f" TOTAL ENERGY {int(e + s)}"
|
|
elif i == 6:
|
|
line += f" SHIELDS {int(s)}"
|
|
else:
|
|
line += f" KLINGONS REMAINING {k9}"
|
|
|
|
print(line)
|
|
print(sep)
|
|
|
|
|
|
def long_range_scan():
|
|
# Print a long range scan.
|
|
global z, g
|
|
|
|
if d[2] < 0:
|
|
print("LONG RANGE SENSORS ARE INOPERABLE")
|
|
return
|
|
|
|
print(f"LONG RANGE SCAN FOR QUADRANT {q1 + 1} , {q2 + 1}")
|
|
print_scan_results(q1, q2, g, z)
|
|
|
|
|
|
def print_scan_results(
|
|
q1: int, q2: int, g: List[List[Any]], z: List[List[Any]]
|
|
) -> None:
|
|
sep = "-------------------"
|
|
print(sep)
|
|
for i in (q1 - 1, q1, q1 + 1):
|
|
n = [-1, -2, -3]
|
|
|
|
for j in (q2 - 1, q2, q2 + 1):
|
|
if 0 <= i <= 7 and 0 <= j <= 7:
|
|
n[j - q2 + 1] = g[i][j]
|
|
z[i][j] = g[i][j]
|
|
|
|
line = ": "
|
|
for line_index in range(3):
|
|
if n[line_index] < 0:
|
|
line += "*** : "
|
|
else:
|
|
line += str(n[line_index] + 1000).rjust(4, " ")[-3:] + " : "
|
|
print(line)
|
|
print(sep)
|
|
|
|
|
|
def phaser_control():
|
|
# Take phaser control input and fire phasers.
|
|
global e, k, g, z, k3, k9
|
|
|
|
if d[3] < 0:
|
|
print("PHASERS INOPERATIVE")
|
|
return
|
|
|
|
if k3 <= 0:
|
|
print("SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS")
|
|
print(" IN THIS QUADRANT'")
|
|
return
|
|
|
|
if d[7] < 0:
|
|
print("COMPUTER FAILURE HAMPERS ACCURACY")
|
|
|
|
print(f"PHASERS LOCKED ON TARGET; ENERGY AVAILABLE = {e} UNITS")
|
|
x = 0
|
|
while True:
|
|
while True:
|
|
units_to_fire = input("NUMBER OF UNITS TO FIRE? ")
|
|
if len(units_to_fire) > 0:
|
|
x = int(units_to_fire)
|
|
break
|
|
if x <= 0:
|
|
return
|
|
if e >= x:
|
|
break
|
|
print(f"ENERGY AVAILABLE = {e} UNITS")
|
|
|
|
e -= x
|
|
if d[7] < 0: # bug in original, was d[6]
|
|
x *= random.random()
|
|
|
|
h1 = int(x / k3)
|
|
for i in range(3):
|
|
if k[i][2] <= 0:
|
|
continue
|
|
|
|
h = int((h1 / fnd(i)) * (random.random() + 2))
|
|
if h <= 0.15 * k[i][2]:
|
|
print(
|
|
"SENSORS SHOW NO DAMAGE TO ENEMY AT " f"{k[i][0] + 1} , {k[i][1] + 1}"
|
|
)
|
|
else:
|
|
k[i][2] -= h
|
|
print(
|
|
f" {h} UNIT HIT ON KLINGON AT SECTOR " f"{k[i][0] + 1} , {k[i][1] + 1}"
|
|
)
|
|
if k[i][2] <= 0:
|
|
print("*** KLINGON DESTROYED ***")
|
|
k3 -= 1
|
|
k9 -= 1
|
|
insert_marker(k[i][0], k[i][1], " ")
|
|
k[i][2] = 0
|
|
g[q1][q2] -= 100
|
|
z[q1][q2] = g[q1][q2]
|
|
if k9 <= 0:
|
|
end_game(won=True, quit=False)
|
|
return
|
|
else:
|
|
print(f" (SENSORS SHOW {round(k[i][2],6)} UNITS REMAINING)")
|
|
|
|
klingons_fire()
|
|
|
|
|
|
def photon_torpedoes():
|
|
# Take photon torpedo input and process firing of torpedoes.
|
|
global e, p, k3, k9, k, b3, b9, docked, g, z
|
|
|
|
if p <= 0:
|
|
print("ALL PHOTON TORPEDOES EXPENDED")
|
|
return
|
|
if d[4] < 0:
|
|
print("PHOTON TUBES ARE NOT OPERATIONAL")
|
|
return
|
|
|
|
while True:
|
|
torpedo_course = input("PHOTON TORPEDO COURSE (1-9)? ")
|
|
if len(torpedo_course) > 0:
|
|
c1 = float(torpedo_course)
|
|
break
|
|
if c1 == 9:
|
|
c1 = 1
|
|
if c1 < 1 or c1 >= 9:
|
|
print("ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'")
|
|
return
|
|
|
|
ic1 = int(c1)
|
|
x1 = c[ic1 - 1][0] + (c[ic1][0] - c[ic1 - 1][0]) * (c1 - ic1)
|
|
e -= 2
|
|
p -= 1
|
|
x2 = c[ic1 - 1][1] + (c[ic1][1] - c[ic1 - 1][1]) * (c1 - ic1)
|
|
x, y = s1, s2
|
|
x3, y3 = x, y
|
|
print("TORPEDO TRACK:")
|
|
while True:
|
|
x += x1
|
|
y += x2
|
|
x3, y3 = round(x), round(y)
|
|
if x3 < 0 or x3 > 7 or y3 < 0 or y3 > 7:
|
|
print("TORPEDO MISSED")
|
|
klingons_fire()
|
|
return
|
|
print(f" {x3 + 1} , {y3 + 1}")
|
|
if not compare_marker(x3, y3, " "):
|
|
break
|
|
|
|
if compare_marker(x3, y3, "+K+"):
|
|
print("*** KLINGON DESTROYED ***")
|
|
k3 -= 1
|
|
k9 -= 1
|
|
if k9 <= 0:
|
|
end_game(won=True, quit=False)
|
|
return
|
|
for i in range(3):
|
|
if x3 == k[i][0] and y3 == k[i][1]:
|
|
k[i][2] = 0
|
|
elif compare_marker(x3, y3, " * "):
|
|
print(f"STAR AT {x3 + 1} , {y3 + 1} ABSORBED TORPEDO ENERGY.")
|
|
klingons_fire()
|
|
return
|
|
elif compare_marker(x3, y3, ">!<"):
|
|
print("*** STARBASE DESTROYED ***")
|
|
b3 -= 1
|
|
b9 -= 1
|
|
if b9 == 0 and k9 <= t - t0 - t9:
|
|
print("THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND")
|
|
print("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!")
|
|
end_game(won=False)
|
|
return
|
|
else:
|
|
print("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER")
|
|
print("COURT MARTIAL!")
|
|
docked = False
|
|
|
|
insert_marker(x3, y3, " ")
|
|
g[q1][q2] = k3 * 100 + b3 * 10 + s3
|
|
z[q1][q2] = g[q1][q2]
|
|
klingons_fire()
|
|
|
|
|
|
def fnd(i):
|
|
# Find distance between Enterprise and i'th Klingon warship.
|
|
return sqrt((k[i][0] - s1) ** 2 + (k[i][1] - s2) ** 2)
|
|
|
|
|
|
def klingons_fire():
|
|
# Process nearby Klingons firing on Enterprise.
|
|
global s, k, d
|
|
|
|
if k3 <= 0:
|
|
return
|
|
if docked:
|
|
print("STARBASE SHIELDS PROTECT THE ENTERPRISE")
|
|
return
|
|
|
|
for i in range(3):
|
|
if k[i][2] <= 0:
|
|
continue
|
|
|
|
h = int((k[i][2] / fnd(i)) * (random.random() + 2))
|
|
s -= h
|
|
k[i][2] /= random.random() + 3
|
|
print(
|
|
f" {h} UNIT HIT ON ENTERPRISE FROM SECTOR " f"{k[i][0] + 1} , {k[i][1] + 1}"
|
|
)
|
|
if s <= 0:
|
|
end_game(won=False, quit=False, enterprise_killed=True)
|
|
return
|
|
print(f" <SHIELDS DOWN TO {s} UNITS>")
|
|
if h >= 20 and random.random() < 0.60 and h / s > 0.02:
|
|
r1 = fnr()
|
|
d[r1] -= h / s + 0.5 * random.random()
|
|
print(f"DAMAGE CONTROL REPORTS '{devices[r1]} DAMAGED " "BY THE HIT'")
|
|
|
|
|
|
def shield_control():
|
|
# Raise or lower the shields.
|
|
global e, s
|
|
|
|
if d[6] < 0:
|
|
print("SHIELD CONTROL INOPERABLE")
|
|
return
|
|
|
|
while True:
|
|
energy_to_shield = input(
|
|
f"ENERGY AVAILABLE = {e + s} NUMBER OF UNITS TO SHIELDS? "
|
|
)
|
|
if len(energy_to_shield) > 0:
|
|
x = int(energy_to_shield)
|
|
break
|
|
|
|
if x < 0 or s == x:
|
|
print("<SHIELDS UNCHANGED>")
|
|
return
|
|
|
|
if x > e + s:
|
|
print(
|
|
"SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION "
|
|
"TREASURY.'\n"
|
|
"<SHIELDS UNCHANGED>"
|
|
)
|
|
return
|
|
|
|
e += s - x
|
|
s = x
|
|
print("DEFLECTOR CONTROL ROOM REPORT:")
|
|
print(f" 'SHIELDS NOW AT {s} UNITS PER YOUR COMMAND.'")
|
|
|
|
|
|
def damage_control():
|
|
# Print a damage control report.
|
|
global d, t
|
|
|
|
if d[5] < 0:
|
|
print("DAMAGE CONTROL REPORT NOT AVAILABLE")
|
|
else:
|
|
print("\nDEVICE STATE OF REPAIR")
|
|
for r1 in range(8):
|
|
print(f"{devices[r1].ljust(26, ' ')}{int(d[r1] * 100) * 0.01:g}")
|
|
print()
|
|
|
|
if not docked:
|
|
return
|
|
|
|
d3 = sum(0.1 for i in range(8) if d[i] < 0)
|
|
if d3 == 0:
|
|
return
|
|
|
|
d3 += d4
|
|
if d3 >= 1:
|
|
d3 = 0.9
|
|
print("\nTECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;")
|
|
print("ESTIMATED TIME TO REPAIR: " f"{round(0.01 * int(100 * d3), 2)} STARDATES")
|
|
if input("WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)? ").upper().strip() != "Y":
|
|
return
|
|
|
|
for i in range(8):
|
|
if d[i] < 0:
|
|
d[i] = 0
|
|
t += d3 + 0.1
|
|
|
|
|
|
def computer():
|
|
# Perform the various functions of the library computer.
|
|
global d, z, k9, t0, t9, t, b9, s1, s2, b4, b5
|
|
|
|
if d[7] < 0:
|
|
print("COMPUTER DISABLED")
|
|
return
|
|
|
|
while True:
|
|
command = input("COMPUTER ACTIVE AND AWAITING COMMAND? ")
|
|
if len(command) == 0:
|
|
com = 6
|
|
else:
|
|
com = int(command)
|
|
if com < 0:
|
|
return
|
|
|
|
print()
|
|
|
|
if com == 0 or com == 5:
|
|
if com == 5:
|
|
print(" THE GALAXY")
|
|
else:
|
|
print(
|
|
"\n COMPUTER RECORD OF GALAXY FOR "
|
|
f"QUADRANT {q1 + 1} , {q2 + 1}\n"
|
|
)
|
|
|
|
print(" 1 2 3 4 5 6 7 8")
|
|
sep = " ----- ----- ----- ----- ----- ----- ----- -----"
|
|
print(sep)
|
|
|
|
for i in range(8):
|
|
line = " " + str(i + 1) + " "
|
|
|
|
if com == 5:
|
|
g2s = quadrant_name(i, 0, True)
|
|
line += (" " * int(12 - 0.5 * len(g2s))) + g2s
|
|
g2s = quadrant_name(i, 4, True)
|
|
line += (" " * int(39 - 0.5 * len(g2s) - len(line))) + g2s
|
|
else:
|
|
for j in range(8):
|
|
line += " "
|
|
if z[i][j] == 0:
|
|
line += "***"
|
|
else:
|
|
line += str(z[i][j] + 1000)[-3:]
|
|
|
|
print(line)
|
|
print(sep)
|
|
|
|
print()
|
|
return
|
|
elif com == 1:
|
|
print(" STATUS REPORT:")
|
|
print(f"KLINGON{'S' if k9 > 1 else ''} LEFT: {k9}")
|
|
print(
|
|
"MISSION MUST BE COMPLETED IN "
|
|
f"{round(0.1 * int((t0+t9-t) * 10), 1)} STARDATES"
|
|
)
|
|
|
|
if b9 == 0:
|
|
print("YOUR STUPIDITY HAS LEFT YOU ON YOUR OWN IN")
|
|
print(" THE GALAXY -- YOU HAVE NO STARBASES LEFT!")
|
|
else:
|
|
print(
|
|
f"THE FEDERATION IS MAINTAINING {b9} "
|
|
f"STARBASE{'S' if b9 > 1 else ''} IN THE GALAXY"
|
|
)
|
|
|
|
damage_control()
|
|
return
|
|
elif com == 2:
|
|
if k3 <= 0:
|
|
print(
|
|
"SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY "
|
|
"SHIPS\n"
|
|
" IN THIS QUADRANT'"
|
|
)
|
|
return
|
|
|
|
print(
|
|
"FROM ENTERPRISE TO KLINGON BATTLE " f"CRUISER{'S' if k3 > 1 else ''}"
|
|
)
|
|
|
|
for i in range(3):
|
|
if k[i][2] > 0:
|
|
print_direction(s1, s2, k[i][0], k[i][1])
|
|
return
|
|
elif com == 3:
|
|
if b3 == 0:
|
|
print(
|
|
"MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS "
|
|
"QUADRANT.'"
|
|
)
|
|
return
|
|
|
|
print("FROM ENTERPRISE TO STARBASE:")
|
|
print_direction(s1, s2, b4, b5)
|
|
return
|
|
elif com == 4:
|
|
print("DIRECTION/DISTANCE CALCULATOR:")
|
|
print(f"YOU ARE AT QUADRANT {q1+1} , {q2+1} SECTOR " f"{s1+1} , {s2+1}")
|
|
print("PLEASE ENTER")
|
|
while True:
|
|
coordinates = input(" INITIAL COORDINATES (X,Y)? ").split(",")
|
|
if len(coordinates) == 2:
|
|
from1, from2 = int(coordinates[0]) - 1, int(coordinates[1]) - 1
|
|
if 0 <= from1 <= 7 and 0 <= from2 <= 7:
|
|
break
|
|
while True:
|
|
coordinates = input(" FINAL COORDINATES (X,Y)? ").split(",")
|
|
if len(coordinates) == 2:
|
|
to1, to2 = int(coordinates[0]) - 1, int(coordinates[1]) - 1
|
|
if 0 <= to1 <= 7 and 0 <= to2 <= 7:
|
|
break
|
|
print_direction(from1, from2, to1, to2)
|
|
return
|
|
else:
|
|
print(
|
|
"FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:\n"
|
|
" 0 = CUMULATIVE GALACTIC RECORD\n"
|
|
" 1 = STATUS REPORT\n"
|
|
" 2 = PHOTON TORPEDO DATA\n"
|
|
" 3 = STARBASE NAV DATA\n"
|
|
" 4 = DIRECTION/DISTANCE CALCULATOR\n"
|
|
" 5 = GALAXY 'REGION NAME' MAP\n"
|
|
)
|
|
|
|
|
|
def print_direction(from1, from2, to1, to2):
|
|
# Print direction and distance between two locations in the grid.
|
|
delta1 = -(to1 - from1) # flip so positive is up (heading = 3)
|
|
delta2 = to2 - from2
|
|
|
|
if delta2 > 0:
|
|
if delta1 < 0:
|
|
base = 7
|
|
else:
|
|
base = 1
|
|
delta1, delta2 = delta2, delta1
|
|
else:
|
|
if delta1 > 0:
|
|
base = 3
|
|
else:
|
|
base = 5
|
|
delta1, delta2 = delta2, delta1
|
|
|
|
delta1, delta2 = abs(delta1), abs(delta2)
|
|
|
|
if delta1 > 0 or delta2 > 0: # bug in original; no check for divide by 0
|
|
if delta1 >= delta2:
|
|
print(f"DIRECTION = {round(base + delta2 / delta1, 6)}")
|
|
else:
|
|
print(f"DIRECTION = {round(base + 2 - delta1 / delta2, 6)}")
|
|
|
|
print(f"DISTANCE = {round(sqrt(delta1 ** 2 + delta2 ** 2), 6)}")
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Game transitions
|
|
# -------------------------------------------------------------------------
|
|
|
|
|
|
def startup():
|
|
# Initialize the game variables and map, and print startup messages.
|
|
global g, z, d, t, t0, t9, docked, e, e0, p, p0, s, k9, b9, s9, c
|
|
global devices, q1, q2, s1, s2, k7
|
|
|
|
print(
|
|
"\n\n\n\n\n\n\n\n\n\n\n"
|
|
" ,------*------,\n"
|
|
" ,------------- '--- ------'\n"
|
|
" '-------- --' / /\n"
|
|
" ,---' '-------/ /--,\n"
|
|
" '----------------'\n\n"
|
|
" THE USS ENTERPRISE --- NCC-1701\n"
|
|
"\n\n\n\n"
|
|
)
|
|
|
|
# set up global game variables
|
|
g = [[0] * 8 for _ in range(8)] # galaxy map
|
|
z = [[0] * 8 for _ in range(8)] # charted galaxy map
|
|
d = [0] * 8 # damage stats for devices
|
|
t = t0 = 100 * random.randint(20, 39) # stardate (current, initial)
|
|
t9 = random.randint(25, 34) # mission duration (stardates)
|
|
docked = False # true when docked at starbase
|
|
e = e0 = 3000 # energy (current, initial)
|
|
p = p0 = 10 # torpedoes (current, initial)
|
|
s = 0 # shields
|
|
k9, b9 = 0, 0 # total Klingons, bases in galaxy
|
|
# ^ bug in original, was b9 = 2
|
|
s9 = 200 # avg. Klingon shield strength
|
|
|
|
c = [
|
|
[0, 1],
|
|
[-1, 1],
|
|
[-1, 0],
|
|
[-1, -1],
|
|
[0, -1],
|
|
[1, -1],
|
|
[1, 0],
|
|
[1, 1],
|
|
[0, 1],
|
|
] # vectors in cardinal directions
|
|
|
|
devices = [
|
|
"WARP ENGINES",
|
|
"SHORT RANGE SENSORS",
|
|
"LONG RANGE SENSORS",
|
|
"PHASER CONTROL",
|
|
"PHOTON TUBES",
|
|
"DAMAGE CONTROL",
|
|
"SHIELD CONTROL",
|
|
"LIBRARY-COMPUTER",
|
|
]
|
|
|
|
# initialize Enterprise's position
|
|
q1, q2 = fnr(), fnr() # Enterprise's quadrant
|
|
s1, s2 = fnr(), fnr() # ...and sector
|
|
|
|
# initialize contents of galaxy
|
|
for i in range(8):
|
|
for j in range(8):
|
|
k3 = 0
|
|
r1 = random.random()
|
|
|
|
if r1 > 0.98:
|
|
k3 = 3
|
|
elif r1 > 0.95:
|
|
k3 = 2
|
|
elif r1 > 0.80:
|
|
k3 = 1
|
|
k9 += k3
|
|
|
|
b3 = 0
|
|
if random.random() > 0.96:
|
|
b3 = 1
|
|
b9 += 1
|
|
g[i][j] = k3 * 100 + b3 * 10 + fnr() + 1
|
|
|
|
if k9 > t9:
|
|
t9 = k9 + 1
|
|
|
|
if b9 == 0: # original has buggy extra code here
|
|
b9 = 1
|
|
g[q1][q2] += 10
|
|
q1, q2 = fnr(), fnr()
|
|
|
|
k7 = k9 # Klingons at start of game
|
|
|
|
print(
|
|
"YOUR ORDERS ARE AS FOLLOWS:\n"
|
|
f" DESTROY THE {k9} KLINGON WARSHIPS WHICH HAVE INVADED\n"
|
|
" THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS\n"
|
|
f" ON STARDATE {t0+t9}. THIS GIVES YOU {t9} DAYS. THERE "
|
|
f"{'IS' if b9 == 1 else 'ARE'}\n"
|
|
f" {b9} STARBASE{'' if b9 == 1 else 'S'} IN THE GALAXY FOR "
|
|
"RESUPPLYING YOUR SHIP.\n"
|
|
)
|
|
|
|
|
|
def new_quadrant():
|
|
# Enter a new quadrant: populate map and print a short range scan.
|
|
global z, k3, b3, s3, d4, k, qs, b4, b5
|
|
|
|
k3 = b3 = s3 = 0 # Klingons, bases, stars in quad.
|
|
d4 = 0.5 * random.random() # extra delay in repairs at base
|
|
z[q1][q2] = g[q1][q2]
|
|
|
|
if 0 <= q1 <= 7 and 0 <= q2 <= 7:
|
|
quad = quadrant_name(q1, q2, False)
|
|
if t == t0:
|
|
print("\nYOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED")
|
|
print(f"IN THE GALACTIC QUADRANT, '{quad}'.\n")
|
|
else:
|
|
print(f"\nNOW ENTERING {quad} QUADRANT . . .\n")
|
|
|
|
k3 = g[q1][q2] // 100
|
|
b3 = g[q1][q2] // 10 - 10 * k3
|
|
s3 = g[q1][q2] - 100 * k3 - 10 * b3
|
|
|
|
if k3 != 0:
|
|
print("COMBAT AREA CONDITION RED")
|
|
if s <= 200:
|
|
print(" SHIELDS DANGEROUSLY LOW")
|
|
|
|
k = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # Klingons in current quadrant
|
|
qs = " " * 192 # quadrant string
|
|
|
|
# build quadrant string
|
|
insert_marker(s1, s2, "<*>")
|
|
for i in range(k3):
|
|
r1, r2 = find_empty_place()
|
|
insert_marker(r1, r2, "+K+")
|
|
k[i] = [r1, r2, s9 * (0.5 + random.random())]
|
|
if b3 > 0:
|
|
b4, b5 = find_empty_place() # position of starbase (sector)
|
|
insert_marker(b4, b5, ">!<")
|
|
for i in range(s3):
|
|
r1, r2 = find_empty_place()
|
|
insert_marker(r1, r2, " * ")
|
|
|
|
short_range_scan()
|
|
|
|
|
|
def end_game(won=False, quit=True, enterprise_killed=False):
|
|
# Handle end-of-game situations.
|
|
global restart
|
|
|
|
if won:
|
|
print("CONGRATULATIONS, CAPTAIN! THE LAST KLINGON BATTLE CRUISER")
|
|
print("MENACING THE FEDERATION HAS BEEN DESTROYED.\n")
|
|
print("YOUR EFFICIENCY RATING IS " f"{round(1000 * (k7 / (t - t0))**2, 4)}\n\n")
|
|
else:
|
|
if not quit:
|
|
if enterprise_killed:
|
|
print(
|
|
"\nTHE ENTERPRISE HAS BEEN DESTROYED. THE FEDERATION "
|
|
"WILL BE CONQUERED."
|
|
)
|
|
print(f"IT IS STARDATE {round(t, 1)}")
|
|
|
|
print(f"THERE WERE {k9} KLINGON BATTLE CRUISERS LEFT AT")
|
|
print(f"THE END OF YOUR MISSION.\n\n")
|
|
|
|
if b9 == 0:
|
|
exit()
|
|
|
|
print("THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER")
|
|
print("FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,")
|
|
if input("LET HIM STEP FORWARD AND " "ENTER 'AYE'? ").upper().strip() != "AYE":
|
|
exit()
|
|
restart = True
|
|
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Entry point and main game loop
|
|
# -------------------------------------------------------------------------
|
|
|
|
|
|
def main():
|
|
global restart
|
|
|
|
f = {
|
|
"NAV": navigation,
|
|
"SRS": short_range_scan,
|
|
"LRS": long_range_scan,
|
|
"PHA": phaser_control,
|
|
"TOR": photon_torpedoes,
|
|
"SHE": shield_control,
|
|
"DAM": damage_control,
|
|
"COM": computer,
|
|
"XXX": end_game,
|
|
}
|
|
|
|
while True:
|
|
startup()
|
|
new_quadrant()
|
|
restart = False
|
|
|
|
while not restart:
|
|
if s + e <= 10 or (e <= 10 and d[6] != 0):
|
|
print(
|
|
"\n** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP "
|
|
"IN SPACE.\nYOU HAVE INSUFFICIENT MANEUVERING ENERGY, "
|
|
"AND SHIELD CONTROL\nIS PRESENTLY INCAPABLE OF CROSS-"
|
|
"CIRCUITING TO ENGINE ROOM!!"
|
|
)
|
|
|
|
command = input("COMMAND? ").upper().strip()
|
|
|
|
if command in f:
|
|
f[command]()
|
|
else:
|
|
print(
|
|
"ENTER ONE OF THE FOLLOWING:\n"
|
|
" NAV (TO SET COURSE)\n"
|
|
" SRS (FOR SHORT RANGE SENSOR SCAN)\n"
|
|
" LRS (FOR LONG RANGE SENSOR SCAN)\n"
|
|
" PHA (TO FIRE PHASERS)\n"
|
|
" TOR (TO FIRE PHOTON TORPEDOES)\n"
|
|
" SHE (TO RAISE OR LOWER SHIELDS)\n"
|
|
" DAM (FOR DAMAGE CONTROL REPORTS)\n"
|
|
" COM (TO CALL ON LIBRARY-COMPUTER)\n"
|
|
" XXX (TO RESIGN YOUR COMMAND)\n"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|