2012-01-13 87 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連接子句。

的另一個問題是與以下卡列表:

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

你可以舉一個新列表的例子嗎? – 2012-01-13 16:43:40

+0

是的,我希望名單是4Hearts,5Clubs,6Diamonds(或6Spades,不是兩個),7Hearts,8Clubs。 – NickV 2012-01-13 18:04:45

回答

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

這些卡片將是一個隨機列表(52箇中的7個),所以這將不起作用。 – NickV 2012-01-13 18:50:12

+0

@NickV所以按數字分組,並從每組中取一個 – 2012-01-14 11:28:49

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子句以檢查必須匹配的卡片數量。 (例如,用於計數== 5我的版本檢查,可以檢查計數> = 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,並且結尾處有五到二張牌,代表一個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>(); 
}