Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Quickly evaluate how many bools are true?

Discussion in 'Scripting' started by Marscaleb, Aug 27, 2023.

  1. Marscaleb

    Marscaleb

    Joined:
    Jan 7, 2014
    Posts:
    986
    I have a function that is being passed four bools in its argument.
    If two or more bools are true, then I have to run a complicated system to evaluate through multiple options, and its going to tax the system a bit.
    But if only one is true, I can bypass all that and return the function fairly quickly.

    So I'd like to have my code decide quickly if I only have one true argument.

    I could go through each bool individually with an if statement that adds to a counter if its true, but that seems wasteful; there should be a more efficient way to handle this.

    I thought I would try to convert the bool to an int and then simple have a single line of "iVal = (int)BoolOne + (int)BoolTwo + etc" but I can't type case a bool to an int, and looking around, well, I can't find any way to convert a bool to an int. C# should be able to use Convert.toInt32 but that doesn't seem to work in Unity.

    How can I count how many "true"'s I have without making an if statement for each one individually? What's the most efficient way to do this?
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Pass the bools in as a collection, using the
    pararms
    keyword for example.

    Though this feels like an XY problem, and it would help to know more context here.
     
    Ryiah and Bunny83 like this.
  3. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Code (CSharp):
    1. bool AnyBoolsTrue(bool b1, bool b2, bool b3, bool b4)
    2.     {
    3.         if (b1) return true;
    4.         if (b2) return true;
    5.         if (b3) return true;
    6.         if (b4) return true;
    7.         return false;
    8.     }
     
  4. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    Are you willing to use Linq?

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4.  
    5. namespace CountBools
    6. {
    7.   class Program
    8.   {
    9.     static void Main(string[] args)
    10.     {
    11.           Console.WriteLine(new List<Boolean> {true, true, false, true}.Count(i => i));
    12.     }
    13.   }
    14. }
    15.  
     
  5. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Ohh you said how many, whoops:
    Code (CSharp):
    1. int HowManyBoolsTrue()
    2.     {
    3.         int x = 0;
    4.         if (bool1) x++;
    5.         if (bool2) x++;
    6.         if (bool3) x++;
    7.         if (bool4) x++;
    8.         return x;
    9.     }
     
  6. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Ohh, you mean the bools(checks) themselves are performance hitting. at the beginning of the Update() make all val's 0, then in their condition give their value = 1, then simple check:
    Code (CSharp):
    1. int val1;
    2.     int val2;
    3.     int val3;
    4.     int val4;
    5.  
    6.     int HowManyBoolsTrue()
    7.     {
    8.         return val1 + val2 + val3 + val4;
    9.     }
    I had an issue like that before, where the bool(check) question was quite taxing, so just make it an separate int or bool, then order them properly. Or make them static if between classes.
     
  7. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    I will suggest if they are int variables and we only need to know if there are two that are set to 1 it can be part of an if test. We don't really care how many there are just that there are 2 or more.

    Code (CSharp):
    1. int val1;
    2. int val2;
    3. int val3;
    4. int val4;
    5.  
    6. if(val1 + val2 + val3 + val4 >= 2)
    7. {
    8.     // logic goes here
    9. }
    10.  
     
    wideeyenow_unity likes this.
  8. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    But if one isn't happy with using Linq and we're stuck using Booleans then this one will return as soon as possible.

    Code (CSharp):
    1. Console.WriteLine(ConditionIsTrue(true, false, false, true));
    2.  
    3. private static Boolean ConditionIsTrue(Boolean b1, Boolean b2, Boolean b3, Boolean b4)
    4. {
    5.     Int32 result = b1 ? 1 : 0;
    6.  
    7.     if((result += b2 ? 1 : 0) >= 2) return true;
    8.     if((result += b3 ? 1 : 0) >= 2) return true;
    9.  
    10.     return ((result += b4 ? 1 : 0) >= 2);
    11. }
     
  9. Marscaleb

    Marscaleb

    Joined:
    Jan 7, 2014
    Posts:
    986
    Err, no. It's code later down the line that's performance hitting, so I'm counting the bools as the easiest way to tell if we can skip the performance-hitting section.
    I was just getting annoyed because my code was looking rather long just to check how many bools were true.

    If I could type cast them then I could just do a single line of int = bool + bool + bool + bool. Instead I've got five lines with four if statements.

    What I wound up with was
    Code (CSharp):
    1. int totalDirections = AllowNorth ? 1 : 0;
    2.         totalDirections += AllowWest ? 1 : 0;
    3.         totalDirections += AllowSouth ? 1 : 0;
    4.         totalDirections += AllowEast ? 1 : 0;
    I don't know if it runs any better than what you posted, but it's mostly splitting hairs.
     
    kdchabuk likes this.
  10. kdchabuk

    kdchabuk

    Joined:
    Feb 7, 2019
    Posts:
    47
    Well, the most efficient might be to use Burst:
    Code (CSharp):
    1.     [BurstCompile]
    2.     public static int CountBool4(bool4 b)
    3.     {
    4.         var x = UnsafeUtility.As<bool4, uint>(ref b);
    5.         return math.countbits(x);
    6.     }
    But no need to complicate it.
     
  11. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,833
    @wideeyenow_unity in post #5 has it. Or if your bools are in an array, the equivalent loop.

    I can think of few means of doing this task "quickly" that are as heavy and slow as creating a whole new List<Boolean> and then iterating with a Linq delegate call. The garbage collection alone will outweigh the original task.
     
    Bunny83 likes this.
  12. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    well in my own travels of being a performance nut, I found that having something in it's own function is somehow faster then if it's together.

    One example was a for loop within a for loop, gave up to 12000 ticks with set conditions, then I made the second for loop just a separate function to be used within the first for loop, and it equated down to 7000 ticks. I don't know why it was, and haven't investigated it further, but I did see something related on a performance video from either Unite or someone else.

    So it's just always stuck with me, to always separate hard logic with their own functions. especially if they return early.
     
    Last edited: Aug 27, 2023
  13. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,833
    If your bools were bits combined in a bitmask in a manner like LayerMask, then this will return true if only one bit is set (because the bitfield as an integer would be a power of two):

    ((bitfield & (bitfield-1)) == 0)  &&  (bitfield != 0)
     
    Bunny83 likes this.
  14. tleylan

    tleylan

    Joined:
    Jun 17, 2020
    Posts:
    521
    Definitely :D If there are only ever 4 of them I would just add them inline and set a Boolean. And then bool is only needed if you want a variable you can use in multiple places otherwise you can add them in your if() test.

    Code (CSharp):
    1. bool shouldProcess = ((AllowNorth ? 1 : 0) + (AllowWest ? 1 : 0) + (AllowSouth ? 1 : 0) + (AllowEast ? 1 : 0)) >= 2);
     
  15. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    I'm not sure what your concern is. If you have 4 individual booleans there's not much you can do. The most straight forward solution for checking if 2 or more of 4 bools are true would simply be

    Code (CSharp):
    1. if (a && b || a && c || a && d || b && c || b && d || c && d)
    Since logical and / or operators do short-circuit you get an early exit when the first pair has been found. Though using an integer or byte to cound the numbers with individual if statements isn't really much different. For more than 4 bools using a counter will certainly be better. Though if you can actually represent your bools as a bit mask, halley's solution would be the simplest one. This works up to 64 bits in an ulong.
     
    dlorre likes this.
  16. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    It more than likely is...

    I mean if simple bool or int checks were causing any sort of performance loss, we'd all have to be resorting to making games like Pac-Man or Frogger.. lol

    To be fair, it's more about readability, and makes sense to you(or a team if big project). I personally like to group things so in my main logic handlers, my code looks like this:
    Code (CSharp):
    1. void Update()
    2.     {
    3.         Constants();
    4.  
    5.         if (gameState == GameState.PlayerTurn)
    6.         {
    7.             if (LastPlayerEndedTurn()) { HandleLastTurn(); }
    8.             else { HandlePlayersTurns(); }
    9.         }
    10.     }
    As you look at it, you might think "ohh, that's super easy", until you look at the methods and realize the class is 3000 lines long, lol. :D
     
  17. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,419
    This thread is filled with a lot of misleading information :eek:

    1. According to both Microsoft and Unity Technologies, Linq should be avoided in performance-critical code. See here for details.

    2. When using the params keyword, a temporary array is created behind the scenes, resulting in a performance penalty and increased strain on the memory allocator.

    3. Checking the state of four boolean variables should rarely cause performance issues. It is more likely that there are other parts of the code that can be optimized for better performance impact than focusing on these four booleans.
     
    Last edited: Aug 27, 2023
    venediklee and Bunny83 like this.
  18. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    Yeah, hence why having the feeling this is an XY problem.

    Considering this is a directional matter from what little context we've been given (as usual), I wonder why coordinates are not being used here alongside some simple calculations.
     
    Bunny83 likes this.
  19. dlorre

    dlorre

    Joined:
    Apr 12, 2020
    Posts:
    700
    For four bools you don't need to worry too much:

    Code (csharp):
    1.  
    2.     bool threshold(bool b1, bool b2, bool b3, bool b4)
    3.     {
    4.         return (b1 && b2) || (b1 && b3) || (b1 && b4) || (b2 && b3) || (b2 && b4) || (b3 && b4);
    5.     }
    6.  
    For more you can consider BitOperations.

    https://learn.microsoft.com/en-us/dotnet/api/system.numerics.bitoperations.popcount?view=net-7.0

    However they don't work in Unity so you will need to code a replacement or use a dll.

    For using stuff like popcount you need to replace your bools with masks which are powers of 2.

    So the first bool would be 1 or 0, the second 2 or 0 , the third 4 or 0 and the last one 8 or 0.
     
    Bunny83 likes this.
  20. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    Programming is not about what seems to be. It‘s about forming a theory and then analyzing it, aka profiling. You‘ll be surprised about the results. Never trust your guts when it comes to „what is wasteful“.
     
  21. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Just to be crystal clear on this one, when you say "params keyword", are you referring to using parameters in a function? i.e.
    Code (CSharp):
    1. bool AnyBoolsTrue(parameter1,  parameter2, etc...)
    2. { handle parameters }
    Or am I completely calling something by the wrong name? I feel like I am... lol

    EDIT: as parameter I mean "bool" in the above example *
     
  22. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,419
    spiney199 and wideeyenow_unity like this.