remaining values from HashSet into secondary out - c#

We've been asked to create a program which takes 2 input (which I have parsed) for seats and passengers in a plane and randomly place passengers on seats in a plane in one output as well as placing the remaining seats in a secondary output.
I was wondering if there is a simple way to replace the remaining values in HashSet, into the listBoxLedige.
As it works now, the seats are being distributing but the values in the secondary output arent related the the first output.
if(passengers > seats)
{
MessageBox.Show("For mange passagerer");
}
else
{
HashSet<int> check = new HashSet<int>();
for(int i = 0; i <= passengers - 1; i++)
{
int resultat = rnd.Next(1, seats + 1);
while(check.Contains(resultat))
{
resultat = rnd.Next(1, seats + 1);
}
check.Add(resultat);
int[] passagerer01 = new int[passengers];
passagerer01[i] = i+1;
listBoxFulde.Items.Add("Passager #" + passagerer01[i] + "på sæde #" + resultat);
}
HashSet<int> ledige01 = new HashSet<int>();
for(int i = 0; i <= (seats - passengers - 1); i++)
{
int tilbage = rnd.Next(1, seats + 1);
while(ledige01.Contains(tilbage))
{
ledige01.Add(tilbage);
}
listBoxLedige.Items.Add("Sæde #" + tilbage);

I'm not exactly sure I understand your problem, but have you taken a look at the Except LINQ extension method? Judging by your wording ("remaining values"), it might be the right method for you.
Edit Here's how it's done:
IEnumerable<int> empty = allSeats.Except(check);
Note how empty is now a deferred enumerator (unless you do a .ToArray(), .ToList() or similar on it).

Here's what I'd do (see Range and ExceptWith):
HashSet<int> ledige01 = new HashSet<int>(
Enumerable.Range(1, seats));
ledige01.ExceptWith(taken);
Note in generating the seeds you can remove the trial and error by simply shuffling the seats and taking the first N:
var taken = HashSet<int>(Enumerable.Range(1,seats).Shuffle().Take(passengers));
For hints on how to do shuffle, see e.g. Optimal LINQ query to get a random sub collection - Shuffle
As an aside:
int[] passagerer01 = new int[passengers];
passagerer01[i] = i+1;
listBoxFulde.Items.Add("Passager #" + passagerer01[i] + "på sæde #" + resultat);
looks to be something other than you need :) But I'm assuming it's unfinished and you're likely aware of that
A 'fully' edited take:
if(passengers > seats)
{
MessageBox.Show("For mange passagerer");
}
else
{
HashSet<int> taken = new HashSet<int>();
for(int i = 0; i <= passengers - 1; i++)
{
int resultat;
do {
resultat = rnd.Next(1, seats + 1);
} while(taken.Contains(resultat));
taken.Add(resultat);
listBoxFulde.Items.Add("Passager #" + (i+1) + "på sæde #" + resultat);
}
HashSet<int> ledige01 = new HashSet<int>(
Enumerable.Range(1, seats));
ledige01.ExceptWith(taken);
if(passengers > seats)
{
MessageBox.Show("For mange passagerer");
}
else
{
HashSet<int> taken = new HashSet<int>();
for(int i = 0; i <= passengers - 1; i++)
{
int resultat;
do {
resultat = rnd.Next(1, seats + 1);
} while(taken.Contains(resultat));
taken.Add(resultat);
listBoxFulde.Items.Add("Passager #" + (i+1) + "på sæde #" + resultat);
}
HashSet<int> ledige01 = new HashSet<int>(Enumerable.Range(1, seats));
ledige01.ExceptWith(taken);
foreach(var tilbage in ledige01)
listBoxLedige.Items.Add("Sæde #" + tilbage);

Not a hundred percent sure I understand your problem or solution, but are you aware that you declare and initialize passagerer01 once for each passenger, then deterministically (not randomly) assign seat i+1 to passenger i and afterwards throw away that array? If you wanted to keep the information, you'd have to declare the array outside the for loop.
Also, it doesn't really seem like you're doing anything meaningful in that second part of your code. To determine the empty seats, it would make sense to go through numbers 1 to passengers, check if they are in the set check and, if not, add them to set ledige01. Or, of course, to do something equivalent using a library method as suggested by sehe.
As a last note, in computer science you usually start counting from zero. Thus, you would usually have seat numbers 0 to seats-1 and choose seats randomly like so: rnd.Next(0, seats). And you would usually loop like this: for(int i = 0; i < passengers; i++) instead of for(int i = 0; i <= passengers - 1; i++).

Related

Creating a highscore list in c#

I need to use arrays/lists for my assignment and thought of using one for my highscore list for my game in windows form.
Right now I'm using a regular int for my Score and I'm wondering how I can get this into an array for a highscore list on my highscore.cs
Current code for score in game.cs
int Score = 0;
Scorelabel.Text ="score:" + score;
if (Picturebox.Left < -180)
{
Picturebox.Left = 800;
Score++;*
Things I've tried:
namespace Game
{
public partial class Highscore : Form
{
public Highscore()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
int[] Highscore = new int[10];
for (int Score = 0; Score < 10; Score++)
{
textBox1.Text = new System.Windows.Forms.TextBox();
}
}
}
I tried to make the initial score int an array. I would end up getting the error:"Operator ++ cannot be applied to operand of type 'int[]'
Also tried to get my int into an array but would get the error that I can't convert ints to arrays.
What I want
I want my scores from the game.cs to show in a listbox on my highscore.cs that shows the top 10 scores descending from highest to lowest.
Let's imagine you've got 20 players and each one got some score.
As you decided to create an array of ints to store all scores, so do I:
int[] scores = new int[20];
For example, I fill that array with random unique scores from 10 to 100. You fill in your way:
int[] scores = new int[20];
Random rnd = new Random();
for (int i = 0; i < scores.Length; i++)
{
int score = 0;
// This loop used to generate new random int until it wouldn't be unique (wouldn't exist in array)
while (score == 0 || scores.Contains(score))
score = rnd.Next(10, 100);
scores[i] = score;
lblAllScores.Text += "Player #" + (i + 1) + " score: " + scores[i] + "\n";
}
I append each score and Player # (as number of iteration + 1) to some Label on Form to show all players and their scores. Something like this:
Now you (and I) want take top 10 scores (obviously in descending order, from highest to lowest). Here the keywords are Take and Descending order. In C# through System.Linq namespace provided such pretty extension methods like Take and OrderByDescending. And seems they can give us desired result if we use it properly.
I (and you) creating a new array of ints to store only Top 10 Scores. Then a take initial array scores and use OrderByDescending method to sort values in it from highest to lowest. (x => x) provided as selector to determine key by which method should order values - in our case by value itself. And then I use Take(10) method to take first 10 scored from that sorted array - this is our Top 10 Scores:
int[] top10Scores = scores.OrderByDescending(x => x).Take(10).ToArray();
And all I need to do - put this into Form, which I can do by simple iteration on top10Scores array with for loop. But I (and probably you) want to know what Player has that score. Upper we use (index of array + 1) as Player Number. And we know that scores are unique for each Player. Than now we can take the score value from top10Scores array and try to find its index in source scores array with Array.FindIndex method:
for (int i = 0; i < top10Scores.Length; i++)
{
int playerNumber = Array.FindIndex(scores, score => score == top10Scores[i]) + 1;
lblTop10Scores.Text += "Player #" + playerNumber + " score: " + top10Scores[i] + "\n";
}
And at our Form we have this:
Complete code:
private void ScorePlayers()
{
int[] scores = new int[20];
Random rnd = new Random();
for (int i = 0; i < scores.Length; i++)
{
int score = 0;
while (score == 0 || scores.Contains(score))
score = rnd.Next(10, 100);
scores[i] = score;
lblAllScores.Text += "Player #" + (i + 1) + " score: " + scores[i] + "\n";
}
int[] top10Scores = scores.OrderByDescending(x => x).Take(10).ToArray();
for (int i = 0; i < top10Scores.Length; i++)
{
int playerNumber = Array.FindIndex(scores, score => score == top10Scores[i]) + 1;
lblTop10Scores.Text += "Player #" + playerNumber + " score: " + top10Scores[i] + "\n";
}
}
Please note, that this is only a sketch and example to demonstrate how to use OrderByDescending and Take extension methods to get first 10 elements of sorted from highest to lowest value collection. In real application you probably will have at least simple Dictionary<string, int>, where would be stored player name (as string) and his score (as int). Or, more likely, it would be a model class called Player with Name and Score properties and each Player would be stored in some List<Player>:
public class Player
{
public string Name { get; set; }
public int Score { get; set; }
}
Hope you can find your solution and this example would be helpful for you.

Remove second occurrence of value from array C#

Instead of removing the first occurrence of a duplicated value, as my code currently does, how can I get it to delete the second occurrence instead?
namespace deletenumber
{
class Program
{
public static void Main(String[] args)
{
Random r = new Random();
int[] a = new int[10];
for (int i = 0; i < 10; i++)
a[i] = r.Next(1, 100);
for (int i = 0; i < 10; i++)
Console.WriteLine(" a [" + i + "] = " + a[i]);
Console.WriteLine();
Console.ReadLine();
Console.WriteLine("What number do you want to delete?");
int item = Convert.ToInt32(Console.ReadLine());
Console.WriteLine();
int index = Array.IndexOf(a, item);
a = a.Where((b, c) => c != index).ToArray();
Console.WriteLine(String.Join(", ", a));
Console.ReadLine();
}
}
}
I'm not sure why exactly you might want to delete only the second occurrence because there should be no matter which one of the duplicates is removed, correct?
Anyway, there are several ways of achieving what you want.
I will not directly write a solution here, but will propose the methods you can use in order to achieve what you want:
Array.LastIndexOf
Use the same IndexOf method again to find the second occurrence, by starting the search from the previous occurrence (myArray.IndexOf("string", last_indexof_index);)
Generally, consider using a HashSet<T> (official docs here) if you want to have a collection of unique elements.
You can also convert your existing collection to HashSet<int>:
int[] a = new int[10];
var set = new HashSet<int>(a);
You could convert your array to a List and then just remove the index before casting it back to an array:
var list = new List<int>(a);
list.RemoveAt(index);
a = list.ToArray();

How to populate two separate arrays from one comma-delimited list?

I have a comma delimited text file that contains 20 digits separated by commas. These numbers represent earned points and possible points for ten different assignments. We're to use these to calculate a final score for the course.
Normally, I'd iterate through the numbers, creating two sums, divide and be done with it. However, our assignment dictates that we load the list of numbers into two arrays.
so this:
10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85
becomes this:
int[10] earned = {10,20,30,40,45,50,20,45,85};
int[10] possible = {10,20,35,50,50,50,20,90,85};
Right now, I'm using
for (x=0;x<10;x++)
{
earned[x] = scores[x*2]
poss [x] = scores[(x*2)+1]
}
which gives me the results I want, but seems excessively clunky.
Is there a better way?
The following should split each alternating item the list into the other two lists.
int[20] scores = {10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85};
int[10] earned;
int[10] possible;
int a = 0;
for(int x=0; x<10; x++)
{
earned[x] = scores[a++];
possible[x] = scores[a++];
}
You can use LINQ here:
var arrays = csv.Split(',')
.Select((v, index) => new {Value = int.Parse(v), Index = index})
.GroupBy(g => g.Index % 2,
g => g.Value,
(key, values) => values.ToArray())
.ToList();
and then
var earned = arrays[0];
var possible = arrays[1];
Get rid of the "magic" multiplications and illegible array index computations.
var earned = new List<int>();
var possible = new List<int>();
for (x=0; x<scores.Length; x += 2)
{
earned.Add(scores[x + 0]);
possible.Add(scores[x + 1]);
}
This has very little that would need a text comment. This is the gold standard for self-documenting code.
I initially thought the question was a C question because of all the incomprehensible indexing. It looked like pointer magic. It was too clever.
In my codebases I usually have an AsChunked extension available that splits a list into chunks of the given size.
var earned = new List<int>();
var possible = new List<int>();
foreach (var pair in scores.AsChunked(2)) {
earned.Add(pair[0]);
possible.Add(pair[1]);
}
Now the meaning of the code is apparent. The magic is gone.
Even shorter:
var pairs = scores.AsChunked(2);
var earned = pairs.Select(x => x[0]).ToArray();
var possible = pairs.Select(x => x[1]).ToArray();
I suppose you could do it like this:
int[] earned = new int[10];
int[] possible = new int[10];
int resultIndex = 0;
for (int i = 0; i < scores.Count; i = i + 2)
{
earned[resultIndex] = scores[i];
possible[resultIndex] = scores[i + 1];
resultIndex++;
}
You would have to be sure that an equal number of values are stored in scores.
I would leave your code as is. You are technically expressing very directly what your intent is, every 2nd element goes into each array.
The only way to improve that solution is to comment why you are multiplying. But I would expect someone to quickly recognize the trick, or easily reproduce what it is doing. Here is an excessive example of how to comment it. I wouldn't recommend using this directly.
for (x=0;x<10;x++)
{
//scores contains the elements inline one after the other
earned[x] = scores[x*2] //Get the even elements into earned
poss [x] = scores[(x*2)+1] //And the odd into poss
}
However if you really don't like the multiplication, you can track the scores index separately.
int i = 0;
for (int x = 0; x < 10; x++)
{
earned[x] = scores[i++];
poss [x] = scores[i++];
}
But I would probably prefer your version since it does not depend on the order of the operations.
var res = grades.Select((x, i) => new {x,i}).ToLookup(y=>y.i%2, y=>y.x)
int[] earned = res[0].ToArray();
int[] possible = res[1].ToArray();
This will group all grades into two buckets based on index, then you can just do ToArray if you need result in array form.
here is an example of my comment so you do not need to change the code regardless of the list size:
ArrayList Test = new ArrayList { "10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85" };
int[] earned = new int[Test.Count / 2];
int[] Score = new int[Test.Count / 2];
int Counter = 1; // start at one so earned is the first array entered in to
foreach (string TestRow in Test)
{
if (Counter % 2 != 0) // is the counter even
{
int nextNumber = 0;
for (int i = 0; i < Score.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(Score[i])))
{
nextNumber = i;
break;
}
}
Score[nextNumber] = Convert.ToInt32(TestRow);
}
else
{
int nextNumber = 0;
for (int i = 0; i < earned.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(earned[i])))
{
nextNumber = i;
break;
}
}
earned[nextNumber] = Convert.ToInt32(TestRow);
}
Counter++
}

Stopwatch displaying wrong times?

public void GnomeSort<T>(IList<T> list, IComparer<T> comparer)
{
sortTimer = new Stopwatch();
sortTimer.Start();
bool stillGoing = true;
while (stillGoing)
{
stillGoing = false;
for (int i = 1; i < list.Count; )
{
T x = list[i - 1];
T y = list[i];
if (comparer.Compare(x, y) <= 0)
i++;
else
{
list[i - 1] = y;
list[i] = x;
i--;
if (i == 0)
i = 1;
stillGoing = true;
}
}
}
sortTimer.Stop();
richTextBox1.Text += "Gnome Sorting completed, total time taken " + sortTimer.Elapsed + "\n";
}
If I run this twice, using the same unsorted randomly generated array here:
randomArray = randomizedArray
(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text));
randomArrayGnome = randomArray;
randomArrayBubble = randomArray;
randomArrayInsertion = randomArray;
GnomeSort(randomArray);
BubbleSort(randomArrayBubble);
But it outputs something close to this:
Gnome Sorting completed, total time taken 00:00:02.5419864
Bubble Sorting completed, total time taken 00:00:00.0003556
but if I switch the call order, the times are drastically different, instead Bubble Sorting may take 6 seconds. What is happening here? How come it isn't sorting them correctly?
Your problem is with your initialisation of the arrays. As shown here:
randomArray = randomizedArray
(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text));
randomArrayGnome = randomArray;
randomArrayBubble = randomArray;
randomArrayInsertion = randomArray;
The above code creates four variables that all reference the same array. So what happens is that the first sort algorithm sorts the array, subsequent ones will encounter an array that is already sorted so will execute very fast.
As simple solution is to use a Linq - ToList to clone the array:
randomArray = randomizedArray
(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text));
randomArrayGnome = randomArray.ToList();
randomArray & randomArrayGnome both holds the reference to randomizedArray.
When you call
GnomeSort(randomArray);
BubbleSort(randomArrayBubble);
the reference array is already sorted, BubbleSort is working on a already sorted array!
You can use Array.Clone() so that four different reference are created!

How to write groups of numbers using Console.Write?

I'm very new to C# (And Stack Overflow, forgive me for any poor etiquette here), and I'm writing the game Mastermind in a console application. I'm trying to show a list of the user's guesses at the end of the game, and I know that using Console.WriteLine(); will just give me 30-odd lines off numbers which don't tell the user anything.
How can I alter my code so that the program displays 4 numbers in a group, at a time? For example:
1234
1234
1234
//Store numbers in a history list
ArrayList guesses = new ArrayList(); //This is the ArrayList
Console.WriteLine("Please enter your first guess.");
guess1 = Convert.ToInt32(Console.ReadLine());
guesses.Add(guess1);
foreach (int i in guesses)
{
Console.Write(i);
}
I assume that each element of your byte array is a single digit (0-9). If that assumption is invalid -- please let me know, I'll modify the code :)
Action<IEnumerable<int>> dump = null;
dump = items =>
{
if(items.Any())
{
var head = String.Join("", items.Take(4));
Console.WriteLine(head);
var tail = items.Skip(4);
dump(tail);
}
};
dump(guesses);
It looks like you're most of the way there, you have a console write that writes them all out without linebreaks. Next add an integer count and set it to zero. Increment it by one in the foreach loop. count % 4 == 0 will then be true for all counts that are a multiple of four. This means you can stick an if block there with a write-line to give you your groups of four.
List<int> endResult = new List<int>();
StringBuilder tempSb = new StringBuilder();
for(int i=0; i < groups.Count; i++)
{
if(i % 4 == 0) {
endResult.Add(int.Parse(sb.ToString()));
tempSb.Clear(); // remove what was already added
}
tempSb.Append(group[i]);
}
// check to make sure there aren't any stragglers left in
// the StringBuilder. Would happen if the count of groups is not a multiple of 4
if(groups.Count % 4 != 0) {
groups.Add(int.Parse(sb.ToString()));
}
This will give you a list of 4 digit ints and make sure you don't lose any if your the number of ints in your groups list is not a multiple of 4. Please note that I am continuing based on what you provided, so groups is the ArrayList of ints.
This is some thing I quickly put together:
Update:
ArrayList guesses = new ArrayList(); //This is the ArrayList
// Four or more
guesses.Add(1); guesses.Add(2);
guesses.Add(3);guesses.Add(4);
guesses.Add(5); guesses.Add(6); guesses.Add(7);guesses.Add(8); guesses.Add(9);
//Uncomment-Me for less than four inputs
//guesses.Add(1); guesses.Add(2);
int position = 0;
if (guesses.Count < 4)
{
for (int y = 0; y < guesses.Count; y++)
{
Console.Out.Write(guesses[y]);
}
}
else
{
for (int i = 1; i <= guesses.Count; i++)
{
if (i%4 == 0)
{
Console.Out.WriteLine(string.Format("{0}{1}{2}{3}", guesses[i - 4], guesses[i - 3],
guesses[i - 2], guesses[i - 1]));
position = i;
}
else
{
if (i == guesses.Count)
{
for (int j = position; j < i; j++)
{
Console.Out.Write(guesses[j]);
}
}
}
}
}

Categories