Unwanted blank lines (yet again): taking multiple things

Hi, all. I’ve only been reading the Inform 7 documentation for 2 weeks, so I apologise if this is covered in there somewhere. I’ve spent hours looking for solutions here (and digressing to solutions to other problems I have. Hopefully this post willl “empty my stack”!).
Here’s my code:

"Glasses as containers 2" by Marbles

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

Tidying is an action applying to nothing.
Understand "tidy" as tidying.
Carry out tidying:
	repeat with item running through the list of empty glasses which are in the location:
		say "[item]: [run paragraph on]";
		try taking the item.
	
test me with "put marble in balloon/put whistle in flute/drop all/tidy/get all".

My problem is that, although “drop all” and “get all” use 1 line for each object, my “tidy” has a blank line between the line for each object. Is there any way of suppressing those blank lines?

(In case you’re wondering what version 1 was, it built a list of tidied glasses then said “You tidy up the [tidied glasses].”… which was even better… till I added the untakeable giant glass, which led to a line saying (cryptically) “That’s fixed in place.\n\n”.)

This seems to be working. I’m not sure why–Inform’s line spacing is pretty hard to follow, and I just banged on it until the right output came out.

"Glasses as containers 2" by Marbles

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

Tidying is an action applying to nothing.

Understand "tidy" as tidying.
Carry out tidying:
	repeat with item running through the list of empty glasses which are in the location:
		say "[item]: [run paragraph on]";
		try taking the item.
	
The standard report taking rule response (A) is "Taken.[conditional run paragraph on]".

test me with "put marble in balloon/put whistle in flute/drop all/tidy/get all".

Lab is a room.

Break suppressed is a truth state that varies.

To say conditional run paragraph on: 
	now break suppressed is true;
	say run paragraph on.
	
First before taking: now break suppressed is false.

Before reading a command when break suppressed is true:
	say line break;
	say run paragraph on;
	now break suppressed is false.

Thanks, Matt! That’s brilliant!
I see ‘Rulebooks’ is chapter 19 of ‘WI’. I’m still on chapter 5 (though I’ve jumped ahead many times, so probably read the equivalent of 8 chapters).

Now… if I could just figure out how to reach Lab :wink:
(I’ve marked your answer as a solution… though it would have been immediately under my question anyway!)

Ha, I usually put in a room name Lab in my code examples to make them compile, and I forgot I was working with an example that had a room in it.

This is a much simpler solution, which has the additional merit of being obvious why it works:

Using ‘try silently taking the item’ suppresses Inform from printing anything (and doing weird things with line spacing in the arcane depths of the Standard Rules) in the case of a successful take. In the case of a failed take, Inform will print a message explaining why as usual.

So, we then just need to check whether the take has been successful (in which case we will need to print our own 'Taken." confirmation) which we can can do by checking if the player is now carrying the item. If the take has been successful, by definition the player will now be carrying the item.

In the example below I’ve pedantically used the slightly opaque ‘say "[text of standard report taking rule response (A)][line break]’ to print ‘Taken.’ -the standard message for this context with the present Standard Rules. In practice this message is unlikely ever to vary, so for readability you could reasonably just replace this with ‘say “Taken.”’ (in which case you also don’t need the line break, because we’re explicitly ‘saying’ a period/full stop at the end of the text, which Inform automatically follows with a line break).

"Glasses as containers 2" by Marbles/PB

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

Tidying is an action applying to nothing.
Understand "tidy" as tidying.
Carry out tidying:
	repeat with item running through the list of empty glasses which are in the location:
		say "[item]: [run paragraph on]";
		try silently taking the item;
		if the player carries the item, say "[text of standard report taking rule response (A)][line break]";
	
test me with "put marble in balloon/put whistle in flute/drop all/tidy/get all".

That is simpler. Thanks.
Too bad one can’t write
try silently…
if the previous action succeeded…
Sometimes it’s not as easy to tell whether an action succeeded.
(In another action, I print the results in the ‘Carry out’ part because it’d be much more complicated to move it to a ‘Results’ part.)

This is yet another approach, which sets up tidying as the kind of action that can take commands acting on one or multiple objects (as in ‘tidy the champagne flute and the pint glass’ or ‘tidy all’.

If the simple command ‘tidy’ is typed, this is altered to be ‘tidy all’.

In fact commands like ‘tidy pint glass’ are also altered to ‘tidy all pint glass’, and ‘take all’ to ‘take all all’ (as can be confirmed by uncommenting the line 'say "[the player’s command is…]) but due to a happy quirk of the parser, the unneeded/unwanted ‘all’ in these constructs is simply ignored.

Two rules ensure that anything that’s not an empty glass is excluded when Inform is considering which objects to run through in the case of ‘tidy all’. The blanket ‘Rule for deciding whether all includes something while tidying: it does not.’ sounds as though it will exclude even empty glasses, but it is overruled by the more specific ‘Rule for deciding whether all includes an empty glass while tidying: it does.’

Because we can now ask to tidy things which are not empty glasses, these are captured by a non-specific Instead rule printing a polite refusal. Again, in the case of empty glasses this blanket rule is overruled by the more specific Instead rule dealing with them.

As this version uses Inform’s standard approach to dealing with actions on multiple objects, it appears to print its usual ‘Taken.’ messages ‘correctly’ without extra blank lines.

"Glasses as containers 3" by Marbles/PB

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

Tidying is an action applying to things.
Understand "tidy [things]" as tidying.

After reading a command:
	if the player's command includes "tidy":
		replace the matched text with "tidy all";
	[say "the player's command is: [the player's command]";]
		
Instead of tidying an empty glass:
	try taking the noun;
	
Instead of tidying:
	say "[We] [can] tidy empty glasses only.";	

Rule for deciding whether all includes an empty glass while tidying: it does.
Rule for deciding whether all includes something while tidying: it does not.
	
test me with "put marble in balloon/put whistle in flute/drop all/tidy/ look /get all".

PS re

(In case you’re wondering what version 1 was, it built a list of tidied glasses then said “You tidy up the [tidied glasses].”… which was even better… till I added the untakeable giant glass, which led to a line saying (cryptically) “That’s fixed in place.\n\n”.)

You could overcome this by making sure that only portable empty glasses are included in your list,

"Glasses as containers 4" by Marbles/PB

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

Tidying is an action applying to nothing.
Understand "tidy" as tidying.
Carry out tidying:
	let tidied glasses be a list of objects;
	repeat with item running through the list of portable empty glasses which are in the location:
		try silently taking the item;
		if the player carries the item:
			add the item to tidied glasses;
	say "[We] tidy away the [tidied glasses].";
	
test me with "put marble in balloon/put whistle in flute/drop all/tidy/look/get all".

Oops- also need to deal with the case where there is nothing to tidy:

"Glasses as containers 4" by Marbles/PB

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

Tidying is an action applying to nothing.
Understand "tidy" as tidying.
Carry out tidying:
	let tidied glasses be a list of objects;
	repeat with item running through the list of portable empty glasses which are in the location:
		try silently taking the item;
		if the player carries the item:
			add the item to tidied glasses;
	if tidied glasses is {}:
		say "There are no empty glasses to tidy away!";
	else:
		say "[We] tidy away the [tidied glasses].";
	
test me with "put marble in balloon/put whistle in flute/drop all/tidy/look/tidy/get all".

You can often use something like:

		try silently taking the item;
		if the rule succeeded:
			add the item to tidied glasses;

as long as you can be sure that the rulebook the code is returning from has set an appropriate outcome on exit (failure, success or no decision)

The above code works in this instance.

Isn’t there a way to do this? I’m sure I remember this being possible, maybe with an extension…

I guess any action getting as far as an After rulebook is likely to exit from there with success (by default) but I get the impression many coders are used to doing most stuff ‘quick & dirty’ with the Instead rulebook or exiting other action rulebooks with the ‘instead phrase’ & therefore exiting the action with failure regardless of actual changes to the game world, or exiting via report rulebook with (I think) default of ‘no decision.’

You’d need to think carefully about how you were coding all the exits from all your action rulebooks (as success or failure) to make using a conditional like this consistent across the board.

As I say, though, it seems to work as one might expect for the Standard Rules for taking, at least.

Given that this is probably not something which will be needed very often, the simple solution if there is no clear game-state to track as a surrogate for success (as in the case of taking something) may be to clear a global flag before calling the relevant action and set it before exiting at the Instead, Carry out or After stage of an action which is judged to have succeeded.

Here’s a sneaky way to do it, based on the principle that if the silent activity has failed, the Standard Rules will have printed some sort of response, but otherwise not.

Also elaborated to avoid a failed action printing only the second half of the ‘item: that’s fixed in place’ message etc. when running through a list ourselves rather than relying on the parser’s multiple-object action routines, by temporarily substituting our own messages for failure:

"Glasses as containers 5" by Marbles/PB

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

When play begins:
	now the carrying capacity of the player is 2;

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

[global variables to hold standard can't take messages and list of tidied glasses]
saved fixed in place response is a text that varies.
saved cant exceed capacity response is a text that varies.
tidied glasses is a list of objects that varies.
standard response rule flag is a truth state that varies.

Tidying is an action applying to nothing.
Understand "tidy" as tidying.
Carry out tidying:
	[set up temporary can't take messages]
	now tidied glasses is {};
	now saved fixed in place response is the substituted form of "[text of can't take what's fixed in place rule response (A)]";
	now can't take what's fixed in place rule response (A) is "[noun]: [saved fixed in place response][run paragraph on]";
	now saved cant exceed capacity response is the substituted form of "[text of can't exceed carrying capacity rule response (A)]"; 
	now can't exceed carrying capacity rule response (A) is "[noun]: [saved cant exceed capacity response][run paragraph on]";
	[do the tidying]
	repeat with item running through the list of empty glasses which are in the location:
		now standard response rule flag is false;
		try silently taking the item;
		if standard response rule flag is false: [this will now be true if a 'failed action message' has been printed]
			add the item to tidied glasses;
	[restore standard can't take messages]
	now can't take what's fixed in place rule response (A) is saved fixed in place response;
	now can't exceed carrying capacity rule response (A) is saved cant exceed capacity response;
	
Report tidying (this is the report tidying up rule):
	if standard response rule flag is true, say "[line break]";
	if tidied glasses is {}:
		say "There are no empty glasses [we] can tidy away!";
	else:
		say "[We] tidy away the [tidied glasses].";

After issuing the response text: now standard response rule flag is true. [this runs after printing any standard response]
	
test me with "put marble in balloon/put whistle in flute/drop all/tidy/i/look/tidy/i".

Per what Daniel said, I think “if the rule succeeded” will work here. I don’t exactly understand how the internals work as far as determining what rule/rulebook to check when an action has just been tried, but it usually seems to track when an action succeeded.

One potential issue here is that if you’re doing this, you really need to be careful about making sure actions’ success and failure tracks what actually happens. An “Instead” rule will result in a failure so you don’t want to do anything where you have an Instead rule that really makes the thing happen.

‘If the rule succeeded’ seems to work for (at least some) unaltered Standard Rules actions - certainly seems to do so for ‘taking’, but I haven’t found a way to make it work reliably for new actions.

They seem to fall out through an unnamed ‘last specific-action processing’ rule after the Reporting stage and the outcome of the rulebook appears to be set by that rule to success regardless of what one attempts to do in the Reporting stage. Exiting from Reporting with failure makes no difference, because this ‘last specific-action processing rule’ runs after that regardless.

It’s not easy to see how to change this without editing the Standard Rules directly (with what unintended consequences?), and being an unnamed rule it can’t I don’t think be removed from the rulebook or replaced directly from I7.

Yonks back Emily Short seemed to indicate in a thread here that there was deliberately no formal system for determining the outcome of actions, it being too messy and fraught with complications.

Yes, I think this is what is intended. From the Standard Rules:

The last specific action-processing rule: rule succeeds.

The thing about the action succeeding once you hit Report, even if you exit a Report rule with failure, is almost certainly intended. If you look at the chart in §12.2, there’s a dotted line marked “Success!” between the Check and Carry Out phases. That means that as soon as an action reaches the Carry Out phase it’s automatically considered a success. (This is said more explicitly somewhere else in the documentation, I think, but I’m not digging it up right now…) So… if you want the action to be considered to have failed, you need to stop it before the Carry Out phase somewhere.

I’m not exactly sure how Inform pulls this off under the hood. It may have something to do with exactly which stage rules get followed, which get abided by, and which get anonymously abided by, but that’s one of the trickier parts of Inform to me.

Which is why it’s not necessarily a great idea to make important things depend on “if the rule succeeded,” which is finicky–as you’ve been saying!

Ah! I think you’ve hit the nail on the head there. That is presumably the purpose of the last specific action-processing rule- to enforce this as a common point of exit from the action rulebook from all stages later than checking.

And… rejigging the code shows that it’s possible to exit the Check stage neatly in failure, showing the expected behaviour :grinning:

This goes back to what it says in the documentation about an action succeeding not necessarily meaning the player has achieved what he intended. It may mean that the intended action passed all checks but the hoped-for alteration in the game world still didn’t happen. In this case, for example, because the giant glass turned out to be fixed-in-place (although that should probably now ideally be checked for at the Check stage by ‘…list of portable, touchable, empty glasses…’, as well as checking that the player’s carrying capacity is not already full).

With multiple take actions being attempted within the main action, of course, some may succeed and some may fail & I guess philosophically, from an Inform perspective, at least 1 successful ‘take’ equals a successful ‘tidy’ since this implies having reached the Carry out stage.

"Glasses as containers 6" by Marbles/PB

R1 is a room. "A nondescript room." The printed name of R1 is "Somewhere". 

When play begins:
	now the carrying capacity of the player is 100

A glass is a kind of container.
a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass are glasses.
The player is holding a marble, a whistle, a bee, a brandy balloon, a champagne flute, a pint glass, a sherry glass, a tumbler, and a highball glass.
A giant glass is a glass in R1. It is fixed in place.

Definition: a container is empty if it does not contain something.

[global variables to hold standard can't take messages and list of tidied glasses]
saved fixed in place response is a text that varies.
saved cant exceed capacity response is a text that varies.
tidied glasses is a list of objects that varies.
standard response rule flag is a truth state that varies.

Tidying is an action applying to nothing.
Understand "tidy" as tidying.

Check tidying:
	now tidied glasses is the list of touchable empty glasses in the location;
	if tidied glasses is {}:
		say "There are no empty glasses to tidy away!";
		Rule fails;
		
Carry out tidying:
	[set up temporary can't take messages]
	now tidied glasses is {};
	now saved fixed in place response is the substituted form of "[text of can't take what's fixed in place rule response (A)]";
	now can't take what's fixed in place rule response (A) is "[noun]: [saved fixed in place response][run paragraph on]";
	now saved cant exceed capacity response is the substituted form of "[text of can't exceed carrying capacity rule response (A)]"; 
	now can't exceed carrying capacity rule response (A) is "[noun]: [saved cant exceed capacity response][run paragraph on]";
	[do the tidying]
	repeat with item running through the list of empty glasses which are in the location:
		now standard response rule flag is false;
		try silently taking the item;
		if standard response rule flag is false: [this will now be true if a 'failed action message' has been printed]
			add the item to tidied glasses;
	[restore standard can't take messages]
	now can't take what's fixed in place rule response (A) is saved fixed in place response;
	now can't exceed carrying capacity rule response (A) is saved cant exceed capacity response;

	
Report tidying (this is the report tidying up rule):
	if standard response rule flag is true, say "[line break]";
	if tidied glasses is {}:
		say "There are no empty glasses [we] can tidy away!";
	else:
		say "[We] tidy away the [tidied glasses].";

After issuing the response text: now standard response rule flag is true. [this runs after printing any standard response]

Every turn:
	say "Tidying up....";
	try tidying;
	if rule succeeded:
		say "Tidied up [number of entries in tidied glasses] glasses!";
	else if rule failed:
		say "Nothing to tidy!";
	else:
		say "In a dither!";
		
test me with "z/put marble in giant glass/drop all/z".

Thanks for all the replies! They’ve given me plenty to think about. (I’ve still got plenty of Wiritng With Inform to read.)
To be honest, I don’t really need a ‘tidy’ command. I’d just got to the bit in WI about ‘Definition:’ and did one for ‘empty’ containers. ‘Version 0’ of Glasses As Containers had to jump through hoops because I couldn’t write ‘empty glasses’.
I’ll probably stick with A drink is a kind of thing. The specification of a drink is "Represents a full glass.". Not so many loose ends to tie up!

I don’t know if this applies to what you’re doing, but you can write rulebooks with named outcomes. (Chapter 19:12)