Re: More oddities

From: Daniel Koepke (dkoepke@california.com)
Date: 12/28/98


Dan Egli wrote:

> void list_languages(struct char_data *ch)
> {
>   char *tmp;
>   int a = 0, i;
>   sprintf(buf, "Languages:\r\n");
>   for (i = MIN_LANGUAGES; i < MAX_SKILLS; i++) {
>     if (SPEAKING(ch) == i) sprintf(buf, "*%s", buf);
>     sprintf(buf, "%-20s %s\r\n", languages[a++], how_good...;
>     sprintf(tmp, "Itteration: %d, Language: %s\r\n", a+1,...;
>     send_to_char(tmp, ch);
>   }
>   sprintf(buf, "%s\r\n* denotes currently spoken language.\r\n", buf);
>   send_to_char(buf, ch);
> }

Let's look at what you're doing really quickly by plugging in values
and simulating the loop, shall we?  I call this little thing a "loop
iteration graph" (I don't know if there's a real name given to it, or
even if anyone else in the world bothers to do this),

  Let MIN_LANGUAGES = 100 and MAX_SKILLS = 106
  Let SPEAKING(ch) = 100 (Common)

  Iteration 1: i = 100
    if (100 == 100) buf = "*Languages:\r\n"; /* bug #1 */
    buf = languages[0] + " " + how_good(GET_SKILL(ch, 100)); /* #2 */
    a += 1; /* since we called a++ in previous sprintf(); a = 1 */
    log("Iteration: " + (a+1) + ", " + language[1]);
    a += 1; /* since we called a++ in previous sprintf(); a = 2 */
    OUTPUT: "Iteration: 2, Troll"
  Iteration 2: i = 101
    buf = languages[2] + " " + how_good(GET_SKILL(ch, 101));
    a += 1; /* a = 3 */
    log(...);
    a += 1; /* a = 4 */
    OUTPUT: "Iteration: 3, Elvish"
  Iteration 3: i = 102
    buf = ...;
    a += 1; /* a = 5 */
    OUTPUT: "Iteration: 6, Dragon"
    a += 1; /* a = 6 */
  .
  .
  . (We have a significant sample size)

There are a variety of bugs here.  First, we're causing a bug with
our attempt to discover a bug by having 'a' incremented after
both sprintf() statements.  My suggestion would be to move the 'a'
stuff into the actual for(;;) portion, rather than have it in the
block.  Hence,

  for (a = 0, i = MIN_LANGUAGES; i < MAX_SKILL; a++, i++) {
    if (GET_SKILL(ch) == i) ...;
    sprintf(buf, "%-20s %s\r\n", languages[a], how_good...;
    .
    .
    .
  }

with 'a' not being incremented within the actual loop code (i.e.,
no 'a++' statements).

The next problem, denoted by "Bug #1" above, is that we call
sprintf() with "*%s" and the argument 'buf'.  Assuming that our
string isn't mangled (it will almost certainly be because buf
will be modified before we incorperate it into the sprintf()
statement, meaning that we'll likely get "**") or we don't get
a crash, we'll at best have "*Languages:" which isn't what we
wanted.  Rather,

  if (GET_SKILL(ch) == i) strcat(buf, "*"); /* add * at end of buf */

Next, denoted by "Bug #2", we overwrite the contents of buf with
the new ones, rather than append them.  What we want to do is,

  sprintf(buf+strlen(buf), ...);

Which will put the new text at the end of buf.  If you don't
understand how this works, I suggest reading a (good) book on C,
one that covers things such as pointer arithematic.

Also, with our sprintf(tmp...) thing: we are crashing because
tmp is a pointer to a random memory address, not an array.  We want
"char tmp[MAX_STRING_LENGTH];" rather than "char * tmp;".

Recommendations:
  1. Get a book covering pointers.  There are a lot of mediocre
     programmers out there that don't understand pointers.  Until
     they get a grasp of the idiosyncrasies of pointers, they'll
     never take that next step.

  2. Desk check your code -- that is, read it once or twice before
     you even try to compile it.  It can save you some time and
     maybe a few headaches.

  3. If you've read over your buggy code a thousand times and things
     just aren't clicking together in your head, try simulating it
     using something like a "loop iteration graph", or splitting it
     up into groups to analyze certain blocks by what function they
     are supposed to serve.

  4. If you have other coders on your MUD, try to establish a "Peer
     Review" method.  In this, each coder would review the other's
     code -- reading it and writing down (or maybe even fixing) the
     potential problems.  The only danger is if any of your coders
     aren't mature enough to handle constructive criticism, or give
     it.

As a side note, I would also suggest implementing something like a
"Peer Review" group with your builders, by asking them to set aside
some time to go through the new areas -- perhaps as three different
PC characters (since areas are typically developed for certain
level/power characters make it one below the target level range, one
of the average of the target level range, and one above the target
level range).  I realize the quantity of areas produced will go
down since builders will have to set aside some time to review one
another's areas, but the quality will rise and provide a more
enjoyable experience for the players.

Note that this isn't necessarily a "quality control" program: you
should make it clear that builders aren't going to be ousted if
they're not producing high quality areas/descriptions/whatever.  The
idea is to have each builder help one another.  "No man is an
island."

-dak : Finally got his DVD-ROM to play DVD movies (It came with bad
       drivers, originally).


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     |  http://qsilver.queensu.ca/~fletchra/Circle/list-faq.html  |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 12/15/00 PST