Recursion problem

This is a discussion / support forum for the Hugo programming language by Kent Tessman. Hugo is a powerful programming language for making text games / interactive fiction with multimedia support.

Hugo download links: https://www.generalcoffee.com/hugo
Roody Yogurt's Hugo Blog: https://notdeadhugo.blogspot.com
The Hugor interpreter by RealNC: http://ifwiki.org/index.php/Hugor

Moderators: Ice Cream Jonsey, joltcountry

Bainespal
Posts: 151
Joined: Fri Jul 09, 2010 8:59 am

Recursion problem

Post by Bainespal »

There's an object in my game that needs to be listened to. I implemented this in the before property of the object with "object DoListen", and that works right. However, I want the simple command "LISTEN" to have the same affect only after the aforementioned object has been moved into the room where this scene is happening (and the object representing the sound to be listened to is not present when the player first enters the room).

I thought this would be a simple matter of blocking "location DoListen" from the room object's before property and telling it to "Perform" the Listen command on the sound object if that object is in the location. When I tried this, the Hugo Engine crashed. I think I know why -- the "location DoListen" rule runs the "DoListen" verbroutine through the Perform() routine, which is caught again by the same "location DoListen" rule. So I have a problem with recursion.

I almost corrected this problem using PreParse (in the player object's before property). The right code is triggered, advancing the game state in the way that I want, but then the default response for an empty "LISTEN" command is printed after my response for listening to the correct object ("Be a little more specific about exactly what you'd like to listen to.")

I don't know how to suppress the library message here. I'll copy my PreParse code below, but I think this code is pretty workable.

Code: Select all

actor PreParse
    {
      if (word[1] = "listen") and Contains (crypt, player)
      {
        if voices in crypt
        {
          Perform(&DoListen, voices)
        }
        else
        {
          return false
        }
      }
      else
      {
        return false
      }
}
Thank you.

User avatar
Tdarcos
Posts: 9333
Joined: Fri May 16, 2008 9:25 am
Location: Arlington, Virginia
Contact:

Re: Recursion problem

Post by Tdarcos »

Bainespal wrote:There's an object in my game that needs to be listened to. I implemented this in the before property of the object with "object DoListen", and that works right. However, I want the simple command "LISTEN" to have the same affect only after the aforementioned object has been moved into the room where this scene is happening (and the object representing the sound to be listened to is not present when the player first enters the room).

I thought this would be a simple matter of blocking "location DoListen" from the room object's before property and telling it to "Perform" the Listen command on the sound object if that object is in the location. When I tried this, the Hugo Engine crashed. I think I know why -- the "location DoListen" rule runs the "DoListen" verbroutine through the Perform() routine, which is caught again by the same "location DoListen" rule. So I have a problem with recursion.

I almost corrected this problem using PreParse (in the player object's before property). The right code is triggered, advancing the game state in the way that I want, but then the default response for an empty "LISTEN" command is printed after my response for listening to the correct object ("Be a little more specific about exactly what you'd like to listen to.")

I don't know how to suppress the library message here. I'll copy my PreParse code below, but I think this code is pretty workable.

Code: Select all

actor PreParse
    {
      if (word[1] = "listen") and Contains (crypt, player)
      {
        if voices in crypt
        {
          Perform(&DoListen, voices)
        }
        else
        {
          return false
        }
      }
      else
      {
        return false
      }
}
Thank you.
Hint. Don't return false. This fails the command. What you might do is set up your own fail for the word listen in the verb section, or have two versions of the verb table, something like this (just off the top of my head, not necessarily right):

Verb "listen"
* DoBlankListen
* object DoListen

So that a "naked" listen goes to one routine, a listen followed by an object goes to the processor. Then you can handle the naked listen as you choose, and handle "listen mailbox" separately.
Alan Francis wrote a book containing everything men understand about women. It consisted of 100 blank pages.

Bainespal
Posts: 151
Joined: Fri Jul 09, 2010 8:59 am

Post by Bainespal »

I have a little more information.

First of all, I've now tried putting a "return true" beneath "Perform(&DoListen, voices)" in the code above, but there's no noticeable change.

It seems like two LISTEN commands are being triggered for some reason. I suspect the default response when I type "LISTEN" by itself is the result of a second command. When I type "LISTEN TO VOICES", I get my own response twice (and no default response).

Finally, the "AGAIN" command makes another little annoying problem, since the intercepted "LISTEN" command isn't registered as the last command. Is there a variable I can set to make the Engine know what I want the previous verbroutine and object to be, so "AGAIN"/"G" will work right?

Bainespal
Posts: 151
Joined: Fri Jul 09, 2010 8:59 am

Post by Bainespal »

Tdarcos wrote:Hint. Don't return false. This fails the command. What you might do is set up your own fail for the word listen in the verb section, or have two versions of the verb table, something like this (just off the top of my head, not necessarily right):

Verb "listen"
* DoBlankListen
* object DoListen

So that a "naked" listen goes to one routine, a listen followed by an object goes to the processor. Then you can handle the naked listen as you choose, and handle "listen mailbox" separately.
Oh, I didn't see your reply. You must have been writing it at the same time I was composing my own follow-up post. Thank you! :)

I think I understand what you're saying. In the thread I started about using a LookIn command on a window object, Roody pointed out that I could use a special verb token just for that one object. So, what I will try in this case when I'm on my laptop again will look something like this:

Code: Select all

verb "listen"
* voices DoListenVoices
I'll tell you if it works.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

Hugo's default DoListen routine isn't designed with both object and room noises in mind (just typing >LISTEN asks you to specify what you want to listen to). I did not like this, either, so I wrote a replacement DoListen routine some months ago: http://hugo.gerynarsabode.org/index.php ... e_DoListen

To save you the time clicking on that:

Code: Select all

replace DoListen
{
	if not object
		{
			if &location.after
				return true
			else
				{
				VMessage(&DoListen, 1)  ! "Be a little more specific..."
				return false
				}
		}
	elseif not object.after
		VMessage(&DoListen, 2)   ! "Not making any sound..."
	! we have to clear verbroutine or else location.after.DoListen will run again
	verbroutine = ""
	return true
}
If you have that, you could have a room like this:

Code: Select all

room the_important_room "The Room Where You Listen"
{
after
        {
        location DoListen
                    {
                    if FindObject(voices,location)
                          Perform(&DoListen,voices)
                    else
                          return false
                    }
         }
}
This code is untested, but it should work! Let me know!

Also, I didn't read this thread too closely so I may have missed an important detail.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

Ok, reading back, Tdarcos is also right. Another reasonable way to handle it is to split Listen into two verb routines like he suggested.

Roody_Yogurt
Posts: 2179
Joined: Mon Apr 29, 2002 6:23 pm
Location: Milwaukee

Post by Roody_Yogurt »

Lastly, if you look at that replacement DoListen routine (or the original), you'll notice that it's one of those verb routines that work off an after routine, so like that room object example up above, you'd want your voices object to have something like:

Code: Select all

object voices "voices"
{
noun "voices"
article "the"
after
      {
      object DoListen
             {
              ! Listening code and responses
             }
       }
}
Having it be a before routine would probably work the same (I can't think of any situations where it'd break something), so it's really just a matter of putting things where the code expects them to be.

User avatar
Tdarcos
Posts: 9333
Joined: Fri May 16, 2008 9:25 am
Location: Arlington, Virginia
Contact:

Post by Tdarcos »

Roody_Yogurt wrote:Ok, reading back, Tdarcos is also right. Another reasonable way to handle it is to split Listen into two verb routines like he suggested.
I'm glad to see that 34 years of programming in twelve different programming languages transfers to ones I'm new at. Some people have the knack that they can really do this stuff fantastically. I have my own heroes of people I knew when I was younger who were wizards then.
Alan Francis wrote a book containing everything men understand about women. It consisted of 100 blank pages.

Bainespal
Posts: 151
Joined: Fri Jul 09, 2010 8:59 am

Post by Bainespal »

Now that it's been mentioned, I do feel that the library's treatment of the command to listen is inadequate -- it should definitely provide for just listening in a room. I think I noticed this a while ago, but I forgot.

That replacement for DoListen looks elegant and comprehensive, but I went with two verb routines instead because I think that approach is more compatible with my existing code. Instead of just making a verb token for the command of listening specifically to the one object like I said I would do, I added a general verb token for "listen" with no other words and assigned it to a routine called DoListenVague. I was pleasantly surprised when the game gave precedence to the new DoListenVague over the library's DoListen when I just typed the command "LISTEN." It was a simple matter to block DoListenVague from the before property of the room object and have the Perform routine trigger DoListen(voices).

So, thank you, both of you. I'm sorry that I took so long to get back.

User avatar
Tdarcos
Posts: 9333
Joined: Fri May 16, 2008 9:25 am
Location: Arlington, Virginia
Contact:

Post by Tdarcos »

Bainespal wrote:Now that it's been mentioned, I do feel that the library's treatment of the command to listen is inadequate
Let's consider that Hugo is a general purpose text-based adventure game processor. As such, the routines are going to be general purpose and targeted for the lowest common denominator, with the ability to override as needed.

So for most actions the default probably works right, but occasionally you need something better. When that happens you write the code that handles the special cases.

As a system, if done correctly, the base installation should hit at least the 95th percentile and hopefully 98th. This means that for 95 to 98% of all games, or 95-98% of even complicated games, the default library is adequate to do the job unchanged.

That means, given, say, that the library (objlib.h, resource.h, system.h, verblib.g, verblib.h, verbstub.g, verbstub.h, window.h, hugofix.g, hugofix.h, hugolib.h) is perhaps 20,000 lines, that at most you shouldn't have to code more than a couple hundred lines of replacement code.
Alan Francis wrote a book containing everything men understand about women. It consisted of 100 blank pages.

Post Reply