Minecraft – How likely is it for an end portal to already be “complete”

minecraft-java-edition

We already know that in Minecraft 1.16, an end portal can generate with 12 eyes of ender already placed, such as in the seed -4530634556500121041. The odds of this are calculable: 0.1^12 = 1 / 1 trillion.

Recently the seed 2483313382402348964 which generates the following end portal was discovered:
Glitched end portal generation

For all practical purposes this is a complete portal, since it gives immediate access to the end. Then the actual odds of a "complete" portal are higher than 1 / 1 trillion. Do we have an estimate or actual value for the odds of a complete portal, including this case?

I've noticed that the completed part of the portal is in a different chunk than the rest of the portal, which is probably a hint as to why the generation happened like this.

Chunks marked

Best Answer

This is what I believe based on 1.16.4 MCP Reborn decompiled code.

The end portal will only generate if all eyes are placed in which sets flag. And if 1.16 is like previous versions, the RNG is seeded per chunk, so a portal can cover 4 chunks, but the chunk with the glitched portal still requires 12 eyes to spawn chance. Source: zolsticezolstice (moderator of r/MinecraftSpeedrun)

So the chance is still on the order of 1/1 trillion rather than 1/6400, but increased due to portals generating over multiple chunks.

From StrongholdPieces.java:

         boolean flag = true;
         boolean[] aboolean = new boolean[12];

         for(int l = 0; l < aboolean.length; ++l) {
            aboolean[l] = p_230383_4_.nextFloat() > 0.9F;
            flag &= aboolean[l];
         }

         this.setBlockState(p_230383_1_, blockstate6.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[0])), 4, 3, 8, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate6.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[1])), 5, 3, 8, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate6.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[2])), 6, 3, 8, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate2.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[3])), 4, 3, 12, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate2.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[4])), 5, 3, 12, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate2.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[5])), 6, 3, 12, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate3.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[6])), 3, 3, 9, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate3.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[7])), 3, 3, 10, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate3.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[8])), 3, 3, 11, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate4.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[9])), 7, 3, 9, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate4.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[10])), 7, 3, 10, p_230383_5_);
         this.setBlockState(p_230383_1_, blockstate4.with(EndPortalFrameBlock.EYE, Boolean.valueOf(aboolean[11])), 7, 3, 11, p_230383_5_);
         if (flag) {
            BlockState blockstate7 = Blocks.END_PORTAL.getDefaultState();
            this.setBlockState(p_230383_1_, blockstate7, 4, 3, 9, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 5, 3, 9, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 6, 3, 9, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 4, 3, 10, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 5, 3, 10, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 6, 3, 10, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 4, 3, 11, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 5, 3, 11, p_230383_5_);
            this.setBlockState(p_230383_1_, blockstate7, 6, 3, 11, p_230383_5_);
         }

My guess is p_230383_4_ is the Random object that does the RNG which comes from generateFeatures in Biome.java, which comes from SharedSeedRandom, which is seeded per chunk.


For the sake of having some numbers, let's assume the portal generating per chunk assumption is correct, and also the portal center is chosen uniformly within a 16x16 chunk. Then if the portal center is on one edge of the chunk, end portals are calculated over two chunks, and if the portal center is in a corner of a chunk, end portals are calculated over 4 chunks. So the two chunk case has 4*14=56 possible positions and the chance of at least one chunk having a 12-eye RNG is 1-(1-10^-12)^2 ≈ 2*10^12, and the four chunk case has 4 possible positions with chance 1-(1-10^-12)^4 ≈ 4*10^-12. The total chance is now about (196 + 2*56 + 4*4)/256 * 10^-12 ≈ 1.266 * 10^-12, so about 25% more likely as the original 1 / 1 trillion chance, but still on the same order of magnitude of nearly impossible.