Room & Dimension Version 2 (Need betatesters)

[quote=“Basti50”]
Docs - errors

  • The use of the terms “width” and “length” in the docs is confusing (“length” is ambiguous). It would be best to use width for the x direction, and height for the y direction.
    But what happens to z? (that is if we’re not talking about image sizes in which case I agree) Sorry, I’m still not really thinking in terms of the z-level. (I can’t really see any good reason to use z-levels except in a large Dwarf-Fortress like map, but performance-wise that’s almost certainly a no-go.) Anyway, I really did stumble over this in one case, but maybe I was just being dense… Maybe mention in the docs that you’re using the terms width (x), length (y), and height (z)?
  • I believe that it is an error not to hammer into the reader’s head, over and over again, that the order of coordinates must be reversed for m_pos depending on whether you are declaring it or adding coordinates after the fact!
    I’m still hoping to leave everything m_pos related to phrases, which align the coordinates in the (x, y, z) order so the end user doesn’t need to bust his/her head about it. I was joking around due to the fact that I spent quite a few minutes believing that the phrases didn’t work, simply because I forgot to reverse the terms. As a user, let me say that I prefer to declare the coordinates rather than assign them procedurally, so I’d still be defining m_pos directly in most cases. But obviously that doesn’t mean that it’s not a good idea to have phrases to do the work as well.

Docs - room for improvement

  • The specification for m_figure does explain the extension_value and offset_coord properties better than the docs proper (which I complained about in an earlier post). It’s still not crystal clear…
    It’s thankfully not that strict. Say you got a map figure with the coordinates {{0, 0, 0} {5, 0, 0} {10, 0, 0}} and the offset values {{0, 0} (→ valid for {0, 0, 0}) {1, 1} (→ valid for {5, 0, 0}) {1, 0} (→ valid for {10, 0, 0}) {5, 5} (→ valid for nothing)} I still don’t get it… :confused: No need to explain it to me here (I’m not using z-levels), but give some thought how to do it in the docs!
    extension_value is strictly for stretching images (which I consider independent from the map figure’s actual ‘physical’ form). I definitely agree that this is an important distinction, and I’m glad you’ve implemented it.

Questions/Requests/Problems

  • How does extension_value work? The specification says that it is an (x,y) measurement(vector, I guess?). However, by trial and error, I got an image that was 2 tiles wide and 1 tile high to print properly by using {{0, 1}} for the extension_value. Wouldn’t it be better for that to be specified using {{2, 1}}, i.e. 2 sectors wide in the x direction, and 1 high in the y direction?
    They’re meant as additional height and width which get added during the drscimage phrase. Doing it the way you suggested would be more intuitive, I agree, but the program would have to subtract them by 1, which would make the code more costly, at least a little. I think I rather make this more clear in the docs. Yes, but why do the x and y seem to be reversed? That seems to be the opposite of what the specification text says? The docs, by the way, don’t seem to address this at all–you have to go to the specification to find it.
  • I’m actually thinking to add ‘auras’ to map figures in the traditional rpg fashion which would work as a portable field of effects that you can attach. That would be a great feature. But even just collision detection would be sweet.
  • Further to that, do I have to use facing directions to reliably determine whether a multisector actor is going to collide with another actor?
    There is at least the phrase ‘To decide whether (target - a mappable thing) is in map reach of (chaser - a mappable thing)’ which checks an object’s entire surroundings and not only a direction. The code is rather inefficient as of yet though. I will try to switch to a more mathematical approach which should go a bit faster. OK, thanks. I didn’t see anything that indicated that this phrase checked all sectors of the actor.
  • AI: The term “steps” seems to be used both for phrases that return/contain coordinates and for phrases that return offsets. Consider standardizing the terminology to make clear what kind of data is in each list.
    Not sure where you see offset values in there. Offset values too are strictly for images, so they shouldn’t have any part in pathfinding. You probably mean the difference between steps & waypoints? I need to find a better terminology, true, once the AI gets out of its baby shoes. I was using “offset” in its general meaning, not specific to R&D’s terminology. (I.e., an offset is a delta value, not a direct specification of a location.) This snippet from the docs seems to be using a “step” directly as a location:while ai_steps of av_yourself is not empty: change step to calculate next step of av_yourself; move player on sector (x-coord of player - Entry 1 of step) and (y-coord of player - Entry 2 of step) in Lab;Are there movement routines that take steps (as opposed to coordinates) as inputs, or does an author have to manually subtract the step from the actor’s current position in order to move?

[/quote]
Thanks for the performance notes. Glimmr bitmap fonts use very large lists (at least 3-4 thousand entries) and still get very good searching performance, but those are lists of a single dimension. I’m sure you’re right that it’s the nested lists that limit the performance (and of course, R&D is doing far more with its maps than the flat list in a bitmap font is doing!). In any case, there are always options for subdividing a map. I just wondered what was possible.

Thanks!

I don’t want to fill my map with a floor tile initially. I’d rather use a wall tile, then “carve” randomly generated rooms out of it, a la Nethack and most other random dungeon generators. Again, maybe the m_room object’s default tile properties should be defined more generally, i.e. as tilecells, not t_floors or t_walls or whatever.

The way that actors are moved into the m_room when they are spawned on the map does indeed make it more difficult to create a Nethack-style map. If objects and characters were in their own rooms, but displayed on a common map, it would provide a nice, simple way to control basic scope.

Is there a way I can store a room number in either the map or an environmental effect? Like so:

· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 1 1 1 1 1 1 1 1 1 1 · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · 1 1 1 1 1 1 1 1 1 1 · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · 1 1 1 1 1 1 1 1 1 1 · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · 1 1 1 1 1 1 1 1 1 1 · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · 1 1 1 1 1 1 1 1 1 1 · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · 1 1 1 1 1 1 1 1 1 1 · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · · · · · · · · · · · · 2 2 2 2 2 2 2 2 2 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 5 5 5 5 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 5 5 5 5 · · · · · · · · · 3 3 3 3 3 · · · · · · · · · · · · · · · 5 5 5 5 · · · · · · · · · 3 3 3 3 3 · · · · · · · · · · · · · · · 5 5 5 5 · · · · · · · · · 3 3 3 3 3 · · · · · 4 4 4 4 4 4 4 4 · · · · · · · · · · · · · · · 3 3 3 3 3 · · · · · 4 4 4 4 4 4 4 4 · · · · · · · · · · · · · · · 3 3 3 3 3 · · · · · 4 4 4 4 4 4 4 4 · · · · · · · · · · · · · · · 3 3 3 3 3 · · · · · 4 4 4 4 4 4 4 4 · · · · · · · · · · · · · · · 3 3 3 3 3 · · · · · 4 4 4 4 4 4 4 4 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 6 6 6 6 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 6 6 6 6 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 6 6 6 6 · · · · · · · · · · · · · · · · · · · 8 8 8 8 8 8 8 · · · 6 6 6 6 · · · · · · · · · · · · · · · · · · · 8 8 8 8 8 8 8 · · · 6 6 6 6 · · · · · · · · · · · · · · · · · · · 8 8 8 8 8 8 8 · · · 6 6 6 6 · · · · · · · · · · · · · · · · · · · 8 8 8 8 8 8 8 · · · 6 6 6 6 · · · · · · · · · 7 7 7 7 7 7 7 · · · 8 8 8 8 8 8 8 · · · 6 6 6 6 · · · · · · · · · 7 7 7 7 7 7 7 · · · 8 8 8 8 8 8 8 · · · · · · · · · · · · · · · · 7 7 7 7 7 7 7 · · · 8 8 8 8 8 8 8 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·

[rant]Oh, in the last post I said: “I can’t really see any good reason to use z-levels except in a large Dwarf-Fortress like map.” Obviously I was referring to the things I’m currently interested in, not to the uselessness of multilevel maps generally.[/rant]

OK, here is a Nethack-style randomized dungeon implemented in R&D, along with the source code:

dl.dropbox.com/u/947038/Randomiz … eon.gblorb
dl.dropbox.com/u/947038/Randomiz … source.txt

Use the arrow keys to move; any other key to begin typing. It takes a few seconds to generate the map at the beginning; please be patient. Even though it’s only a 30 x 30 grid, the performance is pretty poor–poorer than I would have thought. Often, just moving around the map (which is all there is to do) sends git’s CPU usage to 100%. The map-generation algorithm itself is instantaneous on a map of this size if you divorce it from R&D, which suggests one of two things:

  1. There is simply so much memory being used and accessed every turn in R&D–primarily due to lists, I reckon–that the VM strains to deal with it.
  2. There is a bug or implementation detail in R&D, or perhaps in I7’s list code, that is causing massive amounts of processing power to be needed intermittently.

I’m not sure how to decide between these… Maybe someone with a profiling interpreter and the knowledge to use it can tell us what is taking all the time.

In any case, right now at least, it seems that simple per-room maps are more practical for R&D than larger, more complex maps a la a classic roguelike. This is probably not surprising, since per-room maps are the original intended purpose of R&D!

A few more betatesting issues, tucked out of sight:

[spoiler]* Back to the issue of the terminology of “length” vs. “width”. This snippet of code from the phrase for deciding whether a coordinate is out of bounds suggests that they are reversed from what an English speaker would normally presume: (X > room-length of r) or (Y > room-width of r) This has X as the “length” coordinate and Y as the “width”. However, “width” usually suggests horizontality, so X makes much more sense as the width term. (“Length” for any dimension on a grid is pretty odd, as I said before–length makes more sense for describing a feature on a grid (length of a vector, for example.)

  • The graphics section in the docs refers to two things called “additional height” and “additional width”. Is this just a reference to the extension_value we’ve been talking about for map actors? (There’s also a reference to additional width and additional height in the Tilecells section.) The docs don’t make clear what these refer to, so it’s a bit confusing.
  • How do I make the map display the same size at the sight distance so that the two are in sync? Right now, the display is smaller than the sight distance and so the display appears to expand as the player moves (adding columns and rows); see the demo above, for example. I would like the framing to simply follow the player.
  • Noticed a bug with the map generating activity. The code has a “before map generating” rule that is intended to stop the activity if the m_room has no layout. However, activities don’t work like other rulebooks–“before” rules don’t stop the main activity from running (see manual 17.3)–and so this rule will not stop the activity from running. The check needs to be put in the “for” rule to stop the activity.

Looking at this code did give me an idea of how to allow authors to intervene easily to use any tilecell to flood their maps, though. Here’s how I modified the code to do this as well as to fix the problem with the before rule:

[code]Map generating something is an activity.
The map generating activity has a tilecell called the flood-tile. [### Added]

[Before map generating a m_room (called r) (this is the standard map generating check rule)][made this the first “for” rule.]

First before map generating an m_room (called r) (this is the set default flood tile rule):
now the flood-tile is the def_floor of r.

First for map generating an m_room (called r) (this is the standard map generating check rule):
if r has no layout, rule fails;
carry out the map resetting activity with r;
continue the action.[necessary or the activity will stop here.]

Rule for map generating a m_room (called r) (this is the standard map generating rule):
If room-height of r > 0 and room-width of r > 0 and room-length of r > 0:
let r-h be room-height of r;
let r-w be room-width of r;
let r-l be room-length of r;
let s-t be flood-tile;[this is the only change]
etc.[/code]

To use something other than def_floor as the flood tile, an author would just:

Before map generating the land-beneath-the-mountain: now the flood-tile is Granitic Rock.
[/spoiler]

How easy would it be to swap a single list for your list of lists? Maybe a single list would be more performant.

Actually, it seems like using more lists is faster than relying on fewer. Take this new example with the simple map-display, which uses many more but smaller lists (the speed improvement may not be as obvious since I couldn’t tinker enough with the generating algorithm [which is all sorts of awesome btw], sorry).

New source code and updated R&D files can be found here.

As for trying it out with just one list, that would be a though one since I wouldn’t have any easy way to sort graphics by their printing priority anymore.

Reconstructing the lists of a map-display is really darn expensive. It would be great if it would turn out that I’ve just screwed something up in my code. The current speed for any non-static camera past a 20x20 frame is rather abysmal for me (which should be ok for a graphical roguelike, I guess, as long as you don’t go overboard with the details but it’s a downer nonetheless).

@Erik
Again I owe you one. I’m currently a bit occupied (=lazy), so it may need some time before I can get back to your notes (that is I actually want to implement some of the improvements before I run into the danger of clogging up :smiley:). You work, like, way too fast for me, dude. :sunglasses:

Here are some comparative stats on reading in a known location (e.g., “if entry 12 of my list is x”) of flat vs. nested lists, courtesy of the new Benchmarker extension:

Flat lists look to be quite a bit faster than doubly nested lists (i.e., list of lists of lists of whatever).

EDITED to add: Here’s the source code for this:

[code]Include Benchmarking by Dannii Willis.

Simple search list is a list of objects variable.
Nested search list is a list of lists of objects variable. Nested search list is {{}, {}}.
Double-nested search list is a list of lists of lists of objects variable. Double-nested search list is
{ { {}, {} }, { {}, {} } }.

A dog is a kind of person.

Ralph, Scogg, and Zip are dogs.

When play begins:
repeat with x running from 1 to 99:
add a random dog to simple search list;
add the player at entry 90 in simple search list;
repeat with x running from 1 to 2:
repeat with y running from 1 to 50:
add a random dog to entry x of the nested search list;
now entry 40 of entry 2 of the nested search list is the player;
repeat with x running from 1 to 2:
repeat with y running from 1 to 2:
repeat with z running from 1 to 25:
add a random dog to entry y of entry x of the double-nested search list;
now entry 15 of entry 2 of entry 2 of the double-nested search list is the player.

Selecting from a simple list is a test case.
The author is “Erik Temple”.
The description is “Grabs the 90th entry from a list of 100 total objects: {1…90…100}.”
To run simple-list select (this is running simple selection):
if entry 90 in simple search list is the player:
do nothing.
The run phrase is running simple selection.

Selecting from a nested list is a test case.
The description is “Grabs the 90th entry from a nested list of 100 total objects: { {1…50}, {1…40…50} }.”
To run simple-list select (this is running nested selection):
if entry 40 of entry 2 in nested search list is the player:
do nothing.
The run phrase is running nested selection.

Selecting from a double nested list is a test case.
The description is “Grabs 90th entry from a double-nested list of 100 total objects: { { {1…25}, {1…25} }, { {1…25}, {1…15…25} } }.”
To run simple-list select (this is running double-nested selection):
if entry 15 of entry 2 of entry 2 in double-nested search list is the player:
do nothing.
The run phrase is running double-nested selection.[/code]

That’s odd, I did some testing too with the timestamps of the AI code you sent me a while back and the results were rather indicating the opposite. I’m confused :confused:

Relevant code:

[code]tlist1 is a list of numbers that varies.
tlist2 is a list of lists of lists of numbers that varies.
tlist3 is a list of numbers that varies.
tlist4 is a list of lists of lists of numbers that varies.

When play begins:
add {{}} to tlist2;
add {{}} to tlist4;
repeat with y running from 1 to 40:
add {{}} to Entry 1 in tlist4;
repeat with x running from 1 to 40:
add 1 to Entry Y in Entry 1 of tlist4;
repeat with x running from 1 to 1600:
add 1 to tlist3;

Linserts is a test case.
To run linserts (this is running linserts):
repeat with y running from 1 to 40:
add {{}} to Entry 1 in tlist2;
repeat with x running from 1 to 40:
add 1 to Entry Y in Entry 1 of tlist2;
truncate tlist2 to 0 entries;
add {{}} to tlist2.
The run phrase of Linserts is running linserts.

Linsert is a test case.
To run linsert (this is running linsert):
repeat with x running from 1 to 1600:
add 1 to tlist1;
truncate tlist1 to 0 entries.
The run phrase of Linsert is running linsert.

Lpicks is a test case.
To run lpicks (this is running lpicks):
repeat with y running from 1 to 40:
repeat with x running from 1 to 40:
let testing be (Entry x in Entry y in Entry 1 in tlist4).
The run phrase of lpicks is running lpicks.

Lpick is a test case.
To run lpick (this is running lpick):
repeat with x running from 1 to 1600:
let testing be Entry x in tlist3.
The run phrase of lpick is running lpick.[/code]

You might try setting up such tests using the new Benchmarking tool; maybe the length of the lists makes a difference for example? The timing stuff in that AI demo was very rough-and-ready, and Dannii’s benchmarker is a much better approach to the issue–for one thing, it runs the test many times to get a better estimate of actual timing.

'K I fiddled a bit more with the benchmark test and had the following results for a 250x250 map:

From there I went down to 150x150:

And finally 100x100:

So using flat lists seems to be a bad idea when you work on large scale maps, while they win quiet an edge (at least when it comes to picking their content) during any smaller example. I’ll need to keep this in mind.

It strikes me that it might be a good idea to post the source code for these tests. I added mine on the previous page. Interpretations of these tests, and whether they actually apply to possible optimizations for any given application, depend on the precise conditions of the test. My test above, for example, would only apply to an R&D map if you were routinely using the upper z-levels. Most users, though, would probably be using only the first z-level… In other words, to look at R&D, it obviously makes more sense to test configurations that actually reflect how R&D (and its users) might handle things.

Agreed, added my source code above (had to correct the results too while I was at it). It was mostly to test whatever happens when you expand lists, both for map creation and display creation, and how well these different approaches handle multiple entries selection, which would be important if you want to get data from a map. It’s a bit harder to estimate on Gargoyle since it would terminate the test cases with list of x-lists earlier but, from what I can see in Git at least, list of x-lists seems to be the winner here in the long run.

Here’s a sample that reflects alternate ways that the map-objects (basic tilecell map, the m_room property) in R&D could be set up. It tries to reflect what I expect would be a fairly typical usage: a 10x10 map in a single z-level. Each test checks a cell at random from the 100 available to see what object is stored there.

The first is as a flat list of 100 cells, with no z-levels stored within the list:

The second is as a nested list, where each z-level is stored separately as a single flat list. This example assumes that only 1 z-level is represented, so all 100 cells are in the first nested entry:

The third is the way that R&D is actually set up. Each row (y-axis) is stored as a separate nested list, and these are further stored within a grouping that represents the z-level. The example assumes that only 1 z-level is represented, and that the 100 cells are organized into a 10x10 grid.

At this scale (i.e., 100 cells), the third way is slowest. A very small fraction (0.5-1.0µs?) of this is due to the fact that, due to the structure, the third test must select two random numbers, whereas the others must choose only one.

Here’s the same basic setup, but with a 20x10 grid:

Again, but with a 20x20 grid:

With a 40x20 grid, the flat list has become significantly slower than the double-nested list:

A 40x40 grid, the trend is even more pronounced:

Comparing these examples, it seems clear that the third way–the way R&D stores map tiles currently–is the most consistent. Even quadrupling the number of cells, there is essentially no difference in the performance. However, while the other two methods show more degradation, the flat list method is still objectively faster than the double-nested list. But the single flat list doesn’t have any way of storing z-levels, and the second method, which is fully three-dimensional, actually becomes slower than the double-nested list.

In sum, if R&D is to contain z-levels, the way it’s doing it now is probably the best way–at least as far as storage of map tiles is concerned!

Edit to add: Here are numbers for a 400x400 map (160,000 cells). At this scale it’s clear, as in Basti’s examples, that standard lists and nested lists don’t perform nearly as well as double-nested lists:

So, if R&D maps are to have z-levels, large size, or both, then the way that it already stores map data is the best approach. Looks like we need to look at things other than basic structure for optimization ideas.

[spoiler]Source code for 100-cell and 160,000-cell maps:

[code]Include Benchmarking by Dannii Willis.

Simple search list is a list of objects variable.
Nested search list is a list of lists of objects variable. Nested search list is {{}, {}}.
Double-nested search list is a list of lists of lists of objects variable. Double-nested search list is
{ { {}, {}, {}, {}, {}, {}, {}, {}, {}, {} } }.

A dog is a kind of person.

Ralph, Scogg, and Zip are dogs.

When play begins:
repeat with x running from 1 to 100:
add a random dog to simple search list;
repeat with x running from 1 to 2:
repeat with y running from 1 to 100:
add a random dog to entry 1 of the nested search list;
repeat with x running from 1 to 1:
repeat with y running from 1 to 10:
repeat with z running from 1 to 10:
add a random dog to entry y of entry x of the double-nested search list…

Selecting randomly from a simple list is a test case.
The author is “Erik Temple”.
The description is “Checks the identity of a random entry from a list of 100 total objects: {1…100}”.
To run simple-list select (this is running simple random selection):
if entry (a random number between 1 and 100) in simple search list is the player:
do nothing.
The run phrase is running simple random selection.

Selecting randomly from a nested list is a test case.
The description is “Checks the identity of a random object in the first nested list of 100 total objects: { {1…100}, {} }”.
To run simple-list select (this is running random nested selection):
if entry (a random number between 1 and 100) of entry 1 in nested search list is the player:
do nothing.
The run phrase is running random nested selection.

Selecting randomly from a double nested list is a test case.
The description is “Checks the identity of a random object in the first double-nested list of 100 total objects: { { {1…10}, {1…10} }, { {1…10}, {1…10}, etc. } }.”
To run simple-list select (this is running double-nested selection):
if entry (a random number between 1 and 10) of entry (a random number between 1 and 10) of entry 1 in double-nested search list is the player:
do nothing.
The run phrase is running double-nested selection.[/code]

[code]Include Benchmarking by Dannii Willis.

Simple search list is a list of objects variable.
Nested search list is a list of lists of objects variable. Nested search list is {{}, {}}.
Double-nested search list is a list of lists of lists of objects variable. Double-nested search list is {{}}.

A dog is a kind of person.

Ralph, Scogg, and Zip are dogs.

When play begins:
repeat with x running from 1 to 160000:
add a random dog to simple search list;
repeat with x running from 1 to 2:
repeat with y running from 1 to 160000:
add a random dog to entry 1 of the nested search list;
repeat with x running from 1 to 1:
repeat with y running from 1 to 400:
add {{}} to entry x of the double-nested search list;
repeat with z running from 1 to 400:
add a random dog to entry y of entry x of the double-nested search list.

Selecting randomly from a simple list is a test case.
The author is “Erik Temple”.
The description is “Checks the identity of a random entry from a list of 160,000 total objects: {1…160,000}”.
To run simple-list select (this is running simple random selection):
if entry (a random number between 1 and 160000) in simple search list is the player:
do nothing.
The run phrase is running simple random selection.

Selecting randomly from a nested list is a test case.
The description is “Checks the identity of a random object in the first nested list of 160,000 total objects: { {1…160,000}, {} }”.
To run simple-list select (this is running random nested selection):
if entry (a random number between 1 and 160000) of entry 1 in nested search list is the player:
do nothing.
The run phrase is running random nested selection.

Selecting randomly from a double nested list is a test case.
The description is “Checks the identity of a random object in the first double-nested list of 160,000 total objects: { { {1…400}, {1…400}, {1…400}, {1…400}, etc. } }”
To run simple-list select (this is running double-nested selection):
if entry (a random number between 1 and 400) of entry (a random number between 1 and 400) of entry 1 in double-nested search list is the player:
do nothing.
The run phrase is running double-nested selection.[/code][/spoiler]

A quick feature-request post, to be followed by a second post making a stab at optimization suggestions.

  • Currently, there is no support for mappable things, only mappable people (m_actors). What do you think about simply adding the properties needed for map display to the root “thing” kind? You could add a mappable property (e.g., “a thing can be mappable. A thing is usually not mappable”), allowing you to loop over only mappable things for fast printing. AI, of course, could continue to apply only to “person” and its subkinds.

  • R&D currently assumes that the users wants to use all compass directions. It would be nice if the user could limit conversions to use only cardinal directions (i.e. north, south, east, west). I suspect that many folks will not want always want to deal with the full 8-point compass.

Very kind of you to do some independent testing, much appreciated :slight_smile:
I consider making maps jump between method one and three, depending what their current size would favor, but that would require new pick/place phrases so it is more of a ‘when I got time to spare’ kinda idea.

As for optimizing displays though, if lists really are variable-size arrays and if they work as I think they do, it might be a better idea to switch to static arrays for a performance boost during built-up. In short steps:

(1) shouldn’t be a biggie
(2) I’m more afraid of since I would have to do Inform 6 stuff and my knowledge in that field is very basic at best (for example, does I6 even allow multidimensional arrays? I would need those). But since the sizes are fixed (both from (1) and since graphics themselves have a fixed number of elements) it should be easy enough
(3) again shouldn’t be a problem once I figured out (2). The question is if running through the segment twice would slow things down too much but I would be optimistic in that regard
(4) depends mostly if there are already searching algorithms in place or if I would have to make my own Quicksort implementation :confused:

From there it should be a piece of cake though.

The difficulty with this is that you can’t create arrays at run-time, and you can’t resize arrays on the fly. Given that, you wouldn’t be able to use the author’s I7 specification for a room (e.g. room-height and room-width) and create an array. The author would pretty much need to declare the array in I6 and then associate it with the I7 array. (Check out the DM4 for more on I6 arrays.)

I think your best option optimizing display is probably simplification of graphics storage…

Weird, things should already be mappable too from the outset since they all get m_actors (see lower part of Section 0.5). Maybe the name is just misleading?

The examples do so at least, yeah (and do a very crude job at overwriting the standard behavior too. Maybe I should think those alterations better through). I don’t want to set any limitations though, or is there something specific you got in mind?

Edit:

I see, bummer.

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]