Roger Firth's IF pages

Home

Inform 6: Frequently Asked Questions

Email
Back up

Into
the Intro

Setting
the scene

Preparing
to program

Learning
the lingo

Dabbling
in data

Operating
on objects

Verbal
versatility

Bothered
by bugs

History and
hereafter

Worldly
woes

Inside
information

Tips and
techniques

These topics are about tracking down the inevitable errors in your game:

What can I expect when I run my first program?
Help! What's wrong with my code?
    Common traps for the unwary
Why do I get spurious 0s and 1s in my printout?
What's the difference between Squared(x) and (Squared) x?
    The print rule trick
How can I make the debugging process easier?
    Automated replay
    Object inspection and manipulation
Why does my game mention "a apple"?
What were Vile Zero Errors From Hell?
I've found an Inform problem -- what should I do?

What can I expect when I run my first program?

Once you've fixed all the annoying little syntax errors which previously stopped your game from compiling (see What can I expect when I try to compile my program?), you win the pleasure of actually being able to play it. All too often that pleasure is short-lived; almost immediately you'll start to notice deficiencies -- some subtle, some glaringly obvious -- that need to be improved or mended. This is a perfectly normal experience, and you should expect to spend a considerable period exploring the ways in which your game fails to behave as you'd intended.

When correcting the problems that you'll inevitably discover, you should move slowly and cautiously. Work on one issue at a time: identify what's wrong, change the program (maybe only a single line), recompile, and test what you've just done. If you're lucky, you've fixed the problem. If you're unlucky, not only may the problem still remain, but something else may have gone wrong. This is also all too common, but by recompiling frequently and immediately testing the change, you'll much more quickly home in on what's gone wrong and how to fix it. One of the biggest causes of frustration comes from making a large number of changes all at once, not recompiling until every modification is in place, then finding that the game doesn't work nearly as well as it did before... and not having any idea which of the many changes did the real damage.

By the way, if you're about to embark on major surgery to a working program, save a copy of the file before proceeding. That way, if things go seriously pear-shaped, you can always revert to that copy, rather than trying to undo all the individual changes.

Help! What's wrong with my code?

More information in the DM:
§40

Just because you've found all the mistakes which prevented your game from compiling, and you've resolved the warning messages -- you have got rid of them, haven't you? -- it doesn't follow that your game is free from bugs. Here are some common traps for the unwary Inform novice.

  1. The "string"; statement prints the string, outputs a newline, and then returns. This is probably the most common Inform gotcha. If you write these lines:
  2.   "The fire is ";
      if (self has general) print "blazing fiercely"; else print "smouldering gently";
      print " in the grate.";

    then the compiler will complain that the second line is unreachable. It won't complain if you write this, though the situation is just the same:

      if (self has general) "The fire is blazing fiercely"; else "The fire is smouldering gently";
      print " in the grate.";

    Fix: change to ordinary print statements:

      if (self has general) print "The fire is blazing fiercely"; else print "The fire is smouldering gently";
      print " in the grate.";

  3. An if and an else each control the single statement which follows. If you write these lines then you're like to see that the fire "continues to blazesuddenly flares up".
  4.   if (self has general) print "continues to blaze"
      else give self general; print "suddenly flares up";

    Fix: wrap braces around the multiple statements:

      if (self has general) print "continues to blaze"
      else { give self general; print "suddenly flares up"; }

  5. An else relates to the immediately-preceding if. If you write these lines then you're likely to see just "The grate is".
  6.   print "The grate is ";
      if (grate has on)
          if (self has general) print "hot";
      else print "cold.";

    Fix: wrap braces around the second if, or combine the two ifs:

      print "The grate is ";
      if (grate has on)
          { if (self has general) print "hot"; }
      else print "cold.";
      print "The grate is ";
      if (grate has on && self has general) print "hot";
      else print "cold.";

  7. Don't confuse logical not '~~' with bitwise not '~'. If you write these statements, then you'll always print "ok":
  8.   if (~self in location) print "ok";

    This is because the condition self in location evaluates to either false (0) or true (1). A bitwise 'not' toggles each individual bit: '~' applied to 0 ($$0000000000000000) gives -1 ($$1111111111111111), which is true; '~' applied to 1 ($$0000000000000001) gives -2 ($$1111111111111110), which is also true.
    Fix: use '~~' instead:

      if (~~self in location) print "ok";

    A logical 'not' toggles the true/false meaning of all the bits taken together; '~~' applied to 0 ($$0000000000000000) gives 1 ($$0000000000000001), which is true; '~~' applied to 1 ($$0000000000000001) gives 0 ($$0000000000000000), which is false.
    For the same reason, don't confuse '&&' and '||' (which are quite commonly used) with '&' and '|' (found much more rarely).

  9. Don't apply the test '== true' to something which can have a value other than 0 or 1. If you write these statements, then you'll not print "ok" if number is 2,3,...:
  10.   if (self.number == true) print "ok";

    Fix: change the test to one of these:

      if (self.number) print "ok";
      if (self.number ~= false) print "ok";

  11. Don't write just number (or any other property name) instead of self.number (or object.number) -- things will mysteriously fail to work.
  12. Beware of creating name properties with impossible values, such as single letters 'g' (that's a character constant, not a dictionary word) and phrases 'g string' (the parser tests only single words, not phrases with spaces in them). This is correct:
  13.   name 'g//' 'string' 'gstring' 'g-string',

  14. If you refer to a routine without '()' afterwards, the result is the address of that routine rather than the value which it returns:
  15.   x = myRoutine;   ! wrong -- x contains the address of myRoutine
      x = myRoutine(); ! correct -- x contains the return value from running myRoutine

  16. If you get unexpected 0s and 1s appearing, check the next topic.
  17. Arithmetic is limited to numbers in the range -32768..0..32767. If you add or multiply two numbers giving a result outside that range, the outcome may be unexpected.
  18. Don't use the found_in property for moveable objects; they'll magically return home if the player tries to walk off with them. found_in is intended only for objects with a scenery attribute.

Why do I get spurious 0s and 1s in my printout?

Almost certainly, because you've called a routine in the middle of the print statement. The next answer describes the syntax trick to avoid this problem, but here's why it happens. Consider this tiny fragment of code:

  [ Squared x; print x*x; ];

  print "The square of ", 4, " is ", Squared(4), ".^";

What happens here is: the print statement outputs "The square of 4 is ", then calls the routine. The routine outputs the answer of 16 and returns. The print statement then outputs 1 -- the value returned by the routine -- followed by the fullstop and newline. The value returned by the routine? Yes, every routine returns a value, which depends on the last executed statement in the routine.

More information in the DM:
§1.7

As an aide-memoire, remember the letter pairs EF...ST: Embedded routines end as False, Standalone routines end as True.

If the last executed statement is...

then the value returned by the routine is...

return value;

value

return;
or
rtrue;

true (1)

rfalse;

false (0)

] at the end of a standalone routine

true (1)

] at the end of an embedded routine

false (0)

More information in the DM:
§3.5

A 'standalone' routine is one that has a name and exists in its own right; like Squared above. An 'embedded' routine is one without a name which you include within an object definition as the value of a property:

    with  description [; code to print an object's description ],
          ...

What's the difference between Squared(x) and (Squared) x?

It's the difference between the spurious 0 and 1 problem occurring, and not. If your program reads:

  print "The square of ", 4, " is ", Squared(4), ".^";

then the output you'll get is:

  The square of 4 is 161.

However, if you modify the program, leaving the routine unchanged but treating it as a print rule, so that the line now reads:

  print "The square of ", 4, " is ", (Squared) 4, ".^";

then the output you'll get, correctly, is:

  The square of 4 is 16.

More information in the DM:
§1.12

This funny little syntax trick, which applies only when printing, has the effect of calling the specified routine but not outputting the value which it returns. That is, if the routine itself includes a print statement, that's what gets output; should the routine happen not to include a print statement, nothing gets output.

How can I make the debugging process easier?

You're going to be doing a lot of it; much IF authorship comprises brief periods of enjoyable creativity separated by longer spells of laborious testing and frustrating bug hunts. So, if only from a sense of self-preservation, you'd be well advised to get to grips with Inform's debugging capabilities. Two techniques are of particular value:

There's some more information about compiling and debugging on Roger Firth's InfLight pages

If you're already familiar with the UNIX tool gdb, you might like to try the Nitfol interpreter, which incorporates a gdb-like debugger. On the other hand, if you're not already a gdb expert, you probably shouldn't try learning it here.

Why does my game mention "a apple"?

Apparently, the business of outputting "an" rather than "a" before a word beginning with a vowel is by default handled by the interpreter rather than the compiler; unfortunately, some interpreters get it wrong (EXAMPLE?). To be safe, give objects which need it an explicit article property:

  Object  apple "apple"
    with  name 'apple' 'fruit',
          article "an",
          ...

What were Vile Zero Errors From Hell?

That was the fanciful name for a nasty error condition which crashed the Z-machine if X happened to be zero when executing statements like these:

For more background information, see Andrew Plotkin's detailed explanation.

  child(X), parent(X), sibling(X), ...
  if (X.property == ...) ..., X.property = ...
  if (X has attribute) ..., give X attribute
  move X to ..., move ... to X, remove X

Since the availability of Strict error checking in Library 6/11, it's rare for games to crash in this way; instead, you'll just get a runtime error message. You still need to fix the error, but at least nowadays you know pretty well where to start looking.

I've found an Inform problem -- what should I do?

Like any other complex system, the Inform software and its associated documentation contain errors of varying severity. If you think that you've found something wrong, don't just sigh and move on: please, report the problem. The first thing to do, obviously, is to check and re-check your suspicions. Once you've convinced yourself that something really is wrong, then you should study the various published lists; it's quite possible that you're not the first person to have happened upon the problem. There are pages devoted to various topics; each tells you who to write to if you wish to report a new problem or add another suggestion:

If your problem lies elsewhere -- perhaps in the interpreter you're using, or in a library contribution -- then you should write directly to its author. It's generally considered impolite to baldly announce in the rec.arts.int-fiction newsgroup that you've found a bug, though you can ask for assistance there in determining the exact nature of the problem you're experiencing.

Into
the Intro

Setting
the scene

Preparing
to program

Learning
the lingo

Dabbling
in data

Operating
on objects

Verbal
versatility

Bothered
by bugs

History and
hereafter

Worldly
woes

Inside
information

Tips and
techniques