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.
Related
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.
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 😊
I have a class in my Windows application like so:
public class Pets
{
String Name {get;set;}
int Amount {get;set;}
}
In one of my other classes i made a List of that class like so.
List<Pets> myPets = new List<Pets>();
myPets.Add(new Pets{ Name = "Fish", Amount = 8});
myPets.Add(new Pets{ Name = "Dogs", Amount = 2});
myPets.Add(new Pets{ Name = "Cats", Amount = 2});
Is there a way i can get the Index of the Pets whos Name = "Fish"?
I realize i can do this
int pos = 0;
for(int x = 0; x<myPets.Count;x++)
{
if( myPets[x].Name == "Fish")
{
pos = x;
}
}
But in the case that i have ALOT of items in myPets it would take long to loop through them to find the one i am looking for. Is there another way to complete the task above. That would make my application run quicker? In the case that myPets has a lot of items in it.
The way you have your data structured at the moment does not lend itself well to searching by pet's name if the list is large.
So iterating manually like you suggest and what FindIndex is doing is known as a linear search which is a brute force algorithm. If you have N items in your collection, the worst case scenario to find an item is N iterations. This is known as O(N) using the Big O Notation. The speed of search grows linearly with the number of items within your collection.
For faster searching you need to change to a different data structure (like a Hashtable), use a database or implement a different search algorithm, such as a binary search( O(log(n)) complexity ).
Have a look at this question for an example:
Can LINQ use binary search when the collection is ordered?
If you want to find index only to access item of List you can use Dictionary instead.
var pets = new Dictionary<string, int>();
pets.Add("Dogs", 2);
pets.Add("Fish", 8);
int amount = pets["Fish"];
Apologies if I'm struggling to word this properly. OOP is not my expertise but I'm very much trying to learn.
How do I create an instance of an object on say, every third iteration of a loop?
Within a loop, I need to assign values to an object, but the property to assign a value to will depend on the result of a case statement. Once each property of the object has been assigned, I then need to add that object to a list of objects of the same type.
If I create the object before the loop is entered, then my list just contains the same result over and over again, because (I've read) that the list only contains a reference to the object, and if the object is then changed, so does the list.
If I create the object within the loop, then obviously, I'll get a new object each time with just one of the properties assigned to it by the time it adds it to the list. The list would contain different results, but only the last property would be assigned, as a new object is created each time.
What I assumed you could therefore do was create a new object whenever all of the properties had a value assigned to it (or at the start, when none had). So, since my object has three properties, each time through the loop, I would like to add a new object whenever an int iCounter was 0, add the values, and increment iCounter, then when iCounter is 3, set to 0. However, when I attempt to create an object inside of an if statement, the rest of the program doesn't see the object exists.
I also assumed, I could maybe attempt some kind of macro substitution, which is what I would normally resort to in Fox, however, (I've read) that this is a big no-no in c#.
Any ideas?
try
{
cProducts Product = new cProducts();
SqlConn2.Open();
rdr2 = SqlComm2.ExecuteReader();
int iScanLine = 0;
while (rdr2.Read())
{
iScanLine++;
Product.product = rdr2["product"].ToString();
Product.sOrder = rdr2["order_id"].ToString();
switch (rdr2["detail"].ToString())
{
case "Quantity":
Product.quantity = Convert.ToInt16(rdr2["display_value"]) ;
break;
case "Option":
Product.Option = rdr2["display_value"].ToString();
break;
case "Size":
Product.Size = rdr2["display_value"].ToString();
break;
}
if (iScanLine == 3)
{
lProducts.Add(Product);
thisPage.sProducts.Add(lProducts[lProducts.Count() - 1]);
iScanLine = 0;
}
}
}
You could just change this bit:
if (iScanLine == 3)
{
lProducts.Add(Product);
thisPage.sProducts.Add(Product); //<-- We know the object just added is still in Product
iScanLine = 0;
Product = new cProducts(); //<-- Create a new object to start populating
}
Also, I know that .NET framework is quite new, being only a decade old, but you might consider reading the Naming Guidelines:
X DO NOT use Hungarian notation.
Looks like you have table with four columns, where each product represented in three consecutive rows
product | order_id | detail | display_value
A X Quantity 5
A X Option Foo
A X Size XL
B X Quantity 2
...
And you are trying to read products. I suggest you to store current product name and compare it with last product name. If name is changed, then you are reading data of next product, thus you can create new product and add it to list of products:
IDataReader reader = SqlComm2.ExecuteReader();
List<Product> products = new List<Product>();
Product product = null;
while (reader.Read())
{
var name = reader["product"].ToString();
if (product == null || product.Name != name) // check if new product
{
product = new Product(); // create new product
product.Name = name; // fill name
product.OrderId = reader["order_id"].ToString(); // and order
products.Add(product); // add to products
}
object value = reader["display_value"]; // get value from row
switch (reader["detail"].ToString())
{
case "Quantity":
product.Quantity = Convert.ToInt16(value);
break;
case "Option":
product.Option = value.ToString();
break;
case "Size":
product.Size = value.ToString();
break;
}
}
As you can see, I also refactored naming - PascalCase for properties, camelCase for local variables, no Hungarian notation. Also new names for properties introduced - Product.Name instead of odd Product.Product, OrderId instead of sOrder.
Use the Modulus operator to check whether the iterating variable is divisible by expected nth value or not
if(value % 3 == 0)
{
//do stuff
}
value++;
You are repeatedly adding the same Product to your list, and never create a new one. When you get to the end of your loop, it'll appear as though you've only got a single item in there.
After you've added your item (within the if (iScanLine == 3)), I suspect you want to create a new item: Product = new cProducts().
Also, I would like to reference this particular comment that you make in your question:
If I create the object before the loop is entered, then my list just
contains the same result over and over again, because (I've read) that
the list only contains a reference to the object, and if the object is
then changed, so does the list.
The following code will result in 5 separate objects being added to a list:
List<cProducts> list = new List<cProducts>();
cProducts Product = new cProducts();
for (int i = 0; i < 5; i++)
{
list.Add(Product);
Product = new cProducts();
}
You are correct that the list only contains references to the objects - but you are not changing any of the objects; you are creating new ones. This is a fundamental programming principle, and I'd suggest you take the time to understand how it works before moving on.
Not sure if I understand completely but the following loop whould use a counter to execute every third time
int isThirdTime = 0; //Test condition for third time equality
while (true) //neverending loop
{
if (isThirdTime == 3)//test for 3rd time
{
// add to list
isThirdTime = 0; //reset counter
}
isThirdTime++; // Increase the counter
}
I have 2 lists, what are pairs:
List<int> timeStamp = new List<int>();
List<string> ownerStamp = new List<string>();
For example:
timeStamp' elements: 1, 10, 32 ...
ownerStamp' elements: John, Dave, Maria ...
The pairs are: John-1; Dave-10; Maria-32...
I have to order the timestamp list's elements, but i have to hold the context! How can i do it? Manually? Or smthng smart with factory ordering?
There is an overload of Array.Sort() method, which allow you sort one array using other array items as keys. You can convert your lists to arrays, then sort them, and then convert back:
List<int> timeStamp = new List<int>();
List<string> ownerStamp = new List<string>();
int[] timeStampArray = timeStamp.ToArray();
string[] ownerStampArray = ownerStamp.ToArray();
Array.Sort(timeStampArray, ownerStampArray);
timeStamp = new List<int>(timeStampArray);
ownerStamp = new List<string>(ownerStampArray);
You'd probably be better off making a container object, which contains both owner and timestamp, and make it comparable:
class Entry : IComparable<Entry> {
public int TimeStamp { get; set; }
public string Owner { get; set; }
public Entry(int timeStamp, string owner) {
this.TimeStamp = timeStamp;
this.Owner = owner;
}
public int CompareTo(Entry other) {
return this.TimeStamp.CompareTo(other.TimeStamp);
}
}
You could then make a list of these and sort it using the List<T> Sort() method.
To access the timestamp and owner, simply access the TimeStamp and Owner fields of the Entry.
Generally, if you want data to belong together, it's a good idea to explicitly group them together in an object or structure; then the grouping will happen automatically without you needing to take special care to keep things grouped.
Your question doesn't specify exactly what output you're expecting, but the following will give you a sorted collection of Tuple objects, whose first item is the time-stamp and second item is the owner.
timeStamp.Zip(ownerStamp, (i,s) => Tuple.Create(i,s)).OrderBy(t => t.Item1)
Best if you bring the pairs together into a 'SortedDictionary', which will automatically sort the pairs for you, or create a list of tuples for the timeStamp and ownerStamp, and then sort the elements in the tuple list based on the timeStamp key.
A sorted dictionary might help.