Linq - 在数组中获取连续数字

时间:2012-01-13 16:22:53

标签: c# 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!)
}

问题在于Sixes正在重演。不同以及自定义比较功能不起作用,因为这会破坏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个连续的卡。

6 个答案:

答案 0 :(得分: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可以是低值的情况下,这种逻辑将失败 - &gt; {A,2,3,4,5-}。我没有看到提到所需的Ace特定逻辑,所以也许你可以在问题范围之外处理它。

答案 1 :(得分:1)

按编号订购卡片,然后选择1并跳过3,选择你想要的。

答案 2 :(得分: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 :(得分:0)

使用Aggregate方法。您可以修改下面的示例代码以返回各种长度的卡片列表,并更改while子句以检查必须匹配的卡片数量。 (例如,我的版本检查Count == 5,您可以检查Count&gt; = 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();
    }
}

答案 4 :(得分: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>();
}

答案 5 :(得分:0)

愚蠢是愚蠢的!当最快的解决方案是一个简单的位掩码时,我非常担心使用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;