Writing better code: The self taught coder

Boost has been useful in past projects, but it’s like the sausage factory cliche – you really don’t want to know what’s going on inside. Those templates get so complex that they completely broke the analysis tool I use for quite a while, and the error messages are near-indecipherable.

I’ve heard good things about this book.

Going to check that one out, it’s funny how books I bought 10 years ago are suddenly [more] useful because they deal with syntax rather than procedure/process.

I was a self taught programmer, and then I went to college. Learned lots of cool stuff in college… Ultimately, it was my programming hobby skills from when I was in middle school that allowed me to feel comfortable diving in to C++ code in my first job. Big thumbs up for code complete, they should dedicate a course in all comsci curriculums to study this book thoroughly.

Arise!

I thought I would glom onto an existing thread to pick the brains of our resident coders.

I’m dinking around in Gamemaker and using its proprietary ‘language’ GML to do some logic for a retro CRPG.

I’ve run into a bit of a stumper.

So I’m trying to do a game clock that advances one ‘turn’ each time the player does a keypress. This goes into a global variable called ‘turns.’ At the same time, there’s a separate global variable called ‘hours,’ and when that flips over at 24 it resets to zero and a ‘days’ counter ticks up, based on the ‘days’ variable going +=1 every time the ‘hours’ value divides by 24 with a remainder of zero (i.e. modulo).

I’ve been trying to alter the speed at which ‘hours’ ticks up. A player keypress might tick it up by 1, or .1, or .2, or .5, or whatever. I’ve been varying this value in order to get something that ‘feels’ right in terms of the passage of time.

What’s weird is that with certain values of the hour uptick (like 1, 0.25, or 0.5) the math all seems to go fine and the days flip over as expected. But with other values (like .2 or .125) something goes wrong and the days counter doesn’t go up, the hours don’t reset to 0, etc.

I wonder if there’s some weird under-the-hood issue with how the math works. Could it be an issue with whether the variable is defined as a float or an integer? The ‘hours’ variable can handle increments smaller than one (it displays the correct value to two decimal points), but I haven’t really dived into the mathy stuff of GML deeper than that. It’s intuitively hard for me to understand why, for example, increments of .5 would work but .2 wouldn’t.

Any thoughts appreciated!

Here is the code that checks for when a new day has passed (the check is done each ‘step’, meaning continuously at a rate per second defined by the user). The daySwitch variable is there so that the day counter doesn’t start ticking up rapidly on its own once you hit Hour 24.

if((global.hours mod 24 == 0) && (global.hours>0) && (daySwitch==true))
{
global.days += 1;
global.hours=0;
daySwitch=false;
}

Here is the code that advances the turns/hours counters on each player keypress (this is one of 4 keypresses with the same logic except that they move the player in a different cardinal direction).

//move the guy
x-=32;
//increment global turn counter
global.turns += 1;
global.hours +=[whatever increment];

Is your code single-threaded?

As far as I can tell being a newbie to these things, all Gamemaker code is by default single threaded:

You can have multiple things execute ‘per step’ but I think they are executed sequentially in the order in which they are laid down. However, I haven’t dug deep into this.

edit: Also, as far as my research has turned up, Gamemaker only uses floats for numeric variables.

Sorry, I don’t know anything about Gamemaker. Is this on Windows?

On other/*ix platforms I would be suspecting a threading problem for sure, with lots of naked global vars around and non-atomic operations. Does it have any/good tools for code execution tracing? Can you insert tracepoints or some way to log to file certain execution paths, so you can see exactly what order operations are happening?

if((global.hours mod 24 == 0) && (global.hours>0) && (daySwitch==true))
{
// logging pseudocode
fprintf(LOGFILE*,“Tracelog: INCREMENTING DAY %d %h %timestamp…”,global.days,global.hours,date());
// logging end gross sorry TBD WILLFIX etc
global.days += 1;
global.hours=0;
daySwitch=false;
}

Yeah, it’s on Windows. I don’t know much about the guts of Gamemaker (i.e. what’s under the hood of its ‘GML’ syntax) and I’m not a skilled enough coder to really know much about it anyway (I have beginner-to-intermediate Python, that’s about it).

Regarding tracing tools, not sure – I will have to do some research!

Thanks.

2014: may be out of date

GameMaker: trace/log function

Debugging GameMaker games can be fun. Or not fun. Depends on what your defintion of “fun” is.
Either way, in GameMaker: Studio, a new debugger was added, which you can (and probably should) enable via File - Preferences - “Scripts and Code” - “Use the new Debugger”.
That includes a profiler, a step-by-step debugger, and a bunch of other useful things.

It could also just be math wonkyness, especially if you are doing floating point math and exact integer comparisons, etc. Some debugger tools and/or logging will tell you.

The issue here is that floating point numbers are basically base 2 rationals. This means that numbers that appear very neat in a decimal number system actually can’t be represented in floating point. 0.2 is an example of that: the closest representable values are ~0.1999999999999999000799 and ~0.2000000000000000111.

The exception is that numbers that are of the form X/2^Y (with X and Y being integers) can be represented exactly. That’s why 0.5 and 0.25 don’t have any rounding issues. The value you wrote and the value it actually is are the same.

What concrete trouble does this cause your problem? Well, you’re assuming that 24 * 5 * 0.2 is exactly 24. But it’ll actually be a tiny bit more than that, so your modulo operation never results in 0. Just replace it it with global.hours >= 24. That’ll also allow you to remove the daySwitch variable.

So the reason .25 (1/4) and .5 (1/2) work is because they are numbers that fit cleanly into binary?

edit: Oh, I guess that’s exactly what you wrote in your penultimate paragraph.

I believe you may have the ability to reach hour 25 with your current code. Here :
global.hours +=[whatever increment];

Try replacing it something like this

     if (global.hours > 23)
            {
                global.hours = 0;
                global.days = global.days+1;
            }

Also I removed the day switch bool unless you needed it somewhere else.

Hope that helps!
Rod

If that’s the problem, better to just stick to integers and scale it all up by 100 “energy” or whatever, so you tick by 10,15,25, etc instead and roll over when (.hours > 2300)

Thanks for the help guys! I’ll try out these solutions.

Good point on the data type and worth making people aware of.

I find it interesting relatively how few people I work with seem to be aware of the limitations of floating point numbers. It’s creating a very few interesting edge behaviours in our software. But as it’s written based on an in house developed language that’s been shipped to 70+ clients, there is very little desire to rewrite the engine to store numbers differently. So we live with it. Also, we don’t do the kind of life and death calculations where those slight edge cases matter.

That said, most developers who join us have no clue that that’s a thing.

That’s true for pretty much everything in programming though. Something works, you assign it to the headspace of “don’t worry about it”, hit something that doesn’t work as expected, oops, looking into it the previous “works” was a bit of an optimistic take?

And thinking of a common example, a dynamic website, database, a few AJAX webservices, some verification on the client side, the amount of stuff you need, you either assign stuff to the “don’t worry about it” box, or you never get anything done… :D

He, he. That’s a fair point. I do a fair bit of that. :)

So, is derpspace abandoned?

Is there going to be a class action?

Late to the party, but I wanted to add my 10:

I don’t know how GML behaves, but in C/C++ the mod operator is expecting integer % integer. Throwing a float in there will either throw an error or generate unexpected results (see previous explanations of floating point numbers).

@Rod_Humble’s solution to change the logic of the test is good one.
((comparison of floats) == 0) will generally make you a sad panda.