I have to make a function that returns the spells a character has.
So I have 3 parameters: a level (an int from 1-50), an enum representing a character class (only 8 classes) and an enum representing a race (3-4 races).
This function has to return an array with spell IDs. The higher the level, the more spells a character has.
What I did so far is hard-coded everything, but when I have to modify something it is a mess. I don't know what kind of data-structure best suits my needs without recurring to horrible ifs that are hard to change/mantain.
Also, the language is C# and I am using Xna/.NET 4.0
Update
public static int[] ListOfSpells(int level, CharacterClass chClass, CharacterRace chRace)
{
switch (chClass)
{
case CharacterClass.Mage:
return new int[] { 1, 2, 3 };
case CharacterClass.Knight:
return new int[] { 2, 5, 6 };
case CharacterClass.Paladin:
return new int[] { 3, 5, 6, 2 };
default:
return new int[] { };
}
}
// classes
public enum CharacterClass : short
{
Mage = 0x00,
Warlock,
Priest,
Monk,
Knight,
Assassin,
Paladin,
Hunter,
Warrior
}
// races
public enum CharacterRace : short
{
Human = 0x00,
Elf
}
A database if definitly what you need. You can maybe make it works with IEnumerable/Array now, but database will gave you the flexibility that you want for the future of your game.
I don't know what kind of data-structure best suits my needs without recurring to horrible
ifs that are hard to change/mantain.
A simple trivial array, alternativels a table in a database.
Fields:
Spell ID
Character
Class
Level
Description blablabla...
Then filter by matching condition.
And I suggest NOT returning an array - that is useless. make the method IEnumerable, then you CAN ToArray() it if needed, or just run along it.
Related
I'm struggling a bit to understand the iteration piece here of how I would check if if the entity variable below has all of the Enum.Component entries in the variable comps. I can achieve this relatively straight-forwardly if I know there will be only one Component via a .ForEach and a basic comparison (e.g. entity.ForEach(comp => Console.WriteLine(comp.COMPONENT == Enum.Component.EXPERIENCE));), but not if I need to check for multiple components.
I'm trying to understand the nuances of C# a bit better, so I don't want to brute force this with an actual foreach (in the conventional foreach(var x in exes) type of way) or something similar, but really want to understand how I would implement this via these objects through these IEnumerable functions and working with lambda expressions. As such, I need an answer utilizing these things, unless of course this is not technically possible, though probably it is, I'm guessing.
// The Component.IComponent Interface (it's in the Component namespace)
interface IComponent {
Enum.Component COMPONENT {
get;
}
}
// The Enum.Component (it's in the Enum namespace)
enum Component {
EXPERIENCE,
HEALTH
}
// The Component.Experience (it's in the Component namespace)
class Experience : IComponent {
public ThresholdValue xp;
public int level;
public Enum.Component COMPONENT {
get {
return Enum.Component.EXPERIENCE;
}
}
}
// It probably doesn't matter, but ENTITY_MANAGER is this type
Dictionary<Guid, List<Component.IComponent>>
// Trial code beings here:
Guid GUID = new Guid();
ENTITY_MANAGER.getEntities().Add(GUID, new List<Component.IComponent> { new Component.Experience(50, 3), new Component.Health(20, 25) });
List<Component.IComponent> entity = ENTITY_MANAGER.getEntities()[new Guid()];
Enum.Component[] comps = new Enum.Component[] {
Enum.Component.EXPERIENCE,
Enum.Component.HEALTH
};
// This is where I don't know what to do and know this is wrong
comps.All(v => entity.ForEach(comp => Console.WriteLine(comp.COMPONENT == v)));
You can easily do this via Flags!
https://msdn.microsoft.com/en-us/library/system.flagsattribute(v=vs.110).aspx
First do this with your Enum:
[Flags]
enum Component {
None = 0,
EXPERIENCE = 1 << 0,
HEALTH = 1 << 1,
All = (1 << 2) - 1
}
This basically will store your values as powers of 2, with 'All' being the sum of all your flags, in this case Exp and Hp are 1 and 2, so All is 3 (1+2)
Now you can just do this in your entity class:
public Enum.Component Flags => comps.Select(c => c.Component).Distinct().Sum();
public bool HasAllFlags => Flags == Enum.Component.All;
We make our enum all distinct base 2, with all the next step -1, which means All is the sum of all your enum listings.
Then we just sum up the Enums (We might have to convert to an int first then back to the enum, I don't remember if you can just add Enums together in C#) and check if they == Component.All.
There you go!
This question already has answers here:
Equivalent of typedef in C#
(13 answers)
Closed 5 years ago.
I have searched thoroughly as best as i could for an answer to this question. I might not be formulating it correctly due to the lack of my experience with C#, but here goes.
Say i have a list type
List<List<int>> teams;
And wish to use this type in multiple classes, with different fieldnames, without defining its type over and over again. Something like this:
List<TeamType> teams;
List<TeamType> winningTeams;
List<TeamType> losingTeams;
How would i accomplish this?
I have been looking into interfaces, but im not sure if/how they can help tackle this.
Thanks in advance.
I think I understand what it is you mean, you keep having to define
List<List<int>> variable
and want to define a List<int> as a type in order to avoid typing that declaration over and over again.
I would personally suggest creating a class called Team that has a List<int> attribute, then instead of typing List<List<int>> every time to define a set of teams you can just type List<Team> instead. This makes more sense.
Although, I don't understand how a List<int> would represent a team, but as long as it makes sense within the context of your code, this will work fine.
It also will allow you to expand the functionality of your class the further you go in development. You could include attributes in your class definition to represent win-loss numbers, people involved with the team (coaches/managers/owners), etc.
From an object oriented design perspective, winningTeams and losingTeams are aggregates over data rather than the data itself.
To find this information there needs to be a data structure to represent a team and its metrics.
public class Team
{
public int TeamNumber { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
}
Then ask questions about the data to create statistics:
var teams = new List<Team>
{
new Team { TeamNumber = 1, Wins = 5, Losses = 3 },
new Team { TeamNumber = 2, Wins = 1, Losses = 5 },
new Team { TeamNumber = 3, Wins = 1, Losses = 1 }
};
Who are the winning teams? Where Wins > Loses
var winningTeams = teams.Where(team => team.Wins > team.Loses).ToList();
// TeamNumber = 1
Who are the losing teams? Where Loses >= Wins
var losingTeams = teams.Where(team => team.Loses >= Wins).ToList();
// TeamNumber = 2, 3
If you just want something that acts like a typedef for some generic List<T>, you can create a simple derived class like so:
public class TeamType : List<int> { }
and then use it anywhere you would have used the generic type:
List<TeamType> teams;
List<TeamType> winningTeams;
List<TeamType> losingTeams;
Issue:
enum's referenced by gameobject scripts via inspector variables become out of order when the enum has new entries added before the referenced index.
Details:
So I have multiple systems such as item lists, localisation strings, etc which are dynamically built by parsing external files. This parsing creates enum's which are used to reference the items by gameobject's via script inspector variables. Here's the parsed output by my localisation system as an example:
public enum LocaleID
{
LocalisedStrings_ENGB,
LocalisedStrings_ENUS,
//...
MAX,
}
public enum StringID
{
String_EMPTY,
String_Inventory,
String_Recipes,
String_Tools,
String_Journal,
//...
}
public static class LocalisedStrings
{
private static string[] SCLocalisedStrings_ENGB =
{
"",
"Inventory",
"Recipes",
"Tools",
"Journal",
//...
}
private static LocaleID currentLocale = (LocaleID)0;
private static string[] activeSC = SCLocalisedStrings_ENGB;
public static void SetLocale(LocaleID newLocale)
{
currentLocale = newLocale;
switch(newLocale)
{
case LocaleID.LocalisedStrings_ENGB:
activeSC = SCLocalisedStrings_ENGB;
break;
case LocaleID.LocalisedStrings_ENUS:
activeSC = SCLocalisedStrings_ENUS;
break;
}
}
//entry interface:
public static string Get(StringID stringID)
{
return activeSC[(int)stringID];
}
}
This simply returns the string via the enum index based off the set locale.
So I'd have something such as a name of an NPC exposed on a character as:
[SerializeField]
public StringID SpeakerTitle;
and set that through the inspector.
The issue is a rather obvious and expected one - if the enum is parsed differently, for example an extra entry is added to the top (for example for sorting purposes) or removed (for cleaning up obsolete entries), then all referenced enum's will become out of order by 1 spot since they'll be referencing the index of the enum entry.
A simple solution would be to impose a rule of only adding to the end and never removing entries that become stale. This becomes quite wasteful, so is obviously not very preferable.
What are suggested solutions to this issue? Any examples of how others approach this rather common situation? My preference would of course be something which could be sorted and new entries added anywhere, but we can't have everything we want :)
Just specify explicit numeric values for the entries:
public enum StringID
{
String_EMPTY = 0,
String_Inventory = 1,
String_Recipes = 2,
String_Tools = 3,
String_Journal = 4,
//...
}
That way the ordering is entirely irrelevant to the values. Note that you can do this retrospectively, or in a "just in time" way when you need to make what would otherwise be a breaking change.
(I'd personally get rid of the String_ prefix as well, but that's a different matter.)
I would use an explicit map rather than a naked array; for example:
private static Dictionary<StringID,string> SCLocalisedStrings_ENGB =
new Dictionary<StringID,string>
{
{StringID.String_EMPTY, ""},
{StringID.String_Inventory, "Inventory"},
//...
};
and get the value via something like:
string val;
return LocaleStrings.TryGetValue(key, out val) ? val : DefaultStrings[key];
Well... actually, I'd probably have the translations in external files as key/value pairs, but... meh.
Note: you could always put the dictionary data back into an ordered array; but having the explicit map prevents order from mattering.
I have to take a piece of data, and apply a large number of possible variables to it. I really don't like the idea of using a gigantic set of if statements, so i'm looking for help in an approach to simplify, and make it easier to maintain.
As an example:
if (isSoccer)
val = soccerBaseVal;
else if (isFootball)
val = footballBaseVal;
.... // 20 different sports
if (isMale)
val += 1;
else
val += 5;
switch(dayOfWeek)
{
case DayOfWeek.Monday:
val += 12;
...
}
etc.. etc.. etc.. with possibly in the range of 100-200 different tests and formula variations.
This just seems like a maintenance nightmare. Any suggestions?
EDIT:
To further add to the problem, many variables are only used in certain situations, so it's more than just a fixed set of logic with different values. The logic itself has to change based on conditions, possibly conditions applied from previous variables (if val > threshold, for instance).
So yes, i agree about using lookups for many of the values, but I also have to have variable logic.
A common way to avoid large switching structures is to put the information into data structures. Create an enumeration SportType and a Dictionary<SportType, Int32> containing the associated values. The you can simply write val += sportTypeScoreMap[sportType] and you are done.
Variations of this pattern will help you in many similar situations.
public enum SportType
{
Soccer, Football, ...
}
public sealed class Foo
{
private static readonly IDictionary<SportType, Int32> sportTypeScoreMap =
new Dictionary<SportType, Int32>
{
{ Soccer, 30 },
{ Football, 20 },
...
}
private static readonly IDictionary<DayOfWeek, Int32> dayOfWeekScoreMap =
new Dictionary<DayOfWeek, Int32>
{
{ DayOfWeek.Monday, 12 },
{ DayOfWeek.Tuesday, 20 },
...
}
public Int32 GetScore(SportType sportType, DayOfWeek dayOfWeek)
{
return Foo.sportTypeScoreMap[sportType]
+ Foo.dayOfWeekScoreMap[dayOfWeek];
}
}
Use either a switch statement or filter function.
By filter function, I mean something like:
func filter(var object, var value)
{
if(object == value)
object = valueDictionary['value'];
}
Then apply the filter with:
filter(theObject, soccer)
filter(theObject, football)
Note that the filter works much better using a dictionary, but it is not required.
Cribbing from The Pragmatic Programmer, you could use a DSL to encapsulate the rules and write a process engine. For your presented problem, a solution might look like:
MATCH{
Soccer soccerBaseVal
IsMale 5
!IsMale 1
}
SWITCH{
Monday 12
Tuesday 13
}
Then match everything in the first col of MATCH, and the first item in each SWITCH you come to. You can make whatever syntax you feel like, then just write a bit of script to cram that into code (or use Xtext because it looks pretty cool).
Here are a few ideas:
1 Use lookup tables:
var val = 0;
SportType sportType = GetSportType();
val += sportvalues[sportType];
You can load the table from the database.
2 Use the factory pattern:
var val = 0;
val += SportFactory.Create(sportType).CalculateValue();
The Dynamic Factory Pattern is useful in situations were new (sport) types are added frequently to the code. This pattern uses reflection to prevent the factory class (or any global configuration) from being changed. It allows you to simply add a new class to your code.
Of course the use of an dynamic factory, or even a factory can be overkill in your situation. You're the only one who can tell.
As a first step I would probably break up each logical processing area into its own method: (May not be the best names as a first pass)
EnforceSportRules
ProcessSportDetails
EnforceGenderRules
Next, depending on how complex the rules are, I may break each section into its own class and have them processed by a main class (like a factory).
GenderRules
GenderContext
I have nothing special to offer you than to first recommend not to just leave it as a big block-- break it into sections, make comment dividers between important parts.
Another suggestion is if you are going to have many very short tests as in your example, break from convention and put the val incrementors on the same line as the evaluatation and indent so they align with eachother.
if (isSoccer) val = soccerBaseVal;
if (isMale) val += 1;
else val += 5;
switch(dayOfWeek){
case DayOfWeek.Monday: val += 12;
...
}
Excess whitespace can make those hundred things into several hundred lines, making vertical scrolling excessive and difficult to get an overall view of the thing.
If you are really just adding values in this sort, I would either create an enumeration with defined indices that correspond to stored values in an array. Then you can do something like this:
enum Sport
{
football = 0,
soccer = 1,
//...
}
int sportValues[] = {
/* footballValue */,
/* soccerValue */,
/* ...Values */
};
int ApplyRules(Sport sport, /* other params */)
{
int value = startingValue;
value += sportValues[(int)sport];
value += /* other rules in same fashion */;
}
Consider implementing the Strategy Pattern which utilizes inheritance/polymorphism to make managing individual functions sane. By seperating each function into its own dedicated class you can forego the nightmare of having miles-long case blocks or if statements.
Not sure if C# supports it yet (or ever will) but VB.NET integrates XML Comment CompletionList directives into intellisense, which--when combined with the Strategy Pattern--can give you the ease of use of an Enum with the open-ended extensibility of OO.
say I have the following declarations:
public enum Complexity { Low = 0, Normal = 1, Medium = 2, High = 3 }
public enum Priority { Normal = 1, Medium = 2, High = 3, Urgent = 4 }
and I want to code it so that I could get the enum value (not the index, like I earlier mentioned):
//should store the value of the Complexity enum member Normal, which is 1
int complexityValueToStore = EnumHelper.GetEnumMemberValue(Complexity.Normal);
//should store the value 4
int priorityValueToStore = EnumHelper.GetEnumMemberValue(Priority.Urgent);
How should this reusable function look like?
tia!
-ren
Revised answer (after question clarification)
No, there's nothing cleaner than a cast. It's more informative than a method call, cheaper, shorter etc. It's about as low impact as you could possibly hope for.
Note that if you wanted to write a generic method to do the conversion, you'd have to specify what to convert it to as well: the enum could be based on byte or long for example. By putting in the cast, you explicitly say what you want to convert it to, and it just does it.
Original answer
What do you mean by "index" exactly? Do you mean the numeric value? Just cast to int. If you mean "position within enum" you'd have to make sure the values are in numeric order (as that's what Enum.GetValues gives - not the declaration order), and then do:
public static int GetEnumMemberIndex<T>(T element)
where T : struct
{
T[] values = (T[]) Enum.GetValues(typeof(T));
return Array.IndexOf(values, element);
}
You can find the integer value of an enum by casting:
int complexityValueToStore = (int)Complexity.Normal;
The most generic way I know of is to read the value__ field using reflection.
This approach makes no assumptions about the enum's underlying type so it will work on enums that aren't based on Int32.
public static object GetValue(Enum e)
{
return e.GetType().GetField("value__").GetValue(e);
}
Debug.Assert(Equals(GetValue(DayOfWeek.Wednesday), 3)); //Int32
Debug.Assert(Equals(GetValue(AceFlags.InheritOnly), (byte) 8)); //Byte
Debug.Assert(Equals(GetValue(IOControlCode.ReceiveAll), 2550136833L)); //Int64
Note: I have only tested this with the Microsoft C# compiler. It's a shame there doesn't appear to be a built in way of doing this.
I realize this isn't what you asked, but it's something you might appreciate.
I discovered that you can find the integer value of an enum without a cast, if you know what the enum's minimum value is:
public enum Complexity { Low = 0, Normal = 1, Medium = 2, High = 3 }
int valueOfHigh = Complexity.High - Complexity.Low;
This wouldn't work with Priority, unless you added some minimal value of 0, or added 1 back:
public enum Priority { Normal = 1, Medium = 2, High = 3, Urgent = 4 }
int valueOfUrgent = Priority.Urgent - Priority.Normal + 1;
I find this technique much more aesthetically appealing than casting to int.
I'm not sure off the top of my head what happens if you have an enum based on byte or long -- I suspect that you'd get byte or long difference values.
If you want the value, you can just cast the enum to int. That would set complexityValueToStore == 1 and priorityValueToStore == 4.
If you want to get the index (ie: Priority.Urgent == 3), you could use Enum.GetValues, then just find the index of your current enum in that list. However, the ordering of the enum in the list returned may not be the same as in your code.
However, the second option kind of defeats the purpose of Enum in the first place - you're trying to have discrete values instead of lists and indices. I'd rethink your needs if that is what you want.
This is the most simple way to solve your problem:
public static void GetEnumMemberValue<T>(T enumItem) where T : struct
{
return (int) Enum.Parse(typeof(T), enumItem.ToString());
}
It works for me.