Inform 7 Gems – Pathfinding – 1.1

My last post on pathfinding in Inform 7, showed a simple beginning to the power of pathfinding in Inform 7.   I thought I’d expand on this slightly to show a little NPC movement that is a bit smarter than simply following the player around, as I was playing with the code, I found a bug in the code, that I thought I’d like to share the fix first before moving into new territory.

Bug fix from last post

When I fixed the code to be able to move through only open doors, I actually broke it so that if there was not a door between rooms, that the NPC would fail to follow.  So building upon the code from the previous post, we change our every turn rule to be:

Every turn:
	let course be the best route from location of the kitten to the location of the player through kitten-friendly rooms, using doors;
	if course is a direction:
		let the target be the room-or-door course from the location of the kitten;
		if target is not a door:
			try kitten going course;
		otherwise:
			 repeat with doorway running through visible doors:
				 if the other side of doorway from the location of the kitten is room course from the location of the kitten:
					if doorway is open:
						try kitten going course;
					otherwise:
						say "You hear a scratching from the other side of the door.";
	otherwise:
		if location of kitten is the location of the player:
			say "The kitten sits and looks up at you expectantly";
		else:
			say "The kitten tried to enter [the location], but thought better of it."

Here we simply add a check to see if the target of the direction the kitten is attempting to move, is a door or room.  If it’s not a door, then the kitten can simple head in that direction.  If the target is a door, then we drop into our normal code from last time where we check for the open / closed door.  The important section of code above is this:

		let the target be the room-or-door course from the location of the kitten;
		if target is not a door:
			try kitten going course;
		otherwise:

 

With that fixed, I do believe the code should work as described from last time.   Now I’ll move on to exploring pathfinding to try and make this all a bit smarter.

Extensions

I should also note that there are some extensions out there that will wrap a lot of this code up for you.  A few that I’ve looked at before include Patrollers and Planner.  And there is a newer one Problem-Solving Characters, that I want to look at a bit deeper.

So other than perhaps some simple situations we can use those instead.  I like to dig deeper into the code and know how things work, even if I use an extension….so I’ll continue to these subjects…well….because I think it’s fun!

Advertisements

Inform 7 Gems – Pathfinding

Simple pathfinding is really pretty easy in Inform 7.  A very simple example of having a NPC following the player around can be coded up pretty simply.

Kitchen is a room. The description of kitchen is "Smells of last night's dinner still linger in the air."

Family-room is a room. The printed name of family-room is "Family Room". The description of family-room is "A sole rocking chair sits facing the large screen television in the corner." Family-room is south of kitchen.

Dining-room is a room. The printed name of dining-room is "Dining room". The description of dining-room is "A table sits in the middle of the room with just two chairs on either side." The dining-room is north of the kitchen.

The den is a room. The description of den is "The den contains a student desk with a simple wooden chair." The den is west of the dining-room.

The kitten is an animal. The kitten is in the kitchen. The description of kitten is "The small fluffy kitten sits and looks expectantly at you."

Every turn:
 let course be the best route from location of the kitten to the location of the player;
 if course is a direction, try kitten going course.

Pretty simple, yet useful in a lot of situations.  The manual has a decent set of examples with character movements.

What about doors?

As it stands doors will present an issue for the pathfinding routines.  To handle doors, just change the best route line to something more like the following:

let course be the best route from location of the kitten to the location of the player, using doors;

What about closed doors?

This might be ok for a human NPC that can open doors, but for the kitten, how do we stop them from entering through closed doors?

The kitchen is a room. The description of kitchen is "Smells of last night's dinner still linger in the air."

The family-room is a room. The printed name of family-room is "Family Room". The description of family-room is "A sole rocking chair sits facing the large screen television in the corner."

The dining-room is a room. The printed name of dining-room is "Dining room". The description of dining-room is "A table sits in the middle of the room with just two chairs on either side." The dining-room is north of the kitchen.

The den is a room.  The description of den is "The den contains a student desk with a simple wooden chair." The den is west of the dining-room.

The kitten is an animal. The kitten is in the family-room. The description of kitten is "The small fluffy kitten sits and looks expectantly at you."

The kitchen-door is a door. the kitchen-door is north of family-room and south of the kitchen. The kitchen-door is closed. Understand "door" as kitchen-door. The printed name of kitchen-door is "kitchen door".

Every turn:
  let course be the best route from location of the kitten to the location of the player, using doors;
  if course is a direction:
    repeat with doorway running through visible doors:
      if the other side of doorway from the location of the kitten is room course from the location of the kitten:
        if doorway is open:
          try kitten going course;
        otherwise:
          say "You hear a scratching from the other side of the door.";

Let’s add one more tidbit to this little example.  What happens if there isn’t a best course for the kitten to get from it’s location to the player’s location?  In this case, the pathfinding returns the value nothing for the variable course.  When does this happen?  It will occur when the kitten is already in the location of the player, it can occur when there is no direct route between the player and the kitten, and it can occur when there is some criteria we put on the pathfinding that doesn’t match up (there may be other reasons as well, but I’m just going to expand on these few).

Let’s look at the first situation, when the kitten is already in the location of the player. In our path-finding routine in our Every Turn rule, we are already checking to make sure that course is a direction, which if it’s nothing, it won’t be. So let’s add a quick otherwise on that if.

if course is a direction:
 repeat with doorway running through visible doors:
   if the other side of doorway from the location of the kitten is room course from the location of the kitten:
     if doorway is open:
	try kitten going course;
     otherwise:
	say "You hear a scratching from the other side of the door.";
otherwise:
  if location of kitten is the location of the player:
    say "The kitten sits and looks up at you expectantly";

There we are simply checking locations and if they match, print a quick little message.

Now we’ll expand on that a bit with some criteria to keep the kitten out of certain rooms. Let’s say we have some kitten repellent that we’ve put in the Den. Add the following line somewhere in your code.

A room can be kitten-friendly.  A room is usually kitten-friendly.

This way we can make all rooms kitten friendly be default.

Then on the code defining the Den, let’s change it to the following to make it unfriendly to kittens.

The den is a room.  The den is not kitten-friendly.  The description of den is "The den contains a student desk with a simple wooden chair."  The den is west of the dining-room.

Then we will simply make that final otherwise in our every turn rule to read;

	otherwise:
		if location of kitten is the location of the player:
			say "The kitten sits and looks up at you expectantly";
		else:
			say "The kitten tried to enter [the location], but thought better of it."

Now we have this basis for pathfinding for an NPC, that can be expanded on greatly. There are also a few extensions you can get that encapsulates some of the more complex stuff for you, but for simple situations this is really pretty easy to put together.

Pathfinding can be used in many other situations, not just with directional relations between rooms. For example, it could be used in conversational models. I believe Glass by Emily Short is a good example of someone doing this.  I’ll be exploring some other usages that perhaps I’ll write about in the future.

I’d be interested in hearing about or seeing code for any creative usages of the built-in pathfinding functionality.

EDIT (12/22/2013):  Found a small bug in the above code that I detail out in another post.

Inform 7 Gems – Is Transcription On?

As I was poking around the forums the other day, I realized how much information is buried in the forums.  Things I might not even think to search on or are difficult to find even when looking.  And when you do find something, it’s often difficult to piece the useful information together from a long thread.

With that in mind, I’m going to start a series of posts, starting with this one, on things I’ve found in the forums or elsewhere that I find interesting or useful and try to put them in nice digestible bites.  I hope to educate myself through this process and hopefully they’ll be of use to others as well.

Are we transcripting?

My first gem was suggested to me by Andrew Schultz.  Back in 2012 he had started a thread entitled I7 – Check if transcripting is on when noting?  The basic premise behind the post was checking if transcripting is turned on.  As I talked about in my previous post on easy transcription notes, Andrew was doing something similar, but as an added bonus, wanted to check if the transcription was turned on and if so, at a minimum warn the user that it wasn’t turned on.  Very useful for beta-testing and I myself often forget to turn on transcription.

With some help from zarf and others, what we end up with is pretty useful.

Include (-
[ CheckTranscriptStatus;
#ifdef TARGET_ZCODE;
return ((0-->8) & 1);
#ifnot;
return (gg_scriptstr ~= 0);
#endif;
];
-).

To decide whether currently transcripting: (- CheckTranscriptStatus() -)

ignore-transcript-nag is a truth state that varies.

After reading a command:
	if the player's command matches the regular expression "^\*.+":
		if currently transcripting:
			say "Noted.";
		otherwise:
			if ignore-transcript-nag is false:
				say "You've made a comment-style command, but Transcript is off. Type TRANSCRIPT to turn it on, if you wish to make notes.[paragraph break]The long version of this nag will only appear once. You may press any key to continue.";
				wait for any key;
				now ignore-transcript-nag is true;
			else:
				say "(Comment not sent to transcript.)";
		reject the player's command.

What we have is some I6 code that is checking a flag (different depending on zcode vs glulx) that when the user enters a note command, it checks to see if transcription is on and warns the user if it is not.

We can even take it one step further and add this line of code if it is not on:

try switching the story transcript on;

This will prompt the user to turn on transcription and should work in either zcode or glulx.

I seeing more and more that I really need to dive into I6 more to see the possibilities that I may be missing.

Edit: changed the regular expression to work around the I7 bug mentioned in the comments.

 

December 2013 IF Update

I can’t hardly believe it’s December already.  November was a busy month for me.  Work was really keeping me busy and I don’t see that slowing down a whole lot.   Not as much time on personal project that I would have liked to have spent, but I did manage to make some progress.

At the beginning of the month, having just submitted my work (Jack) to EctoComp, I was anxiously awaiting feedback.   I had some say it was actually a decent work considering the time limit.  Despite perhaps some grammatical issues, it was fairly well implemented for a first release.  Others were turned off by the overt violence that begins to happen half-way through the game and by the lack of an ending.  So I received a mixed bag of feedback, but it was all good advice that I was able to internalize and use to make a future release better and to consider in future works.

It also gave me an incentive to start on expanding and making the work better.  I’ve gone into some of this in my postmortem that I did on the game, but I’ve spent part of the month working on finishing it up and fleshing out the back-story more.  It actually evolved beyond what my initial ideas were so it’s going to take a bit longer to finish it up for a post-comp release.  Also some of you that didn’t like the violence, well….I’m trying to make alternatives paths through the game that are less violent, but the back-story has taken a turn and become a bit dark.  It seems to work in my head, but we shall see how others like it.  Maybe by the end of the year, I’ll put out a release.

Another thing that has begun to take my attention away from Jack, is the New Years comp that was just announced.  I’ve got a few ideas for that, that we’ll see if I can expand into something doable by it’s deadline.

And of course, there’s also the Spring Thing, which I really wanted to to enter, but I’ve not got much done, so I’m not sure I’ll meet that deadline.  I’ve got one work that is about 50% done, but just not sure if I’ll find the time (or want to make the time) to finish that up.

I must say that over the last few months that I’ve gotten back into the IF scene, I’ve met some great people that have helped me along quite a bit.  I know that community support is great and everyone for the most part, whether they criticize or praise your work, they do so in the effort to make it better.

I’m hoping to be able to give back to the community in some way through this blog (as well as hopefully releasing some new games).    I’ve got a few ideas for some series of articles here, that I think might benefit the programming community (at least in regards to Inform 7  and game design) at large as well as myself.  I might kick the idea around with a few individuals first to see if it’s something that would be helpful or not before going into any great detail here.

Well, that was my November and hoping for a productive December.  So go north my friends!

Postmortem on my EctoComp Entry – Jack

small coverIn my real life job as a software developer and manager, I like to do postmortem’s on my projects so I know what I did well (so I can do more of that next time) and what I didn’t do so well (so we can make adjustments for the next project).  I thought this would be handy for my IF works as well.

My first released work was for the EctoComp 2013 competition titled Jack.  I came in 16th out of 24 with an average score of 5.14 (out of 10).  While perhaps not as high as I had hoped going into it, having played the games above me, I feel it was about where my game should have been.  So I’m happy.

Being a speed IF competition, the rules allowed for 3 hours of work (actual coding / writing) and no more.  It also, while not a strict requirement, had a specific theme to follow…something Halloween.

What I did poorly.

  1. I didn’t spend enough time prepping.  One caveat too the 3 hour rule is that we could spend as much time prepping the story beforehand.  As long as no work was done on the computer, no coding nor any writing of dialog in a text editor or anything.  I spent too little time here.  I came up with the idea the night before the deadline and spent an hour or so, laying out a brief story line and a smallish map.  When I started coding the next day, I found myself spending too much time rewriting text and changing implementations of puzzles and actions as I just hadn’t thought it through enough.

    There are many different ideas on planning, Emily Short has a nice article on  her thoughts, but I feel I work better if I have a concrete plan in place first, not that I’m stuck with that plan, but it’s a good guide to work with.  Without that, I can usually work pretty well for a portion of my story, but there comes a time when I run into road blocks.  I have plenty of unfinished / unreleased works that fell in this trap and I find it harder to go back after the fact and fix story or implementation issues.

    Takeaway:  Spend more time in the planning phase. Make sure the story makes sense, make sure there is a clear path from start to finish, put the puzzles (if any) in place in the design phase and know how they work, and have in front of me a blueprint and plan of what I need to work on.  This might sound methodical and not very creative, but the creativeness goes in the planning stage and now we are working on writing code.

  2. I was too ambitious.  Not that ambition is a bad thing, but for a speed IF that only gave me three hours to code, I planned too much for my game.  The little planning that I did do for this game, created a map of about 6 rooms.  Not overly large, but because of the time restraint, each room was necessarily sparse.  Also the atmospheric text suffered some.  While I think I did a reasonable job of providing a decent setting, the rooms were not done to my normal level of completeness.  Room descriptions were limited, a few items mentioned were not implemented and the game became very linear.  One other problem I ran into and probably the biggest problem with my game, was the ending.  I was not happy with it at all.  When I was nearing the end, so was the clock.  With little more than about 15 minutes left to go, I still had two rooms to implement to get to the ending that I was trying to setup.  I quickly wrapped those up with nothing more than some quick descriptions and an end-game that just required you to get to the end location.  The ending text also hinted at the story that I was trying to create, but left the reader hanging horribly.

    Takeaway:
      Know the limitations you are working under, whether a time constraint, game play constraint or perhaps theme restrictions.  Plan accordingly.  I would have rather restricted my map to just a room or two, yet made them much more descriptive and interactive.
  3. Not enough testing.  This again, was a problem with the time constraints, but still I could have done better here.  As I started to get feedback, I was dismayed with the amount of spelling errors, grammatical issues and unimplemented (unintentionally) verbs, actions and items.  While I don’t believe there were any major bugs that made the game unwinnable as it exists for the competition, they definitely took away from the immersion into the story.

    In this competition, any time beta testing was included in the 3 hour limit, so there was little I could do.  I did have my son run through it once before I submitted, but he’s not an IF player and found little.  My wife ran through after I had submitted and found a few issues, but those will wait until a second release after the competition is over.  Not sure how much I could have changed with that…but if I take into account my first two points here, then I would have more time for testing.  It did point out the strong need for testing however….when I submitted it, I truly felt it was pretty good with very few problems…and while it was generally OK, and I got some decent remarks, I was still surprised with some of the basic things I missed.

    Takeaway:  If time permits, make sure there is plenty of beta-testing by multiple people if possible.  No matter how good I think it may be, I have to remember I’ve not caught everything.

What I did well.

  1. Made sure there was a path from start to finish.  I wanted to be sure that the game couldn’t get into an unwinnable state.  I made every effort to ensure that even if it stayed very linear, there would be no way you could get stuck.  Sure you could die in this game, but you always quickly did so and could recover from it or make an obvious change in direction next time.  Dying in the game, was actually critical to the story as my intention was to have it help to explain the story a bit each time you died.  I may not have achieved that entirely in this initial version, but the groundwork has been laid.
  2. Make sure the implementation is solid.  While there are some implementation issues, most are relatively harmless in the sense that it didn’t affect the gameplay.  There is some color text lacking and some scenery items lacking decent descriptions.  There are also some verbs that were not implemented, but they did tend for the most part to be secondary ones that yes add flavor, but not necessary.

    I think I was fairly successful in keeping from causing and “guess the verb” issues.  I did that by keeping my vocabulary simple and make sure that anything that was a bit out of the ordinary, was well hinted at.  Based on a few transcripts and comments I received, I think I did fairly well with that also.

  3. Began work on an update right away.  As soon as I submitted the game to the organizer, I began making updates for the post-comp release.  I knew of some things that needed implemented that I hadn’t because I ran out of time.  I went ahead and fixed those.  As I began to received feedback and reviews with suggestions, I began to implement those as well.  I am also planning for an expansion on the story with multiple solutions to puzzles and paths through the game, based on feedback and criticism received.

    I think it was important to start right away while my enthusiasm was high and everything was still fresh in my mind.  I hope to release a version soon after the competition is over that fixes most of the major concerns, with perhaps a third release later that expands on the game in a number of ways.   I hope to find beta-testers for these later releases so I can catch more problems before their released.

Overall, I was happy with the release and the results.  It got me over the hump of actually releasing my work which I hope makes releasing future games easier.  I’m looking forward to seeing some of the comments from the scoring sheets as I’m sure I’ll be able to glean a lot of good information for future games.