It might not be exactly what you are looking for, but I'd suggest attaching the chance of appearance to the actual enemy type. That way you can fine tune it a bit more without needing to modify the actual algorithm, for example you seem to want enemy type 1 to appear at all levels, but not type 2, 3, 4 etc.
EG
struct EnemyType
{
unsigned int id; //Generic id number
int baseLevel; //The player level where this enemy starts to appear but is rare.
int commonLevel; //The player level where the enemy becomes common.
int rareLevel; //The player level where the enemy stops being common again.
int extinctLevel; //The player level where the enemy stops appearing.
}
Then maybe assign a weight to each enemy type based upon the player level:
float getWeight(EnemyType enemyType, int playerLevel)
{
if(playerLevel < enemyType.baseLevel ||
playerLevel >= enemyType.extinctLevel) return 0.0f;
else if(playerLevel >= enemyType.rareLevel ||
playerLevel < enemyType.commonLevel) return 0.5f;
else return 1.0f;
}
Any weighted random selection algorithm should then finish the job, but the probability of getting any one enemy type returned from getEnemyType() should be:
weight of type / total weights of all types
This is a bit rough, as there are many good ways of implementing such an algorithm. But the main point I'm trying to make is specify the probability changes on the enemy type instead of in the actual selection algorithm.
EDIT: Here's an example of the selection algorithm (psuedocode):
//Initialize these somewhere, maybe use a vector or whatever your language's equiv is.
const unsigned int numEnemyTypes;
EnemyType enemyTypeArray[];
EnemyType getEnemyType(int playerLevel)
{
floatrandom = someRNG(); //Gives a number between 0 and 1.
//Find total weights
float totalWeight = 0.0;
for(int i = 0; i < numEnemyTypes; i++)
{
EnemyType enemyType = enemyTypeArray[i]
totalWeight += getWeight(enemyType, playerLevel);
}
//Find enemy type
float range = 0.0;
for(int i = 0; i < numEnemyTypes; i++)
{
EnemyType enemyType = enemyTypeArray[i]
range += getWeight(enemyType, playerLevel)/totalWeight;
if(range >= random) return enemyType;
}
}
Essentially each enemy type gets a portion of the number range between 0 and 1, if the random number falls into that enemy type's range that is the enemy type selected. EG if you have 2 enemies, one with weight 0.5 and one with weight 1.0 then the first will have the range 0.0 to 3.33 while the second will have the range 3.33 to 1.0. If you add a third enemy with weight 1.0 then the ranges will look like:
0 to 0.2 (0.5 / 2.5 = 0.2)
0.2 to 0.6 (1.0 / 2.5 = 0.4)
0.6 to 1.0 (as above)
The code above isn't exactly optimal but hopefully it gives an idea of what I am talking about.