1

I am making a word game that picks 8 random letters from the alphabet and the player must find words that use these letters. I must find out a way to make the letters picked always contain 3 vowels and 5 consonants. I would appreciate your help

Here is the code I am using to pick the random letters for the game

function makeid(length) {
  var result           = '';
  var characters       = 'aaabcdeeefghiiijklmnooopqrstuuuvwxyz';
  var charactersLength = characters.length;
  for (var i = 0; i < length; i++) {
    let letter = characters.charAt(Math.floor(Math.random() * charactersLength));
    while (result.match(letter)) {
      letter = characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    result += letter;
  }
  return result;
}

console.log("Id of 8 characters", makeid(8))
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
pigeoned
  • 15
  • 4

3 Answers3

2

It's a lot of code, but this works for me.

The function begins with a for-loop, which generates 8 different letters. inside of that loop, Math.random() makes it a 37.5% chance that a vowel will be added to the randomArray, and 62.5% that a consonant will be added (8 characters from which 3 are vowels, so 3 / 8 = 0.375).

when that is done 8 times (cuz of the for loop), the array will be turned into a string, and the function will return the string (which is by then an 8-digit letter code with 3 vowels and 5 consonants).

I hope this explanation helps(;

function getRandomString() {
  const vowels = 'aeiou'
  const consonants = 'bcdfghjklmnpqrstvwxyz'
  let randomArray = []
  let amountOfVowels = 0
  let amountOfConsonants = 0

  for (let i = 0; i < 8; i++) {
    if (Math.random() < 0.375 && amountOfVowels < 3) addVowel()
    else if (amountOfConsonants < 5) addConsonant()
    else addVowel()
  }

  function addVowel() {
    randomArray.push(vowels[Math.floor(Math.random() *
      vowels.length)])
    amountOfVowels++
  }

  function addConsonant() {
    randomArray.push(consonants[Math.floor(Math.random() *
      consonants.length)])
    amountOfConsonants++
  }
  let finalString = ''
  for (let i = 0; i < 8; i++) {
    finalString += randomArray[i]
  }
  return finalString;
}

console.log('random string:', getRandomString())
  • 1
    Thanks for a solution but it would be more useful if you explained what the problem was and how you solved it. Otherwise, the OP could copy/paste the code but not learn anything. – Ruan Mendes May 05 '22 at 12:44
  • Yeah, i'll add an explanation! – Lars Verschoor May 05 '22 at 12:50
  • Why did you undo my edit where I made your code runnable? – Ruan Mendes May 05 '22 at 12:53
  • Oh, i am sorry, i think i did that by accident – Lars Verschoor May 05 '22 at 12:59
  • 1
    The only thing that's iffy about this algorithm is that you're not really placing the characters at random locations when you do `if(Math.random() > 0.5) {push()} else {unshift()}`. You can verify what I'm saying by typing your algorithm into https://bost.ocks.org/mike/shuffle/compare.html . Here's [what it looks like](https://imgur.com/a/I8nSYbk) – Ruan Mendes May 05 '22 at 13:01
  • Yeah, but i can't think of a better option, i think this is good enough right? – Lars Verschoor May 05 '22 at 13:09
  • 1
    It's not good enough if the OP requires a real random string. I would just add the letters in order and then [shuffle them as suggested by this post](https://stackoverflow.com/a/3943985/227299) (but without polluting String's prototype). I can fix your answer if you'd like – Ruan Mendes May 05 '22 at 13:12
  • I believe i have fixed it myself now, did i? – Lars Verschoor May 05 '22 at 13:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/244509/discussion-between-juan-mendes-and-lars-verschoor). – Ruan Mendes May 05 '22 at 13:16
  • It's still not distributed evenly :( – Ruan Mendes May 05 '22 at 13:47
1
  1. Have one array of consonants and one array for vowels.

  2. Use a shuffling function, here's a terse one liner:

    const shuffle = array => array.sort(() => Math.random() - 0.5);
    
  3. The function above will return a given array in a random order, so you'll need to shorten each array by 5 (consonants) and 3 (vowels):

    let C = shuffle(consonants);
    let V = shuffle(vowels); 
    
    C.length = 5;
    V.length = 3;
    

    Easy

// Utility Function (optional)
const log = data => console.log(JSON.stringify(data));

const consonants = ["B", "C", "D", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "V", "W", "X", "Y", "Z"];
const vowels = ["A", "E", "I", "O", "U"];

const shuffle = array => array.sort(() => Math.random() - 0.5);

let C = shuffle(consonants);
let V = shuffle(vowels); 

C.length = 5;
V.length = 3;

const result = shuffle(V.concat(C));

log(result);
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • You are not concatenating the two final strings together? Also, this suffers from the same problem as @Lars . [Shuffling by random sorting is not truly shuffling](https://stackoverflow.com/questions/962802/is-it-correct-to-use-javascript-array-sort-method-for-shuffling) – Ruan Mendes May 05 '22 at 13:43
  • A full string isn't as useful unless there's code showing how an 8 char string is handled. Have you ever watched Wheel of Fortune? About the shuffling, the results are the same whether you use a simple function or a Fischer-Yeats algorithm (which is overkill for the current purpose). I would use the latter solution for something that actually needs it like a blackjack game. Click the "Run code button" several times, did it repeat itself or did it provide different combinations of letters that meet the OP's requirements (aside from the 8 letter string which can be combined easily at any time)? – zer00ne May 05 '22 at 14:12
  • Updated result to a shuffled 8 char string 3/5 as per OP's request. Did the code produce what is expected? Or should it be *"more random?"* – zer00ne May 05 '22 at 14:23
  • You're still using `Math.random()` as a custom comparator. It is not "more random". Read the link I sent if you would like to fully understand it. – Ruan Mendes May 05 '22 at 14:39
  • I was being sarcastic about my code needing to be "more random". At this scale what you are so adamant about isn't a factor. Some people worry about performance or the edge cases, I worry if my code delivers results, is simple enough to avoid time consuming maintenance and if it's stable. I understand what you're saying but it's ridiculous to scrutinize working code when your own code doesn't work https://i.ibb.co/Khc5qJp/ii.jpg – zer00ne May 05 '22 at 15:16
  • What doesn't work about my code? There is nothing saying vowels or consonants cannot be repeated, words do have repeated letters. I'm just letting you know that your shuffling function is not producing truly random results. If you don't care, don't worry about it! – Ruan Mendes May 05 '22 at 15:23
  • I'm not you are, your code is generating doubles. The only reason why OP has duplicated vowels is because they were on a single string and harder to get. Note all consonants are single. It's truly not random in how it's made but it is as a result and that's all that matters at this level. – zer00ne May 05 '22 at 15:28
  • There is nothing in the question that says duplicated vowels are not acceptable. The point is to create a word and words can have duplicate letters. It's probably better to just let the OP clarify what behavior they want. – Ruan Mendes May 05 '22 at 15:35
  • So it looks after all my whining, the OP did want unique letters If it's a game to create words, most of these generated strings cannot be permutated to create a word. – Ruan Mendes May 06 '22 at 13:14
  • To me the mechanics of the game (from what was originally posted in the question), is going to have problems, unless it's like [Boggle](https://wordshake.com/boggle) or an alternative version of Hangman. – zer00ne May 06 '22 at 13:38
  • True, I guess we could pick just some of the letters for hangman, but I still think duplicated letters would help the game – Ruan Mendes May 06 '22 at 13:51
1

@Lars answer is almost perfect, the only problem is that the final string is not truly shuffled.

I suggest you just create two arrays: random vowels and random consonants and then just shuffle them using Fisher-Yates' algorithm.

function getRandomCharsFromString(str, length) {
    return Array.from({length}, () => {
      return str[Math.floor(Math.random() *  str.length)]
    });
}

function shuffle(str) {
  var a = str.split(""),
    n = a.length;

  for (var i = n - 1; i > 0; i--) {
    var j = Math.floor(Math.random() * (i + 1));
    // Swap them with ES6 destructuring magic
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a.join("");
}

function getRandomString() {
  const vowels = 'aeiou';
  const consonants = 'bcdfghjklmnpqrstvwxyz';

  const randomVowels = getRandomCharsFromString(vowels, 3);
  const randomConsonants = getRandomCharsFromString(consonants, 5);
  
  const randomWord = [...randomVowels, ...randomConsonants].join('')
  return shuffle(randomWord)
}


console.log('random string:', getRandomString())

No repeated letters

You mentioned you don't want repeated letters; many words in English have duplicate letters. Why is that a requirement?

You can shuffle the vowels and consonants and get the first x characters from that string.

// This version makes sure characters are not repeated
function getRandomCharsFromString(str, length) {
    return shuffle(str).slice(0, length);
}

function shuffle(str) {
  var a = str.split(''),
    n = a.length;

  for (var i = n - 1; i > 0; i--) {
    var j = Math.floor(Math.random() * (i + 1));
    // Swap them with ES6 destructuring magic
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

function getRandomString() {
  const vowels = 'aeiou';
  const consonants = 'bcdfghjklmnpqrstvwxyz';

  const randomVowels = getRandomCharsFromString(vowels, 3);
  const randomConsonants = getRandomCharsFromString(consonants, 5);
  
  const randomWord = [...randomVowels, ...randomConsonants].join('')
  return shuffle(randomWord).join('')
}


console.log('random string:', getRandomString())
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • hi juan!!! thank you so much for this it helps a lot :) the only thing is that it generates duplicate letters which my original code did not. do you think you could help me with implementing this into this new code? thank you!! – pigeoned May 06 '22 at 11:46
  • @pigeoned So the words you create cannot have duplicate letters? You are excluding a lot of words with that restriction Note that [zer00ne's solution](https://stackoverflow.com/a/72127898/227299) already does that. A way to do it with my code would be to shuffle the constants and vowels array and get the first characters instead, like zer00ne did. – Ruan Mendes May 06 '22 at 13:03