02 Jul, 2011, triskaledia wrote in the 1st comment:
Votes: 0
I've been scrolling through the fight code and I have found little to no account for hitroll deciding if you actually hit your target.
The question: Has anyone else put hitroll into the equation before and how did it go?
Also while rolling out the final chances to hit you have:
/*
* The moment of excitement!
*/
while ((diceroll = number_bits (5)) >= 20);

if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac))
{
/* Miss. */
damage (ch, victim, 0, dt, dam_type, TRUE);
tail_chain ();
return;
}

in db.c I found:
int number_bits (int width)
{
return number_mm () & ((1 << width) - 1);
}
… and the question to all that code is, does anyone have a decent explanation for now number_bits works?
02 Jul, 2011, Rarva.Riendf wrote in the 2nd comment:
Votes: 0
while ((diceroll = number_bits (5)) >= 20);

Just return a number between 0 and 19 . number_bits(5) is a hackish way to return a random number that had a sense back in 1992, not anymore, and you better use the rand from the os

thac0 -= GET_HITROLL(ch) * skill/100; <- this is where hitroll matters

And basically all this mean you are d20 based.
02 Jul, 2011, quixadhal wrote in the 3rd comment:
Votes: 0
triskaledia said:
I've been scrolling through the fight code and I have found little to no account for hitroll deciding if you actually hit your target.
The question: Has anyone else put hitroll into the equation before and how did it go?
Also while rolling out the final chances to hit you have:
/*
* The moment of excitement!
*/
while ((diceroll = number_bits (5)) >= 20);

if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac))
{
/* Miss. */
damage (ch, victim, 0, dt, dam_type, TRUE);
tail_chain ();
return;
}

in db.c I found:
int number_bits (int width)
{
return number_mm () & ((1 << width) - 1);
}
… and the question to all that code is, does anyone have a decent explanation for now number_bits works?


It was designed while on crack. :ghostface:

Back in college, the guy who wrote ROM decided he knew better than the folks who write operating systems for a living, so he took his fancy math textbook and whipped up his own psudo-random number generateor, cobbled together into C code as number_mm(). You will note that #ifdef OLD_RAND, all this garbage code, #else, return random() >> 6, #endif.

So, we take a 32-bit random number (16-bit if you're on an OLD platform) and throw away 6 bits worth of randomness for no really good reason.

Then, if you're using the crack-induced number_bits() interface, we take that 26-bit random number and further mask it to the selected number of bits, so number_bits(3) would return a random number from 0 to 2^4-1, or 7.

That part shouldn't scare you, it's the innocent looking line that you didn't ask about that should have you screaming and wondering why in God's name you are touching anything related to this codebase with a ten foot pole.

triskaledia said:
/*
* The moment of excitement!
*/
while ((diceroll = number_bits (5)) >= 20);


You realize that this line of code has the (slim, but not 0%) possibility of NEVER escaping the while loop?
This could be rewritten more obviously as:

while( (diceroll = (random()%32)) >= 20 ) { create_lag(); }


Yes, this really does spin in a loop until a random number happens to fall into the bottom 62.5% of the range being generated. The end result is that it generates a number from 0..19 and produces a random amount of lag, since it may need to run once, or it may need to run thousands of times before the number generated is < 20.

diceroll = random() % 20;


That does the same thing but without the random lag. For some unknown (crack-induced) reason, the Dikurivative authors all seem to really like running things in while loops that don't NEED to be in loops at all. Good luck!
02 Jul, 2011, Runter wrote in the 4th comment:
Votes: 0
Tyche inc
03 Jul, 2011, triskaledia wrote in the 5th comment:
Votes: 0
Quoting Rarva
thac0 -= GET_HITROLL(ch) * skill/100; <- this is where hitroll matters
So hitroll actually needs should be calculated as a negative number within the code to determine if you hit or not…

Quoting Quixadhal
It was designed while on crack. :ghostface:
I'm starting to think it was more than crack… Why is everything to freaking complicated? I'm pretty much the back of the bus coder and I'm sure I could have came up with a better way to code this… Ha.
03 Jul, 2011, triskaledia wrote in the 6th comment:
Votes: 0
if
{
thac0_00 = 20 + GET_HITROLL (ch);
thac0_32 = -4 + GET_HITROLL (ch); /* as good as a thief */
/***That random act_class stuff***/
}
else
{
thac0_00 = class_table[ch->class].thac0_00 + GET_HITROLL (ch);
thac0_32 = class_table[ch->class].thac0_32 + GET_HITROLL (ch);
}
diceroll = number_range(0, 19);

if ((diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac)) && (GET_HITROLL (ch) / number_range(2,5) < GET_HITROLL (victim) / number_range(2,5)))
{
/* Miss. */
damage (ch, victim, 0, dt, dam_type, TRUE);
tail_chain ();
return;
}


Too bad it's not as simple as I thought. I always roll dude.
03 Jul, 2011, triskaledia wrote in the 7th comment:
Votes: 0
Quite bored, tired, and don't want to start a new thread for another question.
dam += GET_DAMROLL (ch) * UMIN (100, skill) / 100;
Someone care to explain UMIN? 100, skilldagger@75 so it rolls a (damroll * (100d75/100))?
03 Jul, 2011, Runter wrote in the 8th comment:
Votes: 0
min functions return the smallest of the set. Or in this case, the two. So UMIN(100, skill) means to either return 100 or the value of skill, which ever is smallest. It's a functional way of setting a maximum effectual cap for skill in that instance.
03 Jul, 2011, Rarva.Riendf wrote in the 9th comment:
Votes: 0
triskaledia said:
Quite bored, tired, and don't want to start a new thread for another question.
dam += GET_DAMROLL (ch) * UMIN (100, skill) / 100;
Someone care to explain UMIN? 100, skilldagger@75 so it rolls a (damroll * (100d75/100))?


check the macro code it basically returning the min value of the two variable
that means unless your skill is over 100 damroll wont be used as all those are int so UMIN (100, skill) / 100 can only give 0 or 1 (0 if your skill is <=74 1 after that) … (and that for some reason your skill is alwas skill + 20 when it is a weapon proficiency (skill = 20 + get_weapon_skill(ch,sn);)

After those few lines, you really should reconsider using the ROM codebase. Unless you want to revive an old mud you played on, and even then, the time poured in debugging ROM would be better used migrating to a better engine.

dam += GET_DAMROLL (ch) * (double)UMIN (100, skill) / 100 is the solution to actually make a difference, but then again when all this is based on d20 values…not so much.
03 Jul, 2011, Runter wrote in the 10th comment:
Votes: 0
Runter said:
min functions return the smallest of the set. Or in this case, the two. So UMIN(100, skill) means to either return 100 or the value of skill, which ever is smallest. It's a functional way of setting a maximum effectual cap for skill in that instance.


And to elaborate in another language you might see something like this:
skill = 75
value = [88, 100, 23, 459, skill].min


In that example skill is set to 75 and then out of an array of integers the min function is used and value is set to what it returns. In this case 23 is returned because it is the minimal value of the set. To be like your original example:

value = [100, skill].min


In this example, if skill is less than 100 it will be the value used. If not, it will be 100 that is returned from the minimal function.

Akin to the min function is the max function.

[1, 10, 100, 1000].max


In this example we have an array of 4 values and the max function is called on the set. It returns the maximum value, so 1000 in this instance.

In other places you may see min-max functions, but it's actually a derivative and I find it better to write it like this:

[1, [100, skill].min].max


If skill is below 1 this returns 1. If skill is above 100 this returns 100. Otherwise it returns the value of skill.

You may see it written in C with a user defined function/macro in some places like this:

i = minmax(1, value, 100);
03 Jul, 2011, Tyche wrote in the 11th comment:
Votes: 0
Runter said:
Tyche inc

They apparently didn't grok it the first time around.
[link=post]54362[/link]

quixadhal said:
Back in college, the guy who wrote ROM decided he knew better than the folks who write operating systems for a living, so he took his fancy math textbook and whipped up his own psudo-random number generateor, cobbled together into C code as number_mm(). You will note that #ifdef OLD_RAND, all this garbage code, #else, return random() >> 6, #endif.


Speaking of garbage…
It was Michael Chastain that added that code to Merc. I guess anyone who bothers to read, understand and implement an algorithm from Knuth, must be a fucking idiot crack smoker. Merc ran on Ultrix, Domain/OS, MsDos, Dynix, SunOS, Macintosh, Hp/UX, Aix, Risc/OS 5.XX, Mach, and Linux…many of which had poor random() functions. It was Russ Taylor who commented it out.

quixadhal said:
So, we take a 32-bit random number (16-bit if you're on an OLD platform) and throw away 6 bits worth of randomness for no really good reason.


It wasn't until 1994, that the GNU C library team decided to discard those lower bits from random().
I guess Russ Taylor was ahead of "the folks who write operating systems for a living".
Russ Taylor and the GNU C library team obviously know the reason. How come you don't?

quixadhal said:
triskaledia said:
/*
* The moment of excitement!
*/
while ((diceroll = number_bits (5)) >= 20);


You realize that this line of code has the (slim, but not 0%) possibility of NEVER escaping the while loop?


No. The probability of never exiting the loop is ZERO.

quixadhal said:
Yes, this really does spin in a loop until a random number happens to fall into the bottom 62.5% of the range being generated. The end result is that it generates a number from 0..19 and produces a random amount of lag, since it may need to run once, or it may need to run thousands of times before the number generated is < 20.


I ran the code, 'while ((diceroll = number_bits (5)) >= 20);', a billion times.

Loops Occurrences Fraction
0 624988764 0.624989
1 234369840 0.234370
2 87893453 0.087893
3 32973889 0.032974
4 12358776 0.012359
5 4635620 0.004636
6 1739313 0.001739
7 650949 0.000651
8 243168 0.000243
9 91109 0.000091
10 34484 0.000034
11 12968 0.000013
12 4824 0.000005
13 1778 0.000002
14 687 0.000001
15 220 0.000000
16 102 0.000000
17 36 0.000000
18 12 0.000000
19 6 0.000000
21 1 0.000000
22 1 0.000000

Total: 1000000000 1.000000

The probably of that loop executing more than 30 times is ZERO.

I whipped up a snippet. Enjoy.
#if defined (OLD_RAND)
static int rgiState[2+55];
#elif defined (TYCHE_RAND)
static long rgiState[32] = {
0x00000011, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02,
0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7,
0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96,
0xac94efdc, 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b, 0x27fb47b9
};
static long *fp = &rgiState[4];
static long *rp = &rgiState[1];
static long *piState = &rgiState[1];
#endif

long number_mm( void )
{
#if defined (OLD_RAND)
int *piState;
int iState1;
int iState2;
int iRand;

piState = &rgiState[2];
iState1 = piState[-2];
iState2 = piState[-1];
iRand = (piState[iState1] + piState[iState2])
& ((1 << 30) - 1);
piState[iState1] = iRand;
if ( ++iState1 == 55 )
iState1 = 0;
if ( ++iState2 == 55 )
iState2 = 0;
piState[-2] = iState1;
piState[-1] = iState2;
return iRand >> 6;
#elif defined (TYCHE_RAND)
long i;
*fp += *rp; i = (*fp >> 1) & 0x7fffffff;
if (++fp >= &rgiState[32]) {fp = piState;++rp;
} else if (++rp >= &rgiState[32]) rp = piState;
return(i >> 6);
#else
return random() >> 6;
#endif
}

void init_mm( )
{
#if defined (OLD_RAND)
int *piState;
int iState;

piState = &rgiState[2];

piState[-2] = 55 - 55;
piState[-1] = 55 - 24;

piState[0] = ((int) current_time) & ((1 << 30) - 1);
piState[1] = 1;
for ( iState = 2; iState < 55; iState++ )
{
piState[iState] = (piState[iState-1] + piState[iState-2])
& ((1 << 30) - 1);
}
#elif defined (TYCHE_RAND)
int i, j = 1;
piState[0] = time(NULL);
for (i = 1; i < 31; i++) piState[i] = 1103515245 * piState[i - 1] + 12345;
fp = &piState[3]; rp = &piState[0];
for (i = 0; i < 10 * 31; i++) number_mm();
#else
srandom(time(NULL)^getpid());
#endif
return;
}
03 Jul, 2011, Runter wrote in the 12th comment:
Votes: 0
Is there anything wrong with using a straight call to the system rng? Does it have the problems that some may have had in the past? I know it's an open ended question since there's a ton of possible operating systems.
03 Jul, 2011, Rarva.Riendf wrote in the 13th comment:
Votes: 0
Runter said:
Is there anything wrong with using a straight call to the system rng? Does it have the problems that some may have had in the past? I know it's an open ended question since there's a ton of possible operating systems.

Not anymore and for the sake of readability, all the old code that was there to go around bug that are not anymore should be dumped.
When you add a patch to fix an os bug, you should comment WHY you did it, so people afterwards can remove it once the bug is indeed fixed.

And whatever Tyche says running something a billion time is not enough to mathematically prove something.
Maybe it is true though, because the seeds makes it so…but I somehow doubt it can be mathematicaly proven.
And when it comes to computer and loops, you better be sure you can logically prove it will exit at some point.
Because computers do not care about statistics, they care about pure logic. (and not too much solar flares)
)
03 Jul, 2011, Runter wrote in the 14th comment:
Votes: 0
I think tyches point is this:

$rng = [1,2,3]
def random
$rng.rotate!
return $rng[0]
end

# get a "random" number between 1 and 3.
while((value = random()) != 3) ; # Can't infinite loop


Okay, so that's not a good random generator function, but the point is it's similar to the deterministic nature of a prng.
03 Jul, 2011, quixadhal wrote in the 15th comment:
Votes: 0
I'll accept that when using THAT code (IE: OLD_RAND is defined), you may be able to say it's not possible for the while loop to be infinite, because it would eventually cycle to a state that would generate a number meeting the criteria in question. That assumes no bugs or logic errors.

However, because the system PRNG is (to the application programmer) a black box, you can't know that is true if you are NOT using OLD_RAND.

In either case though, I maintain that using a while loop to select a number that could be generated and fit to range in one call is stupidly wasteful and teaches very bad habits. Using your test run numbers, 62% of the time, the random number fell into the acceptable criteria, 23% of the time you did twice as much work as you needed to, 8% of the time you did 3X the work you needed to, and the remaining 7% of the time you did even more extra work than that. Good thing CPU is cheap these days.

I write bad code too, and if someone points out how stupid my crap is, I try to make it less stupid. It says something rather sad that this code has persisted since Merc and remains in production use. Maybe that something is, we have too powerful CPU's and nobody cares. Maybe it's that nobody ever bothers to question what's handed to them. What it says to me is, this is dumb, make it less dumb or explain why it's not really dumb at all.
03 Jul, 2011, Vigud wrote in the 16th comment:
Votes: 0
Quote
It says something rather sad that this code has persisted since Merc and remains in production use.
It says no more and no less than "making it more efficient would be more expensive than leaving as it is".

For exactly that reason, people choose higher-level languages like Python or Ruby - because they value their time more than CPU cycles. Is that really that sad?
03 Jul, 2011, Runter wrote in the 17th comment:
Votes: 0
quixadhal said:
I'll accept that when using THAT code (IE: OLD_RAND is defined), you may be able to say it's not possible for the while loop to be infinite, because it would eventually cycle to a state that would generate a number meeting the criteria in question. That assumes no bugs or logic errors.

However, because the system PRNG is (to the application programmer) a black box, you can't know that is true if you are NOT using OLD_RAND.

In either case though, I maintain that using a while loop to select a number that could be generated and fit to range in one call is stupidly wasteful and teaches very bad habits. Using your test run numbers, 62% of the time, the random number fell into the acceptable criteria, 23% of the time you did twice as much work as you needed to, 8% of the time you did 3X the work you needed to, and the remaining 7% of the time you did even more extra work than that. Good thing CPU is cheap these days.

I write bad code too, and if someone points out how stupid my crap is, I try to make it less stupid. It says something rather sad that this code has persisted since Merc and remains in production use. Maybe that something is, we have too powerful CPU's and nobody cares. Maybe it's that nobody ever bothers to question what's handed to them. What it says to me is, this is dumb, make it less dumb or explain why it's not really dumb at all.


I don't disagree with you overall. But I think some of the direction of the discussion was to the appropriateness of the code when it was originally written. It seems from what Tyche says that it may have been appropriate.

Also, the other counter point I'd like to make is that in a vast majority of the test cases for Tyche fell under 3 loop passes. You're being fallacious here when you claim that's X3 the CPU work. Overall, the function itself using this code to determine anything, would for all intents and purposes, take practically an identical amount of time to execute to completion. So there's a vast chasm here between "the function may NEVER leave the loop" and the loop might take 3 passes. In fact, I think the optimization argument is completely removed from the table. I think it's a better argument that it's just gross and (perhaps) useless at this point. There's a lot better optimizations to be had in the codebase than worrying about 3 passes of a 1 instruction loop.
03 Jul, 2011, Rarva.Riendf wrote in the 18th comment:
Votes: 0
Vigud said:
Quote
It says something rather sad that this code has persisted since Merc and remains in production use.
It says no more and no less than "making it more efficient would be more expensive than leaving as it is".

For exactly that reason, people choose higher-level languages like Python or Ruby - because they value their time more than CPU cycles. Is that really that sad?


int rnd_rng(int hi) {//the correct random number generator for 0-hi
int randValue = ((double) rand() / RAND_MAX) * (hi + 1);
if (randValue > hi)
randValue = 0;
return randValue;
}
This is what you should use, not for cpu sake but for simple actual random distribution (using modulo is a cheap way that will break random distribution)
04 Jul, 2011, Tyche wrote in the 19th comment:
Votes: 0
Runter said:
I think tyches point is this:


It's more that every integer in a set from 1 to n can be generated by recursively calling the function with it's result starting by multiplying an starting integer in that set number by some prime number also in that set and adding a number that's a coprime of the modulus n, where n is a multiple of 4. It just so happens that on almost all computers integrals are represent by multiples of 4-bits and the integer math overflow is a modulus function of the integer size. So that something as simple as static unsigned int next = 0; int foo() {next=next*69069+1;} will generate every number up to 2^31 (or whatever the implementation is of signed ints). It's a finite series.

ObAside: Ruby's numbers have the property of automatically overflowing into bignums, so you would have to include the modulus part of the equation.

Anyway, apparently this was all inspired by the idiotic ravings of some 17th century crack-smoking French doofus who couldn't code. Not only couldn't he code his way out of a paper bag, he was even too stupid and lazy to even back it up with any sort of proof. About a hundred years later, another crack-addled German asshole, who also couldn't code worth shit, managed to write up some proofs in some gibberish which only math nerds understand. Eventually some people in the 20th century tried to implement this gibberish on computers, badly of course (definately not like anyone here would). Now I'd mention their names, but I think posters would just make fun of them.

Rarva.Riendf said:
Not anymore and for the sake of readability, all the old code that was there to go around bug that are not anymore should be dumped.
When you add a patch to fix an os bug, you should comment WHY you did it, so people afterwards can remove it once the bug is indeed fixed.


What bug are you talking about? I'm not aware of any bug.
If you think there is a bug, why not explain it and give us a patch?

Rarva.Riendf said:
And whatever Tyche says running something a billion time is not enough to mathematically prove something.


It wasn't presented as a proof. I don't write proofs. I'm too ignorant and lazy to write proofs.
Heck, I barely write enough code to sell to maintain my own crack addiction.
It was presented as possible clue that the statement it was posted in response to just may be in error.

quixadhal said:
I'll accept that when using THAT code (IE: OLD_RAND is defined), you may be able to say it's not possible for the while loop to be infinite, because it would eventually cycle to a state that would generate a number meeting the criteria in question. That assumes no bugs or logic errors.

However, because the system PRNG is (to the application programmer) a black box, you can't know that is true if you are NOT using OLD_RAND.


Well I know it is safe from reading the POSIX specification as random() is a POSIX function.
"The random() function shall use a non-linear additive feedback random-number generator…"
They had me at "additive feedback generator", but then only after previous readings about LCGs.

quixadhal said:
In either case though, I maintain that using a while loop to select a number that could be generated and fit to range in one call is stupidly wasteful and teaches very bad habits.


So what's the simplest way to fix a PRNG that emits "streaking" numbers?
Where does one get off on saying something is "stupidly wasteful" without providing a solution?
Are programmers of web servers that implement DES and RSA encryption algorithms being stupidly wasteful and exhibit bad programming habits for writing loops that call random functions a random number of times?
Maybe "context" IS important.

As far as bad programming habits go… there's this guy who sits in a cubicle next to me goes to lunch at Taco Bell everyday and gets bean burritos. He then proceeds to fart in his cubicle the rest of the afternoon, disturbing my concentration.
More should be said and done to prevent these sorts of bad programming practices.
His code sucks too.
04 Jul, 2011, Runter wrote in the 20th comment:
Votes: 0
Tyche said:
[…]It's a finite series. […]


That was what i was trying to get across. In a way that one of us non-crack smokers could understand ;)
0.0/62