I have to implement a poker game using Test-Driven Development. Now I have to check if the given hand contains straight flush. So I have to check the enumerations of each card if is greater with one than the previous one. Let me show some code.
public bool IsStraightFlush(IHand hand)
{
var sortedCards = hand.Cards.OrderBy(card => card.Face).ThenBy(card => card.Suit);
}
Each card is declaring by two parameters of enums: public Card(CardFace face, CardSuit suit)
CardFace and CardSuits are enums.
Here's what I wrote for now - I sorted the given hand as parameter consisted of five cards. I ordered first by face, then by suit. Now I have to know how to check if the next card of the hand has the CardFace enum + 1 than the current one. Here's the enumeration for CardFace.
public enum CardFace
{
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 11,
Queen = 12,
King = 13,
Ace = 14
}
I hope this will work :
public bool IsStraightFlush(IHand hand)
{
var sortedCards = hand.Cards.OrderBy(card => card.Face).ThenBy(card => card.Suit).ToList();
bool straightFlush = true;
for (int i = 1; i < sortedCards.Count(); i++)
{
if ((int)sortedCards[i].Face != (int)sortedCards[i - 1].Face + 1)
{
straightFlush = false;
break;
}
}
return straightFlush;
}
This might help you, cast you sequence to int, then check that next element -1 equals current:
static bool CheckForSequence(List<int> input)
{
input.Sort();
var result = true;
for (int i = 0; i < input.Count - 1; i++)
{
result = input[i] == input[i + 1] - 1;
if (!result)
break;
}
return result;
}
And one more solution, you still need to cast ti int, but it much shorter:
static bool CheckForSequence(List<int> input)
{
return !input.OrderBy(p => p).Select((p, i) => p - i).Distinct().Skip(1).Any();
}
In this case, if collection sequential, difference between item and index will be same for all elements.
Try using a group by and then checking that there is a single group and all the cars within it are sequential.
public bool IsStraightFlush(IHand hand)
{
var sortedCards = from c in hand.Cards
group c by c.Suit into d
select new
{
Suit = d.Key,
Cards = d.OrderBy(x => x.Face)
};
// all cards are the same suit
if(sortedCards.Count() == 1)
{
Card previousCard = null;
foreach (var card in sortedCards.First().Cards)
{
if(previousCard != null && (card.Face - previousCard.Face > 1))
{
return false;
}
previousCard = card;
}
return true;
}
return false;
}
void Main()
{
var hand = new Hand
{
Cards = new List<Card>
{
new Card { Face = CardFace.Two, Suit = CardSuit.Clubs },
new Card { Face = CardFace.Three, Suit = CardSuit.Clubs },
new Card { Face = CardFace.Four, Suit = CardSuit.Clubs },
new Card { Face = CardFace.Five, Suit = CardSuit.Clubs },
new Card { Face = CardFace.Six, Suit = CardSuit.Clubs },
}
};
Console.WriteLine(IsStraightFlush(hand));
}
Related
I have a list and my goal is to determine how many times the values in that list goes above a certain value.
For instance if my list is:
List = {0, 0, 3, 3, 4, 0, 4, 4, 4}
Id like to know that there were two instances where my values in the list were greater than 2 and stayed above 2. So in this case there were 2 instances, since it dropped below 2 at one point and went above it again.
private void Report_GeneratorButton_Click(object sender, EventArgs e)
{
//Lists
var current = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.Current].ToList();
var SOC = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.Soc].ToList();
var highcell = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.HighestCell].ToList();
var lowcell = _CanDataGraph._DataPoints[CanDataGraph.CurveTag.LowestCell].ToList();
//Seperates current list into charging, discharging, and idle
List<double> charging = current.FindAll(i => i > 2);
List<double> discharging = current.FindAll(i => i < -2);
List<double> idle = current.FindAll(i => i < 2 && i > -2);
//High cell
List<double> overcharged = highcell.FindAll(i => i > 3.65);
int ov = overcharged.Count;
if (ov > 1)
{
Console.WriteLine("This Battery has gone over Voltage!");
}
else
{
Console.WriteLine("This battery has never been over Voltage.");
}
//Low cell
List<double> overdischarged = lowcell.FindAll(i => i > 3.65);
int lv = overdischarged.Count;
if (lv > 1)
{
Console.WriteLine("This Battery has been overdischarged!");
}
else
{
Console.WriteLine("This battery has never been overdischarged.");
}
//Each value is 1 second
int chargetime = charging.Count;
int dischargetime = discharging.Count;
int idletime = idle.Count;
Console.WriteLine("Charge time: " + chargetime + "s" + "\n" + "Discharge time: " + dischargetime + "s" + "\n" + "Idle time: " + idletime);
}
My current code is this and outputs:
This battery has never been over Voltage.
This battery has never been overdischarged.
Charge time: 271s
Discharge time: 0s
Idle time: 68
There are a great many ways to solve this problem; my suggestion is that you break it down into a number of smaller problems and then write a simple method that solves each problem.
Here's a simpler problem: given a sequence of T, give me back a sequence of T with "doubled" items removed:
public static IEnumerable<T> RemoveDoubles<T>(
this IEnumerable<T> items)
{
T previous = default(T);
bool first = true;
foreach(T item in items)
{
if (first || !item.Equals(previous)) yield return item;
previous = item;
first = false;
}
}
Great. How is this helpful? Because the solution to your problem is now:
int count = myList.Select(x => x > 2).RemoveDoubles().Count(x => x);
Follow along.
If you have myList as {0, 0, 3, 3, 4, 0, 4, 4, 4} then the result of the Select is {false, false, true, true, true, false, true, true, true}.
The result of the RemoveDoubles is {false, true, false, true}.
The result of the Count is 2, which is the desired result.
Try to use off-the-shelf parts when you can. If you cannot, try to solve a simple, general problem that gets you what you need; now you have a tool you can use for other tasks that require you to remove duplicates in a sequence.
This solution should achieve the desired result.
List<int> lsNums = new List<int>() {0, 0, 3, 3, 4, 0, 4, 4, 4} ;
public void MainFoo(){
int iChange = GetCritcalChangeNum(lsNums, 2);
Console.WriteLine("Critical change = %d", iChange);
}
public int GetCritcalChangeNum(List<int> lisNum, int iCriticalThreshold) {
int iCriticalChange = 0;
int iPrev = 0;
lisNum.ForEach( (int ele) => {
if(iPrev <= iCriticalThreshold && ele > iCriticalThreshold){
iCriticalChange++;
}
iPrev = ele;
});
return iCriticalChange;
}
You can create an extension method as shown below.
public static class ListExtensions
{
public static int InstanceCount(this List<double> list, Predicate<double> predicate)
{
int instanceCount = 0;
bool instanceOccurring = false;
foreach (var item in list)
{
if (predicate(item))
{
if (!instanceOccurring)
{
instanceCount++;
instanceOccurring = true;
}
}
else
{
instanceOccurring = false;
}
}
return instanceCount;
}
}
And use your newly created method like this
current.InstanceCount(p => p > 2)
public static int CountOverLimit(IEnumerable<double> items, double limit)
{
int overLimitCount = 0;
bool isOverLimit = false;
foreach (double item in items)
{
if (item > limit)
{
if (!isOverLimit)
{
overLimitCount++;
isOverLimit = true;
}
}
else if (isOverLimit)
{
isOverLimit = false;
}
}
return overLimitCount;
}
Here's a fairly concise and readable solution. Hopefully this helps. If the limit is variable, just put it in a function and take the list and the limit as parameters.
int [] array = new int [9]{0, 0, 3, 1, 4, 0, 4, 4, 4};
List<int> values = array.ToList();
int overCount = 0;
bool currentlyOver2 = false;
for (int i = 0; i < values.Count; i++)
{
if (values[i] > 2)
{
if (!currentlyOver2)
overCount++;
currentlyOver2 = true;
}
else
currentlyOver2 = false;
}
Another way to do this using System.Linq is to walk through the list, selecting both the item itself and it's index, and return true for each item where the item is greater than value and the previous item is less than or equal to value, and then select the number of true results. Of course there's a special case for index 0 where we don't check the previous item:
public static int GetSpikeCount(List<int> items, int threshold)
{
return items?
.Select((item, index) =>
index == 0
? item > threshold
: item > threshold && items[index - 1] <= threshold)
.Count(x => x == true) // '== true' is here for readability, but it's not necessary
?? 0; // return '0' if 'items' is null
}
Sample usage:
private static void Main()
{
var myList = new List<int> {0, 0, 3, 3, 4, 0, 4, 4, 4};
var count = GetSpikeCount(myList, 2);
// count == 2
}
I want to count all combinations of poker cards that a player can get in one hand and display all those combinations. I don't care about how many pairs, full houses etc. are there. I just want to count all possible hands that a player can get. So, one hand is made of 5 cards, there must be 4 colors (suits) and one suit must repeat. Maximum is that 4 numbers are same, number of 5th card must be different. Correct result of all possible hand combinations must be 2598960 (52 above 5 is 2598960). I just need to get correct result using my code, but I don't know how to write the algorithm.
I have card.cs class:
class card
{
private int number;
private char suit;
public card(int _number, char _suit)
{
this.number = _number;
this.suit = _suit;
}
public int Number
{
get{return this.number;}
set{this.number = value;}
}
public char Suit
{
get { return this.suit; }
set { this.suit = value; }
}
}
and in Program.cs class, I have Main and this code:
static void Main(string[] args)
{
char[] suits = { '\u2660', '\u2663', '\u2665', '\u2666' }; //Spades, Clubs, Hearts, Diamonds
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
int cnt = 0;
List<card> deck = new List<card>();
//making deck
foreach (char suit in suits)
{
foreach (int number in numbers)
{
deck.Add(new card(number, suit));
}
}
foreach (card first in deck)
{
foreach (card second in deck)
{
if (second.Number != first.Number)
{
foreach (card third in deck)
{
if (third.Number != second.Number && third.Number != first.Number)
{
foreach (card fourth in deck)
{
if (fourth.Number != third.Number && fourth.Number != second.Number && fourth.Number != first.Number)
{
foreach (card fifth in deck)
{
if (fifth.Suit != first.Suit && fifth.Suit != second.Suit && fifth.Suit != third.Suit && fifth.Suit != fourth.Suit)
{
//Console.WriteLine("{0}{1} {2}{3} {4}{5} {6}{7} {8}{9}", first.Number, first.Suit, second.Number, second.Suit, third.Number, third.Suit, fourth.Number, fourth.Suit, fifth.Number, fifth.Suit);
cnt++;
}
}
}
}
}
}
}
}
}
Console.WriteLine("Combinations: {0}", cnt);//Result must be: 2598960
}
Well, as much as I appreciate what you're doing, I find this easier to read:
int count = 0;
int cards_amount = 52;
for (var first = 0; first < cards_amount-4; first++)
for (var second = first + 1; second < cards_amount-3; second++)
for (var third = second+1; third < cards_amount-2; third++)
for (var fourth = third+1; fourth < cards_amount-1; fourth++)
for (var fifth = fourth+1; fifth < cards_amount; fifth++)
count++;
Instead of looking at all the duplicates, and the if this card is different from the previous, what I do is: Put all cards in a row. Select the first, and then the second from the remaining, and then the third from the remaining ... and so on.
This way, you don't need to check for multiples, and you get your right answer :)
Edit:
As for the comment ... Just add this line after you initialize your list:
var deck_array = deck.ToArray();
You might want to do this on your class card:
public override string ToString() {
// have this print your card number & suit
}
And then, just change the count++; line with this:
{
count ++;
Console.WriteLine("{0},{1},{2},{3},{4}", deck_array[first], deck_array[second],
deck_array[third] , deck_array[fourth] , deck_array[fifth] );
}
Solved ... (now your cards know how to print themselves, and you're just printing the hand at the end.
I would like to choose the third element from an enum containing three elements while knowing which two I have already chosen. What is the most effcient way of comparing enums?
EDIT:
So far I have come up with the following:
Drawable.Row alternateChoice = (Drawable.Row)ExtensionMethods.Extensions.DefaultChoice(new List<int>() { (int)chosenEnum1, (int)chosenEnum2 }, new List<int>() { 0, 1, 2 });
Drawable.Row is the enum, the first list is what has already been chosen, and the second list contains the possible choices. The definition of DefaultChoice follows. I am aware it has quadratic time-complexity which is why I'm asking for a better solution:
public static int DefaultChoice(List<int> chosen, List<int> choices)
{
bool found = false;
foreach (int choice in choices)
{
foreach (int chosenInt in chosen)
{
if (chosenInt == choice)
{
found = true;
break;
}
}
if (!found)
{
return choice;
}
found = false;
}
return -1;
}
Try this:
List<MyEnum> selectedValues = new List<MyEnum>();
selectedValues.Add(MyEnum.firstValue); // Add selected value
selectedValues.Add(MyEnum.secondValue); // Add selected value
List<MyEnum> valuesLeftOver = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().ToList();
valuesLeftOver = valuesLeftOver.Except(selectedValues).ToList<MyEnum>(); // This will result in the remaining items (third item) being in valuesLeftOver list.
Short and simple code.
Try not to worry about efficiency too much. Optimization algorithms are so powerful these days that you will probably get same assembly code doing this rather than trying to do it manually.
[Flags]
public enum NumberEnum : byte
{
None = 0,
One = 1,
Two = 2,
Three = 4
};
public string GetRemainingEnumItem(NumberEnum filterFlags = 0)
{
if (((filterFlags & NumberEnum.One) == NumberEnum.One) && ((filterFlags & NumberEnum.Two) == NumberEnum.Two))
{
//1 & 2 are selected so item 3 is what you want
}
if (((filterFlags & NumberEnum.One) == NumberEnum.One) && ((filterFlags & NumberEnum.Three) == NumberEnum.Three))
{
//1 & 3 are selected so item 2 is what you want
}
if (((filterFlags & NumberEnum.Three) == NumberEnum.Three) && ((filterFlags & NumberEnum.Two) == NumberEnum.Two))
{
//2 & 3 are selected so item 1 is what you want
}
}
This is how you call it:
var testVal = NumberEnum.One | NumberEnum.Two;
var resultWillEqual3 = GetRemainingEnumItem(testVal);
You might like to try this approach...
public enum Marx {chico, groucho, harpo};
public Marx OtherOne(Marx x, Marx y)
{
return (Marx)((int)Marx.chico + (int)Marx.groucho + (int)Marx.harpo - (int)x - (int)y);
} // OtherOne
// ...
Marx a = Marx.harpo;
Marx b = Marx.chico;
Marx other = OtherOne(a, b); // picks groucho
This may be of use if you are using an enum marked as [Flags]...
public class Enums2
{
[Flags] public enum Bits {none = 0, aBit = 1, bBit = 2, cBit = 4, dBit = 8};
public static readonly Bits allBits;
static Enums2()
{
allBits = Bits.none;
foreach (Bits b in Enum.GetValues(typeof(Bits)))
{
allBits |= b;
}
} // static ctor
public static Bits OtherBits(Bits x)
// Returns all the Bits not on in x
{
return x ^ allBits;
} // OtherBits
// ...
Bits someBits = Bits.aBit | Bits.dBit;
Bits missingBits = OtherBits(someBits); // gives bBit and cBit
} // Enums2
I am creating a poker system and I am currently streamlining my hand calculator.
The following code works:
public enum CARDS
{
None = 0,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King,
Ace
};
public enum SUITS
{
None = 0,
Diamonds,
Clubs,
Hearts,
Spades
};
public class Card
{
public CARDS Val { get; set; }
public SUITS Suit { get; set; }
}
public class IntIndex
{
public int Count { get; set; }
public int Index { get; set; }
}
static void Test()
{
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Four });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
// I have a processor that iterates through the above card list and creates
// the following array based on the Card.Val as an index
int[] list = new int[] {0,0,0,1,1,2,1,1,0,0,1,0,0,0};
List<IntIndex> indexList =
list.Select((item, index) => new IntIndex { Count = item, Index = index })
.Where(c => c.Count > 0).ToList();
List<int> newList = (from i in indexList
join j in indexList on i.Index equals j.Index + 1
where j.Count > 0
select i.Index).ToList();
// Add the previous index since the join only works on n+1
// Note - Is there a way to include the first comparison card?
newList.Insert(0, newList[0] - 1);
// Nice! - got my straight card list
List<CARDS> cards = (from l in newList
select (CARDS)l).ToList();
}
However, I want to make it more compact as in:
static void Test()
{
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Four });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
List<Card> newList1 = (from i in cardList
join j in cardList on i.Val equals j.Val + 1
select i).ToList();
// Add the previous index since the join only works on n+1
// Similar to: newList1.Insert(0, newList1[0] - 1);
// However, newList1 deals with Card objects so I need
// To figure how to get the previous, non-duplicate card
// from the original cardList (unless there is a way to return the
// missing card!)
}
The problem is that the Sixes are being repeated. Distinct as well as a custom compare function does not work since this will break the n+1 join clause.
Another problem is with the following card list:
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Three });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Spades, Val = CARDS.Six });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = CARDS.Seven });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = CARDS.Eight });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = CARDS.Jack });
I get a return list of 3Hearts, 6Diamond, 7Hearts, 8Hearts since 2 and 3 are consecutive.
What I really want is a list that returns consecutive cards of 5 or greater, or better yet, the top 5 cards of a continuous sequence. So the above list will return empty since there are no 5 consecutive cards in the input list.
If you are interested in getting the subset of cards from cardList that has the highest range of sequential card values, regardless of suit, consider this approach.
//Extension method to find a subset of sequential consecutive elements with at least the specified count of members.
//Comparisions are based on the field value in the selector.
//Quick implementation for purposes of the example...
//Ignores error and bounds checking for purposes of example.
//Also assumes we are searching for descending consecutive sequential values.
public static IEnumerable<T> FindConsecutiveSequence<T>(this IEnumerable<T> sequence, Func<T, int> selector, int count)
{
int start = 0;
int end = 1;
T prevElement = sequence.First();
foreach (T element in sequence.Skip(1))
{
if (selector(element) + 1 == selector(prevElement))
{
end++;
if (end - start == count)
{
return sequence.Skip(start).Take(count);
}
}
else
{
start = end;
end++;
}
prevElement = element;
}
return sequence.Take(0);
}
//Compares cards based on value alone, not suit.
//Again, ignores validation for purposes of quick example.
public class CardValueComparer : IEqualityComparer<Card>
{
public bool Equals(Card x, Card y)
{
return x.Val == y.Val ? true : false;
}
public int GetHashCode(Card c)
{
return c.Val.GetHashCode();
}
}
Given the above, the approach would be to first sort the cards based on the value of the card, not the suit, giving you the cards in descending order. Then create a subset of the distinct cards, again based only on card value, not suit. Then call into FindConsecutiveSequence specifying the Val property for comparison and the amount of elements you need for a valid sequence.
//Sort in descending order based on value of the card.
cardList.Sort((x,y) => y.Val.CompareTo(x.Val));
//Create a subset of distinct card values.
var distinctCardSet = cardList.Distinct(new CardValueComparer());
//Create a subset of consecutive sequential cards based on value, with a minimum of 5 cards.
var sequentialCardSet = distinctCardSet.FindConsecutiveSequence(p => Convert.ToInt32(p.Val), 5);
I think this should cover what you asked in the question and give you something to build on. However, if this if for poker, this logic will fail in the case where Ace can be a low value -> {A,2,3,4,5}. I didn't see mention of Ace specific logic needed, so perhaps you handle it outside of the scope of the question.
Order the Cards by Number then Take 1 and Skip 3 of them to select what you want.
Is this what you want?
First a random list of suits, but sequential list of cards values
Random random = new Random((int)(DateTime.Now.ToBinary() % Int32.MaxValue));
List<Card> hand = new List<Card>();
for(int card = (int)CARDS.Five;card <= (int)CARDS.Nine;card++)
{
SUIT suit = (SUITS)(random.Next(4)+1);
hand.Add(new Card { Suit = suit, Val = (CARDS)card });
}
Or a sequential list of suits would be...
for(int card = (int)CARDS.Five, int suit = (int)SUITS.Diamonds;card <= (int)CARDS.Nine;card++, suit++)
{
if(suit > (int)SUITS.Spades)
suit = (int)SUITS.Diamonds;
hand.Add(new Card { Suit = (SUITS)suit, Val = (CARDS)card });
}
Use the Aggregate method. You can modify the example code below to return various lengths of card lists, and change the while clause to check for the amount of cards that must match. (e.g. my version checks for Count == 5, you could check for Count >= 5, etc.)
public class Card {
public CARDS Val { get; set; }
public SUITS Suit { get; set; }
// added ToString for program below
public override string ToString() {
return string.Format("{0} of {1}", Val, Suit);
}
}
class Program {
static IEnumerable<Card> RandomList(int size) {
var r = new Random((int)DateTime.Now.Ticks);
var list = new List<Card>();
for (int i = 0; i < size; i++) {
list.Add(new Card {
Suit = (SUITS)r.Next((int)SUITS.Diamonds, (int)SUITS.Spades),
Val = (CARDS)r.Next((int)CARDS.Two, (int)CARDS.Ace)
});
}
return list.OrderBy(c => c.Val);
}
// generates a random list of 5 cards untill
// the are in sequence, and then prints the
// sequence
static void Main(string[] args) {
IEnumerable<Card> consecutive = null;
do {
// generate random list
var hand = RandomList(5);
// Aggreate:
// the passed in function is run for each item
// in hand. acc is the accumulator value.
// It is passed in to each call. The new List<Card>()
// parameter is the initial value of acc when the lambda
// is called on the first item in the list
// in the lambda we are checking to see if the last
// card in the accumulator value is one less
// than the current card. If so, add it to the
// accumulator, otherwise do not.
consecutive = hand.Aggregate(new List<Card>(), (acc, card) => {
var size = acc.Count != 0
? ((int)card.Val) - ((int)acc[acc.Count - 1].Val)
: 1;
if (size == 1)
acc.Add(card);
return acc;
});
} while (consecutive.Count() != 5);
foreach (var card in consecutive) {
Console.WriteLine(card);
}
Console.ReadLine();
}
}
The following method should get the best straight hand when supplied with seven cards (including the edge case of A-5), but I haven't tested it thoroughly.
The key point is that if you sort the cards into descending order and remove any duplicate values, there are only a few possible ways for the straight to be arranged (and you only need to check the extremities):
If the first and fifth cards are four apart, they represent the highest straight (since we know the cards between them have no duplicate values).
The same is true, in order, for the second and sixth cards and for the third and seventh cards (if there are that many unique values left).
The only other possibility is if we have an Ace at the start of the sorted list and the cards Five to Two at the end, representing a A-5 straight.
Here's the code:
public static IEnumerable<Card> GetBestStraight(IEnumerable<Card> sevenCards)
{
if (sevenCards.Count() != 7)
{
throw new ArgumentException("Wrong number of cards", "sevenCards");
}
List<Card> ordered = sevenCards.OrderByDescending(c => c.Val).ToList();
List<Card> orderedAndUnique = ordered.Where((c, i) => i == 0 || ordered[i].Val != ordered[i - 1].Val).ToList();
if (orderedAndUnique.Count < 5)
{
// not enough distinct cards for a straight
return Enumerable.Empty<Card>();
}
if (orderedAndUnique[0].Val == orderedAndUnique[4].Val + 4)
{
// first five cards are a straight
return orderedAndUnique.Take(5);
}
else if (5 < orderedAndUnique.Count && orderedAndUnique[1].Val == orderedAndUnique[5].Val + 4)
{
// next five cards are a straight
return orderedAndUnique.Skip(1).Take(5);
}
else if (6 < orderedAndUnique.Count && orderedAndUnique[2].Val == orderedAndUnique[6].Val + 4)
{
// last five cards are a straight
return orderedAndUnique.Skip(2).Take(5);
}
// if there's an A-5 straight, the above won't have found it (because Ace and Two are not consecutive in the enum)
if (orderedAndUnique[0].Val == CARDS.Ace && orderedAndUnique[orderedAndUnique.Count - 4].Val == CARDS.Five)
{
return orderedAndUnique.Where(c => c.Val == CARDS.Ace || c.Val <= CARDS.Five);
}
return Enumerable.Empty<Card>();
}
Stupid is as stipid does! Whay was I so worried about using LINQ when the quickest soulution was a simple bit mask:
List<Card> cardList = new List<Card>();
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Two });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = RANK.Three });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = RANK.Five });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Seven });
cardList.Add(new Card { Suit = SUITS.Hearts, Val = RANK.Four });
cardList.Add(new Card { Suit = SUITS.Clubs, Val = RANK.King });
cardList.Add(new Card { Suit = SUITS.Diamonds, Val = RANK.Ace });
int card = 0;
foreach (Card c in cardList)
{
card |= 1 << (int)c.Val - 1;
}
bool isStraight = false;
RANK high = RANK.Five;
int mask = 0x1F;
while (mask < card)
{
++high;
if ((mask & card) == mask)
{
isStraight = true;
}
else if (isStraight)
{
--high;
break;
}
mask <<= 1;
}
// Check for Ace low
if ((!isStraight) && ((0x100F & card) == 0x100F))
{
isStraight = true;
high = RANK.Five;
}
return card;
I have an unordered list that can look something like this:
1
2.2
1.1.1
3
When i sort the list, 1.1.1 becomes greater than 3 and 2.2, and 2.2 becomes greater than 3.
This is because Double.Parse removes the dots and makes it a whole number.
This is the method i use to sort with:
public class CompareCategory: IComparer<Category>
{
public int Compare(Category c1, Category c2)
{
Double cat1 = Double.Parse(c1.prefix);
Double cat2 = Double.Parse(c2.prefix);
if (cat1 > cat2)
return 1;
else if (cat1 < cat2)
return -1;
else
return 0;
}
}
How can i fix this?
Thanks
Are these version #s by chance? Can you use the Version class? It sorts each part as you seem to want, although it only works up to 4 parts. I would not recommend parsing into a numeric value like you are doing.
It has an IComparable interface. Assuming your inputs are strings, here's a sample:
public class CompareCategory: IComparer<Category>
{
public int Compare(Category c1, Category c2)
{
var cat1 = new Version(c1.prefix);
var cat2 = new Version(c2.prefix);
if (cat1 > cat2)
return 1;
else if (cat1 < cat2)
return -1;
else
return 0;
}
}
If you need something with more than 4 "parts", I think I would create a comparer which split the strings at the dots, and then parse each element as an integer and compare them numerically. Make sure to consider cases like 1.002.3 and 1.3.3 (what do you want the sort order to be?).
Update, here is a sample of what I mean. Lightly tested:
public class CategoryComparer : Comparer<Category>
{
public override int Compare(Category x, Category y)
{
var xParts = x.prefix.Split(new[] { '.' });
var yParts = y.prefix.Split(new[] { '.' });
int index = 0;
while (true)
{
bool xHasValue = xParts.Length > index;
bool yHasValue = yParts.Length > index;
if (xHasValue && !yHasValue)
return 1; // x bigger
if (!xHasValue && yHasValue)
return -1; // y bigger
if (!xHasValue && !yHasValue)
return 0; // no more values -- same
var xValue = decimal.Parse("." + xParts[index]);
var yValue = decimal.Parse("." + yParts[index]);
if (xValue > yValue)
return 1; // x bigger
if (xValue < yValue)
return -1; // y bigger
index++;
}
}
}
public static void Main()
{
var categories = new List<Category>()
{
new Category { prefix = "1" },
new Category { prefix = "2.2" },
new Category { prefix = "1.1.1" },
new Category { prefix = "1.1.1" },
new Category { prefix = "1.001.1" },
new Category { prefix = "3" },
};
categories.Sort(new CategoryComparer());
foreach (var category in categories)
Console.WriteLine(category.prefix);
}
Output:
1
1.001.1
1.1.1
1.1.1
2.2
3
public class CodeComparer : IComparer<string>
{
public int Compare(string x, string y)
{
var xParts = x.Split(new char[] { '.' });
var yParts = y.Split(new char[] { '.' });
var partsLength = Math.Max(xParts.Length, yParts.Length);
if (partsLength > 0)
{
for (var i = 0; i < partsLength; i++)
{
if (xParts.Length <= i) return -1;// 4.2 < 4.2.x
if (yParts.Length <= i) return 1;
var xPart = xParts[i];
var yPart = yParts[i];
if (string.IsNullOrEmpty(xPart)) xPart = "0";// 5..2->5.0.2
if (string.IsNullOrEmpty(yPart)) yPart = "0";
if (!int.TryParse(xPart, out var xInt) || !int.TryParse(yPart, out var yInt))
{
// 3.a.45 compare part as string
var abcCompare = xPart.CompareTo(yPart);
if (abcCompare != 0)
return abcCompare;
continue;
}
if (xInt != yInt) return xInt < yInt ? -1 : 1;
}
return 0;
}
// compare as string
return x.CompareTo(y);
}
}
Maybe you could just string compare it?
I'm surprised that Double.Parse doesn't throw an exception with those numbers with more than one decimal place.
You really need to write some rules about how to compare these strings.
I would split the strings using String.Split() on the dot character, then iterate through the two lists created and as soon as one of the levels contained a lower or higher number than the other, or if you ran out of items in one of the lists then you wold return 1 or -1 as appropriate. If you get to the end of both lists in the same iteration of the loop then they are the same and return 0.
I would write the code but I don't have VS in front of me.