05 Nov, 2008, Concac wrote in the 1st comment:
Votes: 0
Ok, so I got extremely sick of the limitation of the old extended bit system and I decided to try and get runter's infinite bit system to work. I got the code in, it seems to be working, however now I'm kinda stumped as to exactly how the definitions should work. The old system of course Defined a letter to a number, and then defined a bit to that letter. So what I'm wondering is could I just take those letters and just define them as 1-500 as to not completely have to change how the system works? Or do you just define each new affect/act/imm etc.. to a number, so something like "#define ACT_AGGRESSIVE 2" as I do believe 1 is what is used to return as no bit vector. Also, with this system am I going to have to go through the entire code and change every instance of the IS_SET/REMOVE_BIT/SET_BIT to conform to the new system to look like "if (is_set(&ch->act, 2))" instead of "if(IS_SET(ch->act, ACT_AGGRESSIVE))". Just trying to figure out exactly how much time I'm going to have to put into this as there are quite a few instances of these functions throughout the code and I wanna make sure I have to do this before I put the time into it. Thanks for any help in advance.
05 Nov, 2008, David Haley wrote in the 2nd comment:
Votes: 0
I don't really understand your question about the defines. I think the answer is that you need to define the bits as numbers.

You don't have to replace all instances of IS_SET with is_set if you just make IS_SET be a macro for is_set…

And you definitely don't want to remove the constants like ACT_AGGRESSIVE and replace them with their actual values. That is a very bad thing to do…
05 Nov, 2008, Keberus wrote in the 3rd comment:
Votes: 0
An enumerated table works well for changing something like:

/*
* ACT bits for mobs.
* Used in #MOBILES.
*/
#define ACT_IS_NPC BV00 /* Auto set for mobs */
#define ACT_SENTINEL BV01 /* Stays in one room */
#define ACT_SCAVENGER BV02 /* Picks up objects */
#define ACT_NOFLEE BV03 /* Mobs don't flee. -T */
#define ACT_AGGRESSIVE BV05 /* Attacks PC's */
#define ACT_STAY_AREA BV06 /* Won't leave area */
#define ACT_WIMPY BV07 /* Flees when hurt */
#define ACT_PET BV08 /* Auto set for pets */
#define ACT_TRAIN BV09 /* Can train PC's */
#define ACT_PRACTICE BV10 /* Can practice PC's */
#define ACT_IMMORTAL BV11 /* Cannot be killed */
#define ACT_DEADLY BV12 /* Has a deadly poison */
#define ACT_POLYSELF BV13
#define ACT_META_AGGR BV14 /* Extremely aggressive */
#define ACT_GUARDIAN BV15 /* Protects master */
#define ACT_RUNNING BV16 /* Hunts quickly */
#define ACT_NOWANDER BV17 /* Doesn't wander */
#define ACT_MOUNTABLE BV18 /* Can be mounted */
#define ACT_MOUNTED BV19 /* Is mounted */
#define ACT_SCHOLAR BV20 /* Can teach languages */
#define ACT_SECRETIVE BV21 /* actions aren't seen */
#define ACT_POLYMORPHED BV22 /* Mob is a ch */
#define ACT_MOBINVIS BV23 /* Like wizinvis */
#define ACT_NOASSIST BV24 /* Doesn't assist mobs */
#define ACT_NOKILL BV25 /* Mob can't die */
#define ACT_DROID BV26 /* mob is a droid */
#define ACT_NOCORPSE BV27
#define ACT_PUEBLO BV28 /* This is the pueblo flag */
#define ACT_PROTOTYPE BV30 /* A prototype mob */


into:
typedef enum
{
ACT_IS_NPC, /* Auto set for mobs */
ACT_SENTINEL, /* Stays in one room */
ACT_SCAVENGER, /* Picks up objects */
ACT_NOFLEE, /* Mobs don't flee. -T */
ACT_AGGRESSIVE, /* Attacks PC's */
ACT_STAY_AREA, /* Won't leave area */
ACT_WIMPY, /* Flees when hurt */
ACT_PET, /* Auto set for pets */
ACT_TRAIN, /* Can train PC's */
ACT_PRACTICE, /* Can practice PC's */
ACT_IMMORTAL, /* Cannot be killed */
ACT_DEADLY, /* Has a deadly poison */
ACT_POLYSELF,
ACT_META_AGGR, /* Extremely aggressive */
ACT_GUARDIAN, /* Protects master */
ACT_RUNNING, /* Hunts quickly */
ACT_NOWANDER, /* Doesn't wander */
ACT_MOUNTABLE, /* Can be mounted */
ACT_MOUNTED, /* Is mounted */
ACT_SCHOLAR, /* Can teach languages */
ACT_SECRETIVE, /* actions aren't seen */
ACT_POLYMORPHED, /* Mob is a ch */
ACT_MOBINVIS, /* Like wizinvis */
ACT_NOASSIST, /* Doesn't assist mobs */
ACT_NOKILL, /* Mob can't die */
ACT_DROID, /* mob is a droid */
ACT_NOCORPSE,
ACT_PUEBLO, /* This is the pueblo flag */
ACT_PROTOTYPE, /* A prototype mob */
MAX_ACT_FLAG
}
mob_flags;


Which is really equivilant to doing

#define ACT_IS_NPC 0
#define ACT_SENTINEL 1


But its a little easier to write, and maintain because you don't have to update the numbers that way.
05 Nov, 2008, David Haley wrote in the 4th comment:
Votes: 0
It's not entirely equivalent – an enumeration will give you a little more type safety if you're using a decent compiler.

It can be dangerous in some sense if you don't know what you're doing because you really don't want to be inserting entries into the middle of the enum if numbers are significant e.g. due to being stored as-is in file formats. Having an explicit list makes it harder to do that, because you have to go update a bunch of numbers, which is annoying enough to prevent most people from doing it.

(Of course, files really shouldn't be storing the numbers as-is, but, well, yeah.)
05 Nov, 2008, Keberus wrote in the 5th comment:
Votes: 0
Ah, I got ya. Thats why I took Samson's advice a while back and made everything Flagged. So in the area files for mob flags it will print the names instead of a number.

EDIT: not flagged, Key'd is the word I think I was looking for.
05 Nov, 2008, Runter wrote in the 6th comment:
Votes: 0
A full proper conversion is going to mean on many muds handling things like
code->act = 1 | 2 | 3;


You'll need to set those independently. (I think there maybe some tables even that use that format.)

Also, the enum system *could* be safe depending on how you save your information. It would be fine if you were saving and loading based on names in the table instead of actual run-time values. If you are saving actual runtime values (probably the most common way) then you would need to actually ensure that enum's order. You can still do the enum with the same amount of safety:

enum {
ACT_IS_NPC = 1,
ACT_SENTINEL = 2,
ACT_SCAVENDER = 3,
ACT_AGGRESSIVE = 4,

}

Although it certainly may just seem as easy to keep up with #defines.
05 Nov, 2008, David Haley wrote in the 7th comment:
Votes: 0
1 | 2 | 3 == 3 :wink:

(I know what you meant…)
05 Nov, 2008, Runter wrote in the 8th comment:
Votes: 0
Nah. I'm mostly talking about this

ch->affected_by = ch->affected_by | race_table[race].aff;

Or similar code.

I think it goes without saying, but if you're using a higher level solution in C you can't overload the bitwise operators to work with it.
05 Nov, 2008, David Haley wrote in the 9th comment:
Votes: 0
Oh, you can't possibly be suggesting that there's yet another reason to use C++? :wink:
05 Nov, 2008, quixadhal wrote in the 10th comment:
Votes: 0
Actually, you can still use enums if you like…. you just have to specify what every element is.

enum {
ACT_FOO = (1 << 0),
ACT_BAR = (1 << 1),
ACT_PFFT = (1 << 2)
}


Or, you can join me in blaspheme and use functions with string arguments that get loaded at boot time from files. But then you really can't use bitwise operators. Not that that's such a horrible thing anyways.

if (IS_sSET("ACT_FOO ACT_BAR"))
ch_printf("Foobar!\r\n");
05 Nov, 2008, Runter wrote in the 11th comment:
Votes: 0
Quote
Actually, you can still use enums if you like…. you just have to specify what every element is.


Yeah, I think I gave an example of that. ;)
05 Nov, 2008, quixadhal wrote in the 12th comment:
Votes: 0
Yep, I was just pointing out that you can set the values to be the same as the defines, rather than sequential.
06 Nov, 2008, Tyche wrote in the 13th comment:
Votes: 0
If it's an infinite bit system, the use of enums makes it finite. Sorry haven't looked at the infinite bit system code. I use infinite bits in some TeensyMud code that flags quests as solved using the BigNum class, meaning the number of quests one might implement is infinite. Although the number of bits in a BigNum is infinite, you're practically limited by memory store.

Edit: Also when writing C/C++ code I now avoid #defines in favor of const integrals for type checking. Not religiously, but I try. ;-)
06 Nov, 2008, Runter wrote in the 14th comment:
Votes: 0
It is indeed infinite.

set_bit(bm, 299239349) works.
06 Nov, 2008, quixadhal wrote in the 15th comment:
Votes: 0
The BigNum class is another option I had forgotten about. Haven't used that in ages, but it has C/C++ bindings as well.

Personally, I don't see the value in keeping bitwise operations. The miniscule amount of CPU savings is hardly worth the added complexity of maintaining flag values as unique bits. If you really want to be able to check multiple bits in one call, passing the list of bits to check for as an array, or even a string, isn't THAT big a deal.
06 Nov, 2008, Runter wrote in the 16th comment:
Votes: 0
If you were the village elder I'd let you declare we run our village elections that way.
08 Nov, 2008, Runter wrote in the 17th comment:
Votes: 0
Hrm. I don't know how but my last post got on the wrong thread and now I can't edit it to say as much. ;)
08 Nov, 2008, Grimble wrote in the 18th comment:
Votes: 0
I haven't looked at the referenced infinite bit code, but may I suggest…

std::bitset, or better yet, boost::dynamic_bitset

If you haven't already, you'll need to switch over to a C++ compiler to pull in either container.
09 Nov, 2008, Runter wrote in the 19th comment:
Votes: 0
std::bitset is a good library but what people should realize about it is that it uses memory equal to your largest element /8 bytes.

My bitmask code uses interval skipping so it only uses 4 bytes per 32 bit interval. That means if you have no bit but bit 1029102 set then you still only use 4 bytes.

Bitset also requires to know how many bits you want to intend to use when it is created. dynamic_bitset is better in that it lets you resize it.

But it's still not implementing interval skipping. So depending on actually what you want your system to do, it could be using far more memory overall. (and some implementations may not need interval skipping)


It has its purposes, but choose wisely before you decide on which system to use.
09 Nov, 2008, David Haley wrote in the 20th comment:
Votes: 0
Using more memory also means that you are considerably more efficient when it comes to looking up if a byte is set. Arguably, if your bitset is so sparse that skipping is giving you considerable gains to offset the efficiency loss, you are using the wrong data structure.

Runter said:
Bitset also requires to know how many bits you want to intend to use when it is created

In practice, this is rarely a problem, particularly for the purpose being served here.
0.0/27