Home > Software design >  Replace a letter in an array with hidden letters in javascript (hangman school project)
Replace a letter in an array with hidden letters in javascript (hangman school project)

Time:12-14

I have a school project where i'm doing a Hangman game in javascript and it is my first project doing anything in JS so please bear with me here. I have made it a little bit difficult for my self on purpose because i think i will learn more and in a better way if i do so. All of the HTML is created with JS. So my problem is that when i select a correct letter in the word it does'nt change the hidden letter(a questionmark) in the hidden word. I can console.log the correct letter in a function where it checks if the letter is in the word but i can seem to replace it.. Here is my code: I have four buttons with categories created with a function:

/* Categories buttons */
function makeUl(object) {
  let catUl = document.createElement("ul");
  catUl.id = "cat-ul";

  for (let property of Object.keys(object)) {
    let catList = document.createElement("li");
    let catBtn = document.createElement("button");
    catList.className = "cat-list";
    catBtn.className = "cat-btn ";
    catBtn.onclick = select;
    catBtn.appendChild(document.createTextNode(property));
    catList.appendChild(catBtn);
    catUl.appendChild(catList);

    function select() {
      document.getElementById("startGame-btn").style.visibility = "visible";
      wordToFind = getRandomWord(categoriesOb[property]);
      wordToFind = wordToFind.toUpperCase();
      chosenCategory = property.toString();
      gameText.textContent = chosenCategory;
      secretWord();
      console.log(chosenCategory);
      console.log(wordToFind);
      console.log(wordArray);
    }
  }
  return catUl;
}
document.getElementById("category-container").appendChild(makeUl(categoriesOb));

If you press a button you get a random selected word from an object where the categories also are stored:

/* Categories*/
let categoriesOb = {
  Animals: ["rabbit", "horse", "dog", "bird"],
  Cities: ["paris", "london", "boston", "prague"],
  Fruits: ["banana", "apple", "orange", "pear"],
  Movies: ["frost", "jaws", "batman", "avatar"],
};

Then i the random selected word get split and changed in to questionmarks and put in the

-tag:

/* Change letters into questionmarks  */
function secretWord() {
  wordArray = wordToFind
    .split("")
    .map((questionMark) =>
      correctGuesses.indexOf(questionMark) >= 0 ? questionMark : "?"
    )
    .join("");
  wordParagraph.innerHTML = wordArray;
}

This is the function what creates the alphabet from an array of the alphabet and disables the buttons.

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

/* Function to make the letterbuttons and set disabled = true/false */
function letterList(letters) {
  let lettersUl = document.createElement("ul");
  lettersUl.id = "letter-ul";

  for (let i = 0; i < letters.length; i  ) {
    let letters = document.createElement("button");
    letters.className = "letter-list";
    let letterCheck = () => {
      let inTxt = letters.innerText;
      letters.disabled = true;
      console.log(inTxt);
      guessedLetter(inTxt);
    };
    letters.addEventListener("click", letterCheck);
    letters.appendChild(document.createTextNode(alphabet[i]));
    lettersUl.appendChild(letters);
  }
  return lettersUl;
}
document.getElementById("game-container").appendChild(letterList(alphabet));

And here is the function where i check if the selected letter is in the hidden word:

function guessedLetter(inTxt) {
  if (wordToFind.indexOf(inTxt) === -1) {
    console.log("Wrong Letter");
    guessesLeft--;
    wrongGuesses  ;
    updateWrongs();
    wrongGuess();
  } else {
    for (let i = 0; i < wordToFind.length; i  ) {
      if (wordToFind.indexOf(inTxt) >= 0) {
        console.log("Correct Letter");
        // Here is where i'm supposed to have a code to change the questionmark to the correct letter
      }
    }
  }
}

I have tried many things to change it but can seem to figure it out..

This code changes all the questionmark to the selected letter:

wordArray = wordToFind
          .split("")
          .map((correctLetter) =>
            wordArray[i].indexOf(correctLetter) >= 0 ? correctLetter : inTxt
          )
          .join("");
        wordParagraph.innerHTML = wordArray;

I've tried to put

wordParagraph.innerHTML = wordArray[i]; // changes the word to one questionmark
wordParagraph.innerHTML = wordToFind[i]; // changes the word to the last letter in the hidden word

I've tried more stuff to but can't remember them all, been sitting with this for a week now..

I hope there is someone that can help me in the right direction since I supposed to be done tomorrow.

CodePudding user response:

I see you you have two choices:

  1. keep an in-progress string eg foundSoFar
  2. keep a record of each key pressed so far and rebuild each time.

Taking the first option (and keeping the paradigm of using implicit global variables)

// wordToFind = global variable containing the selected word 
//              this doesn't change during the game (ie doesn't get the "?"), 

var foundSoFar = "";  // global containing unguessed ?

function secretWord() {
    foundSoFar = "?".repeat(wordToFind.length);
    wordParagraph.innerText = foundSoFar;
}

Then your guessedLetter can use indexOf to replace the new letter.

You need to convert to an array to replace letter by letter.

    let letters = foundSoFar.split("");
    let pos = 0;
    pos = wordToFind.indexOf(inTxt, pos);
    while (pos >= 0) {
        letters[pos] = originalWord[pos];
        pos = wordToFind.indexOf(inTxt, pos 1);
    }
    foundSoFar = letters.join("");

Updated snippet:

var originalWord = "London"
var wordToFind = originalWord.toUpperCase();
var foundSoFar = "?".repeat(wordToFind.length);

console.log(foundSoFar)

function guessedLetter(inTxt) {
  inTxt = inTxt.toUpperCase();
  if (wordToFind.indexOf(inTxt) === -1) {
     console.log(inTxt, "not found");
  } else {
    console.log(inTxt, "Correct Letter");
    // replace all that match
    // need an array as string are immutable
    // but can access original string character-by-character
    let letters = foundSoFar.split("");
    let pos = 0;
    pos = wordToFind.indexOf(inTxt, pos);
    while (pos >= 0) {
        letters[pos] = originalWord[pos];
        pos = wordToFind.indexOf(inTxt, pos 1);
    }
    foundSoFar = letters.join("");
    if (foundSoFar == originalWord)
       console.log("You win")
    // same code as initial setting ???
    //wordParagraph.innerText = foundSoFar;
  }
}

guessedLetter("n");
console.log(foundSoFar);
guessedLetter("e");
console.log(foundSoFar);
guessedLetter("l");
console.log(foundSoFar);
guessedLetter("o");
console.log(foundSoFar);
guessedLetter("d");
console.log(foundSoFar);

Note: this isn't the most efficient code and certainly not the nicest, but I believe it keeps with the direction you were going.

CodePudding user response:

First an explanation of the solutions you tried:

  1. wordParagraph.innerHTML = wordArray[i]; takes the ith index of wordArray and sets that as the innerHTML. Since you call this for every i, it makes sense that it shows a single question mark because you keep overwriting the innerHTML every time.
  2. wordParagraph.innerHTML = wordToFind[i]; takes the ith index of wordToFind and sets that as the innerHTML. For the same reason as 1., this also shows a single letter.

Now, for a solution let's look at an example. In the case of the word being Rabbit and the letter B has been guessed, I'm assuming you want to change the innerHTML of ?????? to ??bb??. This example is also very useful since it presents us with the problem of having the same letter twice in a word.

With inTxt being the letter being guessed, this is also the letter you would want replacing the question mark(s) at its given index(es).

indexOf tells you exactly which index you need to replace, but since the letter might be present twice (as we have already seen) you can't just pick that index to replace in wordArray. To solve this you would need to iterate through your wordToFind, and for each given index see if the letter matches - and if it does, replace it in wordArray. Then after this iteration, the innerHTML should be set to the new value of wordArray.

Something like this should work:

function guessedLetter(inTxt) {
  if (wordToFind.indexOf(inTxt) === -1) {
    ... // Removed for readability. No changes here.
  } else {
    for (let i = 0; i < wordToFind.length; i  ) {
      if(wordToFind[i] == inTxt) {
        console.log("Correct Letter");
        wordArray[i] = inTxt;
      }
    }
    wordParagraph.innerHTML = wordArray.join("");
  }
}

This assumes that your wordArray is actually an array.

  • Related