Unity storing an item in c# - c#

so I have 100 items that I need to store in some sort of array.
Each item has an att, def, cost, lvl, name and id(array key) value
What would be the best way to store them, keep in mind that i will need to sort the att and def values in descending order. While I have easily done this with php I am having some trouble with c#.
IF anyone could help provide a working example with just a couple of items that would be great thanks.
I am using unity and c#

Define a structure to store all the attribute:
class Enemy
{
public int Attack { get; set; }
public int Defend { get; set; }
public int Cost { get; set; }
public ....
}
Then store all in a list:
var enemies = new List<Enemy>();
...
You can sort the enemies by anything
var sortedEnemies = enemies.OrderBy(item => item.Attack).ToList();

Related

How can I list the keys in a nested dictionary?

I'm just starting to learn c# by creating my own program that is basically a damage calculator for the game Risk of Rain 2. As such, right now I am trying to create the user selection for a ability that a survivor has. I am currently using this format for how I store the ability and it's information:
Dictionary<string, Dictionary<string, double[]>> survivorAbilities =
new Dictionary<string, Dictionary<string, double[]>>();
Dictionary<string, double[]> acridAbilities =
new Dictionary<string, double[]>();
An example of some of the data being stored is:
//Epidemic: Special 1
double[] acridEpidemic = new double[3];
acridEpidemic[0] = (1); //instances
acridEpidemic[1] = (1); //damage %
acridEpidemic[2] = (1); //proc
acridAbilities.Add("epidemic", acridEpidemic);
//Initializing Acrid and his abilities
survivorAbilities.Add("acrid", acridAbilities);
The problem is that the survivor "Acrid" is only one of the four survivors that I decided to add. The other survivors have different dictionary names specific to the survivor. For example, instead of "acridAbilities", there is "commandoAbilities". I want to print each key in these dictionaries, but I'm not sure how to specify which survivor's dictionary to pick from, from the dictionary "survivorAbilities".
Sorry if its a bit confusing and long, not quite sure what to put.
This is not a direct answer to your question, but some advice since you're learning C# as well as an alternate approach.
A dictionary of nested dictionaries is, of course, perfectly valid code, but it can be confusing. Instead, why not create some classes to easily encapsulate your data? For example, you have this in your sample code:
acridEpidemic[0] = (1); //instances
acridEpidemic[1] = (1); //damage %
acridEpidemic[2] = (1); //proc
Notice you're using comments to explain what these keys in the dictionary are supposed to represent? This is not good; the code should be able to explain what it's doing without needing comments like these.
Here is an example, based on my understanding of your problem, of what I'd consider a better approach:
public void SomeExample()
{
var survivors = new List<Survivor>(); // all your survivors go in here
var acrid = new Survivor
{
Name = "Acrid",
};
acrid.Abilities.Add(new Ability
{
Name = "epidemic",
Instances = 1,
Damage = 1,
Proc = 1,
});
survivors.Add(acrid);
}
public class Survivor
{
public string Name { get; set; }
public List<Ability> Abilities { get; set; } = new List<Ability>();
}
public class Ability
{
public string Name { get; set; }
public double Instances { get; set; }
public double Damage { get; set; }
public double Proc { get; set; }
}

Fastest way of comparing two lists without using nested loops

I have two types:
public class SubCategories
{
public static List<SubCategories> subCategories = new List<SubCategories>();
public string title { get; set; }
public string IDfromCategories { get; set; }
public string subCategoryID { get; set; }
public bool isChecked { get; set; }
}
public class UserInsideCategories
{
public string userEmail { get; set; }
public string iDfromSubCategories { get; set; }
}
And two lists both containing this object multiple times.
Now I wanna go through a list with type SubCategories and check each object, if it contains the same value as my other list of type UserInsideCategories. Specifically, I wanna know if any object on the list.SubcategoryID is equal to any object on the other list.IdFromSubCateogires.
I achieved this like so:
List<SubCategories> branch = new List<SubCategories>();
for(int i = 0; i < subCategories.Count; i++)
{
SubCategories e = new SubCategories();
for(int x = 0; x < allSubs.Count; x++)
{
if (e.IDfromCategories == allSubs[x].iDfromSubCategories)
e.isChecked = true;
}
branch.Add(e);
}
So I am using a nested loop. But since I have to do this multiple times, it takes far too long.
I also thought about turning all values from SubCategories into a simple string array and use the Contains function, to see if the current object.IDfromCategories contains the object on the array. This would mean I would NOT use a for loop. But interenally, I believe, the system is still using a loop and therefore there would be no performance benefit.
What would be the best way of checking each object if it contains a value from the other list?
You should use some kind of lookup table. Probably either HashSet or Dictionary. The former only allows checking if a key exists in the set, while the later allows you to also find the object the key belongs to.
To check all the UserInsideCategories that shares an id with a SubCategories you could write:
var dict = subCategoriesList.ToDictionary(s => s.subCategoryID, s => s);
var matches = userInsideCategoriesList.Where(l => dict.ContainsKey(l.iDfromSubCategories));
if you want matching pairs you could write:
foreach (var user in userInsideCategoriesList)
{
if (dict.TryGetValue(user.iDfromSubCategories, out var subCategory))
{
// Handle matched pairs
}
}
This assumes that the ID is unique in respective list. If you have duplicates you would need something like a multi-value dictionary. There are no multi-value dictionary built in, but I would expect there are some implementations if you search around a bit.

Avoiding transposed parameters without creating coupling

I'm currently building a test application that manages parts for engineers, and I've hit a snag. I have a few different classes, including PartsModel and EngineerModel, and I want to update a list of parts that an engineer has, but I'm mindful of issues from either transposed parameters or from structuring the code in a way that unnecessarily couples to a particular class.
The two classes, with some relevant properties:
public class PartModel
{
public int PartId { get; private set; }
public string PartTitle { get; set; }
public string PartDescription { get; set; }
public int Quantity { get; set; }
public int MinimumStock { get; set; }
public void AddToStock (int quantityToAdd) {
Quantity += quantityToAdd;
}
public void RemoveFromStock (int quantityToRemove) {
Quantity -= quantityToRemove;
CheckMinimumStock();
}
}
public class EngineerModel
{
public int EngineerId { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PartModel> PartsInStock { get; set; } = Factory.CreatePartsList();
}
As you can see, each engineer has a list of parts they have in stock via a List<PartModel>. I want to pass another list to this one so that I can update it respectively (incrementing or decrementing quantities, and then adding or removing parts to the list as necessary).
The first warning bell is that it takes two inputs of the same type, and is going to fill one from the other one (which isn't needed afterwards), so you're essentially modifying one input and destroying the other. To me, this presents a danger of the inputs getting transposed and the wrong list being either returned or updated (depending on whether it returns or just acts on the list). Because it removes items that have no quantity, it can't check the list length and just update the longer one, because there are possible cases where the engineer's list is shorter (maybe they're a new engineer, or maybe they just had a large shipment of parts sent when they were running low on stock). If it did just keep parts with quantity zero, then you're threatening scalability of both engineers and parts (not to mention any other objects that use the same operation).
So, put it as a method in the EngineerModel class and operate on PartsInStock, right? But what about when I want to use the same operation on other classes (e.g. if I have a list of parts associated to a work task)? Then I extract the method out to another class and... I'm passing the two lists as parameters in the method, so I'm back to where I was.
Am I being reasonable in not wanting to have two parameters of the same type, and how do I structure the code to deal with this, but without creating unnecessary coupling? If I'm not being reasonable, what am I overlooking?
Use an extension method
Thanks to #DavidBrowne-Microsoft for clarifying this. By defining an extension method for List<PartModel>, it only needs the one parameter - the list containing the updates (foreach below based on #Valentin's answer to this question).
public static class PartsHandler
{
public static List<PartModel> UpdateStockQuantitiesWith(this List<PartModel> stockToBeUpdated, List<PartModel> stockUpdates) {
foreach ( var part in stockUpdates )
{
var partToBeUpdated = stockToBeUpdated.FirstOrDefault(x => x.PartId == part.PartId);
if ( partToBeUpdated != null )
{ partToBeUpdated.Quantity += part.Quantity; }
else
{ stockToBeUpdated.Add(part); }
}
stockToBeUpdated.RemoveAll(x => x.Quantity <= 0);
return stockToBeUpdated;
}
}
Now any class that needs to implement this can simply call it in a method on the respective property. For example, in the EngineerModel class, it can operate on the PartsInStock property:
public void AddPartsToStock(List<PartModel> partsSent) {
PartsInStock.UpdateStockQuantitiesWith(partsSent);
}

How to store an arbitrary number of arrays of an arbitrary type in a dictionary

I am trying to build a data structure that can store the trial by trail results of a group of different tests I am running. The test all consist of a number of trails but some of the information that I want to save and later use is different for the different tests. For example, the results of TestA might look like:
Trial(int) WasResponseCorrect(bool) WhichButtonWasPressed(string) SimulusLevel(double)
1 false "Up" 3.5
2 true "Left" 6.5
Where TestB might have different types of result fields:
Trial(int) WasResponseCorrect(bool) ColorPresented(string) LetterPresented(char) LetterGuessed(Char)
1 false green G C
2 false blue H F
I was thinking of creating a dictionary with the field names as the keys (ex. WasResponseCorrect) and an array of the field values as the values of dic. I can't figure out how to do that. Maybe there is a better way to store the information but I can't think of how to do it. I am working with .net (VB and C#) but I think I can understand and convert most any code if you know of examples in other languages. Thanks!
Without knowing more about your requirements (how you are going to store the data, for example), it seems like polymorphism is what you're looking for. That is, you have a superclass (called Trial) and subclasses that represent the specific trial types. For example:
public class Trial {
public int Id { get; set; }
public bool WasResponseCorrect { get; set; } // if this is in every type of trial
// anything else that is common to ALL trial types
}
public class TrialA : Trial {
public string WhichButtonWasPressed { get; set; }
public double SimulusLevel { get; set; }
}
public class TrialB : Trial {
public string ColorPresented { get; set; }
public char LetterPresented { get; set; }
public char LetterGuessed { get; set; }
}
That way you can have a list of Trial objects, but the actual runtime type of those objects can be TrialA or TrialB.

Class Design Question: Union of List<ChildStat> and AllStats

I have a Player class and a Stat class. The Player class has a List property where PlayerStat has a List and XP properties. I think my design is flawed because I am having trouble doing things that I think should be easy. I want to list out all Players and their XP for all Stats. Below are more details of the classes and the GetCompletePlayerStats method which is what I really don't like. I basically need to list out the XP for all stats for a Player, if the player doesn't have a stat then it should have an XP of zero. Any design help/suggestions would be appreciated.
public class Stat : EntityBase{
public virtual string Name { get; set; }
public virtual UnitOfMeasure Unit { get; set; }
public virtual int UnitXP { get; set; }
}
public class Player : EntityBase{
public virtual string Name { get; set; }
public virtual IList<PlayerStat> PlayerStats { get; set; }
public virtual List<PlayerStat> GetCompletePlayerStats(IQueryable<Stat> stats)
{
var allStats = new List<PlayerStat>();
var playerStatIds = PlayerStats.Select(ps => ps.PlayerStatistic.Id).ToList();
if (playerStatIds.Count == 0)
{
allStats.AddRange(stats.Select(stat => new PlayerStat() {PlayerStatistic = stat, XP = 0}));
}
else
{
var zeroStats = stats.Where(s => !playerStatIds.Contains(s.Id)).ToList();
allStats.AddRange(zeroStats.Select(zeroStat => new PlayerStat() {PlayerStatistic = zeroStat, XP = 0}));
allStats.AddRange(PlayerStats);
}
return allStats;
}
}
public class PlayerStat : EntityBase{
public virtual Stat PlayerStatistic { get; set; }
public virtual double XP { get; set; }
}
I have to admit, I dont really see a major drawback in your class design so far. Of course I dont have any insight in the greater picture and how your game is designed, since this is only a little section of it.
However, you said it is the GetCompletePlayerStats that you dont really like. I had to read it several times to understand what you are trying to do here. If I saw that right, you just want to return a PlayerStat object corresponding to each given Stat object. I guess Stat has an Id field (you are using it in your method) to compare two of them for semantic equality.
Given, that I made the right assumptions so far (unfortunately, you didnt provide much info), the method can be simplified to something like:
public virtual IEnumerable<PlayerStat> GetCompletePlayerStats(IEnumerable<Stat> stats)
{
foreach(Stat stat in stats)
yield return PlayerStats.FirstOrDefault(s => stat.Id == s.PlayerStatistic.Id) ??
new PlayerStat() {PlayerStatistic = stat, XP = 0};
yield break;
}
This method here doesnt require a IQueryable but rather a IEnumerable to iterate over via a foreach loop, and yield out the corresponding PlayerStats object if there is one, or create a new one with XP set to 0 otherwise (The null-coalescing operator ?? is very useful in those cases).
Hope that helps!
With the existing design, this method can be simplified thus:
public virtual IList<PlayerStat> GetCompletePlayerStats(IEnumerable<Stat> stats)
{
// build a dictionary of existing stats by ID to facilitate
// the join with requested stats (effectively doing a hash join)
var playerStatsById = PlayerStats.ToDictionary(ps => ps.PlayerStatistic.Id);
// for each requested stat, return either the corresponding player stat
// or a zero stat if one isn't found, maintaining the original order of stats
return stats
.Select(s => playerStatsById.ContainsKey(s.Id) ?
playerStatsById[s.Id] :
new PlayerStat { PlayerStatistic = s, XP = 0 })
.ToList();
}
Note that since this is effectively an outer-join operation (you're joining stats with PlayerStats but you also need the non-matches to yield as zero) you can also do it with LINQ's GroupJoin() operation, although I suspect that would be much less readable.
What does bother me about your design is the ambiguity of names, for example you have both the PlayerStat class and the PlayerStatistic property and they mean subtly different things; consider naming things differently and things might look better; in fact that's probably the best design advice I can give you, for any situation :)

Categories