Simple Utilities

I made this non-game with 5 simple utilities you can use.

You can choose from utilities like

  • 2 6-sided dice roller
  • d20 roller
  • Custom dice roller
  • Stopwatch
  • Magic 8 Ball
  • Bubsy 3D (wait, that’s not right!)

This app is perfect for many things. Use the Magic 8 Ball when you’re making important decisions like if you should spend your life savings on Taco Bell (“Yes definitely”).
Use the dice when you need to play a little bit of D&D on the go (“Darn, I dropped the dice out the window of the plane! Good thing I have this app!”).
And use the stopwatch when your phone’s stopwatch is too accurate (“I’m really craving a low quality stopwatch, what can I use?”).

build.hex (31.6 KB)

I appreciate feedback, thanks!

5 Likes

A few quick tips…


You may also want to wrap your printed strings with the F macro.

E.g.

  arduboy.print(F("SIMPLE UTILITIES\n\nPRESS A OR B\n\n\n"));
  arduboy.print(F("Mikmoomamimocki"));
  arduboy.print(F("\n\nVersion "));
  arduboy.print(F("1.0"));

And you may find it simpler to use println instead of print in cases where you need a new line after printing something.

E.g.

  arduboy.println(F("SIMPLE UTILITIES\n\nPRESS A OR B\n\n"));
  arduboy.println(F("Mikmoomamimocki"));
  arduboy.print(F("\nVersion "));
  arduboy.print(F("1.0"));

I don’t have time to explain at the moment, but this might help you make your 8-ball more efficient:

(There’s a few comments but you might not know enough to know how to use it yet.)

1 Like

Ok, now I do have a moment to point out a few more things…


This:

Can be simplified to this:

bool pressedButton()
{
  return (arduboy.justPressed(A_BUTTON) || arduboy.justPressed(B_BUTTON));
}

As a rule of thumb, if you ever see an if(condition) return true; else return false; pattern, you know the returns are redundant and you can just return the condition itself.

Hopefully you can see why that works.


This:

Is… well I won’t say ‘unsafe’ as such, but essentially the user can go beyond the number of states that are actually available, which if nothing else might confuse people a bit.

I see that you add some custom button handling in state 1 and 6, but it’s a bit redundant because you could handle all the state changes with the same code.

You’d be better off either ‘clamping’ it so they can’t go outside the 1 to 6 range, or ‘wrapping’ it so that going right on 6 takes the user back to 1, and vice versa.
E.g.

constexpr int firstState = 1;
constexpr int lastState = 6;

void handleStateChange()
{
  if(arduboy.justPressed(LEFT_BUTTON))
  {
    if(gameState > firstState)
    {
      --gameState;
    }
    else
    {
      gameState = lastState;
    }
  }
 
  if(arduboy.justPressed(RIGHT_BUTTON))
  {
    if(gameState < lastState)
    {
      ++gameState;
    }
    else
    {
      gameState = firstState;
    }
  }
}

If you did that, you could use the same state change code for all states, which means you could put the state change function outside your main switch (i.e. reduce redundant code).

Note the use of the constants. This has two benefits:

  • It makes the code easier to read by getting rid of ‘magic numbers’ and replacing them with meaningful names.
  • If you want to change a value then you only need to change it in one place, instead of hunting through your code to change lots of instances of the same number. (Which is especially bad if you can’t remember which number means what.)

You’d probably be best off doing this for your game states as well, so you’re dealing with named states instead of numbered states. (There is actually a better option for that than constants, but I don’t want to dump too much information on you at once.)


This is also probably a good time to mention that if you only have one ‘statement’ (roughly speaking: a line ending in a semicolon) then you can omit the braces:

void handleStateChange()
{
  if(arduboy.justPressed(LEFT_BUTTON))
  {
    if(gameState > firstState)
      --gameState;
    else
      gameState = lastState;
  }
 
  if(arduboy.justPressed(RIGHT_BUTTON))
  {
    if(gameState < lastState)
      ++gameState;
    else
      gameState = firstState;
  }
}

But it’s up to you whether you like that style or not.


I’ve decided not to explain the FlashStringHelper thing for now, because I realised that it probably won’t make much sense to you at the moment and probably isn’t that useful for what you’re doing anyway. (Or at least, it wouldn’t be until you started running low on memory.)

When you’ve learnt about arrays it won’t be quite such a leap, but for now it’s probably a jump too far.


Lastly, just as a bit of trivia: random isn’t actually evenly distributed, so when you roll those dice some numbers will actually occur more frequently than others. (That’s worth knowing if you were using the dice as the dice for a board game or something, because you could abuse the odds.)

1 Like

Although your changes to handleStateChange are much more elegant than @Mikmoomamimocki’s original code, he is handling the wrapping of the gameState in the code properly.

Here >

and here >

I acknowledged that:

But using the way I demonstrated also means that the state change handling function can be called once in loop instead of once per case, as I said:

An added avantage to that is that if a new case is added after 6 then only the lastState variable has to be changed. With the current approach, that special block in case 6 has to be moved from case 6 to case 7 and replaced with a call to standardStateChange.

(Though admittedly, it will break slightly on state 0 because state 0 behaves differently. That’s easy enough to fix though.)

Thanks for the help! I just have a few questions.

What does the F macro do? I don’t see a difference in the code aside from the F itself.

Thanks for pointing this out! I understand that this code is redundant, but what is the advantage of making it less redundant, besides readability?

What’s an alternative to random that I could use?

Thanks for helping!

1 Like

Maintainability?

Its all part of the learning.

2 Likes

The difference is the memory usage.
(Which you can view in the report you get when the code is compiled.)

The Arduboy has three types of memory:

  • 2560 byte of RAM
  • 32768 bytes of Progmem
  • 1024 bytes of EEPROM

RAM is readable and writable, so nearly all of your global variables and some of your local variables will end up here. It’s also the only of the three that’s ‘volatile’, which means the memory contents vanish when you turn the Arduboy off.

Progmem is read-only (except when in bootloader mode, which is how new games are loaded into memory), so all of the compiled machine code and more or less all of your constants in up in progmem. But some things you have to add a special PROGMEM marker to, so the compiler knows to keep it in progmem.

EEPROM is readable and writeable, and it keeps its memory when you turn the Arduboy off. That’s why it’s used for save data. It also has a limited number of read and write cycles, which is why it should be used sparingly (i.e. don’t access it every frame, only access it when you need to load or save), and why people try to spread their save game data out across the address space.

The reason this is relevant to the F macro is that the F macro is another special macro like PROGMEM. Normally strings end up in RAM and you need the F macro to put them in progmem.

The F macro only works with the print and println functions though, for reasons I won’t go in to at the moment.

(It’s to do with data types and function overloading, so you’d need to understand those first before the explanation would make sense. I can explain those if you want, but I don’t want to overload you with information.)

Firstly, never underestimate the value of readability. In the immortal words of Donald Knuth:

Programming is the art of telling another human being what one wants the computer to do.

And

Programs are meant to be read by humans and only incidentally for computers to execute.

Secondly, removing redundancy usually means that you generate less machine code, which is important on the Arduboy (and all embedded devices) because there’s a smaller memory limit than on (for example) a desktop computer.

In this case I suspect the compiler is smart enough to remove the redundancy, but the readability aspect is still notable. Why write “if x is true then it is true, otherwise it is false” (a ‘tautology’) when you can just refer to “x”?

Thirdly, as @filmote hinted at, if you have less redundancy then your code is easier to maintain because there’s fewer bits to keep track of.

See also: the “Duplicate code” article on Wikipedia.

You probably don’t need to worry about that, that was just a bit of trivia.

The problem is called ‘modulo bias’, and it’s only really a problem for security applications and gambling, for games it’s a minor issue. It’s also a lot more severe with larger numbers. For small numbers it’s less of an issue. If you want to know more, read this StackOverflow answer.

If you really want to eliminate the problem...

Use the following function:

uint16_t uniformRandom16(uint16_t min, uint16_t max)
{
	return (min + uniformRandom16(max - min));
}

uint16_t uniformRandom16(uint16_t max)
{
	const uint16_t min = ((~max + 1) % max);

	uint16_t result = 0;

	do result = rand();
	while(result < min);

	return (result % max);
}

Assuming rand and random are themselves free of bias.

There’s several other ways to do this, but that’s the simplest one I had to hand.

The one from the SO answer would also work if properly adapted:

int uniformRandom(int min, int max)
{
	return (min + uniformRandom(max - min));
}

int uniformRandom(int max)
{
	const int limit = (RAND_MAX - (RAND_MAX % n));

	int result = 0;

	do result = rand();
	while(result >= limit);

	return (result % max);
}
1 Like

It tells the compiler to store the string permanently in the atmega’s flash memory instead of consuming valuable ram as well. If you have a bunch of text or large constant arrays it’s better to stow them in the 32K of available flash (program memory) as opposed to the 2K of RAM.

1 Like