intfiction.org

The Interactive Fiction Community Forum
It is currently Wed May 22, 2013 7:15 am

All times are UTC - 6 hours [ DST ]




Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: T3: Generating Actions
PostPosted: Sat Jun 04, 2011 6:11 pm 
Offline

Joined: Sun Mar 01, 2009 8:02 pm
Posts: 902
I'm attempting to implement an Inform-style 'test me' action, and I can't seem to figure out how to use nestedAction or nestedActorAction without causing a run-time error. Here's my code:

Code:
DefineIAction (X1)
    execAction() {
        trawlerItself.turnCount = 13; // this line works fine...
        nestedActorAction(me, TravelVia, trawlerDeck.west); // ...but this one causes an error
        nestedActorAction(me, Take, tiedRope);
        "Advancing.... ";
    }
;
VerbRule(X1)
    'x1'
    : X1Action
    verbPhrase = 'advance/advancing the game'
;

I get a nil object reference in the constructor for this class in parser.t, on the line shown:

Code:
class PreResolvedProd: BasicProd
    construct(obj)
    {
        /* if it's not a collection, we need to make it a list */
        if (!obj.ofKind(Collection))

The same thing happens if I replace nestedActorAction with nestedAction (and get rid of "me, " in the parameters). I don't think the syntax for my nestedActions can be wrong, because the same syntax works in another part of the game.

To make matters more interesting, I'm not sure how to use Workbench's Watch Expressions window to troubleshoot this, since I would need to step backward through the code in order to find out what method is trying to construct a new PreResolvedProd (with a nil object). I don't know how to do that.

There has to be a way to build a list of actions that can be generated automatically from an in-game debugging command. Maybe I'm doing it all wrong. Suggestions welcome....


Top
 Profile Send private message  
 
PostPosted: Sat Jun 04, 2011 6:22 pm 
Offline

Joined: Sun Mar 01, 2009 8:02 pm
Posts: 902
On further reflection, I can see the problem. The problem is that the end-of-turn rules (I7 term, but you know what I mean) are not being run. The nestedActions are being attempted in a context where trawlerDeck.west is still nil.

What I need, then, is a programmatic way to run the end-of-turn rules before each of my nestedActions, so as to arrange the model world appropriately (and generate output) before the next nestedAction is generated. Is there a way to do that?


Top
 Profile Send private message  
 
PostPosted: Sat Jun 04, 2011 8:23 pm 
Offline

Joined: Tue Apr 27, 2010 1:02 pm
Posts: 797
I would probably tackle this using input scripts instead.

It might be nice to have an extension that implemented a "test me" equivalent by letting you define an object X with a list of inputs, writing those inputs to a file on startup, then replaying that file when the "test X" command was issued.

Workbench may provide that sort of thing already but I don't use Workbench, so the above is how I'd go about it. I'll see what I can put together.


Top
 Profile Send private message  
 
PostPosted: Sat Jun 04, 2011 8:49 pm 
Offline

Joined: Sun Mar 01, 2009 8:02 pm
Posts: 902
Replaying an event script works -- but it requires that the tester (namely, me) find the script file in a dialog box. This is slow and annoying. Once I've defined a script, it shows up in the Workbench Scripts window -- but right-clicking on a file in this window and choosing Replay causes the game to start afresh, which is NOT what's wanted for this type of testing command.

I'm seeking a command structure (a) that won't require me to navigate a file dialog -- that I can type as a single command in the game window -- and (b) that can be run at a later point in the game -- perhaps after I've reached the wicked witch's hut and want to try out the 13 steps required to mix a potion.


Top
 Profile Send private message  
 
PostPosted: Sat Jun 04, 2011 9:01 pm 
Offline
User avatar

Joined: Tue Apr 20, 2010 2:48 pm
Posts: 687
Doesn't setScriptFile() pretty much offer exactly what you want?


Top
 Profile Send private message  
 
PostPosted: Sat Jun 04, 2011 11:30 pm 
Offline

Joined: Sun Mar 01, 2009 8:02 pm
Posts: 902
RealNC wrote:
Doesn't setScriptFile() pretty much offer exactly what you want?

It does indeed. The trick is figuring out where to put the command script file. I tried putting it in the Scripts folder -- no go. I tried putting it in the folder with the interpreter app -- no go.

Turns out, the place to put the script is in the same folder with your source files. That works.

This method is still a little more roundabout than the I7 equivalent, but it's not too bad. You can create (and edit as needed) a number of testing scripts and invoke them from within the game. For each script, you need either to define a separate IAction or to create a new Topic object for a 'test' TAction to access. Or else keep editing the 'test me' code before compiling so it will load whatever script you need.

Probably there's a way to use a LiteralTAction, grab the string ('test elixir' giving you the string 'elixir') and then tuck the string into the argument to setScriptFile(). I'll have to try that.


Top
 Profile Send private message  
 
PostPosted: Sat Jun 04, 2011 11:46 pm 
Offline

Joined: Sun Mar 01, 2009 8:02 pm
Posts: 902
This is probably cool enough to qualify as a tip:

Code:
DefineLiteralAction(Test)
    execAction() {
        local str = getLiteral() + '.cmd';
        setScriptFile(str);
    }
;
VerbRule(Test)
    'test' singleLiteral
    : TestAction
    verbPhrase = 'test/testing (what)'
;


Just create a test .cmd file with whatever series of commands you like, of the form:

Quote:
>s
>s
>in
>drink potion


...then name it something like 'potion.cmd' and save it to the same directory as your source files. Now the command 'test potion' in the game will run that series of commands.

The advantage of this method over the Scripts window, as already noted, is that the command script doesn't have to start at the beginning of the game. The advantage over the 'replay' command is that it doesn't open a dialog box.

Grooviness!


Top
 Profile Send private message  
 
PostPosted: Sun Jun 05, 2011 2:03 am 
Offline

Joined: Tue Apr 27, 2010 1:02 pm
Posts: 797
Here's what I came up with, based on your code and my earlier ruminations.

Code:
DefineLiteralAction(Test)
   execAction()
   {
      local target = getLiteral().toLower();
      local script = allTests.valWhich({x: x.testName.toLower == target});
      if (script)
         script.run();
      else
         "Test sequence not found. ";
   }
;

VerbRule(Test)
   'test' singleLiteral
   : TestAction
   verbPhrase = 'test/testing (what)'
;

class Test: object
   testName = 'nil'
   testList = [ 'z' ]
   testFile = 'TEST_' + testName + '.TCMD'

   run
   {
      "Testing sequence: \"<<testName>>\". ";
      local out = File.openTextFile(testFile, FileAccessWrite);
      testList.forEach({x: out.writeFile('>' + x + '\n')});
      out.closeFile();
      setScriptFile(testFile);
   }
;

allTests: object
   lst()
   {
      if (lst_ == nil)
         initLst();
      return lst_;
   }

   initLst()
   {
      lst_ = new Vector(50);
      local obj = firstObj();
      while (obj != nil)
      {
         if(obj.ofKind(Test))
            lst_.append(obj);
         obj = nextObj(obj);
      }
      lst_ = lst_.toList();
   }

   valWhich(cond)
   {
      if (lst_ == nil)
         initLst();
      return lst_.valWhich(cond);
   }

   lst_ = nil
;


Then you can define new tests like so:

Code:
foo: Test
    testName = 'foo'
    testList =
    [
        'x me',
        'i'
    ]
;

bar: Test
    testName = 'bar'
    testList =
    [
        'look',
        'listen'
    ]
;

all: Test
    testName = 'all'
    testList =
    [
        'test foo',
        'test bar'
    ]
;


Top
 Profile Send private message  
 
PostPosted: Sun Jun 05, 2011 5:59 am 
Offline

Joined: Thu Jan 28, 2010 3:57 am
Posts: 136
This looks really useful, so I've packaged it up as an extension for my own use, with one addition:

Code:
DefineSystemAction(ListTests)
    execSystemAction
    {

        if(allTests.lst.length == 0)
        {
            reportFailure('There are no test scripts defined in this game. ');
            exit;
        }
       
        foreach(local testObj in allTests.lst)
        {
            "<<testObj.testName>>";
            if(gAction.fully)               
            {
                ": ";
                foreach(local txt in testObj.testList)
                    "<<txt>>/";
            }
            "\n";
        }
    }
    fully = nil
;

VerbRule(ListTests)
    ('list' | 'l') 'tests' (| 'fully' -> fully)
    : ListTestsAction
    verbPhrase = 'list/listing test scripts'
;


This might be useful if you've set up a number of test scripts and need reminding of what they're called and what they do while testing. The command LIST TESTS simply lists the names of the test script while the command LIST TESTS FULLY additionally supplies the list of commands that each test script invokes.

-- Eric


Top
 Profile Send private message  
 
PostPosted: Sun Jun 05, 2011 12:29 pm 
Offline

Joined: Sun Mar 01, 2009 8:02 pm
Posts: 902
Brilliant! It's still a bit more fussy for the author to set up than in I7 (yes, I was re-reading your comparison article on Brass Lantern yesterday, Eric), but it neatly eliminates the need to have separate files on disk for each test script.

I hope you'll upload this extension to the Archive.


Top
 Profile Send private message  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group