+1 vote
by (250 points)
edited by
I am looking to create a story that has a set beginning and end passage, but the user experiences the passages in between in a random order each time, but without repeating, and once every passage has been experienced once, the next passage becomes the conclusion. Thanks for any help! Also, I don't care which story format is used :)

edit:

Could this possibly be done with arrays & matrices in javascript?

1 Answer

+2 votes
by (159k points)
selected by
 
Best answer

I am going to suggest using SugarCube as it has a built-in pluck() function which you can use to randomly extract an element from an Array. I would use v2.18.0 because the 1.x series is not longer being actively developed.

1. Initialising the list of potential random Passages.

To help automate the process I would assign a Passage Tag (i'm using random) to each of the Passages to be included in the Array story variable (named $randoms), then use code like the following within the StoryInit special passage to populate the array,

/% Initialise the array being used to story the list of random Passages. %/
<<set $randoms to []>>

/% Add the name of each of the random Passages to the array by searching for all Passages in the story tagged as 'random'. %/
<<set _list to Story.lookup("tags", "random")>>
<<for _i to 0, _len to _list.length; _i lt _len; _i++>>
	<<run $randoms.push(_list[_i]['title'])>>
<</for>>

... if uses the Story.lookup() function to find the list of tagged Passages and the <<for>> macro to loop that list so the passage's title (name) can be added to the array.

2. Displaying a link that targets either a random passage or the Conclusion passage.

I would use the PassageFooter special passage to display the link at the end of the current passage's contents using code like the following, this same code could instead be stored in a secondary passage that you manually <<display>> within each of your story's Passages.

<<if passage() is not "Conclusion">>\
<<if $randoms.length gt 0>>\
	<<set _next to $randoms.pluck()>>\
<<else>>\
	<<set _next to "Conclusion">>\
<</if>>\

<<link "Next" _next>><</link>>
<</if>>\

... it uses the passage() function to determine the title/name of the current Passage being shown, the pluck() function to randomly obtain the name of the next random passage if there are any that have not been shown yet, and a <<link>> macro to display the link. The above also uses Line Continuations to suppress unwanted line-breaks.

3. Other passages.

The above code assumes you have a start passage (named whatever you like), a Conclusion passage which you can rename as long as you also change the second <<set>> macro in the PassageFooter, and finally some passages that have been assigned the random passage tag. (which you can also rename as long as you also update the Story.lookup call in the StoryInit passage.

by (68.6k points)
edited by

If you didn't mind leaning a bit more on JavaScript, then the <<set>>+<<for>> used to collect the names of all passages tagged with "random" in greyelf's example could also be written as:

<<set $randoms to Story.lookup("tags", "random").map(function (p) { return p.title; })>>

 

 

[…] this same code could instead be stored in a secondary passage that you manually <<display>> within each of your story's Passages.

If we're going to encourage the use of SugarCube v2 over v1, and we should, then we should also prefer the <<include>> macro over the deprecated <<display>>.

 

by (250 points)
Thank you! Might try this too, but I came up with a solution in Snowman that creates an array, uses Math.floor & Math.random to randomly select an element, splices that element out of the array, and then prints the element as a passage link. If anyone is interested I can paste the code and explain in further detail!
...