Standardizing ending line breaks

This might be very picky, but I’m trying to make sure that the last line reported or said after the results of a turn are processed is always at least 1 empty line, to separate the prompt from the preceding text.

In other words this should never happen:

>command noun
Said response.
>

This should happen instead:

>command noun
Said response.

>

The default things reported by the game seem to do the desirable latter, but if I ever add a “say x” line to my code, it seems to do the undesirable former. Does anyone have a recommendation for the best way to go about managing this?

I’m interested in this too. I’ve been doing a game with lots of “[say something]” type of constructions in it, which create extra line breaks. I believe the parser is expecting some text besides the subtitution, which becomes its own thing.

One way to handle this if your project isn’t large, is to just use the “Output Filtering” extension and not worry about it.

I did some experimentation though.

[rant][code]“Test”

A room has a number called cherryval. Cherryval is usually 15.

To say cherries: say “With punctuation, there are [Cherryval in words] delicious cherries!”.

To say cherries2: say “Without punctuation, there are [Cherryval in words] delicious cherries”.

Orchard 1 is a room. “[Cherries]”.

Orchard 2 is north of Orchard 1. "[Cherries2] ".

Orchard 3 is north of Orchard 2. “Room description words. [cherries]”.

Orchard 4 is north of Orchard 3. “Room description words. [cherries2]”.

Orchard 5 is north of Orchard 4. “Room description, no space.”

Orchard 6 is north of Orchard 5. "Room description, space. "

Orchard 7 is north of Orchard 6. “Room description then cherries, no period after bracket. [cherries]”

Orchard 8 is north of Orchard 7. “Room descripition then cherries, period after bracket. [cherries].”

Orchard 9 is north of Orchard 8. "Room description, then cherries, space after bracket. [cherries] "

Orchard 10 is north of Orchard 9. "Room description, then cherries, period space after bracket. [cherries]. "

Orchard 11 is north of Orchard 10. “Room description then cherries2, no period after bracket. [cherries2]”

Orchard 12 is north of Orchard 11. “Room descripition then cherries2, period after bracket. [cherries2].”

Orchard 13 is north of Orchard 12. "Room description, then cherries2, space after bracket. [cherries2] "

Orchard 14 is north of Orchard 13. "Room description, then cherries2, period space after bracket. [cherries2]. "

[/code]

[code]

Test
An Interactive Fiction
Release 1 / Serial number 131024 / Inform 7 build 6G60 (I6/v6.32 lib 6/12N) SD

Orchard 1
With punctuation, there are fifteen delicious cherries!

n

Orchard 2
Without punctuation, there are fifteen delicious cherries

n

Orchard 3
Room description words. With punctuation, there are fifteen delicious cherries!

n

Orchard 4
Room description words. Without punctuation, there are fifteen delicious cherries

n

Orchard 5
Room description, no space.

n

Orchard 6
Room description, space.

n

Orchard 7
Room description then cherries, no period after bracket. With punctuation, there are fifteen delicious cherries!

n

Orchard 8
Room descripition then cherries, period after bracket. With punctuation, there are fifteen delicious cherries!
.

n

Orchard 9
Room description, then cherries, space after bracket. With punctuation, there are fifteen delicious cherries!

n

Orchard 10
Room description, then cherries, period space after bracket. With punctuation, there are fifteen delicious cherries!
.

n

Orchard 11
Room description then cherries2, no period after bracket. Without punctuation, there are fifteen delicious cherries

n

Orchard 12
Room descripition then cherries2, period after bracket. Without punctuation, there are fifteen delicious cherries.

n

Orchard 13
Room description, then cherries2, space after bracket. Without punctuation, there are fifteen delicious cherries

n

Orchard 14
Room description, then cherries2, period space after bracket. Without punctuation, there are fifteen delicious cherries.

n
You can’t go that way.

[/code][/rant]

This is consistently a pain in the ass. Or rather, inconsistently a pain in the ass.

If you write

say "Foo."

…where the string ends with a period (or other sentence-ending punctuation) (and no extra spaces), you’ll generally get the right result.

If not, add “[line break]” as needed.

I’m need to revisit this question and expand on it. Perhaps I’m too neurotic, but I really want to control the formatting better in general. I understand how to “manually” control ADDING these line breaks, but I’m now getting some other behavior I’d like to adjust.

Basically, there are cases where an activity is producing an empty line, but I don’t want the line break in this case. For example if I have this (I realize this is an over the top example, but it’s simplified for ease of example):

Rule for printing the name of a room: do nothing.

This doesn’t print the name of the room, but it still returns an empty line where it otherwise would have had the room description. How do I prevent the empty line from appearing too?

Typically, in a situation like this, the line break will probably be generated after the activity has completed, by whatever code is calling that activity. For example, in the particular case of your example, that would be the “room description heading rule”, which looks like this:

Carry out looking (this is the room description heading rule):
	say bold type;
	if the visibility level count is 0:
		begin the printing the name of a dark room activity;
		if handling the printing the name of a dark room activity,
			issue miscellaneous library message number 71;
		end the printing the name of a dark room activity;
	otherwise if the visibility ceiling is the location:
		say "[visibility ceiling]";
	otherwise:
		say "[The visibility ceiling]";
	say roman type;
	let intermediate level be the visibility-holder of the actor;
	repeat with intermediate level count running from 2 to the visibility level count:
		issue library message looking action number 8 for the intermediate level;
		let the intermediate level be the visibility-holder of the intermediate level;
	say line break;
	say run paragraph on with special look spacing.

The second-to-last line, “say line break”, is what’s producing the line break.

Of course, you could simply remove the entire rule, with:

The room description heading rule is not listed in the carry out looking rulebook.

or override it with your own rule (which can then decide whether to invoke the original rule or not):

The modified room description heading rule is listed instead of the room description heading rule in the carry out looking rulebook.

This is the modified room description heading rule:
	if the location is the mystery room:
		do nothing;
	otherwise:
		abide by the room description heading rule.

Wow, that modified rule business is really neat. I think that’s the route I’ll fiddle with on various issues. Thank you!

The problem with line breaks IMO is that asking a game’s author to track how many line breaks since the last time something was printed (so he/she can figure out whether a line break needs to be printed before the next thing) is so fiddly and unfair.

The solution I use in my MUD is this: don’t ever allow a player to see more than one blank line. Build that right into the generic printing routines called by anything that needs to print to screen. That way, anytime I am unsure whether a blank line was just printed, I just go ahead and print another, secure in the knowledge that the player will never see two blank lines in a row because that’s impossible by design. I don’t ever have to scour all my MUD text functions to make sure they all print blanks either at the end or at the beginning – I don’t have to synchronise any whitespace printing at all.

So it’s impossible for my MUD to omit the blank line before a command line, because a blank line is built into the ‘print a command prompt’ routine and I can code it that way without fear of generating extra blank lines b/c of the above rule.

Of course, in order to like that solution, you have to agree with me that there is no need ever to print two blank lines in a row. (And we could always provide an override command in case anyone needs to temporarily turn off double-blank suppression.)

I don’t really know enough about Inform 7 to hack it in this manner. Maybe someday.

Ron Newcomb’s “Output Filtering” extension does this, but at a way heavy toll on system resources.

I can understand why, if it is done by ‘filtering’ (i.e. constantly searching output strings for certain characters). However, with ‘return’ (line break) characters specifically, additional search algorithms may not be necessary, since it is very likely that Inform 7 already breaks up text into separate return-delimited lines, internally, prior to (or during) printing. If that assumption holds (I’d be surprised if it didn’t), then duplicate blanks could be checked for at that stage of i7’s ordinary operations without any additional text filtering. The cost in running time should be able to be kept as low as one if/then statement plus one variable assignment (setting a ‘just-printed-a-blank’ boolean true or false) per printed line.

This is the way I do it in the MUD – no string filtering or recasting, I just hacked the actual ‘print a single return-delimited line’ routine to check if it has just printed an empty string (for that player – multiplayer complicates it only slightly). If it has, don’t print another for that player until a non-empty string has been printed for that player. The code is extraordinarily simple as long as it is placed in the correct spot. Any other spot and I would have to resort to searching lists of strings proactively.

I had a poke inside the Standard Rules looking for the most basic universal printing routine. Ultimately all the ‘print the X’ activities seem to just use the ‘say’ command in different ways so I suppose I’d have to directly hack the say command, and I don’t know how to do that or I would – or rather, I don’t know how to search for it effectively within the Standard Rules document. Nor do I know how to universally override such a basic i7 keyword as ‘say’: I was hoping finding the source code in the Standard Rules might give me a clue.

So yeah, as I thought earlier – I just don’t have the skill with this language to implement such stuff conveniently. (At one point I was trying to catalog the whole language in a style amenable to my brain but I sort of gave up tilting at that particular windmill and now I just learn whatever I need.)

Maybe I will have a look inside ‘Output Filtering’ at some point to check out where it’s doing what it’s doing, so I can target that code too, because I would love to not have to worry about spacing in i7. Ron’s code tends to be quite low level however (this is a compliment – I tried prying open his version of Custom Library Messages once & it was obviously quite efficient but difficult for me to grasp).

The say phrases are in section SR5/1/1. You can replace them by rewriting the section completely:

Section New say (in place of Section SR5/1/1 - Saying - Values in Standard Rules by Graham Nelson)

[save old stuff]
To natively say (something - text)
	(documented at ph_say):
	(- print (PrintText) {something}; -).
To natively say (value - sayable value of kind K)
	(documented at phs_value):
	(- print ({-printing-routine:K}) {-pointer-to:value}; -).

To natively say (something - number)
	(documented at phs_value):
	(- print (say__n={something}); -).

To natively say (ch - unicode character) -- running on
	(documented at phs_unicode):
	(- #ifdef TARGET_ZCODE; @push self; self = {ch}; @print_unicode self; @pull self;
	#ifnot; if (unicode_gestalt_ok) glk_put_char_uni({ch}); else print "?"; #endif; -).

To natively say (something - number) in words
	(documented at phs_numwords):
	(- print (number) say__n=({something}); -).
To natively say (something - time) in words
	(documented at phs_timewords):
	(- print (PrintTimeOfDayEnglish) {something}; -).
To natively say s
	(documented at phs_s):
	(- STextSubstitution(); -).
	

[new says]
To say (something - text):
	natively say "-=";
	natively say something;
	natively say "=-";

To say (value - sayable value of kind K):
	natively say ".-*";
	natively say value;
	natively say "*-.";

[... and so on]

It won’t catch all instances of printing text because some parts of the library/engine don’t use the I7 say phrase, and I’m not sure how helpful this is for what you’re trying to do because as you see the I7 phrases are just wrappers for the I6 print() routine. You’d need to write the new printing routines in I6 to change the newline behavior.

Thanks, Juhana! I am not unfamiliar with i6 so it may not be too hard for me to write new i6 printing fundamentals. I don’t really know how to hack them into i7 yet, but I kind of need to research how to hack i7 using i6 anyway - to implement some complex tools I’ve always wanted to convert over from the MUD. They’d be much easier to port to i6 syntax than i7. This could be a project. 8)

I think the I7 paragraphing system is all in the I7 compiler, so hacking those phrases probably won’t help. I think this is something which could just do with a really clear guide.

Dang. Thanks for the heads-up.

Would a Glulx I/O filter be useful?

[code]Section “Preventing Multiple Contiguous Blank Lines” (for Glulx Only)

Include (-
[ set_io_system phrase;
phrase = phrase–>1;
@setiosys 1 phrase;
];
-).
To switch to printing by (P - a phrase Unicode character -> nothing): (- set_io_system({P}); -).
To add (C - a Unicode character) to the story’s output: (- glk_put_char_uni({C}); -).

To decide what Unicode character is a newline: (- 10 -).

The number of contiguous newlines recently printed is a number that varies. The number of contiguous newlines recently printed is zero.
To print (C - a Unicode character) without multiple contiguous blank lines (this is printing without multiple contiguous blank lines):
if C is a newline:
if the number of contiguous newlines recently printed is at least two:
stop;
increment the number of contiguous newlines recently printed;
otherwise:
now the number of contiguous newlines recently printed is zero;
add C to the story’s output.

A startup rule (this is the prevent multiple contiguous blank lines rule):
switch to printing by printing without multiple contiguous blank lines.

Section “Demo”

There is a room.
Instead of looking:
say “X.[line break][line break]Y.[line break][line break][line break]Z.[line break][line break][line break]”.
[/code]

1 Like

Thanks EmacsUser that looks like it could become very helpful. I tested it though and the built-in demo didn’t work. I have trouble understanding the way its been hacked - for example I don’t get where it is that you’ve overridden the ‘say’ command. (But maybe you didn’t override it, and that’s why the demo doesn’t work?) Also I wonder how this sort of filtering compares to ‘Output Filtering’ in terms of performance.

If this works and isn’t slow I’d definitely splice it into my WIP!

Hmm, you are compiling for Glulx, not Z-code, right?

Glulx with Glk has three I/O systems: system 0 discards all output, system 1 runs all output through a function character by character, and system 2, which Inform uses by default, directs output through Glk so that it (usually) shows up in the interpreter. Apart from some bugs where Inform talks directy to Glk (which can be worked around if they give you trouble), all output—from says, prints, or otherwise—runs through the selected I/O system. The startup rule here switches from system 2 to system 1 and chooses the phrase just above as its handler function. That phrase works like the MUD output filter you described earlier, counting newlines and selectively passing characters on to Glk via glk_char_put_uni(…).

Output Filtering works differently, by leaving the I/O system alone and asking Glk to write to memory rather than the screen (under Glulx; the Z-machine has a similar sort of toggle built in). You then get a chance to manipulate the results as indexed text before they’re copied out. That means that Output Filtering incurs more overhead because it has to do memory management and deal with Inform’s currently sluggish heap bookkeeping (which has been revamped for the next release, though that doesn’t help you now). So the code above should be quite a bit faster. On the other hand, Output Filtering works on the Z-machine, and there are things that can be done looking at a whole response that are much trickier getting characters one at a time.

I thought I was since that’s what I normally use, but I had forgotten that I had recently switched that setting to zcode in order to try to produce a zcode-compatible release that includes Parchment (to troubleshoot an issue with playing Infocom games in a browser, mentioned in another thread). So, yeah, I forgot to switch it back – I just did and it works like a charm – thanks for the help!

Very useful explanation. So in order to achieve it you had switch the output system to character-by-character – I expect there to be some performance hit from that alone (don’t real-time games do this, too) but glad to hear it will have much less impact overall than ‘Output Filtering’.