Solved Generating random code

Discussion in 'Spigot Plugin Development' started by Plochem, Feb 6, 2020.

  1. Hi,

    What's the best way to assign a random 4 letter code to the player with minimal collisions? Each code consists of only uppercase or lowercase letters, so there are about 7.3 million unique strings.

    I currently have a file with all those Strings. The file is supposed to keep track of all unused codes. I'm planning to load that list of strings, get a random string from that list, give to a player when they run a command, remove that string from the list, and save the new list back to the file.

    Is there a more efficient way of doing this with minimal collisions? Should i even have a file to store unused codes in the first place?
     
  2. Why don't you just generate a new code using a regex each time instead of saving all possibilities to a file? Check if the id is already taken if not assign it if taken regenerate a new one
     
  3. So in other words, save used codes in a file and if a code is used, randomly generate a new one?

    But let’s say I have one code left that’s not used, won’t the possibility of randomly generating the last one be 1/7.3 million?
     
  4. Depending on how you plan to use that much, yeah lol
    That's pretty much how unique id system works. For example in minecraft, once a new account is created their generate an unique id in a loop, if that id is already taken by someone then they generate a new one

    EDIT: btw there are 6,497,400 possibilities using [a-zA-Z]
     
  5. Ah ok.i doubt I’ll get over 1000000 codes generated so I think I should be good lol

    It should be 7.3 million because 52^4. I’m counting something like aaaa or aabb as a legit codes while permutations don’t.
     
  6. Why strings though? Ive done a similar thing when making "machines" basically a "Random" creating a int of max... and store stuff into the config using it(basically a unique id)

    Made a small func that returns a random int and just added a small (if..exists...) to check if its there.. keeping it simple
     
  7. I'm trying to keep codes to be the same length and be as short as possible. That's why I'm using letters. If I were to use numbers, it'll probably be a 7 digit code.
     
    • Funny Funny x 1
  8. SteelPhoenix

    Moderator

    Why do you want to assign a random 4-character string to a player anyways? I don't see why you can't just use the player's unique id or generate one for the player using UUID#randomUUID().

    Also, the chances of UUID collision are very low:
    "Only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%"
     
    • Agree Agree x 1
    • Winner Winner x 1
  9. I mean you can just make a random to be a specific legth...? Its much easyer than making tricks with strings
     
  10. What you are looking at is also basically a hash-problem. So one solution could look something like this:
    Code (Java):
    int hash = Objects.hash(System.currentTimeMillis());

    byte[] arr = new byte[4];
    for(int i = 0; i < 4; i++) {
        arr[i] = (byte) (hash >> 8*i)
    }

    String s = new String(arr);
    System.out.println(s);
    This generates a pseudo-random String. Keep in mind that, if you want to have only values from a-Z, you will have to do some more operations on your bytes.
     
    #10 Schottky, Feb 6, 2020
    Last edited: Feb 6, 2020
  11. TeamBergerhealer

    Supporter

    I had a similar question a while back. I wanted to have a random looking hash for classes I was generating at runtime. I ended up using something I found on stackoverflow (cant find it, shouldve put a link in the source code...) plus a counter to produce a random-looking hash with 0 collisions:

    Code:
    https://github.com/bergerhealer/Mou...er/mountiplex/reflection/util/UniqueHash.java

    You can convert it to base64/hex for a random looking hash. If your aim is for the next value to be unpredictable and you're dealing with security/crypto, obviously do not use this. This was just for looking nice.
     
  12. it's for a referral code system. I kinda don't want users to type in a whole UUID to redeem.

    Even then, it'll be eventually be slow when checking if the number is in the used list.

    Yeah that might work. I can easily modify it to make sure that I always get a certain length.
     
  13. SteelPhoenix

    Moderator

    You could also convert the player's first join time to a radix 36 string. This would result in an 8 character long string without collisions (very unlikely two players join on the same millisecond)
     
    • Like Like x 1
  14. Literally my solution...

    Edit: nvm, that hash is way better. Sorry for the offense
     
    #14 Schottky, Feb 7, 2020
    Last edited: Feb 7, 2020
    • Friendly Friendly x 1
  15. BananaPuncher714

    Supporter

    Why not just incrementally increase the code each time you need to generate a new one? This way, you can store the players in an array, and use the length to check for the last code used. You can probably find a way to convert an integer into a code, but I would personally keep it entirely numbers, since it's a bit simpler especially since then you don't have to worry about capitalization, or if the I looks like a lowercase l. Assuming this is for a referral system, I don't think you're concerned about keeping the code secret.
     
  16. With numbers there are 10 possibilities, with letters there are 26 possibilities.
     
  17. Why not just have them type the players name rather than the code?
     
  18. Cause people can switch username at anytime since UUIDs.
     
  19. Is radix 36 the same as base 36?
     
  20. SteelPhoenix

    Moderator

    Yes and Java comes with some handy-dandy methods for conversion
    Code (Java):
    long l = ...;
    String s = Long.toString(l, 36);
    long l2 = Long.parseLong(s, 36);
    assert l == l2 : "Values are not equal";
    These characters are used as digits: 0123456789abcdefghijklmnopqrstuvwxyz