Room & Dimension Version 2 (Need betatesters)

Yeah, that’s it–I was misled by the name into thinking I’d seen something in the source that I hadn’t! Thanks.
(Also, probably, the language of the docs, which revers to “avatars”–usually thought of as representations of people.)

I was thinking of adding a phrase option to the built-in phrases to constrain the output to cardinal directions, but since the author is already handling movement and everything on his own, maybe it would be better to just include constraint to cardinal directions in one of the examples–which you already have done! But an AI constrained to 4 directions might be a helpful illustration.

I don’t recall if I’ve already asked for this once or if was just on some now-lost list of mine: AI step routines return a vector (e.g., {1, 1, 0}) describing a direction. Is there a phrase for translating this into a coordinate? Ideally, I’d like to be able to pass a multiplier along with the vector and get back a coordinate on my map. (The multiplier being speed–if my doggy can do 2 cells per turn, then he should move +2x and +2y…)

Here are a few ideas for optimization:

Declaration of maps versus dynamic generation

[spoiler]Keep moving ahead with options to specify full maps declaratively rather than procedurally (i.e., a map dump option, plus the human-readable input Basti has been working on). This won’t help a game with randomized maps, but it will help with predefined maps, especially with larger maps, bigger than 20x20 or so, I guess. (The downside to this is that doing this with large maps requires the user to set very high values for MAX_STATIC_DATA and MAX_ARRAYS.) This code seems to produce all of the data that the map generating activity instantiates:

[code]File of Map is called “mapdump”.

To dump map of (R - an m_room):
write “The map-tiles of [R] are [map-tiles of R in brace notation].[paragraph break]” to the file of map;
append “The map-effects of [R] are [map-effects of R in brace notation].[paragraph break]” to the file of map;
append “The map-graphics of [R] are [map-graphics of R in brace notation].[paragraph break]” to the file of map;
append “The height-storage of [R] is [height-storage of R in brace notation].[paragraph break]” to the file of map;
append “The map-objects of [R] are [map-objects of R in brace notation].” to the file of map;
say “Map data dumped.”[/code][/spoiler]
Map-graphics (and other?) list-storage

[spoiler]Copying lists takes a long time. Take a look at the following comparison. The first is the time it takes to copy a single value from a list (very little). It takes about 25 times as long to copy a list, even one containing only a single value (see trials 2 and 3). Copying each of the values out of the list in turn is less expensive (see the fourth test), but they would need to be either employed right away or stored–individually–in global scope for this to be a useful optimization.

Is there anywhere that you can avoid copying lists? I was thinking of the map-graphics property of m_rooms when I started investigating this (it’s a list of lists of lists of lists of lists of numbers). This property stores a long list (or multiple) for each cell of the map, e.g. The map-graphics of Dungeon are {{{{{0, 1, 1, 102, 0, 0, 1}}, {{0, 1, 2, 102, 0, 0, 1}}}}} for a two-cell dungeon. The first three numbers in each list are the coordinates, while the fourth is the actual graphic reference, and the last three are…I don’t know what. This method of storage seems very wasteful to me, but I’m not sure of all the reasons for it; maybe it is less wasteful than it seems. If this were optimizable, it would speed up everything from map generation, to adding/moving actors to the map, to display/printing. Simplification might mean losing some features…? Again, I’m not sure of all the parameters.

But this probably isn’t the only means of storage that could be improved by some rethinking. Currently, all of the environmental effects associated with a cell of the map are stored in a complex list associated with the map. Some of this information is redundant, since environmental effects are also stored in the tile object, which is already stored in the map-tiles m_room property. Extracting and storing this information redundantly definitely slows down map generation. The degree to which it slows down other function is up in the air, I guess.[/spoiler]
Environmental effects

[spoiler]An idea, partially about optimization and partially about features/usability: What about disallowing environmental effects from being added directly to the map? i.e. all ee’s have to be associated either with a tile type or with a mappable object. (Currently, ee’s are not associated with objects/m_actors, but if you allowed them to be, that would also solve the problem of the “aura system” you were talking about adding.)

You could potentially get rid of the map-effects list property entirely if you used this method, potentially speeding up both map generation and ee-picking. Checking whether an effect should be triggered for a given location would then mean looking at the tile and the map actors at that location and then checking their associated effects. With no free-floating ee’s, you might be able to use relations rather than lists to associate effects with objects (tiles and mappable things/people)–this might be faster than copying and sorting lists, though I haven’t investigated the question…

Having all effects associated with either a tile or an object would, I think, have the added benefit of making the environmental effects system easier for users to work with. For example, the environmental effect would be extended over multiple cells automatically if the mappable object were so extended–no need to do it manually.

Edited to add: Actually, after spending some more time thinking about this, I think the system for ee’s that you’ve created is actually the best, even assuming that it is slower than limiting the feature set could make it. I was thinking in terms of strictly “environmental” effects, but your current design can be used for things that have nothing to do with the tiles used, such as text triggers or AI zones. Having to create new tiles every time you wanted to do those kind of things would be a real burden. So this makes another suggestion that I think you should ignore. Wish PhpBB had strikethrough![/spoiler]

You know, though, while we’ve been talking a lot about optimization, maybe your best option for now is to get R&D to a place where it’s feature-complete and just release it. If you stick with the room-based focus of the title–i.e., keep maps organized by m_rooms as currently–the slowness of larger maps will be less of a problem, and people will be less likely to try doing the larger-scale maps that R&D isn’t good at yet. (The room focus has great advantages in terms of managing scope, for example, and is actually good design!) I’m on a 5-year-old machine, and R&D does pretty darn well with a small map! You might also gain some speed with no extra work if BlkSize is optimized, as mentioned by EmacsUser in the lists thread.

Basically, I don’t want you to think that just because I’m raising these issues, that I think they are fatal. Within a certain scope, R&D is quite performant, and managing expectations may be easier than rethinking huge swaths of your design. When I7 adds arrays, maybe, you could do some benchmarking, take stock of things, and see whether you could create a faster extension using those or not. :slight_smile:

Found a bug. One of the “to move” phrases doesn’t pass on its failure result as it should. Here’s the fixed code:

To move (t - a mappable thing) on sector (X - a number) and (Y - a number) in (r - a m_room): move t on sector X and Y and 1 in r; if rule failed, rule fails;[### added ET 1/22/2012] rule succeeds.

I also wrote some code to move a mappable thing in its facing direction with a certain speed, which you’re free to use or adapt if you like:

[spoiler][code]To move (item - a mappable thing) in its facing-direction with speed of (mult - a number):
move item on sector (x-coord of item + (x-facing of face_dir of m_actor of item * mult)) and (y-coord of item + (y-facing of face_dir of m_actor of item * mult)) in location of item;
if rule failed, rule fails;
rule succeeds.

To decide what number is x-facing of (d - a face_dir):
If d is:
– face_nw: decide on -1;
– face_ne: decide on 1;
– face_e: decide on 1;
– face_se: decide on 1;
– face_sw: decide on -1;
– face_w: decide on -1;
decide on 0.

To decide what number is y-facing of (d - a face_dir):
If d is:
– face_nw: decide on -1;
– face_n: decide on -1;
– face_ne: decide on -1;
– face_se: decide on 1;
– face_s: decide on 1;
– face_sw: decide on 1;
decide on 0.[/code][/spoiler]Edited to add: I guess, though, that controlling speed from the phrase is a bad idea, since effectively we’re skipping one cell when we test for collisions. A higher level control mechanism would be needed for speed. So, forget the speed multiplier!

One other little “buglet”:

“placable” in the extension code is misspelled, should be “placeable”. Placable means something else, and I can see potential namespace clash with someone writing NPC or AI and wanting to use “placable or implacable” as a binary for their characters…

I also have a couple of user interface/design questions. These are based on my actually coming back to the code after a couple of days off, unlike the stuff I posted earlier today, so they are hopefully more cogent. Here goes:

  1. What is the reason for the existence of the m_res/m_figure class? Would it be possible for all of the properties associated with m_figure to be defined as part of the associated thing itself? Most of the phrases that authors use actually target the thing rather than the m_figure anyway, so R&D makes a definite assumption that there won’t be anything mapped that doesn’t actually have a thing object representing it in the gamespace. There are enough times, though, where the author does need to address the m_figure directly that it makes thing a bit confusing–I find myself asking, OK, which entity am I targeting and why? Could m_figures be done away with?

  2. Analogously, is there a reason why facing directions are their own kind of value? Couldn’t you just use directions instead? (E.g., “A thing has a direction called the facing-direction”?)

Probably there’s something I’m missing in both cases, but it can never hurt to ask (I hope)!

Oh, and for fun. Animation. Move with the arrow keys, type “a” to shoot an arrow.

dl.dropbox.com/u/947038/R%26D%20Testing.gblorb

Source code for the animation (to demonstrate that it’s easy-peasy!):

[spoiler][code]Shooting is an action applying to nothing. Understand “a” or “shoot” as shooting.

Instead of shooting:
say “You loose an arrow.”;
spawn the arrow on sector (x-coord of Bowman) and (y-coord of Bowman) in dungeon;
map spin the arrow towards the face_dir of the Bowman;
animate the missle flying as a custom animation at 100 fps, cycling;
delay input until all animations are complete.

Missle flying is an animation track.

Animation rule for missle flying:
move arrow in its facing-direction with speed of 1;
if rule failed:
completely remove the arrow from play;
say “Your arrow shatters against the wall!”;
cease animating the missle flying.[/code][/spoiler]

Basti, by now you’re probably anxiously awaiting my retirement from the scene, but I have another question…

I’m thinking about trying to add an illumination map to the standard top-down display. Basically, this would be a representation of the grid with 1’s where tiledata should print and 0’s where it shouldn’t. (The 0 bit would prevent both static and nonstatic graphics from printing.) The idea is twofold:

  • This should allow non-rectangular room plans to print faster. The display rule would just look at the illumination map, see a 0, and skip immediately to the next cell without printing anything. This should definitely improve speed for a map like this:

1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0

  • With a little work, the illumination map could also respond to m_actor positions–i.e., allowing for light only around light sources. Classically, this would mean a halo of light around the player as he moves, but could also light up the area around other characters, or just around light sources in the environment.

Having the ability to do something like 1 could be nice from a performance perspective, and it could be done without an illumination map. A “null” tile would do it, for example–when the display rule sees a 0 for the tile graphic in a cell (or just a null tilecell object in the map-objects list), it just skips printing both static and nonstatic images for that cell. But regardless of whether the illumination map were overlaid on a more basic solution or not, it would be nice to have a map-based definition of darkness.

So, I assume this is possible given R&D’s drawing system. Just taking a very quick look at the source, there seem to be a few places where one could intervene. What would the best place be to filter?

Quiet the contrary, I’m just flabbergasted about how someone can keep up such a consistent flow of constructive criticism over such a long period of time. You don’t see that everyday, exactly :slight_smile:

But first things first though.

That’s a flaw in my spontaneous design, I guess. I will probably change it in the next iteration so that actual coordinates are stored instead of directions. That way, jumping from one step to the next should be less of a hassle. Added your code too, might be handy.

Added in a not for release section.

What exactly do you understand under “copying”? ‘Let x be y’ or ‘Add x to y’? The copying is necessary because of the sorting algorithm. Looks like the one-by-one copying would provide a little boost at least. (The last three numbers btw are for extended width/height plus a reference number, so the program knows what table to use when choosing a translation table)

I don’t like the idea one bit. Separating objects from the effects was an intentional move so that you wouldn’t have to worry about Inform’s limitation that it can’t create/destroy objects during runtime (no easy way that is). It does make sense in some cases, like effects which fade over time (my example would be a gas grenade). Anything else would be tedious since I would have to create the effect and an object carrying that effect, which can only end in a confused mess at some point. That every map object has only one ‘body’, e.g. that you have to create two lakes if you want two separate instances of a water effect on a world map can only further complicate things… Whoopsie, sorry, read your edit after my rambling :smiley:

Bug? What bug? :stuck_out_tongue:
Fixed the misspelling too while I was at it. Good catch!

Gotta run again, sorry, but I will get back to the rest of your stuff tonight (as well as hopefully getting the activity buffers done, finally :confused:) It would be great if you could evaluate a bit more on what you did during the one-by-one copying, at least when you find the time. We might be able to squeeze a second or two out of the algorithm after all.

Well, some of it’s constructive, anyway. Some of it’s been pretty muddled!

I do think that it is good to have phrases to return both directions and coordinates. The directional vector is useful if the author is constraining his NPCs to certain directions (i.e., cardinal directions), as well as for any other manipulation the author might want to do (mess with one coordinate or the other to represent drunkenness, for example).

By the way, my code should have a loop in it, otherwise it will be able to skip over obstacles due to lack of collision checking:

To move (item - a mappable thing) toward (orientation - face_dir) with speed of (mult - a number): repeat with count running from 1 to mult: move item on sector (x-coord of item + (x-facing of orientation)) and (y-coord of item + (y-facing of orientation)) in location of item; if rule failed, rule fails; rule succeeds.
Of course, authors might want to handle speed their own way, too–e.g., by animating each step rather than just updating the display at the end. But that’s for them to design!

Great! Is import-from-a-file fully functional now?

My test code used “let x be y”. But “add x to y” would also make a copy. Basically, if you can draw the individual simple values you need from inside the list w/o creating a new list, you will save some time (at least, that’s what those tests suggest). For example, given this list {0, 1, 1, 102, 0, 0, 1}, if you pick each simple value out individually, and avoid creating as many new lists as possible, you could save 50% or more over a method that actually copies the list wholesale before doing anything with it. This may or may not help with your actual code–I haven’t checked–but it seems possible that it might.

Combined with the null tile value idea from my earlier post, there could be significant savings on some maps–you read the tile graphic number first, before doing anything else; if it’s the null tile, you immediately stop, and there is no need to read or do anything else with any of the other values.

Better late than never:

It’s kinda of a compromise. A m_figure holds quiet a lot of data and I didn’t wanted to add all those to every object in the game but I didn’t wanted to create another kind of thing, which would render all the standard rules for things useless. Using a dummy instance of an attachable component seemed like a sensible solution and it still does.

This should become obsolete, once it becomes easier to define new directions, as it should be the case with the new release, if I caught that correctly.

I would probably use a technique from one of R&D’s older instances where, for every row, the map would store number of borders and other tilecells. Let’s say you got a row with three border cells, five floor cells, two border cells and then again one floor cell. That would translate into the list {3, 8, 10, 11}. You can save yourself some progressing time with that. For lightning, however, there is probably no way around your suggestion. Best place to include this code would probably be an independent phrase during the map generating activity but I would need some time to think about that.

If you mean that you now can add walls and borders then yes. I’m not entirely happy with the choice of letters (with ‘+’ representing walls and ‘#’ for borders) but it’s a start.

Still can’t think of a good way to not copy the graphics because of the missing sorting algorithm but at least adding the numbers individually improved things a bit although it’s far from mind blowing. :frowning:

Please do look at standardizing the interface, then. Currently, it is very annoying as a user to have to pass the m_figure into some phrases while passing the thing itself into others–I went through a bunch more of this with AI code last night, and believe me I’m not raising the annoyance factor as a theoretical issue! The move and spawn and other phrases all have you passing in the thing, but the AI and maybe a few other phrases only accept the m-figure.

[rant]To explain my thinking about the splitting of m-figures from their things: The m_figure + thing design favors the author who wants to use R&D for one tiny thing (just one room, for example, or to implement a tic-tac-toe board) over the user who actually want to dimension all of his rooms. The latter user not only has to use more resources than he needs (two things for every one he actually wants), he also has to set up and manage twice the number of objects. Yet it’s the latter user that is more likely to actually persevere to learn the complexities of R&D, while the former will probably decide that they don’t need that whatsit anyhow and move on. Maybe this isn’t a big deal in the end, but I feel like the current design is punishing the wrong kind of user.[/rant]

Sorry, I must not have expressed myself well before. My question was, Why there is a need for new directions at all? The new facing directions are really just the standard 8 directions with different names (e.g., face_n vs north). But I can’t think of any reason why I would need facing_dirs when I already have the directions…? It seems like just another thing to have to convert. In brief, why not just use “north” instead of “face_n” when doing facing? Is there something I’m not considering?

That might be useful (assuming it’s automatic and totally hidden from the user). Unlike a null tile, though, it would actually slow things down if you had a complex map with a lot of features (e.g., a textured or tessellated floor, wall features, etc.)

Well, I’m thinking of dynamic lighting–e.g., the light moves when the actor moves–so I’m not sure that it could actually be implemented during the map generating activity. The feature could potentially be limited to dynamic objects so it wouldn’t need any map generation code, or there could be just a couple of phrases for use in map generation that allowed for rectangles of light or dark to be painted into the light map itself.

So you’re hard-coding the symbols? Wouldn’t it be easier on you and more flexible for users if it used a translation table? E.g., the map below could use this table (spoilered):

Table of Map Conversions symbol tile "+" Block Corner "-" Wall Front "*" Wall Torch "|" Vertical Wall "." Floor "&" Lit Floor

+--*---*--+ |.&&&.&&&.| |.........|
That lets the user make all the choices, and is a lot easier to program! (You could put symbols for the default floor, border, wall tiles in the initial table, and the user would be free to use those, or he could overwrite the table and use his own.)

AI Stuff

[spoiler]The user currently has to do lots of list manipulation to use the AI. For example, to use the simple step phrase to have one character approach an object or another figure, you have to 1) Create a new list; 2) Fill that list with the coordinates of the target object, 3) Pass that list into the phrase. It would be faster and easier for the user to be able to pass in bare numbers, or just pass in a mappable thing for the target as well as the thing doing the stepping, e.g.:

To decide which list of numbers is simple step for (f - a mappable thing) towards (xx - a number) and (yy - a number) and (zz - a number): To decide which list of numbers is simple step for (f - a mappable thing) towards (target - a number):

It would also be nice to have a phrase that would allow any actor to face any other regardless of how far away. I wrote some code to do that (it could be generalized for more purposes as well):

[code]To decide which face_dir is the orientation of (agent - something) to (target - something):
let dx be x-coord of target - x-coord of agent;
let dy be y-coord of target - y-coord of agent;
if dx is 0 and dy is 0,
let angle be a real number;
if dx > 0:
if dy > 0:
let angle be the arctangent of ( dy // (dx as a fixed point number ) );
if dy < 0:
let angle be 0.0000 – arctangent of ( (0.0000 – dy) // dx as a fixed point number) ;
if dx < 0:
if dy > 0:
let angle be 180.0000 – arctangent of ( (0.0000 – dy) // dx as a fixed point number) ;
if dy is 0:
let angle be 180.0000;
if dy < 0:
let angle be the ( arctangent of ( dy // (dx as a fixed point number ) ) ) – 180.0000;
if dx is 0:
if dy > 0:
let angle be 90.0000;
else if dy < 0:
let angle be -90.0000;
decide on the face-direction corresponding to (angle as an integer).

To decide which face_dir is the face-direction corresponding to (theta - a number):
let theta be theta rounded to the nearest 45;
if theta is:
– 0: decide on face_e;
– 45: decide on face_se;
– 90: decide on face_s;
– 135: decide on face_sw;
– 180: decide on face_w;
– 270: decide on face_n;
– -90: decide on face_n;
– -45: decide on face_ne;
– -135: decide on face_nw;
– -180: decide on face_w.

[Inform’s built-in rounding is broken for negative numbers, we use this instead]
To decide which number is (X - number) rounded to the nearest (Y - number):
(- (RoundOffValue({X},{Y})) -).

Include (-
[ RoundOffValue t1 t2;
if (t1 >= 0) return ((t1+t2/2)/t2)*t2;
t1 = 0 - t1;
return -((t1+t2/2)/t2)*t2;
]; -) after “Definitions.i6t”.[/code][/spoiler]

Will do. I’m trying to cover up m_figures as good as possible. AI is still not past the conceptual phase though so I didn’t exactly put much effort in there (and probably won’t until I’m really, really sure that I’m satisfied with what I see). Anyway, it should, hopefully, change later.

As for your rant, I must admit that I’m not sold on that one. A clear idea what you want to be displayable seems like a more healthy working practice than just assume that everything is, if only for the sake of memory preservation. Where I do see a potential problem is if you want to include several same looking items like, say, healing potions. Can’t think of an easy way to implement that without doing the map figure assignment per hand for every object. Kind of map figure mayhaps? Translation: Sue me :stuck_out_tongue:

Ah, ok. That’s because of the 3-dimensional space which would allow other directions like north-up/down etc. Just haven’t included those yet.

It would? I was more thinking splitting up between border tiles/every other tile and do some simple arithmetic. Seems easier than creating an entire new layer.

Sorry, the last part was aimed at your last question which was, I think, more about marking map sections for-print and not-for-print. Lightning would be an entirely different beast and would require a run-time solution, I agree.

That’s a good idea. Will add that.

Some worthy thoughts. I will ponder a bit (and again kudos for the code snippets, if I haven’t stressed it out enough. Those always help).

Great, wondering whether a given phrase requires the m_figure or the object itself really is the worst part of it. The rest of it is livable! (Defining using tables helps make the process faster, for example.)

Yikes! Well, that explains it…

Yeah, that sounds fine, and easy to automate so that the user doesn’t have to futz with it. I think you must not have noticed the null tile suggestion, though, since you referred to it as “adding a new layer”. The null tile would just be a tile (with a border effect) that would go in the map-tiles list for an m_room. But when the display routine sees that tilecell, it just automatically advances to the next cell w/o drawing anything. I think a null tile is easier, and undoubtedly faster, than either of the other options we’ve been talking about. (The new illumination layer I proposed was an idea for using a single structure that could handle both dynamic lighting and non-display of “empty” tiles. You wouldn’t want the layer for the latter alone, but if you’re also doing the former, then you could easily use the same layer for both purposes.)

Edited to add: By the way, I wasn’t intending to ask you to implement the illumination map. Assuming I end up actually wanting to use dynamic lighting, I’ll just do it up myself. (Of course, if you want to implement, I wouldn’t try to stop you!)

OK, I’m sorry. I guess I still don’t get this after all. You are planning to create new directions, then, but I still wonder: why not just make them directions? This is all it takes to create a pair of new directions and prettify the index map:

Up-and-north is a direction. The opposite of up-and-north is down-and-south. Index map with up-and-north mapped as north. Index map with down-and-south mapped as south.

Doesn’t that give your users more to work with than a new kind of value would? More importantly, there is no more need for R&D (or your user) to convert between the two schemes!

[spoiler]Shifting gears a bit, you could also assign properties to make some operations more brief (and probably faster than passing lists around):

[code]A direction has a number called the x-step. A direction has a number called the y-step. A direction has a number called the z-step.
The x-step of north is 0. The y-step of north is -1. The z-step of north is 0. [etc]

To move (T - a mappable thing) toward (D - a direction):
move T on sector (x-coord of T + x-step of D) and (y-coord of T + y-step of D) and (z-coord of T + z-step of D);
if rule failed, rule fails.[/code][/spoiler]

Looks like I wasn’t quiet up to date. You couldn’t define directions that easily last time I tried (which was about two or four years ago? :confused:). Should be handy.

The concept wasn’t that hard to grasp. What I meant with layer was that you would have to run through all coordinates and check while with the other system, you would just skip all the empty spaces in between.

I didn’t think that you didn’t grasp it, just that you must have skipped over it. I can’t really see calling it a “layer”, since it adds nothing but a simple identity check (“if x=1”) to the process, one of the fastest things you can do in Inform. This will be negligibly slower than the current map-drawing for a map full of cells, and will be at least a slight improvement over the current performance if there are empty cells, no matter how those cells are arranged.

The compression algorithm you’re talking about, on the other hand, definitely does add a new “layer”–another system of lists, more calculations at setup, and another nested list to access at least once per row of the map display. It would undoubtedly speed things up on a map with a lot of empty cells, particularly where those cells appear in chunks. But it will certainly slow down other maps: those with little or no empties, or one where the empties are randomly distributed. Folks aren’t likely to create many of the latter, I don’t think, but the former will be common. On a full map, the slowdown may be unnoticeable for small maps of the sort that R&D is already good at, but for larger maps, it might be a problem. I’m not sure about either, but theoretically and judging by the benchmarking we’ve been doing of basic operations, that’s a sketch of how I’d expect it to function.

I think that this compression is a nice idea for implementing an illumination map. A light-dark map will usually have just a few areas, grouped in chunks, and any given map is likely to benefit from the compression. The main difficulty would be the need to calculate the compression whenever dynamic lighting changes.

I’ve been thinking about it, and I think that giving authors the option to use headings (such as in the spoilered code above) and distances may be the most flexible tool for writing simple AI. Headings are an easy way to write code to enable goal-seeking behavior. For example, here’s an algorithm for moving an NPC around obstacles to reach a goal, followed by a map in which the @ could get to the X in 5 steps using the algorithm:

Straight-line distance would be the other easy-to-use metric, allowing NPCs to choose between targets, or to select a move to keep them at an optimum distance from an enemy (e.g., if they want to use a missile attack).