/***************************************************************************
* Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming *
* License by Wizards of the Coast. All comments referring to D20, OGL, *
* and SRD refer to the System Reference Document for the Open Gaming *
* system. Any inclusion of these derivatives must include credit to the *
* Mud20 system, the full and complete Open Gaming LIcense, and credit to *
* the respective authors. See ../doc/srd.txt for more information. *
* *
* Emud 2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem. *
* *
* MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey *
* *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe. *
***************************************************************************/
/***************************************************************************
* math.c: math functions for server, duh. *
***************************************************************************/
#include "mud.h"
/*
mathematical expression interpreter - Scandum
*/
#define EXP_VARIABLE 0
#define EXP_STRING 1
#define EXP_OPERATOR 2
#define EXP_PR_DICE 0
#define EXP_PR_INTMUL 1
#define EXP_PR_INTADD 2
#define EXP_PR_BITSHIFT 3
#define EXP_PR_LOGLTGT 4
#define EXP_PR_LOGCOMP 5
#define EXP_PR_BITAND 6
#define EXP_PR_BITXOR 7
#define EXP_PR_BITOR 8
#define EXP_PR_LOGAND 9
#define EXP_PR_LOGXOR 10
#define EXP_PR_LOGOR 11
#define EXP_PR_VAR 12
#define EXP_PR_LVL 13
MATH_DATA *mathnode_s;
MATH_DATA *mathnode_e;
MATH_DATA *mathnode_n;
lg_int mathexp(CHAR_DATA *ch, const char *str)
{
MATH_DATA *node;
push_call("mathexp(%p,%p)",ch,str);
if (mathexp_tokenize(ch, str) == FALSE)
{
if (mud->f_math == NULL)
{
if (IS_NPC(ch))
{
log_build_printf(ch->pIndexData->vnum, "mathexp: invalid input");
}
else
{
ch_printf_color(ch, "mathexp: invalid input: ");
}
}
pop_call();
return 0;
}
node = mud->f_math;
while (node->prev || node->next)
{
if (node->next == NULL || atoi(node->next->str1) < atoi(node->str1))
{
mathexp_level(ch, node);
node = mud->f_math;
}
else
{
node = node->next;
}
}
pop_call();
return tintoi(node->str3);
}
void add_mathnode(char *buf1, char *buf2, char *buf3)
{
MATH_DATA *node;
push_call("add_mathnode(void)");
ALLOCMEM(node, MATH_DATA, 1);
node->str1 = STRALLOC(buf1);
node->str2 = STRALLOC(buf2);
node->str3 = STRALLOC(buf3);
LINK(node, mud->f_math, mud->l_math, next, prev);
pop_call();
return;
}
void del_mathnode(MATH_DATA *node)
{
push_call("del_mathnode(%p)",node);
STRFREE(node->str1);
STRFREE(node->str2);
STRFREE(node->str3);
UNLINK(node, mud->f_math, mud->l_math, next, prev);
FREEMEM(node);
pop_call();
return;
}
#define MATH_NODE(buffercheck, priority, newstatus) \
{ \
if (buffercheck && pta == buf3) \
{ \
return FALSE; \
} \
*pta = 0; \
sprintf(buf1, "%02d", level); \
sprintf(buf2, "%02d", priority); \
add_mathnode(buf1, buf2, buf3); \
status = newstatus; \
pta = buf3; \
}
lg_int mathexp_tokenize(CHAR_DATA *ch, const char *str)
{
char buf1[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH], buf3[MAX_INPUT_LENGTH], *pti, *pta;
int level, status;
push_call("mathexp_tokenize(%p,%p)",ch,str);
level = 0;
status = EXP_VARIABLE;
pta = (char *) buf3;
pti = (char *) str;
while (mud->f_math)
{
del_mathnode(mud->f_math);
}
mud->f_math = mud->l_math = NULL;
while (*pti)
{
switch (status)
{
case EXP_VARIABLE:
switch (*pti)
{
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
*pta++ = *pti++;
break;
case '!':
case '~':
case '+':
case '-':
if (pta != buf3)
{
MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
}
else
{
*pta++ = *pti++;
}
break;
case '"':
if (pta != buf3)
{
if (IS_NPC(ch))
{
log_build_printf(ch->pIndexData->vnum, "mathexp: \" found inside integer");
}
else
{
ch_printf_color(ch, "mathexp: \" found inside integer: ");
}
pop_call();
return FALSE;
}
*pta++ = *pti++;
status = EXP_STRING;
break;
case '(':
if (pta != buf3)
{
if (IS_NPC(ch))
{
log_build_printf(ch->pIndexData->vnum, "mathexp: ( found inside integer");
}
else
{
ch_printf_color(ch, "mathexp: ( found inside integer: ");
}
pop_call();
return FALSE;
}
*pta++ = *pti++;
MATH_NODE(TRUE, EXP_PR_LVL, EXP_VARIABLE);
level++;
break;
case ' ':
pti++;
break;
default:
MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR);
break;
}
break;
case EXP_STRING:
switch (*pti)
{
case '"':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
break;
default:
*pta++ = *pti++;
break;
}
break;
case EXP_OPERATOR:
switch (*pti)
{
case ' ':
pti++;
break;
case ')':
*pta++ = *pti++;
level--;
MATH_NODE(TRUE, EXP_PR_LVL, EXP_OPERATOR);
break;
case 'd':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_DICE, EXP_VARIABLE);
break;
case '*':
case '/':
case '%':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_INTMUL, EXP_VARIABLE);
break;
case '+':
case '-':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_INTADD, EXP_VARIABLE);
break;
case '<':
*pta++ = *pti++;
switch (*pti)
{
case '<':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE);
break;
case '=':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
}
break;
case '>':
*pta++ = *pti++;
switch (*pti)
{
case '>':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE);
break;
case '=':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE);
break;
}
break;
case '&':
*pta++ = *pti++;
switch (*pti)
{
case '&':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGAND, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_BITAND, EXP_VARIABLE);
break;
}
break;
case '^':
*pta++ = *pti++;
switch (*pti)
{
case '^':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGXOR, EXP_VARIABLE);
break;
default:
MATH_NODE(FALSE, EXP_PR_BITXOR, EXP_VARIABLE);
break;
}
break;
case '|':
*pta++ = *pti++;
switch (*pti)
{
case '|':
*pta++ = *pti++;
MATH_NODE(FALSE, 7, EXP_VARIABLE);
break;
default:
*pta++ = *pti++;
MATH_NODE(FALSE, 5, EXP_VARIABLE);
break;
}
break;
case '=':
case '!':
*pta++ = *pti++;
switch (*pti)
{
case '=':
*pta++ = *pti++;
MATH_NODE(FALSE, EXP_PR_LOGCOMP, EXP_VARIABLE);
break;
default:
if (IS_NPC(ch))
{
log_build_printf(ch->pIndexData->vnum, "mathexp: unknown operator: %c%c", pti[0], pti[1]);
}
else
{
ch_printf_color(ch, "mathexp: unknown operator: %c%c: ", pti[0], pti[1]);
}
pop_call();
return FALSE;
}
break;
default:
if (IS_NPC(ch))
{
log_build_printf(ch->pIndexData->vnum, "mathexp: unknown operator: %c", *pti);
}
else
{
ch_printf_color(ch, "mathexp: unknown operator: %c: ", *pti);
}
pop_call();
return FALSE;
}
break;
}
}
if (level != 0)
{
if (IS_NPC(ch))
{
log_build_printf(ch->pIndexData->vnum, "mathexp: unmatched parentheses, level: %d", level);
}
else
{
ch_printf_color(ch, "mathexp: unmatched parentheses, level: %d: ", level);
}
pop_call();
return FALSE;
}
if (status != EXP_OPERATOR)
{
MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR);
}
pop_call();
return TRUE;
}
void mathexp_level(CHAR_DATA *ch, MATH_DATA *node)
{
int priority;
mathnode_e = node;
while (node->prev)
{
if (atoi(node->prev->str1) < atoi(node->str1))
{
break;
}
node = node->prev;
}
mathnode_s = node;
for (priority = 0 ; priority < EXP_PR_VAR ; priority++)
{
for (node = mathnode_s ; node ; node = node->next)
{
if (atoi(node->str2) == priority)
{
mathexp_compute(ch, node);
}
if (node == mathnode_e)
{
break;
}
}
}
node = mathnode_s;
while (node->prev && node->next)
{
if (atoi(node->prev->str2) == EXP_PR_LVL && atoi(node->next->str2) == EXP_PR_LVL)
{
STRFREE(node->str1);
node->str1 = STRALLOC(node->next->str1);
del_mathnode(node->next);
del_mathnode(node->prev);
}
else
{
break;
}
}
return;
}
void mathexp_compute(CHAR_DATA *ch, MATH_DATA *node)
{
char temp[MAX_INPUT_LENGTH];
int value;
switch (node->str3[0])
{
case 'd':
if (tintoi(node->next->str3) <= 0)
{
ch_printf_color(ch, "mathexp: invalid dice: ");
value = 0;
}
else
{
value = tindice(node->prev->str3, node->next->str3);
}
break;
case '*':
value = tintoi(node->prev->str3) * tintoi(node->next->str3);
break;
case '/':
if (tintoi(node->next->str3) == 0)
{
ch_printf_color(ch, "mathexp: division zero: ");
value = tintoi(node->prev->str3) / 1;
}
else
{
value = tintoi(node->prev->str3) / tintoi(node->next->str3);
}
break;
case '%':
if (tintoi(node->next->str3) == 0)
{
ch_printf_color(ch, "mathexp: division zero: ");
value = tintoi(node->prev->str3) / 1;
}
else
{
value = tintoi(node->prev->str3) % tintoi(node->next->str3);
}
break;
case '+':
value = tintoi(node->prev->str3) + tintoi(node->next->str3);
break;
case '-':
value = tintoi(node->prev->str3) - tintoi(node->next->str3);
break;
case '<':
switch (node->str3[1])
{
case '=':
value = tincmp(node->prev->str3, node->next->str3) <= 0;
break;
case '<':
value = tintoi(node->prev->str3) << tintoi(node->next->str3);
break;
default:
value = tincmp(node->prev->str3, node->next->str3) < 0;
break;
}
break;
case '>':
switch (node->str3[1])
{
case '=':
value = tincmp(node->prev->str3, node->next->str3) >= 0;
break;
case '>':
value = tintoi(node->prev->str3) >> tintoi(node->next->str3);
break;
default:
value = tincmp(node->prev->str3, node->next->str3) > 0;
break;
}
break;
case '&':
switch (node->str3[1])
{
case '&':
value = tintoi(node->prev->str3) && tintoi(node->next->str3);
break;
default:
value = tintoi(node->prev->str3) & tintoi(node->next->str3);
break;
}
break;
case '^':
switch (node->str3[1])
{
case '^':
value = ((tintoi(node->prev->str3) || tintoi(node->next->str3)) && (!tintoi(node->prev->str3) != !tintoi(node->next->str3)));
break;
default:
value = tintoi(node->prev->str3) ^ tintoi(node->next->str3);
break;
}
break;
case '|':
switch (node->str3[1])
{
case '|':
value = tintoi(node->prev->str3) || tintoi(node->next->str3);
break;
default:
value = tintoi(node->prev->str3) | tintoi(node->next->str3);
break;
}
break;
case '=':
value = (tincmp(node->next->str3, node->prev->str3) == 0);
break;
case '!':
value = (tincmp(node->next->str3, node->prev->str3) != 0);
break;
default:
value = 0;
break;
}
if (node->prev == mathnode_s)
{
mathnode_s = node;
}
if (node->next == mathnode_e)
{
mathnode_e = node;
}
del_mathnode(node->next);
del_mathnode(node->prev);
sprintf(temp, "%d", EXP_PR_VAR);
STRFREE(node->str2);
node->str2 = STRALLOC(temp);
sprintf(temp, "%d", value);
STRFREE(node->str3);
node->str3 = STRALLOC(temp);
}
lg_int tintoi(const char *str)
{
switch (str[0])
{
case '!':
return !atoll(&str[1]);
case '~':
return ~atoll(&str[1]);
case '+':
return +atoll(&str[1]);
case '-':
return -atoll(&str[1]);
default:
return atoi(str);
}
}
lg_int tincmp(const char *left, const char *right)
{
switch (left[0])
{
case '"':
return strcmp(left, right);
default:
return tintoi(left) - tintoi(right);
}
}
lg_int tindice(const char *left, const char *right)
{
lg_int cnt, numdice, sizedice, sum;
numdice = tintoi(left);
sizedice = tintoi(right);
for (cnt = sum = 0 ; cnt < numdice ; cnt++)
{
sum += lrand48() % sizedice + 1;
}
return sum;
}