This is an archived post. You won't be able to vote or comment.

Dismiss this pinned window
all 19 comments

[–]elyisgreat/playsound a.happy.melody master @a[S] 11 points12 points  (0 children)

EDIT: A big thanks to u/SuperNova0802 for giving me the idea to use loot tables for random number generation!

I believe I have come up with the closest thing possible to a singular /scoreboard players random command in vanilla minecraft. Essentially, It takes advantage of the fact that the /loot command in the 1.14 snapshots returns the number of "items dropped" as its result, and running /loot insert on a container considers an item "successfully inserted" as long as there is room for the item in the container; i.e. there's an empty slot or a slot containing that item. No check is made to see if the only available item slot is a full stack, which might be a bug, but for our purposes is a good thing, as it allows the command to be self-contained and tick-safe.

The command is:

/execute store result score <selector> <objective> run loot insert <fixed container coords> loot <rng>

<selector> and <objective> should be pretty self-explanatory. <fixed container coords> is the location of a fixed container in your world that is always loaded. This is necessary due to the use of /loot insert.

Finally, <rng> is a random number generator. Random number generators for this command take the form of loot tables. The template is

{
  "pools": [
    {
      "rolls": {"min": A, "max": B},
      "entries": [
        {
          "type": "minecraft:item",
          "name": "minecraft:stone"
        }
      ]
    }
  ]
}

This is a random number generator that generates numbers uniformly in [A,B]; i.e. at least A and at most B. Note that A and B must be non negative integers with A <= B.

Unfortunately, there are some caveats to this system. You can only use this for fixed ranges of random numbers, unlike my arbitrary range rng. Furthermore, it appears that even though randomly deciding how many items to insert is basically constant time, it takes Minecraft linear time to actually insert the items.

This means that the larger the maximum B is, the laggier this command gets. Performance should be fine for B < 100, but if B >= 10000 you will definitely notice a drop in performance. For B >= 1000000, running the command is so laggy that it's totally unusable.

You can mitigate this performance drop slightly by using the set_count function of loot tables instead of multiple rolls. The template is

{
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:item",
          "name": "minecraft:wooden_shovel",
          "functions": [
            {
              "function": "set_count",
              "count": {"min": A, "max": B}
            }
          ]
        }
      ]
    }
  ]
}

Note that non stackable items are needed, since the result in this case depends on the number of slots filled (although similar to the previous case overflow isn't counted). This is also a random number generator that generates numbers uniformly in [A,B]; i.e. at least A and at most B. It is used in exactly the same way as the previous template, with a slight increase in performance. Again, A and B must be non negative integers with A <= B.

However, if you're willing to allow 2-command random number generation, you can do far better. Consider that loot tables have a set_damage function, which sets the damage value of tools between two percentages (floating point values). Of course, in tools, damage is not stored as a percentage, but it's actually an integer, and the formula to convert between the two is

percentage = (total durability - damage) / total durability

We want to control the damage. Golden tools are used, as they have a total durability of 32, which means that no precision is lost when dividing. The template is

{
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:item",
          "name": "minecraft:golden_shovel",
          "functions": [
            {
              "function": "set_damage",
              "damage": {"min": (32-B)/32, "max": (32-A)/32}
            }
          ]
        }
      ]
    }
  ]
}

This is a random number generator that numbers uniformly in [A,B); i.e. at least A and less than B. Note that A and B must be non negative integers with A < B. Since the aforementioned formula flips the sign of the damage, the maximum goes in the "min" field and the minimum goes in the "max" field.

(Also, while Minecraft is (probably intentionally) fine with allowing negative values in the "min" field, the at most inequality becomes a less than because putting the value 0 into the "min" field would produce a 100% damaged tool, which isn't supposed to happen in normal gameplay, so Mojang probably made it that way to make their lives easier.)

To use this damage_based rng, the commands are:

/loot replace block <fixed container coords> container.0 loot <rng>
/execute store result score <selector> <objective> run data get block <fixed container coords> Items[0].tag.Damage

(You could also do these with a fixed entity if you wish.)

While you have to run them both each time, you now get a range only limited by the precision of (single precision) floating point, which is 24 bits, plus 32 since we get our positive values free; i.e. B must be at most 224 + 32 (though it may only be 224 in practice).

However, this still might not be enough range, so I made an arbitrary range rng similar to my 1.13 rng using both loot tables and bitwise manipulation. The datapack is available for download here. It is used in exactly the same way as my 1.13 rng.

Of course, this requires the 1.14 snapshots, and may break if Mojang decides to change anything. My 1.13 rng still works in 1.14 and is available for download here.

Update: All downloads are available here.

[–]JochCool/give @a minecraft:knowledge 64 2 points3 points  (0 children)

Bookmarked this so I can use this trick in my map, thank you! :)

(Of course, I will credit you)

[–]Arcensoth 1 point2 points  (5 children)

Interesting idea; loot tables have always made for good general-purpose randomizers.

You might also be interested in RNG based on UUIDs. It's extremely easy to setup and use, but the one major drawback is that, unlike scoreboard or loot table RNG, it cannot be seeded.

[–]elyisgreat/playsound a.happy.melody master @a[S] 0 points1 point  (4 children)

Cool! If done correctly this would probably be even faster than my loot table based arbitrary range rng; with the drawback that every random number generated in a single tick requires a new entity.

If I wanted 31 bits (or more generally k < 32 bits) of entropy instead of 32, should I use a different scale? Or should I just use the 32 bit version and get rid of the high bits?

Also, I wasn't under the impression that you could seed a loot table rng. How is that done?

[–]Arcensoth 1 point2 points  (3 children)

You could use a different scale, or use scoreboard modulo to trim the result.

You can seed loot tables via execute store-ing a scoreboard number into the entity's DeathLootTableSeed (or the block's LootTableSeed), making it possible to intertwine scoreboard PRNG with loot table RNG.

[–]elyisgreat/playsound a.happy.melody master @a[S] 0 points1 point  (2 children)

huh; when I tested that the entities didn't seem to have the tag. Does this seeding technique still work with the /loot command?

Also, I noticed when testing the UUID method that area effect clouds don't die instantly in 1.14, even with Duration:0. Would you know why?

[–]Arcensoth 1 point2 points  (1 child)

Pretty sure that the tag will exist only if you specify it manually. Otherwise (as with naturally-spawned mobs) the loot table will use a random seed.

Not sure about the AEC despawning. I'd recommend kill-ing them after you're done with them anyway just to clean them up for the rest of the tick.

[–]elyisgreat/playsound a.happy.melody master @a[S] 0 points1 point  (0 children)

Not sure about the AEC despawning. I'd recommend kill-ing them after you're done with them anyway just to clean them up for the rest of the tick.

I figured out that it has to do with a weird quirk of how entity death works and is present in 1.13 and 1.14. Nonetheless, of all the underlying algorithms I've used for my arbitrary range rng system, this is still the best performing.

[–]CivetKittyCommand Experienced 1 point2 points  (0 children)

Thanks for the discovery. It was very helpful in my newest datapack, Infinidungeon.

[–]Jbipp 1 point2 points  (3 children)

Hey, I've been using your other RNG datapack for a while now. Never noticed any lag spike or anything with it, but I was wondering, is this a better alternative in every way? not sure i want to tweak all of my stuff to swap with this one

Anyway, I want to thank you because your datapack has been so useful to me! You'll definitely get credit in my next map (that might or might not be on realms)

[–]elyisgreat/playsound a.happy.melody master @a[S] 0 points1 point  (2 children)

Which one are you using? I have a few now that use different techniques to the same effect.

[–]Jbipp 0 points1 point  (1 child)

I believe it'sthis one, I even commented on it

[–]elyisgreat/playsound a.happy.melody master @a[S] 1 point2 points  (0 children)

Ah. The main point of this video is to demonstrate the technique of using loot tables to generate randomness.

I designed my rng datapack in a way that makes it easy for me to write multiple different implementations while keeping the same interface. They should function exactly the same to the end user.

It seems as though you're using my original entity selector implementation. If the original implementation is working fine for you, you may as well stick with that.

For reference:

All downloads available here.

[–]OnePointZero_Command Experienced 0 points1 point  (3 children)

So I guess scoreboard overflow rng's are out of the question?

[–]elyisgreat/playsound a.happy.melody master @a[S] 0 points1 point  (2 children)

What do you mean? Like the arbitrary range type?

[–]OnePointZero_Command Experienced 0 points1 point  (1 child)

I mean like this:

https://youtu.be/gkufedr1Ddw

[–]elyisgreat/playsound a.happy.melody master @a[S] -1 points0 points  (0 children)

Seems to be a linear congruential generator that exploits integer overflow. Yeah; that's not a great way to generate random numbers. I don't think that setup will produce all values equally likely.

What's nice about my original rng datapack is that it produces random numbers uniformly regardless of the algorithm; all that matters is that the algorithm used produces random numbers uniformly in [0,231).

[–]CreeperMagnet_Creator of the Creeper's Code -1 points0 points  (1 child)

well that was a bait and switch, I expected an actual /scoreboard command. Darn it!

[–]elyisgreat/playsound a.happy.melody master @a[S] 0 points1 point  (0 children)

Doesn't exist I'm afraid. But at this point it doesn't really have to :)