#!/usr/bin/env python # Extracted from HP tape image 16-Nov-2003 by Pete Turnbull # **** HP BASIC Program Library ****************************** # # sttr1: Star Trek # # 36243 Rev B -- 10/73 # # **** Contributed Program *********************************** # ***************************************************************** # *** *** # *** Star Trek: by Mike Mayfield, Centerline Engineering *** # *** *** # *** Total Interaction Game - Orig. 20 Oct 1972 # *** *** # ***************************************************************** # # Converted to python by Mike Markowski, mike.ab3ap@gmail.com # Sep 2024 # # Some big changes involved splitting the monolithic code into subroutines. # I also made some small changes: replaced short range scan strings with 2D # array, modified computer range/bearing output, split off computer # calculator to its own library coputer menu item, and added an 'e' command # to end game. I also changed strings to mixed case. Nostalgia is great, # but all upper case...not so much. :-) from numpy import arctan2, cos, pi, sin, sqrt from numpy.random import random, randint import numpy as np import sys, time # The many globals of STTR1! g = np.zeros((8,8), dtype=int) # mm - replaced scanlines with 2D galaxy. k = np.zeros((3,3), dtype=int) n = np.zeros(3, dtype=int) z = np.zeros((8,8), dtype=int) d = np.zeros(8, dtype=int) t0 = t = randint(20,40)*100 t9 = 30 d0 = 0 e0 = e = 3000 p0 = p = 10 s9 = 200 s = 0 q1 = randint(8) q2 = randint(8) s1 = randint(8) s2 = randint(8) t7 = time.time() ds = ['Warp Engines', 'S.R. Sensors', 'L.R. Sensors', 'Phaser Cntrl', 'Photon Tubes', 'Damage Cntrl', 'Shield Cntrl', 'Computer'] b9 = k9 = 0 def adrift(): print('The Enterprise is dead in space. If you survive all impending') print('attack you will be demoted to the rank of Private') while True: if k3 <= 0: lose(True) klingonAttack() def computer(): global d, q1, q2, z if d[7] < 0: print('Computer disabled') return while True: a = input('Computer active and awaiting command: ').strip() if a != '': a = a[0] if a in ['0', '1', '2', '3']: break print('Functions available from computer') print(' 0 = Cumulative galactic record') print(' 1 = Status report') print(' 2 = Photon torpedo data') print(' 3 = Direction calculator') if a == '0': print('Computer record of galaxy for quadrant %d,%d' % (q1+1, q2+1)) print(' 1 2 3 4 5 6 7 8') print(' ----- ----- ----- ----- ----- ----- ----- -----') for i in range(8): print('%d %3d %3d %3d %3d %3d %3d %3d %3d' % (i+1,z[i,0],z[i,1],z[i,2],z[i,3],z[i,4],z[i,5],z[i,6],z[i,7])) print(' ----- ----- ----- ----- ----- ----- ----- -----') elif a == '1': print('\n Status Report\n') print('Number of Klingons left = %d' % k9) print('Number of Stardates left = %d' % (t0+t9-t)) print('Number of Starbases left = %d' % b9) damageControl() elif a == '2': for i in range(3): if k[i,2] <= 0: continue rng, theta = vec(s2, s1, k[i,1], k[i,0]) print(' Range %.1f, bearing %.1f' % (rng, theta)) else: print('You are at quadrant (%d,%d) sector (%d,%d)' % (q1+1, q2+1, s1+1, s2+1)) line = input('Ship\'s & target\'s coordinates are: ') try: line = line.replace(',', ' ').split() line = map(int, line) y0, x0, y1, x1 = line except ValueError: return rng, theta = vec(x0, y0, x1, y1) # print('Direction = %.2f' % theta) # print('Distance = %.2f' % rng) print(' Range %.1f, bearing %.1f' % (rng, theta)) def course(): global c, d, e, g, k3, s, s1, s2, q1, q2, t, t0, t9 # Gte player input. while True: c1 = inputSafe(float, 'Course (1-9): ') if c1 == 0: return if not (1 <= c1 < 9): continue w1 = inputSafe(float, 'Warp factor (0-8): ') if not (0 <= w1 <= 8): continue if d[0] >= 0 or w1 <= 0.2: break print('Warp engines are damaged, maximum speed = warp .2') if k3 > 0: klingonAttack() if s <= 0: lose() elif e <= 0: if s < 1: adrift() print('You have %d units of energy' % e) print('Suggest you get some from your shields which have %d units left' % s) return for i in range(8): if d[i] >= 0: continue d[i] += 1 if random() < 0.2: r1 = randint(8) if random() < 0.5: d[r1] -= random()*5 + 1 print('\nDamage control report: %s damaged\n' % ds[r1]) else: d[r1] += random()*5 + 1 print('\nDamage control report: %s state of repair improved\n' % ds[r1]) n = int(w1*8) # Number of sectors to travel. q[s1][s2] = ' ' # Enterprise leaves sector. x = s1 # Starting point for travel through quadrant. y = s2 x1 = -sin((c1 - 1)*pi/4) # Unit vector, course c1 is multiple of 45 deg. x2 = cos((c1 - 1)*pi/4) blocked = False for _ in range(n): # Sector at a time, check for objects blocking way. x += x1 y += x2 z1 = round(x) # Nearest sector. z2 = round(y) if not (0 <= z1 <= 7 and 0 <= z2 <= 7): # Left quadrant safely. break if q[z1][z2] == ' ': # Traveling in open space. continue blocked = True print(' Warp engines shutdown at sector %d,%d due to bad navigation' % (z1+1, z2+1)) x -= x1 y -= x2 s1,s2 = int(x),int(y) break if not blocked: # Inter-quadrant travel. x = q1 + s1/8 + w1*x1 # Destination quadrant, real. y = q2 + s2/8 + w1*x2 # Prevent leaving galaxy. if x < 0: x = s1 = 0 elif x >= 8: x = s1 = 7 else: s1 = int((8*(x - int(x)))%8) # Sector, integer. if y < 0: y = s2 = 0 elif y >= 8: y = s2 = 7 else: s2 = int((8*(y - int(y)))%8) z1 = int(x) # Quadrant, integer. z2 = int(y) if (q1,q2) != (z1,z2): q1,q2 = z1,z2 mkQuadrant() q[s1][s2] = '<*>' # Enterprise arrival sector. e += -n + 5 if w1 >= 1: t += 1 if t > t0+t9: lose(True) def damageControl(): if d[5] < 0: print('Damage control report is not available') return print('\nDevice State of Repair') for r1 in range(8): print('%-14s%d' % (ds[r1], d[r1])) print('') def dist(x0, y0, x1, y1): return sqrt((x0 - x1)**2 + (y0 - y1)**2) def emptySector(q): while True: r1,r2 = randint(0,8,2) if q[r1][r2] == ' ': return r1,r2 def inputSafe(dtype, prompt): '''Keep trying for correct input until user does it right! This prevents the game from dying on incorrect type of response, e.g., accidentally entering a text command when a number is expected. ''' while True: try: return dtype(input(prompt)) except ValueError: pass def instructions(): print(''' Instructions: <*> = Enterprise +++ = Klingon >!< = Starbase * = Star Command 0 = Warp Engine Control 'Course' is in a circular numerical 4 3 2 vector arrangement as shown. \ ^ / Interger and real values may be \^/ used. Therefore course 1.5 is 5 ----- 1 half way between 1 and 2. /^\\ / ^ \\ A vector of 9 is undefined, but 6 7 8 values may approach 9. course One 'warp factor' is the size of one quadrant. Therefore to get from quadrant 6,5 to 5,5 you would use course 3, warp factor 1 Command 1 = Short Range Sensor Scan Prints the quadrant you are currently in, including stars, Klingons, starbases, and the Enterprise; along with other pertinate information. Command 2 = Long Range Sensor Scan Shows conditions in space for one quadrant on each side of the enterprise in the middle of the scan. The scan is coded in the form XXX, where the units digit is the number of stars, the tens digit is the number of star- bases, the hundreds digit is the number of Klingons. Command 3 = Phaser Control Allows you to destroy the Klingons by hitting him with suitably large numbers of energy units to deplete his shield power. Keep in mind that when you shoot at him, he gonna do it to you too. Command 4 = Photon Torpedo Control Course is the same as used in warp engine control if you hit the Klingon, he is destroyed and cannot fire back at you. If you miss, he will shoot his phasers at you. Note: The Library Computer (command 7) has an option to compute torpedo trajectory for you (option 2). Command 5 = Shield Control Defines number of energy units to be assigned to shields energy is taken from total ship's energy. Command 6 = Damage Control Report Gives state of repairs of all devices. A state of repair less than zero shows that that device is temporaraly damaged. Command 7 = Library Computer The Library Computer contains three options: Option 0 = Cumulative Galactic Record shows computer memory of the results of all previous long range sensor scans Option 1 = Status Report Shows number of Klingons, stardates and starbases left. Option 2 = Photon Torpedo Data gives trajectory and distance between the enterprise and all Klingons in your quadrant ''') def klingonAttack(): global cs, k, s, s1, s2 if cs == 'Docked': print('Star base shields protect the enterprise') return if k3 <= 0: return for i in range(3): if k[i,2] <= 0: continue far = dist(k[i,0], k[i,1], s1, s2) h = (k[i,2]/far)*(2*random()) s = max(0, s-h) print('%4d unit hit on Enterprise at sector %d,%d (%4d left)' % (h, k[i,0]+1, k[i,1]+1, s)) if s <= 0: lose() def lines12(): for i in range(11): print() print() def lose(tooLong=False): if tooLong: print('\nIt is stardate %d' % t) else: print('\nThe Enterprise has been destroyed. ', end='') print('The Federation will be conquered') print('There are still %d Klingon battle cruisers' % k9) sys.exit(0) def lrScan(): global g, q1, q2 if d[2] < 0: print('Long range sensors are inoperable') return print('Long range sensor scan for quadrant %d,%d' % (q1+1,q2+1)) print('-------------------') for i in range(q1-1, q1+2): n[:] = 0 for j in range(q2-1, q2+2): if not (0 <= i <= 7 and 0 <= j <= 7): continue n[j-q2+1] = g[i,j] if d[7] < 0: # mm - Computer unavailable for storage. continue z[i,j] = g[i,j] print(': %3d : %3d : %3d :' % (n[0],n[1],n[2])) print('-------------------') def mkGalaxy(): global b3, b9, k3, k7, k9, s3, g, z while True: # mm - create galaxy. for i in range(8): for j in range(8): r1 = random() if r1 > 0.98: k3 = 3 k9 += 3 elif r1 > 0.95: k3 = 2 k9 += 2 elif r1 > 0.8: k3 = 1 k9 += 1 else: k3 = 0 r1 = random() if r1 > 0.96: b3 = 1 b9 += 1 else: b3 = 0 s3 = randint(1, 9) g[i,j] = k3*100 + b3*10 + s3 z[i,j] = 0 k7 = k9 if b9 > 0 and k9 > 0: break print('You must destroy %d Klingons in %d Stardates with %d Starbases\n' % (k9, t9, b9)) def mkQuadrant(): global b3, k3, s3, b9, k, k9, q, q1, q2, qs, rs, ss, s1, s2, s9, t9 if not (0 <= q1 <= 7 and 0 <= q2 <= 7): print('Out of galaxy. This shouldn\'t happen.') return k3,b3,s3 = list(map(int,list('%03d' % g[q1,q2]))) if k3 != 0 and s <= 200: print('Combat Area Condition Red') print(' Shields Dangerously Low') k[:,:] = 0 # Clear old Klingon info. q = [] for _ in range(8): row = [] for _ in range(8): row.append(' ') q.append(row) q[s1][s2] = '<*>' # Place Enterprise. for i in range(k3): r1,r2 = emptySector(q) q[r1][r2] = '+++' # Place Klingon. k[i,:] = r1, r2, s9 for i in range(b3): r1,r2 = emptySector(q) q[r1][r2] = '>!<' # Place star base. for i in range(s3): r1,r2 = emptySector(q) q[r1][r2] = ' * ' # Place star. def phasers(): global b3, d, e, g, k, k3, k9, q, s3 if k3 <= 0: print('Short range sensors report no klingons in this quadrant') return if d[3] < 0: print('Phaser control is disabled') return while True: if d[7] < 0: print(' Computer failure hampers accuracy') print('Phasers locked on target. Energy available=%d' % e) x = inputSafe(int, 'Number of units to fire: ') if x <= 0: return if e-x < 0: continue break e -= x klingonAttack() if d[7] < 0: x *= random() for i in range(3): if k[i,2] <= 0: # No Klingon here. continue far = dist(k[i,0], k[i,1], s1, s2) h = (x/k3/far)*(2*random()) k[i,2] = max(0, k[i,2]-h) print('%4d unit hit on Klingon at sector %d,%d (%3d left)' % (h, 1+k[i,0], 1+k[i,1], k[i,2])) if k[i,2] <= 0: print('Klingon at sector %d,%d destroyed ****' % (1+k[i,0],1+k[i,1])) k3 -= 1 k9 -= 1 q[k[i,0]][k[i,1]] = ' ' g[q1,q2] = k3*100 + b3*10 + s3 if k9 <= 0: win() if e < 0: lose() def shields(): global d, e, s if d[6] < 0: print('Shield control is non-operational') return while True: x = inputSafe(int, 'Energy available = %d Number of units to shields: ' % (e+s)) if x <= 0: return if e+s-x >= 0: break e += s - x s = x def srLine(line, p0, p1): print('in: "%s"' % line) spaced = '' for i in range((p1-p0)//3): l0 = 3*i + p0 spaced += ' %3s' % line[l0:l0+3] print('out: "%s"' % spaced) return spaced def srScan(): global cs, d, e, p, q, s, s1, s2 global qs, rs, ss d0=0 # mm - Look for star base in adjacent sectors. for i in range(s1-1, s1+2): for j in range(s2-1, s2+2): if not (0 <= i <= 7 and 0 <= j <= 7): continue if q[i][j] == '>!<': d0 = 1 i = j = 10 # mm - Break from loops. if d0 == 1: # mm - Adjacent to star base. cs = 'Docked' e = 3000 p = 10 print('Shields dropped for docking purposes') s = 0 elif k3 > 0: # mm - Klingons here. cs = 'Red' elif e < e0/10: # mm - Enterprise energy level is low. cs = 'Yellow' else: # mm - Beam down for shore leave! cs = 'Green' if d[1] < 0: print('\n*** Short range sensors are out ***\n') return print('---------------------------------') for i in range(8): for j in range(8): print(' %3s' % q[i][j], end='') if i == 0: print('') elif i == 1: print(' Stardate %5d' % t) elif i == 2: print(' Condition %-6s' % cs) elif i == 3: print(' Quadrant %d,%d' % (q1+1,q2+1)) elif i == 4: print(' Sector %d,%d' % (s1+1,s2+1)) elif i == 5: print(' Energy %d' % e) elif i == 6: print(' Photon Torpedoes %d' % p) elif i == 7: print(' Shields %d' % s) print('---------------------------------') def torpedoes(): global b3, c, d, g, k, k3, k9, p, q, q1, q2, s1, s2, s3 if d[4] < 0: print('Photon tubes are not operational') return if p <= 0: print('All photon torpedoes expended') return while True: c1 = inputSafe(float, 'Torpedo course (1-9): ') if c1 == 0: return if 1 <= c1 < 9: break x1 = -sin((c1 - 1)*pi/4) x2 = cos((c1 - 1)*pi/4) # Unit vector, course c1 is multiple of 45 deg. x,y = s1,s2 # Start at current sector. p -= 1 # Use 1 photon torpedo. print('Torpedo track:') while True: x += x1 y += x2 z1 = int(x + 0.5) z2 = int(y + 0.5) # Nearest sector. if not (0 <= z1 <= 7 and 0 <= z2 <= 7): # Torpedo left quadrant. print('Torpedo missed') klingonAttack() return print('%15s%d,%d' % (' ', x+1, y+1)) if q[z1][z2] != ' ': # Torpedo hit something. break if q[z1][z2] == '+++': # Hit Klingon. print('*** Klingon destroyed ***') k3 -= 1 k9 -= 1 if k9 <= 0: win() for i in range(3): if (z1,z2) == (k[i,0],k[i,1]): break k[i,2] = 0 q[z1][z2] = ' ' g[q1,q2] = k3*100 + b3*10 + s3 elif q[z1][z2] == ' * ': # Hit star. print('You can\'t destroy stars silly') print('Torpedo missed') elif q[z1][z2] == '>!<': # Hit star base. print('*** Star base destroyed *** .......Congratulations') b3 -= 1 q[z1][z2] = ' ' g[q1,q2] = k3*100 + b3*10 + s3 klingonAttack() def vec(ex, ey, tx, ty): theta = 1 + arctan2(-(ty-ey), tx-ex)/(pi/4)%8 dist = sqrt((tx-ex)**2 + (ty-ey)**2) return dist, theta def win(): global k7, t, t0, t7 print('\nThe last Klingon battle cruiser in the galaxy has been destroyed') print('The Federation has been saved !!!\n') print('Your efficiency rating = %d' % round((k7/(t-t0))*1000)) t1 = time.time() print('Your actual time of mission = %d minutes' % round((t1-t7)/60)) sys.exit(0) def sttr1(): # lines12() print(' Star Trek\n') print(' by Mike Mayfield, Centerline Engineering') print(' Total Interaction Game - Orig. 20 Oct 1972\n') a = input('Do you want instructions (they\'re long!)? ').strip().lower() if a == '': a = 'n' if a[0] == 'y': instructions() print('') # ***** Program starts here ***** # lines12() mkGalaxy() mkQuadrant() srScan() while True: a = input('Command: ').strip() if a != '': a = a.lower()[0] if a == '0': course() srScan() elif a == '1': srScan() elif a == '2': lrScan() elif a == '3': phasers() elif a == '4': torpedoes() elif a == '5': shields() elif a == '6': damageControl() elif a == '7': computer() elif a == 'e': lose(True) else: print('') print(' 0 = Set course') print(' 1 = Short range sensor scan') print(' 2 = Long range sensor scan') print(' 3 = Fire phasers') print(' 4 = Fire photon torpedoes') print(' 5 = Shield control') print(' 6 = Damage control report') print(' 7 = Call on library computer') print(' e = End game') print('') if __name__ == '__main__': sttr1()