Randomly writing functionally infinite diary pages

Hi again.

This is a flight of fancy but one which I think might be quite valuable for me as far as understanding Inform7.

Let’s say a player discovers a diary and, concerns over violations of privacy aside, reads it.

If I wanted to I could create a series of [one of] statements that would create each diary entry. However, I’d have to create a few options for each [one of] at random, which I have done before.

I was wondering if it were possible to create some kind of diary entry manufacturing code that randomly selects from a list of diary thoughts, a random number of times, without repeating selections.

If it were possible too I’d probably also add some randomness into the sentences as well for extra flavour.

As I say this is not essential but probably a handy thing to understand.

2 Likes

There are many ways to do this kind of thing, but I’ll give you a demo showing a few techniques.

As you’ve probably realised, using [one of]s can only go so far, because then you want [one of]s containing [one of]s… A good way to create the bits that will form the diary entry is with a series of tables. Then we choose a row from each table and print it. Individual grabs from tables can be static, or contain [one of]s.

The structure of prose generated this way can get a bit static, so you can vary it up by having some parts appear optionally using probability.

In the following demo, you’re in a garden, and can read six randomly generated entries from the gardener’s diary. This demo isn’t storing the entries for later use or letting you re-read them – it just creates six on the fly, not repeating the vast majority of stuff, and then says ‘You’ve read all six.’

The way it avoids repeating is by blanking out each table row that it happens to call upon. It’s by seeing if the first table has been entirely blanked out that it works out when to stop producing more entries. Because I decided there are six, and no table row will get used more than once, the minimum number of lines in each table is six. BUT there can be more than six in any table.

So here’ll you see a combination of techniques

  • randomly selecting rows from tables
  • using [one ofs] within some bits of prose
  • printing some bits only occasionally by using probability tests (e.g. if a random number between 1 and 3 is 1… do BLAH. Otherwise print nothing, or something else.) You can do these random number test both in code and even within brackets while printing prose.

You can use combinations of these techniques to put together your own randomly generated prose. There really is no upper ceiling on this kind of thing. I did this pretty quickly and it’s not bad for how fast I made it. Some saminess is okay here because there are only six entries and the diary has a particular topic.

Summary
the Garden is a room.

a table is a fixed in place supporter in the garden.

a diary is on the table.
page of diary is initially 0.

Rule for writing a paragraph about table:
	say "An open [diary] sits here on the table.";

Instead of examining diary:
	if the table of fragment one is empty:
		say "I've read all the diary entries.";
	otherwise:
		print a diary entry;


To print a diary entry:
	increment page of diary;[get the next date in the diary]
	if page of diary is 1:
		say "There are six entries in the diary. I start reading:[paragraph break]";
	say "'November [page of diary]: ";
	if a random number between 1 and 2 is 1:
		choose a random row in the table of intro-fragments;
		say "[prose entry]. ";
		blank out the whole row;
 	say "[one of]Today[or]This morning[or]This evening[or]This afternoon[or]Tonight[or]In the morning[or]In the evening[or]In the afternoon[at random] ";
	[print a sentence beginning]
	choose a random row in the table of fragment one;
	say "[prose entry] ";
	blank out the whole row;
	[print a plant name]
	choose a random row in the table of plants;
	say "[prose entry]. ";
	blank out the whole row;
	[print a final comment]
	choose a random row in the table of fragment two;
	say "[prose entry]";
	blank out the whole row;
	[extra comment made 1 in 3 times:]
	if a random number between 1 and 3 is 1:
		choose a random row in the table of fragment three;
		say ". [prose entry]";
		blank out the whole row;
	say ".'";

table of intro-fragments
prose
"I've got a headache, but I ignored it"
"Wanted to stay in bed. Didn't"
"I was excited for some reason"
"Time for another diary entry"
"Fog's coming in across the valley"
"Fog lifting"
"It rained again"

table of fragment one
prose
"I walked amongst"
"I tended"
"I cut some of"
"I smelled"
"I admired"
"I gathered some of"

table of plants
prose
"the ginger plants"
"the clivias"
"the orchids"
"the daisies"
"the daffodils"
"the roses"
"the agaves"
"the angel's trumpets"
"the sunflowers"
"the lilies"
"the dahlias"
"the ferns"
"the pansies"

table of fragment two
prose
"They're amazing"
"I hope they'll keep growing"
"It's not the right season for them"
"My work[if a random number between 1 and 2 is 1] on them[end if] [one of]is paying off[or]has paid off[at random]"
"I need to water them [one of]now[or]soon[or]tomorrow[or]in a couple of days[at random]"
"The soil here isn't right for them"
"There's too much clay in the soil, though"
"They're beautiful"

table of fragment three
prose
"I feel tired"
"The weather's getting warmer"
"I need to bring some more supplies over, though.."
"My sister should be here in a few days"
"I found a beehive nearby, too"
"I keep dreaming of that greenhouse"

Test me with "read diary/g/g/g/g/g/g".

-Wade

6 Likes

I don’t know if this is too abstract, but if you want to construct random(-ish) widgets (diary entries or anything else) by stitching together pre-made components and:

  • You don’t want duplicate widgets (that is, widgets with exactly the same components as a previously-generated widget)
  • You don’t care if individual components are repeated

…then you can accomplish this by just making sure the number of each kind of component is relatively prime to the number of each other kind of component.

That is, if your widget consists of selecting three components, call them foo, bar, and baz, then if foo has 2 elements, bar has 3 elements, and baz has 5 elements, then because the sizes are all relatively prime (in fact they’re all prime, but in this case we just care that 5 isn’t divisible by 3 or 2, 3 isn’t divisible by 5 or 2, and 2 isn’t divisible by 5 or 3), then you can construct the nth widget by selecting [ foo[n % 2], bar[n %3], baz[n % 5] ], and you won’t start repeating until you’ve gone through every possible widget. Where the total number of widgets is the product of the number of each component, or 2 * 3 * 5 = 30 widgets, so your first repeat would be on widget 31.

This works by just incrementing the widget number, but you could also use a PRNG with an appropriate period, which will (or should, if it’s a good PRNG) similar behavior.

4 Likes

I appreciate this. A lot.

It’ll take me a little to work through this but I’ll come back and let you know how I get on.

2 Likes

Won’t random numbers be fairly likely to repeat? If there are m possible phrases then the odds of seeing the same message on the first and second viewing is 1/m, no? Meaning one in m players will have this happen.

2 Likes

I meant using a PRNG to randomize the sequence (instead of just incrementing through all possible unique combinations), not using a PRNG to pick individual random integers in [0, number_of_unique_widgets).

As a practical matter it doesn’t add any more randomness to a single walk through all permutations, but it means that all walks through the permutations won’t be the same. This probably doesn’t matter for something like @hzdgmg’s specific question, but it can be useful in more general procgen stuff.

2 Likes

Hi again.

I’m fiddling with this a bit and let’s say a fellow wanted to introduce counting that informed a line of the text.

mname is a text that varies.

if month of diary is 1:
	now mname is "December".

if month of diary is 2:
	now mname is "January".


Rule for writing a paragraph about table:
	say "An open [diary] sits here on the table.";

Instead of examining diary:
	[if the table of fragment one is empty:
		say "Riveting! You've read the whole thing!!!";
	otherwise:]
	print a diary entry;


To print a diary entry:
	increment page of diary;[get the next date in the diary]
	if page of diary is 20 and month of diary is 1:
		say "There are almost a year of entries in this diary. You feel sort of bad reading it but, judging by the carnage outside the door, this person likely needs your help:[paragraph break]";
	say "'[mname][page of diary]: ";


This doesn’t seem to do what I expected. I have likely misunderstood 415: “Identity Theft”.

1 Like

I think I did it!

mname is a text that varies.

When play begins:
	now the mname is "December".

and then:

if page of diary is 31 and month of diary is 1:
		now page of diary is 0;
		now month of diary is 2;
		now mname is "January".
2 Likes