Eden Generation

This is a technical analysis of how the game randomly generates the various characteristics of the Eden character. Note that this analysis was performed by BLouBLue on version 1.05 (pre-Afterbirth), and version 1.06.T1 (Afterbirth+). Analysis for version 1.7.9b (Repentance) was performed by SarvaTathagata.

## Launch

When launching the game, the main thing to know is the game will derive a bunch of subseeds that will be used by RNG instances dedicated to items, trinkets, pickups, enemies, level generation and character management.

This allows you to play your run slightly differently, yet still, get the same items at the same place (for the most part - that's not entirely true).

One specific "character" subseed is used for Eden generation. This RNG instance is used during two phases:

1. Generate hearts, pickups, and attributes, before level initialization;
2. Randomly pick cards, pills, trinkets, and items, after level initialization.

It is reset to the character subseed before either phase. This means the same random numbers could come out of the RNG in the same order for both phases.

## Hearts

Hearts are the first thing to be generated. The game tries its best to give you 3 full hearts, Red and Soul hearts altogether:

1. The game randomly picks `x = 0 to 3` Red Hearts;
2. It will randomly pick between `0 and (3 - x)` Soul Hearts;
3. If no red hearts were picked at the first step, the game guarantees the player at least 2 Soul Hearts.

There cannot be any Black Hearts from this phase. Collectible items given afterward may exceed those limits.

## Keys, Bombs, and Coins

Eden has a 1/3 chance to start with either keys, bombs or coins. If you've been granted that chance, here are the possible outcomes:

1. Eden starts with 1 key;
2. Eden starts with 1 or 2 bombs;
3. Eden starts with 1 to 5 coins.

Collectible active/passive items given afterward may give additional pickups, bringing the total beyond those limits.

The chance to start with a pickup is calculated as follows:

```
/\* rand(min,max) returns a random integer between min (inclusive) and max (exclusive) \*/
if(rand(0, 3) == 0)
{ // 1/3 chance that the player will get a pickup
 tmp = rand(0, 3); // either 0, 1 or 2
 if(tmp == 0)
 give 1 key
 else if(tmp == 1)
 give 1 + rand(0,2) bombs
 else
 give 1 + rand(0,5) coins
}

```

## Attributes

Attributes are based on Isaac's base attributes. An additive modifier (float value) is randomly chosen for each of the 6 attributes:

 luck | -1.00 to +1.00

Here's what the attribute initialization part of the routine looks like:

```
// characterRng.Random() returns a float value between 0.0 and 1.0
Player->attributeModifierDamage = (characterRng.Random() \* 2.0f) + -1.0f;
Player->attributeModifierSpeed = (characterRng.Random() \* 0.3f) + -0.15f;
Player->attributeModifierTears = (characterRng.Random() \* 1.5f) + -0.75f;
Player->attributeModifierHeightRange = (characterRng.Random() \* 10.0f) + -5.0f;
Player->attributeModifierShotSpeed = (characterRng.Random() \* 0.5f) + -0.25f;
Player->attributeModifierLuck = (characterRng.Random() \* 2.0f) + -1.0f;

```

## Pocket Items

Pills, cards, trinkets, and items are picked after level generation. Two phases occur:

1. Give the player a chance to have either a pill, card or trinket;
2. Attempt to pick 1 active item and 1 passive item.

So, when consuming an Eden token, you have:

* 1/3 chance to get a trinket;
* 1/6 chance to get a pill;
* 1/6 chance to get a card;
* 1/3 chance to get none of the above.

These options are mutually exclusive. You cannot get a trinket and a card or pill. Here's what the actual routine looks like:

```
if(characterRng.Random(3) > 0) { // 2/3 chance to get pill, card or nothing
 if(characterRng.Random(2) > 0) { // 2/3 \* 1/2 chance to get nothing
 give nothing
 }
 else { // 2/3 \* 1/2 chance to get a pill or a card
 if(characterRng.Random(2) > 0) { // 1/3 \* 1/2 chance to generate a pill
 give a random pill
 }
 else { // 1/3 \* 1/2 chance to generate a card
 give a random card
 }
 }
}
else { // 1/3 chance to get a trinket
 give a random trinket
}

```

More about how pocket items of each type are selected:

* Pill colors and pill effects are shuffled based on a subseed derived from the main seed. 9 or 13 pill effects are selected and randomly assigned to each of the flavors. If entitled to, Eden will randomly get one of those pills;
* Trinkets are randomly chosen among the unlocked trinket pool. There are 32 random repicks, or 64 repicks in Afterbirth+, and another pass if unsuccessful. The game may give up if the player hasn't unlocked any trinket;
* Eden cannot start with the following cards/runes:
	+ Any rune or soul stone,
	+ 2 of Clubs, 2 of Diamonds, 2 of Spades, 2 of Hearts, Joker,
	+ Ace of Clubs, Ace of Diamonds, Ace of Spades, Ace of Hearts,
	+ Queen of Hearts;
		- Chaos Card, Credit Card, Rules Card, A Card Against Humanity and Suicide King,
		- Get out of Jail Free Card, ? Card, Dice Shard, Emergency Contact,
		- Holy Card, Huge Growth, Ancient Recall, Era Walk,
		- Cracked Key, Wild Card
	+ A global set containing every other card except those that are forbidden,
	+ Eden has a 1/25 chance to randomly get a card from the special set, and a 24/25 chance to get one from the global set.
	+ Every Major Arcana Tarot Card has a 1/7 chance to be replaced by its reversed version.

Here's how the card selection is done, in pseudo-code:

```
// both isRunesAllowed and isSpecialCardsAllowed are set to false when initializing Eden.
// Card IDs can be found in the game's xml files
// cardRng.Random(min,max) returns a random integer between min (inclusive) and max (exclusive)
// cardRng.Random(max) returns a random integer between 0 (inclusive) and max (exclusive, so max-1 is the upper bound)
if(cardRng.Random(25) > 0) // 24/25 chance
{
 if(cardRng.Random(10) || !isRunesAllowed)
 {
 bool tmp = (cardRng.Random(5) != 0) || !isSpecialCardsAllowed;
 if(tmp == false)
 {
 card\_first = CARD\_2\_OF\_CLUBS;
 card\_last = CARD\_JOKER;
 }
 else
 {
 card\_first = CARD\_0\_THE\_FOOL;
 card\_last = CARD\_XXI\_THE\_WORLD;
 }
 }
 else
 {
 card\_first = RUNE\_DESTRUCTION\_HAGALAZ;
 card\_last = RUNE\_RESISTANCE\_ALGIZ; // RUNE\_BLACK in AB+
 }
}
else
{
 card\_first = CARD\_CHAOS\_CARD;
 card\_last = CARD\_SUICIDE\_KING; // CARD\_ERA\_WALK in AB+
}

return cardRng.Random(card\_first, card\_last + 1);

```

Here's how the card selection is done in Repentance:

```
if (cardRng.next() % 25 == 0) {
 // Special cards from Chaos Card to Era Walk, with 2 more cards in Repentance.
 int card = (int)(cardRng.next() % 15) + 42;

 if(card == 55){card=78;} // Cracked key
 if(card == 56){card=80;} // Wild Card

 return card;
}

// The checks of Runes and Playing cards are removed in Repentance, too.

// Normal cards from The Fool to The World
int card = (int)(cardRng.next() % 22) + 1;

// Reversed Major Arcana Cards; added in Repentance
if(cardRng.next() % 7 == 0){
 card += 55;
}

return card;

```

## Collectibles

The game does 100 attempts to fill both the active item slot and one passive item slot.
At each attempt, it will randomly pick an item ID between 1 and 346 or 552 or 732.

There are a few disabled item IDs which cause the game to repick a random ID and go on to the next attempt:

* #43: "Pills here!", an active item that does nothing and looks like a pill.
	+ This could be an Easter Egg Number in the Code, Referencing the "Pills Here!" when finding an unidentified pill from Flash Isaac.
* #59: "Tarot Card", a passive item that looks like a card and does nothing.
	+ This ID is for The Book of Belial from Judas' Birthright.
* #61: Literally nothing, and is not defined in items.xml.
* #235: *Not Available*
* #238:
* #239:
* #263: *Not Available*
* #550:  (piece #1)
* #551:  (piece #2)
* #552:

 An item will be disabled if and only if it has the noeden tag.

The first valid candidate (unlocked item) for the passive or active slot is selected, and will not be overwritten by subsequent attempts (there will be 100 attempts no matter what).

The player is given those items after 100 attempts, whether both slots are filled or not.

Therefore, Eden may start with up to 1 active item and up to 1 passive item. It is highly unlikely that a seed exists which gives Eden only 1 or no item.

As nothing weighs some items specifically, it means each item combo probably has about a 0.005% / 0.001% chance of being your reward.
Testing material: 1000 Eden seeds

## C# Code

Code for calculating Eden's items for a given seed. This is missing trinket, pill and stats calculations.

```
void Main()
{
 var seeds = new List<uint>();
 for(uint i = 0; i < uint.MaxValue; i++)
 {
 var startSeed = (uint)i;
 var dropSeed = CalculatePlayerSeed(startSeed);
 var items = CalculateEdenItems(dropSeed);

 {
 seeds.Add(startSeed);
 SeedToString(startSeed).Dump();
 }
 if (i % 100000000 == 0)
 (":" + i).Dump();
 }
 seeds.Select(s => SeedToString(s)).ToArray().Dump();
}

//ItemCount impacts the item pick RNG even though Eden can't start with an item over id 552
public static EdenItems CalculateEdenItems(uint dropSeed, int itemCount = 552)
{
 var rng = new Rng(dropSeed, 0x1, 0x5, 0x13);

 var trinket = 0;
 var card = 0;
 var pill = 0;
 var hearts = 0;
 var soulHearts = 0;
 if ((rng.Next() % 3) == 0)
 {
 //trinket
 }
 else if ((rng.Next() & 1) == 0)
 {
 if ((rng.Next() & 1) == 0)
 {
 card = GetCard(rng.Next());
 }
 else
 {
 //pill
 var pillSeed = rng.Next();
 }
 }

 var activeId = 0;
 var passiveId = 0;
 for (var i = 0; i < 100; i++)
 {
 int itemId = (int)(rng.Next() % itemCount) + 1;

 //BlackList
 if (itemId > 552)
 continue;
 switch(itemId){
 continue;
 default:
 break;
 }

 var itemType = ItemConfig[itemId];
 if (itemType == ItemType.Active)
 {
 activeId = activeId != 0 ? activeId : itemId;
 }
 else if (itemType == ItemType.Passive || itemType == ItemType.Familiar)
 {
 passiveId = passiveId != 0 ? passiveId : itemId;
 }
 //Isaac doesn't exit early :thinking:
 if (activeId != 0 && passiveId != 0)
 break;

 }

 //Hearts and SoulHearts are actually done in Player::Init
 var healthRng = new Rng(dropSeed, 0x1, 0x5, 0x13);
 var halfHearts = (int)healthRng.Next() & 3;
 hearts = halfHearts \* 2;
 soulHearts = ((int)healthRng.Next() % (4 - halfHearts)) \* 2;
 if (hearts == 0 && soulHearts < 4)
 soulHearts = 4;

 return new EdenItems(hearts, soulHearts, activeId, passiveId, trinket, card, pill);
}
public static int GetCard(uint seed, bool playing = false)
{
 var cardRng = new Rng(seed, 0x5, 0x9, 0x7);
 if (cardRng.Next() % 25 == 0) {
 return (int)(cardRng.Next() % 13) + 42;
 }
 if (cardRng.Next() % 10 == 0)
 {
 //Rune
 }
 if (cardRng.Next() % 5 == 0 && playing)
 {
 //Playing card
 return (int)(cardRng.Next() % 9) + 23;
 }
 return (int)(cardRng.Next() % 22) + 1;
}

public static uint CalculatePlayerInitSeed(uint startSeed)
{
 var startRng = new Rng(startSeed, 0x3, 0x17, 0x19);
 //Stage Seeds
 for (var i = 0; i < 0xD; i++)
 startRng.Next();
 return startRng.Next(); //Seeds::PlayerInitSeed
}

public static uint CalculatePlayerSeed(uint startSeed)
{
 var playerInitSeed = CalculatePlayerInitSeed(startSeed);

 //These happen inside Player::Init
 var playerInitRng = new Rng(playerInitSeed, 0x1, 0xB, 0x10);
 //CollectiblesRNG seed
 playerInitRng.Next();
 playerInitRng.Next();
 playerInitRng.Next();
 //CardsRNG seed
 playerInitRng.Next();

 return playerInitRng.Next(); //Entity::DropSeed
}

public class EdenItems
{
 public int Hearts;
 public int SoulHearts;
 public int Active;
 public int Passive;
 public int Trinket;
 public int Card;
 public int Pill;
 public EdenItems(int hearts, int soulHearts, int active, int passive, int trinket, int card, int pill)
 {
 Hearts = hearts;
 SoulHearts = soulHearts;
 Active = active;
 Passive = passive;
 Trinket = trinket;
 Card = card;
 Pill = pill;
 }
}

static string SeedToString(uint num)
{
 const string chars = "ABCDEFGHJKLMNPQRSTWXYZ01234V6789";
 byte x = 0;
 var tnum = num;
 while (tnum != 0)
 {
 x += ((byte)tnum);
 x += (byte)(x + (x >> 7));
 tnum >>= 5;
 }
 num ^= 0x0FEF7FFD;
 tnum = (num) << 8 | x;

 var ret = new char[8];
 for (int i = 0; i < 6; i++)
 {
 ret[i] = chars[(int)(num >> (27 - (i \* 5)) & 0x1F)];
 }
 ret[6] = chars[(int)(tnum >> 5 & 0x1F)];
 ret[7] = chars[(int)(tnum & 0x1F)];

 return new string(ret);
}

public class Rng
{
 public uint Seed;
 public int Shift1;
 public int Shift2;
 public int Shift3;
 public uint Next()
 {
 var num = Seed;
 num ^= num >> Shift1;
 num ^= num << Shift2;
 num ^= num >> Shift3;
 Seed = num;
 return num;
 }

 public Rng(uint seed, int s1, int s2, int s3) {
 this.Seed = seed;
 Shift1 = s1;
 Shift2 = s2;
 Shift3 = s3;
 }
};

public enum ItemType {
 Null = 0,
 Passive = 1,
 Trinket = 2,
 Active = 3,
 Familiar = 4,
}

public static ItemType[] ItemConfig = GetItemConfig();
public static ItemType[] GetItemConfig() {
 var itemConfig = new ItemType[553];
 return itemConfig;
}

```

The full code can be found here Blade's GitHub Gist or for the Repentance version here Zamiel's GitHub Gist.