Home > Net >  "Decimal" flags (instead of binary) - built-in way?
"Decimal" flags (instead of binary) - built-in way?

Time:03-18

I have this formula which I got from some documentation, which is supposed to explain what a "Flags" integer represents:

Flags = Spectator * 1 TemporarySpectator * 10 PureSpectator * 100 AutoTarget * 1000 CurrentTargetId * 10000

I wrote this code which is able to convert a number (flags) to some booleans one integer, just like in the formula:

////////// From Values to Flag //////////

bool Spectator          = true;
bool TemporarySpectator = false;
bool PureSpectator      = true;
bool AutoTarget         = false;
int  CurrentTargetId    = 255;

int Calculate() =>
    (Convert.ToInt32(Spectator)          * 1)  
    (Convert.ToInt32(TemporarySpectator) * 10)  
    (Convert.ToInt32(PureSpectator)      * 100)  
    (Convert.ToInt32(AutoTarget)         * 1000)  
    (Convert.ToInt32(CurrentTargetId)    * 10000);

int Result = Calculate(); // 2550101


////////// From Flag to Values //////////

CurrentTargetId      = Convert.ToInt32(Result / 10000);
AutoTarget           = Convert.ToBoolean((Result - (CurrentTargetId * 10000)) / 1000);
PureSpectator        = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000)) / 100);
TemporarySpectator   = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000) - (Convert.ToInt32(PureSpectator) * 100)) / 10);
Spectator            = Convert.ToBoolean((Result - (CurrentTargetId * 10000) - (Convert.ToInt32(AutoTarget) * 1000) - (Convert.ToInt32(PureSpectator) * 100) - (Convert.ToInt32(TemporarySpectator) * 100)) / 1);

Result = Calculate(); // 2550101

As you can see, my code is also able to do the reverse operation too - which is converting values to a flag.

Fiddle of my code: https://dotnetfiddle.net/ua2wi8

Does this kind of operation have any name? I know about the FlagsAttribute for enums which is similar but stores flags as individual bits ("binary digits") instead of decimal digits like in my case.

Is there any easier or even native way to do this in C#? Using booleans right from a model class would be a plus.

CodePudding user response:

FlagsAttribute for enums just controls how an enum will print when using ToString, and it will list out all values in a nice comma-separated list.

What you are trying to do can be achieved by enums (optimally), but also can be achieved by using ints if you understand binary, or powers of 2.

Using Enum

public enum MyEnum {
    Spectator          = 1 << 0,    // 1 or 2^0
    TemporarySpectator = 1 << 1,    // 2 or 2^1
    PureSpectator      = 1 << 2,    // 4 or 2^2
    AutoTarget         = 1 << 3,    // 8 or 2^3
    CurrentTargetId    = 1 << 4     // 16 or 2^4
}

MyEnum flags =  MyEnum.Spectator | MyEnum.PureSpectator | MyEnum.CurrentTargetId;

////////// Extracting individual values from flags //////////

MyEnum currentTargetId      = flags & MyEnum.CurrentTargetId;
MyEnum autoTarget           = flags & MyEnum.AutoTarget;
MyEnum pureSpectator        = flags & MyEnum.PureSpectator;
MyEnum temporarySpectator   = flags & MyEnum.TemporarySpectator;
MyEnum spectator            = flags & MyEnum.Spectator;

if (flags.HasFlag(MyEnum.AutoTarget))
{
    // Do Stuff
}

if (flags.HasFlag(MyEnum.Spectator))
{
    // Do Stuff
}

Using Pure Ints

const int spectator            = 0b_0000_0001;    // 1 or 2^0
const int temporarySpectator   = 0b_0000_0010;    // 2 or 2^1
const int pureSpectator        = 0b_0000_0100;    // 4 or 2^2
const int autoTarget           = 0b_0000_1000;    // 8 or 2^3
const int currentTargetId      = 0b_0001_0000;    // 16 or 2^4

int flags =  spectator | pureSpectator | currentTargetId;

////////// Extracting individual values from flags //////////

int currentTargetIdVal      = flags & currentTargetId;
int autoTargetVal           = flags & autoTarget;
int pureSpectatorVal        = flags & pureSpectator;
int temporarySpectatorVal   = flags & temporarySpectator;
int spectatorVal            = flags & spectator;

if ((flags & autoTarget) > 0)
{
    // Do Stuff
}

if ((flags & spectator) > 0)
{
    // Do Stuff
}

C# also allows for using Binary Literals, so MyEnum can also be written as so:

public enum MyEnum {
    Spectator          = 0b_0000_0001,
    TemporarySpectator = 0b_0000_0010,
    PureSpectator      = 0b_0000_0100,
    AutoTarget         = 0b_0000_1000,
    CurrentTargetId    = 0b_0001_0000
}

CodePudding user response:

You could probably wrap it all in a class, and use the modulo operator to simplify things:

class DecimalFlags
{
    private int _value = 0;
    public bool Spectator
    {
        get
        {
            return _value % 10 == 1;
        }
        set
        {
            if (value && _value % 10 != 1) _value  = 1;
            else if (_value % 10 == 1) _value -= 1;
        }
    }

    public bool TemporarySpectator
    {
        get
        {
            return (_value/10) % 10 == 1;
        }
        set
        {
            if (value && (_value/10) % 10 != 1) _value  = 10;
            else if ((_value/10) % 10 == 1) _value -= 10;
        }
    }

    public bool PureSpectator
    {
        get
        {
            return (_value / 100) % 10 == 1;
        }
        set
        {
            if (value && (_value / 100) % 10 != 1) _value  = 100;
            else if ((_value / 100) % 10 == 1) _value -= 100;
        }
    }

    public bool AutoTarget
    {
        get
        {
            return (_value / 1000) % 10 == 1;
        }
        set
        {
            if (value && (_value / 1000) % 10 != 1) _value  = 1000;
            else if ((_value / 1000) % 10 == 1) _value -= 1000;
        }
    }
    
    public int CurrentTargetId {get;set;}
    
    public static explicit operator int(DecimalFlags df)
    {
        return df._value   df.CurrentTargetId*10000;
    }
    public static explicit operator DecimalFlags(int i)
    {
        var df = new DecimalFlags();
        df.CurrentTargetId = i/10000;
        df._value = i - df.CurrentTargetId*10000;
        return df;
    }
}

Then when you need to pass your value back to the game you can say

MyGameFunction((int)decimalFlags);

Or receiving the value from the game:

int value = GetValueFromGame();
var decimalFlags = (DecimalFlags)value;
bool isSpectator = decimalFlags.Spectator; //etc

CodePudding user response:

You can create your own struct object to represent the flags. This struct shown below will store all your flags and your CurrentTaregtId in 8 bytes, or the size of a long. You can then use the .Calculate function to get the value your looking for in decimal. All these flags can have values up to 255, whereas the shortcoming of the decimal system was each value could only hold up to 9 to be used as a proper 'flag'.

Keep in mind that this code is not yet tested. So minor alterations may be required.

[StructLayout(LayoutKind.Explicit)]
public struct MyFlags {

    [FieldOffset(0)]
    public ulong Value;
    
    [FieldOffset(0)]
    public byte Spectator;

    [FieldOffset(1)]
    public byte TemporarySpectator;

    [FieldOffset(2)]
    public byte PureSpectator;

    [FieldOffset(3)]
    public byte AutoTarget;

    [FieldOffset(4)]
    public uint CurrentTargetId;
    
    public MyFlags(in ulong value) {
        Value = value;
    }
    
    public MyFlags(in byte spectator, in byte temporarySpectator, in byte pureSpectator, in byte autoTarget, in uint currentTargetId) {
        Spectator = spectator;
        TemporarySpectator = temporarySpectator;
        PureSpectator = pureSpectator;
        AutoTarget = autoTarget;
        CurrentTargetId = currentTargetId;
    }
    
    public long Calculate()
    {
        return Spectator
              TemporarySpectator * 10
              PureSpectator * 100
              AutoTarget * 1000
              CurrentTargetId * 10000;
    }
}

/// Usage

MyFlags flags = new MyFlags();
flags.TemporarySpectator = 4;
flags.PureSpectator = 7;
flags.CurrentTargetId = 255;
flags.Calculate();

if (flags.AutoTarget > 0)
{
    // Do stuff
}

CodePudding user response:

Here is how I ended up doing it, with manual string parsing:

public class SpectatorStatus : IFlag
{
    public SpectatorStatus(int value)
    {
        var valueStr = value.ToString("D7");

        const char True = '1';

        CurrentTargetId = Convert.ToInt32(valueStr[..3]);
        AutoTarget = valueStr[3] == True;
        PureSpectator = valueStr[4] == True;
        TemporarySpectator = valueStr[5] == True;
        Spectator = valueStr[6] == True;
    }

    public bool Spectator { get; }
    public bool TemporarySpectator { get; }
    public bool PureSpectator { get; }
    public bool AutoTarget { get; }
    public int CurrentTargetId { get; }

    public override string ToString()
        => (Convert.ToInt32(Spectator)
              Convert.ToInt32(TemporarySpectator) * 10
              Convert.ToInt32(PureSpectator) * 100
              Convert.ToInt32(AutoTarget) * 1000
              CurrentTargetId * 10000)
            .ToString("D7");
}
  • Related