2012-01-13 2 views
4

Я создаю систему покера, и в настоящее время я рационализирую свой ручной калькулятор.Linq - получение последовательных чисел в массиве

Следующий код работает:

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(); 
} 

Однако, я хочу, чтобы сделать его более компактным, как в:

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!) 
} 

Проблема заключается в том, что Шестерки повторяются. Отдельная функция, а также пользовательская функция сравнения не работает, так как это приведет к поломке предложения n + 1 join.

Другая проблема заключается в следующем списке карт:

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 }); 

получить список возвращаемый 3Hearts, 6Diamond, 7Hearts, 8Hearts так 2 и 3 являются последовательными.

Что я действительно хочу - это список, который возвращает последовательные карты 5 или более, или еще лучше, верхние 5 карт непрерывной последовательности. Таким образом, приведенный выше список будет пустым, так как в списке ввода нет 5 последовательных карт.

+1

Можете ли вы привести пример того, как должен выглядеть новый список? –

+0

Да, я хочу, чтобы список был 4Hearts, 5Clubs, 6Diamonds (или 6Spades, не оба), 7Hearts, 8Clubs. – NickV

ответ

0

Тупой как stipid делает! Я был так обеспокоен тем, что использовал LINQ, когда самая быстрая душа была простой битовой маской:

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; 
1

Закажите карточки по номеру, затем возьмите 1 и пропустите 3 из них, чтобы выбрать то, что вы хотите.

+0

Карты будут случайным списком (7 из 52), так что это не сработает. – NickV

+0

@NickV Так группируйте их по номеру и берете по одному от каждой группы –

0

Это то, что вы хотите?

Первый случайный список костюмов, но последовательный список карт значений

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 }); 
} 

Или последовательный список костюмов бы ...

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 }); 
} 
3

Если вы заинтересованы в получении подмножество карт от cardList, которые имеют самый высокий диапазон значений последовательных карт, независимо от их соответствия, рассмотрите этот подход.

//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(); 
    } 
} 

Учитывая вышесказанное, подход будет первым сортировать карты на основе стоимости карты, а не костюм, давая вам карты в порядке убывания. Затем создайте подмножество отдельных карточек, опять же основанное только на стоимости карты, не подходит. Затем позвоните в FindConsecutiveSequence, указав свойство Val для сравнения и количество элементов, необходимых для правильной последовательности.

//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); 

Я думаю, что это должно охватывать то, что вы задавали в вопросе, и дать вам что-то, на что можно опираться. Однако если это, если для покера, эта логика потерпит неудачу в случае, когда Ace может быть низким значением -> {A, 2,3,4,5}. Я не видел упоминания о конкретной логике Ace, поэтому, возможно, вы справитесь с ней вне сферы действия вопроса.

0

Используйте метод Aggregate. Вы можете изменить приведенный ниже пример кода, чтобы возвращать различные длины списков карточек, и изменить предложение while, чтобы проверить количество карт, которые должны совпадать. (Например, моя версия проверяет Count == 5, вы можете проверить Count> = 5 и т.д.)

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

Следующий метод должен получить лучшую прямую руку, когда поставляется с семью картами (в том числе краевой случае A-5), но я не проверил его полностью.

Ключевым моментом является то, что если сортировать карты в порядке убывания и удалить повторяющиеся значения, есть только несколько возможных способов для прямой, чтобы быть организованы (и вам нужно только проверить конечностями):

  • Если первая и пятая карты четыре, они представляют собой самую высокую прямую (поскольку мы знаем, что карты между ними не имеют повторяющихся значений).
  • То же самое верно для второго и шестого карт и для третьей и седьмой карт (если осталось много уникальных значений).
  • Единственная другая возможность - если у нас есть Ace в начале отсортированного списка и карты Five to Two в конце, представляющие A-5 прямо.

Вот код:

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>(); 
} 
Смежные вопросы