Created
April 28, 2025 07:16
-
-
Save jvgomg/6fe46035103d6b7a7c9ebed1cea8c50f to your computer and use it in GitHub Desktop.
Generate a set of magic words using a Linear Congruential Generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Configurable LCG function | |
function lcg(seed: number, multiplier: number, increment: number, modulus: number) { | |
return function() { | |
seed = (multiplier * seed + increment) % modulus; | |
return seed; | |
}; | |
} | |
export function generateWords( | |
words: string[], | |
seed: number, | |
wordsPerSet: number, | |
multiplier: number = Number(process.env.LCG_MULTIPLIER) || 1103515245, | |
increment: number = Number(process.env.LCG_INCREMENT) || 12345, | |
modulus: number = Number(process.env.LCG_MODULUS) || 2 ** 31 | |
): string[] { | |
if (words.length === 0 || wordsPerSet <= 0) { | |
return []; | |
} | |
const totalCombinations = words.length ** wordsPerSet; | |
const requiredBits = Math.ceil(Math.log2(totalCombinations)); | |
const random = lcg(seed, multiplier, increment, modulus); | |
const randomNumber = random() % totalCombinations; | |
let binaryString = randomNumber.toString(2).padStart(requiredBits, '0'); | |
const splitSize = Math.floor(binaryString.length / wordsPerSet); | |
const selectedWords: string[] = []; | |
for (let i = 0; i < wordsPerSet; i++) { | |
const part = binaryString.slice(i * splitSize, (i + 1) * splitSize); | |
const index = parseInt(part, 2) % words.length; | |
selectedWords.push(words[index]); | |
} | |
return selectedWords; | |
} | |
export function wordsToSeed( | |
words: string[], | |
selectedWords: string[], | |
wordsPerSet: number | |
): number { | |
if (words.length === 0 || selectedWords.length !== wordsPerSet) { | |
throw new Error('Invalid input.'); | |
} | |
const totalCombinations = words.length ** wordsPerSet; | |
const requiredBits = Math.ceil(Math.log2(totalCombinations)); | |
const splitSize = Math.floor(requiredBits / wordsPerSet); | |
let binaryString = ''; | |
for (const word of selectedWords) { | |
const index = words.indexOf(word); | |
if (index === -1) { | |
throw new Error(`Word '${word}' not found in the list.`); | |
} | |
let partBinary = index.toString(2).padStart(splitSize, '0'); | |
binaryString += partBinary; | |
} | |
return parseInt(binaryString, 2); | |
} | |
// Example usage: | |
// const words = ['apple', 'banana', 'cherry', 'date', 'elderberry']; | |
// const seed = 42; | |
// const wordsPerSet = 2; | |
// const generatedWords = generateWords(words, seed, wordsPerSet); | |
// console.log(generatedWords); | |
// const reverseSeed = wordsToSeed(words, generatedWords, wordsPerSet); | |
// console.log(reverseSeed); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment