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.
Related
I am trying to join the next array element together to single element from array if the element of the array is less than length 4. It should add to the next element index.
Another logic is, if the next consecutive array length is also less then 4 char then it joins the next array element also up to 3 times in total. I want to implement this. It's getting complex for me I am not understand this logic.
This is the code, here it has array on titleList, now we have to use the above logic in this array list.
#foreach (var title in randomtitle)
{
</span>#titleList.ElementAt(title)</span>
}
#code {
[Parameter]
public string theTitle { get; set; }
private string[] titleList = Array.Empty<string>();
protected override void OnParametersSet()
{
if (!string.IsNullOrEmpty(theTitle))
{
titleList = theTitle.Split(" ");
}
}
private Random random = new();
private IEnumerable<int> randomtitle =>
Enumerable.Range(0, titleList.Count() - 1) // { 0, 1, 2, 3 } generate sequence
.OrderBy(x => random.Next()) // { 3, 1, 0, 2 } random shuffle
.Take(2) // { 3, 1 } pick two
.ToList();
}
I think you are looking for a method that does following:
take a collection of strings(like a string[])
iterate each string and check if it's length is greater than or equal 4
if so, everything is fine, take it as it is
if not, append the next to the current string and check their length afterwards
if they are still not greater than or equal 4 append the next one, but max 3 times
Then this should work (not tested well though):
Demo: https://dotnetfiddle.net/2uHREv
static string[] MergeItems(IList<string> all, int minLength, int maxGroupCount)
{
List<string> list = new List<string>(all.Count);
for(int i = 0; i < all.Count; i++)
{
string current = all[i];
if(i == all.Count - 1)
{
list.Add(current);
break;
}
if (current.Length < minLength)
{
for (int ii = 1; ii < maxGroupCount && i + ii < all.Count; ii++)
{
int nextIndex = i + ii;
string next = all[nextIndex];
current = current + next;
if (current.Length >= minLength || ii+1 == maxGroupCount)
{
list.Add(current);
i = nextIndex;
break;
}
}
}
else
{
list.Add(current);
}
}
return list.ToArray();
}
Question: You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N.
Examples
[2, 4, 0, 100, 4, 11, 2602, 36]
Should return: 11 (the only odd number)
[160, 3, 1719, 19, 11, 13, -21]
Should return: 160 (the only even number)
Problem: I can find the target number. It is "6" in this instance. But when i try to write it, it is written 3 times. I did not understand why it is not written once.
namespace sayitoplamları
{
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] {1,3,5,6};
int even = 0;
int odd = 0;
foreach (var num in integers)
{
if (num%2==0)
{
even += 1;
}
else
{
odd += 1;
}
if (even>1)
{
foreach (var item in integers)
{
if (item%2!=0)
{
Console.WriteLine(item);
}
}
}
else if (odd>1)
{
foreach (var item in integers)
{
if (item%2==0)
{
Console.WriteLine(item);
}
}
}
}
}
}
}```
Since we are doing this assignment for a good grade :)
We really don't need to count both odd and even elements and can just count odd once as it more convenient - just add up all remainders ( odd % 2 = 1, even % 2 = 0). If we would need number of even elements it would be array.Length - countOdd.
int countOdd = 0;
for (int i = 0; i < array.Length; i++) // or foreach
{
countOdd += array[i] % 2;
}
If you feel that trick with counting is too complicated and only needed for A+ mark on the assignment than variant of your original code is fine:
int countOdd = 0;
foreach (int element in array)
{
if (element % 2 == 1)
countOdd ++; // which is exactly countOdd += 1, just shorter.
}
Now for the second part - finding the outlier. We don't even need to know if it is Odd or Even but rather just reminder from "% 2". We now know how many odd element - which could be either 1 (than desired remainder is 1 - we are looking for odd element) or more than one (then desired remainder is 0 as we are looking for even one).
int desiredRemainder = countOdd == 1 ? 1 : 0;
Now all its left is to check which element has desired remainder and return from our method:
foreach (int element in array)
{
if (element % 2 == desiredRemainder)
{
return element;
}
}
Please note that assignment asks for "return" and not "print". Printing the value does not allow it to be used by the caller of the method unlike returning it. Using early return also saves some time to iterate the rest of array.
Complete code:
int FindOutlier(int[] array)
{
int desiredReminder = array.Sum(x => x % 2) == 1 ? 1 : 0;
return array.First(x => x % 2 == desiredReminder);
}
Alternative solution would be to iterate array only once - since we need to return first odd or first even number as soon as we figure out which one repeats similar how your tried with nested foreach. Notes since we are guaranteed that outlier present and is only one we don't need to try to remember first number - just current is ok.
int FindOutlier(int[] array)
{
int? odd = null; // you can use separate int + bool instead
int? even = null;
int countOdd = 0;
int countEven = 0;
foreach (int element in array)
{
bool isEven = element % 2 == 0;
if (isEven)
{
countEven++;
even = element;
}
else
{
countOdd++;
odd = element;
}
if (countEven > 1 && odd.HasValue)
return odd.Value;
if (countOdd > 1 && even.HasValue)
return even.Value;
}
throw new Exception("Value must be found before");
}
And if you ok to always iterate whole list code will be simple as again we'd need to only keep track of count of odd numbers (or even, just one kind) and return would not need to check if we found the value yet as we know that both type of numbers are present in the list:
int FindOutlier(int[] array)
{
int odd = 0;
int even = 0;
int countEven = 0;
foreach (int element in array)
{
if (element % 2 == 0)
{
countEven++;
even= element;
}
else
{
odd = element;
}
}
return countEven > 1 ? odd : even;
}
You are mixing everything into a single algorithm. In most cases it's a better idea to have separate steps. In your case, the steps could be:
Count odds and evens
Find the outlier
Print the result
That way you can make sure that the output does not interfere with the loops.
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] {1,3,5,6};
int even = 0;
int odd = 0;
// Count odds and evens
foreach (var num in integers)
{
if (num%2==0)
{
even += 1;
}
else
{
odd += 1;
}
}
// Find the outlier
var outlier = 0;
foreach (var item in integers)
{
if (even == 1 && item%2==0)
{
outlier = item;
break;
}
if (odd == 1 && item%2!=0)
{
outlier = item;
break;
}
}
// Print the result
Console.WriteLine(outlier);
}
}
You could even make this separate methods. It also makes sense due to SoC (separation of concerns) and if you want to achieve testability (unit tests).
You want a method anyway and use the return statement, as mentioned in the assignment
Write a method that takes the array as an argument and returns this "outlier" N.
Maybe you want to check the following code, which uses separate methods:
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] { 1, 3, 5, 6 };
var outlier = FindOutlier(integers);
Console.WriteLine(outlier);
}
private static int FindOutlier(int[] integers)
{
if (integers.Length < 3) throw new ArgumentException("Need at least 3 elements.");
bool isEvenOutlier = IsEvenOutlier(integers);
var outlier = FindOutlier(integers, isEvenOutlier ? 0:1);
return outlier;
}
static bool IsEvenOutlier(int[] integers)
{
// Count odds and evens
int even = 0;
int odd = 0;
foreach (var num in integers)
{
if (num % 2 == 0)
{
even += 1;
}
else
{
odd += 1;
}
}
// Check which one occurs only once
if (even == 1) return true;
if (odd == 1) return false;
throw new ArgumentException("No outlier in data.", nameof(integers));
}
private static int FindOutlier(int[] integers, int remainder)
{
foreach (var item in integers)
{
if (item % 2 == remainder)
{
return item;
}
}
throw new ArgumentException("No outlier in argument.", nameof(integers));
}
}
The reason it is printing '6' 3 times is because you iterate over the inetegers inside a loop where you iterate over the integers. Once with num and again with item.
Consider what happens when you run it against [1,3,5,6].
num=1, you get odd=1 and even=0. Neither of the if conditions are true so we move to the next iteration.
num=3, you get odd=2 and even=0. odd > 1 so we iterate over the integers (with item) again and print '6' when item=6.
num=5, you get odd=3 and even=0. Again, this will print '6' when item=6.
num=6, you get odd=3 and even=1. Even though we incremented the even count, we still have odd > 3 so will print '6' when item=6.
It is best to iterate over the values once and store the last even and last odd numbers along with the count (it is more performant as well).
You can then print the last odd number if even > 0 (as there will only be one odd number, which will be the last one) or the last even number if odd > 0.
I.e.
namespace sayitoplamları
{
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] { 1, 3, 5, 6 };
int even = 0;
int? lastEven = null;
int odd = 0;
int? lastOdd = null;
foreach (var num in integers)
{
if (num % 2 == 0)
{
even += 1;
lastEven = num;
}
else
{
odd += 1;
lastOdd = num;
}
}
if (even > 1)
{
Console.WriteLine(lastOdd);
}
else if (odd > 1)
{
Console.WriteLine(lastEven);
}
}
}
}
because you didn't close the the first foreach loop before printing...
namespace sayitoplamları
{
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] {1,3,5,6};
int even = 0;
int odd = 0;
foreach (var num in integers)
{
if (num%2==0)
{
even += 1;
}
else
{
odd += 1;
}//you need to close it here
if (even>1)
{
foreach (var item in integers)
{
if (item%2!=0)
{
Console.WriteLine(item);
}
}
}
else if (odd>1)
{
foreach (var item in integers)
{
if (item%2==0)
{
Console.WriteLine(item);
}
}
}
}
}//not here
}
}
I have a time sensitive application where I get the rank of each object and work out who won or tied for first. With my current algorithm I get some lag in my program so am hoping to speed it up. In this demo version I just get a random number for the ranks as the real rank calc requires too much code to post.
Can anybody suggest how I might do this more quickly as the Update method can get called tens of thousands of times for a single run?
class Program
{
static int runOuts = 0;
static Dictionary<RankedObject, int> dictionaryWins = new Dictionary<RankedObject, int>();
static Dictionary<RankedObject, int> dictionaryDraws = new Dictionary<RankedObject, int>();
static Random random = new Random();
static List<RankedObject> rankedObjects = new List<RankedObject>();
static void Main(string[] args)
{
// create some objects for demo
rankedObjects.Add(new RankedObject());
rankedObjects.Add(new RankedObject());
rankedObjects.Add(new RankedObject());
// add each object to the win/draw dictionaries so we can count wins/draws for each
foreach (RankedObject rankedObj in rankedObjects)
{
dictionaryWins.Add(rankedObj, 0);
dictionaryDraws.Add(rankedObj, 0);
}
// calculate wins/draws many times
for (int i = 0; i < 10000; i++)
{
Update();
}
// set equity results in each ranked combo and print results
foreach (RankedObject rankedCombo in rankedObjects)
{
rankedCombo.WinEquity = dictionaryWins[rankedCombo] / (double)runOuts;
rankedCombo.DrawEquity = dictionaryDraws[rankedCombo] / (double)runOuts;
Console.WriteLine(rankedCombo);
}
}
private static void Update()
{
// keep a list of ranks for each object so we can get max/winning value easily
List<int> ranks = new List<int>();
// get a rank for each object
foreach (RankedObject rankedCombo in rankedObjects)
{
int rank = random.Next(3);
ranks.Add(rank);
rankedCombo.Rank = rank;
}
// get the maximum rank and how many times it occurs, so we know if their is a tie for the win
int max = ranks.Max();
int maxOccurences = 0;
foreach (int i in ranks)
{
if (i == max)
{
maxOccurences++;
}
}
// loop over each object to record if the object won or tied for win
foreach (RankedObject rankedObj in rankedObjects)
{
if (rankedObj.Rank == max && maxOccurences == 1) // current rankedObj was winner
{
dictionaryWins[rankedObj] += 1;
}
else if (rankedObj.Rank == max && maxOccurences > 1) // current rankedObj Tied for win
{
dictionaryDraws[rankedObj] += 1;
}
}
runOuts++;
}
}
class RankedObject
{
int rank;
double winEquity;
double drawEquity;
public int Rank { get => rank; set => rank = value; }
public double WinEquity { get => winEquity; set => winEquity = value; }
public double DrawEquity { get => drawEquity; set => drawEquity = value; }
public override string ToString()
{
return "Win Equity: " + winEquity + ", Draw Equity: " + drawEquity;
}
}
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));
}
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;