Efficiently generating list of directions as room description

I’m working on a slightly peculiar Inform game with a complicated map. There’s already at least one location featuring all four compass directions plus up and down, so I want room descriptions to provide a simple list of all exits. (I don’t anticipate a whole load of locked doors etc.) However, I don’t want to have to write those lists manually because it feels like a massive opportunity for human error.

I already have a solution for this involving setting every room description to “[room-exits]”, but it’s a bit of a monster:

To say room-exits:
	let C be 0;
	let TC be 0;
	repeat with TD running through each direction:
		if room-or-door TD from the location is not nothing:
			increase C by 1;
	repeat with D running through each direction:
		if room-or-door D from the location is not nothing:
			increase TC by 1;
			say "[The room-or-door D from the location] is [D] from here.[run paragraph on][unless TC is C][line break][end if]".

The way this works is to first “count” all the directions leading off from a room with “repeat with TD”. Once that’s done, “repeat with D” prints the actual “Room X is west from here” etc. list with [run paragraph on] followed by a [line break] which is dropped from the very last item in the list (identified by comparing TC to C). This was the only way I could find to avoid Inform creating a double line break between the room description and the “You can see…” text below it.

Though this produces exactly the result I want, I’m concerned that it’s a very inefficient way of producing text that’ll appear every time the player enters or looks at a room. The game already involves a number of fairly demanding elements such as wandering NPCs so I’m keen to avoid it slowing to a crawl while the player navigates the map.

Is there a more efficient way of doing this and still getting it to look neat? In particular, is there any way I could run through the complete list of rooms once at the very beginning of the game, generate a unique description for each, and then simply have that description printed for the player when they visit (so it doesn’t have to be re-generated every time)?

Doesn’t list room exits already?

This doesn’t really help you with your issue, so I’ll hide it like this so it can be easily ignored.

As far as the text that appears every time, if it’s shaping up to be that complicated and/or bulky to have in the main text, have you considered instead listing exits in a multi-level status bar? Or a flexible-window if using Glulx? Relegating permanent exit listing to those, and adding an “exits” command to list on-demand. It would be extra work, and not all interpreters would be sure to support it, so these are already two very good reasons not to do that, plus this very much does not address your actual issue, but still, I thought I’d bring it up.

I’m afraid I don’t understand the question.

In room descriptions, unless you’re overriding them somehow, aren’t the exits listed in “look”?

Actually, yes! It doesn’t quite address the actual issue here - and so far I’m finding that the exit lists aren’t typically too overwhelming - but it is useful to have options for tucking the actual information a little further out of the way as well. I’m hesitant to try anything more ambitious until I’m confident I’m not taking too long to generate the text itself, though.

Maybe a stupid question, and I don’t have Inform installed on this machine to check, but can you have a single loop that always says [run paragraph on] every time, and then say [paragraph break] afterwards (outside the loop)? Or does [run paragraph on] override the explicit [paragraph break]?

Edit: oh wait, you’re already always running the paragraph on, and optionally breaking in addition. So it should work? Anyway, I wouldn’t expect looping twice over 12 (?) potential directions to be a noticeable slowdown, but…

I actually get the appropriate single paragraph break by using [run paragraph on] inside the loop even without manually adding a [paragraph break] outside of it. The problem there is that having the list items all on one line makes them virtually unreadable.

Ohhhh, I’m reading this backwards, sorry. Paragraph break everywhere EXCEPT the last, not paragraph break only at the end.

By default, exits aren’t listed by “look.” The code I’ve posted above will list them, but I’m hoping for a less demanding way of doing this for the reasons explained above.

Seriously, don’t worry about not getting it right away. My initial approach was to try and keep the default breaks and then add a [run paragraph on] at the end, but for some reason Inform just ignores it in that context. You have to [run paragraph on] every time with a [line break] then NOT include the [line break] for the last item. No idea why - that’s just the only method I’ve found that produces line breaks within the list but not an extra bonus one at the end.

If you reckon the current method won’t slow things down too much then I may be overthinking this. I’m just very aware that I could get all that looping out of the way once up-front (where it would probably go unnoticed even if it took multiple seconds to complete), and also that doing so would effectively free up resources for two more randomly wandering NPCs (which I’ve seen specifically highlighted as an “expensive” use of “every turn…” rules).

Yeah, pathfinding has to do a breadth-first search through the whole map to make sure it’s found the shortest path, so algorithmically it’s way more expensive, unless there’s something else costing time with your loop(s).

But I installed Inform; how about moving the line break to the beginning and doing it all except the first time?

To say room-exits:
	let C be 0;
	repeat with D running through each direction:
		if room-or-door D from the location is not nothing:
			increase C by 1;
			say "[unless C is 1][line break][end if][The room-or-door D from the location] is [D] from here.[run paragraph on]"

You can certainly use the description property of each room to set the text once at the start of the game, and cut out one of the loops (EDIT: joshgrams got there first!):

To decide which text is room-exits for (R - room):
	let TC be 0;
	let desc be text;
	repeat with D running through each direction:
		if room-or-door D from R is not nothing:
			if TC is at least 1, now desc is "[desc][line break]";
			increase TC by 1;
			now desc is "[desc][The room-or-door D from R] is [D] from here.[run paragraph on]";
	decide on desc.

When play begins:
	repeat with R running through rooms:
		now the description of R is the substituted form of "[room-exits for R]".

You definitely can store valid exits at the beginning of the game. That will still involve a table lookup during play, but that might be more efficient than repeating through all directions.

Here’s my attempt, there might be a better way to do it.

lab is a room.
the description of lab is "Wow, this is some lab!"

when play begins:
	populate the exit table;

closet is east of lab.
garage is south of lab.

after looking:
	say "Exits: [exits for the location]";
	continue the action;

to say exits for (r - a room):
	choose row with a where of the location from the table of exit lists;
	if the number of entries in valid exits entry is zero:
		say "none.";
	otherwise:
		say "[valid exits entry].";

to decide if (way - a direction) is viable for (place - a room):
	if the room way of place is a room, yes;
	no;
		
to populate the exit table:
	repeat with R running through rooms:
		let L be a list of directions;
		choose a blank row in the table of exit lists;
		now where entry is R;
		repeat with D running through directions:
			if D is viable for R, add D to L, if absent;
		if the number of entries in L is zero:
			now valid exits entry is {};
		otherwise:
			now valid exits entry is L;
			
table of exit lists
where (a room)	valid exits (a list of directions)
--	--
with 10 blank rows [this will probably need to increase] 

edit although this is in my current wip. lol! it seems I spent a lot of time on this post

after looking when the number of viable directions is at least one:
	say "Exit[if the number of viable directions is at not 1]s[end if]: [list of viable directions].";
	continue the action;
	
definition: a direction (called the exit) is viable if the room it of the location is a room.

Based on personal experience, I don’t think your method is going to produce any slowness anyone can detect, or even which will make things worse even if you start to do lots of other very slow-making things.

However, I’d encourage you to test it. A way you can is with the developer tools in Lectrote. Build your game as glulx then run it in Lectrote. Turn developer mode on. In the console pane, you will see the execution time of each entered command in milliseconds. Now because of how Lectrote works, the first run of each command will take longer than subsequent ones. But if you run a command repeatedly, you’ll probably work out the average execution time. Also, these times will be a mite longer than in other interpreters, on average.

So for your game, compare the movement command execution time (walk around in a square or something, to provoke one room desc. after another) of a build that autogenerates the exits versus one that doesn’t. See if the execution time changes at all, or by a few milliseconds, or by horrible magnitudes. That will show you the effect of the programming approach. My prediction is negligible effect, but let me know!

-Wade

Thank you! Even if @JoshGrams got there first I’m marking this the solution since it covers the “when play begins” element I was hoping for. If either of you want a mention in the credits for the help, I’d be more than happy to pop one in.

Based on a couple of responses here including @severedhand’s it also sounds as though my original approach probably wouldn’t bog the game down too much anyway, but I think it’ll be handy to be able to eliminate those very frequent loops for the sake of comparison if nothing else. I’ve already run into performance issues just from letting the skein get too unwieldy, so any opportunity to eliminate some of the stuff that’s running each turn is welcome even if I ultimately add it back in for the final version.

Yeah, the first time I learned my massive skein was slowing me down by seconds, I was all like ‘What?!’. It can make you paranoid about slowdown. But of course there’s no skein buildup in a released game.

-Wade