Linq - getting consecutive numbers in an array

I am creating a poker system, and currently I am rationalizing 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 poles are repeated. A separate as well as a custom comparison function does not work, as this violates the n + 1 join clause.

Another problem is the following list of cards:

  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 sequential.

What I really want is a list that returns consecutive cards of size 5 or more, or, even better, the top 5 cards of a continuous sequence. Thus, the list above will be empty, since there are no 5 consecutive cards in the input list.

+4
source share
6 answers

Stupid how a street does it! I was so worried about using LINQ when the fastest soul was a simple bitmask:

  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; 
0
source

If you are interested in getting a subset of cards with a cardList that has the highest range of serial card values, regardless of whether this approach is suitable.

 //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 is to first sort the cards based on the value of the card, not the suit, giving you cards in descending order. Then create a subset of individual cards, again based only on the value of the card, not suitable. Then go to FindConsecutiveSequence , specifying the Val property for comparison and the number of elements needed for the correct 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 rely on. However, if it is, if for poker, this logic will fail if Ace can be a low value β†’ {A, 2,3,4,5}. I have not seen mention of the specific Ace logic, so maybe you can handle it outside the scope of the question.

+3
source

Order cards by number, then select β€œ1” and β€œSkip 3” to select what you want.

+1
source

Is this what you want?

First a random list of suits, but a list of consecutive lists of cards

 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 costumes will 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 }); } 
0
source

Use the aggregate method. You can modify the code example below to return different lengths of card lists, and change the while clause to check the number of cards that should match. (for example, my version checks Count == 5, you can check 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(); } } 
0
source

The following method should get the best straight hand when it comes with seven cards (including the A-5 edge), but I have not tested it completely.

The key point is that if you sort the cards in descending order and delete any duplicate values, there are only a few possible ways to arrange the line (and you only need to check the limbs):

  • If the first and fifth cards are divided into four, they represent the highest straight line (since we know that the cards between them have no duplicate values).
  • The same is true for the second and sixth cards and for the third and seventh cards (if there are many unique values ​​left).
  • The only other possibility is that we have Ace at the beginning of the sorted list and Five to Two cards at the end, representing the straight A-5.

Here is 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 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>(); } 
0
source

Source: https://habr.com/ru/post/1390870/


All Articles