Synthetic Reality Forums Post New Topic  New Poll  Post A Reply
my profile | directory login | register | search | faq | forum home

  next oldest topic   next newest topic
» Synthetic Reality Forums » General Madness » Random Thoughts » Programming thread [repost] (Page 2)

  This topic comprises 2 pages: 1  2   
Author Topic: Programming thread [repost]
saxamophone0
Obsessive Member
Member # 2917

Member Rated:
4
Icon 1 posted      Profile for saxamophone0   Author's Homepage   Email saxamophone0   Send New Private Message       Edit/Delete Post 
What would be the best way to write my own random quote generator script? Preferably something that will work on the web...?

--------------------
Word is born.

Posts: 8529 | From: NYC | Registered: Oct 2002  |  IP: Logged
MasaMune
Verbose Member
Member # 2121

Icon 1 posted      Profile for MasaMune   Author's Homepage   Email MasaMune   Send New Private Message       Edit/Delete Post 
It costs me about $9 a month, so no, its not that unreasonable. I have a 5 year hosting plan and it wasn't to bad.

Ruby isn't *terrible* as a web language, but that's all i'd use it for.


Sax, there are a ****ton of examples of good random number generators. Just google 'true random number script <language name>'

Posts: 1992 | From: Beaverton, OR | Registered: Apr 2002  |  IP: Logged
$FISTofFATE$
Obsessive Member
Member # 918

Member Rated:
3
Icon 1 posted      Profile for $FISTofFATE$   Email $FISTofFATE$   Send New Private Message       Edit/Delete Post 
Sax I would just stick all the quotes you want into one large array and then use a random number generator to pick a number at random from the length of the array minus one and you are good.

It is easy to attain this with ASP.NET

--------------------
Retired Lord of Internal Affairs
Lords of Destruction

Posts: 6363 | From: Toronto, Ontario | Registered: May 2001  |  IP: Logged
Deadly Headshot
Compulsive Member
Member # 6098

Member Rated:
4
Icon 1 posted      Profile for Deadly Headshot   Author's Homepage   Email Deadly Headshot   Send New Private Message       Edit/Delete Post 
Random Number Generators are never truly random. However, you can improve the algorithm by factoring in other variables, such as the time, etc...

--------------------
"Carpé Agnus"
{GS} Pwnz
I like reviving old topics, it makes me feel needed...

{GS} is dead, sorry.

I've finally changed my av!

Posts: 2885 | From: Great Britain | Registered: Apr 2005  |  IP: Logged
$FISTofFATE$
Obsessive Member
Member # 918

Member Rated:
3
Icon 1 posted      Profile for $FISTofFATE$   Email $FISTofFATE$   Send New Private Message       Edit/Delete Post 
While it is true that a random number generator is not fully random (a comptuer system based on pure bit logic cannot behave in a random way), the use of a random number generator is perfectly acceptable in this case.

--------------------
Retired Lord of Internal Affairs
Lords of Destruction

Posts: 6363 | From: Toronto, Ontario | Registered: May 2001  |  IP: Logged
Flamelord
Compulsive Member
Member # 4491

Member Rated:
4
Icon 1 posted      Profile for Flamelord   Author's Homepage   Email Flamelord   Send New Private Message       Edit/Delete Post 
Well, switch-case-default will work better than if-else. Why don't you just ask for a string to compute, then go through it in order of operations, parsing and computing it?

--------------------
Constantinople! We shall never forget your zany barge poles!

Posts: 4232 | From: Gresham, OR | Registered: Nov 2003  |  IP: Logged
Shon-dahre
Moderator
Member # 650

Member Rated:
4
Icon 1 posted      Profile for Shon-dahre     Send New Private Message       Edit/Delete Post 
It took me AGES to work Pointers out, and then the C++ Builder Complete Reference explained them in a way I understand:

int *i;

Creates a Pointer variable.

int j;

Creates a regular integer.

You can then use the pointer to 'point' to the same MEMORY ADDRESS as the regular variable, using the & symbol:

i = &j;

So if the memory address of j is 3333, then you've just assigned 3333 to i.

The & symbol is the operator used to give the memory address, the * symbol is used to return the value at the given memory address.

So you can then do the following:

j = 10;

cout << *i << endl;

j = 22;

cout << *i << endl;

And the output would be:

10
22

As you are telling the compiler to output the information from a certain location in memory, rather than outputting the value of 'i' itself.


If I've made any mistakes with the usage of the pointer operators, anyone is free to correct me! I'm pretty sure I got it right though, I've done barely any programming for a couple of months.


Oh, and for your purpose of getting a string, checking for two numbers and a math operator, you'd want to use the strcmp and strcat statements on three char* strings.

I'm not going to post a code sample because I'll probably get it wrong [Razz] but that should point you in the right direction.

Posts: 12690 | From: Australia | Registered: Jan 2001  |  IP: Logged
Injury
Healthy Member
Member # 8063

Member Rated:
5
Icon 1 posted      Profile for Injury   Author's Homepage   Email Injury   Send New Private Message       Edit/Delete Post 
I've always though pointers are a better way to reference single digit variables from multiple digit strings, arrays, integers, etc...

idk.

--------------------
Visit http://fuller-dev.com/wos/ for guides, skins and worlds. Enjoy!

Posts: 198 | Registered: Feb 2008  |  IP: Logged
Flamelord
Compulsive Member
Member # 4491

Member Rated:
4
Icon 1 posted      Profile for Flamelord   Author's Homepage   Email Flamelord   Send New Private Message       Edit/Delete Post 
Yes, pointers are quite useful in that regard. Single characters, the whole string from a certain point onward, etc.

--------------------
Constantinople! We shall never forget your zany barge poles!

Posts: 4232 | From: Gresham, OR | Registered: Nov 2003  |  IP: Logged
Shon-dahre
Moderator
Member # 650

Member Rated:
4
Icon 1 posted      Profile for Shon-dahre     Send New Private Message       Edit/Delete Post 
Pointers are also used in functions, such as the following:

code:
void Subtract (int &i, int &j)
{
if (i > j) i -= j;
else j -= i;
}

int main (...)
{
int k = 20;
int m = 10;
cout << k << " " << m << endl << endl;
Subtract(k, m);
cout << k << " " << m << endl << endl;
m = 40;
Subtract(k, m);
cout << k << " " << m << endl;
}

The output would be:
20 10

10 10

10 30


Because when you declare a function with arguments using the & symbol, you're telling it to point to the memory address of the supplied argument. So in this case, instead of creating a variable to be modified by the function, you are instead using a pointer to modify a local variable in a different function.

If you were to declare the function without using the & symbol for the arguments, then you'd simply be assigning values to i and j themselves, rather than using i and j to manipulate k and m.

Once I learned how to use Pointers correctly and I started looking for more examples, I began to realise exactly how useful they are [Wink] .

Posts: 12690 | From: Australia | Registered: Jan 2001  |  IP: Logged
Setaru the man
Member
Member # 8169

Rate Member
Icon 1 posted      Profile for Setaru the man   Email Setaru the man   Send New Private Message       Edit/Delete Post 
Funny. I thought referencing in a function was used to actually replace the values and not just to make copies of them. idk...

--------------------
Constantinople! We shall never forget your zany barge poles!

Posts: 18 | From: Gresham,OR | Registered: Sep 2008  |  IP: Logged
Shon-dahre
Moderator
Member # 650

Member Rated:
4
Icon 1 posted      Profile for Shon-dahre     Send New Private Message       Edit/Delete Post 
That's exactly what I was doing.

In my example, &i points to the memory address of k, and &j points the memory address of m. You're just manipulating the information stored at those addresses. There's no copying involved.

Posts: 12690 | From: Australia | Registered: Jan 2001  |  IP: Logged
Tito Wario
Member
Member # 6946

Member Rated:
5
Icon 1 posted      Profile for Tito Wario   Author's Homepage   Email Tito Wario   Send New Private Message       Edit/Delete Post 
code:
 do {
printf("=> Choose an option: USE [1] DEL [2] EXT [3] \n");
scanf("%d",&opution);
if (opution == 1){ LOADKYARA();}
else if (opution == 2){ DELKYARA(); }
else if (opution == 3){
printf("Logging off... \n");
return 0;
}
} while(opution>=4 || opution<=0);
} // End Login Module

See, this is the first and only use i found for pointers. ^^;
Posts: 42 | Registered: Apr 2006  |  IP: Logged
samsyn
Administrator
Member # 162

Member Rated:
4
Icon 1 posted      Profile for samsyn   Author's Homepage   Email samsyn   Send New Private Message       Edit/Delete Post 
Pointers are the best thing in the world, and it is impossible to live without them.

They are also a major source of program bugs since they can easily be left uninitialized, or pointing no-longer-mapped memory, leading to segfaults.

Many languages try to 'protect' the programmer from making this sort of mistake, but often end up just tying your hands. And in the end, you really can't live without pointers, hence the creation of things with different names (references) with the same basic problems.

Now, a language like Lua at least takes references seriously. A reference/pointer is trult refcounted, and the memory it points to CAN NOT be released until all references to it have been deleted. This guarantees you can't segfault using a pointer which was at least valid once upon a time (but doesn't protect you from corrupted references). But it also leads to loose control of your memory, leading to bloat, leaks, fragmentation, and periodic garbage collection pauses.

But as far as I can see in c++, the switch from a pointer to a reference is just 'where do you prefer to see the ampersand'

code:
fooByPointer( int *a )
{
*a = *a + 1;
}

fooByReference( int &a )
{
a = a + 1;
}

fooNoChange( int a )
{
a = a + 1; // only a local copy of a is changed.
}

foo()
{
int num = 3;

fooByReference( num ); // on return num is 4
fooByPointer( &num ); // on return num is 5
fooNoChange( num ); // on return num is still 5
}

I have an unproven suspicion that using the reference version adds some overhead, without actually protecting you. But I have a pro-pointer bias. I mean when it's justified.

One trend in OOP is to not pass arguments when you don't have to, and just use member variables. Of course, you're still trusting a pointer to the object itself, and if some part of your code deletes the object and then you make another call into it, it will likely segfault as soon as it touches member data.

This can be confusing since you can do something like this:

class foo {
int sum( int a, int b );
};

main
{
foo * myFoo = new foo;
int sum1 = myFoo->sum( 4, 5 );
delete myFoo;
int sum2 = foo:sum( 5, 6 ); // still works
int sum3 = myFoo->sum( 7, 8 ); // probably segfaults
}

The trick there is that the 'code' never goes away. When you 'new' up an object, you are not making an extra copy of the code, you are just allocating space to the classes member data (none in this example) and probably a virtual function table (which allows virtual methods to be overridden). In theory the compiler COULD be set up, maybe only for static objects such that you could even still use the myFoo pointer to call a function which didn't touch member data, but usually that would fail, thanks to the need for the virtual function 'jump table'

Anyway, objects being used after they are deleted is pretty insidious, and even more likely if you ever implement functions like

foo::deleteMe()
{
delete this;
}

And then call that from inside a method in the same class. basically you have just signed the death warrant for anyone with a pointer to that particular foo object. They have no way to know you deleted yourself, and you have no way to tell them unless you force them to register as a user of you (and then call back to all users just before you delete yourself)

Threads pose a similar, but worse, problem if the thread destroys itself.

The paradigm I like is "only the guy who 'new's something, should 'delete' it" And it is his responsibility to inform anyone else he shared the pointer with, and he should share pointers as little as possible.

Now, if you have some object that has to live the lifetime of the program, well, I admit I am not a purist and am willing to even make some nasty global pointer available to everyone (so long as it is initialized to NULL, and returned to NULL if the main program deletes the object and every user of the pointer checks for NULL before using the pointer.. every time. But only for main systems like "the sound system" and not for transient objects like "this particular sound effect"

And I accept that is poor practice, and it burns you if you ever get to the point where suddenly you realize you might like to have two such managers around. Big plumbing change required. So you have to decide which shortcuts you allow yourself, based on what you think the future may hold.

Thanks to my day job, I have become a big fan of 'interfaces' and multiple inheritance (of interfaces only) and a means to discover whether an object implements the interface of interest. So if an object has a behaviour, it offers the interface for that behaviour, otherwise it does not. Behaviours might be "can be picked up", "can be eaten", "can be sold"

So an 'apple' exposes all three, and a 'car' only exposes the last one. But if you 'can be sold' then the 'can be sold' interface has all methods associated with that, and you must implement them all.

Then all objects can have a common interface that they all inherit from "is an object" so as you call functions on objects you are always passing that pointer around, but deep into some algorithm with a pointer to an object you can 'ask' that pointer "does your object offer a 'can be eaten' interface" without having to know anything else about the object

code:
fooSomeFunction( Object* obj)
{
// I wonder if i should put up a menu selection
// by which you could take a bite out
// of this object?
// Does it have the 'eatable' interface?
InterfaceEatable *pEatable = object->eatableIf();
if( pEatable ) {
// It has an eatable interface
int bitesLeft = pEatable->numBitesLeft();
bool rotten = pEatable->isRotten();
if( bitesLeft > 0 && !rotten ) {
// Looks juicy!
AddMenuSelection( "Take a bite of me" );
}
}
else
{
// nope, cannot be eaten, no menu needed
}
}

How you actually query an object pointer to see if it has an interface is up to you. Windows has its own QueryInterface metaphor which I resisted for years (and we implemented our own system at work -- we have to work in windows and linux). Anyway, I like the metaphor, when I am making some new sort of object, that I just have it offer a handful of interfaces that describe what sorts of relationships the user may have with the object. Plus you make aome helper classes which make the actual implementation of interfaces not have much code duplication. Everyone wins. The code also ends up kinda readable, and broken into nice digestible chunks.

So long as you come up with good names, which is hard for me. For example, in Philbert's example (I paraphrase):

Calculator compute; // 'compute' is an 'instance' of class 'Calculator'

Adding the word 'compute' to the mix just takes you away from the original focus. This is the sort of thing I do all the time and regret. I think it's better to stay closer like

Calculator calc;

or

Calculator myCalculator

or, if I were any good at it, I could give a good example. [Smile] I just know that when I rename things at every level of the chain, pretty soon you get down deep and no longer are sure what anything is any more.

Coming up with clear and accurate (and short, and unique, and metaphorically satisfying) names for things is an amazing programming skill to have, and a real struggle for me, even now, though I think I have gotten a *little* better at it through the years.

For example, here is today's failure:

code:
   local result, modifiedPayload = RC_ActionTargetsSpawn( mySerNum, clientStarId, 
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex,
payload )

In this case, the rocket club 'action' system (think 'spell') works like this:

1.) you bind an action to a button or hotkey (you have some number of actions you 'know' and 'learn')

2.) the creator (world dev) of that action has written an action script (in lua) which provides a handful of stock functions, in addition to anything special it does.

3.) you click on a target (in RC, objects are identified by their dna which is the combination of two numbers: starId (unique id of the star system where the object was born/manufactured, and a unique Index value (handed out by that star system and unique only within that star system)

4.) the code calls a function in the CLIENT copy of the action script.

code:
function RC_OnActionButton( 
step, mySerNum, clientStarId,
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex )

The action designer considers the arguments, including the ability to query for more information about the objects (like the level and stats of the character... some of which is only available in detail on the client)

5.) If the CLIENT is happy with the action (it can abort it, if it feels it is inappropriate - like you don't have enough mana, for example), but otherwise, it returns a payload value which is 'opaque' to the c++ code. But the c++ code then sends that payload to the SERVER, which invokes a a function in the SERVER copy of the SAME action script

code:
function RC_OnActionRequest( 
step, mySerNum, clientStarId,
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex,
payload )

Here the SERVER gets a chance to decide whether the action is appropriate. This happens pretty much instantly (well, plus transmission lag) to your clicking the action button. The SERVER uses the arguments passed to it (including the payload) and its local access to stats information to basically completely decide what the outcome will be. Something like "Target will lose 124 hit points, and be paralyzed for 10 seconds"

The server builds a new payload which describes this outcome and returns it to c++ who sends another packet, but this time to 'all clients who care' (not just back to you, the initiator of the action, because there are perhaps bystander witnesses who need to animate and see the result of the action. And maybe the action is an area effect which will affect multiple targets)

6.) So now, some handful of clients all receive their copy of the payload from the server and they pass that back into the CLIENT copies of the action script via this call:

code:
function RC_OnActionResult( 
step, mySerNum, clientStarId,
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex,
payload )

Each client then uses these arguments and the payload and MAYBE their own access to stats (though it's really too late for that) to decide how to implement/animate this action. But being an event driven system, they can't just do the whole thing inside this call because that would freeze the program. We must never freeze the program!

So instead, we modify the payload as we see fit (remember, the payload itself is completely read and written by the action script itself. the c++ code is just passing it around between client and server and clients. c++ has no idea what the payload means/represents.) In theory, two separate action scripts could use entirely different payload syntax. But I suspect some standards will emerge just to allow the use of some helper functions for decoding/encoding payloads. My payloads are things like "thp=100,smp=23,tfz=12" (take away 100 hit points from the target. take away 23 mana from the source/caster, freeze the target for 12 seconds) Name/Value pairs seem the most flexible/easiest way to handle complex interactions. Note that only 'variable' stuff goes in the payload. The action script should 'bake in' anything which is alsywa the same in every use of the action (say, the particle system used to animate the weapon effect).

7.) Anyway, so the client has to return from that function right away, to avoid blocking the program. So the c++ then calls this function repeatedly:

code:
function RC_OnActionStep( 
step, mySerNum, clientStarId,
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex,
payload, stepNum, handle )

This is called on the CLIENT action script (on all clients who 'care') About 10x a second with an increasing step number (goes up by one each time the function is called)

The client script then looks at the step number and notices important moments in time like "right away I should start the particles, but about a second later I should make a little explosion, and 2 seconds after that I should apply the damage to the target and debit the mana from the caster, and ... etc."

So on each call it checks the step number, and only does a little bit of work and returns. The value it returns controls when the action is complete (afte which it is no longer stepped)

ANYWAY, I bring all that up, because today I added a notification FROM the 'action script' TO the 'hive script' (a completely separate script which is running the AI of the object/npc/monster). Basically I needed a function which indicates "hey, mister HAVE script, I, an ACTION script, have just beed targeted on one of your SPAWN."

This is used so, for example, if I am standing off some distance from the spawn of the hive, far enough away that they don't 'see' me (they don't consider me a 'threat') and then I shoot one of them.. this allows the hive to set my threat level, even though I am otherwise 'too far away to matter' by raising my threat level for some period of time (all under the control of the hive script). And presumably the hive AI will then tell that spawn that I am the biggest threat and it should run up and attack me.

Otherwise, I could just stand off and shoot from a distance without any fear, and what fun is that? [Smile]

Anyway, so.. the ACTION script (on the server) calls this function (calls into C++)


code:
   local result, modifiedPayload = RC_ActionTargetsSpawn( mySerNum, clientStarId, 
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex,
payload )

It can't just directly call into the hive script for a number of reasons, one being that scripts, like all assets, are dynamically shared between the players and you may not even have a copy of the hive script yet. Plus at this level the action script doesn't even know if the target is hive spawn (might be just another human, and no script is needed there.. well, as I type that)

Anyway, the C++ receives this call, looks at the targetDna and checks if it is valid, and if it is hive spawn, and if so it then finds the hive object for that spawn (a separate bit of dna -- the hive runs the AI script for all its spawn - which allows it to coordinate multiple spawn, for example, or play out little scripted dialogs between spawn, like a WoS npc conversation in front of you)

Once the c++ has figured out, fetched assets, etc, it invokes this function in the SERVER HIVE script.


code:
function RC_OnActionTargetsSpawn( spawnIndex, 
casterSerNum, casterStarId,
actionSerNum, actionIndex,
srcStarId, srcStarIndex,
tgtStarId, tgtStarIndex,
payload )

Same name, but with 'On' in it. Basically if the function has 'On' in it, the the c++ calls that function in response to an event, and there are relatively few of these.

RC_ without the 'on' means the lua script is calling into c++ to perform some service on its behalf (play a sound, change a stat value, test the state of a token, start an animation, etc.)

All functions, c++ or lua, return right away and do NOT spin wheels waiting for time to go by.

OK, well, that was a nice long unnecessary post.

[Smile]

--------------------
He knows when you are sleeping.

Posts: 10689 | From: California | Registered: Dec 1998  |  IP: Logged
Flamelord
Compulsive Member
Member # 4491

Member Rated:
4
Icon 1 posted      Profile for Flamelord   Author's Homepage   Email Flamelord   Send New Private Message       Edit/Delete Post 
Thanks for the nice long post, Dan! I liked the peek into RC's guts. [Big Grin]

--------------------
Constantinople! We shall never forget your zany barge poles!

Posts: 4232 | From: Gresham, OR | Registered: Nov 2003  |  IP: Logged
Flamelord
Compulsive Member
Member # 4491

Member Rated:
4
Icon 1 posted      Profile for Flamelord   Author's Homepage   Email Flamelord   Send New Private Message       Edit/Delete Post 
Started playing around with the idea of a command-line calculator app, and this is the string-evaluation function I came up with. It's got holes still (and a whole level of the OoO missing), but it's mostly functional.

code:
 void evaluate (char* expression)
{
char first [256] = "";
char last [256] = "";
char result [256] = "";
if (strchr(expression, '(') != NULL || strchr(expression, ')') != NULL) {
int oparray [32]; //Open Parentheses
int cparray [32]; //Close Parentheses
int opcount = 0;
int cpcount = 0;
for (int i=0; i<=strlen(expression); i++) {
switch (expression[i]) {
case '(':
opcount++;
oparray[opcount] = i;
break;
case ')':
cpcount++;
cparray[cpcount] = i;
break;
}
}
if (opcount < cpcount) {
printf("Error; unexpected close parenthesis!\n"); expression = "(null)"; return;
} else if (opcount > cpcount) {
printf("Error; missing close parenthesis!\n"); expression = "(null)"; return;
} else if (oparray[opcount] > cparray[cpcount] || oparray[1] > cparray[1]) {
printf("Error; mismatched parentheses!"); expression = "(null)"; return;
} else {
for (int i=opcount; i>0; i--) {
char inputstring [256];
char outputstring [256];
strncpy(inputstring, &expression[oparray[i]+1], strcspn(&expression[oparray[i]+1], ")"));
inputstring[strcspn(&expression[oparray[i]+1], ")")] = '\0';
memset(first, 0, 256);
strncpy(first, expression, oparray[i]);
strcpy(last, &expression[oparray[i]+strcspn(&expression[oparray[i]+1], ")")+2]); //+2 gets us past both parentheses. Weird stuff.
evaluate(inputstring);
memcpy(expression, first, 255);
strcat(expression, inputstring);
strcat(expression, last);
}
}
}
if (strchr(expression, '{') != NULL || strchr(expression, '}') != NULL || strchr(expression, '^') != NULL) {
printf("No support for this function yet!\n");
return;
}
if (strchr(expression, '*') != NULL || strchr(expression, '/') != NULL) {
int j = strlen(expression);
int expindex = 0; //the index to read the first argument from
int symbindex = 0; //the index of the symbol
double a = 0; //first value
double b = 0; //second value
char* lastptr;
for (int i = 0; i<=strlen(expression); i++) {
switch (expression[i]) {
case '*':
memset(first, 0, 256);
memset(result, 0, 256);
memset(last, 0, 256);
symbindex = i;
a = atof(&expression[expindex]);
b = strtod(&expression[symbindex+1], &lastptr);
a *= b;
i++;
strncpy(first, expression, expindex);
memcpy(last, lastptr, 255);
memcpy(expression, first, 255);
sprintf(result, "%f", a);
strcat(expression, result);
strcat(expression, last);
expression[strlen(expression)] = '\0';
j = strlen(expression);
i = strlen(first) + strlen(result) - 1;
expindex = strlen(first);
break;
case '/':
memset(first, 0, 256);
memset(result, 0, 256);
memset(last, 0, 256);
symbindex = i;
a = atof(&expression[expindex]);
b = strtod(&expression[symbindex+1], &lastptr);
a /= b;
i++;
strncpy(first, expression, expindex);
memcpy(last, lastptr, 255);
memcpy(expression, first, 255);
sprintf(result, "%f", a);
strcat(expression, result);
strcat(expression, last);
expression[strlen(expression)] = '\0';
j = strlen(expression);
i = strlen(first) + strlen(result) - 1;
expindex = strlen(first);
break;
case '+':
case '-':
expindex = i; expindex++;
break;
default:
break;
}
}
}
if (strchr(expression, '*') != NULL || strchr(expression, '/') != NULL) {
int j = strlen(expression);
int expindex = 0; //the index to read the first argument from
int symbindex = 0; //the index of the symbol
double a = 0; //first value
double b = 0; //second value
char* lastptr;
for (int i = 0; i<=strlen(expression); i++) {
switch (expression[i]) {
case '+':
memset(first, 0, 256);
memset(result, 0, 256);
memset(last, 0, 256);
symbindex = i;
a = atof(&expression[expindex]);
b = strtod(&expression[symbindex+1], &lastptr);
a += b;
i++;
strncpy(first, expression, expindex);
memcpy(last, lastptr, 255);
memcpy(expression, first, 255);
sprintf(result, "%f", a);
strcat(expression, result);
strcat(expression, last);
expression[strlen(expression)] = '\0';
j = strlen(expression);
i = strlen(first) + strlen(result) - 1;
expindex = strlen(first);
break;
case '-':
memset(first, 0, 256);
memset(result, 0, 256);
memset(last, 0, 256);
symbindex = i;
a = atof(&expression[expindex]);
b = strtod(&expression[symbindex+1], &lastptr);
a -= b;
i++;
strncpy(first, expression, expindex);
memcpy(last, lastptr, 255);
memcpy(expression, first, 255);
sprintf(result, "%f", a);
strcat(expression, result);
strcat(expression, last);
expression[strlen(expression)] = '\0';
j = strlen(expression);
i = strlen(first) + strlen(result) - 1;
expindex = strlen(first);
break;
default:
break;
}
}
}
return;
}



--------------------
Constantinople! We shall never forget your zany barge poles!

Posts: 4232 | From: Gresham, OR | Registered: Nov 2003  |  IP: Logged
samsyn
Administrator
Member # 162

Member Rated:
4
Icon 1 posted      Profile for samsyn   Author's Homepage   Email samsyn   Send New Private Message       Edit/Delete Post 
Sheesh, I am so easily distracted. here's my quicky attempt at a 'calculator string to answer'

input "3.2 * 4 + (5 / 6 + 2.3)/(3/(3-2))"
output: 13.844

I think it's reasonably robust (in the non-crashing sense) but won't give nice answers if you poorly format the input. Also it won't handle the unary '-'

The more clever thing to do, I think, would be to parse the string into tokens as I did, then re-order the tokens into more of a reverse polish form, and then use a classical stack computation.

My precedence control is kind of silly, but I believe it works (* and / are done before + and -, in the absence of explicit parens to the contrary. Work is otherwise done left to right.)

To use this, call the function

float result = Evaluate( "1 + 2/4*4+(12/5)" );

it will parse the string into terms and make a list of them, then call RecursiveEvaluation on the full list.

RecursiveEvaluation scans the list looking for parentheses and calls itself (recursively) as needed to handle contents of parens

Eventually it is called on a sublist with no parens at all, which it evaluates by calling ReduceTerms 4 times (once for each operator, in the precedence order of the operators). ReduceTerms scans the sublist for the specified operator, performs it and shortens the list. So given a list like

"1 + 2 + 3 + 4"

it will reduce it like this

"3 + 3 + 4"

"6 + 4"

"10"

IN a more realistic example, it only reduces the specified operation so

"1 + 2 * 3 + 9 - 3 * 4" with '*' selected would reduce to:

"1 + 6 + 9 - 12"

then on the '+' pass, that would reduce

"7 + 9 - 12"

"16 - 12"

then n the '-' pass that would reduce

"4"

Anyway, so the code appears in inverse order of calling, so I don't have to forward declare anything, but probably less confusing to read it backwards.

code:
// eval.cpp

#include "stdafx.h"

//
// OK, at least once in my life I have to write a simple expression evaluator
//
// IN: ( ( a * b ) + c ) / ( d / (e * f + g ) - 4 )
// (only no letters, just numbers
// out: the value
//
// The theory, there are keyword operators
//
// * / + - (evaluated with that precedence)
//
// and parens
//
// 1.) scan the input and turn it into a list of eTerms
// an eTerm is either a number (a float) or an operator or a paren
// We scan to convert number strings to floats and detect keywords and remove syntax sugar
//
// 2.) then we process the resuling list if eTerms
//
// 3.) when we have an expression fragment with 'no inner parens' we evaluate it, turning
// it into a simple number passed to the level above
//
// 4.) but if there is a (, we scan for the matching ) [watch out for nesting] and recursively evaluate that before continuing.

// Everything eventually resolves to a single floating point value.

//
// It's all about the data structure. I will make
// a list of these out of whatever you give me
//
//
enum eTermType {
eTermTypeVoid = 0,
eTermTypeNumber = 1,
eTermTypeOperator = 2,
eTermTypeLeftParen = 3,
eTermTypeRightParen = 4,
};

typedef struct E_TERM_ {
eTermType type;
char operatorId; // '*' '/' '+' '-' if it is an operator
float val; // if a number, this is the value
} E_TERM;

#define MAX_E_TERMS ( 100 )

//
//
///////////////////
// ReduceTerms is always given a simple list (no parens) and a single operation to apply as found, left to right, in the list.
//
//

int ReduceTerms( char operatorId, E_TERM * evalList, int numTerms )
{
// this scans the list from left to right
// looks for only the specified operatorId
// applies it to args to left and right of operator
// shrinks the list by 2 (replacing first arg with result, removing operator and second arg)
// We know this is only called on 'simple lists' with no parens
int i;
for( i=1; i<numTerms; i++ )
{
if( evalList[ i ].type == eTermTypeOperator
&& evalList[ i ].operatorId == operatorId
)
{
// this is the operator we're looking for
float arg1 = evalList[ i-1 ].val;
float arg2 = evalList[ i+1 ].val; // yes, I should check for actual term, but I know I won't segfault
// and I didn't promise accuracy, given garbage in
float result = 0;
switch( operatorId ) {
case '*':
result = arg1 * arg2;
break;
case '/':
if( arg2 == 0 ) {
result = arg1; // you're screwed no matter what, but no sense crashing the program
} else {
result = arg1 / arg2; // I didn't promise to protect you from dividing by zero, but I will
}
break;
case '+':
result = arg1 + arg2;
break;
case '-':
result = arg1 - arg2;
break;
}
// ok, now collapse the list to remove this operation
evalList[ i-1 ].val = result;
int j;
for( j=i; j<numTerms-1; j++ )
{
evalList[ j ] = evalList[ j + 2 ];
}
numTerms -= 2;
i--; // oddly enough, we need to reevalute from this spot to catch strings of matching opers "1 + 1 + 1 + 1"
}
}

return numTerms;
}

//
//
//
///////////////////
// RecursiveEvaluation
//
// This is also given a sublist, but it may contain parens, which it will scan for, make a sublist of all the tokens 'inside the outermost parens' and recurse into itself
// Eventually it will get sublists with no parens which it can pass to ReduceTerms()
//
//

float RecursiveEvaluation( E_TERM * evalList, int first, int lastPlusOne )
{
// evaluate the terms left to right, using precedence rules
// () * / + -

// so I think the way I will do this is by copying the terms
// to the stack, and in this way I can convert an entire paren
// section to a single numeric value
E_TERM myEvalList[ MAX_E_TERMS ]; // stack could get pretty deep here, I am not claiming cleverness
int numTerms = 0;
int i;
for( i=first; i<lastPlusOne; i++ )
{
E_TERM *pEin = &(evalList[ i ]); // next input term

if( pEin->type == eTermTypeLeftParen ) {
// find the matching right paren, and then recurse to
// evaluate the paren'd structure and return it as a simple number
if( i < lastPlusOne - 1 ) // ignore an open paren at the end of the list
{
E_TERM *pEout = &(myEvalList[ numTerms++ ]); // allocate a symbol on my local list
int j;
int depth = 0; // must deal with nested parens
bool foundMatch = false;
int rightParenIndex = lastPlusOne; // if we don't FIND a matching eright paren, assume it is at end
for( j=i+1; !foundMatch && j<lastPlusOne; j++ )
{
switch( evalList[ j ].type )
{
case eTermTypeLeftParen:
// uh oh, went deeper
depth++;
break;
case eTermTypeRightParen:
if( depth == 0 ) {
// this is the one we're looking for!
foundMatch = true;
rightParenIndex = j;
break;
}
depth--;
break;
}
}
pEout->type = eTermTypeNumber;
pEout->operatorId = 0;
// this routine wants one past the index to be evaluated, so pointing to right paren itself is correct
pEout->val = RecursiveEvaluation( evalList, i+1, rightParenIndex );

// adjust i to pick up after the right paren
i = rightParenIndex; // i will be incremented before we next use it
}
} else {
// just copy it across
E_TERM *pEout = &(myEvalList[ numTerms++ ]); // allocate a symbol on my local list
*pEout = *pEin;
}
}

// now evaluate what I have in my own list, which is guaranteed to
// have no parens and just need precedence * / + -
// And maybe something special for unary -

// So..

float result = 0;

// Well, this is not very elegant, but I just call my little helper for each precedence
numTerms = ReduceTerms( '*', myEvalList, numTerms );
numTerms = ReduceTerms( '/', myEvalList, numTerms );
numTerms = ReduceTerms( '+', myEvalList, numTerms );
numTerms = ReduceTerms( '-', myEvalList, numTerms );
if( numTerms > 0 ) {
// should be exactly one, unless they got clever and gave us no numbers at all
result = myEvalList[ 0 ].val;
}
return result;
}



//
//
//
///////////////////////////
// Evaluate( char * )
//
// Give me something like "34.5 / (1 + 23.4) - 45.6"
// and I will give you the answer.
//
// I will first parse your input string into a list (an array) of eTerm structures. And then I
// will recursively scan that list.
//

float Evaluate( const char *in )
{
// first we 'lexically parse it' into a list of terms
E_TERM myEvalList[ MAX_E_TERMS ];
int numTerms = 0;
float result = 0;
numTerms = 0;
do
{
E_TERM *pE = NULL;
char numBuff[ 100 ] = "";
int numOff = 0;
while( *in && *in <= ' ' ) {
// skip leading white space
in++;
}
switch( *in )
{
case '*': // multiply
case '/': // divide
case '+': // add
case '-': // subtract
pE = &(myEvalList[ numTerms++ ]); // allocate a symbo
pE->type = eTermTypeOperator;
pE->operatorId = *in++;
pE->val = 0.0f;
break;
case '(': // these could be operators, but it feels like it will simplify life later to have a type
pE = &(myEvalList[ numTerms++ ]); // allocate a symbo
pE->type = eTermTypeLeftParen;
pE->operatorId = *in++;
pE->val = 0.0f;
break;
case ')': //
pE = &(myEvalList[ numTerms++ ]); // allocate a symbo
pE->type = eTermTypeRightParen;
pE->operatorId = *in++;
pE->val = 0.0f;
break;
default: // number or symbol, and since we don't allow symbols, it's a number!
// at this point it simply should be a number, which
// means 0-9 and decimal point only. so we grab nothing but those

while( *in // not yet at end of string
&& (numOff < 100) // not yet overflowing number
&& ( *in == '.' // decimal point
|| (*in >= '0' && *in <= '9') // or decimal digit
)
)
{
numBuff[ numOff++ ] = *in++;
numBuff[ numOff ] = 0;
}
if( numOff > 0 ) {
pE = &(myEvalList[ numTerms++ ]); // allocate a symbol
pE->type = eTermTypeNumber;
pE->operatorId = 0;
pE->val = (float) atof( numBuff );
} else {
// we got some junk, throw it away
if( *in ) {
in++;
}
}
break;
}
} while ( *in && (numTerms < MAX_E_TERMS) );

// OK, we have 'parsed' the input into a nice list, now we just evaluate the terms
// recursively

result = RecursiveEvaluation( myEvalList, 0, numTerms ); // evaluate the entire list

return result;
}



[ 12-31-2008, 11:20 AM: Message edited by: samsyn ]

--------------------
He knows when you are sleeping.

Posts: 10689 | From: California | Registered: Dec 1998  |  IP: Logged
sigma
unregistered


Icon 1 posted            Edit/Delete Post 
Well i dunno what all dis programming is for but i'll put in some old basic [Razz]


1 cls
2 print " is sigma the best "
3 input y$
4 cls
5 if y$= "yes" goto 69
6 if y$= "no" goto 666
7 rem this is as easy as it gets
69 rem i love this number: cls: print "That's right!"
70 goto 69
666 print " Your just jealous!"
667 goto 666

IP: Logged
Deadly Headshot
Compulsive Member
Member # 6098

Member Rated:
4
Icon 1 posted      Profile for Deadly Headshot   Author's Homepage   Email Deadly Headshot   Send New Private Message       Edit/Delete Post 
Not good practise to number in 1s...

I originally learned programming in BBC BASIC.

--------------------
"Carpé Agnus"
{GS} Pwnz
I like reviving old topics, it makes me feel needed...

{GS} is dead, sorry.

I've finally changed my av!

Posts: 2885 | From: Great Britain | Registered: Apr 2005  |  IP: Logged
Beoghztt
Compulsive Member
Member # 2888

Member Rated:
4
Icon 1 posted      Profile for Beoghztt   Author's Homepage   Email Beoghztt   Send New Private Message       Edit/Delete Post 
Double accounts?

--------------------
This signature contains a lot of foul languages.
                                                                                   Also racism.

Posts: 2227 | From: Yokohama, Japan | Registered: Oct 2002  |  IP: Logged
Deadly Headshot
Compulsive Member
Member # 6098

Member Rated:
4
Icon 1 posted      Profile for Deadly Headshot   Author's Homepage   Email Deadly Headshot   Send New Private Message       Edit/Delete Post 
... Who argue with each other on neg energy?

--------------------
"Carpé Agnus"
{GS} Pwnz
I like reviving old topics, it makes me feel needed...

{GS} is dead, sorry.

I've finally changed my av!

Posts: 2885 | From: Great Britain | Registered: Apr 2005  |  IP: Logged
Can Not
Obsessive Member
Member # 2518

Member Rated:
3
Icon 1 posted      Profile for Can Not   Email Can Not   Send New Private Message       Edit/Delete Post 
The original link in this thread is dead :[

--------------------
BBQSANDGANG - Elite Senator

Posts: 6399 | From: Haxe | Registered: Jul 2002  |  IP: Logged
Can Not
Obsessive Member
Member # 2518

Member Rated:
3
Icon 1 posted      Profile for Can Not   Email Can Not   Send New Private Message       Edit/Delete Post 
reup plz

--------------------
BBQSANDGANG - Elite Senator

Posts: 6399 | From: Haxe | Registered: Jul 2002  |  IP: Logged
  This topic comprises 2 pages: 1  2   

Quick Reply
Message:

HTML is not enabled.
UBB Code™ is enabled.

Instant Graemlins
   


Post New Topic  New Poll  Post A Reply Close Topic   Unfeature Topic   Move Topic   Delete Topic next oldest topic   next newest topic
 - Printer-friendly view of this topic
Hop To:


Contact Us | Synthetic Reality

Copyright 2003 (c) Synthetic Reality Co.

Powered by UBB.classic™ 6.7.3