Check if user input was already inputted - c#

Im trying to make a user enter a char and check if they entered that char before. and if that happens ask him to enter another char that he hasn't used before. My knowledge is limited to if statements and loops so I would appreciate it if the solution is something that i can understand.
When i enter any letter e.g E it would throw it into the guessed array, if i enter E again, it throws into the guessed array again instead of asking the user to change the letter.
string check= "";
char wguess ='';
char[] wguess = new char[26];
do
{
check = Console.ReadLine();
if (check!="")
{
wguess = char.ToUpper(Convert.ToChar(check));
for (int i = 0; i <= 25; i++)
{
if (wguess == guessed[i])
{
Console.Write(wguess);
Console.WriteLine(guessed[i]);
Console.WriteLine("Please choose a letter you haven't used yet.");
}
else
{
Console.Write(wguess);
Console.WriteLine(guessed[i]);
temp = wguess;
guessed[i] = wguess;
wguess = ' ';
}
}
}
else
{
Console.WriteLine("Please Enter a letter.");
}
} while (check=="");
Output1 = Guessed Letters: E
Output2 = Guessed Letters: EE

You can use the Contains method, like this:
If(guessed.Contains(wguess)
{
//Whatever happens when the character already has been entered
}
else
{
//Whatever happens when the character has not been guessed
}

Change the code like the following:
do{
check = char.ToUpper(Convert.ToChar(Console.ReadKey()));
if (!guessed.Contains(check))
{
// This is new, not in the guessed
}
else
{
// Already entered
}
} while (check==' ');

Related

How to display or hide letters in a string - Hangman game

I'm a newbie in C# and I'm learning it for the past few weeks basically doing tasks and challenges to improve my knowledge. I came across making a "Hangman" console app and I'm close to finishing it. In fact, I can say that I made it but I would like to improve something and learn one new thing.
This is an example of my question if you can't understand it, sorry, English is not my main language.
You know how when you play hangman, once you guess the right letter that letter "shows itself" on its position in a word.
etc. word Head
you guess "e" and it prints _e__
you guess "d" and it prints _e_d
you guess "H" and it prints He_d
you guess "a" and it prints Head
Here is my code :
using System;
namespace Hangman
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
string word1;
Console.Write("Please enter a random word: ");
word1 = Console.ReadLine();
word1.ToLower();
Console.Clear();
int i = 0;
int j = 0;
do
{
string guess;
Console.Write("Please enter a letter: ");
guess = Console.ReadLine();
if (guess == "" +
"")
{
i += 1;
Console.WriteLine($"Not allowed. You have {word1.Length - i} tries left.\n");
}
else if(word1.Contains(guess) == true)
{
Console.WriteLine("You guessed correctly !\n");
j += 1;
}
else
{
i += 1;
Console.WriteLine($"You guessed wrongly ! You have {word1.Length - i} tries left\n");
}
/* If you guessed all the letters correctly this will print that you won */
if (j == word1.Length)
{
Console.Clear();
Console.WriteLine($"You won ! The guessing word was {word1}.\n");
}
/* If you tried too many times it will end your program and you will lose.
* If you guessed all letters in a word correctly you win and the game is done. */
if(i == word1.Length)
{
Console.Clear();
Console.WriteLine($"You lost ! The guessing word was {word1}.\n");
}
} while (i != word1.Length & j != word1.Length);
}
}
}
There are a few flows with your code, bugs if you will -
If you enter the word head and you keep guessing h you win.
When you enter your guess you can actually enter a string not just a single character.
Also the code does not consider the possibility for multiple character entries. For example if the word is alexleo - and I guess l - it will only give me a single match.
What we have to do is keeping track of the status of each letter in the word - that is whether the letter has been guessed or not.
At each iteration:
The user guesses a letter -
If the letter is contained in the word - we change the status to
guessed.
If the letter has already been guessed we warn the user.
If the letter is not contained in the word -we increase our flag
totalGuessCounts by 1.
We feedback the user and display what has been guessed so far.
The loop completes when we either win or the user has run out of guesses.
Firstly I have defined my letter setting class
public class LetterSetting
{
public char Letter { get; set; }
public bool HasBeenGuessed { get; set; }
public LetterSetting(char letter, bool hasBeenGuessed)
{
this.Letter = letter;
this.HasBeenGuessed = hasBeenGuessed;
}
}
Then I have defined the hangman word class - here is where we keep track of the letter that have been guessed, the logic to see if we have won and the display of what we have so far.
static class HangmanWord
{
static List<LetterSetting> LettersStatus = new List<LetterSetting>();
public static void Initialise(string word)
{
foreach (char letter in word)
{
LettersStatus.Add(new LetterSetting(letter, false));
}
}
public static bool SetGuessStatus(char letter)
{
List<LetterSetting> tempLettersStatus = new List<LetterSetting>();
foreach (LetterSetting item in LettersStatus)
{
if (item.Letter == letter && item.HasBeenGuessed)
{
return false;
}
tempLettersStatus.Add(item.Letter == letter ? new LetterSetting(letter, true) : item);
}
LettersStatus = tempLettersStatus;
return true;
}
public static void Display()
{
StringBuilder hangManWord = new StringBuilder();
foreach (var item in LettersStatus)
{
hangManWord.Append(!item.HasBeenGuessed ? '_' : item.Letter);
}
Console.SetCursorPosition(50, 1);
Console.Write(hangManWord.ToString());
Console.SetCursorPosition(0,1);
}
public static bool HaveWeWon()
{
return !LettersStatus.Any(letter => letter.HasBeenGuessed == false);
}
}
You main class will simply encapsulate the to retrieve the word and the game-loop:
class Program
{
static void Main(string[] args)
{
int totalGuessCounts = 0;
int maxNumberOfGuess = 0;
Console.Write("Please enter a random word: ");
string word = Console.ReadLine()?.ToLower();
while (word != null && word.Contains(" ") || string.IsNullOrWhiteSpace(word))
{
Console.Clear();
Console.WriteLine("Word must not contain spaces or be nothing ");
Console.Write("Please enter a random word: ");
word = Console.ReadLine()?.ToLower();
}
maxNumberOfGuess = word.Length;
HangmanWord.Initialise(word);
Console.Clear();
HangmanWord.Display();
do
{
Console.Write("Please enter a letter: ");
char guess = Console.ReadKey().KeyChar;
Console.WriteLine("\n");
Console.Clear();
if (!char.IsLetterOrDigit(guess))
{
continue;
}
if (word.Contains(guess))
{
Console.WriteLine(HangmanWord.SetGuessStatus(guess)
? "You guessed correctly!"
: "You already guessed that letter.");
}
else
{
totalGuessCounts++;
Console.WriteLine($"You guessed wrongly ! You have {word.Length - totalGuessCounts} tries left\n");
}
if (HangmanWord.HaveWeWon())
{
Console.WriteLine($"You won ! The guessing word was {word}.\n");
break;
}
if (totalGuessCounts == maxNumberOfGuess)
{
Console.WriteLine($"You lost ! The guessing word was {word}.\n");
}
HangmanWord.Display();
} while (totalGuessCounts != word.Length);
}
}
Here are a couple of results:
you can change the feedback layout - to your like - add a dictionary of words so you dont have to enter the word yourself - draw the hangman whilst the user guesses - Ideally you might want to move all the logic within the class HangmanWord - that is for failure , success and full feedback.
I coded something like this. This is my suggestion for solving this problem, but remember that it probably isn't the best. You can try to optimize it.
bool[] guessed = new bool[wordToGuess.Length];
int index = 0;
while ((index = wordToGuess.IndexOf(guess, index, wordToGuess.Length - index)) != -1)
{
guessed[index] = true;
index++;
}
for(int i = 0; i < wordToGuess.Length; i++)
{
if(guessed[i])
Console.Write(wordToGuess[i]);
else
Console.Write('_');
}
I used String.IndexOf(String, Int32, Int32) method for this purpose.

displaying and uncovering letters using asterisks

I am trying to create a hangman game that picks a random word from a text file and then displays the word as asterisks and has the user guess the letters of the word. If the user guesses correct then the letter gets uncovered and they keep guessing until they uncover the word. It will then display the number of misses and ask if the user wants to try to guess another word.
The problem I am having is when you try to guess the first letter in any word and guess it correctly it still says its incorrect and doesn't uncover the word.The other letters uncover but you then keep guessing forever because the first letter of the word can't be uncovered. I am not sure how to fix this
static void Main(string[] args)
{
char[] guessed = new char[26];
char guess = ' ';
char playAgain= ' ';
int amountMissed = 0, index = 0;
do
{
// initilization of word and testword so that we could generate a testword with the same length as original
char[] word = RandomLine().Trim().ToCharArray();
char[] testword = new string('*', word.Length).ToCharArray();
char[] copy = word;
Console.WriteLine(testword);
Console.WriteLine("I have picked a random word on animals");
Console.WriteLine("Your task is to guess the correct word");
while (!testword.SequenceEqual(word))
{
try
{
Console.Write("Please enter a letter to guess: ");
guess = char.Parse(Console.ReadLine());
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
bool right = false;
for (int j = 0; j < copy.Length; j++)
{
if (copy[j] == guess)
{
Console.WriteLine("Your guess is correct.");
testword[j] = guess;
guessed[index] = guess;
index++;
right = true;
}
}
if (right != true)
{
Console.WriteLine("Your guess is incorrect.");
amountMissed++;
}
else
{
right = false;
}
Console.WriteLine(testword);
}
Console.WriteLine($"The word is {copy}. You missed {amountMissed} times.");
try
{
Console.WriteLine("Do you want to guess another word? Enter y or n: ");
playAgain = char.Parse(Console.ReadLine());
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
} while (playAgain == 'y' || playAgain == 'Y');
Console.WriteLine("Good-Bye and thanks for playing my Hangman game.");
}
public static string RandomLine()
{
// store text file in an array and return a random value
string[] lines = File.ReadAllLines("D:\\CPSC1012 ADV PORTFOLIO\\Advanced Portfolio\\Advanced1.csv");
Random rand = new Random();
return lines[rand.Next(lines.Length)];
}
}
That code works fine for me once I modify RandomLine() to just return zebra (since I don't have access to your words file).
So the first thing I would check is that your animal names are actually all lower case in that file. I note that, if I have it return Zebra instead, it exhibits the behaviour you describe when I try to guess the lower case z.
If that is the case, you just need to make your comparisons case-insensitive, something like modifying RandomLine() to ensure the word is all lower case:
return lines[rand.Next(lines.Length)].ToLower();
and this to ensure your guess is lower case:
guess = char.Parse(Console.ReadLine().ToLower());

Need to remove duplicate values from C# program

I need some help with a C# program that i am creating. So in this scenario i am inputting duplicate values into the program. For Example, a,b,b,c,c.
The exercise is that if there are any duplicated letters inputted (no numbers) i should get an error stating "Duplicate Value. Please Try Again!" and will not accept the duplicate value, and should show the values as a,b,c,d,e.
class Program
{
static void Main(string[] args)
{
char[] arr = new char[5];
//User input
Console.WriteLine("Please Enter 5 Letters only: ");
for (int i = 0; i < arr.Length; i++)
{
arr[i] = Convert.ToChar(Console.ReadLine());
}
//display
for(int i = 0; i<arr.Length; i++)
{
Console.WriteLine("You have entered the following inputs: ");
Console.WriteLine(arrArray[i]);
}
}
}
Choose right data structure at beginning, use HashSet instead of array since the operations are mainly looking up & inserting.
Using a hashtable (Generic Dictionary) is an efficient way to determine if an entered character has already been encountered.
Also, the Char.IsLetter method in the .NET framework is a great way to check for bad data.
static void Main(string[] args) {
Dictionary<char, bool> charsEntered = new Dictionary<char, bool>();
Console.WriteLine("Please enter 5 characters, each on a separate line.");
while (charsEntered.Count() < 5) {
Console.WriteLine("Enter a character:");
char[] resultChars = Console.ReadLine().ToCharArray();
if(resultChars.Length != 1 || !Char.IsLetter(resultChars[0])) {
Console.WriteLine("Bad Entry. Try again.");
} else {
char charEntered = resultChars[0];
if (charsEntered.ContainsKey(charEntered))
Console.WriteLine("Character already encountered. Try again.");
else
charsEntered[charEntered] = true;
}
}
Console.WriteLine("The following inputs were entered:");
Console.WriteLine(String.Join(", ", charsEntered.Keys));
Console.ReadLine();
}
Use Any linq expression to validate duplicates. char.TryParse will validates input and returns true when succeeded.
public static void Main()
{
char[] arr = new char[5];
//User input
Console.WriteLine("Please Enter 5 Letters only: ");
for (int i = 0; i < arr.Length; i++)
{
char input;
if(char.TryParse(Console.ReadLine(), out input) && !arr.Any(c=>c == input))
{
arr[i] = input;
}
else
{
Console.WriteLine( "Error : Either invalid input or a duplicate entry.");
i--;
}
}
Console.WriteLine("You have entered the following inputs: ");
//display
for(int i = 0; i<arr.Length; i++)
{
Console.WriteLine(arr[i]);
}
}
Working Code
Elaborating on Shelvin's answer of using HashSet
HashSet<char> chars = new HashSet<char>();
//User input
Console.WriteLine("Please Enter 5 Letters only: ");
for (int i = 0; i < 5; )
{
char c = Convert.ToChar(Console.ReadLine());
if(!("abcdefghijklmnopqrstuvwxyz".Contains(c.ToString().ToLower())))
{
Console.WriteLine("Please enter an alphabet");
continue;
}
else if (!chars.Contains(c))
{
chars.Add(c);
i++;
}
else
{
Console.WriteLine("Duplicate value please try again");
continue;
}
}
//display
Console.WriteLine("You have entered the following inputs: ");
foreach(char c in chars)
Console.WriteLine(c.ToString());
Console.Read();
Keep it simple, and although a HashSet is nice semantically, it's not needed for 5 elements (it's actually slower than a List in that case). Worse, it requires a parallel structure to track the characters (assuming you care about order).
Clearly none of these considerations matter for such a small example but it's good to learn them up front and don't always jump to big-O notation when actually measured performance and memory consumption should be your guide for most practical applications.
Instead you can simply do:-
List<char> chars = new List<char>(5);
while (chars.Count < 5)
{
char c = Console.ReadKey().KeyChar;
if (!char.IsLetter(c)) continue;
if (chars.Contains(char)) continue;
chars.Add(char);
}
Plus whatever error messages you want to add.

C# how can I check users input

I´m having a string with allowed chars. I´d like that user is only able to use this chars.
My idea was to loop through the unser inputs string and compare char for char. But the problem which I have is when the first char in string allowed is "A" and the first in the users input "B" is, it gives me an error...
Totally confused right now
string allowed = "abc";
string read= Console.ReadLine();
for (int i = 0; i < allowed.Length; i++ )
{
if (allowed[i] == read[i])
{
Console.WriteLine("Okay");
}
else
{
Console.WriteLine("Invalid char on" +index);
}
}
If you wanna check if the user input has any of not allowed characters you need a nested loop, because you wanna compare each char in the user input against the chars in the allowed:
foreach(var r in read)
{
bool isValid = false;
foreach(var c in allowed)
{
// if we found a valid char set isValid to true
if(c == r)
isValid = true;
}
// if it's still false then the current char
// doesn't match any of the allowed chars
// so it's invalid
if(!isValid)
{
Console.WriteLine("the string has invalid char(s)");
break;
}
}
Or, to simplify this you can use LINQ:
bool isInvalid = read.Any(c => !allowed.Contains(c));
If you want to know which chars are invalid, you can use Except method:
var invalidChars = read.Except(allowed);
foreach(var c in invalidChars)
{
Console.WriteLine(c);
}
You either need to search the char of user input within the allowed characters or you could use a regular expression.
Search approach:
private string allowed = "abc";
private string userInput = "some string entered";
bool stringIsValid = false;
for (int i = 0; i < userInput.Length; i++)
{
if (!allowed.IndexOf(userInput[i]))
{
stringIsValid = false;
break; // You can stop the loop upon the first occurance of an invalid char
}
}
Regular expression approach:
private string allowed = "abc";
private string userInput = "some string entered";
bool stringIsValid = Regex.IsMatch(allowed, userInput);
Please note that the regular expression approach is more flexible. If you learn about regular expressions, you will find it very powerful.
You need another loop in your first one:
string allowed = "abc";
string read= Console.ReadLine();
for (int i = 0; i < read.Length; i++ )
{
bool isValid = false;
for (int j = 0; j < allowed.Length; j++)
{
if (read[i] == allowed[j])
{
isValid = true;
break;
}
}
if (isValid)
{
Console.WriteLine("Okay");
}else{
Console.WriteLine("Invalid char on" +index);
}
}
Right now, what you're saying is "every character in read must be exactly the same as in allowed".
What you're trying to say (I think) is "every character in read must be present somewhere in allowed" – that's what the second loop does. It looks for the character in allowed and if it finds it, sets isValid to true. Otherwise the character wasn't found and it's incorrect.
As other answers here state, you can use LINQ or (preferrably) regular expressions (regex) for things like this. I assume this is homework, or you're new to C# or programming, so I provided a basic answer to (hopefully) help you understand what's not working currently with your code.
If this should indeed be a homerwok or studying-related question, then let me recommend you put that in your question next time, for it's not forbidden to ask about homework.
The "real world" solutions we would use are of no help to you if you're trying to figure out the basics, so if we know it's about learning stuff then we'll provide answers that are more useful for you.
When using a collection to store not allowed items (instead of a plain string) it opens a whole new spectrum of LINQ expressions you can use:
public static void Main(string[] args)
{
var allowed = new List<string> { "a", "b", "c" };
var read = Console.ReadLine().Select(c => c.ToString()).ToList();
if (read.All(allowed.Contains))
{
Console.WriteLine("Okay");
}
else
{
var firstNotAllowed = read.First(a => !allowed.Contains(a));
var firstIndex = read.FindIndex(a => !allowed.Contains(a));
Console.WriteLine("Invalid char: {0}, at index: {1}", firstNotAllowed, firstIndex);
}
}

Hangman loop, letter issue C#

Hej, does anyone know how i can make my method for guessing letters remember
the guesses before? Currently it turns all the "right" guesses into the last right letter guessed :(
public void myGuess(char letter)
{
string wordToGuess = label4.Text;
string wordToShow = label5.Text;
for (int i = 0; i < wordToGuess.Length; i++)
{
if (wordToGuess[i] == letter || wordToGuess[i] == wordToShow[i])
wordToShow = wordToShow.Remove(i, 1).Insert(i, Char.ToString(letter));
}
label5.Text = wordToShow;
if (wordToGuess == wordToShow)
this.Close();
Form Win = new Win();
Win.Show();
}
Have a global list of char and add to it after every guess
List<char> guesses = new List<char>();
Then in your method just add to it
guesses.Add(letter);
and then you can check using this
if (guesses.Contains(letter))
{
//DoSomething
}
The problem is with your loop.
Your test checks if the character equals either the guessed letter or the letter at the same position in the already guessed words.
If the test succeeds you then effectively replace the value with the guessed letter.
You need to either remove the second part of the test:
if (wordToGuess[i] == letter)
wordToShow = wordToShow.Remove(i, 1).Insert(i, Char.ToString(letter));
or change the replacement
if (wordToGuess[i] == letter || wordToGuess[i] == wordToShow[i])
wordToShow = wordToShow.Remove(i, 1).Insert(i, Char.ToString(wordToShow[i]));
Also instead of removing/inserting if you change the wordToShow to a char array first you can then just directly change it's value and convert back to a string when finished, this makes the code a little easier to read. It may also be better performance than all the insert/removes.
var newWord = wordToShow.ToCharArray();
for (var i = 0; i<wordToGuess.Length; i++) {
if (wordToGuess[i] == letter) {
newWord[i] = letter;
}
}
wordToGuess = new string(newWord);

Categories

Resources