Wandering NPCs, doors, and windows

I’m not sure how much of this is a coding question and how much of this is a design question. Apologies if I wander a bit. I’ve posted a stripped-down version of the code I’m working with at the end of this post.

So I have a number of wandering, NPCs that track an object or location that they’re currently trying to reach, and each of which makes a move calculated using the best route from ... to ... each turn that they’re currently seeking a goal.

I also have a set of windows that I want the PC to be able to climb through – there’s at least one “gain access to an area by managing to climb through a window” puzzle in the middle game, so I’ve declared a window is a kind of door and written rules that check whether windows are physically accessible based on whether the PC has manipulated the physical environment appropriately to solve the puzzle.

The problem is that the best route phrases will happily route the NPCs through windows even when there are non-window paths available, which I’d like to avoid. (It’s incongruous for an NPC to say “Welp, I’m going to bed now” and then to get to their bedroom by jumping out the bathroom window and climbing back in through their bedroom window instead of just walking down the hallway, even if it is the same number of moves.)

I guess I’m wondering what the best way to control this behavior is. Ideally, I’d like to be able to place conditions on the route found by the the best route phrase. Writing with Inform 6.14 mentions a ...using [type of object] qualifier, but is a little vague about the syntax. I’ve tried every variation in phrasing on using rooms and non-window doors that I can think of, but nothing works. (I’d love a way to say “avoiding windows” or “avoiding windows if possible,” but I don’t see any indication that that’s a phrase that might work.)

Alternately, I suppose I could implement windows without making them a kind of door, but that also seems like a lot of work, because doors are special cases in a lot of ways: they appear in two rooms; there are a whole bunch of rules applying to them that I’d rather not rewrite; they already work with Locksmith; and so forth.

Does anyone have a suggestion on what they easiest way is to keep NPCs from jumping out windows?

Here’s a pared-down sample of what I’m currently doing. I’d love to be able to prevent the meter-reader from coming through windows.

"temp" by "Patrick Mooney"

The story headline is "A scratchpad".

Include Locksmith by Emily Short.


Section - Door stuff

A window is a kind of door. A window is usually locked.

A door is usually lockable. A door is usually closed.

A door is usually undescribed. The Can't Go Through Undescribed Doors rule is not listed in any rulebook.


Section - Game World

Living Room is a room. "Just an empty space with carpeting. The movers haven't delivered your stuff yet.[if someone is in Front Lawn][paragraph break]Outside the window, you can see [the list of people in Front Lawn].[end if][paragraph break]Your bedroom is north. Your bathroom is northeast. Your front lawn is to the southwest. You can head out to the garage to the east.  You could in theory climb through your front window, to the south, and out onto the lawn, if you'd like.".  

Front Lawn is a room.  "A patch of grass. At least the previous owners bothered to mow out here shortly before selling the house.[paragraph break]Your living room is through your front door, to the northwest, or through your front window, to the north. The garage door leads northeast to your garage. The back lawn is through a gate to the west.".
the Living Room Window is a window. It is south of Living Room and north of Front Lawn.
Front Door is a locked door. It is northwest of Front Lawn and southwest of Living Room.

Bedroom is a room.  "If your stuff ever arrives, this is going to be the coziest bedroom ever.[paragraph break]Your bathroom is east; the living room is to the south. Your back lawn is through the window to the west.".
the Bedroom door is a door. It is north of Living Room and south of Bedroom.

Bathroom is a room. "All the standard fixtures, plus some very stylish avocado-green linoleum peeling on the floor.[paragraph break]Your bedroom is west. Your living room is southwest.".
the First Bathroom Door is a door. It is east of Bedroom and west of Bathroom.
the Second Bathroom Door is a door. It is northeast of Living Room and southwest of Bathroom.

Garage is a room. "An empty space with an oil-stained concrete floor.[paragraph break]Your living room is back through the door to the west. Your front lawn is through the garage door to the southwest.".
the Interior Garage Door is a door. It is west of Garage and east of Living Room.
the Exterior Garage Door is a locked door. It is northeast of Front Lawn and  southwest of Garage.

Back Lawn is a room.  "The fence keeps the neighbors from seeing the yard, and the previous owners let the grass get pretty shaggy back here.[paragraph break]Your front lawn is through the gate to the south, or you could climb in through your bedroom window, to the west.".
The fence is scenery in Back Lawn.  The water meter is scenery in Back Lawn.
the Gate is a locked door. It is  west of Front Lawn and south of Back Lawn.
the Bedroom Window is a window. It is west of Bedroom and east of Back Lawn.

Street is a room. "Just an ordinary street in suburbia, just like jillions of other ordinary streets in suburbia.[paragraph break]Your new house is to the north." It is south of Front Lawn.


Section - People and Movement

A person can be awake or asleep. A person is usually awake.

A person has an object called the destination. The destination of a person is usually nothing. 

Every turn (this is the People Seek Their Goals Rule):
	let L be the list of awake people;
	repeat with P running through L:
		if the destination of P is not nothing:
			let the destination sought be an object;
			if the destination of P is not a room:
				now the destination sought is the location of the destination of P;
			otherwise:
				now the destination sought is the destination of P;
			if P is enclosed by the destination sought:
				now the destination of P is nothing;
			otherwise if P is awake and the destination sought is a room:
				let the way be the best route from the location of P to the destination sought, using doors;
				if the way is not a direction:
					let the way be the best route from the location of P to the destination sought, using even locked doors;
				if the way is a direction:
					let old location be the location of P;
					try P going the way.


Section - Characters

The player carries the master key. The master key unlocks Front Door. The master key unlocks Garage Door. The master key unlocks Bedroom Window. The Master Key unlocks Living Room Window. The master key unlocks Gate.

Your husband is a man in Bathroom. 
Every turn: 
	if a random chance of one in four succeeds:
		now the destination of husband is a random room.

Every turn when husband is visible, say "'Honey,' says your husband, 'have you seen [one of]the[or]my[purely at random] [one of]electric[or]cryogenic[or]8-bit[or]miniaturized[or]simplified[or]steam-powered[or]discount[or]hipster[or]enhanced[or]new[or]watermelon-flavored[or]damaged[or]chiptune-producing[at random] [one of]hedge clippers[or]razor[or]laptop[or]shirt press[or]skinny jeans[or]remote control[or]waffle iron[or]George Foreman grill[or]broom[or]foot locker[or]coffee pot[or]letter opener[or]screwdriver[or]reanimation kit[at random]?'[paragraph break]'Sorry, no,' you sigh."

Husband carries the garage-door opener. The opener unlocks Exterior Garage Door.

The Meter-Reader is a woman in Street. The description of the meter-reader is "A nondescript person in a nondescript uniform." The destination of the Meter-Reader is the water meter.

Every turn when the Meter-Reader can see the water meter:
	If the player can see the water meter:
		say "The meter-reader, glancing at the meter, looks very angry.";
	now the destination of the Meter-Reader is  the player.

Every turn when the Meter-Reader can see the player:
	if the destination of the Meter-Reader is the water meter:
		say "'Sir? Sir? I need to get access to your water meter to read it, please,' says the meter reader.";
	else:
		say "The meter-reader glowers at you. 'You've tricked out your water meter to underrepresent your usage,' he says. 'That's fraud.'[paragraph break]'No!' you yell. 'We just moved in this morning! That must have been done by the previous owners!'[paragraph break]'Tell it to the judge,' sneers the meter-reader, 'just like everyone else does. You're going away for a long, long time, bucko.'";
		end the story saying "Jail is no fun.".

There’s an error in your example; the description of the Back Lawn says that the bedroom window is to the west, but it’s actually to the east. :slight_smile:

The only valid syntax for the using part are the two options given – using doors or using even locked doors (or neither of these).

You can specify additional conditions on the rooms being travelled through, e.g. with best route from A to B through visited rooms, using doors. (And you can use any other adjective from visited as you like.) Unfortunately I don’t think there’s anything you can check regarding a single room that does anything useful with regard to the connection between the rooms.

You can specify a custom relation that will tell you whether two rooms are adjacent via something other than a window:

To decide whether (A - a room) and (B - a room) are non-window adjacent:
	if A is not adjacent to B, decide no;
	let D be the best route from A to B, using doors;
	if D is nothing, decide no;
	if the door D from A is a window, decide no;
	decide yes.

Non-window adjacency relates a room (called A) to a room (called B) when A and B are non-window adjacent.

But while Inform does have a syntax for finding “routes” via relations, this only works for explicitly declared various-to-various relations, not a conditional relation as defined above. (If you try it anyway then you get a Runtime Problem.)

There certainly are ways to allow only certain actors to traverse through doors – so you can block the meter reader from jumping through the window easily enough – but unfortunately the best route evaluation doesn’t take a specific actor into account, so the NPC will still think they can do it and will get permanently stuck trying and failing to use the window.

So I don’t think there are any great options, unfortunately (not short of creating a new version of the route-finding code, anyway).

Short of that, I think the only options are to either make the windows not a kind of door, or to ensure that the windows are never unlocked (as this will block route-finding) while overriding rules to let the player go through them anyway. (Either ignoring the state entirely, or substituting an alternative adjective to keep track of the state separately.)

I had a bit of a play with the latter, but using Locksmith complicates things since it defines a lot of extra rules that rely on locked.

1 Like

Well, there goes my hope for a Best Implementation XYZZY for “The Meter-Reader.” Darn it.

Thank you, Gavin. That’s a helpful way to start thinking about it. If you don’t mind, I have two questions about your custom relation:

To decide whether (A - a room) and (B - a room) are non-window adjacent:
	if A is not adjacent to B, decide no;
	let D be the best route from A to B, using doors;
	if D is nothing, decide no;
	if the door D from A is a window, decide no;
	decide yes.

Non-window adjacency relates a room (called A) to a room (called B) when A and B are non-window adjacent.

First: when you say let D be the best route from A to B, using doors, is it not the case that the selected route D might be through a window, even if there are other routes available? (I’m thinking about the two connections between Living Room and Front Lawn in the example I wrote earlier, both of which are through doors, one of which is a window.) In that case, if I’m reading the code right, the relation will decide no, even though there’s an option available through a non-window door. Am I reading that correctly?

Also, what happens with the decision at the line if the door D from A is a window, decide no when D is not a door, but another room?

Again, thank you. That’s helpful.

Yes, that’s correct. If you’re going to do that sort of thing, you need to make sure that you don’t have doors and windows linking the same rooms together (i.e. your windows are always shortcuts, not merely alternatives).

(It’s not really a good idea to do that anyway, as Inform doesn’t really have a good way to find the direction between two rooms other than via this sort of route-finding phrase, unless you already know the door between them.)

D isn’t a door, it’s a direction.

Admittedly I didn’t test that very thoroughly, but I’m fairly certain that it will just be nothing, which of course is not a window, so it will skip that check. This would produce the correct answer since it means there isn’t a window between the two rooms.

If I wasn’t in the middle of writing a new Minecraft mod at the moment (for… Reasons™) I might have a go at adding door filters to the route finder. But that’s a bit too heavy right at the moment.

1 Like

In that case, you could limit the path to rooms that are indoors or outdoors. The Meter-Reader, if outside, could take a step via outdoors rooms towards the front door, then go in, then move towards the meter via indoors rooms.

1 Like

Sorry, see that non-window-adjacent has already been suggested :grinning:

Lots of good advice. Thank you again, Gavin.

I think what I’m going to wind up doing is hand-waving the problem by cutting off routes through windows by marking some rooms – those right outside problem windows – as not NPC-traversable:

A room can be NPC-traversable. A room is usually NPC-traversable.  

Rear Walkway and Back of Alley are not NPC-traversable.

… and wrapping that into the best route logic (using NPC-traversable rooms).

Alternatively, going with the idea of navigating via relations, you could set up relations before play begins as connections between rooms that don’t involve a window:

Section - Route-finding

Direct-adjoinment relates various rooms to each other. The verb to directly-adjoin means the direct-adjoinment relation.

When play begins:
	Repeat with R running through rooms:
		Repeat with T running through rooms:
			Unless R directly-adjoins T: 	[if there's not already a relation established]
				If R is adjacent to T:
					Now R directly-adjoins T; [adjacent means no doors]
					next;
				Let moves be the number of moves from R to T, using even locked doors;
				Unless moves is 1: [no direct route]
					next;						 			
				Let the way be the best route from R to T, using even locked doors;		[then check for one and create one if appropriate]
				Let the target be the room-or-door the way from R;
				If the target is a window:
					next;
				if the target is a door:
					now R directly-adjoins T;
				if the target is a room:
					now R directly-adjoins T;

Then move your NPCs from room to room, route-finding using these relations instead of the map:

let the next-room be the next step via the direct-adjoinment relation from the location of P to the destination sought

This method has the added advantage that, if need be, these initial relations could be recalculated or amended in play, so for example, if an NPC became ‘desperate’ to reach their objective, direct-adjoinment relations between rooms connected only by windows could be included, or if a large window became invitingly left open, a specific direct-adjoinment relation between the rooms either side could be created.

1 Like

PS it occurs to me that there is a potential problem with the above code for automatically establishing direct-adjoinment relations when there happens to be two or more door-like connections between a given pair of rooms, at least one of which is a window- as is the case in the example scenario between the Living Room and the Front Lawn. if Inform decides when considering the situation from both sides that ‘the best route’ between the two rooms happens to be the window, it will not create a direct-adjoinment relation. As it happens, this doesn’t seem to occur when I use the code in the example scenario, but as it seems to be undocumented how ‘the best route’ is decided when two equivalent choices of ‘door’ are presented (possibly it’s random, or at least not readily predictable) it might be unwise to rely on ‘plain’ doors always being preferred to windows.

It might therefore be advisable in the specific case where alternate routes via doors/windows are defined between two rooms to explicitly declare the relation:

The Living Room directly-adjoins the Front Lawn.
1 Like

This post of mine will be (is?) slightly sucky, as it disregards the original preference for a solution involving the best route functionality. And you all in this topic have worked on that a lot now, and maybe you @patrick_mooney are close or getting there.

However, in my decade on this forum I have occasionally benefited from a ‘toss out the kitchen sink’ idea when I’ve been working on some mechanism for ages but not quite got it. So I thought I’d chuck in the total alternative idea of writing your own path-finding mechanism of some kind, possibly involving hardcoding routes into the game… or having a routine that will generate the routes, then you embed them in your source, so that you can re-run the routine and re-paste the output if the routes change. Or the game just works them out when it boots.

This suggestion of rolling your own used to come up more often in the past when authors were having problems with Inform’s route-finding, especially to help with slow execution speeds on less powerful devices. Presumably the latter’s less of a problem now, but it’s not gone. For instance there’s the following topic, which may help you learn a bit more about the built-in route finding code anyway, or give other ideas if nothing else:

-Wade

1 Like

And now, in a further iteration, some I6 hacking of the template files (aka Standard Rules or Library) to implement the idea of windows as a distinct kind of door in Inform route-finding via the map. It works with either fast or slow route-finding.

Apart from cutting and pasting the I6 inclusions section verbatim (which also defines window as a kind of door, and the global flag ‘use windows’ all you have to do is write ‘Now use windows is true’ to include windows in route-finding and ‘Now use windows is false’ to exclude windows from route-finding. Windows otherwise act exactly like doors, which can be included or excluded from route-finding by the fact of being doors, or by being locked or unlocked, using the ‘using doors’ or ‘using even locked doors’ phrasing.
You can of course also continue to write your own I7 rules that apply only to windows or only to doors or to both.

With a bit more work, it would be possible to implement a more generic door filter as an I7 description of objects, similar to the one that can be applied to rooms for route-finding, but the I6 code presented here keeps changes to the I6 templates to the minimum by following the way in which ‘locked’ and ‘unlocked’ are treated as a simple hard-coded filter in the library code.

"Test_Route-finding" by PB


Section - I6 inclusions

Include (- Global use_windows; Global last_use_windows; -) after "Definitions.i6t".
Include (-

Replace SlowRouteTo;
Replace ComputeFWMatrix;
Replace MapRouteTo;

-)  before "Relations.i6t".

Include (-

[ SlowRouteTo from to filter use_doors  obj dir in_direction progressed sl through_door;
	if (from == nothing) return nothing;
	if (to == nothing) return nothing;
	if (from == to) return nothing;
	objectloop (obj has mark_as_room) obj.vector = 0;
	to.vector = 1;
	!print "Routing from ", (the) from, " to ", (the) to, "^"; !####Rem thi sout
	while (true) {
		progressed = false;
		!print "Pass begins^";
		objectloop (obj has mark_as_room)
			if ((filter == 0) || (filter(obj)))
				if (obj.vector == 0)
					objectloop (dir ofclass K3_direction) {
						in_direction = Map_Storage-->((obj.IK1_Count)*No_Directions + dir.IK3_Count);
						if (in_direction == nothing) continue;
						!print (the) obj, " > ", (the) dir, " > ", (the) in_direction, "^";
						if ((in_direction)
							&& (in_direction has mark_as_room)
							&& (in_direction.vector > 0)
							&& ((filter == 0) || (filter(in_direction)))) {
							obj.vector = dir | WORD_HIGHBIT;
							!print "* ", (the) obj, " vector is ", (the) dir, "^";
							progressed = true;
							continue;
						}
						if (use_doors && (in_direction ofclass K4_door) && (~~((in_direction ofclass window_class) && (use_windows == false))) &&
							((use_doors & 2) ||
							 (in_direction has open) ||
							 ((in_direction has openable) && (in_direction hasnt locked)))) {
							sl = location; location = obj;
							through_door = in_direction.door_to();
							location = sl;
							!print "Through door is ", (the) through_door, "^";  ! rem this out
							if ((through_door)
								&& (through_door has mark_as_room)
								&& (through_door.vector > 0)
								&& ((filter == 0) || (filter(through_door)))) {
								obj.vector = dir | WORD_HIGHBIT;
								!print "* ", (the) obj, " vector is ", (the) dir, "^";
								progressed = true;
								continue;
							}
						}
					}
		objectloop (obj has mark_as_room) obj.vector = obj.vector &~ WORD_HIGHBIT;
		if (from.vector) return from.vector;
		if (progressed == false) return from.vector;
	}
];

#ifdef FAST_ROUTE_FINDING;

[ ComputeFWMatrix filter use_doors  oy ox oj axy ayj axj dir diri nd row;
	! print "Recomputing Matrix^";
	objectloop (oy has mark_as_room) if (oy.room_index >= 0)
		objectloop (ox has mark_as_room) if (ox.room_index >= 0)
			FWMatrix-->(oy.room_index*NUM_ROOMS + ox.room_index) = 0;

	objectloop (oy has mark_as_room) if (oy.room_index >= 0) {
		row = (oy.IK1_Count)*No_Directions;
		for (diri=0: diri<No_Directions: diri++) {
			ox = Map_Storage-->(row+diri);
			if ((ox) && (ox has mark_as_room) && (ox.room_index >= 0)) {
				FWMatrix-->(oy.room_index*NUM_ROOMS + ox.room_index) = No_Directions + diri;
				continue;
			}
			if (use_doors && (ox ofclass K4_door) &&
				(((use_doors & 2) && (~~((ox ofclass window_class) && (use_windows == false)))) || (DoorRoutingViable->(ox.IK4_Count)))) {  !#### altered
				@push location; location = oy;
				ox = ox.door_to();
				@pull location;
				if ((ox) && (ox has mark_as_room) && (ox.room_index >= 0)) {
					FWMatrix-->(oy.room_index*NUM_ROOMS + ox.room_index) = No_Directions + diri;
					continue;
				}
			}
		}	
	}

	objectloop (oy has mark_as_room) if (oy.room_index >= 0)
		objectloop (ox has mark_as_room) if (ox.room_index >= 0) {
			axy = (FWMatrix-->(ox.room_index*NUM_ROOMS + oy.room_index))/No_Directions;
			if (axy > 0)
				objectloop (oj has mark_as_room) if (oj.room_index >= 0) {
					ayj = (FWMatrix-->(oy.room_index*NUM_ROOMS + oj.room_index))/No_Directions;
					if (ayj > 0) {
						!print "Is it faster to go from ", (name) ox, " to ",
						!   (name) oj, " via ", (name) oy, "?^";
						axj = (FWMatrix-->(ox.room_index*NUM_ROOMS + oj.room_index))/
							No_Directions;
						if ((axj == 0) || (axy + ayj < axj)) {
							!print "Yes^";
							FWMatrix-->(ox.room_index*NUM_ROOMS + oj.room_index) =
								(axy + ayj)*No_Directions +
								(FWMatrix-->(ox.room_index*NUM_ROOMS + oy.room_index))%
									No_Directions;
						}
					}
				}
		}
];

#endif;

-).


Include (-

[ MapRouteTo from to filter use_doors count  oy oyi ds;
	if (from == nothing) return nothing;
	if (to == nothing) return nothing;
	if (from == to) return nothing;
	if ((filter) && (filter(from) == 0)) return nothing;
	if ((filter) && (filter(to) == 0)) return nothing;
	! print "filter changed:",(last_filter ~= filter), "; use doors changed:",(last_use_doors ~= use_doors),"; use windows changed:",(last_use_windows ~= use_windows),"^"; !####Rem this out
	if ((last_filter ~= filter) || (last_use_doors ~= use_doors) || (last_use_windows ~= use_windows)) map_has_changed = true;  !#### changed
	oyi = 0;
	objectloop (oy has mark_as_room) {
		if ((filter == 0) || (filter(oy))) {
			if (oy.room_index == -1) map_has_changed = true;
			oy.room_index = oyi++;
		} else {
			if (oy.room_index >= 0) map_has_changed = true;
			oy.room_index = -1;
		}
	}
	oyi = 0;
	objectloop (oy ofclass K4_door) {
		ds = false;
		if ((use_doors & 2) ||
			(oy has open) || ((oy has openable) && (oy hasnt locked))) ds = true;
		if ((ds == true) && (oy ofclass window_class))  {ds = use_windows; } !#### inserted
		if (DoorRoutingViable->oyi ~= ds) map_has_changed = true;
		DoorRoutingViable->oyi = ds;
		oyi++;
	}
	if (map_has_changed) {
		#ifdef FAST_ROUTE_FINDING; ComputeFWMatrix(filter, use_doors); #endif;
		map_has_changed = false; last_filter = filter; last_use_doors = use_doors; last_use_windows =use_windows;  !#### changed
	}
	#ifdef FAST_ROUTE_FINDING;
	if (count) return FastCountRouteTo(from, to, filter, use_doors);
	return FastRouteTo(from, to, filter, use_doors);
	#ifnot;
	if (count) return SlowCountRouteTo(from, to, filter, use_doors);
	return SlowRouteTo(from, to, filter, use_doors);
	#endif;
];

-).



use windows is a truth state that varies. The use windows variable translates into I6 as "use_windows".
A window is a kind of door.  The window kind translates into I6 as "window_class".

Section - The Test Area



Lab is a room.
The lab window is a window. It is locked. It is east of Lab and west of Decontamination.
The tearoom is a room. it is south of Lab and west of the storeroom. The 
The access door is a door. It is closed and unlocked. It is south of Decontamination and north of the storeroom.
The decontamination door is a door. It is closed and locked. it is north of Decontamination and southeast of the corridor.
The decontamination window is a window. It is closed and unlocked. It is northwest of Decontamination and east of the corridor.
The  lab door is a door. It is closed and unlocked. It is north of the Lab and southwest of the corridor.
The lab roofspace is above the Lab. The west corridor roofspace is north of the lab roofspace.  The middle corridor roofspace is east of the west corridor roofspace. The east corridor roofspace is east of the middle corridor roofspace. The decontamination roofspace is above Decontamination and south of the east corridor roofspace.
The storeroom window is a door. It is closed and locked. It is southeast of the Lab and northeast of the storeroom

Section - When Play Begins

Use slow route-finding.
xyzzy is a direction that varies.
moves is a number that varies.
When play begins:
	Now xyzzy is the best route from Lab to Decontamination;
	Now moves is the number of moves from Lab to Decontamination;
	Say "The best route to Decontamination using no doors or windows is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";
	Now xyzzy is the best route from Lab to Decontamination, using doors;
	Now moves is the number of moves from Lab to Decontamination, using doors;
	Say "The best route to Decontamination using (unlocked) doors (but no windows) is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";
	Now xyzzy is the best route from Lab to Storeroom, using doors;
	Now moves is the number of moves from Lab to Storeroom, using doors;
	Say "The best route to the storeroom using (unlocked) doors (but no windows) is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";
	Now xyzzy is the best route from Lab to Decontamination, using even locked doors;
	Now moves is the number of moves from Lab to Decontamination, using even locked doors;
	Say "The best route to Decontamination using even locked doors (but no windows) is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";
	Now use windows is true;
	Now xyzzy is the best route from Lab to Decontamination, using even locked doors;
	Now moves is the number of moves from Lab to Decontamination, using even locked doors;
	Say "The best route to Decontamination using even locked doors or locked windows is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";
	Now xyzzy is the best route from Lab to Decontamination, using doors;
	Now moves is the number of moves from Lab to Decontamination, using doors;
	Say "The best route to Decontamination using (unlocked) doors or (unlocked) windows is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";
	Say "[bold type]You unlock the storeroom window.[roman type][paragraph break]";
	Now the storeroom window is unlocked;
	Now xyzzy is the best route from Lab to Storeroom, using doors;
	Now moves is the number of moves from Lab to Storeroom, using doors;
	Say "The best route to the storeroom using (unlocked) doors or (unlocked windows) is [printed name of xyzzy] via the [room-or-door xyzzy from location] (in [moves] move[s]).[paragraph break]";

2 Likes

Oops!

Have just edited the code to remark-out the line where it was helpfully (for debugging purposes) announcing that it is recomputing the matrix for fast route-finding… :roll_eyes: and fix a bug in the code to determine whether the status of ‘window-doors’ has changed, in order to trigger a recompute ( a relic from the original version, when windows were excluded from route-finding under all circumstances)

1 Like

Ha, thank you, Peter! I will give it a try this week and see how it works!