Arduboy Classes and Header files?

Its very similar to drawGridAt. Heres the code.

void Game::updatePlayerPanel(uint16_t x, uint16_t y) {
  for (uint8_t yi = 0; yi < 2; yi++) {
    uint16_t yPos = y + (yi * 15);

    arduboy.setCursor(x + 15, yPos + 2);
    arduboy.drawRect(x, yPos, 10, 10);
    arduboy.print(yi + 1);
    if (player == yi) {arduboy.fillRect(x + 2, yPos + 2, 6, 6);}

In that case yeah,

if(static_cast<uint8_t>(player) - 1 == yi)
  arduboy.fillRect(x + 2, yPos + 2, 6, 6);

would work.

You might want to consider maintaining the player’s index and the player’s cell/symbol separately though, especially if you later plan to add the ability to let a player choose whether they’re noughts or crosses.

You could have something like:

class PlayerInfo
  Cell playerCells[2];
  uint8_t playerIndex;

  const Cell & getPlayerCell(void) const
    return this->playerCells[this->playerIndex];

To hold the player state.

I really just want to add saving now, not much else since i dont think a game as simple as this needs a character select

Fair enough.

I’d recommend going back to it at a later date to add stuff though.
Noughts and crosses is a good game for practicing code design on.

Ive been trying to add a system that checks if the board is full and with no matches, I made this:

  // If game returns no winner
  if (winrar == Cell::Empty) {
    // Do 2 iterations, y has a second conditional to break its loop when catsGame is not 1 anymore
    for (uint8_t y; y < 3 and isCatsGame == 1; y++) {
      for (uint8_t x; x < 3; x++) {
        // If the selected grid value is equal to Cell::Empty, then make isCatsGame 0 then break, therefore breaking the outer loop i think
        if (grid[y][x] == Cell::Empty) {

    // If the variable has not changed then that means no empty spaces are on the board, therefore cats game
    if (isCatsGame == 1) {
      sound.tone(400, 100, 300, 100, 200, 100);
      gstate = 2;
      // else, normal place sound.
    } else {sound.tone(200, 100, 150, 100);}

the issue is that it does not ever change the variable to zero at all I think, which means that it will register that there is no more spaces left and make the game output that it was a cats game. Not sure why.

The best solution for solving issues like this is to write down how you would solve the problem, identify the logical conditions involved, formalise that into pseudocode and then turn that into proper C++ code.
(That’s pretty much what most programmers do, but usually they do it in their head unless it’s a particularly hard problem.)

E.g. You might describe the solution as:

  • Look at every cell from top left to bottom right
    • if any cell is empty, it’s not a draw
    • else, it’s a draw

Or to put it another way, you know that there’s a draw if no cells are empty and it’s not a draw if there are any empty cells, so what you’re really asking is “are there any empty cells?”.

The first bit is your loop, the second parts are what the condition would be.

bool hasEmptyCell(void)
	// Look at every cell
	for (uint8_t y; y < 3; y++)
	for (uint8_t x; x < 3; x++)
		// If a cell is empty...
        	if (grid[y][x] == Cell::Empty)
			// Found an empty
        		return true;
	return false;

Then you can do:

if (hasEmptyCell())
	sound.tone(200, 100, 150, 100);
	sound.tone(400, 100, 300, 100, 200, 100);
	gstate = 2;

Note that you could turn that loop into its own “isADraw()” function, or more accurately “hasNoEmptyCells” (or invert the logic and call it “hasEmptyCells”).

I was going to blather on about how this loop is effectively a more optimal generic version of the || operator combined with a !, but I’ll leave that for now.

Basically the short circuiting logic operators || and && can be generalised to work over the entire contents of a data structure.
(Normal C++ has a thing for doing that in its stdlib, but Arduino doesn’t have a version of the stdlib for various reasons.)

Lastly: if you find yourself wanting to call a variable is something, then it should probably be a bool. If it shouldn’t, you’ve probably picked the wrong name.

Turns out it was just that I keep forgetting to set my for loop variables to zero, expecting c++ to do it for me.

I’m pretty sure not setting them to 0 by default is supposed to be an optimisation from back in the days where it made a big impact (before optimisers were clever).

It’s better to always be explicit anyway, then you (generally) don’t have to remember what the default it.

Hey uh, Im having trouble with enums again. I ended up putting the enums into their own header/cpp pair files.

I put this in classes.h
enum class States : uint8_t;

And put this in classes.cpp

enum class States : uint8_t {
  Title, Game, // Game screens
  Main, Paused, Winner // Game states

I included classes.h in the game.h file and wrote this:
States GameState = States::Main;

For some reason it raises this error even though everything is correct.

exit status 1
'Main' is not a member of 'States'

Scoped enums don’t need to be separated into .h and cpp files.
If you separate them then things including the .h don’t know what values are available to assign to the enum.

Separating into .h and cpp is mainly for (non-template) functions (and sometimes progmem stuff).

But when I put the stuff in classes.cpp into classes.h I get this error:

multiple definition of 'enum class States'

Is the .h file missing include guards or a #pragma once by any chance?

(#pragma once is easier.)

yeah, pragma fixed it for some reason.

If you don’t have include guards or pragma once then a file can accidentally have its contents included twice.

For definitions (e.g. a full enum class definition, a class definition or a functiondefinition) that results in an error because things are supposed to only be defined once (but can be declared multiple times).

#pragma once is a compiler extension (available on pretty much all compilers because of how popular it is) that prevents a file being included twice.

Include guards are another way to prevent something being defined twice, but they’re a bit more olf fashioned and take longer to write (plus you have to manually make sure there aren’t any clashes).

Unrelated question, What does ! do in if statements?
ex: if (!arduboy.nextFrame()) {return;}

! is the ‘not’ operator (formally ‘logical negation’), it inverts a bool to be its opposite.
I.e. !false == true and !true == false

So an alternative would be to do:

void setup(void)
    // Do stuff

But then the whole function would be inside the brackets.
It’s easier to just invert the logic and exit early.
(And easier to read since you don’t have to track the brace to find out when the if ends.)

Another question:
Could I structure this: player = (player == Cell::Nought) ? Cell::Cross : Cell::Nought;
But inside an arduboy.print() function and instead it checks for the winner and returns a string. Think of a shorter version of this

if (winner == Cell::Cross) {
  arduboy.setCursor(5, 45);
} else if (winner == Cell::Nought) {
  arduboy.setCursor(5, 45);
} else {
  arduboy.setCursor(5, 45);

Depends why you want to make it shorter.
Are you aiming to save progmem or make it more readable?

If you’re trying to make it more readable then that’s probably the best you can do (either that or use a switch).

If you’re trying to make it use less progmem then there’s something you might be able to do but it’s not as pretty.

cant i do something like this?
arduboy.print((winner == Cell::Cross) ? "Crosses" /*elif thing?*/ (winner == Cell::Nought) : "Noughts" : "Draw!")

If you wrap the strings in F then yes, but it’s not as easy to read and not guaranteed to save progmem.

arduboy.print((winner == Cell::Cross) ? F("Crosses") : (winner == Cell::Nought) ? F("Noughts") : F("Draw!"));