------------- `--- ------'
`-------- --' / /
The USS Enterprise --- NCC - 1701
Your mission begins with your starship located
in the galactic quadrant Canopus.
Your orders are as follows:
Destroy the 24 Klingon warships which have invaded
the galaxy before they can attack Federation Headquarters
on stardate 3732. This gives you 32 days. There are
3 starbases in the galaxy for resupplying your ship.
Hit any key to accept command.
. . . . * . . . Stardate 3700
. . . . . . . . Condition GREEN
. * . . . . . . Quadrant (5, 4)
. . . * . . . . Sector (5, 2)
. <E> . . . . . . Photon Torpedos 10
. * . . . . . . Total Energy 3000
* . * * . . . . Shields 0
. . . . . . . * Klingons 24
Here is my entry into The Code Project Random Programming Competition for Summer 2008. It seems that Michael Birken beat me to being the first to post a Star Trek article. Since the two programs have a shared history and game play, which he covers extensively in his article, it's best if you read that one first. (It's OK.... I'll wait.)
I'll just concentrate on the difference in my design.
This port took a roundabout journey to the pages of CodeProject. As Mr. Birken describes, Mike Mayfield's program was ported to PCs by Creative Computing Magazine, and appeared in David Ahl's "Basic Computer Games, Personal Computer Edition" (I still have my autographed copy) in the late 1970s. Due to the limitations of the language, it was a nightmare of spaghetti code. Then, in 1996, a man named Chris Nystrom took that code, and ported it to Standard C. However, it was a very literal port (much like Mr. Birken's C# version): virtually all variables are global, maintaining their cryptic one or two letter names, and nearly all functions pass their parameters and return values using global variables.
I liked the idea, but figured since we now had a powerful language to work with, we should use it to its full advantage. So, in 1997, I set out to write a fully object-oriented version. The goal was for it to have the same look & feel and game-play as the original, but with a modern OO design.
Unfortunately, Visual C++ 5.0 was a bit lacking back then -- templates were a bit flakey, and the STL didn't work at all. I gave up after a day's work. A year later, now armed with the more stable Visual C++ 6.0, I put in all of three days work, before getting bored and abandoning it again.
When the contest was announced, I remembered this long forgotten project, and to my surprise, I was able to find the code on one of the PCs on my home network (old computers never die, they are just repurposed).
So, with 10 years more experience with OO design (if somewhat rusty C++ skills) and a C++ compiler that mostly works (Visual Studio 2008 comes with Microsoft C++ v15), I set out to finally complete the task.
Using the Code
There's nothing particularly notable about the code, unless you want to track how my views on Hungarian notation and instance member prefixes changed over the years. Or how my native C++ coding looks a lot like managed C# code with things like Interfaces and Properties.
The key design principle is Separation of Concerns. The whole universe of the game is divided up into little objects, and they handle everything about their little piece of the world, and, with few exceptions, know nothing about anything outside of it.
At the heart of this is an object called, naturally enough,
Game object knows about our
Galaxy, and the Current Time, and not much else. It's major task is to handle setting up the game, determining when the game is won, and accepting the players commands. It also handles the major "cheat" in the design -- having the Enemy respond to something the
Next we have the
Ship object. The
Ship knows a bit more, but only things related to the ship as a whole. It knows its position in the galaxy, how much energy it has, and if it is docked to a starbase. It also knows that it has several subsystems, each of which derived from the base class
ShipSystem and its derived classes are the core of the game. There is one derived class for each of the game's commands. (One of those commands is "Resignation" which is not really a ship's system, but is a
ShipSystem derived class, so analogy breaks a bit, but we'll ignore that).
Via the base class, each
ShipSystem knows its name, description (these are used to display the help output when you type a bad command) and its command keyword, and what ship owns it.
Ship doesn't even know how many subsystems it has, or what their keywords are -- It just asks each, in turn, if the user's input activates them.
ShipSytem base class also handles the managing of damage and repairs to each system.
Then, the derived classes handle the details of their own particular system. For example, the
Engines object accepts a direction and distance to travel, and then asks the
Quadrant object to move it. The
Quadrant object reports back where it arrived, and what happened to it along the way (did it hit something? did it enter a new quadrant?)
LongScan object would look at a small part of the galaxy and display what it finds. You might think that for the
LongScan to look at the
Galaxy object is breaking encapsulation but it is exactly what that system would do in real life (if you consider Star Trek "real life").
Two other base classes are of interest:
IDisplayable functions like an Interface in the .NET world; it has only one method and it's pure virtual:
obj.DisplayOn(cout); would write the object, in its own unique format, onto
cout. I can then define
ILocatable is a implemented base class, despite its name. It's used as the base class for any object that has a position in a sector: your ship, the klingons, stars and torpedos.
Note: I've used pieces of the Boost Libraries (www.boost.org), so you'll need to have those installed to re-compile the source code. Boost is an extensive open-source library of C++ classes written by some of the best minds in the field. If you do any C++ coding, you should have it installed anyway.
. . . . . . . . Stardate 2800
. . . . . . . . Condition GREEN
. . . . . . . . Quadrant (7, 8)
. . . . * . * . Sector (6, 7)
. . . * . . . . Photon Torpedos 10
. . . . . .<e /><E>. Total Energy 3000
. . . . . . . . Shields 0
. . . . . . . . Klingons 17
Long Range Scan for Quadrant (7, 8)
: 004 : 208 : *** :
: 006 : 003 : *** :
: 102 : 016 : *** :
As with every project, along the way I placed where a major change would make a big improvement, but a deadline loomed, and I didn't want to do anything radical. But a major overhaul is needed.
The first thing is that I realized too late the difference between a Quadrant & a Sector -- there isn't one -- so those two classes should be merged. That would make a large swath of code much simpler.
I also decided that this really needs an event-driven design, so the quadrant would fire an event say an object moved within it, and that Klingons would listen to the quadrant, and know to fire back. Rather than the current design, where the Engines tell the
Game object to tell the klingons to fire back. But the Standard C++ library doesn't include a message passing framework, and I didn't want to build my own.
However, .NET does have such a framework, built right into its core. I had first thought I couldn't port this easily to C#, because I had used multiple inheritance, but I now believe that would be easy to factor out. But, I put a lot of other work aside for the summer to work on this, so that project will have to wait for next summer.
- 5th August, 2008 - Considering the history of this program (see above), it's a bit silly to call it "version 1.0", but, whatever, this is the first release of this rendition.