How to... 2 dimensional matrix using object as index? - c#

If have 2 classes, Teams and Games.
Something like this:
class Team {...}
class Games
{
Team team1;
Team team2;
...
}
And I have a List of all games played...
List<Games> allGames ...
What I now want to do is: for further statistics I need a matrix that counts how often each team played against every other team.
Earlier I would have solved this as follows:
int[,] countTeamVsTeam = new int[allTeams.Count, allTeams.Count];
foreach (Game game in allGames)
{
countTeamVsTeam[game.team1.SOMENUMBER,game.team2.SOMENUMBER]++;
}
Now I wonder if it is a good idea to skip that artificial SOMENUMBER attribute and use the objects by themselves as an index of my data structure:
Dictionary<Team, Dictionary<Team, int>> countTeamVsTeam = new Dictionary<Team, Dictionary<Team, int>>();
// initialize
foreach (Team team1 in allTeams)
{
countTeamVsTeam[team1] = new Dictionary<Team, int>();
foreach (string team2 in allTeams)
{
countTeamVsTeam[team1][team2] = 0;
}
}
foreach (Game game in allGames)
{
countTeamVsTeam[game.team1,game.team2]++;
}
Any thoughts on this?
Is this a good way of doing it?
Is that Dictionary<Team, Dictionary<Team, int>> a good data structure for representing the 2 dimensional matrix? (yes I know it's not an array but a dictionary of dictionaries...)

I see a couple of options.
A Dictionary can actually have a tuple/composite key so you can define it as follows
Dictionary<(Team, Team), int> countTeamVsTeam = new Dictionary<(Team, Team), int>();
But as pointed out in the comments above, you need to strictly control the order of each team (home vs away, alphabetical, ordered by id etc.) as
countTeamVsTeam[(team1, team2)] will not have the same value as countTeamVsTeam[(team2, team1)]
Another option is to create a custom collection. Within which you internally control the teams in a manner that is not visible externally so that you can abstract this detail and control the order of the teams so that you only store a single value no matter which order the teams are passed into the collection.

Related

How do I get properties for each object in the array?

I'm trying to write a card game and I have this question.
I have a List<int> address1Player; which contains a set of cards with parameters.
I need to pull the parameter of each card from this sheet.
For example, List<int> address1Player contains cards with id 1, 2, 3. I need to find out what colors of cards are in List<int> address1Player. Colors are stored in int.
The color getting parameter looks like this
public int PropertyColor(int address){
return allProperties[address].GetComponent<Plot>().GetColor();
}
How do I make sure that I end up with an array with the colors of each card?
A List<int> only contains a list of integers - in your case, IDs. You want to store a data structure of colors (and ostensibly, some other values about the cards), so a List is not the collection you want. First, let's think about the data structure that our collection will hold, then we'll come back to the collection.
Our card in our game has at least two properties: ID and an integer based Color. In C#, we write classes or structs to group up logical bundles of properties into an object. It would look (pretty simply) like this:
public struct KorsunsCard
{
public int Id;
public int CardColor
}
Now, we have a "card" object that has properties that we can check and set like so:
KorsunsCard greenCard = new KorsunsCard() { Id = 1, CardColor = 6 };
greenCard.CardColor = 5; // change the color to "5"
if (greenCard.Id == 2) { /* ... do some stuff ... */ }
Then, we can have methods just return the entire card:
public KorsunsCard GetCardWithID(int Id)
{
KorsunsCard returnCard = // ...
// ... get a card ...
return returnCard;
}
Now, about those collections. Selecting a data structure to use is the heart of C#. Collections are "groups" of objects - in our case, KorsunsCards. Each collection can do different things well - Lists can get access to a card by "index" (not Id), iterate over the whole list, sort themselves, etc. Dictionarys are meant for looking up a card by a key, and while they can iterate over the whole dictionary, they aren't meant for that typically so the syntax is a little more involved. Not difficult, just not as easy as a List. You also might want a HashSet, a collection that can only have one unique item - but is unsorted, like a dictionary.
I can't suggest the best solution, because it depends on your game's rules (will your deck always have the same number of cards? one and only of each kind of card? does a user build their own deck from a pool of available cards?).
Let's start with some cards:
KorsunsCard ace = new KorsunsCard() { Id = 1, Color = 1 };
KorsunsCard deuce = new KorsunsCard() { Id = 2, Color = 2 };
KorsunsCard trey = new KorsunsCard() { Id = 3, Color = 3 };
If you wanted a List, you could declare it and add some values to it like so:
List<KorsunsCard> myDeck = new List<KorsunsCard>();
myDeck.Add(ace);
myDeck.Add(deuce);
myDeck.Add(trey)
int deuceColor = deuce.Color; // deuce's color
return myDeck[0]; // ace, but be aware the list can be shuffled/sorted!
foreach (KorsunsCard kc in myDeck) // iterate on the whole deck
{
kc.Color = 4; // set the entire decks color to 4 , one at a time
}
The generic collection types Dictionary, HashSet, Queue, Stack may all be relevant for your game, depending on how you typically interact with the deck and the game rules. Hopefully I've given you enough from List that you can go and read up on these other collection types and put them to use.

C# Merging dictionaries inside of an object

I realize there are tons of posts on this subject but I still haven't found what I am looking for reading the answers.
I get two objects A and B, that both have a dictionary and a set of booleans. Dictionaries are shaped the same, just the data differs. My end goal here is to create a 3rd object C, copy of A with its dictionary updated to have values from A and B according to a set of rules (for instance C.dictionary would have all A.dictionary.[x] if A.Dictionary.[x].Value.days < 5 and and all B.dictionary.[x] if days > 5).
If you have a solution for this that would be awesome, otherwise, here is where I am at and where I fail:
I am trying to create a new dictionary and and loop through A and B to add the values... I'll add the rules after. Once I will have it I'll find a way to place this dictionary into object C. (tell me if over engineered, I am very new to C#).
var dict = new Dictionary<DateTime, TypeData>();
foreach (var item in A.dictionary.keys)
{
dict.Add(A.Dictionary[item]);
}
This does not work for 2 reasons:
item does represents A.dictionary[x].key not A.dictionary[x] (also contains value and non public members)
"Add" gets underlines and does not show more info for the error
Thanks a lot for checking my post !
try this
var dictionariesToCombine = new Dictionary<DateTime, TypeData>[] {dictA,dictB};
Dictionary<int, int> dictC = new Dictionary<DateTime, TypeData>();
for (var i=0; i< dictionariesToCombine.Length; i++)
{
foreach (var item in dictionariesToCombine[i])
{
if(
(i==0 and your conditions)
|| (i==1 and your conditions)
)
resultDict.Add(item.Key, item.Value);
}
}

Is there a way to add a Key/Value pair to the end of a Dictionary?

Currently working on a project to create the card game "War" in Windows Forms. I am using dictionaries to keep track of the image file as well as the value for the card. I am running into an issue where cpuDeck.add(x, y); is adding the dictionary entery to the front of the dictionary as opposed to the back.
cpuPlayer = cpu's deck
humanPlayer = human's deck
cpuWinnings and humanWinnings are temporary dictionaries to store the cards in in order to pull the image for the GUI before they enter back into the main dictionaries
if (cpuPlayer.Values.ElementAt(0) >= humanPlayer.Values.ElementAt(0))
{
Image x = cpuPlayer.Keys.ElementAt(0);
int y = cpuPlayer.Values.ElementAt(0);
Image a = humanPlayer.Keys.ElementAt(0);
int b = humanPlayer.Values.ElementAt(0);
cpuPlayer.Remove(x);
humanPlayer.Remove(a);
cpuPlayer.Add(x, y);
cpuPlayer.Add(a, b);
cpuWinnings.Add(x, y);
imgcpuwinning.Image = cpuWinnings.Keys.ElementAt(0);
cpuWinnings.Clear();
}
else if ((humanPlayer.Values.ElementAt(0) > cpuPlayer.Values.ElementAt(0)))
{
Image x = cpuPlayer.Keys.ElementAt(0);
int y = cpuPlayer.Values.ElementAt(0);
Image a = humanPlayer.Keys.ElementAt(0);
int b = humanPlayer.Values.ElementAt(0);
cpuPlayer.Remove(x);
humanPlayer.Remove(a);
humanPlayer.Add(x, y);
humanPlayer.Add(a, b);
humanWinnings.Add(a, b);
imghumanwinning.Image = humanWinnings.Keys.ElementAt(0);
humanWinnings.Clear();
}
else
{
}
A Dictionary<Image, int> doesn't have a concept of having an order. The idea of using a SortedDictionary<Image, int> wouldn't make sense as you still don't control where an item is added - that would be based on the key.
What you really need is some sort of list where you do control the position that you add items.
It's likely that List<(int Value, Image Image)>, or even Queue<(int Value, Image Image)>, is what you want. The type (int Value, Image Image) is a tuple containing two elements. It's much like KeyValuePair<Image, int> that you're currently using.
I'm going to suggest some code for you to play with, but I'm afraid it's difficult to know exactly what you are trying to do with the minimal code that you presented.
I'm also a little confused as to what you're attempting with this code:
cpuWinnings.Add(x, y);
imgcpuwinning.Image = cpuWinnings.Keys.ElementAt(0);
cpuWinnings.Clear();
You appear to want to add an element to the end of cpuWinnings, but then you use the first item of the list, and then you clear the list. What was the point of adding an item to the end if you're going to just clear it? Or, if you're expecting .Add(x, y) to put it to the front of the list then why not just do imgcpuwinning.Image = x; before clearing the list?
I'm going to assume that you have the desire to add it to the end of the list.
So here's the code. To start I want to define a way to say who the current player is and to reduce the number of variables you have.
Let's create a Player enum:
public enum Player
{
Human, Cpu
}
Now if you're trying to select the first item from a list and then remove that item you probably want a Queue. And we want a queue for each of the Human and Cpu players.
var player = new Dictionary<Player, Queue<(int Value, Image Image)>>()
{
{ Player.Human, new Queue<(int Value, Image Image)>() },
{ Player.Cpu, new Queue<(int Value, Image Image)>() },
};
It seems to me for the winnings you just need a list.
var winnings = new Dictionary<Player, List<(int Value, Image Image)>>()
{
{ Player.Human, new List<(int Value, Image Image)>() },
{ Player.Cpu, new List<(int Value, Image Image)>() },
};
And for your PictureBox elements you just need this:
var holder = new Dictionary<Player, PictureBox>()
{
{ Player.Human, imghumanwinning },
{ Player.Cpu, imgcpuwinning },
};
Now to select and remove the first element from the queue you do this:
(int Value, Image Image) xy = player[Player.Cpu].Dequeue(); //remove first
(int Value, Image Image) ab = player[Player.Human].Dequeue(); //remove first
Note: I've kept the xy and ab variable names from your code, I've just joined them together. Ideally you'd name these something a little more meaningful.
Next we want to find out who the current player is:
var currentPlayer = xy.Value >= ab.Value
? Player.Cpu
: Player.Human;
Now processing the values is easy:
player[currentPlayer].Enqueue(xy); //add to end of queue
player[currentPlayer].Enqueue(ab); //add to end of queue
winnings[currentPlayer].Add(xy);
holder[currentPlayer].Image = winnings[currentPlayer].First().Image;
winnings[currentPlayer].Clear();
There's no if any more.
Now, I might be wrong about needing a Queue, but it seems to fit you current code. It could be that you need to use a Queue for winnings too. Maybe just a List for both would suffice. You'd need to provide more detail for me to have a better response.
You shouldn't depend on the order of keys in a Dictionary. If you need ordering, you should use an OrderedDictionary or SortedDictionary or a List of KeyValuePair if you want to change elements manually.
From the documentation:
For purposes of enumeration, each item in the dictionary is treated as
a KeyValuePair structure representing a value and its
key. The order in which the items are returned is undefined.
The order of elements in a dictionary is non-deterministic. So don't rely on enumerating in the same order as elements were added to the dictionary. That's not guaranteed.
I hope it helps 😊

Need to get all combinations of several lists, taking sets amounts from each list

class Employee
{
int id;
Position position;
}
class Position
{
string name;
}
public List<List<Tuple<Position, Employee>>> getAllCombinations(Dictionary<Position, int> positionToCountMap, List<Employee> allEmployees)
{
}
positionToCountMap to would be something like this: {"Manager", 1}, {"Developer", "3"}, {"PM", "1"} (it's unknown how many keys there are)
I need to return a list of all possible combinations of allEmployees such that it satisfies the count requirements in the positionToCountMap. I need al combinations of 1 manager, 3 developers and 1 PM. My first step was to create a new dictionary of position to a list of employees with that position.
var positionToEmployeeMap = new Dictionary<Position, List<Employee>>()
//Loop through allEmployees adding each to this dictionary
Now the problem becomes that I have several lists, and I need to find all possible combinations taking the amount specified in positionToCountMap from each list.
Is this a good way to approach the problem? Even if it is, I can't wrap my head around how I would actually brute force this. I was originally trying to think of some recursive solution, but the size of the list may be large enough that recursion might not be a great choice. I'm stuck and could use some advice.
EDIT
I think I have a solution, though it's not great and i'd still love to get some advice.
var positionToEmployeeMap = new Dictionary<Position, List<Employee>>()
//Loop through allEmployees adding each to this dictionary
var relevantLists = new List<Employee>();
//for each key in positionToCountMap, find the list in positionToEmployeeMap and add it to relevantLists
var allCombos = new List<List<Employee>>();
//Loop through relevantLists. For each list, recursively generate all possible combinations of sublists of size N, where N is the number in positionToCountMap. Add a list of all the combinations to allCombos
//recursively loop through allCombos finding all possible combinations taking 1 element from each list
I'd use LINQ. You can do something similar to a SQL CROSS JOIN operation with the following:
var ListA = new List<object>();
var ListB = new List<object>();
var ListC = new List<object>();
var result = (from a in listA
from b in listB
from c in listC
select new { a, b, c }).ToList();
This will result in a list including all combinations of the values in ListA, ListB, and ListC.

Check that all values of an array have been used

Sorry if this is a stupid noob question. I'm doing a very small project for my girlfriend - a list of countries and she has to enter their capitals (obscure countries, mind you) . Since I'm a total beginner, I had to resort to using two arrays, one for countries and the other for capitals, with matching indexes. That way it's easy to check for the right answer and I don't have to parse any text files or use any data-bases. I'm using random numbers to make it more interesting. To stop the program from generating the same countries over and over again, I'm using a List of integers that keeps tracks of what indexes have already been used and regenerates the number if the list contains the previous one. Pretty basic stuff. Surprisingly, it all works.
But I'm having a problem. How do I check that I've run out of countries, basically? :) I can't simply check the List size against my countries array, since List probably includes more values than the array, and if (taken.Equals(Countries.Length)) doesn't seem to work. Or I can't find the right place in the code to put this check.
Sorry if this is simple, but I can't seem to find a proper solution.
EDIT
Wow, what an amazing community. During the short walk from Starbucks to my place I get dozens of quality answers which cover a huge array of design techniques. This is so great! Thank you everyone! Obviously, the question has been answered but I will post the code for you, if anyone has any additional comments.
// JUST A TEST FOR NOW, 13 COUNTRIES
string[] Countries = {"Belgium", "France", "The Netherlands", "Spain", "Monaco", "Belarus", "Germany",
"Portugal", "Ukraine", "Russia", "Sweden", "Denmark", "South Africa"};
string[] Capitals = {"Brussels", "Paris", "Amsterdam", "Madrid", "Monaco", "Minsk", "Berlin",
"Lisbon", "Kiev", "Moscow", "Stockholm", "Copenhagen", "Pretoria"};
Random number = new Random();
List<int> taken = new List<int>();
int index;
int score = 0;
private int Generate()
{
while (true) {
index = number.Next(0, Countries.Length);
if (taken.Contains(index)) continue;
// THIS IS WHAT I WAS INITIALLY TRYING TO DO
if (taken.Equals(Countries.Length)) {
MessageBox.Show("Game over!");
return -1;
}
return index;
}
}
private void Form1_Load(object sender, EventArgs e)
{
index = Generate();
taken.Add(index);
label1.Text = Countries[index];
label3.Text = "0 out of " + Countries.Length.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Trim() == Capitals[index].ToString()) {
label2.Text = "You win!";
index = Generate();
taken.Add(index);
label1.Text = Countries[index];
textBox1.Clear();
label3.Text = ++score + " out of " + Countries.Length.ToString();
}
else {
label2.Text = "Wrong!";
textBox1.Clear();
}
}
}
}
To stop the program from generating the same countries over and over again, I'm using a List of integers that keeps tracks of what indexes have already been used and regenerates the number if the list contains the previous one.
...
How do I check that I've run out of countries, basically?
You might want to consider an alternative approach, as this is going to be quite expensive and overly complicated.
Instead of trying to add one country at random, checking against ones you've already added, you could just make the entire list of countries, then perform a shuffle ("random sort") on the collection. This way, you'll get all of the countries in one shot in a random order.
Instead of using two arrays, or an array and a list, let's introduce something of C# 4.0 that actually looks and is easy to use and seems to be made for this type of assignments.
Follow this code with your eyes and specifically look how these "anonymous types" are used in the end. It makes life real easy.
// initialize your array like so,
// now you can access your items as countries[1].name and countries[1].city
// and you will never have to worry about having too much cities or countries
// PLUS: they're always together!
var countries = new [] {
new { name = "The Netherlands", city = "Amsterdam"},
new { name = "Andorra", city = "Vaduz" },
new { name = "Madagascar", city = "Antananarivo"}
};
// randomize by shuffling (see http://stackoverflow.com/questions/375351/most-efficient-way-to-randomly-sort-shuffle-a-list-of-integers-in-c-sharp/375446#375446)
Random random = new Random();
for (int i = 0; i < countries.Length; i += 1)
{
int swapIndex = random.Next(i, countries.Length);
if (swapIndex != i)
{
var temp = countries[i];
countries[i] = countries[swapIndex];
countries[swapIndex] = temp;
}
}
// go through all your items in the array using foreach
// so you don't have to worry about having too much items
foreach(var item in countries)
{
// show your girlfriend the country, something like
string inputString = DisplayCountry(item.country);
if(inputString == item.city)
{
ShowMessage("we are happy, you guessed right!");
}
}
// at the end of the foreach-loop you've automatically run out of countries
DisplayScore(to-your-girlfriend);
Note: you can easily expand on this anonymous types by adding whether or not that particular country/city pair was guessed right and make a subsequent test with the ones she failed.
You could use a HashSet<int> to keep track of indexes that have been used. This won't accept duplicate values. The Add method returns a boolean that indicates whether the value was already in the list:
if (hashSet.Add(index))
DisplayValue(index);
else
//regenerate
But I would probably use your existing stragegy, but backwards: create a list pre-filled with values from 0 to Count - 1. Pick indexes from this list, removing them as you use them. This is logically similar to Reed Copsey's suggestion of sorting, but probably requires less change to your existing code.
var availableIndexes = new List<int>(Enumerable.Range(0, countryCount));
var random = new Random();
while (availableIndexes.Count > 0)
{
var index = availableIndexes[Random.Next(0, availableIndexes.Count)];
DisplayValue(index);
availableIndexes.Remove(index);
}
You can use a key/value pair, like a Dictionary<string, string> to store your countries and capitals. Then iterate through the collection using a random LINQ orderby clause:
Dictionary<string, string> Countries = new Dictionary<int, string>();
// populate your collection of countries
foreach(var country in Countries.OrderBy(c => Guid.NewGuid()))
{
Console.WriteLine("Key: {0} Value: {1}", country.Key, country.Value);
}
Create a Country class and a Capital class.
Then model your classes to use a Dictionary<TKey, TValue> Generic Collection so that you declare the generic Dictionary object as:
Dictionary<Country, Capital>
where Country is the key and Capital is its value.
For MSDN reference to Dictionary and its sample usage, you can follow below link:
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
As you keep using Countries and Capitals, add them to above Dictionary instance after checking for their existence in the Dictionary instance, if any of them do exist then either popup an info message or a warning.
Quick and dirty, not necessarily efficient or secure.
Dictionary<string, string> countriesAndCapitals = new Dictionary<string, string>()
{
{ "Afghanistan", "Kabul" },
{ "Albania", "Tirane" },
{ "Algeria","Algers" },
{ "Andorra", "Andorra la Vella" } //etc, etc
};
foreach (var countryCapital in countriesAndCapitals.OrderBy(f => Guid.NewGuid()))
{
Console.WriteLine(countryCapital.Key + " " + countryCapital.Value);
}
It seems like what you need is a different type of data structure, two sets of lists would work fine but it is complicated for nothing. I suggest looking into the dictionary list type.
Dictionary<string,string> countryList = new Dictionary<string,string>();
countryList.Add("Canada","Ottawa");
countryList.Add("Thailand","Bankok");
etc...
You could then iterate through the list while a boolean value sees whether or not there was a hit. More info on Dictionary list type.
Why don't you remove the items from the list that you used? Then you don't have conflicts. Then you check states.Count() > 0.
The quickest thing I can think to do is to use the Distinct() call on your list. Then your count of items in the list can be compared to your array's count to see if all have been used.
if(myUsedList.Distinct().Count() < myArray.Count) { ... }

Categories