29 Jul, 2015, mikesmoniker wrote in the 1st comment:
Votes: 0
I have an old ROM codebase I've been trying to clean up, modernize, etc. Like most ROM derivs, it uses regular integer bits and has the same OLC system most others do.

I assume I could expand to 64 bits relatively easily, but I'd rather implement something more flexible.

My goals are a solution that:
- provides an infinite number of bits/flags (or more than I'd ever need)
- makes it relatively easy to read/write the bits as a list of string "flags" (i.e., no more parsing "ABDgaa" or raw integer values)
- is hopefully a least path of resistance in implementing into my existing MUD (OLC, pfile saving/loading, etc.)
- is flexible enough to allow adding/removing of bits/flags over time

I don't care about being able to use straight bitwise operations (e.g., FOO|BAR).
I'm not overly concerning about memory usage, within reason.
The MUD doesn't use any C++ features right now, but compiles cleanly in g++.

Things I've looked at:
- wrapping std::bitset, not sure how
- Runter's bit system, would require changing the loading/saving code, but otherwise looks nice
- using plain old arrays of ints and functions/macros to manipulate the ints
30 Jul, 2015, Rarva.Riendf wrote in the 2nd comment:
Votes: 0
Just plain words in an hashtable., so you can also store them in a database easily and in a readable langage.
Then a test of a properties is jsut a look in this hashtable for the key you want.
is_blind(char) woudl be just checktable(blind)
problem solved.
30 Jul, 2015, quixadhal wrote in the 3rd comment:
Votes: 0
Here, this might be useful:

http://pastebin.com/7rLmJyEi

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>

#define BUCKETS 100

struct s_stringMap;

typedef struct s_stringMap {
const char *key;
const char *value;
struct s_stringMap *next;
} stringMap;

unsigned int hashmap( const char *s );
void hash_init( stringMap *map );
void hash_add( stringMap *map, const char *k, const char *v );
const char * hash_find(stringMap *map, const char *k);

unsigned int hashmap( const char *s )
{
unsigned int hash = 0;

if(!s || !*s) return 0;
do {
hash += *s;
hash *= 13;
s++;
} while (*s);

return hash % BUCKETS;
}

void hash_init( stringMap *map )
{
int i;

for(i = 0; i < BUCKETS; i++)
{
map[i].key = NULL;
map[i].value = NULL;
map[i].next = NULL;
}
}

void hash_add( stringMap *map, const char *k, const char *v )
{
unsigned int hashcode;
stringMap *p;

hashcode = hashmap(k);
p = &map[hashcode];
while(p->key && strcmp(p->key, k) && p->next)
p = p->next;

if(!p->key) {
/* First node? */
p->key = (const char *)strdup(k);
p->value = (const char *)strdup(v);
p->next = NULL;
} else if(!strcmp(p->key, k)) {
/* Found our match! */
if(p->value)
free((void *)p->value);
p->value = (const char *)strdup(v);
} else {
/* New key */
p->next = (stringMap *)calloc(1, sizeof(stringMap));
p = p->next;
p->key = (const char *)strdup(k);
p->value = (const char *)strdup(v);
p->next = NULL;
}
}

const char * hash_find(stringMap *map, const char *k)
{
unsigned int hashcode;
stringMap *p;

hashcode = hashmap(k);
p = &map[hashcode];
while(p->key && strcmp(p->key, k) && p->next)
p = p->next;

if(!p->key)
return NULL;

if(!strcmp(p->key, k))
return p->value;

return NULL;
}
30 Jul, 2015, Davion wrote in the 4th comment:
Votes: 0
mikesmoniker said:
- Runter's bit system, would require changing the loading/saving code, but otherwise looks nice
- using plain old arrays of ints and functions/macros to manipulate the ints


Emphasis mine. No matter what you do you're going to have to change the way bits are saved and loaded. And that's not all. You'll have to change the way objects are saved. One of the biggest snag you'll find is when you convert weapon flags into whatever dataset you convert them to. The way item types are handled in rom, it uses a case statement that handles obj_data::values[], these values are an integer array and ROM counts on flags being a single integer so it can use it as a value[] slot.

An example of this is
#define IS_WEAPON_STAT(obj,stat)(IS_SET((obj)->value[4],(stat)))



Also the way they save exit flags is just nuts. It's quite the undertaking.
30 Jul, 2015, Rarva.Riendf wrote in the 5th comment:
Votes: 0
Oh and if you want an 'easy way' just give up :)
Just use a hashtable for the new properties you need, add the old ones to this table, and from time to time when you edit code, change the old code to use the new code when you see it.
Sure you will have both system in place for a while but it will allow you to change code without breaking anything, and not be stuck on checking everywhere that you forgot something.

After a while you ill have replaced enough to be able to just look for the obsolete code and change what is left of it.
02 Aug, 2015, MayaRK wrote in the 6th comment:
Votes: 0
I went this route too with my ROM and changed bits to enums. It's maybe less efficient for reading from files since you have to read in strings and do string comparisons, but it's just another way to do it. I like it since it auto adjusts numbers as flags are added and removed.

declared in header file:
enum {
ENUM_QUIET,
};


saving to file:
fprintf(fp, "Flags %s End\n", flag_bit_name(ch->flags));


loading from file:
case 'Q':
if (!str_cmp(flag, "quiet"))
BITSET(ch->flags, ENUM_QUIET);
11 Aug, 2015, Omega wrote in the 7th comment:
Votes: 0
I created a nifty little system awhile back, it would require changing your code, but I assure you, it works nicely. All bit flags are are stored in 1 single enum, res/imm/act/etc, would all be changed into a singular enum.

#ifndef _Flag_hpp
#define _Flag_hpp

// **************************************************************
// our flag system, this is used in all sorts of places; and is
// essentialy a god when it comes to bitsetting; (well not really)
// but it is simplistic, which is important.
// this class is virtualized for a reason, we want to inherit this
// class and have direct access to the flag manipulation.
class Flag
{
public:
Flag()
{
highestFlag = 0;
}
virtual ~Flag() { }
private:
std::map<int, bool>mFlag;
int highestFlag;
public:
void cleanFlags ( void )
{
std::map<int, bool>::iterator iter, iter_next;
for ( iter = mFlag.begin(); iter != mFlag.end(); iter = iter_next )
{
int x = iter->first;
bool s = iter->second;
iter_next = ++iter;
// strip out our old flag.
if ( !s )
{
mFlag.erase ( x );
}
}
}

bool hasFlag ( int flag )
{
std::map<int, bool>::iterator iter, iter_next;
for ( iter = mFlag.begin(); iter != mFlag.end(); iter = iter_next )
{
int x = iter->first;
bool s = iter->second;
iter_next = ++iter;
// could be true or false?
if ( x == flag )
{
return s;
}
}
return false;
}

// toggle the flag on/off!
void toggleFlag ( int flag )
{
std::map<int, bool>::iterator iter, iter_next;
// set the flag
if ( !hasFlag ( flag ) )
{
mFlag[flag] = true;
if ( flag > highestFlag )
{
highestFlag = flag;
}
return;
}
// get rid of the flag
for ( iter = mFlag.begin(); iter != mFlag.end(); iter = iter_next )
{
int x = iter->first;
iter_next = ++iter;
if ( x == flag )
{
mFlag.erase ( x );
break;
}
}
}

void setFlag ( int flag, bool value )
{
// removing the flag!
if ( hasFlag ( flag ) && !value )
{
std::map<int, bool>::iterator iter, iter_next;
for ( iter = mFlag.begin(); iter != mFlag.end(); iter = iter_next )
{
int x = iter->first;
iter_next = ++iter;
if ( x == flag )
{
mFlag.erase ( x );
break;
}
}
return;
}
// we don't have it, so we don't set it (save memory)
if ( !value )
{
return;
}
mFlag[flag] = value;
if ( flag > highestFlag )
{
highestFlag = flag;
}
return;
}

std::string flagToString ( void )
{
std::string retStr ( "" );
int x = 0;
while ( x <= highestFlag )
{
char buf[10];

// – February 15 2014
// – in a std::map, when you call mFlag[x]
// – if the iterator to the location of x doesn't exist
// – it creates it with a default value, we later clean this
// – up and remove that pointer to the empty data.
// – savings are minimal, but worth it.
// – David Simmerson (Omega)
snprintf ( buf, 10, "%d", mFlag[x] );
retStr.append ( buf );
// increment towards our final destination
x++;
}
// this cleans up our 'other' flags. ones that are 0 instead of 1
// with std::map's when you access them, it creates the default value
// so using mFlag[x], if it wasn't set before, it sets it. cleanFlags
// strips all those that are set to 0; minimal memory saved, but long-term
// it could be a healthy life saver.

// – possible testing to verify if this works the way intended or if is a pointless attempt
// – to conserve minimal memory. More testing required.
// –cleanFlags();
return retStr;
}

void stringToFlag ( const std::string &str )
{
size_t x = 0;
while ( str[x] != '\0' )
{
if ( x > str.length() )
{
break;
}
// only if we are set to 1 shall we add it :)
if ( str[x] == '1' )
{
mFlag[x] = true;
highestFlag = x;
}
// increment!
x++;
}
return;
}
};
#endif


This is an inherited class, but with minor modification it could be a simple pointer.

typedef enum
{
CF_SEE_LOGS = 0,
CF_SEE_BUGS,
CF_SEE_SECURITY,
CF_SEE_LOGINS,
CF_SEE_CTIMER,
CF_SEE_ABORT,
CF_SEE_SCRIPT,
CF_SEE_COMMANDS,
CF_SEE_DEBUG,
CF_SEE_LUA,
CF_SEE_MISSIONS_COMPLETE,
CF_SEE_OLC,
CF_SEE_USAGE,
CF_SEE_UNUSED5,
CF_SEE_UNUSED6
} game_flags;


Simple enumeration.

Writing flags to file:
WRITE_COMMENT(fp, "FLAGS : SET BY ONES AND ZEROS TO REPRESENT ON/OFF");
WRITE_STRING( fp, INDENT"FLAGS", C_STR ( flagToString() ) );


Loading flags from file: (something of this sort)
case 'F':
if ( SameString ( word, "Flags" ) )
{
fMatch = true;
const char *tmpStr = readString ( fp );
stringToFlag ( tmpStr );
delete [] tmpStr;
}
break;


Checking to see if someone / someobject / etc has the flag.
if ( cr->hasFlag ( CR_SEE_LOGS ) )


Setting a flag
cr->setFlag( CR_SEE_LOGS, true); // false removes the flag, true sets the flag.



How it saves the flags:

It counts how many flags are set to true within the map assigned. Then, it iterates over that map and outputs a series of 1's and zero's, representing on/off.

You could have a million zero's at the end of your map from previously set true then false. But unless it see's another 1 down the line, it won't write it.

It turns it into a beautiful string like so: 110010100001010100010101111010101

When it loads, it reads the string, and converts each 1 and zero into a true/false in the map. Effectively creating endless bits.

In my mud I have 1 enum with about 800 bits or so. Half of them are not used by creatures, half are not used by objects. (typically) but the possibility of using them on anything is there, which is good
since anything can be an object, and vice versa.

But again, that's just how I did it. A little C++.
13 Aug, 2015, Rhien wrote in the 8th comment:
Votes: 0
MayaRK said:
I went this route too with my ROM and changed bits to enums. It's maybe less efficient for reading from files since you have to read in strings and do string comparisons, but it's just another way to do it. I like it since it auto adjusts numbers as flags are added and removed.

declared in header file:
enum {
ENUM_QUIET,
};


saving to file:
fprintf(fp, "Flags %s End\n", flag_bit_name(ch->flags));


loading from file:
case 'Q':
if (!str_cmp(flag, "quiet"))
BITSET(ch->flags, ENUM_QUIET);


Would you mind sharing flag_bit_name and/or BITSET (and/or an UNSET)? I couldn't locate any instances with Google and I kind of like the simplicity of this.
13 Aug, 2015, Rarva.Riendf wrote in the 9th comment:
Votes: 0
For everything bit related you may want to read this:
http://graphics.stanford.edu/~seander/bi...

example of code (I dont use a 010101100 pattern but a CHAR pattern (because using a full CHAR jsut for 0 and 1 is wasted (and 01011 is as unreadable than a char value anyway)

with that 
char* pcdata->visitedroom = (char *)calloc(MAX_MAP_ROOM_SAVED , sizeof(char));

bool is_room_visited(CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex) {
if ( !ch || !ch->pcdata || !pRoomIndex)
return FALSE;
if (is_demi(ch))
return TRUE;
return ch->pcdata->visitedroom[pRoomIndex->vnum / 8] & (1 << (pRoomIndex->vnum % 8)) ? TRUE : FALSE;
}

bool is_room_sn_visited(CHAR_DATA *ch, unsigned int vnum) {
if ( !ch || !ch->pcdata)
return FALSE;
if (is_demi(ch))
return TRUE;
return ch->pcdata->visitedroom[vnum / 8] & (1 << (vnum % 8)) ? TRUE : FALSE;
}

void set_room_sn_visited(CHAR_DATA *ch, unsigned int vnum) {
if (!is_room_sn_visited(ch, vnum)) {
//TODO send room info with msdp
}

ch->pcdata->visitedroom[vnum / 8] |= (1 << (vnum % 8));
}

void set_room_visited(CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex) {
if ( !ch || !ch->pcdata || !pRoomIndex || IS_SET(pRoomIndex->room_flags, ROOM_NO_MAP))
return;
set_room_sn_visited(ch, pRoomIndex->vnum);
}


void unset_room_sn_visited(CHAR_DATA *ch, unsigned int vnum) {
ch->pcdata->visitedroom[vnum / 8] &= ~(1 << (vnum % 8));
}


This code allow me to know if a char has visted a room or not, and store it in a simple string, basically each bit is a room vnum.
14 Aug, 2015, Rhien wrote in the 10th comment:
Votes: 0
Thank you for sharing. :-)
14 Aug, 2015, Rarva.Riendf wrote in the 11th comment:
Votes: 0
Basically remove everything with /8 or %8 and it is basically bitset and bitunset. 8 is because of size of char, that I shoudl actually replace by it…but heh…
17 Aug, 2015, MayaRK wrote in the 12th comment:
Votes: 0
Quote
Would you mind sharing flag_bit_name and/or BITSET (and/or an UNSET)? I couldn't locate any instances with Google and I kind of like the simplicity of this.


Sure, Rhien.

These are all the related macros used for the bit setting, clearing, checking, et cetera, and the flag_bit_name() function you were interested in.

#define BITMASK(b) (1 << ((b) % CHAR_BIT))
#define BITSLOT(b) ((b) / CHAR_BIT)
#define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b))
#define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b))
#define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b))
#define BITNSLOTS(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT)

char *flag_bit_name(char *flags) {
static char buf[MSL];
buf[0] = '\0';

if (BITTEST(flags, FLAG_QUIET))
strncat(buf, " quiet", sizeof(buf) - strlen(buf) - 1);
//other if checks for any other bits you have…

return (buf[0] != '\0') ? buf + 1 : "none";
}


I hope it helps!
18 Aug, 2015, Rhien wrote in the 13th comment:
Votes: 0
Thank you very much Maya!
0.0/13