Exp, Hp, and Mana all going into the negative...

Posted by Toy on Mon 24 May 2004 08:07 AM — 35 posts, 119,152 views.

#0
Ran into a problem which is sorta well known, but searching through the forum, I couldn't find exactly how to fix it.

I raised the top level of my mud from 60 to 210, and the integers went into the negatives. Something i expected, but not sure how to fix. Can someone gimme some advice?

-Toy
USA #1
How far up do the numbers go before they go negative?

I can't remember if the numbers are ints or shorts... but if they're shorts, changing them to int should be enough.
#2
Ok, I went into mud.h and changed each of these from sh_int to int

hit
max_hit
mana
max_mana
move
max_move
practices
exp

everything is ok except for exp, which reaches a negative number at lvl 120 going from 2,022,190,800 to any number over 2,100,000.

-Toy
USA #3
You could make exp a long, or even a long long (__int64 on Windows).

Although, perhaps you should use sub-levels instead. It will be harder to program, but you won't have all these number problems.
USA #4
Or make it an unsigned int which will take you to 4.2 million. If you need more than 4.2mil, go to unsigned long long and youll have more range than you should ever need.
#5
How do I make it an unsigned integer?

-Toy
USA #6
Instead of 'int' use 'unsigned int'.
USA #7
I'm not sure, but you may have to use another var name or something. Like I had to use %llu for long long unsigned.
#8
Ok, I changed it in mud.h to look like this:

unsigned int exp

recompiled and ran the mud, and exp still goes into the negative after reaching 2,100,000.

Test - level 122 - exp: 2,125,873,200
Test - level 123 - exp: -2,115,949,696

and for some reason, once your exp goes negative, your mental state takes a shot of around -90 points. Get this message, which is wierd, because the Test character is a mage:

Damn you heathen! Go forth and do evil or suffer the consequences!
You feel very unmotivated.

-Toy
USA #9
Somethin somewhere didnt get recast to unsigned. Youll need to be sure EVERY reference to that pointer gets recast to display and reference as unsigned int.
#10
I've looked throughout handler.c and mud.h. Did a grep from all calls for exp, and I either can't find it or have overlooked it. Any ideas of what I'm overlooking?

-Toy
Canada #11
It may even be in the function that calculates the xp. If you have one variable that is used, but is not of the same type, it will assign a negatinve number into the long int, which is still valid, and from there keep getting used.
USA #12
For one, you need to get the right numbers :)

Quote:
2,100,000.

Test - level 122 - exp: 2,125,873,200


Your first number is 2 million, the second number is 2 billion.

And lo and behold:

2^32 = 4,294,967,296 (max+1 of unsigned int)
4294967296 / 2 = 2,147,483,648 (max+1 of signed int)

So, somewhere you still have a signed integer instead of an unsigned integer.

However there is no short lying around, because that is:

2^16 = 65536

Clearly the experience was already an integer; you just have to make sure that *every* experience calculation uses unsigned ints and not ints.
#13
Ok, I've tried to find all the exp calculations, this is what I've found so far:

in handler.c:
get_exp( CHAR_DATA *ch )
get_exp_worth( CHAR_DATA *ch ) and it's call to int exp
get_exp_base( CHAR_DATA *ch )
exp_level( CHAR_DATA *ch, sh_int level )
level_exp( CHAR_DATA *ch, unsigned int exp )

in magic.c:
rd_parse(CHAR_DATA *ch, int level, char *exp)
dice_parse(CHAR_DATA *ch, int level, char *exp)

in update.c:
void gain_exp( CHAR_DATA *ch, unsigned int gain ) and it's call to int modgain

in mud.h:
(under class_type) int exp_base
(under race_type) int exp_multiplier
(under mob_index_data) int exp
(under char_data) int exp

(under handler.c call) get_exp args( ( CHAR_DATA *ch ) );
get_exp_worth args( ( CHAR_DATA *ch ) );
exp_level args( ( CHAR_DATA *ch, sh_int level ) );

(under magic.c call)
dice_parse args( (CHAR_DATA *ch, int level, char *exp) );

(under update.c call)
gain_exp args( ( CHAR_DATA *ch, unsigned int gain ) );

Have spent awhile working on this, and still looking. If anyone knows any I've missed, lemme know so I can finish this up. :)

-Toy
USA #14
Quote:
(under class_type) int exp_base
(under race_type) int exp_multiplier
(under mob_index_data) int exp
(under char_data) int exp

For one, those want to be unsigned ints...

Basically, every time you see an integer in an exp calculation, make it an unsigned integer.

[addendum]If you can, compile with g++, even if you're coding in C. It'll give you a lot of freaky (to the coder not aware of the intricacies of C/C++) warnings and even errors, but it's much stricter and will help you find places where you mixed int and unsigned int.
Amended on Tue 25 May 2004 12:19 PM by David Haley
#15
Sorry if I confused you, but I guess I have that tendancy :0

All the things I posted in my last post I've already changed into unsigned ints. At the moment I'm going through each .c file search for the word "int" trying to track down each instance to make sure I'm not missing anything.

I don't really have any other compilers other then cygwin. How would I go about compiling in G++ in cygwin?

-Toy
Canada #16
The only thing you need to do to compile with g++ instead of gcc is to open your make file, change the line that says

cc = gcc

to
cc = g++

Should be all you need. Make clean, and have fun cleaning up all the errors and warnings it generates, heh. I've done it to mine, didn't take to long.
USA #17
Well, be careful - you don't want to change ALL occurences of integers to uints. There are legitimate negative numbers, after all. :) If you change them all to uint, you'll just be breaking something the other way.

And yes, like Greven said I think that g++ comes with gcc and you just change the compiler executable, but it may be a separate package. And yes - have fun. :-)
Australia Forum Administrator #18
Yes, consider this loop:


unsigned int i;

for (i = 10; i >= 0; i--)
  // do something


This counts backwards from 10. However as the condition "i < 0" will never be met it will loop indefinitely.
Australia Forum Administrator #19
There are other traps, like this. Take a simple example program, where someone's experience is decreased when they die:


#include <stdio.h>
int main (void)
  {

  int experience;

  experience = 50;  // new player's experience

  experience -= 100;  // loses experience when he dies

  // ensure not negative after death
  if (experience < 0)
    experience = 0;

  printf ("Experience now %i.\n", experience);

  return 0;

  }

Output Experience now 0.


Now change it to use unsigned integers:


#include <stdio.h>
int main (void)
  {

  unsigned int experience;

  experience = 50;  // new player's experience

  experience -= 100;  // loses experience when he dies

  // ensure not negative after death
  if (experience < 0)
    experience = 0;

  printf ("Experience now %u.\n", experience);

  return 0;

  }

Output Experience now 4294967246.


Notice how after dying they suddenly have heaps of experience? This is because of subtracting 100 from 50 using unsigned arithmetic.


Amended on Tue 25 May 2004 10:14 PM by Nick Gammon
#20
ok, I decided to start fresh, and reverted all of the unsigned ints back into ints. Figured I'd use this g++ stuff you guys mentioned. So I altered the makefile, and ran smack-dab into some errors. Problem is I can't seem to find the problems.

$ make
make smaug
make[1]: Entering directory `/home/KK & LILI/smaug/dist/src'
g++ -c -O -g3 -Wall -Wuninitialized -DSMAUG -DTIMEFORMAT -DREGEX act_comm.c
In file included from act_comm.c:32:
mud.h:398: error: syntax error before `;' token
mud.h:452: error: syntax error before `new'
mud.h:973: error: syntax error before `;' token
mud.h:1025: error: syntax error before `;' token
mud.h:1894: error: syntax error before `;' token
mud.h:2000: error: syntax error before `;' token
act_comm.c: In function `char* translate(int, const char*, const char*)':
act_comm.c:165: error: syntax error before `new'
act_comm.c:196: error: syntax error before `new'
act_comm.c: In function `void do_group(CHAR_DATA*, char*)':
act_comm.c:2413: error: syntax error before `->' token
act_comm.c:2414: error: operands to ?: have different types
act_comm.c:2414: error: syntax error before `:' token
act_comm.c:2437: error: syntax error before `->' token
act_comm.c:2442: error: operands to ?: have different types
act_comm.c:2442: error: syntax error before `:' token
act_comm.c:2455: error: syntax error before numeric constant
act_comm.c: In function `void add_profane_word(char*)':
act_comm.c:3223: warning: comparison between signed and unsigned integer
expressions
make[1]: *** [act_comm.o] Error 1
make[1]: Leaving directory `/home/KK & LILI/smaug/dist/src'
make: *** [all] Error 2

So I attempted to find the errors, but I can't seem to find them. Like this for example. Mud.h, line 398-

int class; /* Classes not allowed to use this */

I'm not seeing any syntax errors. Same with this line, 452

char * new;

Am I missing something here?

-Toy, The-Ever-Question-Asking-Coding-Noob-In-Training. :)
Australia Forum Administrator #21
You want to be careful here. You are doing a C++ compile, not a C compile, and words like "class" and "new" are reserved words for C++, thus the errors you are getting.

You might be better off using gcc rather than g++ and using -Wpedantic to get more warnings.
#22
OK, if I did what you told me to right, this is what I got:

$ make
make smaug
make[1]: Entering directory `/home/KK & LILI/smaug/dist/src'
gcc -c -O -g3 -Wall -Wuninitialized -Wpedantic -DSMAUG -DTIMEFORMAT -DREGEX act_comm.c
cc1: error: unrecognized option `-Wpedantic'
make[1]: *** [act_comm.o] Error 1
make[1]: Leaving directory `/home/KK & LILI/smaug/dist/src'
make: *** [all] Error 2

I'm gonna take a guess here and say I goofed?

-Toy
USA #23
Indeed - g++ is probably defaulting to C++, because C++ is a stricter and hence better language. Ignore the silly myths you may have heard about C++ being slower. C++ is *exactly* the same as C in speed if you don't use any of the extra features.

In any case, just like Nick said it's not working because all those are reserved words in C++. It would be like writing:
char char;
in C. There are two ways to fix this:
- replace 'class' with 'Class' - making the first character uppercase tends to be enough
- put g++ into C mode (it's a command-line parameter, but I can't remember what)
- do what Nick said, use gcc, but step up the warning level considerably. The pedantic mode can be a PITA sometimes, but, well, it also helps you get your code right. :-)


[amendment] You can try adding '-x c' to your compiler options to tell it it's C code, but it should be automatically detecting that anyways.
Amended on Wed 26 May 2004 02:10 AM by David Haley
USA #24
Maybe it's just '-pedantic', not '-Wpedantic'.
Australia Forum Administrator #25
Sorry, it was -pedantic.

Try "man gcc" and searching for words like that to clear things up.
#26
quick question as I work my way through things. Ran into some calculations in fight.c I'm confused about. Sould I make these type of calculations into unsigned ints?


		if ( ch->fighting->who == victim )
	    		xp_gain = (int) (ch->fighting->xp * dam) / victim->max_hit;
		else
	    		xp_gain = (int) (xp_compute( ch, victim ) * 0.85 * dam) / victim->max_hit;


-Toy
USA #27
Yes. xp_gain should also be an unsigned int.
#28
OK, 2 days of trying to chase down ints and here's where I am so far. After using grep to find all the calls I made, I'm altered all these calls so far:

fight.c: unsigned int xp_compute args( ( CHAR_DATA *gch, CHAR_DATA *victim ) );
fight.c: unsigned int xp_gain; KEY( "Exp_Mult", race->exp_multiplier, fread_np ) xp_gain = (unsigned int) (ch->fighting->xp * dam) / victim->max_hit;
fight.c: xp_gain = (unsigned int) (xp_compute( ch, victim ) * 0.85 * dam) / victim->max_hit;
fight.c: fight->xp = (unsigned int) xp_compute( ch, victim ) * 0.85;
fight.c: unsigned int xp;s has maggots worming through its exposed muscle.",
fight.c:: void gain_exp( xp = (unsigned int) (xp_compute( gch, victim ) * 0.1765) / members;
fight.c: unsigned int xp_compute( CHAR_DATA *gch, CHAR_DATA *victim )
fight.c: unsigned int xp;e_table[ch->race]->exp_multiplier/100.0);
fight.c: unsigned int xp_ratio;xp floor of level */
fight.c:: xp_ratio = (unsigned int) gch->played / gchlev;h->level))

handler.c: unsigned int get_exp( CHAR_DATA *ch ), (ch->level+1)) - 1);
handler.c: unsigned int get_exp_worth( CHAR_DATA *ch )>exp >= exp_level(ch, ch->level+1))
handler.c: unsigned int exp;( ch, "You have now obtained experience level %d!\n\r", ++ch->
handler.c: unsigned int get_exp_base( CHAR_DATA *ch )
handler.c: unsigned int base;
handler.c: unsigned int exp_level( CHAR_DATA *ch, sh_int level )
handler.c: int level_exp( CHAR_DATA *ch, unsigned int exp )

magic.c: unsigned int xp = ch->fighting ? ch->fighting->xp : victim->fighting->xp;
magic.c: unsigned int xp_gain = (unsigned int) (xp * dam * 2) / victim->max_hit;
magic.c: unsigned int xp = ch->fighting ? ch->fighting->xp : vch->fighting->xp;
magic.c: unsigned int xp_gain = (unsigned int) (xp * dam * 2) / vch->max_hit;
magic.c: unsigned int xp_gain;
magic.c: unsigned int xp = ch->fighting ? ch->fighting->xp : victim->fighting->xp;
magic.c: xp_gain = (unsigned int) (xp * af.modifier*2) / victim->max_hit;

update.c: void gain_exp( CHAR_DATA *ch, unsigned int gain )
update.c: unsigned int modgain;

Still stuck with a negative exp. Still trying to track it down, but I'm getting nowhere atm.

-Toy
USA #29
Hmm, I remember when adding a new variable that was unsigned long long, I had to change fread_number to unsigned long long. Perhaps you have to change that to unsigned int?
USA #30
You can assign signed/unsigned to each other as long as they have the same size. For example unsigned long long to long long is fine, but unsigned long long to long is not fine.

Toy, I noticed the following function, which should have return type uint I believe:
int level_exp( CHAR_DATA *ch, unsigned int exp )

Don't forget to check your printing functions as well, make sure they're printing unsigned ints and not normal ints.
#31
After 2 days of trying to track down this issue, I'm realizing it's more trouble then it's worth. Decided to put the max levels down to 100. Fits in better with what I'm looking to do (level 50 hero, level 100 legend..) and it avoids the irrating neg. ints.

Thanks anyways for all the help guys! :)

-Toy
USA #32
It would be more work, but you may be better off having sub-levels. It would mean more work, but of a much less tedious kind. :)
USA #33
Yeah, it was fun attempting what you just tried, when I changed max level to 1015 on a Final Fantasy MUD I was coding for. Then I simply resorted to changing the level requirements, and ways of getting exp.
USA #34
yea what zeno said.. you might wanna change the way smaug determines how much exp per level. at lvl 210 your talking...lvl * lvl * lvl * get_exp_base(ch).. or if your exp base is say 1000, 9,261,000,000, just a though. check you exp_level function in handler.c, i upped my mud to 210 levels and i had to change that. then again if you do that you might wanna redo the exp system a little keeping in mind that you get exp with every hit against a mob and when you kill it.