[WIP] Memory Rhythm

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