炸金花遊戲(1)--炸金花遊戲的模型設計和牌力評估

 

前言:
  很久沒寫博客了, 今天來補上一篇, 是關於炸金花遊戲模型的設計和牌力評估. 其核心思想和以前談到過的德州模型很像, 本文也想爲炸金花遊戲這個系列開個頭, 但願後面能寫寫AI相關的文章.html

 

相關文章:
  德州撲克AI--Programming Poker AI(譯)
  系列文章說來慚愧, 以前一直叫嚷着寫德州AI, 不過惋惜懶癌晚期, 一直沒去實踐, T_T. 相比而言, 炸金花簡單不少, 也更偏重於運氣和所謂的心理對抗.
  系列文章:
  1. 炸金花遊戲的模型設計和牌力評估 
  2. 炸金花遊戲的勝率預估 
  3. 基於EV(指望收益)的簡單AI模型
  4. 炸金花AI基準測試評估
  5. 動態收斂預期勝率的一種思路python

 

遊戲規則簡介:
  炸金花是每一個參與玩家, 手握三張底牌, 經過跟牌/加註/PK等操做, 最後決出最後勝利者的遊戲. 它和德州不同, 它沒有公共牌這個變量, 在發牌就決定了牌力大小, 波動也小, 最後的結果取決於玩家的心理對抗和策略了.
  函數

  迴歸主題, 炸金花的牌力大小, 按以下規則來定義:
  豹子(炸彈) > 順金(同花順) > 金(同花) > 順子 > 對子 > 高牌
  這邊要特別說明下, 若是按照出現機率來評定牌力大小, 出現豹子(炸彈)的機率>順金的機率, 即順金理應比豹子大, 但炸金花約定, 豹子>順金, 這算特例(須要尊重).工具

 

模型設計:
  和德州同樣, 咱們這邊把三張手牌映射爲一個可比較的整數. 其牌力大小和整數數值成正比.
  先定義牌型:測試

# 高high
HIGH_TYPE = 0

# 對子
PAIR_TYPE = 1 << 12

# 順子
STRAIGHT_TYPE = 2 << 12

# 同花(金)
FLUSH_TYPE = 3 << 12

# 同花順
STRAIGHT_FLUSH_TYPE = 4 << 12

# 豹子
LEOPARD_TYPE = 5 << 12

  牌力值其由4個半字節(4 * 4 = 16位)組成, 最高半字節爲牌型, 後續三個半字節爲該牌型下, 最大的手牌值填充.
  1. 牌型爲豹子, 第二高半字節爲豹子手牌數值, 三/四半字節缺省爲0, 如(HA, DA, SA) => [5, 14, 0, 0].
  2. 牌型爲順金, 第二高半字節爲順子中最大的數值, 三/四半字節缺省爲0, 如(HA, HK, HQ) => [4, 14, 0, 0].
  3. 牌型爲金, 二到四半字節, 依次存放排序後的手牌值, 如(HA, HK, HT) => [3, 14, 13, 10].
  4. 牌型爲順, 第二高半字節爲順子中最大的手牌數值, 三/四半字節缺省爲0, 如(HA ,HK, SQ) => [2, 14, 0, 0].
  5. 牌型爲對子, 第二高半字節爲對子手牌, 三半字節爲剩下的單牌, 四半字節缺省爲0, 如(H9, D9, ST) => [1, 9, 10, 0].
  6. 牌型爲高牌, 二到四半字節, 依次存放排序後的手牌值, 如(H9, DA, ST) => [0, 14, 10, 9].優化

 

核心代碼:
  定義常量:ui

# !/usr/bin/env python
# -*- coding:utf-8 -*-

import sys
reload(sys)
sys.setdefaultencoding("utf-8")


CARD_CONST = {
    "A": 14,
    "2": 2,
    "3": 3,
    "4": 4,
    "5": 5,
    "6": 6,
    "7": 7,
    "8": 8,
    "9": 9,
    "T": 10,
    "J": 11,
    "Q": 12,
    "K": 13
}


class Card(object):
    """
        牌的花色+牌值
    """
    def __init__(self, val):
        self.suit = val[0]
        self.rank = val[1]
        self.value = CARD_CONST[val[1]]

    def __str__(self):
        return "%s%s" % (self.suit, self.rank)

  核心評估函數:lua

# 核心思路和德州一致, 把牌力映射爲一個整數
# 牌力組成: 4個半字節(4位), 第一個半字節爲牌型, 後三個半字節爲牌型下最大的牌值
# 牌型, 0: 單張, 1: 對子, 2: 順子, 3: 金, 4: 順金, 5: 豹子

# 高high
HIGH_TYPE = 0

# 對子
PAIR_TYPE = 1 << 12

# 順子
STRAIGHT_TYPE = 2 << 12

# 同花(金)
FLUSH_TYPE = 3 << 12

# 同花順
STRAIGHT_FLUSH_TYPE = 4 << 12

# 豹子
LEOPARD_TYPE = 5 << 12


class ThreeCardEvaluator(object):
    """
    工具類
    """

    @staticmethod
    def evaluate(cards):
        if not isinstance(cards, list):
            return -1
        if len(cards) != 3:
            return -1

        vals = [card.value for card in cards]
        # 默認是從小到大排序
        vals.sort()

        # 豹子檢測
        leopard_res, leopard_val = ThreeCardEvaluator.__leopard(cards, vals)
        if leopard_res:
            return LEOPARD_TYPE + (vals[0] << 8)

        # 同花檢測
        flush_res, flush_list = ThreeCardEvaluator.__flush(cards, vals)
        # 順子檢測
        straight_res, straight_val = ThreeCardEvaluator.__straight(cards, vals)

        if flush_res and straight_res:
            return STRAIGHT_FLUSH_TYPE + (straight_val << 8)
        if flush_res:
            return FLUSH_TYPE + (flush_list[2] << 8) + (flush_list[1] << 4) + flush_list[2]
        if straight_res:
            return STRAIGHT_TYPE + (straight_val << 8)

        # 對子檢測
        pair_res, pair_list = ThreeCardEvaluator.__pairs(cards, vals)
        if pair_res:
            return PAIR_TYPE + (pair_list[0] << 8) + (pair_list[1] << 4)

        # 剩下的高high
        return HIGH_TYPE + (vals[2] << 8) + (vals[1] << 4) + vals[2]

    @staticmethod
    def __leopard(cards, vals):
        if cards[0].rank == cards[1].rank and cards[1].rank == cards[2].rank:
            return True, cards[0].value
        return False, 0

    @staticmethod
    def __flush(cards, vals):
        if cards[0].suit == cards[1].suit and cards[1].suit == cards[2].suit:
            return True, vals
        return False, []

    @staticmethod
    def __straight(cards, vals):
        # 順子按序遞增
        if vals[0] + 1 == vals[1] and vals[1] + 1 == vals[2]:
            return True, vals[2]
        # 處理特殊的牌型, A23
        if vals[0] == 2 and vals[1] == 3 and vals[2] == 14:
            return True, 3
        return False, 0

    @staticmethod
    def __pairs(cards, vals):
        if vals[0] == vals[1]:
            return True, [vals[0], vals[2]]
        if vals[1] == vals[2]:
            return True, [vals[1], vals[0]]
        return False, []

  

測試集:
  編寫一些casespa

if __name__ == "__main__":

    card_cases = [
        [Card('HA'), Card('SA'), Card('DA')],      # 豹子
        [Card('HA'), Card('HK'), Card('HQ')],      # 順金
        [Card('HA'), Card('HK'), Card('HT')],      # 金
        [Card('HA'), Card('HK'), Card('SQ')],      # 順子
        [Card('H9'), Card('D9'), Card('ST')],      # 對子
        [Card('H9'), Card('DA'), Card('ST')]       # 高牌
    ]

    for case in card_cases:
        card = ', '.join([str(_) for _ in case])
        hand_value = ThreeCardEvaluator.evaluate(case)
        print "[{}] = {}".format(card, hand_value)

  測試的輸出結果:設計

[HA, SA, DA] = 24064
[HA, HK, HQ] = 19968
[HA, HK, HT] = 16094
[HA, HK, SQ] = 11776
[H9, D9, ST] = 6560
[H9, DA, ST] = 3758

  

總結:   總的來講, 炸金花的核心模型和牌力映射比德州簡單多了, 由於其沒有組合優化的問題, 因此比較直接暴力. 後續的文章, 但願本身寫寫AI方面的想法, ^_^.   對待博彩遊戲, 但願你們娛樂心態行娛樂之事, 切勿賭博, ^_^.

相關文章
相關標籤/搜索