[WIP] Memory Rhythm

To move files on GitHub, just edit the file and add the new path in front of the file name, using / as the separator. The new directory (path) will be created if it doesn’t exist. To move to the parent of a directory, use ../

1 Like

This blew my mind and kinda made everything click. So, if I understood right, rnd() is the key here and thats how it ‘steps’ through actions? So if rnd() is ran once from X seed during learn it running once for play with have the same number since it shares the same seed.

That’s brilliant and is super exciting to me. Can’t wait to implement this!


Can you go into more detail on the animation side of things? How is action affecting drawSelfMasked()'s frames? Maybe I am missing something here.

Simple really. There are 8 possible actions (Idle, Jump, Duck, Slide Left, Slide Right, Raise Left, Raise Right, and Done) and each action corresponds to a single frame of the sprite. So using the archived TeamARG sprite sheet converter I have a 24x32 sprite with 8 frames one for each action. The animation part is handled by actionDuration and is set to 16 and decremented once per step inside the check to everyXFrames giving us 16 steps per animation. Each animation can be setup to do something different, for example if the action is SLIDING_LEFT or SLIDING_RIGHT then the roadOffset variable gets adjusted accordingly, in the case of the JUMP action the sprite is offset by the preset values in the lookup table (one value for each of the 16 steps).

The reason for drawSelfMasked is because my sprites are 1 color (white) with black for transparent. The masks are helpful for drawing sprites with both white and black while also having transparency as the mask will clear away whatever’s behind it (but only the parts masked out) before then drawing the sprite over that.

2 Likes

I see that you’re “abusing” TONE_HIGH_VOLUME and just playing every tone at high volume. It would add a bit of code but you could call
tones.volumeMode(VOLUME_ALWAYS_HIGH);
at the beginning to accomplish this instead of putting TONE_HIGH_VOLUME on every note .

If you find the regular volume too low, that may not be the case for others. You could add a user option to select the volume and use
tones.volumeMode(VOLUME_ALWAYS_NORMAL);
or
tones.volumeMode(VOLUME_ALWAYS_HIGH);
to accomplish this.

1 Like

Yeah it’s hard to tell how it sounds to other users since I have tinnitus so these little piezo speakers are muffled quite a bit. Even with high volume I can barely hear it.

The next update I planned on first adding a menu to set options, and enter a Tutorial mode (allows you to just press buttons to do different poses without any scoring), Demo mode (keeps playing a randomly generated set of sequences), and Normal mode (current mode).

Since I didn’t need the extra space I’m calling the normal arduboy.begin() so the startup functions allowing the user to enable/disable audio work as expected at least.

2 Likes

The issue is with me not understanding bitmaps and how drawing a frame in a bitmap works. I personally need to research and play around with this.

Off to google and the forums!

EDIT:

I actually am really enjoying the tones even through they are loud. Going with a deeper tone for jumping is satisfying for some reason. Plus the game it self is a nice kill a minute game that’s fun.

Though program size isn’t yet an issue here, with the sprite images you’re currently using you might benefit from using Arduboy2Base::drawCompressed() instead of Sprites::drawSelfMasked. I found the images compressed down to about 45% of the original size. For your 8 24x32 sprites you would save about 400 byes.

To keep the code similar to what you have now you could use an array of pointers to the individual sprite arrays. Whether the additional code required to use drawCompressed() would nullify the data savings is something that would have to be tested.

It should be noted that you could have used the Arduino random() function and re-seeded it with randomSeed() as necessary.

One thing I find a bit annoying is that you have to wait for an action to complete before you can press the next button in the sequence. You should be able to enter the sequence as fast as you like.

You could test for a new button using justPressed() while the current action is playing and, if detected, abort the action early and process the new button.

I don’t know if ending the jump sequence early would make things look strange, though. You might need to add a short “quickly end action” sequence when the new button press is detected. This would erase the button icon and return the character to idle in only a few frames. EDIT: maybe you could “quickly end action” by skipping everyXFrames() until the action is complete.

Now, I would like to see your take on a simple game. There is still time left on making a simple game.

True, however I’ve found that this form of RNG is basic enough that if I want to have full control over it’s functionality (sometimes it’s helpful to seed it, generate some numbers, store it’s current state, generate some new numbers, jump back to a previous state). While it’s possible to do that I find it easier to to use this simplistic xorshift RNG.

Yeah, it’s a bit annoying, but it’s also very early on mostly getting it to a releasable state as quickly as possible. Later, when I get a chance, I’ve got ideas for fast forwarding the animation. At the same time I feel the current limitation forces you to pace yourself otherwise it just becomes too much like simon says. I wanted to have the button presses correspond to doing something on screen.

2 Likes

I turn my back for five minutes and suddenly there’s a 16 new comments. :P


You could create some organisations - one for your Arduboy stuff and one for your Pokitto stuff.
Not really what organisations are intended for, but it would probably work.

(I was also going to suggest what @MLXXXp said, but he beat me to it.)

I’d give bonus likes/points for this if it were an option.

I want to print this out and frame it so I can point to it if I ever get any complaints. :P

I expect I’ve probably told you this before, but just to raise awareness of it, using % with a non-power-of-two will incur modulo bias.

There’s actually two ways of doing it:

Do I take it that this means you didn’t know how PRNGs work before this?

If so I’m genuinely surprised.

Now I’m tempted to run it with the volume on to see what it’s like since I have an almost opposite problem.

It’s quite simple really…
Here’s an example adapted from Easter Panic:

constexpr uint8_t smallRabbitWidth = 8;
constexpr uint8_t smallRabbitHeight = 8;

constexpr uint8_t smallRabbit[] PROGMEM
{
	// Dimensions
	smallRabbitWidth, smallRabbitHeight,
	
	// Frame 0 (North)
	0x00, 0x78, 0x7E, 0x78, 0x78, 0x7E, 0x78, 0x00,
	
	// Frame 1 (East)
	0x00, 0x00, 0x78, 0x7E, 0x48, 0x78, 0x00, 0x00,
	
	// Frame 2 (South)
	0x00, 0x78, 0x4E, 0x78, 0x78, 0x4E, 0x78, 0x00,
	
	// Frame 3 (West)
	0x00, 0x00, 0x78, 0x48, 0x7E, 0x78, 0x00, 0x00,
};

The Sprites image format is an array of uint8_t that consists of a width, a height, and then a sequence of frames with each frame being a single bitmap.

The sprite drawing functions figure out how large each frame should be using the width and height, and from that they can access the correct frame by offsetting the start point by that many bytes.

So in this case each frame is (8 * 8) / 8 ((width * height) / bits_per_uint8_t), which is 8 bytes.
Then the start of any specified frame is smallRabbit[2 + (8 * frameIndex).

To simulate frames with arduboy.drawBitmap or Arduboy2Base::drawCompressed you’d typically use an array, although theoretically you could actually use a Sprites-formatted image with the right code.

E.g.

inline void drawBitmap(Arduboy2 & arduboy, int16_t x, int16_t y, const uint8_t * image, uint8_t frameIndex)
{
	const size_t width = pgm_read_byte(&image[0]);
	const size_t height = pgm_read_byte(&image[1]);
	const size_t frameSize = ((width * height) / 8);	
	const uint8_t * frame = &image[2 + (frameSize * frameIndex)];
	
	arduboy.drawBitmap(x, y, frame, width, height, WHITE);
}

Though that would make the game harder to port accurately.
random doesn’t guarantee what PRNG engine is used,
whereas using a specific PRNG engine does provide that guarantee,
thus allowing the game to play exactly the same sequences even when ported.

Some Arduino environments don’t even provide random (as I found out when publishing my fixed points library).

Xorshift is probably cheaper anyway.

3 Likes

Normally I try to avoid double-posting, but I think in this case it makes sense for this to be separate…


If we’re critiqueing the code, my main complaints/suggestions would be:

  • You should use constexpr instead of static const
    • Con.5: Use constexpr for values that can be computed at compile time
    • static (in the context of global variables) actually means “only make this visible to this translation unit” and hence is completely pointless here.
      • Presumably you’ve been told the partial myth that static const saves memory or is more efficient. That’s a C-ism and there’s only limited truth to it. In C static const int x = 0; and const int x = 0; have different meanings, but in C++ that isn’t the case (for a global variable at least).
    • constexpr is better than const because it guarantees that the variable can be computed at compile time
  • You shouldn’t use all caps (typically called “macro case” in the C++ world) for anything other than macros
  • Ideally you ought to be using an enum class instead of semantically unrelated variables
    • Enum.2: Use enumerations to represent sets of related named constants
    • enum classes are type safe and provide a semantic relationship between several alternative values.
    • If you use an enumeration in a switch and forget to include a case label for any value, you’ll get a warning, which can help prevent mistakes
    • E.g. An enumeration for action would look like this:
    enum class Action : uint8_t
    {
    	Idle,
    	Jump,
    	Duck,
    	SlideLeft,
    	SlideRight,
    	RaiseLeft,
    	RaiseRight,
    	Done,
    };
    
    Action action = Action::Idle;
    
  • state comparisons should ideally be in a switch rather than a long list of ifs because switch is more readable and more importantly is a better way to communicate your intent.
    • switch effectively says “I want to do several different things depending on the exact value of this specific variable”, whereas if is much more broad and doesn’t communicate the intent as well.

Part of the reason I’m picking on these things is because I want beginners to learn good habits early on.

Less important but still worth mentioning:
  • You don’t actually need a sprites object, you can just do Sprites::drawSelfMasked.
    • The advantage of the latter is that getting access to global variables is more awkward than getting access to static functions. The former requires an extern declaration somewhere, the latter only requires #include <Arduboy2.h> or #include <Sprites.h>
  • Splitting loop up into more functions would make it a bit easier to digest.
    • Contrary to what you might expect, this actually reduces code size. The compiler has an easier time digesting lots of smaller/medium-sized functions than it does one large function. Think of it like this: it’s easier to eat slices/mouthfuls of cake than it is to eat a whole cake in one go.
  • It would be nice if your arrays are also marked constexpr to indicate that they’re never modified.
  • Personally I (among others) consider chained assignment to be bad form.
  • A few more blank lines would help the readability.
    • (In particular I was almost caught out by the if after else if (arduboy.everyXFrames(actionRate)), for a moment I thought it was another else if in the chain.)
  • rnd() probably ought to return the calculated seed.
    • E.g. uint32_t value = rnd(); if (action == (value >> 1) % 6 + 1) ...
    • It actually works out cheaper because local variables are typically cheaper than global variables because they can be kept in registers and the compiler can prove that they haven’t been changed. The compiler can’t prove that a global variable hasn’t been changed since it last read the value. This is especially true on AVR.
  • rnd() isn’t really a very descriptive name. Contractions make life harder. I would presume that’s especially true for beginners.
    • To put it another way, contractions are how C ended up with function names like strxfrm which C++ inherited. Without checking the documentation I haven’t the first clue what that function does and I’m really glad I’ve never had cause to use it. Theres a long list of similar offenders.
  • numMoves is also a relatively cryptic name. Contractions aside, ‘number’ in what sense? Quantity, capacity, count, limit?
    • For comparison, names like actionRate and currentMove are good names because they’re descriptive and precise.
  • Really rnd ought to be implemented as a class to make it more useful, but I can understand not doing that to try to make it easier for beginners to digest.

Here’s a version of your code incorporating a number of these suggestions.
(It’s 50 bytes smaller as a result.)

Code
#include <Arduboy2.h>
#include <ArduboyTones.h>
#include "graphics.h"

enum class Action : uint8_t
{
	Idle,
	Jump,
	Duck,
	SlideLeft,
	SlideRight,
	RaiseLeft,
	RaiseRight,
	Done,
};

enum class State : uint8_t
{
	MainMenu,
	Learn,
	Play,
	Done,
};

constexpr uint8_t actionButtons[7]
{
	0,
	UP_BUTTON,
	DOWN_BUTTON,
	LEFT_BUTTON,
	RIGHT_BUTTON,
	A_BUTTON,
	B_BUTTON,
};

//Using Arduboy2 instead of Arduboy2Base for now so I can use arduboy.print for debug info
Arduboy2 arduboy;
ArduboyTones tones(arduboy.audio.enabled);
Sprites sprites;

//notes starts at 208hz since below that can't be heard well on the Arduboy's tiny piezo
//notes stops at 7902hz for similar reasons
//I've also removed all the sharps/flats to keep it to normal notes for increased pitch as the player progresses
//5 octaves with 7 notes per octave and 6 actions to perform leaving 1 note for the interim beat.
constexpr uint16_t notes[]
{
	 262, 294, 330, 349, 392, 440, 494, //octave 0
	 523, 587, 659, 698, 784, 880, 988, //octave 1
	1047,1175,1319,1397,1568,1760,1976, //octave 2
	2093,2349,2637,2794,3136,3520,3951, //octave 3
	4186,4699,5274,5588,6272,7040,7902  //octave 4
};

constexpr uint8_t jumpOffsets[]
{
	4, 8, 11, 14, 16, 18, 19, 20,
	20, 19, 18, 16, 14, 11, 8, 4
};

uint32_t score = 0;
uint8_t octave = 0;

Action action = Action::Idle;
uint8_t actionDuration = 0;
uint8_t actionRate = 5;
uint8_t increaseRate = (6 - actionRate);

int8_t roadOffset = 0;
State state = State::Done;
uint8_t numMoves = 3;
uint8_t currentMove = 0;

uint32_t startSeed = 0;
uint32_t currentSeed = 0;

uint32_t generateValue()
{
	currentSeed ^= currentSeed << 13;
	currentSeed ^= currentSeed >> 7;
	currentSeed ^= currentSeed << 17;
	return currentSeed;
}

Action generateAction()
{
	return static_cast<Action>((generateValue() >> 1) % 6 + 1);
}

void setup()
{
	arduboy.begin();
	arduboy.clear();
	arduboy.setFrameRate(60);
	
	startSeed = arduboy.generateRandomSeed();
	currentSeed = startSeed;
}

void loop()
{
	if (!arduboy.nextFrame())
		return;
		
	update();
	draw();

	arduboy.display(CLEAR_BUFFER);
}

void update()
{
	if (actionDuration == 0)
	{
		switch(state)
		{
			case State::MainMenu:
				updateMainMenuState();
				break;
		
			case State::Learn:
				updateLearnState();
				break;
				
			case State::Play:
				updatePlayState();
				break;
				
			case State::Done:
				updateDoneState();
				break;
		}
	}
	else if (arduboy.everyXFrames(actionRate))
	{
		--actionDuration;
		if (actionDuration <= 4)
		{
			tones.noTone();
			if (action != Action::Jump) //end ducking animation slightly early
				action = Action::Idle;
		}
		
		if (action == Action::SlideLeft)
			--roadOffset;
		else if (action == Action::SlideRight)
			++roadOffset;
	}

	if (roadOffset < 0)
		roadOffset += 8;
	else if (roadOffset >= 8)
		roadOffset -= 8;
}

void draw()
{
	drawDebugInfo();

	//Draw the current action
	sprites.drawSelfMasked(112, 0, icons, static_cast<uint8_t>(action));

	//Draw the sprite
	if (action == Action::Jump)
		sprites.drawSelfMasked(52, 24 - jumpOffsets[actionDuration], sprite, static_cast<uint8_t>(action));
	else
		sprites.drawSelfMasked(52, 24, sprite, static_cast<uint8_t>(action));

	//The screen is 16 tiles wide meaning at most 17 can be visible
	for (uint8_t i = 0; i <= 17; ++i)
		sprites.drawSelfMasked(i * 8 - roadOffset, 56, road, 0);
}

void updateMainMenuState()
{
	// Currently unimplemented
}

void updateLearnState()
{
	action = generateAction();
	actionDuration = 16;
	
	tones.tone(notes[static_cast<size_t>(action) - 1 + octave * 7] + TONE_HIGH_VOLUME);
	++currentMove;
	
	if (currentMove == numMoves)
	{
		state = State::Play;
		currentMove = 0;
		currentSeed = startSeed;
	}
}

void updatePlayState()
{
	arduboy.pollButtons();
	action = Action::Idle;
	
	for (uint8_t i = 1; i < 7; ++i)
	{
		if (arduboy.justPressed(actionButtons[i]))
			action = static_cast<Action>(i);
	}
	
	if (action != Action::Idle)
	{
		if (action == generateAction())
		{
			tones.tone(notes[static_cast<size_t>(action) - 1 + octave * 7] + TONE_HIGH_VOLUME);
			actionDuration = 16;
			
			++currentMove;
			++score;
			
			if (currentMove == numMoves)
			{
				currentSeed = startSeed;
				score += numMoves;
				currentMove = 0;
				
				++numMoves;
				--increaseRate;
				
				if (increaseRate == 0 && actionRate > 1)
				{
					--actionRate;
					++octave;
					increaseRate = 6 - actionRate;
				}
				
				state = State::Learn;
			}
		}
		else
		{
			state = State::Done;
			action = Action::Done;
		}
	}
}

void updateDoneState()
{
	if (!tones.playing())
		tones.tone(notes[4] + TONE_HIGH_VOLUME, 100, notes[2] + TONE_HIGH_VOLUME, 100, notes[0] + TONE_HIGH_VOLUME, 100);
		
	arduboy.pollButtons();
	
	if (arduboy.justPressed(A_BUTTON | B_BUTTON))
	{
		startSeed = currentSeed = arduboy.generateRandomSeed();
		score = 0;
		octave = 0;
		action = Action::Idle;
		actionDuration = 0;
		actionRate = 5;
		increaseRate = (6 - actionRate) * 2;
		state = State::Learn;
		numMoves = 3;
		currentMove = 0;
	}
}

void drawDebugInfo()
{
	//Draw some debug info
	arduboy.setCursor(0, 0);

	//Always surround constant strings in F() in order to put them in PROGMEM and save precious RAM
	arduboy.print(F("Score: "));
	arduboy.println(score);
	arduboy.print(F("Speed: "));
	arduboy.println(6 - actionRate);
}
2 Likes

I’ve submitted a pull request on GitHub to do this.

But the game starts with a call to generateRandomSeed() in setup(), so there’s no intent to ever have exactly the same sequence. It may be beneficial during development but not after release.

You didn’t put markdown code tags around your code. I added them.

Fortunately I noticed.
(Put it this way, @tuxinator2009 almost ended up with two PRs doing exactly the same thing.)

True. Xorshift might still be cheaper though.

That depends on the game.
There are some games where it’s expected that the same seed will always generate the same level.
For this game I could certainly imagine different seeds being assigned level numbers were it to be more fleshed out.

As it currently stands though, the only confirmed benefits are improved portability.
(As I mentioned before, some Arduino environments don’t have random.)

I’m fairly certain that I did have them at some point.
I’m guessing that instead of pasting the code in between the ```cpp ``` markers I ended up selecting the markers as well and thus overwrote them.
Either way, no harm done.

1 Like

I’ve merged that request, though I had some minor local changes to backup first.

There’s a reason for this. There’s no debug reason to try and have the same seed every time. What’s happening is when the state switches between the “Learn” and “Play” state it needs to start the sequence back at the beginning again and repeat the entire sequence (adding one step at the end in “Learn” mode). So when a game is started it gets a random starting seed value (IIRC the one in the Arduboy2 library uses a combination of the time value and a value read from an unconnected analog input pin). From there it keeps track of the currentSeed as it progresses through each step and then resets currentSeed to the startSeed so as to rewind the sequence back to the first value. The alternative (and slightly more common approach) is to pre-allocate a dynamic array large enough to contain more steps than you would think most people would be able to perform anyway (numMoves is an unsigned 8-bit integer so it would only take 256 bytes of RAM to create such an array). Then the array is either filled in in advance or filled in as each step is added. With this approach numMoves can be changed to any integer type (even a 64-bit unsigned integer on other platforms) and you only need to keep track of it’s initial seed and the currentSeed for whichever step your on. That’s why I initialize the seed with a call to generateRandomSeed() even though I’m using my own PRNG.

Was too clever of a trick that you guys missed it’s purpose? :P

I saw it in its original form, once seen it cannot be unseen :P

I’ll implement this part later as I don’t have the time right now. Unless you want to pull the latest changes and submit a PR for it. Might be useful since the main purpose of this project was a sort of demonstration of getting started making a game. As such I’ve tried to maintain very frequent commits so that people can use the commit history to better learn the process (the initial commit was literally some blank files and empty setup and loop functions with nothing else).

Worth mentioning that it’s actually kind of beneficial for it being written the way it is since this is how most beginners would end up writing something similar (though not using constexpr is different and I’ll explain below). Also I wrote it in a hurry while doing some random doodling but with code :P

Actually I used static const in place of #define since I believe it was you that mentioned it was better because it ensures type safety. Partly why I still used the ALL_CAPS macro case.

Of course, and part of the reason I haven’t used some of these modern designs is I got out of touch with programming (life got in the way of hobbies for too long) right around the time frame c++11 was finally being standardized. Up until then I lost interest in trying to implement some things because each compiler had it’s own implementations at that point (and I was developing a large-scale software with cross-platform and cross-compiler portability in mind).

Yeah I borrowed it cause it was the simplest PRNG off the top of my head and was mentioned in a recent magazine. Forgot to rename it to something like randomize. Now that I think about it it could be made more portable (as in easily usable elsewhere) if it was changed to take a seed as a parameter and return the next pseudo-random value.

That’s actually the only one I was having trouble coming up with a decent name for. Others that came to mind was sequenceSize or numSteps but couldn’t really decide on a descriptive enough name. As you noticed with actionRate and currentMove one of my first and foremost thoughts when creating a variable is giving it a good name.

For me these are the loose criteria when coming up with a name:

  • Avoid abbreviations at all cost. Like you mentioned what does strxfrm even do, I don’t know and I don’t want to have to dig through documentations to find out.
  • Avoid excessively long names (keep it 1-3 words, 4 tops). I’ve seen variable names like variableToTrackNumberOfChangesMade which can easily be written as numberChangesMade or even just changesMade because it’s already stated in the language’s overall syntax that it is a variable.
  • Avoid 1-3 letter 1-word names (some exceptions like x, y, z for coordinates or the common i for temporary iterators). This can lead to typos not being caught at compile time because if you have a variable c that points to a char variable for example and a variable d that points to some data you could easily typo them (c and d are right near each other on the keyboard) and cause all kinds of painful debugging to track down what went wrong.
  • Prefixing pointer types with p? I’m still on the fence on this one myself, I don’t like the old style of all variables being prefixed to indicate their type, the only time I do that is when using Qt5 or some other complex UI toolkit so I remember what type of UI element the variable is (ie. bVariableName indicates it’s a button and chkVariableName indicates a checkbox, etc). Sometimes I do prefix pointer variables with a p but usually only if its pointing to a variable in the same scope or for char iterators (like for (char *pChar = someString; *pChar != '\0'; ++pChar)). Any thoughts on this one (or helpful links @Pharap as I’m sure you have some).
1 Like

I’m not an expert in every thing. :P

At some level I’m sure I did but seeing it at that level gave a better understanding. Plus in my day to day programming I don’t really need to use random numbers. With the Arduboy its life.

After spelling it out for me it is simple. It’s a multi-dimensional array while being one-dimension. Which, now that I think about it is how most screens work? A one dimensional array is probably cheaper if you tell it what the dimensions are.

Using randomSeed() to re-seed the random() function will do just that. The random() function is a pseudo-random generator, just like your rdn().

I used the same technique with my ArduboyLife sketch to allow replaying the same random starting pattern.

This is my favorite game on the Arduboy BTW. In between Arduboy projects it is always loaded on mine to show off how cool the Arduboy is.

1 Like

I can do if I have chance and if you’re happy with all of the changes.

I prefer to communicate before creating a PR because it’s easier to write a new PR with all desired changes than it is to remove an undesired change from an existing PR.

Typically beginners write code as one long block because they lack the skills to do anything else, not necessarily because it’s easier for them to understand.

Did I actually specify static const or something else?

If I ever actually said this then I’d like to find this offending comment so I can correct it with a large hammer.

To quote the Cpp Core Guidelines:

Note Do not use ALL_CAPS for constants just because constants used to be macros.

So there’s no wriggling out of it. :P

I.e. turned into a pure state transforming function.

One of the things functional programming has taught me is that any system reliant on mutable state can be expressed as an equivalent system in which there is no mutable state, only pure functions and immutable write-once variables.

(That said, when creating PRNGs I find it helps to supply both pure state transition functions and an impure class that adheres to the expectations of <random> - and in particular satisfies the UniformRandomBitGenerator requirements.)

If I understood what it did I’d offer up an alternative name.

I actually read the documentation and I’m still not entirely sure.
It’s something to do with C locales and being more efficient to use strxfrm and then strcmp rather than using lots of strcoll.

In other words, you’re unlikely to ever need it unless you’re sorting a list of words in a language other than English.

Technically it’s also already stated by the language’s semantics that it’s a number,
which is one of the issues I have with using ‘num’ or ‘number’ in a variable name.

My other issue is that ‘number’ is a vague and somewhat meaningless concept.
To be useful a number needs a context - is it a weight, a length, a cardinal, an ordinal et cetera.

Though in an ideal world, that kind of information ought to be encoded in the type system.

Personally I still prefer index when the intent is to be used an an array index and counter when the intent is to be used just as a simple accumulative counter.

x, y, z and w are firmly rooted mathematical convention though - they actually have a proper meaning.
(Plus they don’t really have any meaningful alternative.)

Pointers don’t deserve special treatment.

After all, where does that put smart pointers?
They’re classes, but they behave like pointers with respect to * and ->.
Do you start prefixing std::unique_ptr with up and std::shared_ptr with sp?

Of course the real question is “what’s the motivation behind prefixing pointer variables with p?”.

To give it its proper title: “Hungarian Notation”.

I’ve seen lots of arguments for and against it over the years.
I’m firmly in the camp that believes hungarian notation is a poor attempt to make up for a lack of strong typing.

It should then come as no surprise that, in regards to the canonical Stack Overflow question on Hungarian Notation, I firmly back this answer as being the proper answer.

If you’ve never seen it and you have the time to spare (and the attention span),
watch Bjarne Stroustrup’s 2012 ‘Going Native’ Keynote lecture.
It’s a really important talk because it challenges a lot of people’s assumptions by demonstrating that:

  • std::vector can outperform linked lists
  • high level code can be more efficient than low level code

But the main reason I refer to it is because he discusses encoding SI units into the type system (starting at 18:50).

One of the examples he gives:

Speed sp1 = 100m / 9.8s; // very fast for a human
Speed sp2 = 100m / 9.8s2; // error (m/s2 is acceleration);
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit)
Acceleration acc = sp1 / 0.5s; // too fast for a human

The idea that you could write code like auto velocity = 10_km / 1_s; and end up with velocity having a deduced type of si_units::kilometres_per_second really demonstrates the benefits of a strong type system.

The C++ standard library doesn’t have a standardised SI unit library, but it does have something along similar lines in the form of the <chrono> library.

As of C++14, if you add using namespace std::chrono_literals to a local scope, you can write auto time = 0.5min; and henceforth time will be a std::chrono::minutes value equivalent to half a minute.
Being able to write std::this_thread::sleep_for(1000ms); makes the world a much brighter place.

Personally I’d prefer xxxButton and xxxCheckbox.


Nope, you’ve lost me.

Cheaper than what?

2 Likes