Actually, turns reality is slightly more complicated
On my actual copy of lemmings, the code for the next level depends on the percentage of lemmings rescued when completing the current one, and all different codes generated this way work. In addition, there is a variable (that I called "salt" in the listing below), the lowest 4 bit of which are encoded in the code. All different salts generate valid codes, and restoring a code also restores the salt, so subsequent codes will encode the same value. Or, in other words, if you enter a code for a different percentage you'll get the same codes for the next levels, but if you enter a code with a different salt you'll get a different set of codes.
On my actual copy of lemmings, the code for the next level depends on the percentage of lemmings rescued when completing the current one, and all different codes generated this way work. In addition, there is a variable (that I called "salt" in the listing below), the lowest 4 bit of which are encoded in the code. All different salts generate valid codes, and restoring a code also restores the salt, so subsequent codes will encode the same value. Or, in other words, if you enter a code for a different percentage you'll get the same codes for the next levels, but if you enter a code with a different salt you'll get a different set of codes.Code Select
// rating: 0 .. 4
// level: 0 .. 29
// percentage: 0 .. 100
// salt: 0 .. 0x0f
function codeFor(rating, level, percentage = 100, salt = 0x00) {
const secret = "AJHLDHBBCJ".split("").map((x) => x.charCodeAt(0));
const levelIndex = 30 * rating + level;
const code = new Array(10).fill(" ".charCodeAt(0));
code[0] = ((levelIndex & 0x01) << 3) | secret[0] | ((salt & 0x01) << 1) | ((percentage & 0x01) << 2);
code[1] = ((levelIndex & 0x02) << 1) | secret[1] | ((percentage & 0x02) >> 1);
code[2] = (levelIndex & 0x04) | secret[2] | ((percentage & 0x04) >> 1) | ((salt & 0x02) >> 1);
code[3] = ((levelIndex & 0x08) >> 3) | secret[3] | ((percentage & 0x08) >> 2);
code[4] = ((levelIndex & 0x10) >> 3) | secret[4] | ((percentage & 0x10) >> 1) | ((salt & 0x04) >> 2);
code[5] = ((levelIndex & 0x20) >> 5) | secret[5] | ((percentage & 0x20) >> 3) | ((salt & 0x08) >> 2);
code[6] = ((levelIndex & 0xc0) >> 4) | secret[6] | ((percentage & 0x40) >> 6);
code[7] = (levelIndex & 0xf) + secret[7];
code[8] = (levelIndex >> 4) + secret[8];
code[9] =
((code[0] + code[1] + code[2] + code[3] + code[4] + code[5] + code[6] + code[7] + code[8]) & 0xf) + secret[9];
for (let i = 7 - (levelIndex & 0x07); i > -1; i--) {
const tmp = code[6];
code[6] = code[5];
code[5] = code[4];
code[4] = code[3];
code[3] = code[2];
code[2] = code[1];
code[1] = code[0];
code[0] = tmp;
}
return code.map((x) => String.fromCharCode(x)).join("");
}