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();
Related
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.
Have an assignment to allow a user to input a word in C# and then display that word with the first and third characters changed to uppercase. Code follows:
namespace Capitalizer
{
class Program
{
static void Main(string[] args)
{
string text = Console.ReadLine();
char[] delimiterChars = { ' ' };
string[] words = text.Split(delimiterChars);
string Upper = text.ToUpper();
Console.WriteLine(Upper);
Console.ReadKey();
}
}
}
This of course generates the entire word in uppercase, which is not what I want. I can't seem to make text.ToUpper(0,2) work, and even then that'd capitalize the first three letters. Only solution I can think of now that would make the word appear on one line (and I don't know if it works) is to move the capitalized letters and lowercase letters into a character array and try to get that to print all values in a modified order.
The simplest way I can think of to address your exact question as described — to convert to upper case the first and third characters of the input — would be something like the following:
StringBuilder sb = new StringBuilder(text);
sb[0] = char.ToUpper(sb[0]);
sb[2] = char.ToUpper(sb[2]);
text = sb.ToString();
The StringBuilder class is essentially a mutable string object, so when doing these kinds of operations is the most fluid way to approach the problem, as it provides the most straightforward conversions to and from, as well as the full range of string operations. Changing individual characters is easy in many data structures, but insertions, deletions, appending, formatting, etc. all also come with StringBuilder, so it's a good habit to use that versus other approaches.
But frankly, it's hard to see how that's a useful operation. I can't help but wonder if you have stated the requirements incorrectly and there's something more to this question than is seen here.
You could use LINQ:
var upperCaseIndices = new[] { 0, 2 };
var message = "hello";
var newMessage = new string(message.Select((c, i) =>
upperCaseIndices.Contains(i) ? Char.ToUpper(c) : c).ToArray());
Here is how it works. message.Select (inline LINQ query) selects characters from message one by one and passes into selector function:
upperCaseIndices.Contains(i) ? Char.ToUpper(c) : c
written as C# ?: shorthand syntax for if. It reads as "If index is present in the array, then select upper case character. Otherwise select character as is."
(c, i) => condition
is a lambda expression. See also:
Understand Lambda Expressions in 3 minutes
The rest is very simple - represent result as array of characters (.ToArray()), and create a new string based off that (new string(...)).
Only solution I can think of now that would make the word appear on one line (and I don't know if it works) is to move the capitalized letters and lowercase letters into a character array and try to get that to print all values in a modified order.
That seems a lot more complicated than necessary. Once you have a character array, you can simply change the elements of that character array. In a separate function, it would look something like
string MakeFirstAndThirdCharacterUppercase(string word) {
var chars = word.ToCharArray();
chars[0] = chars[0].ToUpper();
chars[2] = chars[2].ToUpper();
return new string(chars);
}
My simple solution:
string text = Console.ReadLine();
char[] delimiterChars = { ' ' };
string[] words = text.Split(delimiterChars);
foreach (string s in words)
{
char[] chars = s.ToCharArray();
chars[0] = char.ToUpper(chars[0]);
if (chars.Length > 2)
{
chars[2] = char.ToUpper(chars[2]);
}
Console.Write(new string(chars));
Console.Write(' ');
}
Console.ReadKey();
I need to convert a byte array of a text file to it's string character representation.
For example, if I have a text file that has:
hello (tab) there (newline) friend
I would like to convert that to an array:
my_array = {'h', 'e' ,'l','l','o', '\t', 't', 'h','e','r','e', '\r','\n', 'f', 'r' ,'i','e','n', 'd'};
I'm having trouble with converting the control characters to their escaped strings, i.e.:
0x09 = '\t';
0x0D = '\r';
0x0A = '\n';
I have tried this, but the tabs and new lines aren't represented here:
byte[] text_bytes = File.ReadAllBytes("ok.txt");
char[] y = Encoding.ASCII.GetChars(text_bytes);
I know I can just loop through each byte and have a condition to look for 0x09 and if I find it, then replace with "\t", but I'm wondering if there is something built in.
There are several ways you could do it. The simplest would be to load the entire file into memory:
string theText = File.ReadAllText(filename);
Then use string.Replace to replace the items you're interested in:
// "escaping" the '\t' with '\\t' makes it write the literal characters '\' and 't'
theText = theText.Replace("\t", "\\t");
theText = theText.Replace("\r", "\\r");
theText = theText.Replace("\n", "\\n");
Then you can create your array of characters. If you're sure that it's all ASCII text, you can use Encoding.ASCII:
byte[] theChars = Encoding.ASCII.GetBytes(theText);
Or, if you want a character array:
char[] theChars = theText.ToCharArray();
That's probably going to be fast enough for your purposes. You might be able to speed it up by making a single pass through the string, reading character by character and copying to a StringBuilder:
StringBuilder sb = new StringBuilder(theText.Length);
foreach (char c in theText)
{
switch (c)
{
case '\t' : sb.Append("\\t"); break;
case '\r' : sb.Append("\\r"); break;
case '\n' : sb.Append("\\n"); break;
default : sb.Append(c); break;
}
}
byte[] theChars = Encoding.ASCII.GetBytes(sb.ToString());
If you want to escape all control characters then you can use Regex.Escape.
string myText = File.ReadAllLines("ok.txt");
//to optimize, you could remove characters that you know won't be there (e.g. \a)
Regex rx = new Regex(#"[\a\e\f\n\r\t\v]", RegexOptions.Compiled);
myText = rx.Replace(myText, m => { return Regex.Escape(m.Value); });
Console.WriteLine(myText);
You can't convert it to a char array in the way you've posted because an escaped control character would count as two characters (\ and t). But if you don't mind each character being separate, you can simply do
char[] myCharArray = myText.ToCharArray();
In the "y" array, the "escaped characters" will have their actual values (0x09, 0x0D, etc.) with an unprintable character as the "text".
When you write \t, \n, \r, etc. you could have written (char)0x09, (char)0x0D and this is what the data gets written as. In other words the "\t" character doesn't exist!
Whether you roll your own, or use an existing library, someone is going to have to map 0x09 to the "\t" escape sequence and inject it into your string.
If you don't mind it being somewhat slower than a hand-rolled solution, then you could use a CodeDomProvider (which would probably be fast enough).
I found sample code here: http://code.google.com/p/nbehave-cf/source/browse/trunk/CustomTool/StringExtensions.cs?spec=svn5&r=5
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
namespace CustomTool
{
public static class StringExtensions
{
public static String ToLiteral(this String input)
{
using (var writer = new StringWriter())
{
using (var provider = CodeDomProvider.CreateProvider("CSharp"))
{
provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
return writer.ToString();
}
}
}
}
}
You would use it by reading the string using Encoding.Ascii.ReadString(), and then use .ToLiteral() to convert it to a string, then .ToCharArray() to get the final result.
This gives the correct result with, for example:
// You would do (using your sample code):
// string test = Encoding.ASCII.GetString(text_bytes);
string test = "hello\tthere\nfriend";
char[] result = test.ToLiteral().ToCharArray();
If you inspect result you will see that it has the correct characters.
However, I'd just use a loop and a switch statement to convert the characters. It's easy to write and understand, and it'd be much more efficient.
Is it possible to pass over a string, finding the white spaces?
For example a data set of:
string myString = "aa bbb cccc dd";
How could I loop through and detect each white space, and manipulate that space?
I need to do this in the most effecient way possible.
Thanks.
UPDATE:
I need to manipulate the space by increasing the white space from an integer value. So for instance increase the space to have 3 white spaces instead of one. I'd like to make it go through each white space in one loop, any method of doing this already in .NET? By white space I mean a ' '.
You can use the Regex.Replace method. This will replace any group of white space character with a dash:
myString = Regex.Replace(myString, "(\s+)", m => "-");
Update:
This will find groups of space characters and replace with the tripple amount of spaces:
myString = Regex.Replace(
myString,
"( +)",
m => new String(' ', m.Groups[1].Value.Length * 3)
);
However, that's a bit too simple to make use of regular expressions. You can do the same with a regular replace:
myString = myString.Replace(" ", " ");
This will replace each space intead of replace groups of spaces, but the regular replace is much simpler than Regex.Replace, so it should still be at least as fast, and the code is simpler.
If you want to replace all whitespace in one swoop, you can do:
// changes all strings to dashes
myString.Replace(' ', '-');
If you want to go case by case (that is, not just a mass replace), you can loop through IndexOf():
int pos = myString.IndexOf(' ');
while (pos >= 0)
{
// do whatever you want with myString # pos
// find next
pos = myString.IndexOf(' ', pos + 1);
}
UPDATE
As per your update, you could replace single spaces with the number of spaces specified by a variable (such as numSpaces) as follows:
myString.Replace(" ", new String(' ', numSpaces));
If you just want to replace all spaces with some other character:
myString = myString.Replace(' ', 'x');
If you need the possibility of doing something different to each:
foreach(char c in myString)
{
if (c == ' ')
{
// do something
}
}
Edit:
Per your comment clarifying your question:
To change each space to three spaces, you can do this:
myString = myString.Replace(" ", " ");
However note that this doesn't take into account instances where your input string already has two or more spaces. If that is a possibility you will want to use a regex.
Depending on what you're tring to do:
for(int k = 0; k < myString.Length; k++)
{
if(myString[k].IsWhiteSpace())
{
// do something with it
}
}
The above is a single pass through the string, so it's O(n). You can't really get more efficient that that.
However, if you want to manipulate the original string your best bet is to Use a StringBuilder to process the changes:
StringBuilder sb = new StringBuilder(myString);
for(int k = 0; k < myString.Length; k++)
{
if(myString[k].IsWhiteSpace())
{
// do something with sb
}
}
Finally, don't forget about Regular Expressions. It may not always be the most efficient method in terms of code run-time complexity but as far as efficiency of coding it may be a good trade-off.
For instance, here's a way to match all white spaces:
var rex = new System.Text.RegularExpressions.Regex("[^\\s](\\s+)[^\\s]");
var m = rex.Match(myString);
while(m.Success)
{
// process the match here..
m.NextMatch();
}
And here's a way to replace all white spaces with an arbitrary string:
var rex = new System.Text.RegularExpressions.Regex("\\s+");
String replacement = "[white_space]";
// replaces all occurrences of white space with the string [white_space]
String result = rex.Replace(myString, replacement);
Use string.Replace().
string newString = myString.Replace(" ", " ");
LINQ query below returns a set of anonymous type items with two properties - "sybmol" represents a white space character, and "index" - index in the input sequence. After that you have all whitespace characters and a position in the input sequence, now you can do what you want with this.
string myString = "aa bbb cccc dd";
var res = myString.Select((c, i) => new { symbol = c, index = i })
.Where(c => Char.IsWhiteSpace(c.symbol));
EDIT: For educational purposes below is implementation you are looking for, but obviously in real system use built in string constructor and String.Replace() as shown in other answers
string myString = "aa bbb cccc dd";
var result = this.GetCharacters(myString, 5);
string output = new string(result.ToArray());
public IEnumerable<char> GetCharacters(string input, int coeff)
{
foreach (char c in input)
{
if (Char.IsWhiteSpace(c))
{
int counter = coeff;
while (counter-- > 0)
{
yield return c;
}
}
else
{
yield return c;
}
}
}
var result = new StringBuilder();
foreach(Char c in myString)
{
if (Char.IsWhiteSpace(c))
{
// you can do what you wish here. strings are immutable, so you can only make a copy with the results you want... hence the "result" var.
result.Append('_'); // for example, replace space with _
}
else result.Append(c);
}
myString = result.ToString();
If you want to replace the white space with, e.g. '_', you can using String.Replace.
Example:
string myString = "aa bbb cccc dd";
string newString = myString.Replace(" ", "_"); // gives aa_bbb_cccc_dd
In case you want to left/right justify your string
int N=10;
string newstring = String.Join(
"",
myString.Split(' ').Select(s=>s.PadRight(N-s.Length)));
I'm implementing a c# program that should automatize a Mono-alphabetic substitution cipher.
The functionality i'm working on at the moment is the simplest one: The user will provide a plain text and a cipher alphabet, for example:
Plain text(input): THIS IS A TEST
Cipher alphabet: A -> Y, H -> Z, I -> K, S -> L, E -> J, T -> Q
Cipher Text(output): QZKL KL QJLQ
I thought of using regular expressions since I've been programming in perl for a while, but I'm encountering some problems on c#.
First I would like to know if someone would have a suggestion for a regular expression that would replace all occurrence of each letter by its corresponding cipher letter (provided by user) at once and without overwriting anything.
Example:
In this case, user provides plaintext "TEST", and on his cipher alphabet, he wishes to have all his T's replaced with E's, E's replaced with Y and S replaced with J. My first thought was to substitute each occurrence of a letter with an individual character and then replace that character by the cipherletter corresponding to the plaintext letter provided.
Using the same example word "TEST", the steps taken by the program to provide an answer would be:
replace T's with (lets say) #
replace E's with #
replace S's with &
Replace # with E, # with Y, & with j
Output = EYJE
This solution doesn't seem to work for large texts.
I would like to know if anyone can think of a single regular expression that would allow me to replace each letter in a given text by its corresponding letter in a 26-letter cipher alphabet without the need of splitting the task in an intermediate step as I mentioned.
If it helps visualize the process, this is a print screen of my GUI for the program: alt text http://img43.imageshack.us/img43/2118/11618743.jpg
You could also make a map of source to destination characters, then simply loop through the string and replace as you go:
Dictionary<char, char> replacements = new Dictionary<char, char>();
// set up replacement chars, like this
replacements['T'] = '#';
replacements['E'] = '#';
replacements['S'] = '&';
replacements['#'] = 'E';
replacements['#'] = 'Y';
replacements['&'] = 'J';
// actually perform the replacements
char[] result = new char[source.Length];
for (int i = 0; i < result.Length; i++) {
result[i] = replacements[source[i]];
}
return new string(result);
I don't think regular expressions are the right tool here. In Perl you would use the transliteration feature tr/TES/EYJ/. C# doesn't have this but you can do it by using a StringBuilder and looking at each character individually.
private static string Translate(string input, string from, string to)
{
StringBuilder sb = new StringBuilder();
foreach (char ch in input)
{
int i = from.IndexOf(ch);
if (i < 0)
{
sb.Append(ch);
}
else if (i < to.Length)
{
sb.Append(to[i]);
}
}
return sb.ToString();
}
The source code is a modified version of this answer from this similar question. The answers there show some other ways of doing this.