[I6] Objects' names as the Examine command

Hi.

Is there any simple solution to make parser recognize an object’s name as the try to examine this object?

For example, if player types “> apple” it is understood as “>x apple”.

You could try something like this:

Noticing is an action applying to one thing.

Understand "[something]" as noticing.

Carry out noticing:
	try examining the noun.
3 Likes

Thank you for the answer, but I’m afraid it’s not my case - I need solution for Inform 6.

You can add an UnknownVerb routine to do this. See DM4 chapter 30, the UnknownVerb discussion at the end of the chapter.

AFAIK,here is customary, when asking for I6-related questions, putting [Inform 6] or [I6] in the subject.

HTH and
Best regards from Italy,
dott. Piergiorgio.

3 Likes

Thanks! I’ve tried to use this routine, but gave up soon. Will try harder this time.

Could you post a test game? That way we could point out where you went wrong and set you on the right path.

I tried to solve this too, and got almost nothing so far. Here’s my test game for this problem: https://code.oreolek.ru/oreolek/inform6_examination/

Inform Designer’s Manual is very terse on the subject, it just gives a couple of code samples and sends off to read the source code of Balances or AskTellOrder.h.

Using UnknownVerb is only the first step. The second step is to compare the input word to every name of every object in scope; I tried to use LoopOverScope and WordInProperty but it causes a memory error. It looks like the IDM’s recommended method, so what’s wrong in that code?

Maybe BeforeParsing() is more appropriate than the UnknownVerb() function.

1 Like

This is the approach in AskTellOrder.h and I tried it; I think it still needs a second step to compare the input to every object, to skip compass directions and gibberish. It’s harder (because we don’t have verbs or nouns yet, only an array of words as array-strings) and would bring no gains here IMO.

Maybe something like that? (“Somewhat unintuitively.”)

Include "parser";
Include "verblib";

Object room "Room"
   with description "You are in room.",
has light;

Object apple "apple" room
   with name 'apple',
   description "A beautiful red apple.",
has edible;

Global wordToExamine;

[ IdObj name_o   o n i;
   objectloop (o provides name) {
      n = o.#name / WORDSIZE;
      for(i = 0 : i < n : i++)
         if (o.&name-->i == name_o) return o;
   }
   rfalse;
];

[ UnknownVerb word;
   wordToExamine = IdObj(word);
   if (wordToExamine) return 'unknown';
   rfalse;
];

[ ExamineObjectSub;
   << Examine (wordToExamine) >>;
];

[ Initialise;
   location = room;
];

Include "grammar";

verb 'unknown' * -> ExamineObject;

Well, we all have rather similar results:

Global examined_obj;

[ UnknownVerb word x;
	objectloop (x) {
		if (WordInProperty(word, x, name)) {
			examined_obj = x;
			return 'ZZZ';
		};
	};
	rfalse;
];

[ ZZZSub;
	<<Examine examined_obj>>;
];

Verb "ZZZ" * -> ZZZ;

It works fine, but there is a problem with the Russian library: we don’t write the whole forms of words in the name property: for their endings tend to change according to the case (nominative, accusative, etc.)

Though, can I somehow parse the word variable to get the object it possibly belongs to? Without straightforward comparison by WordInProperty?

…Moreover, this way doesn’t solve any ambiguities.

Hacky solution, but—could you just insert 'x',' ' at the beginning of the input buffer, then call the parser again?

(Alternately, push wn, set wn to zero, call NounDomain, see if wn is at the end of the input, if so, redirect to examine, else pop wn? This is getting into a level of parser hacking that makes me wary of breaking something, but…)

Try this for the word radical:

RADICALMIN 3 with apple: ‘a’==false; ‘ap’==false; ‘app’==true; ‘appl’==true; ‘apple’==true; ‘apples’==false;

Constant RADICALMIN 3;

[ Radical flag    w at len len_dic i j;
   at = parse->(4 * verb_wordnum+1);
   len = parse->(4 * verb_wordnum);
   len_dic = (dict_end-dict_start)/dict_entry_size;
   for (i = 0 : i < len_dic : i++) {
      w = No__Dword(i);
      if ( (w->#dict_par1) & flag ) {
         @output_stream 3 StorageForShortName;
         print (address) w;
         @output_stream -3;
         if (len > StorageForShortName-->0) continue;
         j = 0;
         while (j < len) {
            if (buffer->(j+at) ~= StorageForShortName->(j+WORDSIZE)) break;
            j++;
         }
         if (j < len) continue;
         if (j < RADICALMIN && len ~= StorageForShortName-->0) continue;
         return w;
      }
   }
   rfalse;
];

[ UnknownVerb word   x;
   word = Radical(DICT_NOUN);
   objectloop (x) {
      if (WordInProperty(word, x, name)) {
         examined_obj = x;
         return 'ZZZ';
      };
   };
   rfalse;
];

** updated **

Hooray, that works. Next question: how to make it honor ambiguity? It doesn’t ask which item to examine, it just selects the first one.

(have to admit BeforeParsing probably was the right way… because we get the object ID and we need to take the word itself and UnknownVerb argument doesn’t look like a string)

here’s my code (doesn’t work):

Global thing_to_examine;

[ BeforeParsing thing;
  if (num_words > 1) {
    rfalse;
  }
  thing_to_examine = NextWord();
  ! Check that such thing exists in scope, so that we don't catch
  ! compass directions or gibberish.
  objectloop ( thing provides name) {
    if (TestScope(thing, player) && Refers(thing, 1)) {
      ! At least one such thing exists, we can alter the input
      buffer->0 = 'examine';
      buffer->1 = thing_to_examine;
      Tokenise__(buffer, parse);
      rtrue;
    };
  };
  rfalse;
];

It must be said that the problem with chopped off words has been solved with the help of standard function Refers (obj, wnum) and its localized variant LanguageRefers. Neat stuff.

But it’s true that the problem with ambiguities persists.

This will be the next try: check if typed ‘word’ refers to any of the objects then insert ‘examine’ in the buffer and reparse.

Not really sure what it does. Could you elaborate on this, please?

Well. It took 19 lines.

[ BeforeParsing o i j n flag;
	for (i=1 : i<=parse-->0 : i++) {						! cycle through all typed words
		objectloop (o provides name) {						! cycle through all objects with 'name'
			if (TestScope(o, player) && Refers(o, i)) {		! check if the objects is in scope and word in question refers to it
				flag = 1;									! if an object is in the scope and word belongs to it, set the flag to 1
			}
		}
		n = n + flag;										! increase the counter if the flag of the last word has been set
		flag = 0;											! set flag to zero for the next cycle
	}
	if (n < parse-->0) rfalse;								! if counter is lower than the number of typed words, then not all of them have referred to any object. return false and let the parser deal with it
	for (j=buffer-->0 : j>0 : j--) {						! cycle through all entries in the buffer array
		buffer-->(j+2) = buffer-->j;						! move all entries forward by 2 positions
	}
	buffer-->1 = 'x';										! in the first entry write character "x" (like "examine")
	buffer-->2 = ' ';										! in the second entry write space character (to divide the verb and the noun)
	buffer-->0 = buffer-->0 + 2;							! increase buffer length by 2
	VM_Tokenise(buffer, parse);								! reparse the buffer
];

Now I officially call this issue solved.

1 Like