How to pick a random string from string array only once - c#

I'm trying to make Hangman in C#, and in the beginning of a game you will need a word to guess, and so the game wont be boring you can get many words (only one at a time). But when you start a new game you wont get a word you've already guessed. So i have to choose a random string that i havent chosen already.
I've tried multiple methods to solve this, but none have succeded.
Method #1:
Here I run the NewWord-function, and then add 1 to numberOfTries.
string[] wordArr = { "PROGRAMMERING", "CSHARP", "STOL", "ELEV", "VISUAL", "STUDIO" };
int numberOfTries = 0;
int randomNumber = -1;
protected string NewWord()
{
if (!(numberOfTries >= wordArr.Length))
{
randomNumber = RandomNumberFromTo(0, (wordArr.Length - numberOfTries));
ChangeWord(((wordArr.Length - numberOfTries)-1), randomNumber);
return wordArr[(randomNumberl)];
}
else
{
return "There are no more new words!! :(";
}
}
private int RandomNumberFromTo(int NumberA, int NumberB)
{
System.Threading.Thread.Sleep(2);
Random minRandomGenerator = new Random();
System.Threading.Thread.Sleep(3);
return minRandomGenerator.Next(NumberA, NumberB);
}
protected void ChangeWord (int NumberA, int NumberB)
{
string cashe1 = wordArr[NumberA];
wordArr[NumberA] = wordArr[NumberB];
wordArr[NumberB] = cashe1;
return;
}
Method #2 I've found here on StackOverflow but it didn't work.
Here I also run the NewWord-function, and then add 1 to numberOfTries.
string[] wordArr = { "PROGRAMMERING", "CSHARP", "STOL", "ELEV", "VISUAL", "STUDIO" };
int numberOfTries = 0;
Random random = new Random();
protected string NyttOrd()
{
if (!(numberOfTries >= wordArr.Length))
{
var names = new List<string> { "PROGRAMMERING", "CSHARP", "STOL", "ELEV", "VISUAL", "STUDIO" };
System.Threading.Thread.Sleep(3);
int index = random.Next(names.Count);
var name = names[index];
names.RemoveAt(index);
return name;
}
else
{
return "There are no more new words!! :(";
}
}
I have also tried a version where I had two different arrays, one an Int-Array and the second a String-Array. It was really messy and did not work.
I am also very new to C# i only know of the basics, like +-/*, convert, functions, and arrays.

Conceptually, you either keep track of strings you already used, or you remove strings from the list of optional strings as you use them.
To implement the first method, you can keep a hashtable of strings you've already used, and when pulling a new string - see if it is present in the hashtable (and if so, pick another one, until a "fresh" string is picked).
To implement the second method, just remove the strings you picked from the list as you pick them.

If you shuffle your word array:
var r = new Random();
var shuffledWords = wordArr.OrderBy(_ => r.Next());
then push your words into a Stack:
var wordStack = new Stack<string>(shuffledWords);
now you have a structure that will hand you random word from the collection while simultaneously removing it from the collection by using the Pop method of Stack<T> (considerably more efficiently than removing items from the front/middle of a List<T>, although your collection is so small, it hardly matters).
var someWord = wordStack.Pop();

static readonly List<string> Words = new List<string>(wordArr);
static readonly Random Rnd = new Random();
public string Next()
{
if(Words.Count < 1)
return "There are no more new words!! :(";
var index = Rnd.Next(0, Words.Length);
var result = Words[index];
Words.RemoveAt(index);
return result;
}

Use KeyValuePair.
The key will be your word, the value will be how many times it's been used, then just take the least used word and increment its counter.
List<KeyValuePair<string, int>>

You should represent your WordArr as a list. It's easier to work with to suit your needs.
Here's an easy way to randomize your list:
List<string> wordArr = new List<string>()
{
"PROGRAMMERING", "CSHARP", "STOL", "ELEV", "VISUAL", "STUDIO"
};
Random random = new Random();
wordArr = wordArr.OrderBy(x => random.Next()).ToList();
Then just always take the first word in the list so that you can simply remove each word that you use like this:
wordArr.RemoveAt(0);
When the wordArr is empty then you are done.

Since you've said you're new to programming, I'll explain the key line quickly:
.Except() compares your array to the other array and returns only unique elements.
.OrderBy(x => rn.Next()) will return the array in a random order.
.FirstOrDefault() will get the first entry from the array or, if the array is empty, return null.
public void GenerateWord()
{
Random rn = new Random();
string[] attemptedWords = LoadUsedWords();
string[] wordArr = { "PROGRAMMERING", "CSHARP", "STOL", "ELEV", "VISUAL", "STUDIO" };
string word = wordArr.Except(attemptedWords).OrderBy(x => rn.Next()).FirstOrDefault();
if (string.IsNullOrEmpty(word))
{
Console.WriteLine("Oh no");
}
else
{
Console.WriteLine(word);
}
}
public string[] LoadUsedWords()
{
return new string[] { "PROGRAMMERING", "CSHARP", "STOL", "ELEV", "VISUAL" };
}

Related

How can I split a string to store contents in two different arrays in c#?

The string I want to split is an array of strings.
the array contains strings like:
G1,Active
G2,Inactive
G3,Inactive
.
.
G24,Active
Now I want to store the G's in an array, and Active or Inactive in a different array. So far I have tried this which has successfully store all the G's part but I have lost the other part. I used Split fucntion but did not work so I have tried this.
int i = 0;
for(i = 0; i <= grids.Length; i++)
{
string temp = grids[i];
temp = temp.Replace(",", " ");
if (temp.Contains(' '))
{
int index = temp.IndexOf(' ');
grids[i] = temp.Substring(0, index);
}
//System.Console.WriteLine(temp);
}
Please help me how to achieve this goal. I am new to C#.
If I understand the problem correctly - we have an array of strings Eg:
arrayOfStrings[24] =
{
"G1,Active",
"G2,Inactive",
"G3,Active",
...
"G24,Active"
}
Now we want to split each item and store the g part in one array and the status into another.
Working with arrays the solution is to - traverse the arrayOfStrings.
Per each item in the arrayOfStrings we split it by ',' separator.
The Split operation will return another array of two elements the g part and the status - which will be stored respectively into distinct arrays (gArray and statusArray) for later retrieval. Those arrays will have a 1-to-1 relation.
Here is my implementation:
static string[] LoadArray()
{
return new string[]
{
"G1,Active",
"G2,Inactive",
"G3,Active",
"G4,Active",
"G5,Active",
"G6,Inactive",
"G7,Active",
"G8,Active",
"G9,Active",
"G10,Active",
"G11,Inactive",
"G12,Active",
"G13,Active",
"G14,Inactive",
"G15,Active",
"G16,Inactive",
"G17,Active",
"G18,Active",
"G19,Inactive",
"G20,Active",
"G21,Inactive",
"G22,Active",
"G23,Inactive",
"G24,Active"
};
}
static void Main(string[] args)
{
string[] myarrayOfStrings = LoadArray();
string[] gArray = new string[24];
string[] statusArray = new string[24];
int index = 0;
foreach (var item in myarrayOfStrings)
{
var arraySplit = item.Split(',');
gArray[index] = arraySplit[0];
statusArray[index] = arraySplit[1];
index++;
}
for (int i = 0; i < gArray.Length; i++)
{
Console.WriteLine("{0} has status : {1}", gArray[i] , statusArray[i]);
}
Console.ReadLine();
}
seems like you have a list of Gxx,Active my recomendation is first of all you split the string based on the space, which will give you the array previoulsy mentioned doing the next:
string text = "G1,Active G2,Inactive G3,Inactive G24,Active";
string[] splitedGItems = text.Split(" ");
So, now you have an array, and I strongly recommend you to use an object/Tuple/Dictionary depends of what suits you more in the entire scenario. for now i will use Dictionary as it seems to be key-value
Dictionary<string, string> GxListActiveInactive = new Dictionary<string, string>();
foreach(var singleGItems in splitedGItems)
{
string[] definition = singleGItems.Split(",");
GxListActiveInactive.Add(definition[0], definition[1]);
}
What im achiving in this code is create a collection which is key-value, now you have to search the G24 manually doing the next
string G24Value = GxListActiveInactive.FirstOrDefault(a => a.Key == "G24").Value;
just do it :
var splitedArray = YourStringArray.ToDictionary(x=>x.Split(',')[0],x=>x.Split(',')[1]);
var gArray = splitedArray.Keys;
var activeInactiveArray = splitedArray.Values;
I hope it will be useful
You can divide the string using Split; the first part should be the G's, while the second part will be "Active" or "Inactive".
int i;
string[] temp, activity = new string[grids.Length];
for(i = 0; i <= grids.Length; i++)
{
temp = grids[i].Split(',');
grids[i] = temp[0];
activity[i] = temp[1];
}

Random method return two values without duplicate

I created two methods that take random values from database table
This method will return an integer random value
private int GetDayValue()
{
Random rnd = new Random();
List<int> days = GetDays();
int r = rnd.Next(days.Count);
return days[r];
}
This method will return also an integer random value
private int GetPeriodValue(string grd_id)
{
Random rnd = new Random();
List<int> periods = GetPeriods(grd_id);
int r = rnd.Next(periods.Count);
return periods[r];
}
Here there is a for loop will take random value from two methods
for (int z = 0; z <= secPortion; z++)
{
//here the random value of day
day = GetDayValue();
//here the random value of period
period = GetPeriodValue(grd_id);
//this method will insert the value on database table
SetFirstShot(teacher, sub, sec, period, day);
}
My question:
I want to create method will return random value of day and period together for example random value (ex: 3,5 ... 4,6 .... 1,1) and not repeated during the loop for example 4,6 will generated once time then another random value but if it 4,6 the method will create again a random value.
You can use a hash function instead that has a good random distribution aspect to it. A hash function with return the same value for the same inputs each time, while the results will "seem" random, which might be all the "randomness" you need.
using System;
using System.IO;
using System.Security.Cryptography;
// ...
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
using (var hash = MD5.Create())
{
writer.Write(day);
writer.Write(period);
var data = md5Hash.ComputeHash(stream.ToArray());
var randomNumber = BitConverter.ToInt32(data);
}
The added benefit is that you don't have to store previously-computed values in any sort of database. Any two agents with the same hashing algorithm will generate the same hashes. The values may not be completely unique, but they should vary wildly from input to input while also being deterministic with no hidden state. Truly unique would require querying outside state and probably introduce race conditions if your system is distributed.
It appears that GetDays() and GetPersiodValue() returns two int lists, so I'm writing two mock methods for those.
static List<int> GetDays()
{
return new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
}
static List<int> GetPeriods()
{
return new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
}
First thing you need to do is move the new Random() out of the methods (so that it's outside the loop). Otherwise when you iterate so fast it will keep giving you the same number.
static int GetRandomDay(Random rnd)
{
var days = GetDays();
return days[rnd.Next(days.Count)];
}
static int GetRandomPeriod(Random rnd)
{
var periods = GetPeriods();
return periods[rnd.Next(periods.Count)];
}
Then I'll do something like this to see if the generated pair of random values were generated before. Add to a list (or DB in your case) if not.
static void Main(string[] args)
{
var RandomList = new List<Tuple<int, int>>();
Random rnd = new Random();
for (int i =0; i < 10; i++)
{
bool isUnique = false;
do
{
int day = GetRandomDay(rnd);
int period = GetRandomPeriod(rnd);
if(RandomList.Where(x => x.Item1 == day && x.Item2 == period).FirstOrDefault() == null)
{
RandomList.Add(new Tuple<int, int>(day, period));
isUnique = true;
}
} while (!isUnique);
}
Console.ReadLine();
}
I tried to use KeyValuePair and I solved, its works well !
Here the code that I used:
// I created a list contain two pairs..
List<KeyValuePair<int, int>> dictionary = new List<KeyValuePair<int, int>>();
for (int z = 0; z < secPortion; z++)
{
day = GetDayValue();
period = GetPeriodValue(grd_id);
//here I pass the value of day and period to the pairs
KeyValuePair<int, int> myItem = new KeyValuePair<int, int>(day, period);
//here I created a list which is the list of values for the random key, if it is exist or not.
List<int> values = (from kvp in dictionary where kvp.Key == day select kvp.Value).ToList();
// this is a bool variable its value by default is true, once there is a duplicated value it will be changed to false....
bool FirstChecker = true;
if (values.Contains(period))
{
FirstChecker = false;
z--;
}
else
{
dictionary.Add(myItem);
SetFirstShot(teacher, sub, sec, period, day);
}
}
I think this is what you need.
Although the example is a solution for a one-dimensional array, actually in your case the two list days and periods can be mapped to one sequence whose length is days.Count * periods.Count. Or you can do this for each list separately but some cases will be missed.

Can I represent a given word (String) as a number?

Suppose I have a list of words e.g.
var words = new [] {"bob", "alice", "john"};
Is there a way to represent each of those words as numbers so that one could use such numbers to sort the words.
One use-case which I think this can be used for is to use Counting Sort to sort a list of words. Again I am only interested in whether this is at all possible not that it may not be the most efficient way to sort a list of words.
Do note this is not about hash-codes or different sorting algorithms. I am curious to find out if a string can be represented as a number.
You can use a dictionary instead of an array.
public class Program
{
static void Main(string[] args)
{
IDictionary<int, string> words = new Dictionary<int, string>();
words.Add(0, "bob");
words.Add(1, "alice");
words.Add(2, "john");
foreach (KeyValuePair<int, string> word in words.OrderBy(w => w.Key))
{
Console.WriteLine(word.Value);
}
Console.ReadLine();
}
}
NOTE: It's better to work with collections in place of arrays is easier to use for most developers.
I don't understand the down votes but hey this is what I have come up with so far:
private int _alphabetLength = char.MaxValue - char.MinValue;
private BigInteger Convert(string data)
{
var value = new BigInteger();
var startPoint = data.Length - 1;
for (int i = data.Length - 1; i >= 0; i--)
{
var character = data[i];
var charNumericValue = character;
var exponentialWeight = startPoint - i;
var weightedValue = new BigInteger(charNumericValue * Math.Pow(_alphabetLength, exponentialWeight));
value += weightedValue;
}
return value;
}
Using the above to convert the following:
var words = new [] {"bob", "alice", "john" };
420901224533 // bob
-9223372036854775808 // alice
29835458486206476 // john
Despite the overflow the output looks sorted to me, I need to improve this and test it properly but at least it is a start.

algorithm for selecting N random elements from a List<T> in C# [duplicate]

This question already has answers here:
Randomize a List<T>
(28 answers)
Closed 6 years ago.
I need a quick algorithm to select 4 random elements from a generic list. For example, I'd like to get 4 random elements from a List and then based on some calculations if elements found not valid then it should again select next 4 random elements from the list.
You could do it like this
public static class Extensions
{
public static Dictionary<int, T> GetRandomElements<T>(this IList<T> list, int quantity)
{
var result = new Dictionary<int, T>();
if (list == null)
return result;
Random rnd = new Random(DateTime.Now.Millisecond);
for (int i = 0; i < quantity; i++)
{
int idx = rnd.Next(0, list.Count);
result.Add(idx, list[idx]);
}
return result;
}
}
Then use the extension method like this:
List<string> list = new List<string>() { "a", "b", "c", "d", "e", "f", "g", "h" };
Dictionary<int, string> randomElements = list.GetRandomElements(3);
foreach (KeyValuePair<int, string> elem in randomElements)
{
Console.WriteLine($"index in original list: {elem.Key} value: {elem.Value}");
}
something like that:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
int n = 4;
var rand = new Random();
var randomObjects = new List<int>();
for (int i = 0; i<n; i++)
{
var index = rand.Next(list.Count);
randomObjects.Add(list[index]);
}
}
}
You can store indexes in some list to get non-repeated indexes:
List<T> GetRandomElements<T>(List<T> allElements, int randomCount = 4)
{
if (allElements.Count < randomCount)
{
return allElements;
}
List<int> indexes = new List<int>();
// use HashSet if performance is very critical and you need a lot of indexes
//HashSet<int> indexes = new HashSet<int>();
List<T> elements = new List<T>();
Random random = new Random();
while (indexes.Count < randomCount)
{
int index = random.Next(allElements.Count);
if (!indexes.Contains(index))
{
indexes.Add(index);
elements.Add(allElements[index]);
}
}
return elements;
}
Then you can do some calculation and call this method:
void Main(String[] args)
{
do
{
List<int> elements = GetRandomelements(yourElements);
//do some calculations
} while (some condition); // while result is not right
}
Suppose that the length of the List is N. Now suppose that you will put these 4 numbers in another List called out. Then you can loop through the List and the probability of the element you are on being chosen is
(4 - (out.Count)) / (N - currentIndex)
funcion (list)
(
loop i=0 i < 4
index = (int) length(list)*random(0 -> 1)
element[i] = list[index]
return element
)
while(check == false)
(
elements = funcion (list)
Do some calculation which returns check == false /true
)
This is the pseudo code, but i think you should of come up with this yourself.
Hope it helps:)
All the answers up to now have one fundamental flaw; you are asking for an algorithm that will generate a random combination of n elements and this combination, following some logic rules, will be valid or not. If its not, a new combination should be produced. Obviously, this new combination should be one that has never been produced before. All the proposed algorithms do not enforce this. If for example out of 1000000 possible combinations, only one is valid, you might waste a whole lot of resources until that particular unique combination is produced.
So, how to solve this? Well, the answer is simple, create all possible unique solutions, and then simply produce them in a random order. Caveat: I will suppose that the input stream has no repeating elements, if it does, then some combinations will not be unique.
First of all, lets write ourselves a handy immutable stack:
class ImmutableStack<T> : IEnumerable<T>
{
public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
private readonly T head;
private readonly ImmutableStack<T> tail;
public int Count { get; }
private ImmutableStack()
{
Count = 0;
}
private ImmutableStack(T head, ImmutableStack<T> tail)
{
this.head = head;
this.tail = tail;
Count = tail.Count + 1;
}
public T Peek()
{
if (this == Empty)
throw new InvalidOperationException("Can not peek a empty stack.");
return head;
}
public ImmutableStack<T> Pop()
{
if (this == Empty)
throw new InvalidOperationException("Can not pop a empty stack.");
return tail;
}
public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);
public IEnumerator<T> GetEnumerator()
{
var current = this;
while (current != Empty)
{
yield return current.head;
current = current.tail;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
This will make our life easier while producing all combinations by recursion. Next, let's get the signature of our main method right:
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinationsInRandomOrder<T>(
IEnumerable<T> data, int combinationLength)
Ok, that looks about right. Now let's implement this thing:
var allCombinations = GetAllPossibleCombinations(data, combinationLength).ToArray();
var rnd = new Random();
var producedIndexes = new HashSet<int>();
while (producedIndexes.Count < allCombinations.Length)
{
while (true)
{
var index = rnd.Next(allCombinations.Length);
if (!producedIndexes.Contains(index))
{
producedIndexes.Add(index);
yield return allCombinations[index];
break;
}
}
}
Ok, all we are doing here is producing random indexees, checking we haven't produced it yet (we use a HashSet<int> for this), and returning the combination at that index.
Simple, now we only need to take care of GetAllPossibleCombinations(data, combinationLength).
Thats easy, we'll use recursion. Our bail out condition is when our current combination is the specified length. Another caveat: I'm omitting argument validation throughout the whole code, things like checking for null or if the specified length is not bigger than the input length, etc. should be taken care of.
Just for the fun, I'll be using some minor C#7 syntax here: nested functions.
public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinations<T>(
IEnumerable<T> stream, int length)
{
return getAllCombinations(stream, ImmutableStack<T>.Empty);
IEnumerable<IEnumerable<T>> getAllCombinations<T>(IEnumerable<T> currentData, ImmutableStack<T> combination)
{
if (combination.Count == length)
yield return combination;
foreach (var d in currentData)
{
var newCombination = combination.Push(d);
foreach (var c in getAllCombinations(currentData.Except(new[] { d }), newCombination))
{
yield return c;
}
}
}
}
And there we go, now we can use this:
var data = "abc";
var random = GetAllPossibleCombinationsInRandomOrder(data, 2);
foreach (var r in random)
{
Console.WriteLine(string.Join("", r));
}
And sure enough, the output is:
bc
cb
ab
ac
ba
ca

Random that doesn't pull the same value twice unless all values within range were pulled (C#)

I have an array with values that should be displayed randomly, but I don't want one value to be displayed twice unless all values from said array were already displayed.
This is the code I have so far:
//Array of strings that will be displayed
static string[] array1 = { "value1", "value2", "value3", "value4", "value5" };
//Common int rndIndex
int rndIndex;
//Method that runs the random number generator (will be used within other methods)
//There will be one method that will call this method and display the value before the loop
private void runRnd(ref int rndIndex)
{
Random rnd = new Random();
rndIndex = rnd.Next(4);
textBlock.Text = array1[rndIndex];
}
private void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
e.Handled = true;
//This will check whether user input matches what is displayed, then will call runRnd method again
bool checkEquals = textBox.Text.Equals(array1[rndIndex], StringComparison.OrdinalIgnoreCase);
if (checkEquals == true)
{
runRnd(ref rndIndex);
}
}
}
Anybody can help me?
Any time you dont want random numbers to repeat, you want a shuffle, because random values can repeat.
I don't want one value to be displayed twice unless all values ... were already displayed
It doesnt seem like there is much value in the second set/iteration coming up in the same order the second time around. Random usually means unpredictable, so this will reshuffle the set:
Random rng = new Random();
Stack<String> shoe = new Stack<string>();
private void button1_Click(...
{
if (shoe.Count == 0)
{
// refill when empty
shoe = new Stack<string>(GetNewValues());
}
// display next, remove from "deck"
lb1.Items.Add(shoe.Pop());
}
private string[] GetNewValues()
{
string[] values = { "value1", "value2", "value3",
"value4", "value5" };
//simple, usually-good-enough randomizer
return values.OrderBy(r => rng.Next()).ToArray();
}
Using a Stack prevents having to keep an index variable around for an array or List. Getting the next value (Pop) automatically uses them up. When it is empty, it is time for more values. Alternatively, use the Fisher-Yates shuffle:
private void ShuffleArray(string[] items)
{
string tmp;
int j;
for (int i = items.Length - 1; i >= 0; i--)
{
j = rng.Next(0, i + 1);
tmp = items[j];
items[j] = items[i];
items[i] = tmp;
}
}
For this, GetNewValues() would be:
...
ShuffleArray(values);
return values;
If you do want the same order in the second set, you can append the shuffled array to itself:
// shuffle or OrderBy, either will work
values = values.OrderBy(r => rng.Next()).ToArray();
// append second set to first
return values.Concat(values).ToArray();
Create a copy of the Array?
static string[] array1 = { "value1", "value2", "value3", "value4", "value5" };
List<String> list1 = array1.ToList();
int rndIndex;
//Method that runs the random number generator (will be used within other methods)
//There will be one method that will call this method and display the value before the loop
private void runRnd(ref int rndIndex)
{
Random rnd = new Random();
rndIndex = rnd.Next(list1.Count-1);
textBlock.Text = list1[rndIndex];
list1.RemoveAt(rndIndex);
if(list1.Count == 0)
{
list1 = array1.ToList();
}
}
Will create a copy of the Array, remove each value as it used until it is empty, then repeat.
(Not Tested)

Categories