Author
Topic: Randomized Names [Ver 1.1] (Read 4718 times)

IntroductionThis plugin will generate random names for characters in your game - which could include NPCs and Actors. It's pretty cool if you want your villagers to have unique names but don't want to sit around and think of something for everyone. Note that names are meant to sound unrealistic and fantasy-esque. These names are calculated from looking at two parts of a name, the start and the end (depending on gender). Most name parts are broken down from real names, but that doesn't mean they won't get pretty odd sometimes.

Features- Generate a random name for an NPC or actor- Specify names by gender- Assign unique IDs to NPCs and use message codes to draw the name- Can be used to randomize an actor's name before name input for some added flavor- Change parameters to allow names to be unique (will not generate the same name twice)- Specify how many names will be unique (includes actors first, then npcs)

Changelog- Ver 1.1 -

- Added a parameter for a maximum # of unique names (a fail-safe for games that might generate a lot of names and trigger too many loops)- Cleaned up code

Keep in mind that your uniqueness loop is likely to run more and more rounds as more names are exhausted. If you end up running through all the names next time you'll have an infinite loop at your hands. Given the large number of possibilities it's probably not that big of a deal, but just something to keep in mind.

I agree with Zeriab on the string literal case advice. When you consider the end user, they may not care or know to pay attention to case sensitivity when writing the plugin command. That can easily become a source of frustration. You seem to have taken notice of that with the args parsing so it might have been an oversight.

Additionally, keep in mind that MV will parse any preceding white space in the command box as a null string, which will then become your command argument. You might want to check if that is the case and take a peek at args[0]. It's probably not like to happen but if it does, it's another source of frustration and potential confusion for the end user.

I also want to comment on the braces. Even for single line statements, include braces. It's a bad habit to get into, and is the source of many bugs - and they do exist in real world code out there (Apple had a case of that a few years ago with their SSL certification validation process; a duplicate 'goto fail;' line ended up in the main body of the function and it exposed a vulnerability in the security). There's often an argument regarding screen real estate efficiency but I find it lacks in comparison to protecting yourself from future bugs and general maintainability of code.

Lastly, a bigger one building on from Zeriab's concern about your loop. This is mostly for learning but feel free to use it practically.

Making use of performance.now() (a built-in function that takes a time stamp that is independent of the system clock), I looked at how long it takes your loop to operate over varying numbers of names used in total in a potential game.

Basically, store 1000 generated names in the array and record the time it takes to complete. I also kept a count of how many times the do/while executed in a single pass to show how often a collision might occur.

Average Time Taken: 4.93ms

So, 1000 names is quite ok. Let's increase that to 2000.

Average Time taken: 20.05ms

Twice the names, and 5x the time taken. Still only 1 retry on average, so that's ok.

Let's double it up again to 4000.

Average Time Taken: 73.61ms

You can already see that it is taking exponentially longer when doubling up the count.

Let's look at 10k names, so increasing the count by 2.5x.

Average Time Taken: 471.47ms

At about 29k names, it starts to get into an infinite loop of retries to find a unique name. So that's around the limit. I figured that out by testing with 100k names but it wouldn't finish. After leaving it for about 20 minutes and then coming back to check where it was (by pausing the code via debug), i was at 29982.

The reason that it takes so long is because you use an Array for storing currently used names. Arrays are great when it comes to indexing or inserting things, but they're terrible for searching through. This is because it has to be done sequentially, from start to finish. In the case of looking to see if a name already exist, it starts from index 0 and runs through to index n, which is either the same name or the end of the array. This makes our "infinite loop" also take longer and longer to try to resolve itself; it's taking seconds (we can assume by deduction from exponential explosion theory) to retry each time a conflict happens.

Instead, I turned the Array into a Map, and used the generated name as a key. I'm only doing this for demonstration and it requires a change to the nameRepeat function. As an implementation to the problem, it isn't ideal at all because you want to be able to refer to names by their ID and this would be ineffecient for that purpose - we'd have to search all values to find a match and that's worse than indexing an array.

The same 10k names took an average of 4.91ms. That's as fast as 1k names with an Array.

Because of how Maps break down when it comes to searching, they're quick for this kind of purpose. In terms of their time complexity (a measurement of time units that an operation takes), Maps have constant time searching whereas Arrays are linear time depending on their size and where the target actually is.

You wouldn't want to use a Map solely for this problem - you really do want the ID number to be the key and not the name itself. You could, however, consider keeping a seperate Map that exists purely for keep a track of currently used names and then store an array of names in the $gameVariables as you are doing. But this comes at a cost of increased memory usage; you're effectively doubling it to save time.

Given that a game won't typically have huge numbers of NPCs, it probably isn't an issue. So take this as a thought exercise that simply points out the ups and downs to what data structure you choose to use. There are multiple solutions to this problem that I can think of from the top of my head which might make for some good learning opportunities.

(Why do I always feel like it's the end of the world and I'm the last man standing?)

Oh, wow!! Thank you guys for taking the time to provide such awesome feedback! My knowledge is growing, but still very limited. I'm about to start an intensive training course for an entry level developer position and I'm really happy to have any chances possible to get ahead and learn more about code!

@ ZeriabGreat point on the .toLowerCase() check! I actually totally forgot to run that. ;-; I'll rename those args, too!

@LoganFOK, I'll try to implement a way to check on that preceding whitespace. Also I'm glad you brought up the use of braces - I thought I was being "neater", but then it became more of a bad habit. If anything, the braces would be neater AND safer!

I'm glad you pointed out the flaws of Arrays for searching through so much data! Until just yesterday, I had no idea what a Map even was. I read a few tutorials on it, but I'm still not entirely sure how it works. I'm going to research it and play around with a few tutorials.

Either way, I may default the max to 1k names - most people shouldn't get anywhere close to that! The second option sounds potentially less limiting for the player. However, I don't think most people would care (and they could change it anyways). I'm wondering if I would save on memory if the array itself were limited? Though I'm not even sure if that matters because the array pushes the data and grows as needed.