Parsing dictionary words as objects

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

Parsing dictionary words as objects

Post by Bainespal »

At a point in my game, the PC will be in a dark room. I want to respond to variations of the command "turn on the lights" without implementing a "lights" object. The problem is made more complicated by the fact that I already have an object in the game with the nouns "light" and "lights," but that object has nothing to do with the action I'm trying to intercept here.

After a lot of bumbling around, I took Roody's advice for his thread by putting my code in "actor PreParse" in the before rule of the "you" object. I'm trying to insert my own text with a CustomError message, by calling ParseError with the number corresponding to the one I used in my replacement of CustomError. (I learned that GetInput is designed for something totally different).

I think I'm starting to make a little bit of progress, but now I'm about out of ideas. I used the Debugger to follow the code, and I noticed that Parse gets called after my condition "if verbroutine = &DoSwitchOn" is evaluated. I think this may be part of the problem. I'm thinking the whole use of PreParse is to supplant Parse altogether for this command.

Sorry to bother you guys about this. I'll copy my code below. If anyone has any suggestions, they would be greatly appreciated.

Code: Select all

before
  {
    actor PreParse
    {
      if self in upper_storey and upper_storey is not light
      {
        if verbroutine = &DoSwitchOn  ! "&" because I don't want to call the routine
        {
          if word[3] = "light" or "lightswitch" or "lights"
          {
            ParseError(103)
            return true
          }
          else
          {
            return false
          }
        }
      } ! end verbroutine
    } ! end actor
  } ! end before
Note that "upper_storey" is the room.

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

Re: Parsing dictionary words as objects

Post by Tdarcos »

Bainespal wrote:

Code: Select all

if verbroutine = &DoSwitchOn  ! "&" because I don't want to call the routine
        {
          if word[3] = "light" or "lightswitch" or "lights"
Two questions: what do "verbroutine" and "DoSwitchOn" represent in this context and why is it important not to call something?
"I really feel that I'm losin' my best friend
I can't believe this could be the end."
- No Doubt, Don't Speak

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

Post by Roody_Yogurt »

The game doesn't know what the verbroutine is until Parse is done with the input line, so checking for any verbroutine in PreParse will only apply to the previous turn.

Try:

Code: Select all

if (word[1] = "turn","switch") and word[2] = "on" and (word[3] = \
"light","lightswitch","lights")

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

Post by Roody_Yogurt »

Also, if your parsing routing doesn't work out for you, you could always make a "turn on the lights" verb grammar entry that points to a routine that does what you want if the conditions are right or otherwise tries to Perform(&DoSwitchOn, lightobject).

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

Post by Bainespal »

Tdarcos wrote:Two questions: what do "verbroutine" and "DoSwitchOn" represent in this context and why is it important not to call something?
DoSwitchOn is the library routine that controls the default action caused by the player trying to turn something on. I don't want this routine to actually try to run when I'm just trying to test whether the command that the player entered is covered by it. Without the "&" character, I discovered through the Debugger that the DoSwitchOn routine was being called and was trying to act on "nothing". It actually succeeded in turning "nothing" on, so that next time I tried it, part of the weird response was that Nothing was already on!
Roody_Yogurt wrote:The game doesn't know what the verbroutine is until Parse is done with the input line, so checking for any verbroutine in PreParse will only apply to the previous turn.
Now that I think about, I suppose that makes sense. How can Hugo know what the current verbroutine is unless it's already done some parsing on the player's input? I was thinking that if I could only figure out a way to use the standard action routine and pass off a dictionary word to replace the nonexistant object, it would be a very elegant and economic solution. Oh, well.

Next, I'll try to intercept all three words in the command. I would have had to allow for synonymous commands like "FLIP SWITCH" anyways. I knew there must have been some way to make all this work using grammar definitions, but I haven't looked into that path at all yet.

As always, thank you. :)

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

Post by Bainespal »

Specifying each word that I want to intercept does indeed work, but I discovered the correct place for this code is in a replacement of the PreParse routine and not in an "actor" rule in the player object's before routine.

When I had the PreParse code in the player object, once the condition for displaying the CustomError message was initially met, all subsequent commands in the game produced the CustomError message, including meta-commands like "QUIT" and "RESTORE." I tried returning in various places, but nothing seemed to make any difference.

Looking through the Hugo by Example wiki, I read the article about the PreParse routine. I noticed the use of replacing PreParse in the code snippet from Down that was posted, which is more complicated than what I want to do but about the same idea. So, now I'm good. :-)

I appreciate all the work that's gone into that wiki. It pretty much saved my project here.

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

Post by Roody_Yogurt »

Hmm, you should still be able to trap the words in the actor before stuff, but I'd have to see the code to troubleshoot it. Anyhow, you got it working which is all that matters.

As far as the wiki goes, I've written the majority of the code examples on there, yet even I go to the wiki all of the time to remind myself how to do things. Coding without internet access these days is a little like coding in the dark because of how much I use it, so it has very much been matching my original vision for it (which Royce Odle made a reality). I'm glad to hear that it is as useful to others as it has been for me!

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

Post by Bainespal »

It turns out I was mistaken. The problem probably had nothing to do with the code being in the player object's before property. I tried replacing PreParse early one morning before I had to run off to class, and I'd thought that it had worked, but it really hadn't.

However, now I really have found the solution! I noticed how Kent Tessman is always using local variables in his routines, including those that use the word[] array, such as the very code from Down that I had been looking at. I was suspecting that Hugo was setting the word[] entries to the words I was trying to test for when it read the line:

Code: Select all

if word[1] = "turn" and word[2] = "on" and word[3] = "lights"
Examining the executed code in the Debugger, I noticed that the CustomError message was always displayed whenever PreParse made it to that condition, even if the condition was not true.

I added local variables in my replaced PreParse routine and set them to the specific dictionary words from the player's command:

Code: Select all

local 1st, 2nd, 3rd
1st = word[1]
2nd = word[2]
3rd = word[3]
And now the condition that was giving me so much trouble looks like this:

Code: Select all

if 1st = "turn" or "switch" and 2nd = "on" and 3rd = "light" or "lights" or "lightswitch" or "lamp"
I'm pretty sure that the Hugo Book has some examples of testing for a specific array entry (but probably not the word[] array), so I don't understand why I had to do this, but it works, so I'm done questioning.

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

Post by Roody_Yogurt »

Yeah, I don't think that conditional statement is your problem, either. If you want to post you code here or PM me with it, I'd be happy to troubleshoot it for you. Otherwise, good luck!

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

Post by Bainespal »

Well, okay. Here's the complete routine as it is now:

Code: Select all

replace PreParse
{
  local 1st, 2nd, 3rd
  ! Let's see what happens if I try to store the player's command in local variables!
  1st = word[1]
  2nd = word[2]
  3rd = word[3]
  ! CustomError(100) holds the text for the PC trying to turn on his lights
  if player in upper_storey and upper_storey is not light
  {
    if 1st = "turn" or "switch" and 2nd = "on" and 3rd = "light" or "lights" or "lightswitch" or "lamp"
    {
      ParseError(100)
    }
    elseif 1st = "flip" and 2nd = "switch" or "lightswitch"
    {
      ParseError(100)
    }
    if tried_the_lights = false
    {
      tried_the_lights = true ! Needs to be set to determine when both necessary actions have been taken
    }
  }
}
It seems to be doing what I want it to, although I haven't fully implemented my plan for the global variable "tried_the_lights" yet. That's controlled by other code, and there are still a couple of things I think I can try before I ask for help.

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

Post by Roody_Yogurt »

I'll try to throw some working code together later, but in the meantime, according to that code, tried_the_lights is set to true no matter what the player types in (as long as he is in upper_storey in the dark), which is probably not exactly what you want.

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

Post by Bainespal »

Roody_Yogurt wrote:I'll try to throw some working code together later, but in the meantime, according to that code, tried_the_lights is set to true no matter what the player types in (as long as he is in upper_storey in the dark), which is probably not exactly what you want.
Very true. Thank you!

I suppose most of my problems with Hugo are due to my poor understanding of conditions. Frequently, I end up setting a condition to be true when I mean to test for it.

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

Post by Roody_Yogurt »

Here should be some workable code for you:

Code: Select all

replace PreParse
{
	if Contains(upper_storey,player) and upper_storey is not light
		{
		if (word[1] = "turn","switch") and (word[2] = "on") and (word[3] = "light","lights","lightswitch","lamp")
			{
			ParseError(100)
			tried_the_lights = true
			}
		elseif (word[1] = "flip") and (word[2] = "switch","lightswitch")
			{
			ParseError(100)
			tried_the_lights = true
			}
		}	
}
I mean to eventually do a shortcuts page on Hugo by Example, but yeah, 'if word[1] = "flip" or word[1] = "switch"' can be shortened to 'if word[1] = "flip","switch"'. Very helpful for parsing conditionals.

Also, it probably wouldn't apply to your game, but I used Contains instead of "if player in room_name" since Contains is a bit more robust. "if player in room_name" doesn't work if the player is in a vehicle or a platform (like a chair) since that makes the player a grandchild of the room, not a child. Contains will return true still in that case.

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

Post by Bainespal »

Awesome! :-)

My previous code had stopped working after I changed something that I thought was unrelated. I'm grateful for this.

Post Reply