Space Fighter - a space shooter

What platform? Do you ask about Fortran? That’s Linux for me. I’m working at IceCube and I’m playing with some maximum likelihood stuff, such as MultiNest and PolyChord, which are written in Fortran. But I believe RPG is way harder to understand than Fortran, is it?

I support a Fortran 77 environment (gcc) for some old chemistry calculations.

Back to the Safe zone.

Oh, that RPG, I thought @filmote meant he was programming RPGs.

That makes a lot more sense.

For a moment there I thought he had a cool millitary job once upon a time.
(Which would have made sense given Choplifter, 1943, 1943’s possibly secret precursor and several other things.)


Speaking of cool jobs, that is a cool job.

That said, I wouldn’t be able to do it, too much complex maths involved.

2 Likes

It’s unreasonable to discuss level five details on a public forum especially since the ape incident.

2 Likes

Sure is.

I didn’t know people still used FORTRAN. The fact that I spell it in all upper-case should be the give away. I did FORTRAN-77 at University - Fortran-90 had not even been released.

Hence t the question on platform. I thought you might be using it on an old IBM Mainframe or Midrange machine. I did RPG II, III and IV on the System/36 and AS/400 for many, many years. It not a particularly hard language just a throwback from punched-cards. The code consists of various instruction ‘cards’ file input and output specs, computational lines and the code must be laid out in particular columns as if it was a punched card. There is a loop (‘cycle’) just like an Arduboy!

1 Like

I have seen FORTRAN and I am happy, I can work with Fortran 95 at least. But I wish I could use at least Fortran 2008 and C++11 (I also have to stick with boost if I need something fancy in C++ at work -.-). I am very sorry for you :smiley: I can’t imagine what a pain it is to work with RPG II. It may not be hard, but was it any fun?

@Pharap
Speaking of cool jobs, that is a cool job.

Well, I am at the analysis group, so I don’t have to go to the South Pole. :sweat_smile:

Back to topic:
I updated the game (see first post) and now it actually makes some fun. Next I’ll work on an increasing difficulty and lifepoints for the player, so you don’t loose your upgrades that easily. I also have to see how I can tune the speed of the ship. It is either slow or okayish or way too fast.

1 Like

I can’t wait for Fortran 2018 :P

I’ve never had to use Boost because the stdlib is full of useful stuff these days, but Boost has some very interesting stuff. A lot of stuff in the stdlib started off in Boost.

Boost does tend to be overly verbose and boilerplate heavy though. I’ve seen some very scary macro hacks.
(Spoiler: this is also how Arduino supports binary literals - a long line of ugly macros.)

Oops, that wasn’t intended as a pun.

At the moment it’s on the slow side.

Especially the projectiles, they just float around the screen and I find I spend over half the time dodging them. They ought to be moving 1.5 to 2 times as fast as the ships.

I like the graphics though. Especially the little jellyfish ships.

I can see a number of potential memory savings here and there (mostly progmem, but one or two RAM savings), but I suspect you’re waiting until you’ve got all your main features done before trying to reduce the code size.

Yeah, but if you have to stick to an old C++ compiler and you want to use stuff like a decent random number generator, you need Boost. I believe Boost is something like C++ Beta for upcoming releases. :smiley:

I got a bit frustrated with loosing all the upgrades after every death, so I gave the player lifepoints. Beware the bigger ships. Their bullets can still kill you with one hit.

You are right. I started this code nearly two years ago and I am a bit surprised it doesn’t look worse. :sweat_smile: But after implementing some features, I will clean that up and then port it for the Pocketstar.

1 Like

I guess, I made the final update for a while since the game is playable and makes some fun, but I am open for further suggestions.
@Pharap You mentioned some improvements mainly for progmem? Can you give me any hints before I start another game?

I’m going to have a quick scan through and push any savings I find to a fork rather than try to make an exhaustive list of all the space saving techniques that I’m aware of.


By the way, is it possible for player.speed to be anything other than 1?
If so I may have just found a bug.

I find one or two of the design choices a bit strange.

Mainly both the ship type and the player’s invincible counting using shifting instead of addition and subtraction.

Some of these could be resulting in wasted progmem.
In fact I know of at least one or two techniques that could be used to save space if you were able to swap to using consecutive numbers and add/sub instead of shifting.

Some just end up making the code look a bit cryptic,
like b.y = enemies[i].y + (enemies[i].height >> 1);.
(If you’d used a / 2 the compiler would have substituted for a bit shift anyway and it would make the intent clearer,
especially to readers who don’t know that >> 1 and / 2 are equivalent.)


Shouldn’t the line if(player.y-player.height < enemies[i-1].y) be if(player.y + player.height < enemies[i-1].y)?

The origin of the screen is the top left, not the bottom right.


I’ve hit 1032 bytes of progmem saved, so I decided to stop to give you chance to catch up and check that I haven’t accidentally changed the game behaviour somehow.

I can still see room for savings, but unless you’re planning to add more features or wanting to tidy the code up a bit, I’m not sure there’s much point in adding too many savings.

1 Like

Wow, you found a lot in the short time.

Mainly both the ship type and the player’s invincible counting using shifting instead of addition and subtraction.

I can’t say why I did this. It seems odd to me too.

(If you’d used a / 2 the compiler would have substituted for a bit shift anyway and it would make the intent clearer,
especially to readers who don’t know that >> 1 and / 2 are equivalent.)

At least for unsigned types you are right, which is the case here. I am not sure about signed variables (in case someone else reads this and is wondering about it). I guess, shifts make more sense if you actually look at the bits of a variable.

Shouldn’t the line if(player.y-player.height < enemies[i-1].y) be if(player.y + player.height < enemies[i-1].y)?

From a quick look at my code I’d say that few lines are garbage. I guess, I never created an enemy which uses this.

I can still see room for savings, but unless you’re planning to add more features or wanting to tidy the code up a bit, I’m not sure there’s much point in adding too many savings.

I believe, you already did a lot. Thank you for that. Since I would like to start a new project with a bit cleaner code and more thoughts before I start writing code, I won’t add too much to this one.

I implemented some of your suggestions from your fork already.

Yeah, I’ve become quite good at shrinking code.
It’s the main reason I was asked to be involved with Dark & Under.

For signed types it’s technically “implementation defined”:

For negative a, the value of a >> b is implementation-defined (in most implementations, this performs arithmetic right shift, so that the result remains negative).

But all the modern processors I’m aware of use arithmetic shift right (i.e. copy the sign bit).
At the very least AVR has an ASR instruction, so for the Arduboy it’s arithmetic shift right.

If you’re thinking of the variable an an array of bits then yes,
but personally my rule of thumb is to stick to the “intent” of an expression:

  • If your intent is to manipulate bits, use bit shift.
  • If your intent is to divide two numbers, use division and let the compiler figure out how to optimise.

(AVR doesn’t even have a ‘divide’ opcode, so the compiler has to generate a mixture of shift, add, subtract and multiply by inverse anyway, so whatever division you give it it has to do some thinking.)

That explains why the bug never seemed to crop up.

I found a few other bits of dead code but commented them rather than removed them.

I thought as much.

Maybe you or someone else will pick it up again at a later date and make use of the saved space.
Either way it was a good little excercise to see which savings worked and which ones didn’t.

If you were interested in all of them I would have just PRed, but didn’t want to make a PR before you were happy with the changes in case it seemed like I was forcing them on you.

There’s one space-saving change I can see that you missed.
Around here and here, getting rid of all the enemies[i].tick++; and moving the enemies[i].tick = (enemies[i].tick+1)%60; to the bottom. All those increments add up to a decent amount of progmem, and doing a mod each time shouldn’t be very detrimental to speed.

If you’re worried about speed, you could change it to:

case 4:
	enemies[i].tick++;
	if(enemies[i].tick <= 15) {
		enemies[i].direction = MOVE_LEFT;
	} else if(enemies[i].tick <= 30) {
		enemies[i].direction = MOVE_UP;
	} else if(enemies[i].tick <= 45) {
		enemies[i].direction = MOVE_LEFT;
	} else if(enemies[i].tick <= 60) {
		enemies[i].direction = MOVE_DOWN;
		enemies[i].tick %= 60;
	}
break;

One last note:
The one major stylistic complaint I have is the use of case fallthrough.

Aside from making the code harder to follow, it’s often unnecessary because the compiler’s usually quite good at figuring out when it can fuse two cases and use fallthrough.
For example I changed the movement code to get rid of all the case fallthrough and it didn’t increase the code size at all.
(Also playerShoots gave me a bit of a headache when trying to reduce its size because I kept forgetting about the fallthrough.)

If you do find that case fallthrough reduces the code size or simplifies the logic, then I’d suggest adding a label and a goto to make the fallthrough more explicit.
(See also: Duff’s device.)

… and then I hit him up again when making Karateka, Choplifter, Lode Runner and probably any future development I do!

2 Likes