Hangman case insensesetive - javascript

we are in our 3 week of our Bootcamp.
Project is to code the Hangman game, it is running in the terminal.
We got preety far in just 2 days I think.
We are now stucked on the case insensesetivity, meaning if the word we are looking for is Java that if you type j or J, you still would get the same result J_ _ _.
I looked all over the Internet and found the locale.compare method which isn´t working for me. If i type a wrong letter everything is fine but if I type a right one I get a error and also it doesn´t ignore the case of the letter.
We are not using functions yet because we haven´t learned how they work.
const constants = require('./constants');
// In node.js: install a prompt library by running: `npm install prompt-sync` in the current folder
const prompt = require("prompt-sync")();
// Here you see an example how to get your
// constants from constants.js
/*for(let figure of constants.HANGMAN_PICS)
{
console.log(figure);
}
*/
let Answer = [];
var Words = constants.WORDS_TO_GUESS[Math.floor(Math.random() * constants.WORDS_TO_GUESS.length)];
/*console.log(Words.length); //Wortlänge von random Word
*/
for (let i = 0; i < Words.length; i++) {
Answer[i] = "_";
}
console.log(Answer.join(" "));
for (; Answer !== Words;) {
input = prompt(`Finde das Wort.`);
if (Words.includes(input)) {
for (let i = 0; i < Words.length; i++) {
if (Words[i] === input) {
Answer[i] = input;
}
}
console.log(Answer.join(" "));
console.log(Answer.localeCompare(Words, {
sensitivity: `base`
}));
} else if (!Words.includes(input)) {
console.log("Falsche Eingabe - keep trying!");
console.log(Answer.join(" "))
}
}
// how to use the prompt - e.g.:
// const name = prompt('What is your name?');

my way of doing this (which may not be the best) is setting both strings to lower case and comparing them with includes() function.
The for loop afterwards will just collect right position/s that you want to show up after right guess.
const word = 'JavaScript'
const char = 's' //input
const indexes = [] //output
//https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
//includes() function
if(word.toLowerCase().includes(char.toLowerCase())){
//if you need to get the index/es of the latter/s
for (let i = 0; i < word.toLowerCase().length; i++) {
if (word.toLowerCase()[i] === char.toLowerCase()) {
indexes.push(i)
}
}
}
console.log(indexes)

Make sure that Words (which should be word, singular and not capitalized) is in lower case (or upper case) by doing .toLocaleLowerCase() (or .toLocaleUpperCase()) at the end of where you assign it:
var Words = constants.WORDS_TO_GUESS[Math.floor(Math.random() * constants.WORDS_TO_GUESS.length)].toLocaleLowerCase();
// >>> −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^
Then make sure input is lower case (or upper case):
input = prompt(`Finde das Wort.`).toLocaleLowerCase();
At that point, you'll be comparing j and j (or J and J), so you'll find a match.
If you don't want to consistently use one capitalization in your code, it's possible to do it differently, but more complicated.
Basically, you do the .toLocaleLowerCase() (or .toLocaleUpperCase()) everywhere you do a comparison:
for (; Answer !== Words;) {
input = prompt(`Finde das Wort.`).toLocaleLowerCase();
// Get the input in lower case −−−−−−^^^^^^^^^^^^^^^^^^^^
if (Words.toLocaleLowerCase().includes(input)) {
// ^^^^^^^^^^^^^^^^^^^^−−− make Words lower case for the includes call
for (let i = 0; i < Words.length; i++) {
if (Words[i].toLocaleLowerCase() === input) {
// ^^^^^^^^^^^^^^^^^^^^−− make Words[i] lower case for the ===
Answer[i] = Words[i];
// ^^^^^^^^−−− use the one from Words, not the user's input
}
}
// ...
}
}

Related

Wordle implementation - dealing with duplicate letters (edge case)

I am trying to create my own implementation of wordle from scratch in angular and am stuck trying to resolve one of the edge cases. Assume the word to guess for all the following cases is "CLOSE".
CASE 1 - Both duplicate letters are in the wrong position.
If my guess is "CHEER", both of the 'E's are in the wrong position, but "CLOSE" only has one 'E' and hence only one should get a yellow highlight. My code IS dealing with this as desired
CASE 2 - The letter with the correct position comes first followed by the same letter in the incorrect position
If my guess is "COCKS", the first 'C' is in the correct position and should get a yellow highlight whereas 'C' should get a gray highlight since "CLOSE" only has one 'C'. My code IS dealing with this as desired as well
CASE 3 - The letter with the incorrect position comes first followed by the same letter in the correct position
If my guess is "LEAVE", the first 'E' is in the incorrect position, but because the second 'E' is in the correct position, and "CLOSE" only has one 'E', it should get a gray highlight and the second 'E' should get a green highlight. This is the edge case that I want to fix
The outcome of my implementation:
The desired outcome of this case:
The logic that I am using at the moment:
let remainingLettersInWord: string = this.chosenWord;
for (let i = 0; i < 5; i++) {
if (this.currentGuess[i] === this.chosenWord[i]) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Green);
} else if (remainingLettersInWord.includes(this.currentGuess[i])) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Yellow);
} else {
this.setAttributeStyle(i, COLOR.Gray);
}
}
Here, chosenWord = "CLOSE", currentGuess = "CHEER" for case 1, "COCKS" for case 2 and "LEAVE" for case 3
I ended up fixing the problem with the help of one of my senior devs and the comment from #Chris G
Here is the solution I used:
let remainingLettersInWord: string = this.chosenWord;
for (let i = 0; i < 5; i++) {
if (this.currentGuess[i] === this.chosenWord[i]) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Green);
} else {
this.setAttributeStyle(i, COLOR.Gray);
}
}
for (let i = 0; i < 5; i++) {
if (remainingLettersInWord.includes(this.currentGuess[i]) && this.currentGuess[i] !== this.chosenWord[i]) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Yellow);
}
}
I split the logic for the yellow check into a separate loop and I am removing the green words from the list before checking the yellow list. That adheres to all my edge cases now
A different approach by counting the letters of the wanted word.
const
getColors = ([...word], [...input]) => {
const
count = {},
result = Array(5).fill('-'); // only to show the result.
for (let i = 0; i < input.length; i++) {
if (word[i] === input[i]) result[i] = 'green';
else count[word[i]] = (count[word[i]] || 0) + 1;
}
for (let i = 0; i < input.length; i++) {
if (word[i] === input[i] || !count[input[i]]) continue;
count[input[i]]--;
result[i] = 'yellow';
}
return result;
};
console.log(...getColors("CLOSE", "CHEER"));
console.log(...getColors("CLOSE", "COCKS"));
console.log(...getColors("CLOSE", "LEAVE"));

Can i make this code more efficient / shorter to run?

So Im pretty new to javascript and coding in general. Im making a Wordle algorithm just for fun to build my skills in coding. and while making this algorithm i realized that i was going to have to kind of recreate Wordle. so i looked online for wordle recreations in java script, and all the ones I found I saw one flaw that I didn't want to have.
The flaw that I saw was in the IF statement for checking a letter in the wordle answer. Specifically when the letter is in the word but not in the right spot. The recreations that i saw had an IF statement that looked something like this.
IF (answerWord.includes(guessLetter[i])) {
guessLetter[i] = color.(yellow)
}
(this isnt exactly how they wrote it, but its the main idea)
My main focus is on the .includes. This would not work because say our guess word is "agree" and the answer word "ready". the 2 E's in "agree" would be yellow, and we dont want that because their is is only 1 E in "ready. So we only want the first E in "agree" to be yellow and not the second E.
So i decided to figure it out on my own, and i was able to come up with this and it works. At least Im pretty sure it works, base on the the many words i tested it on.
So my question is, since Im making an algorithm im going to be making alot of calculations, is this the most efficient i could write this or can i make it better?
let guessWord = "agree"; // the first word thats inputed
let answerWord = "ready"; // the answer to the wordle
/*
'letterCheck' is an array that tells what condition each letter in 'startLetter' is, based on the answer word
2 = the letter is in the word and in the correct place (GREEN)
1 = the letter is in the word but not in the correct place (YELLOW)
0 = the letter in not in the word (GREY)
*/
var letterCheck = ["0", "0", "0", "0", "0"];
var guessLetter = ["A", "A", "A", "A", "A"]; // the separated letters of 'startword' in a array
var answerLetter = ["A", "A", "A", "A", "A"]; // the separated letters of 'answord' in a array
//adds the start word and the answer world to the arrays
for (var i = 0; i < 5; i++) {
guessLetter[i] = guessWord.substring(i, i + 1);
answerLetter[i] = answerWord.substring(i, i + 1);
}
console.log(guessLetter);
console.log(letterCheck);
console.log(answerLetter);
//this loops goes though every letter one by one
for (var i = 0; i < 5; i++) {
//checks if the letter is in the right spot
if (guessLetter[i] == answerLetter[i]) {
letterCheck[i] = "2";
console.log(guessLetter[i]);
console.log(letterCheck);
} else if (answerWord.includes(guessLetter[i])) {
//checks if there is more than one letter in start word or its the first letter
if (guessWord.split(guessLetter[i]).length - 1 == 1 || i == 0) {
letterCheck[i] = "1";
console.log(guessLetter[i]);
console.log(letterCheck);
//checks if the the amount of same letters in start words is equel to the amount of same letters in answer word
} else if (guessWord.split(guessLetter[i]).length - 1 == answerWord.split(guessLetter[i]).length - 1) {
letterCheck[i] = "1";
console.log(guessLetter[i]);
console.log(letterCheck);
//opposite of above
} else if (guessWord.split(guessLetter[i]).length - 1 != answerWord.split(guessLetter[i]).length - 1) {
letterCheck[i] = "1";
console.log(guessLetter[i]);
console.log(letterCheck);
// checks if any of the letters infront of it are the same as it
for (var j = 0; j < i; j++) {
if (guessLetter[i] == guessLetter[j]) {
letterCheck[i] = "0";
console.log(guessLetter[i]);
console.log(letterCheck);
}
}
}
} else {
letterCheck[i] = "0";
console.log(guessLetter[i]);
console.log(letterCheck);
}
}
console.log(guessLetter);
console.log(letterCheck);
console.log(answerLetter);
( I will remove the console.log's later they just helped my to debug.)
Sorry if my code might be confusing, im not the best at keeping my code clean. if you have any confusion or questions, I'll try my best in clear up any confusion.
To get all of the states (in precedence order: correct, wrong-position, incorrect), you should check in that precedence order, and remove letters from contention once the state in that letter's position has been set.
function check(guess, targetWord) {
const checkStates = new Array(5).fill("incorrect");
const letters = guess.split('');
const targetLetters = targetWord.split('');
for (let i=0; i<5; i++) {
if (targetLetters[i] === letters[i]) {
checkStates[i] = "correct";
targetLetters[i] = '';
letters[i] = '';
}
}
for (let i=0; i<5; i++) {
if (!letters[i]) continue;
let fi = targetLetters.findIndex(t => t===letters[i]);
if (fi > -1) {
checkStates[i] = "contains";
targetLetters[fi] = '';
}
}
return checkStates;
}
let solution = "ready"
console.log(`solution is ${solution}`);
let guess = "agree";
console.log(`checking ${guess}... ${check(guess, solution)}`);
guess = "roars";
console.log(`checking ${guess}... ${check(guess, solution)}`);
guess = "boars";
console.log(`checking ${guess}... ${check(guess, solution)}`);
solution = "beast"
console.log(`\nsolution is ${solution}`);
guess = "treat";
console.log(`checking ${guess}... ${check(guess, solution)}`);
Well, here's something to consider in simplifying. You can use a simple MAP loop to determine which letters are incorrect, then later highlight those in the word.
let guessWord = "agree"; // the first word thats inputed
let answerWord = "ready"; // the answer to the wordle
let tmpGuess = guessWord.split('');
let incorrects = answerWord.split('').map(e => {
let i = tmpGuess.indexOf(e);
if (i > -1) { // found it!
tmpGuess.splice(i, 1); // remove
return '';
}
return e;
}).filter(f => f);
console.log(incorrects);

Javascript - Loop string until null character found

I want to iterate through a string until a null character is found (\0), like how we do in C language. I have listed down the steps which I have tried below.
let exampleValue = 'abcdef';
let i = 0;
// Trial 1
while (exampleValue[i] !== '\0') {
i++;
// This seems to go on infinitely
}
// Trial 2
while (exampleValue[i] !== '\0'.charCodeAt(0)) {
i++;
// This seems to go on infinitely
}
// Trial 3
while (exampleValue[i] !== \0) {
i++;
// This throws an invalid character error
}
// Trial 4
while (exampleValue[i] !== undefined) {
i++;
// This seems to work
}
Based on the above samples, Trial 4 seems to work. Can I continue using Trial 4 for my desired output or is there a better way to solve my problem?
EDIT:
I apologize for not specifying my problem, I want to print each letter of the string by iterating through it without using exampleValue.length
EDIT 2:
After I read tadman's comment, I got to know Javascript's do not terminate their string using a null character but instead it stores it's keeps track of it characters and stores the length seperately.
If you just want to count the length then just do exampleValue.length
let exampleValue = 'abcdef';
let i = exampleValue.length;
console.log(i);
Or if you want to perform some logic using that char, you can try:
let exampleValue = 'abcdef';
let i = 0;
exampleValue.split('').forEach(c => {
console.log(c);
i++;
// Your logic here.
});
console.log(i);
Or using plain for loop:
let exampleValue = 'abcdef';
let i = 0;
for (let x = 0; x < exampleValue.length; x++) {
console.log(exampleValue.charAt(i));
i++;
// Your logic here.
}
console.log(i);
You are getting these cases because you will never get an '\0' in your string so the loop will run infinitely.
And the last case is working fine because after the last character of your string, the next character will be undefined.
Now, if you want to loop through the string then you can do it this way:
let exampleValue = 'abcdef';
let i = 0;
while (i < exampleValue.length ) {
console.log(exampleValue[i]);
i++;
}
Since you didn't say exactly what you realy want... If you want to print the individual characters,
let exampleValue = 'abcdef';
for(var j = 0; j < exampleValue.length; j++){
console.log(exampleValue[j]);
}
And if you are just interested in the number of elements:
int i = exampleValue.length;
console.log(i)

Logical error keeps for loop looping infinite, Why?

I'm trying to build a JavaScript devowelizer, but I'm producing an infinite loop.
I'm hoping someone on Stack Overflow can help?
The code =>
let userWord = prompt("Type a word to devowelize: ");
userWord = Devowelize(userWord);
alert(userWord);
function Devowelize(word) {
for (let i = 0; i <= word.length; i++) {
let eatChars = "aeiou"
for (let i2 = 0; i2 <= eatChars.length;) {
if (word[i] == eatChars[i2] &&
word[i] != "") {
word = word.replace(word[i], "");
} else {
i2++;
}
}
}
return word
}
You are using here for (let i = 0; i <= word.length; i++) this part i <= word.length isn't correct because you will try to access the array word using the i index after that in your code so in the last iteration you will access an index which is not defined by your array the last index of an array in javascript is always arrayLength-1 if you access an item which is out of the array you will get an undefined as value which will generates an infinte loop in your case you have done the same thing here for (let i2 = 0; i2 <= eatChars.length;) but the first loop is the responsible of the infinite loop in your code
Your solution is almost there, but you're trying to solve this problem in a very roundabout way. Let's make it a bit easier to understand.
In JavaScript, you can easily check if a string contains another string. For example, if we wanted to check if a character was in a string, we could do this:
let eatChars = "aeiou"
eatChars.includes('e') === true
So knowing that we can do that in a single statement, let's reuse some of the code you've got and substitute the character 'e' for the characters in your word.
let outWord = ""
const eatChars = "aeiou"
// '<= word.length' becomes this, because the string positions only go up to 'word.length - 1
for (let i = 0; i < word.length; i++) {
if (!eatChars.includes(word[i])) { // so the character isn't a vowel
outWord += word[i]
}
}
return outWord
The comments mention learning about 'map' and 'filter'. I'd recommend using a reducer for this if you wanted to be fancy! You could try something like this:
const devowel = (word) => Array.from(word).reduce((out, currentCharacter) => ...)

Javascript: matching a dynamic string against an array

I'm attempting to teach myself javascript. I chose something I assumed was simple, but ran into problems relatively quickly.
I'm attempting to search a string for another string given by the user.
My code so far is:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = [];
var one = 0;
var two = 0;
var k = 0;
var sourceSearch = function(text) {
for(i = 0; i < source.length; i++) { //for each character in the source
if(source[i] === searchString[0]) { //if a character in source matches the first element in the users input
one = source.indexOf(i); //confused from here on
for(p = searchString.length; p > 0; p--) {
}
}
}
};
sourceSearch(searchString);
My idea was:
check to see if the first loop finds a character that matches the first character in the user input
if it matches, check to see if the next X characters after the first match the next X characters in the source string
if they all match, push them to the hits array
My problem: I have no idea how to iterate along the arrays without nesting quite a few if statements, and even then, that wouldn't be sufficient, considering I want the program to work with any input.
Any ideas would be helpful. Thanks very much in advance.
Note: There are a few un-used variables from ideas I was testing, but I couldn't make them work.
You can try:
if (source.indexOf(searchString) !== -1) {
// Match!
}
else
{
//No Match!
}
As the other answers so far point out, JavaScript strings have an indexOf function that does what you want. If you want to see how it's done "by hand", you can modify your function like this:
var sourceSearch = function(text) {
var i, j, ok; // always declare your local variables. globals are evil!
// for each start position
for(i = 0; i < source.length; i++) {
ok = true;
// check for a match
for (j = searchString.length - 1; ok && j >= 0; --j) {
ok = source[i + j] === searchString[j];
}
if (ok) {
// searchString found starting at index i in source
}
}
};
This function will find all positions in source at which searchString was found. (Of course, you could break out of the loop on the first success.) The logic is to use the outer loop to advance to each candidate start position in source and use the inner loop to test whether that position actually is the position of a match to searchString.
This is not the best algorithm for searching strings. The built-in algorithm is much faster (both because it is a better algorithm and because it is native code).
to follow your approach, you can just play with 2 indexes:
var sourceSearch = function(text) {
j = 0;
for(i = 0; i < source.length; i++) {
if(source[i] === text[j]) {
j++;
} else {
j = 0;
}
if (j == text.length) {
console.log(i - j); //this prints the starting index of the matching substring
}
}
};
These answers are all pretty good, but I'd probably opt for something like this:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = source.split(searchString);
var hitsCount = hits.length - 1;
This way you have all of the data you need to figure out where each hit occurred in he source, if that's important to you.

Categories