Teen Programmers Unite  
 

 

Return to forum top

C++ word recognition

Posted by damert [send private reply] at November 16, 2001, 05:11:05 PM

I'm trying to make a simple, text-based game [in C++] that will have a user input a two- or three-word command, and the player will do actions accordingly. Example: I say "read book", and the computer says "You read the book." Etc.

My problem is I have no idea how to get the computer to recognise a word. I have something now which doesn't work involving a cin.get(); used to input a character array, and then I check the characters individually. But I've digressed. How do I do word recognition in C++?

Thanks,
Mike

Posted by Cobbs [send private reply] at November 16, 2001, 11:25:42 PM

You can do something like this,

cin >> char_array1 >> char_array2;

The first word will go into char_array1, and then second into char_array2. I know that if that doesn't work, because I haven't used C++ in a while, there's scanf in stdio.h

Posted by Psion [send private reply] at November 17, 2001, 07:30:58 AM

damert, this is a VERY simple task that you should do without the aid of any built-in functions or classes, at least as an exercise to make sure you understand how C strings work. Obviously, it CAN be done by checking the characters individually. Once you know how to do that, you can use something like strtok() to save code.

Posted by Cobbs [send private reply] at November 17, 2001, 01:29:38 PM

Aren't you making a 3d game or something...?

Posted by damert [send private reply] at November 17, 2001, 03:34:29 PM

I was making a 3D game, and am still, to some degree. My problem is that my ambition far outstripped my programming skill, so I've not made much progress at all.

Psion, I'm sure this is very easy, but I asked here because I didn't know how to do it. I tried

cin >> char_array;

and then breaking up the array, but that doesn't work, because the array has spaces in it, which don't seem to work correctly. My program instead would find the first space and treat that as though it were the end of the string, which of course wasn't the case. If there were some way to get the program to realize what spaces are, then it would be VERY simple. How do I do that?

Posted by damert [send private reply] at November 17, 2001, 04:17:15 PM

SORRY!

I for some reason figured Cobbs's code wouldn't work (why?) and didn't try it until five minutes ago. It worked. Thanks a million++.

Posted by taubz [send private reply] at November 17, 2001, 04:36:54 PM

Stick to stdio.h, and don't use C++'s io. C++ only obfuscates good programming practice.

- taubz

Posted by damert [send private reply] at November 17, 2001, 06:30:29 PM

I apologize once more.

cin>> array1 >> array2;

requires 2 arrays. This is no good. Ironically enough, I'd had the correct answer all along, I was just doing something incredibly stupid and not catching it:

integer1 = function( argumentA );
integer1 = function( argumentB );

But thanks for the help, and for putting up with my stupid questions ;o)

Posted by lordaerom [send private reply] at November 18, 2001, 02:18:47 PM

If you say so, taubz!

Posted by taubz [send private reply] at November 18, 2001, 03:08:14 PM

Correction: C++'s iostream only obfuscates...

Posted by lordaerom [send private reply] at November 18, 2001, 08:58:54 PM

If you say so, taubz =]

Posted by unknown_lamer [send private reply] at December 01, 2001, 08:29:41 PM

why is everyone using char arrays? You should use C++ strings instead. They are ranged checked too. Example:

char[10] foo;
std::cin >> foo;

if i enter more than 9 characters, oops. The equiviland with strings:

#include <string>
#include <iostream>

std::string foo;
std::cin >> foo;

You can then make a map of strings and functions that will be called on a keyword. Example:

typedef void (*event_func) ();

std::map<std::string,event_func> game_keywords;

Now you can write a simple function to look up words and call the appropriate function:

void dispatch_event (std::string key) {

event_func temp = game_keywords.find (key);
if (temp)
temp ();

return;

}

that should work (except my typedef might be wrong, dunno, i've been coding a lot more common-lisp than C++ lately). Adding a key to the system is just as simple:

void add_game_function (std::string key, event_func func) {

game_keywords[key] = func;

}

You can even use that to replace functions with new functionality if you need to. Example:

void foo(); // handler when you enter "foo"
void new_foo(); // hypothetical new function

void change_event_foo () {

add_game_function ("foo", new_foo);

}

int main (int argc, char** argv) {

add_game_function ("foo", foo);
add_game_function ("bar", change_event_foo);

start_game();

return 0;

}

So, when you type "bar" it changes what happens when you enter "foo". Searching a large map is efficient because the C++ standard guarantees O(log2 n) time on searching for a key (where n is the number of keys).

Of course, you would probably want to make a class with the game_keywords and the related functions to make things better (ex: more than one game going on at once), or really to enforce the integrity of the game_keywords map.

An idea for handling multi-word commands: a command is always one word ("move"), but when you register it, you pass an unsigned int that says how many extra words it takes (add_game_func ("move", move_handler, 1). The event_func now has to an argument: std::vector<std::string>. You can throw an exception (say not_enough_words) if there aren't enough words on input. An easy way to do this is to use an istream_iterator to iterate through the input stream reading words until you hit a newline and then calling the dispatch function with a vector with all the words on the line. This way you have single word commands that have options, for "move" you may have "north" "south" "east" and "west". If you have a really complex set of commands that all start with the same word, you can write a new dispatch function that is passed to the normal dispatch function (or method if you use a class) that uses the extra words to call other commands as needed. Email me at unknown_lamer@hackedtobits.com if you need me to clarify things more.

Posted by unknown_lamer [send private reply] at December 01, 2001, 08:31:28 PM

argh. my attempt to escape the < and > failed. So, here it is again without the entities. (my apologies):

Why is everyone using char arrays? You should use C++ strings instead. They are ranged checked too. Example:

char[10] foo;
std::cin >> foo;

if i enter more than 9 characters, oops. The equiviland with strings:

#include <string>
#include <iostream>

std::string foo;
std::cin >> foo;

You can then make a map of strings and functions that will be called on a keyword. Example:

typedef void (*event_func) ();

std::map<std::string,event_func> game_keywords;

Now you can write a simple function to look up words and call the appropriate function:

void dispatch_event (std::string key) {

event_func temp = game_keywords.find (key);
if (temp)
temp ();

return;

}

that should work (except my typedef might be wrong, dunno, i've been coding a lot more common-lisp than C++ lately). Adding a key to the system is just as simple:

void add_game_function (std::string key, event_func func) {

game_keywords[key] = func;

}

You can even use that to replace functions with new functionality if you need to. Example:

void foo(); // handler when you enter "foo"
void new_foo(); // hypothetical new function

void change_event_foo () {

add_game_function ("foo", new_foo);

}

int main (int argc, char** argv) {

add_game_function ("foo", foo);
add_game_function ("bar", change_event_foo);

start_game();

return 0;

}

So, when you type "bar" it changes what happens when you enter "foo". Searching a large map is efficient because the C++ standard guarantees O(log2 n) time on searching for a key (where n is the number of keys).

Of course, you would probably want to make a class with the game_keywords and the related functions to make things better (ex: more than one game going on at once), or really to enforce the integrity of the game_keywords map.

An idea for handling multi-word commands: a command is always one word ("move"), but when you register it, you pass an unsigned int that says how many extra words it takes (add_game_func ("move", move_handler, 1). The event_func now has to an argument: std::vector<std::string>. You can throw an exception (say not_enough_words) if there aren't enough words on input. An easy way to do this is to use an istream_iterator to iterate through the input stream reading words until you hit a newline and then calling the dispatch function with a vector with all the words on the line. This way you have single word commands that have options, for "move" you may have "north" "south" "east" and "west". If you have a really complex set of commands that all start with the same word, you can write a new dispatch function that is passed to the normal dispatch function (or method if you use a class) that uses the extra words to call other commands as needed. Email me at unknown_lamer@hackedtobits.com if you need me to clarify things more/

Posted by CodeRed [send private reply] at December 03, 2001, 10:02:34 AM

Strings are character arrays, it makes little difference

Posted by unknown_lamer [send private reply] at December 03, 2001, 12:37:51 PM

Yes it does make a difference. You _cannot_ overflow a STL string. If you do, it is an implementation failure, and you should report the bug to the implementor. strings expand as you read more data into them, so you don't have the risk of running off the end of the array (buffer overflow anyone?). This is the same reason you should use the STL vector instead of arrays. Arrays are there in C++ to implement higher level features; you should only use them if you have to. Why waste your time checking the length of input, expanding arrays, or checking for overflows when you can leave that all to the implementation? Also, strings may be optimized (The GNU C++ library does this, IIRC) for fast copying (copy-on-write) and concatation (the internal string is split into more than one array). Why deal with those little (annoying at times) details when you can just forget about it and let the standard library implementor deal with it once. The STL is there for a reason, use it.

Posted by lordaerom [send private reply] at December 03, 2001, 05:18:18 PM

Bravo.

Posted by Psion [send private reply] at December 03, 2001, 05:29:49 PM

There are multiple possible viewpoints here, unknown. :-)

Many people in computer science would say that as long as you can prove that no nasty behavior like buffer overflows occurs, then it is better to avoid those checks. It's sort of like not checking if a binary search tree is valid every time you change it when you know your operations preserve this property.

Posted by unknown_lamer [send private reply] at December 03, 2001, 06:41:44 PM

yes Psion. But I am talking about user input in a text based game. What happens when a command is larger than the buffer? Making a gigantic buffer (say 1MB) would waste too much memory, so why not just use standard strings?

Posted by gian [send private reply] at December 05, 2001, 12:29:48 AM

It seems to me that implementing a simple forever expanding character array would not be that hard (considering I've done it before), using a few malloc's and realloc's.

Posted by CodeRed [send private reply] at December 05, 2001, 12:40:45 AM

exactly... STRINGS ARE CHARACTER ARRAYS. When you use a string, your instantiating an object of the string class. this class contains a CHARACTER ARRAY and methods that allow you to easily manipulate it.

Posted by taubz [send private reply] at December 05, 2001, 07:56:59 AM

unknown is simply saying that is has extra checks to make sure you don't do bad stuff, not that it's not a character array. The flip side is just programming properly without it - using functions that wont go over the edge of the character array. Obviously both are good for different reasons...

- taubz

Posted by unknown_lamer [send private reply] at December 05, 2001, 12:09:06 PM

Gian, by why should _you_ do it, when it is implemented in the standard library? It is better to use standard components than to roll your own because you increase portability and reduce the number of potential bugs in your program. For user input, it is often impossible to ensure that the user will only enter the required number of characters. This is why std::string should be used for all C++ input from users. Also, strings may have certain performance enhancing characteristics like copy-on-write and fast searching. You can always extract a const char* from a standard string using c_str if you want, and then use that string with any legacy libraries you want. You also don't have to worry about things like freeing the string because its dtors do that for you. The only time a char* should be used is when the string will be generated without user intervention and there is no oppurtunity to overflow the string.

Posted by gian [send private reply] at December 06, 2001, 12:39:20 AM

Because, I am a C programmer. I am the anti-C++.

No, not really, but I use stdio only, no iostream for me!

I only use a C++ compiler to incorperate a few "useful" C++ features.

Posted by unknown_lamer [send private reply] at December 06, 2001, 12:09:00 PM

Then use C. Using C++ simply as a "better C" ignore some of its best features. (maybe everyone should just use Common-LISP and be happy...)

You must be logged in to post messages and see which you have already read.

Log on
Username:
Password:
Save for later automatic logon

Register as a new user
 
Copyright TPU 2002. See the Credits and About TPU for more information.