自己實現鬥地主引擎

周見智發表於2018-06-05

對於所有類似鬥地主這種卡牌類遊戲,其實遊戲思路都是差不多的。先判斷出牌是否是‘有效牌型’,若是,再判斷該牌型的權重值用來比較大小。本篇文章將介紹如何實現一個鬥地主的卡牌遊戲引擎,洗牌、發牌、牌型檢查並比較大小。核心程式碼比較完整,後面給了一個GUI的demo,完成了洗牌、發牌、選牌出牌,牌型檢查,但是沒有實現電腦自動出牌的功能,有興趣的可以down下來看一下。

Github原始碼 

 

實現思路

1)根據鬥地主規則,羅列出所有可能出現的牌型。比如‘單張’、‘對子’、‘三連對’、‘五連順’、‘六連順’、‘飛機’、‘炸彈’等等。一共37種牌型;

2)對於上面的37種牌型,每種牌型又存在多種情況,對於‘單張’而言,存在3、4、A、2、小王、大王等等,並且其權重值依次增大。同理,對於‘五連順’而言,存在3-4-5-6-7、4-5-6-7-8...10-J-Q-K-A等等,並且其權重值依次增大。列舉出所有細分牌型,併為其賦予權重值,下一次就會以O(1)的時間複雜度快速判斷輸入卡牌是否是有效牌型;

3)在鬥地主遊戲中,預設只有相同牌型才能比較大小(比較其權重值),但是‘炸彈’牌型(四張相同的卡牌或者一對王)是個例外;

4)鬥地主遊戲中,不分花色。所以一對‘紅桃A+黑桃A’跟一對‘梅花A+方塊A’是一樣大的。因此在引擎計算中,需要先將所有有花色的輸入卡牌,全部去除花色,然後再計算;

 

資料字典初始化

以‘K-Q-Q-Q-K-K-Q-K’輸入手牌為例(輸入手牌可以無序),存在以下資料字典:

[Key]       ==>  K-K-K-K-Q-Q-Q-Q(降序排序,保證key唯一)

[Values]  ==> 

             {Display=K-K-K-K-Q-Q-Q-Q,  Name=四帶兩對,     Weight=300}

             {Display=Q-Q-Q-Q-K-K-K-K,  Name=四帶兩對,     Weight=200}

             {Display=K-K-K-Q-Q-Q-K-Q,  Name=飛機帶兩張”,  Weight=300}

對於同一個輸入手牌,對應三種牌型。對於任何輸入,都能通過O(1)時間複雜度判斷該輸入是否有效,若有效,返回對應牌型資料(可以包含多個,綠色部分為相同牌型,可以比較大小)。

資料字典初始化需要一定時間,本機測試大約耗時5秒。關於牌型檢查這裡,通過機器學習應該可以快速判斷出牌型,但是並不能得出其對應的權重值,要想得到權重值,還是需要通過列舉(有疑問?)

 

卡牌定義

4種花色

        /// <summary>
        /// 4 colors
        /// diamonds, clubs, hearts, spades
        /// </summary>
        public static List<String> CardColors = new List<string> {"D", "C", "H", "S" };

15張不帶花色卡牌

        /// <summary>
        /// 15 kinds of card without color
        /// 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace, 2, Little joker, Big joker
        /// </summary>
        public static List<String> CardValues = new List<string> {"3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A", "2", "LJ", "BJ" };

54張全部帶花色卡牌(‘K*H’代表‘紅桃K’,‘4*D’代表‘方塊4’)

        /// <summary>
        /// return all cards with color in the game
        /// 3*D 3*C 3*H 3*S 4*D 4*C 4*H 4*S 5*D 5*C 5*H 5*S  ... 2*D 2*C 2*H 2*S LJ BJ 
        /// </summary>
        public static List<String> Cards
        {
            get
            {
                List<String> cards = new List<string>();

                // 13*4==52
                for (int index = 0; index < 13; index++ )
                {
                    foreach (String card_color in CardColors)
                    {
                        cards.Add(CardValues[index] + "*" + card_color);  //  value*color
                    }
                }

                // 52+2 == 54
                cards.Add(CardValues[13]);
                cards.Add(CardValues[14]);

                return cards;
            }
        }

 

洗牌

隨機打亂54張帶花色卡牌,並以List<String>的形式返回

        /// <summary>
        /// shuffle the cards. 
        /// </summary>
        /// <returns>a list of 54 cards. has color but disordered.</returns>
        public List<String> Shuffle()
        {
            List<String> cards = EngineValues.Cards;

            Random rand = new Random(Guid.NewGuid().GetHashCode());
            List<String> newList = new List<String>();

            foreach (String card in cards)
            {
                newList.Insert(rand.Next(0, newList.Count), card);
            }

            // need reorder the first element
            newList.Remove(cards[0]);
            newList.Insert(rand.Next(0, newList.Count), cards[0]);

            return newList;
        }

 

發牌

傳入5個引數,分別是:

  • 洗完牌後的list(作為輸入, 54張)
  • 第一個人發牌list(作為輸出, 17張)
  • 第二個人發牌list(作為輸出, 17張)
  • 第三個人發牌list(作為輸出, 17張)
  • 底牌list(作為輸出, 3張)
        /// <summary>
        /// deal the cards to 3 parts, 17 cards(such as A*H, BJ, 4*D ...) for each part, another 3 cards will be asigned to the guy who is the landlord.
        /// </summary>
        /// <param name="original_cards">the original cards list, returned by Shuffle method.</param>
        /// <param name="first">the first part</param>
        /// <param name="second">the second part</param>
        /// <param name="third">the third part</param>
        /// <param name="last_cards">the last 3 cards</param>
        /// <returns>true if deal successfully</returns>
        public bool Deal(List<String> original_cards, List<String> first, List<String> second, List<String> third, List<String> last_cards)
        {
            // check if the parameters are valid
            if (original_cards == null 
                || original_cards.Count != 54
                || first == null
                || first.Count != 0
                || second == null
                || second.Count != 0
                || third == null
                || third.Count != 0
                || last_cards == null
                || last_cards.Count != 0)
            {
                return false;
            }

            // simulate player catch the card, everyone has 17 times.
            for (int index = 0; index < 17; index++)
            {
                first.Add(original_cards[index * 3]);
                second.Add(original_cards[index * 3 + 1]);
                third.Add(original_cards[index * 3 + 2]);
            }
            
            // the last 3 cards
            last_cards.Add(original_cards[51]);
            last_cards.Add(original_cards[52]);
            last_cards.Add(original_cards[53]);

            return true;
        }

 

檢查牌型

根據輸入手牌,直接通過資料字典查詢,返回結果中包含對應權重值。

public List<CardType> Check(List<String> input)
{
    //format the input, remove color if it has, sort the card
    String formated_input = EngineTool.FormatCardStr(String.Join("-", input));
    List<CardType> result = new List<CardType>();
    if (EngineValues.Set.ContainsKey(formated_input))
    {
        result.AddRange(EngineValues.Set[formated_input]);
    }
    return result;
}

 

 

相關文章