Gwynne's Blog

Sa souvraya niende misain ye

Missions of the Reliant: Math is fun, or why I wish I hadn’t flunked geometry 1 Comment

At last, an update!

  1. Absolutely nothing visible to the user has changed whatsoever.
  2. The internal structure of the code has been significantly reorganized.

As with the lament of all programmers faced with the demands of the technologically disinclined, I’ve accomplished a great deal, but since it can’t be seen, it might as well be nothing at all. Wasted time, the hypothetical slave driver- I mean, boss- would say. But it isn’t, I swear to all two of you who read this blog!

And now, another math rant.

Once again, as with so many things, the way Mike did things in the original code was correct and logical for the time he did it, but doesn’t fit into the object-oriented model I’m cramming his code into, despite its pitiful cries for mercy from such rigid structure. There are days I wish we were living in times when code could be so freeform as his was and still be comprehensible, but you can’t do that in Cocoa. Oh sure, I could port all the Pascal functions 1-to-1, but the Toolbox calls would be sticky at best. Anyway, in this particular case, I was trying to wrestle with the radar range calculation.

The original code reads something vaguely like: screenPos = Planetabs - (Playerabs - Playerscreen)  inRadarRange = n <= screenPos / 16 <= m. Translating, this means that whether or not a given entity (a planet in this case) is within radar range of the planet is dependant upon the Player’s position in screen coordinates, as well as in the game’s absolute coordinate system.

In the old days, this design made a certain amount of sense. He already had the screen coordinates immediately handy, so why take the hit of indirecting through A5 to touch a global for the absolute position? However, my design makes the screen coordinates a bit dodgy to use. So I had to recalibrate n and m to represent distances in game coordinates.

Algebra to the rescue. The code above, reduced and replacing the inequalities, becomes the algebraic equation (x - (y - z)) / 16 = a, where a is the radar range coordinate. The only screen coordinate term in this equation is z, so solve to eliminate z:

(x - (y - z)) / 16 = a
x - (y - z) = 16a       - multiply both sides by 16
x - y + z = 16a         - distribute the subtraction over the parenthetical expression
x - y = 16a - z         - subtract z from both sides

But, because both a (the radar range) and z (the player’s position on screen) are actually constants, all I had to do was take Mike’s original numbers (let’s use 64 for a and 268 for z) and calculate 16*64 - 268 = 756. Then, retranslating, the equation becomes the inequality inRadarRange = (myPosition - playerPosition) <= 756;. Repeat for the lower and upper bounds of x and y coordinates, and boom, no screen coordinates at all and I can calculate whether or not an object's in radar range based on nothing but its offset from the player.

To be clear, what I did up there was to eliminate a term from the inequalities so that they could be evaluated based on the position of the given entity in game space, rather than on the position of the entity's sprite on the screen.

I can't believe it took me a week to doodle out that bit of math.

, , , , , , , ,
April 22, 2010 at 3:07 pm

Algebraic simplification 2 Comments

I was translating Pascal to C as usual for Missions when I came across the code fragment for computing the time bonus earned on victory given the current game time:

x := BSR(gCycle, 4) + 1;
x := round(50000 / x) * 10;

Now, I could have translated that to C as timeBonus = (50000 / ((cycle >> 4) + 1)) * 10, but I decided to get clever. Uh oh. I applied algebra to simplify the equation.

To translate it algebraically, I had to convert it to a purely mathematical equation. The right shift operator >> isn’t algebraic, but given the identity that x >> y = x / 2y, a right shift of four bits is an integer divide by 16. “cycle” is just a variable, so call it x, giving us the algebraic equation (50000 / ((x / 16) + 1)) * 10, or:

(50000/((x/16)+1)) * 10

Next, multiply 10 (as 10 / 1) into the equation:

500000 / ((x/16)+1)

Using the equality a/b + c/d = ad + bc / bd, rewrite (x / 16) + 1:

500000 / ((x + 16) / 16)

Rewrite using the equality a / (b / c) = a * (c / b):

8000000 / (x + 16)

Leaving us with two operations (add and divide) where originally there were four (shift, add, divide, multiply). Isn’t math cool?

All equation images generated by Apple’s Grapher program. Nice little feature that thing has, copy equation as TIFF.

, , , , , , ,
March 17, 2010 at 6:35 pm

Code funnies No Comments

I ran across this bit in Mike’s code, and couldn’t help but smile:

IF totalEnergy < 0 THEN
    totalEnergy := 0;   {this line saved my sanity! Believe me, shieldLevel _can_ be negative}

I’ve met few programmers who don’t put funny comments in their code now and again. For example, here’s one of mine from the warp drive subsystem:

if (enteringWarp && velocity > minWarpVelocity)     // We hit 88 miles per hour! Activate the flux capacitor!

I pity anyone whose code review guidelines forbid them from doing things like this. When trying to figure out someone else’s code, a little humor is desperately needed.

, , , ,
March 11, 2010 at 7:04 pm

Missions of the Reliant: Warp drive online, Captain! 1 Comment

The post title does not decieve; the ship’s warp drive now works.

That was an adventure in arctangents, power-of-two exponents, multiply-add operations, rounding errors… I have to say, this was a particular section of code where Mike’s style was a bit hard to decipher. No offense, Mike, but honestly, wow *sweat*. Let me hasten to clarify that the code wasn’t actually bad, just confusing. Confusing because of sections like this:

i := BSR((s + 1), 1);
j := trunc(72 / i);
z := round(round(exp2((s + 8) / 3)) / i);

Which in C was translated to:

uint32_t i = (s + 1) >> 1,
         j = 72 / i,
         z = lround(exp2((s + 8) / 3) / i);

That was an example where the translation was mostly one-to-one, save for BSR() being >> and trunc() not being needed at all, and one of the round()s being detrimental to the calculation… see how even the simplest-seeming things proliferate? Then there was the calculation of the angle from the player’s current position to the warp destination. In Pascal code that was a lot of fun with FixRatio() and AngleFromSlope() and various manual additions and subtractions of 180 and divisions by 10 and what have you. In C, because I chose to store the current player’s angle in a different form than Mike (I store the actual angle in degrees, whereas he stored an index into the set of 35 ship sprites – which was appropos at the time), I got to do some magic with atan2():

double          dx = d.x - pos.x,
                dy = d.y - pos.y,
                theta = atan2(-dy, dx), theta_deg = round(fma(theta, 180.0 / M_PI, 360.0 * signbit(theta)));

And that just gives me the angle from the player’s current position to the warp destination (nor is this the exact code; there are even more calculations done to get the correct coordinate values that aren’t necessary to this discussion); from there I have to calculate the difference between that and the player’s current facing and turn one increment per “tick” of the game timer to eventually reach the correct facing. Those of you who remember the original game (or have been playing it in SheepShaver, which actually emulates it damn near flawlessly if you run it with a NewWorld ROM and OS 9.0.4) will remember that the ship tends to oscillate back and forth between two facing angles during a warp jump, as there are only 36 sprites, meaning the angle the ship needs to be traveling almost never corresponds to a particular sprite. More multiplies and divides by 10, but there I got a break; the code to handle that was already implemented in the ship navigation subsystem, which handles the turn left and turn right keys. I passed the necessary numbers over to that and it did the job for me.

I was not able to pass off the responsibility of moving the ship to the ship engine subsystem (which handles forward and reverse thrust, as well as full stop), as that code carefully limits the player’s maximum speed for impulse drive. Also, the warp drive has to do some different management of non-maximum speeds; in the end it was better to reimplement it in the warp drive subsystem. The warp drive does, however, rely on the impulse engines to drop out of warp, by requesting a full stop. This had the rather neat side effect of automatically disabling the impulse drive’s user responses while warp was active, without me having to check for that anywhere in the impulse code.

Oh, and the emergency warp drive also works.

But enough about the warp drive. I’ve also got the energy capcaitor (remember? that green bar telling you you’re gonna die ’cause you used up too much power just getting where you were going and had nothing left to charge your lasers with when you got there?) going. The navigation (again, turn left and right) system is now separate from the impulse drive and can take individual damage. Yes that’s right, in version 3.0 of Missions, the turn thrusters can start to die just like everything else, although I was lenient and gave them very low hit-to-damage ratios. Speaking of which, the damage system is implemented too; ship’s systems can now take damage and lose functionality, though right now there’s nothing that does damage to them. Obviously to do the warp drive I had to upgrade the long range scanners, so those are now even closer to fully functional.

Oh, and I also made the “lights” draw exactly correctly at last. They weren’t quite right before.

Yay progress!

, , , , , ,
March 8, 2010 at 2:48 pm

Missions of the Reliant: Complications 3 Comments

I’ve accomplished surprisingly little in the last couple of days, in functional terms. I can sum up why pretty easily: I’ve had to stop and puzzle out exactly how Michael did some of the things in his code. Player velocity, especially, is giving me grief.

This isn’t Michael’s fault. He didn’t write obscure code (well, a little…) or implement anything stupidly. The problem was his confinement to old Mac Toolbox APIs in Pascal. One does not simply toss around floating-point math in System 7. (Nor does one simply walk into Mordor, but that’s another story.) Pascal had a floating-point type (REAL), but in those days it was slower than my brain on a Monday morning. I’m not sure why, since almost every Mac since the Mac II had a MC68881 or better FPU, but we were always told to use the FixMath package for efficiency just the same. So we used fixed point types and did fun little things like this:

integerPart := trunc(Fix2X(fixedValue));
fractionPart := fixedValue - Long2Fix(integerPart);

With math like that, and manual compensation for overflow going on, I think I can be forgiven for having to stare blindly at the uncommented code for about two hours before it finally made sense to me. It’s been quite a few years since I’ve worked without native floating-point, and a lot of that time was spent dredging up the memories. 21 lines of Michael’s Pascal code, all of them necessary in the environment it was written for, boil down to a single line in modern C, and in fact a single assembly language instruction too if you care to look at things at that level. In these days of multimedia CPU extensions, if I thought it were necessary for performance I could write it such that all the calculations for all the game objects that needed to move around were done in one vector instruction (SIMD add). I don’t think it’s necessary, but the fact that I could is a sign of just how much CPUs have changed. It’s also a tribute to all those people who did it the hard way 15 years ago.

Other progress includes implementation of the sector object map (meaning planets and starbases, and their locations), reconversion of the rather broken SapirSans font (just opening it in Font Book made ATSUI whine quite loudly, and the italic variant was progmatically indistinguishable from the plain one) into a correctly-formed font suitcase by the very helpful if cryptic FontForge font editor program, and cargo, personnel, and crew management code. As usual, all of this stuff is backend and not visible in a build yet, so I have no screenshots to show. I will soon, though; now that I’ve figured out how the player moves around I can at least make the viewscreen work. Stay tuned.

P.S.: If there’s anyone who follows and enjoys the little roleplay blurbs, speak up in a comment and I’ll continue them. It only takes one voice!

Alliance Headquarters
Stardate 2310.13816640048673

, , , , , , , , , , ,
February 19, 2010 at 11:10 am

Missions of the Reliant 4 Comments

Those who have been using Macs for at least 14 years may or may not remember a space game for old Macs that went by the name “Missions of the Reliant”.

It was a really fun little game with a few missions in it, changable crew members in your ship, powerups for your ship, a nice big galaxy to hang around in, systems that took damage and could be repaired… if you’re thinking Rescue!, don’t, Missions was much better.

Anyway, like all the old Mac games, it’s long since nonfunctional on modern machines. But I wasn’t willing to settle for that, so I pulled up my e-mail and wrote a letter to Michael Rubin, the original author of Missions, asking if I might get the source code and take a crack at porting it to OS X.

His enthusiasm was beyond anything I could have hoped for. I’m very grateful to him for the opportunity he’s given me to bring a classic back to the Mac. I’ll be posting updates here regularly about my progress on the port.

Progress Report 1

Well, I’ve got the code, and I’ve looked it over. Ah, the old glory days of Pascal, inline A-traps, GWorlds, manual event handling… The Mac Toolbox did almost nothing for you; it was a true low-level interface to the OS, something I feel we’ve gotten away from in these days of Cocoa. Sure, OS X has the POSIX interfaces, but they’re a whole different world. Anyway, the code is a real trip back to olden times, and I love every minute of it.

First step was to create the Xcode project. That took about three hours.

Wait, what? Three hours? Well, once you figure setting up the project settings, the target settings, tweaking the things Xcode’s templates don’t get quite right, editing the pregenerated files to not have broken line breaks and incorrect heading comments, and writing the entire Info.plist for the application, that’s a lot of work! I had to look up Info.plist keys, UTI listings, sweep the original source code for the proper value of NSHumanReadableCopyright, and ask a question or two about semantics in the #macdev IRC channel.

Next step, I figure, is to sweep up all the original visual resources – strings, pictures, icons – and reorganize them out of old-style rsrc files into a modern application’s Resources folder. Yes, I’m aware you can still use resource files in OS X, but I feel if you’re going to do something, you might as well do it right!

After that, I have to take a little time out to brush up on my OpenGL 2D, it’s been awhile since I used it and I never did use it for anything this complex. Binding several dozens of textures to represent all the various sprites should be a fascinating undertaking.

I’m enjoying the hell out of myself *grin*. Thanks again, Michael!

, , , , , , , , , , , ,
February 7, 2010 at 10:41 pm