Routes through the map

So in the map with the automatically-generated exits, it turned out most convenient to map all adjacent rooms as adjacent (in acknowledgment of the fact that they are next to each other, whether you can get there or not) and then use a reciprocal relation to define which rooms are inaccessible from each other and block any attempts to go to a room inaccessible from the current location.

The problem is that there’s an NPC who needs to find his way through the map. The “best route” syntax can’t be made to work with the relation (as far as I know) because that would only work with the square where the NPC starts: I can’t make the algorithm re-evaluate which squares are now accessible at every step. (I can just have him pick a different direction if the one the algorithm chooses is inaccessible, but then he’ll get stuck in a corner.)

Any way to make the NPC run the map intelligently?

Alas.

My other option is to only use the actual compass directions for actual exits, and then define my own system of coordinates (“northish,” “southish,” or what have you) for rooms that are adjacent but inaccessible.

One problem being that you can’t define reciprocal relations between different things (see? Terribly useful function).

The more frustrating problem being that any input would be given in the form of regular directions, and I’d continually have to manually convert them to the custom directions (if the second noun is north: let the target room be the room northish of the location).

I’m not sure exactly what your end result is–do you need an alternate scheme of connections for NPCs, reflecting the fact that NPCs can walk through walls or dig tunnels or something like that? Maybe there might be an alternate approach to the problem that we could help brainstorm?

–Erik

I’m still fuzzy on what the problem actually is. Why did you change the definition of adjacent again? If it breaks pathfinding, I’d fix the adjective first and foremost.

So I have a grid map where the positions of rooms are defined by 3D coordinates (longitude, latitude, and level). Rooms with neighboring coordinates are adjacent (1,1 would be next to 1,2, for instance). If adjacent rooms are on the same level (or if there’s a ramp or similar), you can move between them. If they aren’t, you can’t, but there still needs to be a way to tell that they’re adjacent so that you can have actions like THROW GRAPPLING HOOK NORTH, which would make the room to the north accessible.

So the options are:
a) Map all neighboring rooms as adjacent, and then put in an Instead (or Check) statement blocking you from moving to inaccessible rooms (which is what I did, but there’s the NPC problem), or
b) Map only accessible rooms as adjacent, and then come up with an alternate system for mapping inaccessible neighboring rooms.

Thank you for the clarification. With this option:

…couldn’t you block NPC travel as well as player travel by using “Check an actor going” (instead of just “Check going”? If you want to give NPCs the ability to attempt to reach adjacent rooms using nonstandard means (grappling hooks, e.g.), you could use a check/instead rule of this sort to redirect to an “AI” routine to let the NPC check its options. Something like this pseudo-code:

[code]Definition: A person is an NPC if the person is not the player.

Instead of an NPC going:
[check accessibility]
if the destination is inaccessible:
try the actor grappling to the destination.[/code]

Does that make sense, or have I missed something?

–Erik

Yes, but the problem is that I need to find the best route through the map. The “best route” algorithm, of course, ignores my user-defined accessibility rules (I can’t just say “using only accessible rooms” because accessibility is defined relative to a given room, rather than in absolute terms). So the algorithm will just run him into a corner and get stuck.

I would go with option (b), since you don’t want to be reimplementing pathfinding. Finding adjacent rooms should be quite straightforward, yes? For cases like THROW GRAPPLING HOOK NORTH, you can change the map during runtime, I should think, which would automatically allow the NPC to chase you along the grappling hook. See Reliques for an example of runtime mapping, but basically:

To decide which room is adjacent to (R - a room) in direction (dir - a direction):
    [lots of math-based code using coordinates]

To throw the grappling hook (dir - a direction):
    let R be the room adjacent to the location in direction dir;
    now the room dir from the location is R.

Something like that ought to work?

Ah, sorry about that–you’d think I could have taken the time to scroll up and reread your original question. Sheesh.

Here’s another idea: You could potentially use standard map connections between accessible rooms (rooms on the same level), but place invisible doors between inaccessible neighboring rooms. That way, standard pathfinding gets your NPC to any accessible room, while “using doors” gets you to the inaccessible rooms as well. You could also have locked invisible doors if you needed a second state (testable “using even locked doors”); NPCs trying to reach inaccessible rooms could “unlock” locked doors, for example.

A when play begins rule could probably set all this up dynamically for you based on the coordinates, so that you don’t have to set up all the connections manually.

–Erik

I’m working on Zahariel’s solution; the code is as simple as this:

To decide which room is near (R - a gorge room) to the (D - a direction): let x be the latitude of R; let y be the longitude of R; if D is north: now global-x is x; now global-y is y - 1; if global-y > 0: decide on a random x-y-correct room; otherwise if D is south: now global-x is x; now global-y is y + 1; if global-y < 11: decide on a random x-y-correct room; otherwise if D is east: now global-x is x + 1; now global-y is y; if global-x < 9: decide on a random x-y-correct room; otherwise if D is west: now global-x is x - 1; now global-y is y; if global-x > 0: decide on a random x-y-correct room.

But there’s a big problem. If the room is on the edge of the map, there’s nothing in one direction. But I can’t make inform decide on nothing. Suggestions?

And why are there such limitations to when and where the “nothing” object can be used? I’ve found several situations where I wanted to use it but couldn’t.

P.S. for Erik: A good idea, except that the connections are generated automatically, and automatically placing doors would be very tricky indeed!

Try “To decide which object is near (R - a gorge room) to the (D - a direction)”–the nothing object is not a room, which I think is what triggers the run-time error.

–Erik

Is it possible to “decide on nowhere”? I think it might be, but the Index doesn’t seem to support my theory.

No, because “nowhere” is not itself a room–it’s a description of a room. “Nothing,” on the other hand, is an object, and can be decided upon safely as long as the decision’s outcome is specified as an object.

–Erik

I don’t think this is true. In Inform 6, the DM4 is quite specific in saying, “Nothing is not an object.” When I compile a test I7 game and look in the index, there is no object called “nothing,” and ‘showme nothing’ does not respond by displaying the properties of a nothing object.

Also, the line, “Understand “zilch” as nothing” won’t compile. If nothing were an object, I would expect it to compile normally.

–JA

Let’s amend what I said to “for these purposes, ‘nothing’ is of the object kind”. ‘Nowhere’ is just a description, so you can’t “decide on nowhere” any more than you can “decide on visited rooms”. Nothing, on the other hand, is a special-case value (object number 0) which Inform 7 treats as a valid object reference in certain circumstances, though it can’t be assigned properties or do many of the other things that standard objects can do; again, it is valid to “decide on nothing” if the outcome of your phrase is of the kind object. Here are some perfectly valid phrases that treat “nothing” as an object:

[code]To decide which number is the ID of (O - an object):
(- {O} -)

To decide which object is the Almighty:
decide on nothing.

When play begins:
say “The Almighty is [the Almighty]: [ID of nothing].”[/code]

–Erik

The problem with “decide on nothing” in these situations is that it’s translated into I6 in exactly the same way as “make no decision.” Which is silly, but there you go. Presumably it’s something to do with how this works in I6, which I’m not familiar with.

Take a look at how Reliques does the labyrinth. As you discovered, you can’t decide on nothing when trying to figure out what room is north of here, so instead make a special non-room (I think the Solid Rock room was used for this purpose in Reliques) and decide on that. Then you can check for it later (If the room near the location to the north is Solid Rock, say “There ain’t nothing that way, just rock!” instead.)

To a programmer (like me) this is called the “null object pattern”; it’s especially useful when you’re doing some kind of inheritance-based data structure (like say a binary-or-ternary tree, I don’t know why you’d need such a thing but you can do it if you want). You make the Empty object that replaces null at the bottom of your structure, which behaves more or less like null but understands all the behaviors you want your tree to support. That way you can get rid of a lot of null-detection code in the behaviors of the actual objects. In this case you make the Solid Rock room, which shows up whenever you ask for a room that doesn’t exist. It’s a real room for language and engine purposes, but at the game logic level it’s a fake object you can easily detect.

Well, not exactly. “Decide on nothing” yields return nothing (the nothing object) in I6, whereas “make no decision” yields return false. I guess both of those evaluate to “return 0” at some level, but not really in a way that’s meaningful to I7. “Decide on nothing” returns the nothing pseudo-object in I7, while “make no decision” is just illegal in phrases that return a value. The only real issue with returning “nothing” from a phrase to decide a value is that it needs to be of the kind “object”, so nothing will throw a run-time error when the outcome of your phrase is more specific than object. Luckily, it’s not often that the result really needs to be more specific, so widening the scope of what can be returned can be a good way to make use of the null object.

You can do this, of course, but it isn’t really necessary in the situation that the OP shared with us. If you have a decide phrase that returns a value, such as the one we’ve been talking about, which begins like this:

To decide which object is near (R - a gorge room) to the (D - a direction):

…then the result returned will either be a room (see the code as katz posted it), or the nothing pseudo-object. You can then test which was returned using either “if X is nothing” or “if X is a room”–and thus there’s no need for you to create your own null object. Now, there are situations where you might need your own null room (you want your null to have properties, or you need to return a strict room value from your phrase, for example), but on the basis of what we’ve seen so far, that isn’t necessary here.

–Erik