MathematicaTrek

George V. Woodrow III

From    to  
  
  (The changes to the code are even greater....)

Goals

Move from c-based programming to Mathematica

Move from Problem-solving mode to Communication mode in Mathematica Programming

Rekindle the joy of coding

Introduction

I learned how to program in 1970. In 2010, I decided to celebrate the occasion by rewriting in Mathematica the program I used to learn programming . That program was spacwr.bas, otherwise knows as Star Trek. At the 2012 Wolfram Technology Conference, Luc Barthlet gave a talk “Introduction to Dynamics — Learn to Build a Game”, in which he rewrote Space Invaders in Mathematica.

Bringing the Star Trek game up to date was an even greater challenge, since it was written for a 110 baud ASR 33 teletype and all graphics were ASCII graphics. Over the years, I had updated the program for more modern hardware: a Tektronix 4010 graphics CRT, an Apple ][ using my trusty Trinitron TV, and finally a version for an early Macintosh. I had added new functions without altering the basic game play.

Learning to code in the 1970s

I managed to get a B.A in Mathematics, including a course in numerical analysis, without touching a computer. My university got its first computer in 1968, the year after I graduated. I was introduced to programming in 1970 as a work study student at the University of Pennsylvania Medical school. My principal investigator soon discovered that I knew maths, so a post-doc introduced me to the computer, a DEC PDP-5, soon upgraded to a PDP-10. This was a time-sharing system and had a version of BASIC. This combination meant that my introduction to computing was with an interactive system. I never used punch cards, and did not use a compiled language until I learned to program c in the late 1980s. (In 1970, c was still under development at Bell Labs.)

According to Wikipedia, the young Bill Gates had essentially the same early experience with computers, although he was 13 or 14, instead of 24. I believe that being introduced to computers with an interactive system made a profound difference to Bill Gates, as it did to me.

The PDP-10 used an ASR-33 teletype as its primary console.



It had a paper-tape punch/reader for saving programs. The PDP-10 also had DECTape. The Teletype printed at 110 baud, which meant that a short range sensor scan, which is part of the Star Trek game, could take a minute or so to print out. One of the neat things about the ASR-33 was that it had a real bell, and you could make it ring simply by typing a control-G into a string.

My primary programming jobs were to do mathematical analysis of our experimental data. The PDP-10 had a CalComp plotter, which had a BASIC interface. I used it to generate 3-D ball and stick molecular diagrams for the department. We could make pictures of five or ten atoms. Most programs were less than 100 lines long. The Star Trek program was the largest program I worked on until I started programming using the Macintosh.

As my career developed, I used other computers, a PDP-8 clone, which used a Tektronix 4010 graphics CRT terminal, various HP devices, culminating in an Apple ][. I wrote an interface for an HP plotter, so we could generate high quality graphics with out computer. I got my first Macintosh in 1984, and learned Forth. I finally abandoned BASIC when I learned c in the late 1980s, and started using Mathematica in 1989.

History of the Star Trek program

The Wikipedia article has a lot of lore and history surrounding the Star Trek Program. The original program was written by Mike Mayfield in 1971 for a SDS Sigma 7 computer. It was quickly ported to HP and DEC equipment. I first came across the program when the Wharton School got a new computer that had Star Trek as part of its default distribution. The Medical School computer facility soon got a copy.
I already know how to program BASIC, but virtually everything I had done before this was related to number crunching and plotting data. I learned how to really program by taking apart the Star Trek program and making modifications. When I got access to the Tektronix computer, I learned the rudiments of 3-D graphics by creating a model of the Star Ship Enterprise. (It was hard to come by molecular model coördinates in those days.)
When the Apple ][, TRS-80, and Commodore PET came out, new versions were produced (in BASIC) for these computers. A version, called Super Star Trek appeared in BASIC Computer games.

According to Wikipedia, this game has been ported to a number of languages, including c and FORTRAN. My port to Mathematica is a continuation of this tradition.

Design of the Mathematica Version of Star Trek

When I set out to rewrite Star Trek for Mathematica, I had to decide which version of the program to use as a base. Although I based my early efforts on the spacwr.bas source code I found, I had quickly modified it to do more. In addition, I had implemented a graphical version using a Tektronix 4010 graphics CRT. Over the years, I lost the source code for these versions, so I really only had the version I downloaded from the Internet.

I decided to keep the visual appearance of the displays as close as possible in spirit to the early teletype version. All the displays used a monospaced font, and to imitate the early text-only CRTs, white letters on a black background. It was during beta testing that we decided to add colored text.
I though of retaining the text field method of entering text. That quickly proved to be unworkable, and the major inputs to the program: navigation, weapons, shields, were handled by user interface widgets. I also decided to use a tab pane to switch between the various views: Short Range Scan, Long Range Scan, Galaxy, Damage control.
With the original teletype game, it was possible to use up several feet of paper for each game. Since even the best scrolling field could not duplicate the real estate available with real paper, we used the tab pane so that the user could do the equivalent of looking at an old printout of a short range scan, for example. Dealing with all the messages that the program produces provided an additional challenge. Mathematica can have scroll bars on a pane, but it cannot by default scroll to the bottom of the text. Instead, we ‘top post’ all the messages.

Internally, we wanted to keep the code as close as practical to the BASIC original, while not doing anything that is poor coding in Mathematica. Theoretically, we could have done a line-by-line translation of the code — Mathematica does have a ‘goto’ command, after all. Instead, we made functions and tried to keep the number of variables down to a minimum. We created the project using Dynamic Module so that we would have freedom that Manipulate does not.One of the possible next steps, now that we got working code, is to revise the program to run as a Manipulate.

Moving from BASIC to Mathematica

Programming Paradigms

BASIC has had a reputation among ‘real’ programmers that it primarily teaches poor programming practices. After spending time with the code for Star Trek, I can understand this idea completely. Every variable is global. In the early form of BASIC, there were a limited number of variables, so the same variable was often used for different purposes. There were sub-routines, but there was no data hiding or scoping at all. A subroutine was a way to structure code that might need to be run several times in the course of a program.

A subroutine can be used roughly as a function, but it is possible to execute statements that create side-effects, since the value of global variables can be altered at will.

In many ways, Mathematica can be made to work in much the same way, with some differences. It is possible to create functions that alter global variables not in the function’s parameter list. Since Mathematica has a Goto function, it would be possible to copy the BASIC paradigm almost exactly. One of Mathematica’s strengths is that it can support many different kinds of programming paradigms.

The code presented in this notebook is a first pass at the problem. The first step in any project like this is to get something that works, and Mathematica certainly made it easy to do this. For situations where getting a quick answer, this is probably all that is needed. Certainly, the BASIC program I wrote for the lab in the 1970s were written to solve problems quickly, and the code itself did not matter beyond basic functionality. I suspect that the majority of Mathematica programs written today fall into this category.

However, the Star Trek game goes beyond just basic functionality — then and now. We want a structured program, with a few global variables as possible. The code I present here is a first step in that process. This code makes use of programming paradigms I learned from writing c code on the Macintosh.

C as an Intermediary Language

I spent the last 25 years switching between Mathematica and c programming, specifically c programming on Macintosh computers. I would solve problems using Mathematica, and I would code the results in c for a real-time process control system I developed. Recently, I added Objective c to my repertoire, having completely bypassed c++. 
c is a much stronger language than BASIC, but it is not without its own problems. You can do almost anything in c, including shooting yourself in the foot. With c, I could get close enough to the hardware that I no longer needed to program in assembly language.
What c provides are flexible function calling, and organized variables: structures and classes.
Function calling in Mathematica is very similar to c, with one principal difference: you can pass variables by reference, essentially passing the address of a variable instead of its value. I will discuss the solution to this problem in the next section.
Data structures, as they exist in c, are difficult to implement in Mathematica. For a relatively small program like Star Trek, I used lists, which works, but you have to remember the meaning of each variable in the list.

As far as Mathematica goes, c-style coding is fully supported. The existing code for Star Trek is essentially a Mathematica version of a c port of the original BASIC code. The next version of Star Trek for Mathematica will try to make use of functional programming styles.

Challenges

As discussed above, BASIC and Mathematica have very different design philosophies. In addition, coming to Mathematica from c presented its own difficulties. When it came to designing the UI, I was spoiled by years of using the GUI design tools available with the Macintosh, from ResEdit to Xcode.

Passing variables by Reference

I got help from the Wolfram Community website for this problem. You can use SetAttributes[] to set the attribute of a function to HoldAll. When you do this, then effectively, the passed variable is sent by reference.

Doing this solved my problem. I was able to have functions that modified arrays in place, for example, or that changed the values of variables as a side effect of the primary computation. The downside is that I cannot test these functions by passing numbers or uninitialized symbols as parameters. I also found that it works best to define these functions within the DynamicModule that is the core of the program.
I am sure that HoldAll is a perfectly good Mathematica paradigm. In the next version of Star Trek, I will see if I can rewrite the code so that its use will not be needed. However, my goal is to write clean, robust code, and if this require HoldAll, or even Goto, I will use it. Mathematica’s strength is that it is very flexible.

User Interface Design

By far, the largest challenge I faced in creating the Mathematica version of Star Trek was creating the user interface. The only way to create a UI in Mathematica is programmaticly. When programming the Mac, I always used a GUI creation program, starting with ResEdit in 1987 and ending with Interface Builder in Xcode. There is no equivalent in Mathematica.

In addition, while you can set the size of the widgets (sliders, checkboxes, plots, etc.) programmaticly, you cannot directly specify where the widgets go, relative to the window, enclosing panel, etc. Mathematica places these elements in a ‘pleasing’ way. You can set an option to have the widget centered, left, right, top or bottom of the enclosing object.

For short programs like Demonstrations, this is fine. For beginners, having a slider appear with a default position and size is just one less thing to worry about.

For more involved interfaces, things get more complicated. It is possible to use panes and panels to specify the size of enclosing objects, and Grid, Column, and Row to organize graphical objects, but there is a lot of trial and error.

While I was preparing this talk, I found another difficulty. Unless you are careful to specify all font sizes explicitly, changing style sheets or changing from Working to Slide Show screen environment will alter font sizes and any widgets that have a type component.

I also found that it was difficult to embed a custom control that is encapsulated with a function. The custom Locator panels that I used to set direction for navigation and photon torpedo aiming were initially inside a function. The code is currently used twice. This is not an issue for this program, but if I had dozens of custom controls, I would have to find a better way.

I had to use a custom control to set a direction instead of a gauge, since gauges all have angular limits. I needed to have a control where the user could drag the arrow indefinitely in either direction without coming to a stop. Fortunately, the sample code for LocatorPane[] provided the basis for the control.

Compared to the current set of widgets available in Xcode (and presumably in Windows), some widgets are missing. A true circular slider is one. Stepper, DatePicker, and ComboBox are others. Most of these can be created in Mathematica code, to be sure, as we did with the circular slider.

The new gauges, introduced in version 9, provide a lot of design flexibility, and in many cases, provides customizations not available by default in Xcode. The only option missing is the ability to specify the location of the top, left corner of the widget relative to the enclosing pane, panel, or window. However, careful use of options like position and padding in Grids, Columns and Rows can result in acceptable performance.

The current Star Trek code works, but it was built incrementally, from the inside out. Now that it is finished, I am sure that there are redundant specifications that could be removed.

Neat Code Solutions

Creating a Quadrant

In the BASIC Star Trek game, a new quadrant is created every time the Enterprise enters one. The galaxy has a number that represents the number of Klingons, Star bases, and stars for the quadrant. When a subroutine is called, the program uses a random number generator to populate the 8 by 8 grid that makes up the quadrant. The problem is that the program has to check to see of the sector coördinate is already occupied, and if it is, generate another random number. (If you are interested, the code is in the source code I included in this notebook, from lines 910 through 1260.)

For the Mathematica version, I just made a list of the 64 elements, created by joining lists for Klingons, starbases, stars, the Enterprise, and the remaining empty sectors, use RandomSample[] (without specifying a number) to get a random permutation, and then using Partition to make an 8 by 8 array. We then use Position[] to get the coördinates of the Enterprise, star base (if any), and klingons.

createQuadrantA[galaxyElement_] := Module[{quadData, nK, nB, nS, entCoord, baseCoord, klingons, theQuadrant, i}, 
  quadData = Take[IntegerDigits[1000 + galaxyElement], -3];
  {nK, nB, nS} = quadData;
  theQuadrant = Partition[RandomSample[Join[Table[i, {i, 1, nK}], Table["B", {nB}], Table["*", {nS}], {"E"}, Table["", {63 - Total[quadData]}]]], 8];
  
  entCoord = Position[theQuadrant, "E"][[1]];
  If[nB > 0, baseCoord = Position[theQuadrant, "B"][[1]], baseCoord = {0, 0}];
  
  klingons = Table[{{1, 1}, 0}, {3}];
  i = 1;
  While[i <= nK, (klingons[[i, 1]] = Position[theQuadrant, i][[1]];
    klingons[[i, 2]] = 200.0;);
   i++];
  {theQuadrant, nK, nB, nS, entCoord, baseCoord, klingons}
  ]

Custom Controls

As I indicated previously, I could not use a circular gauge because I wanted the user to be able to move the pointer at will. The following code shows how we did this, which is really just a gussied up version of the sample code from Locator Panel. (Harry Calkins helped, too.)

DynamicModule[{pt={0,1}},LocatorPane[Dynamic[pt],Graphics[{White,Disk[],LightGray,Thickness[0.11],Circle[{0,0}, 0.8],Thin, Black,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.1Sin[i π/4], .1Cos[i π/4]}}], {i, 0, 7}], Thick,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.9Sin[i π/4], .9Cos[i π/4]}}], {i, 0, 7}],Red,Disk[{0,0}, .1],Arrowheads[Medium],Arrowheads[.15],Arrow[{{0,0},If[Dynamic[Normalize[pt]]=={0,0},{0,1},Dynamic[Normalize[pt]]]}]},ImageSize->160,Frame->True,FrameTicks->None,PlotRange->1.3],{{-1,-1},{1,1}},Appearance->None, Background -> Black]]

While it hardly qualifies as a ‘custom control’, I set the appearance of all the buttons I used to ‘Frameless’, and used a colored square at the beginning of the button’s string to imitate the buttons on the console of the original Enterprise. From close observation of the Enterprise bridge, I can see that none of their buttons were labeled, a level of realism I was unwilling to duplicate. The UI for the Enterprise was really bad.

MathematicaTrek

StarTrekGame[]

Next Steps

Star Trek 1.1

The obvious first step is to clean up the current code and make sure that there are no bugs. There are redundant styles applied to some parts of the code. The code is already at version 1.0.1, so this process in close to being complete.

The program needs to split into two tracks. The first track has a goal of making the code a single Manipulate[], so that it can be deployed as a CDF, and if the size requirement is ever relaxed, as a Demonstration. I would also like to be able to distribute a version of Star Trek for Wolfram Cloud.

After I discovered that making functions inside a DynamicModule works, the need to structure the code can be met internal to the Manipulate[]. 

The second track has an almost opposite goal: to build a package for the code. Future versions of Star Trek will probably be too complex to implement inside a Manipulate[], and building a package may be the best way to deploy the program. I intend to use Workbench to accomplish this goal. I have used Workbench for some toy projects, but never mastered its use. I will also be able to see how the debugger works, and perhaps implement some form of version control.

After all these changes, the program(s) will work pretty much as the current version does.

Star Trek 2.0

For the next version of Star Trek, we will make structural changes to the program. The primary change will be the creation of all the quadrants in the galaxy at the start of the game.
  
The consequences of this change will alter most aspects of the program. For example, navigation will be more precise. If the Enterprise is at the right of a sector and travels East one unit at impulse, it should end up at the left side of the next quadrant. The Enterprise will need to see if that sector is occupied, which means that the short - range scan will need to change. If we make the short - range scan centered on the Enterprise, what happens if there are Klingons in the quadrant that are not visible in the short range scan?
  
Creating the Galaxy will have to change as well. The way the current program works, Klingons have a tendency to be grouped. If we just put Klingons at random in a 64 by 64 grid, they will not likely be clumped. The algorithm that creates the galaxy will need to change.
  
In this version, we may add some new game - play options. One of the things I did for the BASIC version was to add the Organian planet. James Blish' s Star Trek novel Spock Must Die! had come out in 1970, I added its scenario to my game. The Organian planet had been shielded by the Klingons, which is why they could launch a war. In the game, you had the option of slogging it out with the Klingons or finding Organia and freeing it. From my reading, other people who have ported the BASIC game have made similar changes.
   
The basic text - based displays will remain, and the underlying organization will be the 64 by 64 array.
  
In this version, the controls will be mapped to a standard game controller. (This is very easy to do with Mathematica.)

Star Trek 3D

In this version, we dispense with the text-based graphics and locate objects in 3-space.

When I made a version of Star Trek for the Tektronix monitor, I made little images of the enterprise and the Klingon vessel, and when you shot phasers or photons, there were graphical representations. I couldn’t do much more at the time, since the computer (a PDP-8 clone) did not have a lot of RAM.

With Mathematica, I can make 3-D models of the spaceships, the star bases and stars. From what I have read, some 1980s-era commercial offshoots of the original game did something similar. Some other variants allow the Klingons to move, and we can implement this as well.

Implementing this functionality will be a good exercise in 3-D graphics. It is likely that this version of Star Trek will not get much further than this. Navigation, aiming photon torpedoes, etc., become a lot more complicated in 3-space, even if we limit the range of the z axis. Trying to update the details of game play from the original series is problematic, since the way things work, and the visual components, are inconsistent.

Final thoughts

For many years, I used Mathematica in a manner similar to the way I used BASIC in my laboratory work. I wrote short programs to accomplish specific tasks. In the case of Mathematica, I did the R&D for my large process control project, but did the implementation in c.

For both languages, Star Trek was my break-out project. It was the first largish project I had that was not work related, and which extended beyond simple numerical analysis and display.

I have read and made use of many books on Mathematica programming, from Roman Maeder’s to Paul Wellin’s. They are all excellent books, but they finish just at the point where more complex projects, such as the Star Trek game, begin. For a wide class of Mathematica users, this does not matter at all. For doing experimental calculations, making demonstrations, or just doing homework, the additional level of complexity simply do not matter.

Clearly, for developers at Wolfram, who use Mathematica to develop Wolfram|Alpha and Mathematica itself, projects such as mine are just trivial. They are versed in using Workbench, and have experts down the hall who can help with any issues. There are other people, many of them at this conference, who have acquired a similar level of expertise.

I think that there is a level of Mathematica programmer in between these levels. Making the transition from ‘casual user’ to developer is never an easy path, and I think that there is an opportunity to provide educational resources to help people in this position.

When I started programming, I was fortunate to learn on a time-sharing system and use BASIC. One could argue that FORTRAN was a better option, but my primary job was experimental biochemist, not programmer, and I was able to do what I needed to do with BASIC without becoming a computer scientist.

Today, Mathematica can easily fill the role that BASIC did for me and many other people in the same situation I was in 1970. It is an exquisite tool.

When I started with Mathematica, I used it for R&D, but wrote production code in c. For early versions of Mathematica, and the hardware available in the 1990s, this was the only viable choice for a mission critical, real-time system. However, today’s Mathematica is more than capable of handling almost everything that I had to write c code for. In fact, my last proposal to my former company was to simply implement a modern database, and use Mathematica exclusively for number crunching and user interactions.

The only technical difficulty I saw in this project was the lack of educational resources needed to make the transition. Programming is largely a matter of trial and error, but in a commercial environment, it is important to contain the chaos as much as possible.

My Star Trek for Mathematica project is a really silly example of one way to make the transition. I had made this transition before, when I learned c, so it was mostly just a matter of deciding that this was something that I wanted to do. I hope that others can find my experience to be useful.

References

Wikipedia article:

http://en.wikipedia.org/wiki/Star_Trek_(text_game)

Links to source code for various implementations of the Star Trek Game:

http://www.dunnington.u-net.com/public/startrek/

Information on the ASR-33 Teletype, including videos:

http://www.pdp8.net/asr33/asr33.shtml

http://www.pdp8.net/asr33/videos.shtml

BASIC Code

The following is the source code I used to create the Mathematica version of Star Trek.

100     REM *** PROGRAM SIMULATES TV PROGRAM STARTREK
110     REM *** WRITTEN BY MIKE MAYFIELD, CENTERLINE ENGINEERING
170	RANDOMIZE
180     PRINT “                  * * *  STAR TREK  * * *”:PRINT
200	INPUT “DO YOU WANT INSTRUCTIONS (THEY’RE LONG!)”;A$
210	IF A$<>”YES” THEN 230
220    GOTO 5820
230	REM *** PROGRAM BEGINS HERE
240     Z$,R$,Q$=”                                                                        “
260	DIM G(8,8),C(9,2),K(3,3),N(3),Z(8,8)
290	T0,T=INT(RND(1)*20+20)*100
300	T9=30:D0=0:E0,E=3000:P0,P=10:S9=200:S,H8=0
360 	DEF FND(D)=SQR((K(I,1)-S1)**2+(K(I,2)-S2)**2)
370	Q1=INT(RND(1)*8+1)
380	Q2=INT(RND(1)*8+1)
390	S1=INT(RND(1)*8+1)
400	S2=INT(RND(1)*8+1)
410	T7=TIME(0)
420	C(2,1),C(3,1),C(4,1),C(4,2),C(5,2),C(6,2)=-1
430	C(1,1),C(3,2),C(5,1),C(7,2),C(9,1)=0
440	C(1,2),C(2,2),C(6,1),C(7,1),C(8,1),C(8,2),C(9,2)=1
450	MAT D=ZER
460	D$=”WARP ENGINESS.R. SENSORSL.R. SENSORSPHASER CNTRL”
470	D$=D$+“PHOTON TUBESDAMAGE CNTRL”
480	E$=”SHIELD CNTRLCOMPUTER”
490	B9,K9=0
491	REM *** SETS UP WHAT EXISTS IN GALAXY
500	FOR I=1TO8
510	FOR J=1TO8
520	R1=RND(1)
530	IF R1>.98 THEN 580
540	IF R1>.95 THEN 610
550	IF R1>.8 THEN 640
560	K3=0:GOTO 660
580	K3=3:K9=K9+3:GOTO 660
610	K3=2:K9=K9+2:GOTO 660
640	K3=1:K9=K9+1
660	R1=RND(1)
670	IF R1>.96 THEN 700
680	B3=0:GOTO 720
700	B3=1:B9=B9+1
720	S3=INT(RND(1)*8+1)
730	G(I,J)=K3*100+B3*10+S3
740	Z(I,J)=0
750	NEXT J
760	NEXT I
770	K7=K9
775	PRINT:PRINT
780	PRINT”YOU MUST DESTROY”K9” KLINGONS IN”T9” STARDATES WITH “B9” STARBASES”
790	IF B9>0 THEN 810
800	G(6,3)=114
810	K3,B3,S3=0
820	IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 920
830	X=G(Q1,Q2)*.01
840	K3=INT(X)
850	B3=INT((X-K3)*10)
860	S3=G(Q1,Q2)-INT(G(Q1,Q2)*.1)*10
870	IF K3=0 THEN 910
880	IF S>200 THEN 910
890	PRINT”COMBAT AREA      CONDITION RED”
900	PRINT”   SHIELDS DANGEROUSLY LOW”
910	MAT K=ZER
920	FOR I=1TO3
930	K(I,3)=0
940	NEXT I
950	Q$=Z$:R$=Z$
970	S$=MID(Z$,1,48)
971	REM *** PUT ENTERPRISE SOMEWHERE
980	A$=”<*>”
990	Z1=S1
1000	Z2=S2
1010	GOSUB 5510
1020	FOR I=1TOK3
1030 GOSUB 5380
1031	REM *** PUT KLINGONS SOMEWHERE
1040	A$=”+++“
1050	Z1=R1
1060	Z2=R2
1070	GOSUB 5510
1080	K(I,1)=R1: K(I,2)=R2: K(I,3)=S9
1110	NEXT I
1120	FOR I=1TOB3
1130	GOSUB 5380
1131	REM *** PUT STARBASE(S) SOMEWHERE
1140	A$=”>!<”: Z1=R1: Z2=R2
1170	GOSUB 5510
1180	NEXT I
1190	FOR I=1TOS3
1200	GOSUB 5380
1201	REM *** PUT STARS SOMEWHERE
1210	A$=” * “: Z1=R1: Z2=R2
1240	GOSUB 5510
1250	NEXT I
1260	GOSUB 4120
1270	INPUT “COMMAND:”;A
1290	IF A=0 GOTO 1410
1291	IF A=1 GOTO 1260
1292	IF A=2 GOTO 2330
1293	IF A=3 GOTO 2530
1294	IF A=4 GOTO 2800
1295	IF A=5 GOTO 3460
1296	IF A=6 GOTO 3560
1297	IF A=7 GOTO 4630
1298	IF A=8 GOTO 6510
1310	PRINT:PRINT”   0 = SET COURSE”
1320	PRINT”   1 = SHORT RANGE SENSOR SCAN”
1330	PRINT”   2 = LONG RANGE SENSOR SCAN”
1340	PRINT”   3 = FIRE PHASERS”
1350	PRINT”   4 = FIRE PHOTON TORPEDOES”
1360	PRINT”   5 = SHIELD CONTROL”
1370	PRINT”   6 = DAMAGE CONTROL REPORT”
1380	PRINT”   7 = CALL ON LIBRARY COMPUTER”
1390	PRINT”   8 = END THE CONTEST”:PRINT
1400	GOTO 1270
1401	REM *** COURSE CONTROL CODE BEGINS HERE
1410	INPUT “COURSE (1-9):”;C1
1430	IF C1=0 THEN 1270
1440	IF C1<1 OR C1>9 THEN 1410
1450	INPUT “WARP FACTOR (0-8):”;W1
1470	IF W1<0 OR W1>8 THEN 1410
1480	IF D(1)>=0 OR W1<=.2 THEN 1510
1490	PRINT “WARP ENGINES ARE DAMAGED, MAXIMUM SPEED = WARP .2”
1500	GOTO 1410
1510	IF K3<=0 THEN 1560
1520	GOSUB 3790
1530	IF K3<=0 THEN 1560
1540	IF S<0 THEN 4000
1550	GOTO 1610
1560    IF E>0 THEN 1610
1570    IF S<1 THEN 3920
1580	PRINT “YOU HAVE”E” UNITS OF ENERGY”
1590	PRINT “SUGGEST YOU GET SOME FROM YOUR SHIELDS WHICH HAVE”S” UNITS LEFT”
1600 	GOTO 1270
1610	FOR I=1TO8: IF D(I)>=0 THEN 1640
1611	REM *** FIX ANY DAMAGED DEVICE
1630	D(I)=D(I)+1
1640	NEXT I
1650	IF RND(1)>.2 THEN 1810
1660	R1=INT(RND(1)*8+1)
1670	IF RND(1)>= .5 THEN 1750
1680	D(R1)=D(R1)-(RND(1)*5+1)
1690	PRINT:PRINT “DAMAGE CONTROL REPORT:”;
1710	GOSUB 5610
1720	PRINT” DAMAGED”:PRINT:GOTO 1810
1750	D(R1)=D(R1)+(RND(1)*5+1)
1760	PRINT:PRINT “DAMAGE CONTROL REPORT:”;
1780	GOSUB 5610
1790	PRINT” STATE OF REPAIR IMPROVED”:PRINT
1810	N=INT(W1*8):A$=”   “:Z1=S1:Z2=S2
1850	GOSUB 5510
1860	X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
1870	X=S1:Y=S2
1890	X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
1900	X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1))
1910	FOR I=1TON:S1=S1+X1:S2=S2+X2
1940	IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 2170
1950	S8=S1*24+S2*3-26: IF S8>72 THEN 1990
1970	IF MID(Q$,S8,3)=”   “ THEN 2070
1980	GOTO 2030
1990	IF S8>144 THEN 2020
2000	IF MID(R$,S8-72,3)=”   “ THEN 2070
2010	GOTO 2030
2020	IF MID(S$,S8-144,3)=”   “ THEN 2070
2030	PRINT”WARP ENGINES SHUTDOWN AT SECTOR “S1”,”S2” DUE TO BAD NAVAGATION”
2040	S1=S1-X1:S2=S2-X2:GOTO 2080
2070	NEXT I
2080	A$=”<*>”:Z1=S1:Z2=S2
2110	GOSUB 5510
2120	E=E-N+5:IF W1<1 THEN 2150
2140	T=T+1
2150	IF T>T0+T9 THEN 3970
2160	GOTO 1260
2170	X=Q1*8+X+X1*N:Y=Q2*8+Y+X2*N
2190	Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8):S2=INT(Y-Q2*8)
2230	IF S1<>0 THEN 2260
2240	Q1=Q1-1:S1=8
2260	IF S2<>0 THEN 2290
2270	Q2=Q2-1:S2=8
2290	T=T+1:E=E-N+5
2310	IF T>T0 + T9 THEN 3970
2320	GOTO 810
2321	REM *** LONG RANGE SENSON SCAN CODE BEGINS HERE
2330	IF D(3)>=0 THEN 2370
2340	PRINT “LONG RANGE SENSORS ARE INOPERABLE”
2360	GOTO 1270
2370	PRINT”LONG RANGE SENSOR SCAN FOR QUADRANT “Q1”,”Q2
2380	PRINT”-------------------“
2390	FOR I=Q1-1 TO Q1+1
2400	MAT N=ZER
2410	FOR J=Q2-1 TO Q2+1
2420	IF I<1 OR I>8 OR J<1 OR J>8 THEN 2460
2430	N(J-Q2+2)=G(I,J)
2440	IF D(7)<0 THEN 2460
2450	Z(I,J)=G(I,J)
2460	NEXT J
2470	P1$=”: ### : ### : ### :”
2471	PRINT USING P1$,N(1),N(2),N(3)
2480	PRINT”-------------------“
2490	NEXT I
2500	GOTO 1270
2501	REM *** PHASER CONTROL CODE BEGINS HERE
2530	IF K3<=0 THEN 3670
2540	IF D(4)>=0 THEN 2570
2560	GOTO 1270
2570	IF D(7)>=0 THEN 2590
2580	PRINT “ COMPUTER FAILURE HAMPERS ACCURACY”
2590	PRINT”PHASERS LOCKED ON TARGET.  ENERGY AVAILABLE=”E
2600	INPUT “NUMBER OF UNITS TO FIRE:”;X
2620	IF X<=0 THEN 1270
2630	IF E-X<0 THEN 2570
2640	E=E-X
2650	GOSUB 3790
2660	IF D(7)>=0 THEN 2680
2670	X=X*RND(1)
2680	FOR I=1TO3
2690	IF K(I,3)<=0 THEN 2770
2700	H=INT((X/K3/FND(0))*(2*RND(1)))
2710	K(I,3)=K(I,3)-H
2720	PRINTH” UNIT HIT ON KLINGON AT SECTOR “K(I,1)”,”K(I,2);
2721	PRINT”    (“K(I,3)” LEFT)”
2740	IF K(I,3)>0 THEN 2770
2750	GOSUB 3690
2760	IF K9<=0 THEN 4040
2770	NEXT I
2780	IF E<0 THEN 4000
2790	GOTO 1270
2791	REM *** PHOTON TORPEDO CODE BEGINS HERE
2800	IF D(5)>=0 THEN 2830
2810	PRINT “PHOTON TUBES ARE NOT OPERATIONAL”
2820	GOTO 1270
2830	IF P>0 THEN 2860
2840	PRINT “ALL PHOTON TORPEDOES EXPENDED”
2850	GOTO 1270
2860	INPUT “TORPEDO COURSE (1-9):”;C1
2880	IF C1=0 THEN 1270
2890	IF C1<1 OR C1>=9 THEN 2860
2900	X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
2910	X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1))
2920	X=S1:Y=S2:P=P-1
2950	PRINT “TORPEDO TRACK:”
2960	X=X+X1:Y=Y+X2
2980	IF X<1 OR X>=9 OR Y<1 OR Y>=9 THEN 3420
2990	PRINT”               “X”,”Y
3010    A$=”   “:Z1=X:Z2=Y
3040	GOSUB 5680
3050	IF Z3=0 THEN 3070
3060	GOTO 2960
3070	A$=”+++“:Z1=X:Z2=Y
3100	GOSUB 5680
3110	IF Z3=0 THEN 3220
3120	PRINT “*** KLINGON DESTROYED ***”
3130	K3=K3-1:K9=K9-1
3150	IF K9<=0 THEN 4040
3160	FOR I=1TO3:IF INT(X)<>K(I,1) THEN 3190
3180	IF INT(Y)=K(I,2) THEN 3200
3190	NEXT I
3200	K(I,3)=0:GOTO 3360
3220	A$=” * “:Z1=X:Z2=Y
3250	GOSUB 5680
3260	IF Z3=0 THEN 3290
3270	PRINT “YOU CAN’T DESTROY STARS, SILLY”
3280	GOTO 3420
3290	A$=”>!<”:Z1=X:Z2=Y
3320	GOSUB 5680
3330	IF Z3=0 THEN 2960
3340	PRINT “*** STAR BASE DESTROYED *** .......CONGRATULATIONS”
3350	B3=B3-1
3360	A$=”   “:Z1=X:Z2=Y
3390	GOSUB 5510
3400	G(Q1,Q2)=K3*100+B3*10+S3
3410	GOTO 3430
3420	PRINT “TORPEDO MISSED”
3430	GOSUB 3790
3440	IF E<0 THEN 4000
3450	GOTO 1270
3451	REM *** SHIELD CONTROL CODE BEGINS HERE
3460	IF D(7)>=0 THEN 3490
3470	PRINT “SHIELD CONTROL IS NON-OPERATIONAL”
3480	GOTO 1270
3490	PRINT “ENERGY AVAILABLE =”E+S;
3500	INPUT “   NUMBER OF UNITS TO SHIELDS:”;X
3510	IF X<=0 THEN 1270
3520	IF E+S-X<0 THEN 3490
3530	E=E+S-X:S=X
3550	GOTO 1270
3551	REM *** DAMAGE CONTROL REPORT CODE BEGINS HERE
3560	IF D(6)>=0 THEN 3590
3570	PRINT “DAMAGE CONTROL REPORT IS NOT AVAILABLE”
3580	GOTO 1270
3590	PRINT:PRINT “DEVICE        STATE OF REPAIR”
3610	FOR R1=1TO8
3620	GOSUB 5610
3630	PRINTD(R1)
3640	NEXT R1:PRINT
3660	GOTO 1270
3670	PRINT”SHORT RANGE SENSORS REPORT NO KLINGONS IN THIS QUADRANT”
3680	GOTO 1270
3690	PRINT “KLINGON AT SECTOR “K(I,1)”,”K(I,2)”DESTROYED ***”
3710	K3=K3-1:K9=K9-1:A$=”   “:Z1=K(I,1):Z2=K(I,2)
3760	GOSUB 5510
3770	G(Q1,Q2)=K3*100+B3*10+S3
3780	RETURN
3790	IF C$<>”DOCKED” THEN 3820
3800	PRINT “STAR BASE SHIELDS PROTECT THE ENTERPRISE”
3810	RETURN
3820    IF K3<=0 THEN 3910
3830	FOR I=1TO3:IF K(I,3)<=0 THEN 3900
3850	H=INT((K(I,3)/FND(0))*(2+RND(1))):S=S-H
3870	PRINTH” UNIT HIT ON ENTERPRISE AT SECTOR “K(I,1)”,”K(I,2);
3871	PRINT”     (“S” LEFT)”
3890	IF S<0 THEN 4000
3900	NEXT I
3910	RETURN
3920	PRINT “THE ENTERPRISE IS DEAD IN SPACE.  IF YOU SURVIVE ALL IMPENDING”
3930	PRINT “ATTACKS YOU WILL BE DEMOTED TO THE RANK OF PRIVATE”
3940	IF K3<=0 THEN 4020
3950	GOSUB 3790
3960	GOTO 3940
3970	PRINT:PRINT “IT IS STARDATE”T
3990	GOTO 4020
3991	REM *** NO ENERGY LEFT
4000	PRINT:PRINT”THE ENTERPRISE HAS BEEN DESTROYED.  THE FEDERATION WILL BE CONQUERED”
4020	PRINT “THERE ARE STILL “K9” KLINGON BATTLE CRUISERS”
4030    PRINT:PRINT:PRINT:PRINT “YOU GET ANOTHER CHANCE....”:GOTO 230
4040	PRINT:PRINT”THE LAST KLINGON BATTLE CRUISER IN THE GALAXY HAS BEEN DESTROYED”
4050	PRINT”THE FEDERATION HAS BEEN SAVED!!!!!”:PRINT
4075	E5=((K7/(T-T0))*1000)
4080	PRINT “YOUR EFFICIENCY RATING =”E5
4100	PRINT”YOUR ACTUAL TIME OF MISSION =”INT((TIME(0)-T7)/60);” MINUTES”
4105	PRINT:PRINT:PRINT
4106	INPUT”DO YOU WANT TO TRY AGAIN”;R$
4107	IF R$ = “YES” THEN 230
4110	GOTO 6510
4111	REM *** SHORT RANGE SENSOR SCAN AND STARTING POINT CODE
4120	FOR I=S1-1TO S1+1
4130	FOR J=S2-1TO S2+1
4140	IF I<1 OR I>8 OR J<1 OR J>8 THEN 4200
4150	A$=”>!<”:Z1=I:Z2=J
4180	GOSUB 5680
4190	IF Z3=1 THEN 4240
4200	NEXT J
4210	NEXT I
4220	D0=0:GOTO 4310
4240	D0=1:C$=”DOCKED”:E=3000:P=10
4280	PRINT “SHIELDS DROPPED FOR DOCKING PURPOSES”
4290	S=0:GOTO 4380
4310	IF K3>0 THEN 4350
4320	IF E<E0 *.1 THEN 4370
4330	C$=”GREEN”
4340	GOTO 4380
4350	C$=”RED”:GOTO 4380
4370	C$=”YELLOW”
4380	IF D(2)>=0 THEN 4430
4390	PRINT:PRINT”*** SHORT RANGE SENSORS ARE OUT ***”:PRINT
4420	GOTO 4530
4430	O1$=”---------------------------------“
4435	PRINT USING O1$
4440	O2$=” \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \”
4445	PRINT USING O2$,MID(Q$,1,3),MID(Q$,4,3),MID(Q$,7,3),

		MID(Q$,10,3),MID(Q$,13,3),MID(Q$,16,3),MID(Q$,19,3),

		MID(Q$,22,3)
4450	O3$=O2$+“        STARDATE        #####”
4455	PRINT USING O3$,MID(Q$,25,3),MID(Q$,28,3),MID(Q$,31,3),

		MID(Q$,34,3),MID(Q$,37,3),MID(Q$,40,3),

		MID(Q$,43,3),MID(Q$,46,3),T
4460	O4$=O2$+“        CONDITION        \     \”
4465	PRINT USING O4$,MID(Q$,49,3),MID(Q$,52,3),MID(Q$,55,3),

		MID(Q$,58,3),MID(Q$,61,3),MID(Q$,64,3),MID(Q$,67,3),

		MID(Q$,70,3),C$
4470	O5$=O2$+“        QUADRANT         #”
4475	PRINT USING O5$,MID(R$,1,3),MID(R$,4,3),MID(R$,7,3),

		MID(R$,10,3),MID(R$,13,3),MID(R$,16,3),MID(R$,19,3),

		MID(R$,22,3),Q1;
4476	PRINT “,”;Q2
4480	O6$=O2$+“        SECTOR           #”
4485	PRINT USING O6$,MID(R$,25,3),MID(R$,28,3),

		MID(R$,31,3),MID(R$,34,3),MID(R$,37,3),MID(R$,40,3),

		MID(R$,43,3),MID(R$,46,3),S1;
4486	PRINT “,”;S2
4490	O7$=O2$+“        TOTAL ENERGY   ######”
4495	PRINT USING O7$,MID(R$,49,3),MID(R$,52,3),MID(R$,55,3),

		MID(R$,58,3),MID(R$,61,3),MID(R$,64,3),MID(R$,67,3),

		MID(R$,70,3),E
4500	O8$=O2$+“        PHOTON TORPEDOES  ###”
4505	PRINT USING O8$,MID(S$,1,3),MID(S$,4,3),MID(S$,7,3),MID(S$,10,3),

		MID(S$,13,3),MID(S$,16,3),MID(S$,19,3),MID(S$,22,3),P
4510	O9$=O2$+“        SHIELDS        ######”
4515	PRINT USING O9$,MID(S$,25,3),MID(S$,28,3),MID(S$,31,3),

		MID(S$,34,3),MID(S$,37,3),MID(S$,40,3),MID(S$,43,3),

		MID(S$,46,3),S
4520	PRINT USING O1$
4530	RETURN
4620	REM *** LIBRARY COMPUTER CODE BEGINS HERE
4630	IF D(8)>=0 THEN 4660
4640	PRINT “COMPUTER DISABLED”:GOTO 1270
4660	INPUT “COMPUTER ACTIVE AND AWAITING COMMAND:”;A
4680	IF A=0 GOTO 4740
4681	IF A=1 GOTO 4830
4682	IF A=2 GOTO 4880
4690	PRINT “FUNCTIONS AVAILABLE FROM COMPUTER”
4700	PRINT “   0 = CUMULATIVE GALACTIC RECORD”
4710	PRINT “   1 = STATUS REPORT”
4720	PRINT “   2 = PHOTON TORPEDO DATA”
4730	GOTO 4660
4731	REM *** CUMULATIVE GALACTIC RECORD CODE BEGINS HERE
4740	PRINT”COMPUTER RECORD OF GALAXY FOR QUADRANT “Q1”,”Q2
4760	PRINT”     1     2     3     4     5     6     7     8”
4770	PRINT”   ----- ----- ----- ----- ----- ----- ----- -----“
4780	FOR I=1TO8
4790	N1$=”#   ###   ###   ###   ###   ###   ###   ###   ###”
4795	PRINT USING N1$,I,Z(I,1),Z(I,2),Z(I,3),Z(I,4),Z(I,5),Z(I,6),

		Z(I,7),Z(I,8)
4800	PRINT”   ----- ----- ----- ----- ----- ----- ----- -----“
4810	NEXT I
4820	GOTO 1270
4821	REM *** STATUS REPORT CODE BEGINS HERE
4830	PRINT “   STATUS REPORT”
4840	PRINT “NUMBER OF KLINGONS LEFT =”K9
4850	V5=(T0+T9)-T
4851	PRINT “NUMBER OF STARDATES LEFT =”;V5
4860	PRINT “NUMBER OF STARBASES LEFT =”B9
4870	GOTO 3560
4880	PRINT:H8=0
4881	REM *** PHOTON TORPEDO DATA CODE BEGINS HERE
4900	FOR I=1TO3
4910	IF K(I,3)<=0 THEN 5260
4920	C1=S1:A=S2:W1=K(I,1):X=K(I,2)
4960	GOTO 5010
4970	PRINT”YOU ARE AT QUADRANT ( “Q1”,”Q2” )  SECTOR ( “S1”,”S2” )”
4990	INPUT “SHIP AND TARGET COORDINATES ARE:”;C1,A,W1,X
5010	X=X-A:A=C1-W1
5030	IF X<0 THEN 5130
5031	IF A<0 THEN 5190
5050	IF X>0 THEN 5070
5051	IF A=0 THEN 5150
5070	C1=1
5080	IF ABS(A) <= ABS(X) THEN 5110
5085	V5=C1+(((ABS(A)-ABS(X))+ABS(A))/ABS(A))
5090	PRINT “DIRECTION =”V5
5100	GOTO 5240
5110	PRINT “DIRECTION =”C1+(ABS(A)/ABS(X))
5120	GOTO 5240
5130	IF A>0 THEN 5170
5140	IF X=0 THEN 5190
5150	C1=5:GOTO 5080
5170	C1=3:GOTO5200
5190	C1=7
5200	IF ABS(A)>=ABS(X) THEN 5230
5210	PRINT “DIRECTION =”C1+(((ABS(X)-ABS(A))+ABS(X))/ABS(X))
5220	GOTO 5240
5230	PRINT “DIRECTION =”C1+(ABS(X)/ABS(A))
5240	PRINT “DISTANCE =”SQR(X**2+A**2)
5250	IF H8=1 THEN 5320
5260	NEXT I
5270	H8=0
5280	INPUT “DO YOU WANT TO USE THE CALCULATOR”;A$
5300	IF A$=”YES” THEN 4970
5310	IF A$<>”NO” THEN 5280
5320	GOTO 1270
5321	REM *** END OF LIBRARY COMPUTER CODE
5380	R1=INT(RND(1)*8+1):R2=INT(RND(1)*8+1):A$=”   “:Z1=R1:Z2=R2
5430	GOSUB 5680
5440	IF Z3=0 THEN 5380
5450	RETURN
5510	REM *** INSERTION IN STRING ARRAY FOR QUADRANT ***
5520	S8=Z1*24+Z2*3-26:IF S8>72 THEN 5560
5540    Q$=LEFT(Q$,S8-1)+A$+RIGHT(Q$,S8+3)
5550	GOTO 5600
5560	IF S8>144 THEN 5590
5570    R$=LEFT(R$,S8-73)+A$+RIGHT(R$,S8-69)
5580	GOTO 5600
5590    S$=LEFT(S$,S8-145)+A$+RIGHT(S$,S8-141)
5600	RETURN
5610	REM *** PRINTS DEVICE NAME FROM ARRAY***
5620	S8=R1*12-11:IF S8>72 THEN 5660
5640	PRINT MID(D$,S8,11),:GOTO 5670
5660	PRINT MID(E$,S8-72,11),
5670	RETURN
5680	REM ***STRING COMPARISON IN QUADRANT ARRAY***
5690	S8=Z1*24+Z2*3-26:Z3=0:IF S8>72 THEN 5750
5720	IF MID(Q$,S8,3)<>A$ THEN 5810
5730	Z3=1:GOTO 5810
5750	IF S8>144 THEN 5790
5760	IF MID(R$,S8-72,3)<>A$ THEN 5810
5770	Z3=1:GOTO 5810
5790	IF MID(S$,S8-144,3)<>A$ THEN 5810
5800	Z3=1
5810	RETURN
5820	&”     INSTRUCTIONS”
5821	&:&”THE GALAXY IS DIVIDED INTO AN 8,8 QUADRANT GRID”
5822	&”WHICH IS IN TURN DIVIDED INTO AN 8,8 SECTOR GRID.”
5823	&:&”THE CAST OF CHARACTERS IS AS FOLLOWS:”
5830	&”<*> = ENTERPRISE”
5840	&”+++ = KLINGON”
5850	&”>!< = STARBASE”:& “ *  = STAR”
5870	&”COMMAND 0 = WARP ENGINE CONTROL:”
5880	&”  COURSE IS IN A CIRCULAR NUMERICAL       4  3  2”
5890	&”  VECTOR ARRANGEMENT AS SHOWN.             \ ^ /”
5900	&”  INTEGER AND REAL VALUES MAY BE            \^/”
5910	&”  USED.  THEREFORE COURSE 1.5 IS         5 ----- 1”
5920	&”  HALF WAY BETWEEN 1 AND 2.                 /^\”
5930	&”                                           / ^ \”
5940	&”  A VECTOR OF 9 IS UNDEFINED, BUT         6  7  8”
5950	&”  VALUES MAY APPROACH 9.”
5960	&”					    COURSE”
5970	&”  ONE WARP FACTOR IS THE SIZE OF”
5980	&”  ONE QUADRANT.  THEREFORE TO GET”
5990	&”  FROM QUADRANT 6,5 TO 5,5 YOU WOULD”
6000	&”  USE COURSE 3. WARP FACTOR 1”
6005	&
6010	&”COMMAND 1 = SHORT RANGE SENSOR SCAN”
6020	&”  PRINT THE QUADRANT YOU ARE CURRENTLY IN.  INCLUDING”
6030	&”  STARS, KLINGONS, STARBASES, AND THE ENTERPRISE, ALONG”
6040	&”  WITH OTHER PERTINATE INFORMATION.”
6045	&:&”COMMAND 2 = LONG RANGE SENSOR SCAN”
6060	&”  SHOWS CONDITIONS IN SPACE FOR ONE QUADRANT ON EACH SIDE”
6070	&”  OF THE ENTERPRISE IN THE MIDDLE OF THE SCAN. THE SCAN”
6080	&”  IS CODED IN THE FORM XXX, WHERE THE UNITS DIGIT IS THE “
6090	&”  NUMBER OF STARS, THE TENS DIGIT IS THE NUMBER OF STAR-“
6100	&”  BASES.  THE HUNDREDS DIGIT IS THE NUMBER OF KLINGONS.”
6110	&:&”COMMAND 3 = PHASER CONTROL”
6120	&”  ALLOWS YOU TO DESTROY THE KLINGONS BY HITTING HIM WITH”
6130	&”  SUITABLY LARGE NUMBERS OF ENERGY UNITS TO DEPLETE HIS “
6140	&”  SHIELD POWER.  KEEP IN MIND THAT WHEN YOU SHOOT AT HIM,”
6150	&”  HE GONNA SHOOT AT YOU, TOO!”
6160	&:&”COMMAND 4 = PHOTON TORPEDO CONTROL”
6170	&”  COURSE IS THE SAME AS USED IN WARP ENGINE CONTROL”
6180	&”  IF YOU HIT THE KLINGON, HE IS DESTROYED AND CANNOT FIRE”
6190	&”  BACK AT YOU.  IF YOU MISS, YOU ARE SUBJECT TO HIS “
6200	&”  PHASER FIRE.”
6210	&:&”  NOTE:  THE LIBRARY COMPUTER (COMMAND 7) HAS AN OPTION”
6220	&”  TO COMPUTE TORPEDO TRAJECTORY FOR YOU (OPTION 2).”
6230	&:&”COMMAND 5 = SHIELD CONTROL”
6240	&”  DEFINES NUMBER OF ENERGY UNITS TO BE ASSIGNED TO SHIELDS”
6250	&”  ENERGY IS TAKEN FROM TOTAL SHIP’S ENERGY.”
6251	&”  NOTE THAT TOTAL ENERY INCLUDES SHIELD ENERGY.”
6260	&:&”COMMAND 6 = DAMAGE CONTROL REPORT”
6270	&”  GIVES STATE OF REPAIRS OF ALL DEVICES.  A STATE OF REPAIR”
6280	&”  LESS THAN ZERO SHOWS THAT THE DEVICE IS TEMPORARALY”
6290	&”  DAMAGED.”
6300	&:&”COMMAND 7 = LIBRARY COMPUTER”
6310	&”  THE LIBRARY COMPUTER CONTAINS THREE OPTIONS:”
6320	&”    OPTION 0 = CUMULATIVE GALACTIC RECORD”
6330	&”	WHICH SHOWS COMPUTER MEMORY OF THE RESULTS”
6340	&”	OF ALL PREVIOUS LONG RANGE SENSOR SCANS”
6350	&”    OPTION 1 = STATUS REPORT”
6360	&”	WHICH SHOWS NUMBER OF KLINGONS, STARDATES,”
6370	&”	AND STARBASES LEFT.”
6380	&”    OPTION 2 = PHOTON TORPEDO DATA”
6390	&”	GIVES TRAJECTORY AND DISTANCE BETWEEN THE”
6400	&”	ENTERPRISE AND ALL KLINGONS IN YOUR QUADRANT”
6500    GOTO 230
6510	END

Implementation

supplyEnterpriseA[maxEnergy_] := Module[{maxPhotons, energy, shieldStrength, shieldPct, direction, velocity},
maxPhotons = 10;
energy = maxEnergy * 1.0;
shieldStrength = 0.0;
shieldPct = 0.0;
direction = 0.0;
velocity = 0.0;

{maxPhotons, energy,shieldStrength, shieldPct, direction, velocity}
]

srStatusA[sDate_,eCondition_, currQuad_, eSector_, eEnergy_, nPhotons_, shields_, totKlingons_, endDate_] :=
Module[{myGrid},
myGrid = Style[Grid[{{"=================================", SpanFromLeft},
{"     SHORT   RANGE   REPORT     ", SpanFromLeft},
{"=================================", SpanFromLeft},
{"STARDATE", sDate},
 {"CONDITION", eCondition},
 {"QUADRANT", currQuad},
 {"SECTOR", eSector},
{"TOTAL ENERGY", NumberForm[eEnergy,{4, 0}]},
 {"PHOTON TORPEDOES", nPhotons},
{"SHIELDS", If[shields > 0, StringForm["UP: `` ",NumberForm[shields, {4,0}]], "DOWN"]},
{"KLINGONS LEFT", totKlingons}, {"TIME LEFT", endDate - sDate},
{"=================================", SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"},
 Background->Black, Spacings -> {2,Automatic}], 14];
myGrid

]

shortRangeScanA[theQuadrant_,srUPQ_, sDate_, eCondition_, currQuad_, eSector_, eEnergy_, nPhotons_, shields_, totKlingons_, endDate_] :=
 Module[{scan,status},
scan = quadrantDisplayA[theQuadrant, srUPQ];
status = srStatusA[sDate,eCondition, currQuad, eSector, eEnergy, nPhotons, shields, totKlingons, endDate];
Grid[{{status, scan}},Background->Black, Frame-> True]
]

initializeVariablesA[] := Module[ {sDate, currQuad, dockedQ, maxEEnergy, klingMaxE, logStart},
sDate = Floor[(RandomReal[] * 20 + 20) *100];
currQuad = RandomInteger[{1,8},2];
dockedQ = False;
maxEEnergy = 3000.0;
klingMaxE = 200.0;
logStart = "v1.0.1            ---- S  T  A  R    T  R  E  K ----\r\r Captain's Log \n";
{sDate, sDate + 40, currQuad, dockedQ, maxEEnergy, klingMaxE, logStart}
]

SetAttributes[longRangeScanUpdateGalaxyA, HoldFirst];

longRangeScanUpdateGalaxyA[theGalaxy_, theEntQuadrant_] := Module[{ rowE = theEntQuadrant[[1]], colE = theEntQuadrant[[2]], i, j, startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
For[i = startRow, i ≤ endRow, i++,
For[j = startCol, j ≤ endCol, j++,
theGalaxy[[i, j]] = makeQuadrantVisible[theGalaxy, {i,j}]
]
];
]

longRangeScanDisplay[theGalaxy_, theEntQuadrant_] := Module[{LRarray,LRgrid,topLegend, leftLegend, rowE = theEntQuadrant[[1]], colE = theEntQuadrant[[2]], i, j, startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
LRarray = theGalaxy[[startRow ;; endRow, startCol ;; endCol]];
LRgrid = Grid[Map[formatLongRangeElement,LRarray, {2}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin,Dashed, Gray}, ItemSize -> 3, Spacings -> {0, 2},
 Background -> Black];
leftLegend = Table[i, {i,startRow , endRow}];
topLegend = Table[i, {i, startCol, endCol}];
Style[Grid[{{Style["LR", White, Bold],Grid[ {topLegend}, ItemSize -> 3, Spacings -> {0,2}, ItemStyle -> {Italic, White}]}, {Grid[Transpose[{leftLegend}], Spacings -> {0,2}, ItemStyle -> {Italic, White}], LRgrid}}, Background->Black], 13]
]

(* for now, when LR sensors are down, show the grid with "???" *)

longRangeScanDispDOWN[theGalaxy_, theEntQuadrant_] := Module[{LRarray,LRgrid,topLegend, leftLegend, rowE = theEntQuadrant[[1]], colE = theEntQuadrant[[2]], i, j, startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
LRarray = Table["???", {i,startRow , endRow}, {j,startCol , endCol}];
LRgrid = Grid[LRarray, Alignment -> Center, Frame -> All,FrameStyle -> {Thin,Dashed, Gray}, ItemSize -> 3, Spacings -> {0, 2}, Background -> {None, None, {{1+ rowE - startRow,1 + colE - startCol} -> LightGray}}];
leftLegend = Table[i, {i,startRow , endRow}];
topLegend = Table[i, {i, startCol, endCol}];
Grid[{{"LR",Grid[ {topLegend}, ItemSize -> 3, Spacings -> {0,2}, ItemStyle -> Italic]}, {Grid[Transpose[{leftLegend}], Spacings -> {0,2}, ItemStyle -> Italic], LRgrid}}]
]

(* This is the exposed function *)
(* for consistency, we need to make two functions, one that updates the galaxy, and one that does the display only *)
(* 25 Sept 2013: Now that we are using HoldFirst, we can pass the galaxy by reference and combine the functions again. *)

SetAttributes[LongRangeGalaxyUpdateA, HoldFirst];
LongRangeGalaxyUpdateA[theGalaxy_, theEntQuadrant_, LRscannersWorkingQ_] :=
Module[{},
If[LRscannersWorkingQ, 
longRangeScanUpdateGalaxyA[theGalaxy, theEntQuadrant];

,
theGalaxy[[theEntQuadrant[[1]], theEntQuadrant[[2]]]] = makeQuadrantVisible[theGalaxy, theEntQuadrant];
];
]

SetAttributes[LongRangeSensorScanOutputA, HoldFirst];

LongRangeSensorScanOutputA[theGalaxy_, theEntQuadrant_, LRscannersWorkingQ_] :=
Module[{myGrid,scanOutput},

myGrid = Style[Grid[{{"=================================", SpanFromLeft},
{"     LONG   RANGE   REPORT     ", SpanFromLeft},
{"=================================", SpanFromLeft},
 {"QUADRANT", theEntQuadrant},

{"=================================", SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"},
 Background->Black, Spacings -> {2,Automatic}], 14];

LongRangeGalaxyUpdateA[theGalaxy, theEntQuadrant, LRscannersWorkingQ];
If[LRscannersWorkingQ, 

scanOutput = longRangeScanDisplay[theGalaxy, theEntQuadrant];
,

scanOutput = longRangeScanDispDOWN[theGalaxy, theEntQuadrant];];
Grid[{myGrid,scanOutput}]]

createQuadrantA[galaxyElement_] := Module[{quadData, nK, nB, nS, entCoord, baseCoord, klingons, theQuadrant, i}, 
quadData = Take[IntegerDigits[1000 + galaxyElement], -3];
{nK,nB,nS} = quadData;
theQuadrant =Partition[RandomSample[Join[Table[i, {i, 1,nK}],Table["B", {nB}],Table["*", {nS}],{"E"}, Table["", {63 - Total[quadData]}]]],8];

entCoord = Position[theQuadrant, "E"][[1]];
If[nB > 0, baseCoord = Position[theQuadrant, "B"][[1]], baseCoord ={0,0}];

(* Bug Fix: 29 Sept 2013 need to create a table of 3 Klingons no matter what. 
Rely on energy ≠ 0 to flag that the ship is real. Otherwise we'll exceed loops.
The coordinates set are legal but meaningless *)
klingons = Table[{{1,1},0}, {3}];
i = 1;
While[i ≤ nK, (klingons[[i, 1]] = Position[theQuadrant, i][[1]];
klingons[[i, 2]] = 200.0;);
i++];
{theQuadrant, nK, nB, nS, entCoord, baseCoord, klingons}
]

(* Note that we handle SR sensors down within the function *)

quadrantDisplayA[theQuadrant_,  srUPQ_] :=Module[{hdr, qdrnt, i,j},
If[srUPQ,(qdrnt =Grid[Map[formatSectorElementA,theQuadrant, {2}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 2, Spacings -> {0, 1}, Background -> Black];
)
,
(qdrnt = Grid[Table[Style["?", Gray, FontSize->7], {i,1,8}, {j, 1,8}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 2, Spacings -> {0, 1}, Background -> Black];
)];
hdr = Table[i, {i, 1,8}];
Style[Grid[{{Style["SR", White, Bold],Grid[ {hdr}, ItemSize -> 2, Spacings -> {0,2}, ItemStyle -> {Italic, White}]}, 
{Grid[Transpose[{hdr}], ItemSize -> 2, Spacings -> {0,1}, ItemStyle -> {Italic, White}, Background->Black], qdrnt}}, Background->Black, Frame -> True],14]

]
(* In keeping with the ASCII tradition, we draw the elements with characters. *)

formatSectorElementA[element_] := Module[{retString = ""},
If[element == "B", retString = Style["B", FontFamily->"Courier New", Bold, Green]];
If[element == "E", retString = Style["E", FontFamily->"Courier New", Bold, White]];
If[IntegerQ[element], retString = Style["K", FontFamily->"Courier New", Bold, Red]];
If [element == "*", retString = Style["*", FontFamily->"Courier New", Bold, Yellow]];

retString
]

(* -------------------------------------------------   makeGalaxy   ------------------------------------------------- *)
(* We make separate 8 by 8 arrays for klingons, starbases, and stars. We make sure that there is at least 1 starbase *)
(* We also need the total number of klingons and starbases. We make the galaxy by adding the separate arrays. *)
(* Finally, we make the quadrant that the Enterprise is in visible *)

makeGalaxy[currentQuadrantCoords_] := Module[{klArray, sbArray, stArray, nKlingons, nStarBases, galaxy},
klArray = Table[makeKlingons[], {i, 1, 8}, {j, 1, 8}];
nKlingons = Total[klArray, 2];
sbArray = Table[makeStarBases[], {i, 1, 8}, {j, 1, 8}];
nStarBases = Total[sbArray, 2];
If[nStarBases == 0, (sbArray[[RandomInteger[{1,8}],RandomInteger[{1,8}]]] = 1;
nStarBases = 1;)];
stArray = Table[RandomInteger[{1,8}], {i, 1, 8}, {j, 1, 8}];
galaxy = 100 * klArray + 10 * sbArray + stArray;
galaxy[[currentQuadrantCoords[[1]], currentQuadrantCoords[[2]] ]] += 1000;
{galaxy, nKlingons, nStarBases}]

makeStarBases[] := If[RandomReal[] > 0.96, 1, 0]

(* We made it easier to make more Klingons. The original was .8, .95, .98 *)
makeKlingons[] := Module[{rNum, nKlingons}, rNum = RandomReal[];
nKlingons = 0;
If[rNum > 0.8, nKlingons ++];
If[rNum > 0.92, nKlingons ++];
If[rNum > 0.955, nKlingons ++];
nKlingons]


(* -------------------------------------------------   galaxyDisplay   ------------------------------------------------- *)


galaxyDisplay[theGalaxy_, computerUPQ_] :=Module[{hdr, galax, i},

If[computerUPQ,
( galax =Style[Grid[Map[formatLongRangeElement,theGalaxy, {2}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 3, Spacings -> {0, 1}, Background -> Black], 13];)
,
(galax = Style[Grid[Table[Style["?", Gray, FontSize->7], {i,1,8}, {j, 1,8}], Alignment -> Center, Frame -> All,FrameStyle -> {Thin, RGBColor[.15, .15, .15]}, ItemSize -> 3, Spacings -> {0, 1}, Background -> Black], 13];
)];
hdr = Table[i, {i, 1,8}];
Style[Grid[{{Style[ "ST", FontFamily->"Courier New", White, Bold],Grid[ {hdr}, ItemSize -> 3, Spacings -> {0,1}, ItemStyle -> { Italic, White}]}, 
{Grid[Transpose[{hdr}], ItemSize -> 2, Spacings -> {0,1}, ItemStyle -> {Italic, White}, Background->Black], galax}}, Background->Black, Frame -> True], 13]
]

(* if there is a 1 in the 1000 place, return the value of the array mod 1000. This way we only show quadrants 'seen' by a LR scan *)
formatLongRangeElement[element_] := Module[{retString = ""},
 If[element >= 1000, retString = IntegerString[Mod[element, 1000]]];
Style[retString, FontFamily->"Courier New", Bold, White]
]

makeQuadrantVisible[theGalaxy_, currentQuadrant_] := Module[{qData}, 
qData = theGalaxy[[currentQuadrant[[1]], currentQuadrant[[2]]]];
1000 + Mod[qData,1000]]

upDateGalaxy[theGalaxy_, x_, y_] := Module[{testGal = theGalaxy},testGal[[x,y]]= makeQuadrantVisible[testGal, {x,y}];
testGal]

(* -------------------------------------------------   galaxyStatus   ------------------------------------------------- *)


galaxyStatus[ totSBs_, totKlingons_, entCompnentStatus_,sDate_, eCondition_, currQuad_, endDate_, computerUPQ_] :=
Module[{myGrid},
If[computerUPQ,
(myGrid = Style[Grid[{{"=================================", SpanFromLeft},
{"----  GALAXY   REPORT  ----   ", SpanFromLeft},
{"=================================", SpanFromLeft}, 
{"STARDATE", sDate},
 {"CONDITION", eCondition},
 {"QUADRANT", currQuad},
{"STAR BASES", totSBs},
{"KLINGONS LEFT", totKlingons},
 {"TIME LEFT", endDate - sDate}}, Alignment -> {{Right, Left}, {Center, Center}},
 ItemStyle -> {White, FontFamily -> "Courier New"}, Background->Black, Spacings -> {2,Automatic}], 13];)
,
(myGrid = Style[Grid[{{"=================================", SpanFromLeft},{"     GALAXY   REPORT         ", SpanFromLeft},{"=================================",
 SpanFromLeft}, 
{"STAR BASES LEFT", "????"},{"KLINGONS LEFT", "????"},{"COMPUTER IS DOWN!        ", SpanFromLeft},
{"=================================", SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"}, Background->Black, Spacings -> {2,Automatic}], 13];)];

Column[{myGrid}, Background -> Black]

]

(* -------------------------------------------------   galaxyStatusReport   ------------------------------------------------- *)

galaxyStatusReport[theGalaxy_,computerUPQ_, totSBs_, totKlingons_, entComponentStatus_, starDate_, entCond_,currentQuad_, endSDate_] := Module[{scan,status},
scan = galaxyDisplay[theGalaxy,computerUPQ];
status = galaxyStatus[totSBs, totKlingons, entComponentStatus, starDate, entCond, currentQuad, endSDate,  computerUPQ];
Grid[{{status, scan}},Background->Black, Frame-> True]
]

supplyEnterprise[maxEnergy_] := Module[{maxPhotons, energy},
maxPhotons = 10;
energy = maxEnergy * 1.0;
{maxPhotons, energy}
]


setUpShipStatus[] := Module[{components, status},
components = {"WARP ENGINES", "S. R.SENSORS", "L. R. SENSORS", "PHASER CONTROL",
 "PHOTON TORPEDOS", "DAMAGE CONTROL", "SHIELD CONTROL", "COMPUTER"};
status = Table[0.0, {Length[components]}];
{components, status}]

damageControlReport[entStatus_] :=
Module[{myGrid},
If[entStatus[[2,6]] ≥ 0.0,
(myGrid = Style[Grid[{ {"=================================", SpanFromLeft},
{"   DAMAGE CONTROL REPORT     ", SpanFromLeft},

{entStatus[[1,1]], formatDCelement[entStatus[[2,1]]]},{entStatus[[1,2]], formatDCelement[entStatus[[2,2]]]},{entStatus[[1,3]], formatDCelement[entStatus[[2,3]]]},
{entStatus[[1,4]], formatDCelement[entStatus[[2,4]]]},{entStatus[[1,5]], formatDCelement[entStatus[[2,5]]]},{entStatus[[1,6]], formatDCelement[entStatus[[2,6]]]},
{entStatus[[1,7]], formatDCelement[entStatus[[2,7]]]},{entStatus[[1,8]], formatDCelement[entStatus[[2,8]]]},

{"=================================", SpanFromLeft} },
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"}, Background->Black, Spacings -> {2,Automatic}], 13];)
,
(myGrid = Style[Grid[{{"-- DAMAGE CONTROL REPORT  -- ", SpanFromLeft},{"DAMAGE CONTROL IS DAMAGED   ", SpanFromLeft},
{"=================================", SpanFromLeft}},
Alignment -> {{Right, Left}, {Center, Center}}, ItemStyle -> {White, FontFamily -> "Courier New"}, Background->Black, Spacings -> {2,Automatic}], 13];)];

myGrid

]

formatDCelement[statusNumber_] :=
Module[{retString = Style["GOOD", FontFamily->"Courier New", Bold, Green]},
If[statusNumber < 0.0, retString = Style[ToString[StringForm["FIX IN `` DAYS",Ceiling[Abs[statusNumber]]]],FontFamily->"Courier New", Bold, Red]];
retString
]

deviceStatus[entStatus_, deviceNum_] := 
If[deviceNum ≥ 1 && deviceNum ≤ 8,entStatus[[2,deviceNum]] ≥ 0.0, False]

enterpriseDockedQ[currentQuadrant_, entcoords_] := Module[{nearby,rowE = entcoords[[1]], colE = entcoords[[2]], startRow,endRow, startCol, endCol},
startRow = Max[1, rowE - 1];
startCol = Max[1, colE - 1];
endRow = Min[8,rowE + 1];
endCol = Min[8,colE + 1];
nearby = currentQuadrant[[startRow ;; endRow, startCol ;; endCol]];
MemberQ[Flatten[nearby], "B"]
]

enterpriseCondition[amIDockedQ_, nKlingons_, energyFraction_] := Module[{status = Style["GREEN", FontFamily->"Courier New", Bold, Green]},
If[energyFraction <.1, status = Style["YELLOW", FontFamily->"Courier New", Bold, Yellow]];
If[nKlingons > 0, status = Style["RED", FontFamily->"Courier New", Bold, Red]];
If[amIDockedQ, status = Style["DOCKED", FontFamily->"Courier New", Bold, Green]];
status
]

photonSound = ;
phaserSound = ;
redAlertSound = ;
console1Sound = ;

(* -------------------------------------------------   StarTrekGame   ------------------------------------------------- *)



StarTrekGame[] := DynamicModule[{starDate, endStarDate, currentQuadCoords, enterpriseSectCoords, dockedQ,kMaxEntEnergy,kMaxKlingEnergy,
entPhotons, entEnergy, entShields, entShieldPCT,shipSystems,entDamConStatus, entCondition, theGalaxy, nStarBases, nKlingons,currentQuadrant,entWarpFactor, entDirection,
currQuadNKlingons, currQuadNStarBases, currQuadNStars, currQuadBaseCoords, currQuadKlingonsArray,entShipStatus, phaserEnergy,aimedPhotonDir,
 capLog, gameOverQ, executeButton, setShields,weaponControl, moveEnterpriseA, shieldControl, NavControls, klingonAttack, setPhotonDirByComputer, firePhoton, firePhasers,checkEndGame,
decrementKlingonInGalQuadrant},
(* new 23 Sept *)
Dynamic[entEnergy];
Dynamic[entShields];
Dynamic[entShieldPCT];

Dynamic[entWarpFactor];
Dynamic[entDirection];

Dynamic[phaserEnergy];
Dynamic[aimedPhotonDir];


(* Functions made internal.  *)

(* ================================================   shieldControl   ================================================ *)
SetAttributes[shieldControl,  HoldFirst];
shieldControl[shieldPCT_,eEnergy_, sEnergy_, shieldControlOKQ_, isGameOverQ_] := DynamicModule[{tEnergy, newEnergy, newShields, currPCT},
tEnergy = sEnergy + eEnergy;

currPCT = If[(tEnergy ≥ 0.0),100.0 * sEnergy/(tEnergy + 0.00001), 0.0];

Panel[If[(shieldControlOKQ && (tEnergy > 0.0) && !isGameOverQ) , 
(
Column[{
Text[Style["Shield Control", White, 20,Bold, FontFamily -> "Courier New"]],
Dynamic[Control[{{shieldPCT, currPCT},0, 100,ControlType -> Slider, ImageSize -> Medium}]],
Dynamic[Style[StringForm["Enterprise Energy : `` \rShield Energy:      `` ", NumberForm[0.01 * (100 -shieldPCT) * tEnergy, {4,0}, NumberPadding -> {" ", ""}], NumberForm[0.01 * shieldPCT* tEnergy, {4,0}, NumberPadding -> {" ", ""}]],13, White, FontFamily -> "Courier New"]],

Button[Row[{Style["■" , Green],Style[" Set Shields!", Italic,FontFamily -> "Courier New"]}], executeButton[5],BaseStyle -> {White, 20}, Appearance -> Frameless]}]
)
,
(
Column[{
Text[Style["Shield Control", White, 20,Bold, FontFamily -> "Courier New"]],
If[isGameOverQ,Text[Style["Game over!", 17,Bold, White,FontFamily -> "Courier New"]],
Text[Style["Shield Control DOWN", 17,Bold, Red,FontFamily -> "Courier New"]]]}]
)], Background->Black,ImageSize->{250, 130}, Alignment->{Left, Top}

]
];


(* ================================================   NavControls   ================================================ *)
SetAttributes[NavControls, HoldAll];
NavControls[direction_, velocity_,warpOKQ_, eEnergy_, isGameOverQ_] := DynamicModule[{engageDrive = False, driveType = 1,warpVec, heading, doMe },

doMe[myHdg_] := (direction = myHdg;executeButton[1]);

velocity = 0.01;
If[eEnergy > 0.0, engageDrive = True];
If[isGameOverQ, engageDrive = False];
If[!warpOKQ,driveType = 0];
warpVec = {Sin[direction * π/180.], Cos[direction * π/180.]};
(* display controls *)
Column[{
Text[Style["Navigation", White, 20, Bold,FontFamily -> "Courier New"]],
If[warpOKQ,
Style[RadioButtonBar[Dynamic[driveType], {1 ->"Warp  ",0 -> "Impulse"}, LabelStyle -> {White, Bold, 16,FontFamily -> "Courier New"}], ControlsRendering->"Generic"]
,If [isGameOverQ,Text[Style["Game Over", White, 13, Bold,FontFamily -> "Courier New"]],Text[Style["Warp Drive Down, Impulse only", Red, 13, Bold,FontFamily -> "Courier New"]]]],
Row[{LocatorPane[Dynamic[warpVec],Graphics[{White,Disk[],LightGray,Thickness[0.11],Circle[{0,0}, 0.8],Thin, Black,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.1Sin[i π/4], .1Cos[i π/4]}}], {i, 0, 7}],
 Thick,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.9Sin[i π/4], .9Cos[i π/4]}}], {i, 0, 7}],Red,Disk[{0,0}, .1],Arrowheads[Medium],Arrowheads[.15],Arrowheads[Medium],
 Arrow[{{0,0},Dynamic[Normalize[warpVec]]}]},ImageSize -> 100, PlotRange -> 1.1],{{-1,-1},{1,1}},Appearance->None], Spacer[60],
Dynamic[Control[{{velocity, 0.01},0.01, If[driveType == 1,8.0, .2],ControlType -> VerticalSlider, ImageSize -> {20, 95}, Appearance -> "RightArrow"}]]}],

Row[{Dynamic[Style["Heading :" <>ToString[NumberForm[heading =Round[Mod[-((180./π)Arg[warpVec[[1]] + I warpVec[[2]]]- 90), 360], 1], 3, NumberPadding -> {"0", ""}]], White, FontFamily -> "Courier New",12]],
 Spacer[19], Dynamic[Style["Factor :"<> ToString[NumberForm[Round[velocity, .01],{4, 2}, NumberPadding -> {" ", "0"}]], White, FontFamily -> "Courier New", 12]]}, Alignment -> Center],

If[isGameOverQ,Text[Style[" Game over!", White,20, FontFamily -> "Courier New" ]],
Button[Row[{Style["■" , Green],Style[" Engage!", Italic]}], doMe[heading],Enabled ->engageDrive,  BaseStyle -> {White, 20, FontFamily -> "Courier New"}, Appearance -> Frameless
]]}, Background -> Black,  Frame -> True]
];

(* ================================================   weaponControl   ================================================ *)
SetAttributes[weaponControl,HoldAll];
weaponControl[nPhotons_, nKlingonsInQuad_, photonsOKQ_, phasersOKQ_, computerOKQ_, phsrEnergy_,eEnergy_, photonDir_, isGameOverQ_] := DynamicModule[{photonVec,doMe2, heading},

doMe2[myHdg_] := (photonDir = myHdg;executeButton[3]);

photonVec = {Sin[photonDir * π/180.], Cos[photonDir * π/180.]};

Column[{
Text[Style["Photon Torpedos", White, 20,Bold, FontFamily -> "Courier New"]],

If[(photonsOKQ && (nPhotons > 0) && !isGameOverQ),
(
Panel[Column[{
Row[{
LocatorPane[Dynamic[photonVec],Graphics[{White,Disk[],LightGray,Thickness[0.11],Circle[{0,0}, 0.8],Thin, Black,
Table[Line[{{Sin[i π/4], Cos[i π/4]},{.1Sin[i π/4], .1Cos[i π/4]}}], {i, 0, 7}], Thick,Table[Line[{{Sin[i π/4], Cos[i π/4]},{.9Sin[i π/4], .9Cos[i π/4]}}], {i, 0, 7}],Red,Disk[{0,0}, .1],
Arrowheads[Medium],Arrowheads[.15],Arrowheads[Medium], Arrow[{{0,0},Dynamic[Normalize[photonVec]]}]},ImageSize -> 75, PlotRange -> 1.1],{{-1,-1},{1,1}},Appearance->None],
Column[{Style[ToString[StringForm["  Torpedos Left: ``",nPhotons]],FontFamily->"Courier New", Bold, White, 13],

Dynamic[Style["  Torpedo Heading:\r  "<>ToString[NumberForm[heading =Round[Mod[-((180./π)Arg[photonVec[[1]] + I photonVec[[2]]]- 90), 360], 5],3, NumberPadding -> {"0", ""}]], White, 13, Bold, FontFamily -> "Courier New"]]
}]
}, FrameMargins->15],



Button[Row[{Style["■" , Green, 20],Style[" Fire Torpedo!", Italic, FontFamily->"Courier New"]}], doMe2[heading], Enabled -> ((photonsOKQ && (nPhotons > 0)) == True),BaseStyle -> {White, 20, FontFamily -> "Courier New"}, Appearance -> Frameless]}],ImageSize->{250, 120} ,  Background ->  Black]
)
,
(If[isGameOverQ,Panel[Column[{Text[Style["Game over!", White, 14,Bold, FontFamily -> "Courier New"]],Text[Style["Torpedoes unavailable!", White, 14,Bold, FontFamily -> "Courier New"]]}],ImageSize->{250, 140}],
Panel[Column[{If[nPhotons == 0, Text[Style["■ No Photon Torpedos left!", Red, 14,Bold, FontFamily -> "Courier"]]],If[!photonsOKQ,Text[Style["■ Torpedo Control Down!", Red, 14,Bold, FontFamily -> "Courier New"]]]}],ImageSize->{250, 120}]]
)],

Text[Style["Phasers", 20,Bold,White, FontFamily -> "Courier New"]],

Panel[
If[(phasersOKQ && !isGameOverQ),
(
Column[{
Dynamic[Control[{{phsrEnergy, eEnergy/20.},0, eEnergy,ControlType -> Slider, ImageSize -> Medium}]],
Dynamic[Style[StringForm["Phaser Energy : `` ", NumberForm[phsrEnergy, {4,0}, NumberPadding -> {" ", ""}]],12, White, FontFamily -> "Courier New"]],
Button[Row[{Style["■" , Green],Style[" Fire Phasers!", Italic, FontFamily->"Courier New"]}], executeButton[4], Enabled -> phasersOKQ, BaseStyle -> {White,20}, Appearance -> Frameless]
}]),

If[isGameOverQ,Text[Style["Game Over!", 20,Bold, White,FontFamily -> "Courier New"]],Text[Style["■ Phasers DOWN", 20,Bold, Red,FontFamily -> "Courier New"]]]]

, ImageSize -> {250, 80}
]
},Background -> Black,  Frame -> True]
];

(* executeButton[] is a new function that acts as a dispatcher for the buttons in the UI. *)
(*  As we add functionality, we implement new commands *)
(* 1 = navigate, 2 = setPhoton course by computer, 3 = Fire Photon, 4 = Fire Phaser, 5 = adjust shields *)
(* 23 Sept: add functionality for shields. command number == 5 *)
(* 28 Sept: add functionity for weapons, commands 2, 3, 4 *)
executeButton[butNum_] :=
Which [butNum == 1,moveEnterpriseA[currentQuadrant, currentQuadCoords, entWarpFactor, entDirection,enterpriseSectCoords,
theGalaxy,currQuadNKlingons, currQuadNStarBases, currQuadNStars, currQuadBaseCoords, currQuadKlingonsArray, dockedQ, entCondition, entEnergy, kMaxEntEnergy, entShields,starDate, capLog,entDamConStatus ,entPhotons,endStarDate, nKlingons, gameOverQ],


butNum == 3,firePhoton[entEnergy,aimedPhotonDir,capLog, currQuadNKlingons, currQuadKlingonsArray,
 entDamConStatus,entShields,dockedQ, enterpriseSectCoords, nKlingons, currentQuadrant, theGalaxy, currentQuadCoords, kMaxEntEnergy, entCondition, entPhotons, starDate, endStarDate, gameOverQ],
butNum == 4,firePhasers[entEnergy,phaserEnergy,capLog, currQuadNKlingons, currQuadKlingonsArray,
 entDamConStatus,entShields,dockedQ, enterpriseSectCoords, nKlingons, currentQuadrant, theGalaxy, currentQuadCoords, kMaxEntEnergy, entCondition, entPhotons, starDate, endStarDate, gameOverQ],
butNum == 5,setShields[entShieldPCT, entEnergy, entShields, entCondition, currQuadNKlingons,dockedQ,kMaxEntEnergy,capLog]];

(* new functions *)



(* ================================================   firePhoton   ================================================ *)

SetAttributes[firePhoton, HoldAll];
firePhoton[eEnergy_,photonDir_,log_, nQuadKlings_, klingsArray_,sDamageControl_, eShields_,dockStatus_, 
EntPos_, totKlingons_, theQuad_, theGalaxy_, curGQuad_, maxEntEnergy_, eCond_, nPhots_, curSDate_, finSDate_, isGameOverQ_] := Module[{vector, testPos, testCoords, okQ, kNum, myLog},

EmitSound[photonSound];
myLog = "Torpedo away: ";
nPhots--;
vector = { -Cos[photonDir * π/180], Sin[photonDir * π/180]};

testPos = EntPos + vector;
testCoords = Round[testPos];
myLog = myLog <> ToString[StringForm["{``,``}->", testCoords[[1]],testCoords[[2]]]];
okQ = True;
While[okQ,

If[testCoords[[1]] > 8 || testCoords[[1]] < 1 || testCoords[[2]] > 8 || testCoords[[2]] < 1,
(
(* The Photon torpedo left the quadrant *)
okQ = False;
myLog = myLog <> " Torpedo missed!\r";

),
(
If [theQuad[[testCoords[[1]], testCoords[[2]] ]] != "",
(
(* We hit something. Find out what *)
If[theQuad[[testCoords[[1]], testCoords[[2]] ]] == "B", (
myLog = myLog <> " Starbase shields Absorbed the hit. Aim better next time!\r";
okQ = False;
)];
If[theQuad[[testCoords[[1]], testCoords[[2]] ]] == "*", (
myLog = myLog <> " You can't destroy a star!\r";
okQ = False;
)];
If[IntegerQ[theQuad[[testCoords[[1]], testCoords[[2]] ]] ], (
myLog = myLog <> " Klingon destroyed!\r";
kNum =theQuad[[testCoords[[1]], testCoords[[2]] ]];
klingsArray[[kNum, 2]] = 0.0;
theQuad[[testCoords[[1]], testCoords[[2]] ]] = "";
nQuadKlings --;
totKlingons --;
decrementKlingonInGalQuadrant[theGalaxy, curGQuad];
okQ = False;
)];


),
(
(* We are on the path *)
testPos += vector;
testCoords = Round[testPos];
myLog = myLog <> ToString[StringForm["{``,``}->", testCoords[[1]],testCoords[[2]]]];

)];
)];
];
(* If there are any klingons in the quadrant, they can shoot back *)
dockStatus = enterpriseDockedQ[theQuad, EntPos];


If[nQuadKlings > 0, klingonAttack[nQuadKlings,klingsArray, sDamageControl, eEnergy, eShields,dockStatus, log, EntPos, curSDate, finSDate, totKlingons, isGameOverQ]];

If[dockStatus,(myLog = myLog<> " Docked. Starbase shields protect the ship.\r";
{nPhots, eEnergy} =supplyEnterprise[maxEntEnergy];
eShields = 0.0;
sDamageControl[[2,1]] = 0.0;
sDamageControl[[2,2]] = 0.0;
sDamageControl[[2,3]] = 0.0;
sDamageControl[[2,4]] = 0.0;
sDamageControl[[2,5]] = 0.0;
sDamageControl[[2,6]] = 0.0;
sDamageControl[[2,7]] = 0.0;
sDamageControl[[2,8]] = 0.0;

)];

log = myLog <> log;
eCond = enterpriseCondition[dockStatus, nQuadKlings, eEnergy/maxEntEnergy];
checkEndGame[curSDate, finSDate, eEnergy, eShields, totKlingons, log, sDamageControl, isGameOverQ];

];
(* ================================================   firePhasers   ================================================ *)

SetAttributes[firePhasers, HoldAll];
firePhasers[eEnergy_,phasNRG_,log_, nQuadKlings_, klingsArray_,sDamageControl_, eShields_,dockStatus_,EntPos_, totKlingons_, theQuad_, theGalaxy_, curGQuad_, maxEntEnergy_, eCond_, nPhots_, curSDate_, finSDate_, isGameOverQ_] := 
Module[{accuracy, kNum, actualNRG, dist, hitEnergy, startNKl, myLog},

(* spend the energy up front, even if we did not use all of it. *)
eEnergy -= phasNRG;
myLog = "";
startNKl =nQuadKlings;
(* a damaged computer reduces accuracy *)
accuracy = 1.0;
If[sDamageControl[[2,8]] < 0, (
accuracy -= 0.1 * RandomReal[] * Abs[sDamageControl[[2,8]]];
myLog = myLog <> " Damaged computer hampers accuracy\r";
)];
actualNRG = accuracy * phasNRG;
(* We must allow for a dumb captain shooting phasers at nothing. *)
If[nQuadKlings > 0,
(
For[kNum = 1, kNum <= Length[klingsArray], kNum++,
If[klingsArray[[kNum, 2]] >0.0,(
dist = Max[N[EuclideanDistance[klingsArray[[kNum, 1]], EntPos]], 1.0];
hitEnergy =  Floor[(actualNRG/(Sqrt[dist] * nQuadKlings)) * (1.5 + RandomReal[{-0.5, 0.5}])];
EmitSound[phaserSound];
klingsArray[[kNum, 2]] -=  hitEnergy;
myLog = myLog <>ToString[StringForm["`` unit hit on Klingon at ``. ", hitEnergy,klingsArray[[kNum, 1]]]];
If[klingsArray[[kNum, 2]] <= 0,(
myLog = myLog <> "Klingon destroyed!\r";
nQuadKlings --;
totKlingons--;
theQuad[[klingsArray[[kNum, 1,1]],klingsArray[[kNum, 1,2]] ]] = "";
decrementKlingonInGalQuadrant[theGalaxy, curGQuad];
),
myLog = myLog <>ToString[StringForm["`` left.\r", klingsArray[[kNum, 2]]]];
];
)];
];

)];



dockStatus = enterpriseDockedQ[theQuad, EntPos];
If[dockStatus,(myLog = myLog<> " Docked. Starbase shields protect the ship.\r";
{nPhots, eEnergy} =supplyEnterprise[maxEntEnergy];
eShields = 0.0;
sDamageControl[[2,1]] = 0.0;
sDamageControl[[2,2]] = 0.0;
sDamageControl[[2,3]] = 0.0;
sDamageControl[[2,4]] = 0.0;
sDamageControl[[2,5]] = 0.0;
sDamageControl[[2,6]] = 0.0;
sDamageControl[[2,7]] = 0.0;
sDamageControl[[2,8]] = 0.0;

)];

log = myLog <> log;

(* If there are any klingons in the quadrant, they can shoot back *)
If[nQuadKlings > 0, klingonAttack[nQuadKlings,klingsArray, sDamageControl, eEnergy, eShields,dockStatus, log, EntPos, curSDate, finSDate, totKlingons, isGameOverQ]];

eCond = enterpriseCondition[dockStatus, nQuadKlings, eEnergy/maxEntEnergy];
checkEndGame[curSDate, finSDate, eEnergy, eShields, totKlingons, log, sDamageControl, isGameOverQ];
]; 



SetAttributes[decrementKlingonInGalQuadrant, HoldAll];
decrementKlingonInGalQuadrant[theGal_,currQuad_] := Module[{quadRep, nKlings, myDigits},
quadRep = theGal[[currQuad[[1]], currQuad[[2]]]];
myDigits = IntegerDigits[quadRep];
nKlings = myDigits[[-3]];
nKlings --;
If[nKlings <0, nKlings = 0];
myDigits[[-3]] = nKlings;
theGal[[currQuad[[1]], currQuad[[2]]]] = FromDigits[myDigits];
];

(* ================================================   moveEnterpriseA   ================================================ *)
(*                                                                                                                     *)
(* ================================================   moveEnterpriseA   ================================================ *)

(* theQuad will be modified in place. *)
SetAttributes[moveEnterpriseA, HoldAll];
moveEnterpriseA[theQuad_,theQuadCoords_, wFactor_, theHeading_, startEntPos_, theGal_, nQuadKlings_, nSBs_, nStars_, sBcoords_, klingsArray_, dockStatus_, eCond_, eE_,maxEE_, eShields_,theStardate_,theLog_, sDamageControl_, nPhotons_,finSDate_, totKlingons_, isGameOverQ_] := Module[{myQuad,warpVec, qVel, oldEntPos, testEntPos, testEntCoords,oldEntCoords, maxSteps,tQCoords,newQuadCoords, stepNum, okQ, mess, inQuadQ, doneQ, incStarDateQ , energyUsed, usedWFactor, damDev,damage, dev},

mess = "";
newQuadCoords = {0,0};
myQuad =theQuad;

(* EmitSound[console2Sound]; *)
Beep[];

(* If there are klingons in the quadrant, the can attack here *)
If[nQuadKlings > 0, klingonAttack[nQuadKlings,klingsArray, sDamageControl, eE, eShields,dockStatus, mess, startEntPos, theStardate, finSDate, totKlingons,isGameOverQ]];


(* moving uses energy. We want to be sure we have enough. If we don't and there is enough from the shields,
then use some shield energy. If we still don't have enough, move just one sector. *)

usedWFactor =wFactor;

energyUsed = (8.0*usedWFactor);
If[energyUsed > eE,
(
If[eShields >energyUsed, 
(eE += energyUsed;
 eShields -= energyUsed;
mess = mess <>" Using shield energy to move.\r";
)
,
(
mess = mess <>" Almost out of Energy. Minimal headway\r";
usedWFactor = 0.01;
)];
)];

maxSteps = Floor[8 * usedWFactor]+ 1;
stepNum = 0;

incStarDateQ = False;
(* Going faster than warp 1 increments the stardate, even if we don't leave the quadrant. *)
If[usedWFactor ≥ 1.0, incStarDateQ = True];
(* Similarly, we expend the same energy, even if we are blocked. *)
eE -= (8.0*usedWFactor);

warpVec = { -Cos[theHeading * π/180], Sin[theHeading * π/180]};

oldEntPos = startEntPos;
(* compute coords for new Quadrant, just in case we need it *)
newQuadCoords = theQuadCoords;
tQCoords = Round[theQuadCoords + Max[usedWFactor, .7]* warpVec];
If[tQCoords[[1]] < 1,tQCoords[[1]] = 1];
If[tQCoords[[1]] > 8,tQCoords[[1]] = 8];
If[tQCoords[[2]] < 1,tQCoords[[2]] = 1];
If[tQCoords[[2]] > 8,tQCoords[[2]] = 8];


mess = mess <>ToString[StringForm[" Moving, heading `` ,warp factor `` .\r",theHeading, usedWFactor] ];

(* Repair devices. Notify if a device is fixed. *)
For[dev = 1, dev ≤ 8, dev++,
If[sDamageControl[[2,dev]] < 0,(
sDamageControl[[2,dev]] ++;
If[sDamageControl[[2,dev]] ≥ 0.0,mess = mess <>ToString[StringForm[" `` repaired.\r",sDamageControl[[1,dev]]] ] ]
)]
];


(* remove ent from starting position *)
myQuad[[oldEntPos[[1]], oldEntPos[[2]] ]] = "";
oldEntCoords = oldEntPos;
okQ = True;
inQuadQ = True;
doneQ = False;

While[okQ,

testEntPos = oldEntPos + warpVec;
testEntCoords = Round[testEntPos];

If[testEntCoords[[1]] > 8 || testEntCoords[[1]] < 1 || testEntCoords[[2]] > 8 || testEntCoords[[2]] < 1,
(
(* We want to leave the current Quadrant. Unless we are at the edge, we do *)
inQuadQ = False;
okQ = False;
incStarDateQ = True;
testEntCoords = tQCoords;
(* If we try to leave the galaxy, we can't. This is the same as being blocked. *)
If[newQuadCoords == tQCoords, (myQuad[[oldEntCoords[[1]], oldEntCoords[[2]] ]] = "E";
testEntCoords = oldEntCoords;
inQuadQ = True;  (* we really didn't leave the galaxy *)
doneQ = True;
mess = mess <> " Stopped by energy barrier at edge of the Galaxy!\r";)]
)
,
(
(* We are in the same quadrant. See if we are blocked by something.
If we are, then we back up to the previous location and stop *)
If [myQuad[[testEntCoords[[1]], testEntCoords[[2]] ]] != "",
(myQuad[[oldEntCoords[[1]], oldEntCoords[[2]] ]] = "E";
mess = mess <> ToString[StringForm[" Enterprise blocked by object at `` , `` due to bad navigation.\r",testEntCoords[[1]], testEntCoords[[2]]] ];
testEntCoords = oldEntCoords;
okQ = False;
doneQ = True;
(* Not in BASIC Game: If we hit something, we have damage *)
damDev = RandomInteger[{2,8}];
damage = 0.5 + RandomReal[] * 1.0;
sDamageControl[[2,damDev]] -= damage;
mess = mess <>ToString[StringForm["`` Damaged. `` units \r", sDamageControl[[1,damDev]],NumberForm[damage,6]]];
)];
stepNum++;
oldEntPos = testEntPos;
oldEntCoords = testEntCoords;
If[stepNum >= maxSteps, okQ = False];
)
]
]; (* while *)
(* We got through the While statement with no problem. Put the Enterprise in the current location *)
If[inQuadQ && !doneQ, myQuad[[testEntCoords[[1]], testEntCoords[[2]] ]] = "E"];
(* If we are not in the same quadrant, we need to do stuff:
   1. update the quadrant co-ordinates
 2. Make a new quadrant. This affects other globals.
3. Increment Startdate
 4. Update message. *)
If[!inQuadQ,(newQuadCoords = tQCoords;
{myQuad, nQuadKlings, nSBs, nStars, testEntCoords, sBcoords, klingsArray} =createQuadrantA[theGal[[newQuadCoords[[1]], newQuadCoords[[2]]]]];

)];

(* To improve game play, we cause damage for moves. We restrict the damage to warp 4 and above. *)
If[wFactor > 4.0,

If[(RandomReal[] < 0.012 * wFactor),(
damDev = RandomInteger[{1,8}];
damage = 0.5 + RandomReal[] * 1.0;
sDamageControl[[2,damDev]] -= damage;
mess = mess <>ToString[StringForm["`` Damaged due to high warp. `` units \r", sDamageControl[[1,damDev]],NumberForm[damage,6]]];

)]];


theQuad = myQuad;
startEntPos = testEntCoords;
theQuadCoords = newQuadCoords;
wFactor = 0.01;
theHeading = 0.0;
dockStatus = enterpriseDockedQ[theQuad, startEntPos];
 
eCond = enterpriseCondition[dockStatus, nQuadKlings, eE/maxEE];
If[mess ≠ "",
theLog = mess <>theLog;
mess = "";];

If[incStarDateQ,(
theStardate++; 

mess = ToString[StringForm["StarDate: `` -----  Quadrant: {``, ``} ------------ \r", starDate,newQuadCoords[[1]], newQuadCoords[[2]]]];
If[nQuadKlings >0 && eShields < 20.0, mess = mess<> " Klingons in Quadrant. Suggest raising shields.\r"];
)];
If[nQuadKlings > 0, EmitSound[redAlertSound]];

If[dockStatus,(mess = mess<> " Docked. Starbase shields protect the ship.\r";
{nPhotons, eE} =supplyEnterprise[maxEE];
eShields= 0.0;
sDamageControl[[2,1]] = 0.0;
sDamageControl[[2,2]] = 0.0;
sDamageControl[[2,3]] = 0.0;
sDamageControl[[2,4]] = 0.0;
sDamageControl[[2,5]] = 0.0;
sDamageControl[[2,6]] = 0.0;
sDamageControl[[2,7]] = 0.0;
sDamageControl[[2,8]] = 0.0;
)
];
If[mess ≠ "",
theLog = mess <> "\r\r" <> theLog;
mess = "";];
checkEndGame[theStardate, finSDate, eE, eShields, totKlingons, theLog, sDamageControl, isGameOverQ];
];

(* When we set shields, we also have to compute the enterprise condition *)
(* The BASIC game uses energy % of non-shield energy. I think it should be total energy, but I left it as the BASIC game had it. *)
SetAttributes[setShields,HoldAll];
setShields[theShieldPCT_,  entNRG_, shNRG_, eCond_, nKling_, amIDockedQ_, maxEE_, mess_] :=
Module[{theTotEnergy, tmpPct},


EmitSound[console1Sound];


theTotEnergy =entNRG + shNRG;
tmpPct = theShieldPCT;
entNRG = 0.01 * (100.0 - theShieldPCT) * theTotEnergy;
shNRG = 0.01 * theShieldPCT * theTotEnergy;
eCond = enterpriseCondition[amIDockedQ, nKling, entNRG/maxEE];
mess = ToString[StringForm[" Shields Raised to `` units \r", NumberForm[shNRG,6]]]<> mess;
];

SetAttributes[klingonAttack,HoldAll];
klingonAttack[currQuadNKling_,currQuadKlingArray_, eDamConStatus_, eEnergy_, shEnergy_, amIDockedQ_, logMessage_, entCords_, cSDate_, eSDate_, tKlings_, isGameOverQ_] 
:= Module[{doneQ = False, kNum, hitEnergy = 0.0, dist, ergLoss, damDev, damage, myMess},

myMess = "";
If[amIDockedQ, (doneQ = True;
myMess = myMess<> "Starbase shields protect ship from attack!\r"; )];
If[currQuadNKling ≤ 0, doneQ = True];

If[!doneQ,
EmitSound[redAlertSound];
(* We need to loop through the entire array, only acting if the energy is > 0.0 *)
For[kNum = 1, kNum <= Length[currQuadKlingArray], kNum++,
If[currQuadKlingArray[[kNum, 2]] >0.0,(
dist = Max[N[EuclideanDistance[currQuadKlingArray[[kNum, 1]], entCords]], 1.0];
hitEnergy = Floor[(currQuadKlingArray[[kNum, 2]]/dist) * 2.0 * RandomReal[]];
shEnergy -=  hitEnergy;
myMess = myMess <>ToString[StringForm["`` unit hit from Klingon at ``, Shield energy:`` \r", hitEnergy,currQuadKlingArray[[kNum, 1]], shEnergy]];
(* We want to disable a device if the shield energy is negative. This is not in the original game. *)
If[shEnergy < 0,(
ergLoss = Abs[shEnergy];
shEnergy = 0.0;
eEnergy -= ergLoss;
damDev = RandomInteger[{1,8}];
damage = 1.0 + RandomReal[] * 2.0;
eDamConStatus[[2,damDev]] -= damage;
myMess = myMess <>ToString[StringForm["`` Damaged. `` units \r", eDamConStatus[[1,damDev]],NumberForm[damage,6]]];
)];
)];
]; 
];
logMessage = myMess <> logMessage;
checkEndGame[cSDate, eSDate, eEnergy, shEnergy, tKlings, logMessage, eDamConStatus, isGameOverQ];
];

SetAttributes[checkEndGame,HoldAll];
checkEndGame[curSDate_, endSDate_, eERGY_, eShields_, totKlings_, myLog_, eDamageControlStats_, gameOverQ_] := Module[{totNRG, allDots, endDots, endingQ, myMess},


allDots = "*******************************************************************\r";
endDots = "**\r";
endingQ = False;
gameOverQ = False;
myMess = "";
totNRG = eERGY + eShields;

If[totKlings ≤ 0, (myMess = allDots;
myMess = myMess <> endDots;
myMess = myMess <> ToString[StringForm["**     It is Stardate: ``. All Klingon vessels have been Destroyed!\r", curSDate]];
myMess = myMess <> "**                Y O U    W I N !\r";
myMess = myMess <> endDots;
myMess = myMess <> allDots;
endingQ = True;

)];

If[curSDate > endSDate, (myMess = allDots;
myMess = myMess <> endDots;
myMess = myMess <> ToString[StringForm["**     It is Stardate: ``. `` Klingon vessels remain.\r", curSDate,totKlings]];
myMess = myMess <> "**                Y O U    L O S E ! \r";
myMess = myMess <> endDots;
myMess = myMess <> allDots;
endingQ = True;
)];

If[totNRG ≤0.0, (myMess = allDots;
myMess = myMess <> endDots;
myMess = myMess <> ToString[StringForm["**     It is Stardate: ``. `` Klingon vessels remain.\r", curSDate,totKlings]];
myMess = myMess <> "**     You are out of energy!\r";
myMess = myMess <> "**                Y O U    L O S E ! \r";
myMess = myMess <> endDots;
myMess = myMess <> allDots;
endingQ = True;
)];

If[myMess ≠ "", myLog = myMess <> myLog];

(* If we are ending,diasble ability to do stuff. Make sure displays are enabled *)
If[endingQ,(

eDamageControlStats[[2,2]] = 0.0;
eDamageControlStats[[2,3]] = 0.0;


eDamageControlStats[[2,6]] = 0.0;

eDamageControlStats[[2,8]] = 0.0;
gameOverQ = True;
)];

];

(* ---------------------------- Start of game proper  -------------------------------------- *)

{starDate, endStarDate, currentQuadCoords, dockedQ, kMaxEntEnergy, kMaxKlingEnergy, capLog} = initializeVariablesA[];
{entPhotons, entEnergy,entShields,entShieldPCT, entDirection, entWarpFactor } = supplyEnterpriseA[kMaxEntEnergy];
entDamConStatus = setUpShipStatus[];


{theGalaxy, nKlingons, nStarBases} = makeGalaxy[currentQuadCoords];
{currentQuadrant, currQuadNKlingons, currQuadNStarBases, currQuadNStars, enterpriseSectCoords, currQuadBaseCoords, currQuadKlingonsArray} = createQuadrantA[theGalaxy[[currentQuadCoords[[1]], currentQuadCoords[[2]]]]];

dockedQ = enterpriseDockedQ[currentQuadrant, enterpriseSectCoords];
entCondition = enterpriseCondition[dockedQ, currQuadNKlingons, entEnergy/kMaxEntEnergy];

aimedPhotonDir = 0.0;
gameOverQ = False;
capLog = capLog <> ToString[StringForm["StarDate: `` ----- \r Your mission is to destroy `` Klingons by Stardate : ``.\r There are `` Starbases. \r", starDate, nKlingons, endStarDate, nStarBases]];

Deploy[Panel[Row[{Column[{TabView[{"Short Range Scan" ->  Panel[Dynamic[shortRangeScanA[currentQuadrant, deviceStatus[entDamConStatus, 2],starDate, entCondition, currentQuadCoords, enterpriseSectCoords, entEnergy, entPhotons, entShields, nKlingons, endStarDate]], ImageSize->{650,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ],
 "Long Range Scan" -> Panel[Dynamic[LongRangeSensorScanOutputA[theGalaxy, currentQuadCoords, deviceStatus[entDamConStatus, 3]]], ImageSize->{650,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ],
"Galaxy" -> Panel[Dynamic[galaxyStatusReport[theGalaxy,deviceStatus[entDamConStatus, 8],  nStarBases,nKlingons,entDamConStatus,starDate, entCondition, currentQuadCoords,endStarDate ]], ImageSize->{650,300}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ],
"Damage Control Report" -> Panel[Dynamic[damageControlReport[entDamConStatus]],
 ImageSize->{640,280}, Background -> Black, Alignment -> Center,DefaultBaseStyle -> "Text" ]}, Background -> Black, LabelStyle->{FontFamily->"Courier New", 13, Bold}],


 Pane[Dynamic[Style[capLog,White,FontFamily->"Courier New", 13 ]], ImageSize -> {640, 240}, DefaultBaseStyle -> "Text", Scrollbars->Automatic]}],

Column[{Dynamic[NavControls[entDirection,entWarpFactor,deviceStatus[entDamConStatus, 1],entEnergy , gameOverQ]],
 Dynamic[weaponControl[entPhotons,currQuadNKlingons, deviceStatus[entDamConStatus, 5], deviceStatus[entDamConStatus, 4], deviceStatus[entDamConStatus, 8],phaserEnergy,entEnergy,aimedPhotonDir, gameOverQ]], 
Dynamic[shieldControl[entShieldPCT,entEnergy, entShields, deviceStatus[entDamConStatus, 7], gameOverQ]]}]  }, Frame -> All, Background->Black],ImageSize->{950, 630}, Background->Black]]
]