0 votes
by (8.9k points)

I have an array containing magic words.

<<set setup.magicWords to [
  'Quib quib'

When a wizard casts a spell, I would like him to pick three random words from this array.  My first thought was to go for...

The wizard casts a spell.  "<<print Array.random(setup.magicWords)>>, <<print Array.random(setup.magicWords)>>, <<print Array.random(setup.magicWords)>>!"

However, that runs the risk of the wizard casting "Quib quib, Quib quib, Quib quib!" and this is not the effect I seek.  Is there a way to call magic words from the array only once, so they are not repeated?

2 Answers

+1 vote
by (63.1k points)
selected by
Best answer

You can use the <array>.pluck() method to remove and return random elements from an array; since the elements are removed, there won't be any repeats.  However, you'll probably want to return the elements afterward.  Here's a way to do that via a widget, using the <array>.concat() method:

<<widget 'spell'>>\
		<<set _output to []>>
		<<for _output.length lt $args[0]>>
			<<run _output.push(setup.magicWords.pluck())>>
		<<print _output.join(', ')>>
		<<set setup.magicWords to setup.magicWords.concat(_output)>>


The wizard casts a spell.  "<<spell 3>>!"

You pass the number of magic words you want to the widget.  Don't pass a higher number than the number of words in the array, though.

by (8.9k points)
Shazam, Mictian, Quib quib!  Thank you, Chapel.
by (8.6k points)
An alternative to populating _output is setup.magicWords.map(w => w).shuffle().slice($args[0]). You can leave out the map(w => w) call if you don't mind the setup.magicWords array being randomly shuffled around, all it does is make a copy of it.
+2 votes
by (68.6k points)

This answer is a bit late to the party, however, I'm adding for future reference.

Starting with SugarCube v2.20.0, you may use the <Array>.randomMany() method to non-destructively select multiple unique elements from an array (<Array>.pluckMany() also exists).  For example:

"<<print setup.magicWords.randomMany(3).join(", ")>>!"