Hangman string char StringBuilder - c#

I'm doing a raw hangman text game as a speedy coding exercise. Right now, my blanks should turn into correctly-chosen letters, while leaving underscores as placeholders for other letters, but instead, all of my blanks are being converted to the currently-selected, correct letter. Eg, if my word (wordDisp) is 'bag' and I guess 'a', all my blanks turn to 'a'; instead of the desired output of '_ a _'.
using System;
using System.Text;
class Hang {
public static string blanks = "_";
public static string hangParts = "_____,|___|, | , O , /|\\ , / \\ ";
public static int tries = 0;
public static void PrintHang(){
Console.WriteLine("What's your word? ");
string wordDisp = Console.ReadLine();
char[] charDisp = wordDisp.ToCharArray();
string stringDisp = charDisp.ToString();
StringBuilder temp = new StringBuilder();
for(var i=0; i<charDisp.Length;i++){
temp.Append(blanks);
}
Console.WriteLine(charDisp.Length);
Console.WriteLine(temp);
while(tries < 6){
for(var s=0;tries<6; s++){
Console.WriteLine("What's your letter? ");
var pick = Console.ReadLine();
if(charDisp[s].ToString().Contains(pick)){
temp.Replace(temp[s],Convert.ToChar(pick));
Console.WriteLine(temp);
}
else{
tries++;
Console.WriteLine(hangParts[s]);
}
}
}
}
}
class Prog{
public static void Main(){
Hang.PrintHang();
}
}

The problem is temp.Replace(temp[s],Convert.ToChar(pick)) you're replacing underscore with 1st char of string from Console.ReadLine() but temp starts with all chars as underscore. So every letter is set to 1st letter of your input.
You'll need to map the index position of each letter in the target string in some way. You'll also need to account for the input string having multiple letters that are the same "hello" for example with 2 'l'.
Then you can replace your underscore with the correct letter based on the index in the target string.
Couple other things...
You probably don't need the char array. You can do the contains directly with the string wordDisp.Contains(pick).
Also you've got an error in how you're using "tries". You should take out the "for" loop and just exit after a certain number of "misses". You also need to account for the user having successfully matched all the letters - print something to let them know they matched it and then exit.

Related

Regex Replace - Based on Char Input

Lets say we have string:
Hello
The user enters a char input "e"
What is the correct way of returning the string as the following using a regex method:
-e---
Code tried:
public static string updatedWord(char guess, string word)
{
string result = Regex.Replace(word, guess, "-");
console.writeline(result);
return result;
}
Assuming the input were e, you could build the following regex pattern:
[^e]
Then, do a global replacement on this pattern, which matches any single character which is not e, and replace it with a single dash.
string word = "Hello";
char guess = 'e';
string regex = "[^" + guess + "]";
string result = Regex.Replace(word, regex, "-");
Console.WriteLine(result);
This prints:
-e---
Note that to ensure that we handle regex metacharacters correctly, should they be allowed as inputs, we can wrap the regex pattern above in Regex.Escape:
Regex.Escape(regex)
This can be done without Regex, you need to "loop" all characters of the secret word and replace not yet guessed characters with -, regex will loop letters also, but c# methods are more comprehensible ;)
You need to keep collection of already guessed letters.
public class Guess
{
private readonly string _word;
private readonly HashSet<char> _guessed;
public Guess(string word)
{
_word = word;
_guessed = new HashSet<char>();
}
public string Try(char letter)
{
_guessed.Add(letter);
var maskedLetters = _word.Select(c => _guessed.Contains(c) ? c : '-').ToArray();
return new string(maskedLetters);
}
}
Usage
var game = new Guess("Hello");
var result = game.Try('e');
Console.WriteLine(result); // "-e---"

Compare if input string is in correct format

Check if input string entered by user is in format like IIPIII, where I is integer number, any one digit number can be used on place of I and P is a character.
Example if input is 32P125 it is valid string else N23P33 is invalid.
I tried using string.Length or string.IndexOf("P") but how to validate other integer values?
I'm sure someone can offer a more succinct answer but pattern matching is the way to go.
using System.Text.RegularExpressions;
string test = "32P125";
// 2 integers followed by any upper cased letter, followed by 3 integers.
Regex regex = new Regex(#"\d{2}[A-Z]\d{3}", RegexOptions.ECMAScript);
Match match = regex.Match(test);
if (match.Success)
{
//// Valid string
}
else
{
//// Invalid string
}
Considering that 'P' has to matched literally -
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
string st1 = "32P125";
string st2 = "N23P33";
Regex rg = new Regex(#"\d{2}P\d{3}");
// If 'P' is not to be matched literally, reeplace above line with below one
// Regex rg = new Regex(#"\d{2}[A-Za-z]\d{3}");
Console.WriteLine(rg.IsMatch(st1));
Console.WriteLine(rg.IsMatch(st2));
}
}
OUTPUT
True
False
It can be encapsulated in one simple if:
string testString = "12P123";
if(
// check if third number is letter
Char.IsLetter(testString[2]) &&
// if above succeeds, code proceeds to second condition (short-circuiting)
// remove third character, after that it should be a valid number (only digits)
int.TryParse(testString.Remove(2, 1), out int i)
) {...}
I would encourage the usage of MaskedTextProvided over Regex.
Not only is this looking cleaner but it's also less error prone.
Sample code would look like the following:
string Num = "12P123";
MaskedTextProvider prov = new MaskedTextProvider("##P###");
prov.Set(Num);
var isValid = prov.MaskFull;
if(isValid){
string result = prov.ToDisplayString();
Console.WriteLine(result);
}
Use simple Regular expression for this kind of stuff.

How to change the position of letters in a word in an input string

i am trying to make a program wherein i want to swap/change the position of the input word:
for example:
input word: overflows
output: vorelfwos
I want to swap out the 1st and 2nd letters the 3rd and 4th and so on, and if it has a letter at the end that has no partner for swapping, i want to just leave it as is. Can somebody help me? i will be using c# btw.
C# strings have an indexer, called System.String.Chars:
char letter = "Hello World"[0];
letter is now H.
But strings are immutable, so you can't change a character like this:
string myString = "Hello";
myString[1] = 'R'; // Produces an error.
So I would suggest using a System.Text.StringBuilder, so here is an extension method using it:
public static string Swap(this string str, int charToSwapA, int charToSwapB)
{
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(str);
oldCharToSwapA = stringBuilder[charToSwapA];
stringBuilder[charToSwapA] = stringBuilder[charToSwapB];
stringBuilder[charToSwapB] = stringBuilder[oldCharToSwapA];
return stringBuilder.ToString();
}
Which can be used like this:
string myString = "dello WorlH".Swap(0, 10);
myString is now "Hello World".

Reverse the words in a string without using builtin functions like split and substring in C#

I wrote the following:
class Program
{
public static void Main(string[] args)
{
string name = "Hello World"
StringBuilder builder = new StringBuilder();
for (int i = name.Length - 1; i >= 0; i--)
{
builder.Append(name[i]);
}
string newName =builder.ToString();
Console.WriteLine(newName);
}
}
I am getting "dlrow olleh " as output. I want "World Hello ".
I wrote a quick solution which uses 2 loops.
The first loop is iterating over the string from left to right.
When it encounters a whitespace (which delimits a word) the second loop starts.
The second loop is looping through the word from right to left. The second loop can not go from left to right because we have no idea where the word will begin (unless we would remember where we met the previous whitespace). Hence the second loop iterates from right to left through the string until it encounters a whitespace (beginning of the word), or until the index becomes 0 (which is a corner case). The same corner case can be observed in the first loop.
Here it is :
String name = "Hello World";
StringBuilder builder = new StringBuilder();
for (int i = 0; i < name.length(); i++)
{
char tmp = name.charAt(i);
if(tmp == ' ' || i == name.length() - 1) {
// Found a whitespace, what is preceding must be a word
builder.insert(0, ' ');
for(int j = i - 1; j >= 0; j--) {
if(i == name.length() - 1 && j == i - 1) {
// Exceptional case (necessary because after the last word there is no whitespace)
builder.insert(0, name.charAt(i));
}
char curr = name.charAt(j);
if(curr == ' ') {
// We passed the beginning of the word
break;
}
else if(j == 0) {
builder.insert(0, curr);
break;
}
//builder.append(curr);
builder.insert(0, curr);
}
}
}
String newName = builder.toString();
System.out.println(newName);
Note that this is java code but it should be straightforward to translate it to C#.
In pseudo code, a solution to this problem might look something like this:
result = empty string
last = input.length
for i from input.length - 1 to 0
if input(i) is whitespace or i is 0
for j from i to last - 1
append input[j] to result
last = i
This starts at the back of the string, and loops backwards. When it finds a whitespace (or gets to the beginning of the stirng), it knows it has found a complete word and adds it to the list. The variable last keeps track of where the last word that got added started.
You will have to tweak this a bit to get the spaces between the words right.
Just because you can't use "built-in" functions, doesn't mean you can't write your own. What you should be learning from a task like this is how to break a problem down into a set of sub problems.
string Reverse(string pInput) {
// iterate backwards over the input, assemble a new string, and return it
}
List<string> Split(string pInput, char pSplitOn) {
// iterate forwards over the input
// build up a new string until the split char is found
// when it is found, add the current string to a list
// and start the building string over at empty
// maybe only add strings to the list if they aren't empty
// (although that wouldn't preserve extra whitespace, which you may want)
// make sure to add the end of the string since it probably
// doesn't end with the split char
// return the list
}
string Join(List<string> pWords, char pSeparator) {
// build up a new string consisting of each of the words separated by the separator
}
string ReverseWords(string pInput) {
// split the input on a space
// for each "word" in the resulting list, reverse it
// join the reversed words back together into one string, with spaces separating them
}
This assumes that the only whitespace that you will encounter consists of spaces. Also assumes you're allowed to use List<T>.

Display string as underscores but keeping value

I am writing a basic hangman game. I want to store a string Word as the word to be found by the user. My problem is that I want to display this word as underscores (like you do in a paper hangman) but keep it's string value. A friend of mine said this could be done via Regex but researching it doesn't seem to help me.
As suggested by Farinha, but using HashSet
Some importang points: we are using a HashSet because in the end the letters can have two states: discovered (present in the HS) or not discovered (not present). We initialize the HashSet passing as a parameter StringComparer.InvariantCultureIgnoreCase because we consider L and l to be the same thing. To use the StringComparer.InvariantCultureIgnoreCase we have to use an HashSet<string> (an HS of strings) instead of an HashSet<char> (an HS of chars). So when using the discovered.Contains() we have to convert the c char in a string with a ToString
static string ConvertWord(string word, HashSet<string> discovered)
{
StringBuilder sb = new StringBuilder(word.Length);
foreach (char c in word)
{
if (discovered.Contains(c.ToString()))
{
sb.Append(c);
}
else
{
sb.Append('_');
}
}
return sb.ToString();
}
HashSet<string> discovered = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
// The "secret" word
string word = "Hello world";
// How to add letters to the ones discovered
discovered.Add("l");
// The word ready to be shown
string convertWord = ConvertWord(word, discovered);
We could have done the ConvertWord in much less characters, but for today it's enough :-)
Ok... I'll give you an example, in C# 4.0:
static string ConvertWord(string word, HashSet<string> discovered)
{
return string.Concat(word.Select(p => discovered.Contains(p.ToString()) ? p : '_'));
}
I am not sure if this is exactly what you want, but you can take a look if it is helpful.
The game was simulated by sed and echo. of course, you can use variable for the secret word, not echo as plain text.
say, my secret word is "memory"
#user gives a letter "a"
kent$ echo "memory"|sed 's/[^a]/_/g'
______
#user gives a letter "r"
kent$ echo "memory"|sed 's/[^ar]/_/g'
____r_
#user gives a letter "m"
kent$ echo "memory"|sed 's/[^arm]/_/g'
m_m_r_
#user gives a letter "o"
kent$ echo "memory"|sed 's/[^armo]/_/g'
m_mor_
#user gives a letter "x"
kent$ echo "memory"|sed 's/[^armox]/_/g'
m_mor_
#user gives a letter "y" then "e"
kent$ echo "memory"|sed 's/[^armoxy]/_/g'
m_mory
kent$ echo "memory"|sed 's/[^armoxye]/_/g'
memory
Using a Regex doesn't seem like a good approach. I would instead grab the word, break it down into characters, and build a Dictionary<char,bool> where the bool indicates if the letter has been discovered yet. Then you an look through the Dictionary and for each item display the char if the letter has been discovered, or an underscore if not.
// build the Dictionary
string originalWord = "Stack Overflow";
Dictionary<char, bool> dict = new Dictionary<char, bool>();
for(int i = 0; i < originalWord.Length; i++)
dict.Add(originalWord[i], false);
// output the current state
string current = "";
foreach(KeyValuePair<char, bool> pair in dict)
{
current += pair.Value ? pair.Key.ToString() : "_";
}
Why not using a textbox and having its entry type as password, then set his password character as 'underscore'
I wouldn't bother with regex - you can write a fairly simple function to take an array of characters which represent the guesses made and return the representation of the word as required:
string word = "AMBULANCE";
public string Hangman(char[] guesses)
{
char[] output = new char[word.Length];
for (int i = 0; i < word.Length; i++)
{
if (guesses.Contains(word[i]))
{
output[i] = word[i];
}
}
return new String(output).Replace('\0','_');
}
and then call it using something like:
char[] guesses = new char[] { 'A', 'E' };
Console.WriteLine(Hangman(guesses));
Console.ReadLine();

Categories

Resources