Journal Articles
Browse in : |
All
> Journals
> CVu
> 011
(10)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Adventures In C
Author: Martin Moene
Date: 17 June 2010 08:51:00 +01:00 or Thu, 17 June 2010 08:51:00 +01:00
Summary: A few thoughts on using C to write Adventure Games
Body:
In this article I hope to reveal some of the structuring features of C that would enable an adventure game to be written in an easy to understand and easy to modify form. To my mind one of the most useful features of C in writing and adventure type program is the ability to store and later use pointers to functions just like any other data. This allows a very elegant and generalised approach to be made to the problem of processing the phrases that the player types into the adventure so that they have an effect on the adventure 'world' represented inside the computer.
The adventure parser 'adshell' presented here should serve as a backbone for an adventure program: all that is missing is an adventure to work on. The adventure writer will have to devise some way of representing the world of the adventure and providing action functions to affect this world in response to the recognition of keywords.
The writer of the adventure has simply to supply a set of action functions written in C that interact with each other in a defined way through the mechanism described below and by the modification of global variables to control each others action.
In the command 'HIT DRAGON' the action of the 'HIT' handling function obviously depends on the thing being hit and the action of the 'DRAGON' function must have fore-knowlege that the dragon is being hit. Each must leave some indication of how the two will interact to cause a change in the state of the adventure as defined by the global data structures.
The parse function will do no syntactic analysis itself. It simply seperates each word of the input string and looks it up in the action table to find a function to call. If no match is found for a particular word the parser just goes on to the next. Some words in the input by be considered as parameters to the action functions. In the above example of 'HIT'and 'DRAGON' it would make sense to have a handler for hit that assumes that the next word is the thing to be hit and acts accordingly.
The parser is invoked from the main() of the adventure program as
parse(line_of_input, action_table)
If parse returns false (0) then NONE of the input line could be understood and the main program can print an appropriate message. A return value of non zero will indicate that the input has affected the adventure world in some way.
The action functions are stored in a table of strings and function pointers. The function pointer is called if the string token matches the current word. Matches are case sensitive in the code as provided but this can be changed by adding a '.tolower()' call in the building of the array of words on a line which forces all user input to lower case before comparison is carried out.
Each function will be called with an integer and a string parameter: the first parameter will be a pointer to the complete string that is being parsed. The second parameter is an integer that defines the offset of the whole line string that can be used to access the word that caused the function to be invoked.
func(line_of_input, index_of_token)
The return value of func is very important to parse. It must be either a positive or negative integer. Parse will make the next word it considers the index of the token just processed + the return value of the processing function. This means that a negative value can cause the parser to back up and a positive value other than 1 can cause words to be missed. A return value of 0 will cause the function to be re_invoked - useful if the function wishes to re-enter itself from the top.
A dummy application for parse is provided that recognises the words 'help' and 'exit' only but shows the principles of use.
I do not have the time or the imagination to develop a full blown adventure but I hope that routine will prove a useful tool for those that have.
/* * Adventure parser shell header file. * By Martin Houston for CUG(UK) * 25/5/86 */ /* default sizes for various things */ #define WORDLEN 25 /* no words longer than 25 chars */ #define LINELEN 255 /* < 255 characters in one input from user */ /* declaration for function look up table structure */ struct look_up_func { char token[WORDLEN]; int (*action)(); /* a POINTER to a function */ }; /* * Adventure parser shell code file. * By Martin Houston for CUG(UK) * 25/5/86 */ #include <ctype.h> #include "adshell.h" #define SAME(A,B) (!strcmp(A,B)) #define TRUE 1 #define FALSE !(TRUE) #define NULL (char *)0 int parse(line, tab) char *line; struct look_up_func tab[]; /* array of structs */ { int acted = FALSE; /* we have not done anything yet */ static int wordstarts[LINELEN/2]; /* * As each word is seperated from the next by * whitespace there can only be at most LINELEN / 2 */ static char words[LINELEN/2][WORDLEN]; int slot; /* slot in table */ int lindex; /* maximum used slot in table */ int cn; /* character counter */ int x; /* dogsbody variable ! */ char *str; /* input string */ char c; /* next character */ int fpslot; struct look_up_func *fp; char between_words; /* * Build up tables of words and word starts */ str = line; between_words = TRUE; for(lindex=0, slot=0, cn=0; lindex<LINELEN; lindex++) { if(((c = *str++) == '\0') || isspace(c)) { if(cn > 0) { /* terminated a word */ strncpy(words[slot], &line[wordstarts[slot]], cn); words[slot][cn] = '\0'; cn = 0; slot++; } between_words = TRUE; if(c == '\0') { break; /* no more input line */ } } else { if(between_words) { /* started a word */ wordstarts[slot] = lindex; between_words = FALSE; } if(cn < WORDLEN) cn++; /* count a character */ } } /* now try to find a match */ for(x=0; x < slot; ) { fpslot = 0; while((fp = &tab[fpslot++]) != NULL) { if(SAME(words[x], fp->token)) { acted = TRUE; x += (*fp->action)(line,wordstarts[x]); if(x < 0) x = 0; break; } if(SAME(fp->token, "")) break; } /* if no match was found above ignore this word */ if(SAME(fp->token, "")) x++; } return acted; } /* * Adventure parser shell dummy use file. * By Martin Houston for CUG(UK) * 25/5/86 */ #include "adshell.h" /* * Must declare all funcs we will put in table explicitly. */ int help(); int fexit(); struct look_up_func tab[] = { "help", help, "exit", fexit, "", (int (*)())0 }; int help(line, index) char *line; int index; { printf("No help for the wicked!\n"); /* tell parser to examine next word */ return 1; } int fexit(line, index) char *line; int index; { /* allows get out from prog */ exit(0); } main() { char line[LINELEN]; while(1) { printf("What next ? "); gets(line); if(!parse(line, tab)) { printf("I dont understand you !\n"); } } } |
Listing 1 |
Notes:
More fields may be available via dynamicdata ..