Confusion over using abstract class in c# - c#

I have a question since I'm new in programming Im really confused about how to implement a best practice solution for the following problem, Its a game logic,here are the possible ways of making points
EnemyA 400,
EnemyB 500,
EnemyC 700,
Coin 200,
FireBall 300
means hitting Coin gives you 200 points and shooting FireBall gives you 300 &...
if you reach 1000 points you will get an extra life, the logic simple but implementing the best practice is not(at least to me)should I use abstract class or Dictionary?
So far, I used a Dictionary, I read a given file (txt) file which is written (EnemyA,EnemyB,Coin,Coin,Coin,Coin) then I calculate the points:
public int pointCal(IEnumerable<string> enemyType)
{
var possiblePoints = new Dictionary< EnemisEntity,int>()
{
{new EnemisEntity{enemyType="EnemyA"},400 },
{new EnemisEntity{enemyType="EnemyB" },500 },
{new EnemisEntity{enemyType="EnemyC"},700 },
{new EnemisEntity{enemyType="Fireball"},300 },
{new EnemisEntity{enemyType="Coin"},200 },
};
int z=0;
List<int> myPoints=new List<int> ();
foreach (var item in enemyType)
{
z = possiblePoints.FirstOrDefault(f => f.Key.enemyType.Equals(item)).Value;
myPoints.Add(z);
}
int finalPonts= g.Sum(s=>Convert.ToInt32(s));
return finalPonts;
}
Enemy entity class:
public class EnemisEntity
{
public string enemyType { get; set; }
}

It depends. Are the enemies different types of objects (with different properties and such)?
Then it might make sense to create an abstract class and child classes.
public abstract class Shootable {
public int points;
}
public class EnemyA: Shootable {
}
public class EnemyB: Shootable {
}
public class Coin: Shootable {
}
// etc
If all your items are just shootable with one description, then
public class Shootable {
public int points { get; set; }
public string enemyType { get; set; }
public Shootable(int points, string enemyType ){
this.points = points;
this.enemyType = enemyType;
}
}
// then create like
var coin = new Shootable(500, "coin");
If all enemies can be modeled in the same class, then you only need shootable class
Then get the points:
IEnumerble<Shootable> shootableItems = GetShootableFromFile();
var score = shootableItems.Sum(s => s.Points);
You GetShootableFromFile should create one object per file row. So it is a viable situation to create the same objects twice:
// This is a mock to indicate multiple instances of the same class.
public IEnumerble<Shootable> GetShootableFromFile() {
List<Shootable> shootable = new List<Shootable>();
shootable.Add(new Shootable(500,"coin"));
shootable.Add(new Shootable(500,"coin"));
shootable.Add(new Shootable(500,"coin"));
shootable.Add(new Shootable(300,"enemyA"));
shootable.Add(new Shootable(300,"enemyB"));
// etc
}

To me, this is a question of design. When the only difference between enemies is just the value of points as well as their name it is not a good idea to define a class hierarchy. The only thing different between a EnemyA and a EnemyB class would be just the values contained within each class. So you can use a single common class to hold information for each enemy and process the points.
Below is the simplest working prototype that I could code that implements this design. It relies on two classes. The EnemyEntity class to hold the type of enemy and its points, and a Game class that contains the logic behind scoring and keeping a record of all possible enemies.
public class EnemyEntity
{
public EnemyEntity(string type, int points)
{
Type=type;
Points=points;
}
public string Type { get; }
public int Points { get; }
}
public class Game
{
public Game(params (string type, int points)[] values)
{
this.Enemies = new List<EnemyEntity>();
foreach (var (type, points) in values)
{
Enemies.Add(new EnemyEntity(type, points));
}
}
public List<EnemyEntity> Enemies { get; }
public int CalculatePoints(IEnumerable<string> targets)
{
int points = 0;
foreach (var item in targets)
{
var target = Enemies.FirstOrDefault((enemy) => enemy.Type.Equals(item));
if (target!=null)
{
points+= target.Points;
}
}
return points;
}
}
class Program
{
static void Main(string[] args)
{
var game = new Game(
("EnemyA", 400),
("EnemyB", 500),
("EnemyC", 700),
("Coin", 200),
("FireBall", 300));
var input = "EnemyA,EnemyB,Coin,Coin,Coin,Coin";
var targets = input.Split(',');
var points = game.CalculatePoints(targets);
Console.WriteLine(points);
// 1700
}
}
NOTES:
The simplest approach is to use a List<EnemyEntity> and do the lookup with .FirstOrDefault(). I could use a Dictionary<string,EnemyEntity> which would simplify the lookup process. Here is how the Game class would change using a dictionary.
public class Game
{
public Game(params (string type, int points)[] values)
{
this.Enemies = new Dictionary<string, EnemyEntity>();
foreach (var (type, points) in values)
{
Enemies[type] = new EnemyEntity(type, points);
}
}
public Dictionary<string, EnemyEntity> Enemies { get; }
public int CalculatePoints(IEnumerable<string> targets)
{
int points = 0;
foreach (var item in targets)
{
var target = Enemies[item];
if (target!=null)
{
points+= target.Points;
}
}
return points;
}
}

Related

What is an efficient way to store and reference hundreds of values?

In my game, I have many different types of units that are instantiated/destroyed with regularity. They have certain values, like MaxHP or Level or Defense, which need to be referenced at instantiation, and may vary based on certain factors. For example, MaxHP may be higher if Level increases, but not based on a strict formula, which varies by unit. So, for example:
Soldier Level 1 Max HP: 5
Soldier Level 2 Max HP: 6
Soldier Level 3 Max HP: 8
Mage Level 1 Max HP: 3
Mage Level 2 Max HP: 4
Mage Level 3 Max HP: 5
The class name would be easy to reference, and the level of the units would be stored elsewhere, and I'd need to know which trait I'm looking up to look it up in the first place. Therefore, what makes sense to me is to store these as key/value pairs. I can programmatically check something like (className + lvlString + traitName) as the key, so the key would end up being Mage2MaxHP and the value would be 4.
Usually my instincts in this regard are pretty bad since I never learned much about data structures. So, I'm wondering if there's a better way to achieve this and organize this amount of data. For example, it seems like this would be incredibly large for a dictionary. Perhaps it would be more manageable split into several dictionaries (one for MaxHP, one for Defense, etc.), but I still feel like I'm overlooking a data structure more suitable for this purpose. Any recommendations?
The following would approach your issue using both inheritance and Dictionaries in order to avoid having to class-name-strings etc.
public abstract class Unit
{
// This approach would use one Dictionary per Trait
protected abstract Dictionary<int, int> MaxHpByLevel { get; }
public int Level { get; set; } = 1;
public int MaxHp => this.MaxHpByLevel[this.Level];
}
public class Soldier : Unit
{
protected override Dictionary<int, int> MaxHpByLevel => new Dictionary<int, int>
{
[1] = 5,
[2] = 6,
[3] = 8
};
}
public class Mage : Unit
{
protected override Dictionary<int, int> MaxHpByLevel => new Dictionary<int, int>
{
[1] = 3,
[2] = 4,
[3] = 5
};
}
You would use it like that:
var soldier = new Soldier { Level = 2 };
Console.WriteLine(soldier.MaxHp); // 6
Just like you, I also think one could solve this issue in different ways.
What I like about this particular approach is that it is based on OOP-Principles and that it reduces redundant structural elements (e.g. avoids enums for trait-types etc.). You can access the properties of a unit over - well - Properties of a unit.
The Dictionaries used here would be rather small.
However, I agree with the folks from the comments/answers when it comes to the size-restrictions. No matter what approach you choose, you will likely not bring your dictionaries any close to its limits.
"Hundreds" really isn't much in terms of a modern CPU, a Dictionary adds a lot of complexity and ties you to one access pattern. Often it's best to start simple: with a List<T> and figure out what your access patterns will be. You can use LINQ against it to query by class name, hp, level or any combination thereof. Once you have it working, if you find that you need to boost performance for some queries, then, at that time go ahead and refactor it maybe using a Dictionary as an index.
But never underestimate the power of a modern compiler and a modern CPU to iterate over a contiguous block of memory blindingly fast beating many other data structures than might offer a better theoretical performance for very large values of N.
I usually use code like below :
public class Unit
{
public static Dictionary<string, Unit> units { get; set; } // string is unit name
public string name { get; set; }
public int level { get; set; }
public Dictionary<string, int> attributes { get; set; }
}
You might want to have the classes and attributes as enums, which I'd expect to come in handy in a lot of other places, too. Something like:
public enum Class
{
Soldier,
Mage,
/*...*/
}
public enum Attribute
{
Health,
Magic,
/*...*/
}
And then combine them to have a key for a dictionary that should be a bit more efficient and "useful" than just a concatenated string. Something like:
public struct AttributeKey : IEquatable<AttributeKey>
{
public AttributeKey(Class #class, Attribute attribute, int level)
{
Class = #class;
Attribute = attribute;
Level = level;
}
public readonly Class Class;
public readonly Attribute Attribute;
public readonly int Level;
public bool Equals(AttributeKey other)
{
return Class == other.Class && Attribute == other.Attribute && Level == other.Level;
}
public override bool Equals(object obj)
{
return obj is AttributeKey && Equals((AttributeKey)obj);
}
public override int GetHashCode()
{
unchecked
{
return (((Class.GetHashCode() * 397) ^ Attribute.GetHashCode()) * 397) ^ Level;
}
}
}
Most important for the use as a key in a dictionary is to be very deliberate with the Equals and especially the GetHashCode methods.
See for example Why is it important to override GetHashCode when Equals method is overridden? and MSDN for further information about that.
There is no particular reason why this is better than any other approach. However you could use a class with an Indexer that has an internal Dictionary with a Tuple key
Note : Ideally you would want to store and load this stuff from db, or a file, or maybe just generate it dynamically if there is
not too much. However, depending on the nature of the game, this maybe
a novel approach.
Demo here
Main
private static readonly CharLookup _lookup = new CharLookup();
private static void Main()
{
_lookup[CharacterClass.Mage, CharacterTrait.SingingAbility, 2] = 123;
_lookup[CharacterClass.Mage, CharacterTrait.SingingAbility, 3] = 234;
_lookup[CharacterClass.Soilder, CharacterTrait.MaxBeers, 3] = 23423;
Console.WriteLine("Mage,SingingAbility,2 = " + _lookup[CharacterClass.Mage, CharacterTrait.SingingAbility, 2]);
Console.WriteLine("Soilder,MaxBeers,3 = " + _lookup[CharacterClass.Soilder, CharacterTrait.MaxBeers, 3]);
}
Enums
public enum CharacterClass
{
Soilder,
Mage,
SmellyCoder
}
public enum CharacterTrait
{
MaxHp,
MaxBeers,
SingingAbility
}
CharLookup
public class CharLookup
{
private Dictionary<Tuple<CharacterClass, CharacterTrait, int>, int> myDict = new Dictionary<Tuple<CharacterClass, CharacterTrait, int>, int>();
public int this[CharacterClass characterClass, CharacterTrait characterTrait, int level]
{
get => Check(characterClass, characterTrait, level);
set => Add(characterClass, characterTrait, level, value);
}
public void Add(CharacterClass characterClass, CharacterTrait characterTrait, int level, int value)
{
var key = new Tuple<CharacterClass, CharacterTrait, int>(characterClass, characterTrait, level);
if (myDict.ContainsKey(key))
myDict[key] = value;
else
myDict.Add(key, value);
}
public int Check(CharacterClass characterClass, CharacterTrait characterTrait, int level)
{
var key = new Tuple<CharacterClass, CharacterTrait, int>(characterClass, characterTrait, level);
if (myDict.TryGetValue(key, out var result))
return result;
throw new ArgumentOutOfRangeException("blah");
}
}
This is still storing in a dictionary but includes simple enums and methods to store and retreive the data.
public enum UnitType
{
Soldier,
Mage
}
public enum StatType
{
MaxHP,
MaxMP,
Attack,
Defense
}
// Where the unit initialisation data is stored
public static class UnitData
{
private static Dictionary<string, Dictionary<StatType, int>> Data = new Dictionary<UnitType, Dictionary<StatType, int>>();
private static string GetKey(UnitType unitType, int level)
{
return $"{unitType}:{level}";
}
public static AddUnit(UnitType unitType, int level, int maxHP, int maxMP, int attack, int defense)
{
Data.Add(GetKey(unitType, level),
new Dictionary<StatType, int>
{
{ StatType.MaxHP, maxHP },
{ StatType.MaxMP, maxMP },
{ StatType.Attack, attack },
{ StatType.Defense, defense }
});
}
public static int GetStat(UnitType unitType, int level, StatType statType)
{
return Data[GetKet(unitType, level][statType];
}
}
// The data is not stored against the unit but referenced from UnitData
public class Unit
{
public UnitType UnitType { get; private set; }
public int Level { get; private set; }
public Unit(UnitType unitType, int level)
{
UnitType = unitTypel
Level = level;
}
public int GetStat(StatType statType)
{
return UnitData.GetStat(UnitType, Level, statType);
}
}
// To initialise the data
public class StartClass
{
public void InitialiseData()
{
UnitData.Add(UnitType.Soldier, 1, 5, 0, 1, 1);
UnitData.Add(UnitType.Soldier, 2, 6, 0, 2, 2);
UnitData.Add(UnitType.Soldier, 3, 8, 0, 3, 3);
UnitData.Add(UnitType.Mage, 1, 3, 10, 1, 1);
UnitData.Add(UnitType.Mage, 2, 4, 15, 2, 2);
UnitData.Add(UnitType.Mage, 3, 5, 20, 3, 3);
}
}
// Use of units
public class Level1
{
public List<Unit> Units = new List<Unit>();
public void InitialiseUnits()
{
Units.Add(new Unit(UnitType.Soldier, 1));
Units.Add(new Unit(UnitType.Soldier, 1));
Units.Add(new Unit(UnitType.Mage, 1));
Units.Add(new Unit(UnitType.Mage, 1));
}
public void Something()
{
int maxHP = Units.First().GetStat(StatType.MaxHP);
// etc
}
}

c# access child class from parent

I have two classes. Jewellery is base and Ring inherits from it.
class Jewellery
{
public string Name { get; set; }
......
public Jewellery(string name)
{
Name = name;
}
}
.
class Ring : Jewellery
{
public string Size { get; set; }
public Ring(string name, string size) :base(name)
{
Size = size
}
}
Now in main i created List of Jewellery and in that list i added Ring object.
Ring ring = new Ring("Diamond", "Very big");
List<Jewellery> jewellery = new List<Jewellery>();
jewellery.Add(ring);
Now when debugging i can access ring object from jewellery list. Can i do it from code? I think this should be done like this, but this doesn't work.
jewellery[0].Ring
You need to cast it, e.g.:
var myRing = (Ring)jewellery[0];
or
var maybeRing = jewellery[0] as Ring;
if (maybeRing != null)
{
// do stuff
}
or
if (jewellery[0] is Ring)
{
// Cast and do stuff
}
For multiple types you can
if (jewellery[0] is Ring)
{
// Cast and do stuff
}
else if(jewllery[0] is Necklace)
{
// and so on
}
See MSDN on safe casting.
Depending on what you want to do you can use Linq to filter by type:
Given:
List<Jewellery> things = new List<Jewllery>();
Then:
public IList<T> GetJewellery<T>(this jewellery) where T : Jewellery
{
return jewellery.OfType<T>().ToList();
}
Can:
IList<Necklace> necklaces = things.GetJewellery<Necklace>();
IList<Ring> rings = things.GetJewellery<Ring>();

Creating list/array of class instances?

I'm fairly new to C# and I have just learned about creating custom classes. The problem is, I can't figure out how to take the 40~65 instances of this class and put them in a list/array (whichever one I need) where I can locate and choose one based on an attribute defined in it.
Here's the class I have created right now:
public class Team
{
protected int teamNum;
protected double averageMatchPoints;
protected string location;
protected int matchesPlayed;
protected int matchesPending;
protected int blowouts;
//Team Number
public void SetNumber(int num)
{
teamNum = num;
}
public int GetNumber()
{
return teamNum;
}
//Average Points per match
public void AverageMatchPoints(double p)
{
averageMatchPoints = p;
}
public double GetAverageMatchPoints()
{
return averageMatchPoints;
}
//location information
public void SetLocation(string l)
{
location = l;
}
public string GetLocation()
{
return location;
}
//Number of Played Matches
public void PlayedMatches(int mat)
{
matchesPlayed = mat;
}
public int GetPlayedMatches()
{
return matchesPlayed;
}
//Number of matches pending (not played)
public void PendingMatches(int pen)
{
matchesPending = pen;
}
public int GetPendingMatches()
{
return matchesPending;
}
//Number of Blowouts (matches where the robot was disbaled for any number of reasons)
public void SetBlowouts(int b)
{
blowouts = b;
}
public int GetBlowouts()
{
return blowouts;
}
}
Now, if I had 40~65 of these teams competing at an event and I made an instance of this class for each one, how would I populate a combobox with each team number (teamNum) and then locate one specific team out of all the instances in the program by their team numbers?
I recommend a dictionary!
// Declared somewhere
private Dictionary<int, Team> _teamDictionary = new Dictionary<int, Team>();
.
.
.
//Initialization code - I assume you have gotten your teams from a database or somewhere?
foreach (var team in myTeamsList)
{
_teamDictionary.Add(team.teamNum, team);
}
.
.
.
// Later when you want to locate a team:
var team = _teamDictionary[selectedTeamNum];
Have you tried creating a List yet?
List<Team> Teams { get; set; }
You can then bind your combobox to the list/collection/IEnumerable of all the teams that you have. To initialize the teams up to 40/60 do the following?
for(int i = 0; i < 60; i++)
{
Team t = new Team();
t.Name = "Team 1";
t.TeamNumber = i + 1;
Teams.Add(t);
}
List<Team> allTheTeams = new List<Team>();
for(var i = 0; i < 65; i++){
allTheTeams.Add(new Team { teamNum = i });
}
And to get the team with number 34:
allTheTeams.FirstOrDefault(x => x.teamNum == 34);
Like this:
Add a constructor to your class that takes the teamnumber:
(this is the best solution if every team needs to have a number. So you can not forget to set the team number as you can not create an object of type team without setting the number in the constructor)
public class Team
{
protected int _teamNum;
public Team(int teamNum)
{
_teamNum = teamNum;
}
public int getTeamNum()
{
return _teamNum;
}
//more logic
}
Populate a dictionary, the comboBox and get a team for its number:
Dictionary<int, Team> dictionary = new Dictionary<int, Team>();
int teamNum = 1;
// Add your Teams to a dictionary (example)
dictionary.Add(teamNum ,new Team(teamNum++));
dictionary.Add(teamNum, new Team(teamNum++));
dictionary.Add(teamNum, new Team(teamNum++));
// Populate a comboBox
foreach(KeyValuePair<int,Team> kvp in dictionary)
{
comboBox1.Items.Add(kvp.Value.getTeamNum().ToString());
}
// get a team for a given teamNumer
int targetTeamNumber = 2;
if (dictionary.ContainsKey(targetTeamNumber))
{
Team team = dictionary[targetTeamNumber];
// do something with the team
}

Initilialising an array of objects that contain an array of other objects?

I am trying to declare an array of buildings but the floors associated with each building are never initialised. I want to provide values to the data members for each instance of floor that is associated with each instance of building:
class Floor
{
public int number;
public int rooms;
//constructor
Floor()
{
rooms = 5;
number= 0;
}
}
class Building
{
public Floor[] floors= new Floor[6];
}
public partial class frmF2 : Form
{
Building[] x = new Building[7];
...
}
But it is very ugly.
class Building
{
Building()
{
floors = new Floor[6];
for(int i=0; i<6;++i)
floors[i] = new Floor();
}
public Floor[] floors;
}

serialize obfuscated class C#

I currently have a helper class that I am using to obfuscate a static class that keeps track of high scores in a game I am working on. I am using Eazfuscator on my release and found that when my scores were being serialized, this exception was thrown:
ArgumentException Identifier ' ' is not CLS-compliant
Is there a way I can store my list of high scores in my helper class and still be able to serialize it after obfuscation?
try
{
GameHighScore highScoreHelper = new GameHighScore();
highScoreHelper.CreateGameHighScore(highScore);
XmlSerializer serializer = new XmlSerializer(typeof(GameHighScore));
serializer.Serialize(stream, highScoreHelper);
}
catch(Exception e)
{
Logger.LogError("Score.Save", e);
}
My helper class:
public class GameHighScore
{
public List<HighScoreStruct<string, int>> highScoreList;
private HighScoreStruct<string, int> scoreListHelper;
[XmlType(TypeName = "HighScore")]
public struct HighScoreStruct<K, V>
{
public K Initials
{ get; set; }
public V Score
{ get; set; }
public HighScoreStruct(K initials, V score) : this()
{
Initials = initials;
Score = score;
}
}
public GameHighScore()
{
highScoreList = new List<HighScoreStruct<string, int>>();
scoreListHelper = new HighScoreStruct<string, int>();
}
public void CreateGameHighScore(List<KeyValuePair<string, int>> scoreList)
{
for (int i = 0; i < scoreList.Count; i++)
{
scoreListHelper = new HighScoreStruct<string, int>(scoreList[i].Key, scoreList[i].Value);
highScoreList.Add(scoreListHelper);
}
}
}
Best solution would be not to obfuscate classes needed for any kind of serialization. You'll gain 2 benefits of doing so:
no strange names will be used for classes
re-running obfuscation will not produce new names for the same classes/fields.
Most obfuscators allow to specify attributes that keep particular classes/methods non-obfuscated.
Otherwise - write your own serialization.
Try using the XmlElement attribute on your properties.

Categories