C# get/set solution - c#

To give some background I'm trying to solve the Project Euler Problem 54 involving poker hands. Though there's infinite approaches to this. What I would like to do is enumerate through a list of strings, for example:
{ "8C", "TS", "KC", "9H", "4S" };
I would like to "get" an instance of class card with properties value, and suit, for each respective string. I've not yet utilized get/set so maybe there is an obvious approach to this I'm missing.
Ultimately I would like to have a list of objects type Card, I don't mind building all the card's ahead of time, such that "2H" returns an instance of type Card where suit = Hearts, and value = 2, for example.
I know this code is wrong, but it should give an idea of what I'm trying to do. Any suggestions would be appreciated.
class Card
{
public string suit;
public int value;
public string cardname
{
get
{
if (cardname == "2H") Card TwoH = new Card();
TwoH.suit = "Hearts"
TwoH.value = 2;
return TwoH;
}
}
}

Why not make a constructor that fills suit and value based on a string parameter
public Card(string name)
{
switch(name)
{
case "2H":
this.suit = "Hearts";
this.value = 2;
break;
//...
}
}

This might not be the exact solution you seem to be asking for but if the values you'll be getting (eg 2H, 3C etc) are all 2 characters long, then you can try this:
public class Card
{
public string suit { get; set; }
public int value { get; set; }
public static Card GetCard(string cardName)
{
string tmpSuit;
int tmpValue;
char[] cardNameParts = cardName.ToCharArray();
switch(charNameParts[0])
{
case "A":
tmpValue = 1;
break;
case "2":
tmpValue = 2;
break;
...
}
switch(charNameParts[1])
{
case "H":
tmpSuit= "Hearts";
break;
case "C":
tmpSuit= "Clubs";
break;
...
}
return new Card() { suit = tmpSuit, value = tmpValue };
}
}

I would do it like that:
public class Card
{
public string Suit { get; set; }
public int Value { get; set; }
public static Card FromString(string s)
{
if (s == "2H") return new Card() { Suit = "Hearts", Value = 2 };
else if (s == "....")
...
else return null;
}
}
I have converted your suit and value field into properties and instead of some getter method which in your case wouldn't work I have added a static method.
You can use it like this Card card2H = Card.FromString("2H");

Maybe use two switch statements, first
switch (cardname[0])
{
...
}
then
switch (cardname[1])
{
...
}
Before that, check that cardname.Length == 2. In each switch, have a default section where you throw an exception in case the char value doesn't make sense.

Related

How best to use Interfaces/Generics for this Card/Deck example

I'm working on learning generics/using dependency injection/interfaces in order to have loosely coupled classes - however, I'm struggling.
For the below trivial card/deck example, how could I best adapt these classes (using the aforementioned techniques) so that I can pass any type of "Card/ICard" List in, for Deck to be able to populate/print said type. So for example, in Main, I want to be able to pass in List or List, and for deck to be able to handle this either way.
Deck.cs
public class Deck
{
List<Card> cards;
public Deck(List<Card> cards)
{
this.cards = cards;
foreach (Card.Suit suit in Enum.GetValues(typeof(Card.Suit))) {
foreach (Card.Val value in Enum.GetValues(typeof(Card.Val))) {
cards.Add(new Card(suit, value));
}
}
}
public void printDeck()
{
foreach(Card x in cards)
{
Console.WriteLine(x.ToString());
}
}
}
}
Card.cs
public class Card : ICard {
Suit suit;
Val value;
public Card(Suit suit, Val value)
{
this.suit = suit;
this.value = value;
}
public override string ToString()
{
string formatCard = "Suit: " + suit + " Value: " + value;
return formatCard;
}
public enum Suit
{
HEARTS = 1,
SPADES = 2,
CLUBS = 3,
DIAMONDS = 4
}
public enum Val
{
ONE = 1,
TWO = 2,
THREE = 3,
FOUR = 4,
FIVE = 5
}
}
}
SpecialCard.cs
public class SpecialCard : ICard{
Suit suit;
Val val;
public SpecialCard(Suit suit, Val val)
{
this.suit = suit;
this.val = val;
}
public override string ToString()
{
string formatCard = "Dragon: " + suit + " Value: " + val;
return formatCard;
}
public enum Suit
{
RIDGEBACK = 1,
FIREBALL = 2,
SHORTSNOUT = 3,
HORNTAIL = 4
}
public enum Val
{
UNO = 1,
DOS = 2,
TRES = 3,
QUATTRO = 4,
CINCZ = 5
}
}
}
ICard.cs
public interface ICard
{
string ToString();
}
Main
static void Main()
{
Deck deck = new Deck(new List<Card>());
deck.printDeck();
Console.ReadKey();
}
Thanks for any pointers!
FYP
I wouldn't have the suit and value as enums as that makes it hard to substitute different implementations. In general I'd separate classes which hold state such as a card or a deck from classes which provide logic, such as a deck builder.
I don't think you need ToString() in ICard as all classes inherit System.Object, which implements ToString(). Anyway, I don't think the card should know its own long description. Why not let the client decide based on the information (suit, value) it has available.
Given that the implementation of both types of cards is the same I'd just have a single class with very little logic. Suit and value can just be a string for now as all you do is print the values. You may wish to change Value to be a class with an int value and string description if you need to compare strength of cards later.
public class Card
{
public string Suit { get; set; }
public string Value { get; set; }
}
your deck would then be a list of cards and the type of suit used and would delegate the building of itself elsewhere to avoid violating separation of concerns.
public class Deck
{
public IEnumerable<Card> Cards { get; set; }
public string SuitName { get; set; }
}
then the bulk of your logic would exist in the deck builder. To enable the client to know which deck to build I would create an enum.
public enum DeckType
{
Standard,
Special
}
and the builder would be (static as it holds no state)
public static class DeckBuilder
{
public Deck Build(DeckType deckType)
{
var deck = new Deck();
switch (deckType)
{
case DeckType.Standard:
deck.SuitName = "Suit";
deck.Cards = CreateStandardCards();
break;
case DeckType.Special:
deck.SuitName = "Dragon";
deck.Cards = CreateSpecialCards();
break;
default:
throw new ArgumentException("deckType");
}
}
private IEnumerable<Card> CreateStandardCards()
{
var suits = new List<string> { "Hearts", "Clubs", "Diamonds", "Spades" };
var values = new List<string> { "One", "Two", "Three", "Four", "Five" };
return CreateCards(suits, values);
}
private IEnumerable<Card> CreateSpecialCards()
{
var suits = new List<string> { "Ridgeback", "Fireball", "Shortsnout", "Horntail" };
var values = new List<string> { "Uno", "Dos", "Tres", "Quattro", "Cincz" };
return CreateCards(suits, values);
}
private IEnumerable<Card> CreateCards(IEnumerable<string> suits, IEnumerable<string> values)
{
foreach (var suit in suits)
foreach (var value in values)
yield return new Card { Suit = suit, Value = value };
}
}
an alternative to this approach would be to create an IDeckBuilder with different implementations for StandardDeckBuilder and SpecialDeckBuilder, meaning the enum isn't needed. I would do this if the switch statements became larger (i.e. if you added more properties which were dependant on the deck type).
The client needs to call the appropriate builder. I would also move the printDeck logic here. The deck class shouldn't know how to print. It shouldn't know that the output goes to console.
static void Main()
{
var deck = DeckBuilder.Build(DeckType.Standard);
foreach (var card in deck.Cards)
Console.WriteLine(deck.SuitName + ": " + card.Suit + " Value: " + card.Value);
Console.ReadKey();
}
I haven't tried this so it may not compile - it's an illustration of the overall design I would use.

Advice - How to implement the same code with different parameters

I would like an advice. My project have a lot of equals methods with different values, and i would like to do a single method that does the same.
The methods are this:
private void Enum1()
{
Console.WriteLine(Enum.GetValue(ENUM1.Code));
Console.WriteLine(Enum.GetValue(ENUM1.Info));
}
private void Enum2()
{
Console.WriteLine(Enum.GetValue(ENUM2.Code));
Console.WriteLine(Enum.GetValue(ENUM2.Info));
}
private void Enum3()
{
Console.WriteLine(Enum.GetValue(ENUM3.Code));
Console.WriteLine(Enum.GetValue(ENUM3.Info));
}
This is the enums:
public enum ENUM1
{
Code = 1,
Info = 3
}
public enum ENUM2
{
Code = 91,
Info = 4
}
public enum ENUM3
{
Code = 6,
Info = 27
}
There is only a way to create a method by inserting the input type of enum to use? maybe a similar solution of this:
private void General("ENUM1")
{
var type = ENUM1;
switch (p)
{
case "ENUM1":
type = ENUM1;
case "ENUM2":
type = ENUM2;
case "CASALINGHI":
type = ENUM3;
default:
type = ENUM1;
}
Console.WriteLine(Enum.GetValue(type.Code));
Console.WriteLine(Enum.GetValue(type.Info));
}
I think something like this is what you are looking for:
private void General<T>()
{
var values = Enum.GetValues(typeof(T));
foreach(var value in values)
Console.WriteLine(value);
}
General<Enum1>();
General<Enum2>();
General<Enum3>();
Or this, depending on how you want to use it:
private void General(Type enumType)
{
var values = Enum.GetValues(enumType);
foreach(var value in values)
Console.WriteLine(value);
}
General(typeof(Enum1));
General(typeof(Enum2));
General(typeof(Enum3));
Why do you keep using enums, when you can easily use classes? Read more about Object-Oriented programming.
Create a single class:
public class MyEnum
{
public int Code
{
get; set;
}
public int Info
{
get; set;
}
public string Display()
{
Console.WriteLine(this.Code);
Console.WriteLine(this.Info)
}
//
// This will keep your enums static, available from any method
//
private static List<MyEnum> _globals = new List<MyEnum();
public static List<MyEnum> Globals ()
{
if (this._globals.Count == 0)
{
this._globals.Add(new MyEnum(){ Code = 1, Info = 3 });
this._globals.Add(new MyEnum(){ Code = 91, Info = 4 });
this._globals.Add(new MyEnum(){ Code = 6, Info = 27 });
}
return this._globals;
}
}
After this you can easily print out all the enums with the following code:
foreach (MyEnum* en in MyEnum.Globals())
{
en.Display();
}
Please look into solutions similar to this one, since your enum's obviously represent some data.

Enum with multiple descriptions

I have my enum like this.
public enum Gender
{
Man = 1,
Woman = 2
}
And I use ASP MVC4 to display the choices in a drop down like this.
#Html.DropDownListFor(model => model.Gender, new SelectList(Enum.GetValues(typeof(Namespace.Models.Enum.Gender))))
This works like a charm, it display Man/Woman in the drop down.
My problem is that I would like to show different names on those enums in different contexts.
Like one context would be if you are a Mom or a Dad. I would like to use the gender enum as base, but display Mom/Dad instad of Man/Woman.
Another context would be Boy/Girl, I still would like to use the gender enum, but display a different text.
Is this possible in any way?
EDIT
I used Kevin's solution and also added another extention method like this.
public static List<KeyValuePair<string, int>> GetValues(IGenderStrategy genderStrategy)
{
Dictionary<string, int> arr = new Dictionary<string, int>();
foreach (Gender g in System.Enum.GetValues(typeof(Gender)))
arr.Add(g.ToValue(genderStrategy), (int)g);
return arr.ToList();
}
Which I used like this in my view.
#Html.DropDownListFor(model => model.Gender, new SelectList(Chores.Models.Enum.EnumExtentions.GetValues(new Chores.Models.Enum.ParentStrategy()), "value", "key"))
I like #RakotVT answer of using an extension method but would extend it a bit further as you would need a new extension method for every situation which is not great.
I think a variation of the Strategy pattern might work better here (http://www.dofactory.com/Patterns/PatternStrategy.aspx)
Something like this -
public enum Gender
{
Man = 1,
Woman = 2
}
public interface IGenderStrategy
{
string DisplayName(Gender gender);
}
public class ParentStrategy : IGenderStrategy
{
public string DisplayName(Gender gender)
{
string retVal = String.Empty;
switch (gender)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
default:
throw new Exception("Gender not found");
}
return retVal;
}
}
public static class EnumExtentions
{
public static string ToValue(this Gender e, IGenderStrategy genderStategy)
{
return genderStategy.DisplayName(e);
}
}
public class Test
{
public Test()
{
Gender.Man.ToValue(new ParentStrategy());
}
}
Try to add Extentions class for your Enum. Here is an example of this class.
public static class EnumExtentions
{
public static string ToChildValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Boy";
break;
case Gender.Woman:
retVal = "Girl";
break;
}
return retVal;
}
public static string ToParentValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
}
return retVal;
}
}
Dunno if this is the neatest way, but how about something like:
#functions{
IEnumerable<SelectListItem> GetGenderSelectList(GenderContext genderContext)
{
return Enum.GetValues(typeof(Namespace.Models.Enum.Gender)).ToList().ConvertAll(x => new SelectListItem(){Value= x.ToString(), Text= GetGenderDescription(x, genderContext)});
}
string GetGenderDescription(Gender gender, GenderContext genderContext)
{
switch (GenderContext)
{
case Children: return gender == Man? "Boy" : "Girl";
case Parents: return gender == Man? "Dad" : "Mom";
default: return gender.ToString();
}
}
}
#Html.DropDownListFor(model => model.Gender, GetGenderSelectList(model.GenderContext))
Here 'GenderContext' is another Enum.
obviously you don't need to have those functions in the page functions - Could just add the list of items to the ViewBag before even getting to the view.

DumpObject with GetFields and GetProperties for instance with nested class

this is my first question in stackoverflow and I am a beginner in using reflection.
I would like to dump all values of an object instance for reference (to keep track about used values on a test). I am using Compact Framework 3.5 not the full framework. Keep that in mind for your suggestions.
Imagine following classes:
public class Camera : IDisposable
{
public Camera.FilenameProperties SnapshotFile;
public double DigitalZoomFactor { get; set; }
public bool DisplayHistogram { get; set; }
public int ImageUpdateInterval { get; set; }
public Camera.ImprintCaptionPosType ImprintCaptionPos { get; set; }
public string ImprintCaptionString { get; set; }
}
where the 'special' types are:
public class FilenameProperties
{
public string Directory { get; set; }
public string Filename { get; set; }
public Camera.FilenamePaddingType FilenamePadding { get; set; }
public Camera.ImageType ImageFormatType { get; set; }
public Camera.ImageResolutionType ImageResolution { get; set; }
public int JPGQuality { get; set; }
public void Restore();
public void Save();
public enum Fnametype
{
tSnapshot = 0,
tCircularCapture = 1,
}
}
public enum ImprintCaptionPosType
{
Disabled = 0,
LowerRight = 1,
LowerLeft = 2,
LowerCenter = 3,
UpperRight = 4,
UpperLeft = 5,
UpperCenter = 6,
Center = 7,
}
Now, I can get the 'base' names and properties and the field names of an instance of camera:
Camera cam = new Camera();
dumpProperties(cam);
...
void dumpProperties(object oClass)
{
System.Diagnostics.Debug.WriteLine(oClass.ToString());
FieldInfo[] _Info = oClass.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
for(int i = 0; i<_Info.Length; i++)
{
System.Diagnostics.Debug.WriteLine(_Info[i].Name + ":'" + _Info[i].GetValue(oClass).ToString()+"'");
}
foreach (PropertyInfo pi in oClass.GetType().GetProperties())
{
System.Diagnostics.Debug.WriteLine(pi.Name + ":'" + pi.GetValue(oClass, null)
+ "' Type=" + pi.PropertyType.ToString());
}
}
and then get soemthing like this:
Intermec.Multimedia.Camera
SnapshotFile:'Intermec.Multimedia.Camera+FilenameProperties'
DigitalZoomFactor:'1' Type=System.Double
DisplayHistogram:'False' Type=System.Boolean
ImageUpdateInterval:'1' Type=System.Int32
ImprintCaptionPos:'Disabled' Type=Intermec.Multimedia.Camera+ImprintCaptionPosType
ImprintCaptionString:'' Type=System.String
Now, for simple properties like DigitalZoomFactor and ImageUpdateInterval I get what I need, but for the nested class (correct wording?) I only get the type as for example with SnapshotFile. For the nested enum I get the value as with 'ImprintCaptionPos'.
How can I get the values of the nested values like FilenameProperties.Filename of the SnapshotFile field/property?
If I use dumpProperties(cam.SnapshotFile), I get the output I am looking for:
Intermec.Multimedia.Camera+FilenameProperties
Directory:'\Program Files\FrmCamera' Type=System.String
Filename:'myphoto' Type=System.String
ImageFormatType:'JPG' Type=Intermec.Multimedia.Camera+ImageType
FilenamePadding:'None' Type=Intermec.Multimedia.Camera+FilenamePaddingType
ImageResolution:'Medium' Type=Intermec.Multimedia.Camera+ImageResolutionType
JPGQuality:'100' Type=System.Int32
But how can I automate that?
I did a lot of search and test coding but was unable to find a solution. The problem seems to be getting the instance of the field to be able to iterate thru it.
I do not have the source code of the Camera class, so I cannot add or remove code in there.
Can anyone help?
I need to get something like the debugger shows:
You simply need to use recursion, and loop back into the method if your property is a class. Here's an example of an XML Serialization routine we use, that effectively walks the properties of a target using reflection, and generates an XElement from it. Your logic would be somewhat different as you're not going to build up XML, but the structure of what you're going to do will be pretty similar.
public XElement Serialize(object source,
string objectName,
bool includeNonPublicProperties)
{
XElement element;
var flags = BindingFlags.Instance | BindingFlags.Public;
if(includeNonPublicProperties)
{
flags |= BindingFlags.NonPublic;
}
var props = source.GetType().GetProperties(flags);
var type = source.GetType();
string nodeName;
if(objectName == null)
{
if (type.IsGenericType)
{
nodeName = type.Name.CropAtLast('`');
}
else
{
nodeName = type.Name;
}
}
else
{
nodeName = objectName;
}
element = new XElement(nodeName);
foreach (var prop in props)
{
string name = prop.Name;
string value = null;
bool valIsElement = false;
if (!prop.CanRead) continue;
if(prop.PropertyType.IsEnum)
{
value = prop.GetValue(source, null).ToString();
}
else
{
string typeName;
if (prop.PropertyType.IsNullable())
{
typeName = prop.PropertyType.GetGenericArguments()[0].Name;
}
else
{
typeName = prop.PropertyType.Name;
}
switch (typeName)
{
case "String":
case "Boolean":
case "Byte":
case "TimeSpan":
case "Single":
case "Double":
case "Int16":
case "UInt16":
case "Int32":
case "UInt32":
case "Int64":
case "UInt64":
value = (prop.GetValue(source, null) ?? string.Empty).ToString();
break;
case "DateTime":
try
{
var tempDT = Convert.ToDateTime(prop.GetValue(source, null));
if (tempDT == DateTime.MinValue) continue;
value = tempDT.ToString("MM/dd/yyyy HH:mm:ss.fffffff");
}
catch(Exception ex)
{
continue;
}
break;
default:
var o = prop.GetValue(source, null);
XElement child;
if (o == null)
{
child = new XElement(prop.Name);
}
else
{
child = Serialize(o, prop.Name, includeNonPublicProperties);
}
element.Add(child);
valIsElement = true;
break;
}
}
if (!valIsElement)
{
element.AddAttribute(name, value);
}
}
return element;
}
OK, I find a way (a workaround) to get all properties (in an XML but who cares) using the code from here:
The output is xml like but acceptable for me. Here an excerpt:
<xml version="1.0" encoding="utf-8">
<Camera xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
...
<ImprintCaptionPos>Disabled</ImprintCaptionPos>
<SnapshotFile>
<Directory>\\Program Files\\FrmCamera</Directory>
<Filename>myphoto</Filename>
<ImageFormatType>JPG</ImageFormatType>
<FilenamePadding>None</FilenamePadding>
<ImageResolution>Medium</ImageResolution>
<JPGQuality>100</JPGQuality>
</SnapshotFile>
...
In my code I just have to call
string s = serialization.mySerialize.SerializeObject<Intermec.Multimedia.Camera>(cam);
To get a 'dump' of all current properties of the instance.
Thanks to all for your help. Possibly I was misunderstood with my question and reflection is unable to give what I want.
Thanks
Josef
You have to do the iteration to all inner object members like you did for outer class. Will be an exercise for you to implement it for complete set of .NET types. Below is the pseudo code of simple implementation
void dumpProperties(object target)
{
if (target.GetType().IsSimple() || target.GetType().IsMethodImplemented("ToString"))
System.Diagnostics.Debug.WriteLine(target.ToString());
else if (target is IEnumerable)
{
foreach (object item in (IEnumerable)target)
{
System.Diagnostics.Debug.WriteLine(dumpProperties(target));
}
}
else
{
foreach (FieldInfo fieldInfo in target.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
{
System.Diagnostics.Debug.WriteLine(dumpProperties(fieldInfo.FieldHandle.Value));
}
foreach (PropertyInfo propertyInfo in oClass.GetType().GetProperties())
{
System.Diagnostics.Debug.WriteLine(dumpProperties(propertyInfo.GetGetMethod().MethodHandle.Value));
}
}
}
Or you can use Cinchoo framework, which has built in function to dump any object.
Console.WriteLine(ChoObject.ToString(camera));

Best way to represent game card class in C#

I use class Card which contains 2 enumerated properties (suite - hearts diamonds spades and clubs) and card value from 2 to A. And overrides ToString() method to returns something like Ah Ad etc. All ok, but enum value can't starts with number, therefore my card value enumerated looks like x2, x3, x4 ... it is not beautiful.
Also need simple approach to parse few cards from single string.
Who know the best approach to design this class?
Couldn't you assign Jack, Queen, King, and Ace to be 11, 12, 13, and 14, respectively? It'd end up looking something like:
public class Card
{
public int Value { get; private set; }
public enum SuitType
{
Clubs, Spades, Hearts, Diamonds
}
public SuitType Suit { get; private set; }
public Card(int value, SuitType suit)
{
Suit = suit;
Value = value;
}
public Card(string input)
{
if (input == null || input.Length < 2 || input.Length > 2)
throw new ArgumentException();
switch (input[0])
{
case 'C': case 'c':
Suit = SuitType.Clubs;
break;
case 'S': case 's':
Suit = SuitType.Spades;
break;
case 'H': case 'h':
Suit = SuitType.Hearts;
break;
case 'D': case 'd':
Suit = SuitType.Diamonds;
break;
default:
throw new ArgumentException();
}
int uncheckedValue = (int)input[1];
if (uncheckedValue > 14 || uncheckedValue < 1)
throw new ArgumentException();
Value = uncheckedValue;
}
public string encode()
{
string encodedCard = "";
switch (Suit)
{
case SuitType.Clubs:
encodedCard += 'c';
break;
case SuitType.Spades:
encodedCard += 's';
break;
case SuitType.Hearts:
encodedCard += 'h';
break;
case SuitType.Diamonds:
encodedCard += 'd';
break;
}
encodedCard += (char) Value;
return encodedCard;
}
public override string ToString()
{
string output = "";
if (Value > 10)
{
switch (Value)
{
case 11:
output += "Jack";
break;
case 12:
output += "Queen";
break;
case 13:
output += "King";
break;
case 14:
output += "Ace";
break;
}
}
else
{
output += Value;
}
output += " of " + System.Enum.GetName(typeof(SuitType), Suit);
return output;
}
}
Edit:
I added some string functionality.
I took structure of Card(string input) from Jon Hanna's answer.
There's an obvious numeric value for the pip-cards, and we can add J=11, Q=12, K=13.
It may be more convenient to have A=14 than A=1 depending on the game being modelled (so one can more simply compute different relative values of hands).
Enums gives no real advantage, especially since enums allow out-of-range values unless you explicitly check for them (e.g. there is nothing to stop someone assigning (CardValue)54 to the card-value enumeration value).
ToString can be aided with an array of the values {null,"1","2","3","4","5","6","7","8","9","10","J","Q","K"}. Likewise {'♥','♦','♠','♣'} could give a nicer output.
Parsing always trickier than outputting a string, even if you are very strict in what you accept, as you have to deal with the potential for invalid input. A simple approach would be:
private Card(string input)
{
if(input == null)
throw new ArgumentNullException();
if(input.length < 2 || input.length > 3)
throw new ArgumentException();
switch(input[input.Length - 1])
{
case 'H': case 'h': case '♥':
_suit = Suit.Hearts;
break;
case 'D': case 'd': case '♦':
_suit = Suit.Diamonds;
break;
case 'S': case 's': case '♠':
_suit = Suit.Spades;
break;
case 'C': case 'c': case '♣':
_suit = Suit.Clubs;
break;
default:
throw new ArgumentException();
}
switch(input[0])
{
case "J": case "j":
_cardValue = 11;
break;
case "Q": case "q":
_cardValue = 12;
break;
case "K": case "k":
_cardValue = 13;
break;
case "A": case "a":
_cardValue = 1;
break;
default:
if(!int.TryParse(input.substring(0, input.Length - 1), out _cardValue) || _cardValue < 2 || _cardVaue > 10)
throw new ArgumentException;
break;
}
}
public static Card Parse(string cardString)
{
return new Card(cardString);
}
You might want to add a static method that read a larger string, yield returning cards as it parsed, to allow for easier encoding of several cards.
When I first started on the card.dll, I was using enumerations for suits and card rankings but then I didn't want to have to deal with that same issue and writing extra code to compensate for the strings, there for I wrote a abstract class Info with only two variables
(Flag (byte)) and (Name(string)) to be implemented by the Rank class and Suit class which would be members of the Card class. I have found this to work a lot better for naming conventions and filtering purposes. I love using enums but having to work around variable naming can be a hassle so sometimes it is best not to if you have to get the variable name as string.
So when the Card constructor get called the card ID is entered and then it passes into the Rank and Suit which will then separate what the ID means in code (101 = 100 (suit flag) +
1 (rank flag)). The protected abstract SetName(int cardID) and SetFlag(int cardID) while handle the rest from there in the info's constructor via Rank and Suit. No more issues with the enumeration and it can still be filtered by number via the Flag.
This card naming system uses a 1 through 4 * 100 (telling the suit flag) + 1 through 13 (for card rank). 500 + 14 through 16 are Little Joker, Big Joker, and Wild.
public class Card
{
short id;
public Card(string zFile)
{
this.id = Convert.ToInt16(zFile.Split('.')[0].Trim());
this.Rank = new Rank(id);
this.Suit = new Suit(id);
}
public override string ToString()
{
if (Suit.Flag == 5)
return Suit.Name;
return string.Concat(Rank.Name, " of ", Suit.Name);
}
public override int GetHashCode()
{
return id;
}
public Rank Rank { get; private set; }
public Suit Suit { get; private set; }
public static Card GetGreaterRank(Card value1, Card value2)
{
return (value1.Rank >= value2.Rank) ? value1 : value2;
}
public static bool CompareRank(Card value1, Card value2)
{
return (value1.Rank.Flag == value2.Rank.Flag);
}
public static bool CompareSuit(Card value1, Card value2)
{
return (value1.Suit.Flag == value2.Suit.Flag);
}
};
public abstract class Info
{
protected Info(short cardID)
{
Flag = SetFlag(cardID);
}
protected string SetName(short cardID, params string[] names)
{
for (int i = 0; i < names.Length; i++)
{
if (Flag == (i + 1))
return names[i];
}
return "Unknown";
}
protected abstract byte SetFlag(short cardID);
public static implicit operator byte(Info info)
{
return info.Flag;
}
public byte Flag { get; protected set; }
public string Name { get; protected set; }
};
public class Rank : Info
{
internal Rank(short cardID) : base(cardID)
{
string name = SetName(cardID, "A","2","3","4","5","6","7",
"8","9","10","J","Q","K","Little Joker","Big Joker","Wild");
Name = (name == "Unknown") ? string.Concat(name, " Rank") : name;
}
protected override byte SetFlag(short cardID)
{
return Convert.ToByte(cardID.ToString().Remove(0, 1));
}
};
public class Suit : Info
{
internal Suit(short cardID) : base(cardID)
{
string name = SetName(cardID,"Clubs","Diamonds","Hearts","Spades");
Name = (name == "Unknown") ? string.Concat(name, " Suit") ? name;
}
protected override byte SetFlag(short cardID)
{
return Convert.ToByte(cardID.ToString().Remove(1));
}
};
So now if you have your card image file named 101.png and pass it into the Card ctor it will pass to the Rank and Suit getting the info for you. Really all you are doing in giving the image file a code(numeric) for a name.
I would probably start out with 2 enums, 1 representing the Suits and 1 representing the Faces. Then declare a public property "Suit" and a public property "Face" based off of these enums. You will also probably need an array with the different unique values that a card can have (i.e. 1 throught 13).
You can start enums with number (although it is preferred to start at zero)
public enum Card
{
Two = 2,
Three,
Four,
...
}
Scratch what I wrote before, this is better.
using System;
enum Suit
{
Clubs,
Hearts,
Diamonds,
Spades
}
class Card
{
Suit Suit
{
get;
private set;
}
int Value
{
get;
private set;
}
Card(Suit suit, int value)
{
Suit = suit;
Value = value;
}
private const string[] valsToString = new string[] { "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };
bool IsValid()
{
return Value >= 2 && Value <= 14;
}
override string ToString()
{
return string.Format("{0} of {1}", valsToString[Value - 2], Suit);
}
}

Categories

Resources