Room & Dimension Version 2 (Need betatesters)

Awesome, thanks!

That’s probably correct for something as low-level as environmental effects. But you have all the data you need to print a map actor stored in its properties, right? (At least, that’s my impression of how map figures are packaged.) If so, there’s no need to pass anything to the rule other than the name of the actor. You have everything you need at your fingertips just by accessing the properties for the actor that was passed into the rule, and the rule can call lower-level print phrases by passing the actor’s properties into those phrases.

Rulebooks give you plenty of control over this. You can refer to properties of the object (or any other global state) in the rule header, so you have fine-grained control over when a particular rule activates. My example used default outcomes to end the rulebook as soon as the first suitable rule was found. This may or may not be the right solution for R&D (I think that it probably isn’t), but it also allows a degree of control. You can also use rulebook variables if you want to track the state of things within a particular run of the rulebook. And of course if you name all of your rules, your users can rearrange them to suit their own needs.

Yep!

[spoiler]Off on a tangent, I noticed this in one of your printing rules: let graphic be a list of numbers; add 0 to graphic; add 0 to graphic; add 0 to graphic; add 0 to graphic; add 0 to graphic; add 0 to graphic; add 3 to graphic;

It is possible to just do this for readability:

let graphic be a list of numbers; now graphic is {0, 0, 0, 0, 0, 0, 3};[/spoiler]

Not quiet. The current z-level displayed would be unknown to the map figure, it would have to get that information from the map-display (example: a creature three levels hight, first level would be the feet, second level torso and arms, third level the head). I didn’t dwell too much on that topic so the issue is probably not that obvious. It would be possible to add another parameter to map figures to handle this too but in that case I would wonder if that sort of fleeting data wouldn’t be better stored within one object like the buffer to save some space. Tell you what, I think I’ll just try it out and look where it goes. When you’ve got an idea which makes all your worries go away ™ and that you’ve obviously fallen in love with, then the best course of action is to go for it until you hit the next obstacle so hard that you’re left with a dizzy feeling and a bleeding nose.

Cool! You’re probably referring to the stuff from chapter 25.8? Gotta try that out. Also thanks for the other code snippet!

OK, a bunch more betatesting stuff. I’m working on map actors again, and also on AI.

[spoiler]Docs - errors

  • The docs say “t_wall - Has a generic environmental effect called g_ee_wall in its default setup”, but it’s actually called g_ee_block.
  • 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.
  • 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!

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, though. First position in the list specifies z=1, second position z=2? Or first position is z=0? For offset_coord, it would be a good idea to verify that it is specified in sectors (I had to study the examples to figure that out)–it would be natural to want to offset an image by a few pixels to improve it’s placement on the map, for example, so a user might wonder which this is for.
  • It’s not clear from the docs whether the “add coordinate” phrases for map figures do something different than the extension_value property? What does the latter do? Is it purely for graphical display, whereas the former is actually stored in the map?
  • I think it’s great to cover some topics only the examples, but the docs could use better cross-referencing in those cases. For example: In the body of the docs, you could refer to the fact that map actors with more than one coordinate will have special issues when triggering effects, such as walls and collisions, and then tell readers to look at the Ben & Jill example for how to handle that. Ditto for how to stop actors from moving into each others’ sectors, which is also to be found in the Ben & Jill example but probably needs to be highlighted better.
  • Effects are pretty confusing, and I could use a bit more handholding in the docs. There’s g_ee_block, for example, but also ee_wall. It would be nice see you lay out explicitly how these fit together, as well as what your conventions are for naming these objects. This would help users to see at a glance what they are at a glance rather than having to study the source code or the index to see how they are defined.

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?
  • Ben & Jill shows how to prevent the player from moving onto a sector occupied by an actor that extends over multiple sectors. However, it seems a lot harder to detect when a multisector actor moves into a sector occupied by another actor. How the heck do I do that? Is there anything you could do to make it easier? Would it be possible for map actors to have ee_effects as well?
  • Further to that, do I have to use facing directions to reliably determine whether a multisector actor is going to collide with another actor? If so, that would force me to implement facing directions even though I don’t want them…
  • In the Ben example, the wall tilecell is given a touch activation effect to prevent Ben’s extension from being able to overprint the wall. Is there any reason not to give all walls both touch and block activation by default?
  • If you’re still planning to separate AI into its own extension, please consider getting rid of the smart_m_figure type in favor of making the AI capabilities possible for any m_figure. As a user, I don’t care about adding a few more list properties to all actors, especially if I know I’m going to be using AI (i.e., I installed the extension). That’s more convenient for me than having to juggle two kind-trees.
  • Is there a way to specify directly the number of tiles that should be shown in a map-display. If you size the tile-width and tile-height directly, the map may extend off the screen. Or does this need to be done using the sight distance? (And if so, does that work only for part-maps?)
  • AI: The term “steps” seems to be used both for phrases that return/contain coordinates and for phrases that return offsets. Users, however, have to treat the two totally differently. Consider standardizing the terminology to make clear what kind of data is in each list. (i.e., are they offsets or sector coordinates?)
  • It would be nice to have “to move” phrases that take coordinate lists as input, in addition to those that accept individual numbers. For example:To move (t - a mappable thing) to coordinate (c - a list of numbers) in (r - a m_room): if the number of entries of c is 2: move t on sector (entry 1 of c) and (entry 2 of c) in r; otherwise: move t on sector (entry 1 of c) and (entry 2 of c) and (entry 3 of c) in r.
    [/spoiler]

Edited to Add: What would I need to do to randomly generate a map in the style of Nethack? I guess this is a two-part question:

  1. Does the part-map feature work performantly with a larger map (maybe 256 tiles wide)?
  2. Creating a large map, even if featureless, is incredibly slow. Any way to speed it up? (Say, by limiting it to one z-level?)

Thanks!

About the other stuff, I wouldn’t get my hopes up if I were you. Even if the map-display remains static, I get perhaps a 100x100 field with minimal details displayed with tolerable performance but that’s about it. Maybe only using purely text will be faster since it doesn’t require to draw and scale images though, haven’t tried that yet.

Funny thing when it comes to map creation is that the speed in fact goes up when you add z-levels (so a 3x10x10 map is actually cheaper than creating a 1x30x10 map). I think I read something about it in the Inform 7 performance thread a while ago, that adding elements to a list seems to be the troublemaker here (I was actually thinking that sorting would be the most expensive thing to do with a list), especially the more elements are already present. This is a real problem when building up complex map-displays since they rely on a list of lists (see how much faster the simple map-displays run in comparison, which use a list of lists of lists, much like the original image-maps do). To make something like random dungeon creation run faster, you could probably try to first create a map template (that is just empty lists within lists which will fit the frame of a map) dump that in an external-file, copy&paste the result into the source code, as you suggested, then let your generator first-place tilecells at the appropriate places. This should speed things up, since it cuts down on the adding phrases during runtime.

Other than that though, I’m not sure if anything can be done as long as no one somehow finds a way to optimize the process of adding elements to lists.

[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.