Evaluate a struct as true/false?

Hi,
In the following code snippet I have declared a struct that has an array member variables.

This is hard for me to explain, so please bear with me as I try to explain what I'm looking to do.

I am wondering if there is a shorthand way to evaluate the condition of a specified ONE of those array variables' state as True if the value of ANY of the fields of that one variable in the struct is > zero or false if == zero?

i.e: if player[any number].tIndex > 0 then True can be returned based on that field, ignoring any of the other array members' values.

struct Player {
  uint32_t  ident ;                    
  uint8_t    sLevel ;               
  bool        randomised = false;           
  uint8_t    tSequence[10];
  uint16_t  rTotal ;             
  uint8_t    tIndex ;              
  uint8_t    aCount ;               
};
struct Player player[100];

void setup() {
Serial.begin(38400);  // put your setup code here, to run once:
delay(500);

player[10].ident = 1024;

player[5].tIndex = 5;  // random 'other' field (of many) with a value

if (player[5].tIndex) Serial.println("struct player should evaluate as True");

player[5].tIndex = 0;

if (!player[5].tIndex) Serial.println("struct player should evaluate as False if ALL tIndex == 0");

}

void loop() {
  // put your main code here, to run repeatedly:

}

Off of the top of my head, not really.
You could, however have a global variable 'all_Index'

Each time you set a particular player[].tIndex, you add that value to all_Index. Any time you clear a player[].tIndex, you could subtract the value (before you clear it) from all_Index.
That way, if all_Index != 0, you can return TRUE

I don't understand yet. You have a large array of Player objects. Those objects have small arrays inside them. But your example is just looking at a single int field in the object.

if (!player[5].tIndex) Serial.println("struct player should evaluate as False if ALL tIndex == 0");

What other tIndex do you want to examine when determining if player[5] meets your criteria? There's only one tIndex in each Player.

darrob:
You could, however have a global variable 'all_Index'

Each time you set a particular player[].tIndex, you add that value to all_Index. Any time you clear a player[].tIndex, you could subtract the value (before you clear it) from all_Index.
That way, if all_Index != 0, you can return TRUE

Yes, that would work, thank you.
I was trying to avoid the repetitive looping required to check if any of the players were still in the game, and that'll do nicely.

You could also add a member function which would check the values of all the data fields.

Internally it is the repetitive looping you are trying to avoid, but it allows you to just write something like:

if (player[5].isNonZero()) {...

This avoids having to modify an index value with a separate line of code every time you put data in the structure.

I agree with darrob with one suggestion. You could just establish a byte, increment it when you set a player[n].tIndex to a non-zero value, and decrement it when you set player[n].tIndex to zero. That way the value of byte will always equal the number of non-zero player[n].tIndex.

DKWatson:
I agree with darrob with one suggestion. You could just establish a byte, increment it when you set a player[n].tIndex to a non-zero value, and decrement it when you set player[n].tIndex to zero. That way the value of byte will always equal the number of non-zero player[n].tIndex.

Yes, #Darrob saw what I couldn't while over-thinking the issue - it looks to be the simplest way to do what I am after.

To clarify further, what I am after is to tell when a group of (up to 200) players ALL have decremented their "turns" field (.tIndex) to Zero. They do this at differing rates, and in differing group sizes.

MorganS:
What other tIndex do you want to examine when determining if player[5] meets your criteria? There's only one tIndex in each Player.

It's not a single player that concerns me, but the status of the group as a whole - the instant when when all in the group finish.

lefstin:
You could also add a member function which would check the values of all the data fields.
Internally it is the repetitive looping you are trying to avoid, but it allows you to just write something like:if (player[5].isNonZero()) {...This avoids having to modify an index value with a separate line of code every time you put data in the structure.

I may be missing something with your suggestion #lefstin, but is that not a variation on what I'm looking to avoid as I'd have to repetitively iterate through the players to sense when they all become .tIndex == Zero

I’d go further down the OOP road. Expand your Player stuct to be a full-blown class with public methods that perform the required manipulation of the data members (which should then be made private). You could then have a static (and private) data member to hold the single counter suggested by @darrob and @DKWatson. It would be incremented / decremented / tested by the member functions as required.

Reads as if the solution is a semaphore.

gfvalvo:
I’d go further down the OOP road. Expand your Player stuct to be a full-blown class with public methods that perform the required manipulation of the data members (which should then be made private). You could then have a static (and private) data member to hold the single counter suggested by @darrob and @DKWatson. It would be incremented / decremented / tested by the member functions as required.

That is just so far over my head, @gfvalvo, I'm afraid!
Could you possibly explain a little further, or point me at relevant documentation to help deepen my understanding of what you are suggesting?

Object Oriented Programming (OOP) is an immense topic and, truthfully, I’m only part way on my journey. You can start by checking out its entry on Wikipedia.

Basically, OOP uses the concepts of ‘classes’ (which you can think of as blue prints for building a house) and ‘objects’ (actual houses built from those blue prints). You can build (instantiate) as many objects from a given class as you want -- platform resources permitting. The core tenant of OOP is that you define how data objects manipulate their own internal state and interact with each other rather than writing procedures to operate on your data.

A pared-down example of how I (as an intermediate-level C++ programmer) might implement your Player concept as a class is shown below, along with Serial port output from the program.

//--------------------- Declare the Player class (blue print)---------------------
class Player {
  private:
    uint32_t ident;
    uint8_t tIndex;
    static uint8_t tIndexCounter;

  public:
    Player() : ident(0), tIndex(0) {}
    void setIdent(uint32_t id);
    uint32_t getIdent();
    void setTIndex(uint8_t tI);
    uint8_t getTIndex();
    static bool allTurnsZero();
};

//--------------------- Define what each of the class's members do ---------------------
uint8_t Player::tIndexCounter = 0;

void Player::setIdent(uint32_t id) {
  ident = id;
}

uint32_t Player::getIdent() {
  return ident;
}

void Player::setTIndex(uint8_t tI) {
  if ((tI == 0) && (tIndex != 0)) {
    tIndexCounter--;
  } else if ((tI != 0) && (tIndex == 0)) {
    tIndexCounter++;
  }
  tIndex = tI;
}

uint8_t Player::getTIndex() {
  return tIndex;
}

bool Player::allTurnsZero() {
  return (tIndexCounter == 0);
}

const uint8_t numPlayers = 10;

//--------------------- Instantiate (build) objects of the class ---------------------
Player players[numPlayers];

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting");
  Serial.println();

  for (uint8_t i = 0; i < numPlayers; i++) {
    players[i].setIdent(random(1, 1000));  // Set each object's Ident
    players[i].setTIndex(random(1, 25));  // Set each object's T-index
  }

  //--------------------- Print out the objects we've built --------------------- 
  for (uint8_t i = 0; i < numPlayers; i++) {
    Serial.print("Player Number: ");
    Serial.print(i);
    Serial.print(", Player Ident: ");
    Serial.print(players[i].getIdent());
    Serial.print(", Player T-Index: ");
    Serial.println(players[i].getTIndex());
  }

  Serial.println();
  Serial.println();

//---------------------  Set each object's T-index back to zeero --------------------- 
//---------------------  use the 'allTurnsZero() method to see when all T-indexs are zero  ---------------------
  for (uint8_t i = 0; i < numPlayers; i++) {
    Serial.print("Setting T-Index of Player Ident ");
    Serial.print(players[i].getIdent());
    Serial.print(" to zero. All players finished = ");
    players[i].setTIndex(0);
    Serial.println(Player::allTurnsZero()? "True": "False");
  }
}

void loop() {}
Starting

Player Number: 0, Player Ident: 824, Player T-Index: 2
Player Number: 1, Player Ident: 348, Player T-Index: 3
Player Number: 2, Player Ident: 185, Player T-Index: 9
Player Number: 3, Player Ident: 673, Player T-Index: 15
Player Number: 4, Player Ident: 162, Player T-Index: 14
Player Number: 5, Player Ident: 829, Player T-Index: 6
Player Number: 6, Player Ident: 763, Player T-Index: 3
Player Number: 7, Player Ident: 910, Player T-Index: 24
Player Number: 8, Player Ident: 53, Player T-Index: 2
Player Number: 9, Player Ident: 44, Player T-Index: 5


Setting T-Index of Player Ident 824 to zero. All players finished = False
Setting T-Index of Player Ident 348 to zero. All players finished = False
Setting T-Index of Player Ident 185 to zero. All players finished = False
Setting T-Index of Player Ident 673 to zero. All players finished = False
Setting T-Index of Player Ident 162 to zero. All players finished = False
Setting T-Index of Player Ident 829 to zero. All players finished = False
Setting T-Index of Player Ident 763 to zero. All players finished = False
Setting T-Index of Player Ident 910 to zero. All players finished = False
Setting T-Index of Player Ident 53 to zero. All players finished = False
Setting T-Index of Player Ident 44 to zero. All players finished = True

gfvalvo:
Object Oriented Programming (OOP) is an immense topic and, truthfully, I’m only part way on my journey. You can start by checking out its entry on Wikipedia.

Basically, OOP uses the concepts of ‘classes’ (which you can think of as blue prints for building a house) and ‘objects’ (actual houses built from those blue prints). You can build (instantiate) as many objects from a given class as you want -- platform resources permitting. The core tenant of OOP is that you define how data objects interact with each other rather than writing procedures to operate on your data.

A pared-down example of how I (as an intermediate-level C++ programmer) might implement your Player concept as a class is shown below

I cannot thank you enough @gfvalvo for this hugely informative and detailed input - you have truly put a sizeable amount of effort in to it.
I need to sit down in an hour or so, probably for a day or so, to read, digest and see if I can manage to build on this whole concept.

No problem. Don’t get me wrong, you can certainly create well-written code to complete your project without using OOP. It’s kind of a tradeoff between getting things done and learning new stuff / enhancing your coding techniques. As a learning exercise, I try to increase OOP-ness in each project I do compared to the previous one.

If the 200 players can decrement their indexes 'uncontrolled' then you must have a loop to go through all players and examine the indexes one at a time.

If it's 'controlled' by one single function then you could have that function just keep a running sum of all players indexes and each time it decrements a player, it decrements the sum too. I believe this is an extremely limited and risky way of doing it. There should be many function allowed to interact with the player struct.

OOP can improve your chances of control. The operations which work on internal data items inside the Player object are themselves inside the object. So you have all of that centralized in one place and it's easier to work with the functions which affect that values inside Player.

But that would require that each of the Player objects edits the global runningSum variable, which must exist outside of all Players. Not a good idea.

I see the best solution is a simple function which does the looping. Give the function a name which describes what it returns: allPlayersTurnsFinished(). Then you can use this in if() statements. Just don't expect to call it 1000 times per second.

MorganS:
But that would require that each of the Player objects edits the global runningSum variable, which must exist outside of all Players.

Yes, but that runningSum variable is not "global". It's a private, static data member of the class. So, only class static and instance functions can accesses it. In other words it's encapsulated in the class. So, if you write the class methods correctly, control is complete.

No. The structure as presented has 200 separate objects. There's no 'common' area.

Maybe a variable 'static' to the class could do it.

MorganS:
No. The structure as presented has 200 separate objects. There's no 'common' area.
Maybe a variable 'static' to the class could do it.

Sorry, misread your post. I was referring to my example OOP code in Reply #10.

MorganS:
If it's 'controlled' by one single function then you could have that function just keep a running sum of all players indexes and each time it decrements a player, it decrements the sum too. I believe this is an extremely limited and risky way of doing it. There should be many function allowed to interact with the player struct.

Yes, if I get where you are coming from, a function which up-dated the (say) skill level variable might fail to action if the all_Index function took control precedence.

MorganS:
I see the best solution is a simple function which does the looping. Give the function a name which describes what it returns: allPlayersTurnsFinished(). Then you can use this in if() statements. Just don't expect to call it 1000 times per second.

But here you confound me! Are you now not describing the "one single function" of above, which "is an extremely limited and risky way of doing it"?

Then we have the final point regarding looping repetitively. One of the problems I face in my application is having to catch the many, disparate and complex inputs from each player's activity in the field, generated by XBee3 DigiMesh devices, all clamouring for attention as they play. This may explain my aversion to any repetitive loops I can possibly avoid while the play phase is ongoing.
Yet I need to be able to react to the completion of play by the last of the players - hence my original post query.

If I have a group of players - 10 for arguments sake - and each has 5 turns to complete, then I can (as per the obvious suggestion of @darrob) set an index to 10x5 = 50 at the start of play, and decrement it as each player returns a score. Each player is done in his own good time, but the index only reaches zero once all are done.
Is there a potential problem with this approach?

I am still trying to digest your contribution @gfvalvo, so forgive me if I don't mention your OOP methodology just yet...

OldManNoob:
But here you confound me! Are you now not describing the "one single function" of above, which "is an extremely limited and risky way of doing it"?

No, it's not risky because the data is only stored in one place. You can update any player's data at any time and the next time you run the allPlayersTurnsFinished() you get the correct answer.

OldManNoob:
Then we have the final point regarding looping repetitively. One of the problems I face in my application is having to catch the many, disparate and complex inputs from each player's activity in the field, generated by XBee3 DigiMesh devices, all clamouring for attention as they play. This may explain my aversion to any repetitive loops I can possibly avoid while the play phase is ongoing.
Yet I need to be able to react to the completion of play by the last of the players - hence my original post query.

If I have a group of players - 10 for arguments sake - and each has 5 turns to complete, then I can (as per the obvious suggestion of @darrob) set an index to 10x5 = 50 at the start of play, and decrement it as each player returns a score. Each player is done in his own good time, but the index only reaches zero once all are done.
Is there a potential problem with this approach?

Well now we are discussing performance optimizations. If you have to check this value thousands of times per second then you don't want to be looping over 200 players each time. 200,000 iterations is a significant drag on your game's performance. That's when you might consider making the program more complex by storing the total separately from the individual players.

The potential problem is you add a new function like "bonus turns" to give one player an extra turn. But you forget that this function must add to the total. So you've made a bug: the total thinks player turns is zero but someone (not always the one with the bonus) has turns available. This shows up as "Sometimes player 199 doesn't get all of his turns" and the problem wasn't originated by #199 and it's not consistent.

So it adds an extra step that must be tested each time you make a change to the game: you must test that the total turns is correct under all combinations of bonuses and penalties and whatever else might happen during the game.

Conversely, if you change the design so that it doesn't have to check all players for all turns thousands of times per second then spending a millisecond adding up the turns is not expensive and you don't need to optimise away the looping. Or you limit yourself to 10 players, then the looping is not expensive either.

If turns only decrement to zero during operation, then there is no need to keep track of the number of turns or the amount by which they have decremented.

Whenever you decrement a player’s turn, check to see if it has decremented to zero. If it has, then increment a counter. When the counter equals the number of players, all players have reached zero turns.