Gnurro / AIDscripts

JS scripts for AI Dungeon (play.aidungeon.io)
21 stars 3 forks source link

Encounters: Shuffle algorithm or similar for multiple encounters #4

Open valahraban opened 3 years ago

valahraban commented 3 years ago

The encounter script adds a great feature to AID and already does many things I desire when it comes to outputting encounters. encounterWordLists can be adapted to output random encounters in fashion akin to classic video RPGs. Let's use Pokemon mobs as a simple example. We can already do encounterWordLists = {Route1:["Rattata", "Pidgey", "Spearow"]}. To encounter multiple correct mobs on Route 1 we can call {Route1}, {Route1}, {Route1} and it should output the mobs with proper grammar to boot.

On to the issue and feature request. The problem is the above has the possibility to output Pidgey, Pidgey, Pidgey which is not always desired behavior. Adding an option to iterate over an encounterWordList while removing the possibility of duplication gives the users even more control over their encounters while not being too difficult to implement.

So a function that does pickedInsert = getRndFromList(encounterWordLists[insertTag]) i number of times as defined by the user, with duplicates removed or prevented. The Durstenfeld shuffle is an efficient example of this, posting example code below. I believe this would be a cool feature to add to the Encounters script.

function shuffle(array) {
   for (var i = array.length - 1; i >= 0; i--) {
      var rnd = Math.floor(Math.random() * (i + 1));
      var temp = array[i];
      if (i >= 1) {
         //document.write(array[rnd] + ", ");
         console.log(array[rnd] + ", ");
      }
      else {
         //document.write(array[rnd]);
         console.log(array[rnd]);
      }
      array[i] = array[rnd];
      array[rnd] = temp;
   }
}

For our implementation we replace array.length with value determined by the user as long as it's <= array.length. Of course the word list needs to be neatly spliced in an array too.

Thank you for all your hard work and the scripts.

EDIT: Using an improved algorithm and fixed an error I made in previous sample code. I also recorded a gif of how this looks like with regular JS. https://files.catbox.moe/16ug6q.gif

Gnurro commented 3 years ago

I'm quite tired right now, so I might miss something important about the algorithm, but why yould you need to shuffle the array?

I'd simply make a temporary copy of the database array, randomly pick things from it and splice them out. Once the temporary array is empty, a new temporary copy of the database array takes its place. No sorting or shuffling of any kind needed. This can also be expanded on to have Undertale-like random encounters by keeping the temporary list in state.

Would also allow to keep the non-limited randomization as it is right now, but add modes to it. As a slightly crude example, that could then allow for "After catching a {Route1} and fighting off another wild {Route1}, you run into {!Route1Trainer}, who chooses {trainer21Mon}, then {trainer21Mon}, and finally {trainer21Mon}." {Route1} picks any string from 'Route1', and doesn't care about repeating or not, possibly leading to catching and then fighting off two of the same. {!Route1Trainer} would lead to creation of a state copy of 'Route1Trainer', and the randomly picked one will be removed forever, since you already fought them. The state copy will stick around, and will prevent the occurence of that trainer, since that trainer isn't even there anymore. {trainer21Mon} would lead to the creation of a temporary copy of the 'trainer21Mon' list, from which the picked ones get removed. That temporary list will then disappear once the fully filled textNote is done, allowing general reuse while preventing duplicates within a single textNote. Thing of note here: It would also be possible to use {Route1} instead of {trainer21Mon}, and just don't set up specific list for trainers, but reuse the mons that are in the area anyways, while making sure that guy doesn't have three Pidgeys in a row.

...I really don't want any more complex syntax than that for encounterWordList functionality. It's supposed to be straight forward and simple to use, not force people to learn it's own syntax to allow multiple insertions into one placeholder or whatever other shenanigans one might think off. There are other parts of Encounters where more complex things are better fit, like branches.

Gnurro commented 3 years ago

Added the {*placeholders} with non-persistent repetition prevention, making it Beta7.

Persistent version ({!placeholders}) will work basically the same under the hood, but needs something more for cases when the list has been emptied - what that will be, I'll gladly take suggestions on: This is more or less like how random encounters in Undertale work, and the 'thing that happens when the list is empty' is that eery, deserted and over time disturbing empty battle screen. In Undertale that's a basic thing, but Encounters would need to either have a general fallback (not preferred, as that entails hardcode) or something defined for it somewhere. - Keep in mind that this is about words/phrases to insert here, not about chaining, despite the Undertale example! -

If I find a use for the shuffling algorithm, I'll gladly implement it, though. Might be useful for advanced weightedList juggling that'll prolly come up sooner or later, to keep the ranges same while having different potential arrays there and such. I do like my array.push/.pop, due to NLP experience, and this basically allows non-repetitive use of those for the same array, if I understand it correctly.