前言:
  我也是突然心血来潮, 想写写炸金花这类游戏的AI实现. 本文算是这一系列的第二篇, 主要写炸金花的胜率预估, 主要基于蒙特卡罗的思想, 胜率是炸金花AI的核心决策数据, ^_^.

相关文章:
  德州扑克AI--Programming Poker AI(译). 
  系列文章说来惭愧, 之前一直叫嚷着写德州AI, 不过可惜懒癌晚期, 一直没去实践, T_T. 相比而言,***简单很多, 也更偏重于运气和所谓的心理对抗.
  系列文章:
  1. 炸金花游戏的模型设计和牌力评估 
  2. 炸金花游戏的胜率预估 
  3. 基于EV(期望收益)的简单AI模型
  4. 炸金花AI基准测试评估
  5. 动态收敛预期胜率的一种思路

蒙特卡罗(Monte Carlo):
  该算法属于模拟统计, 通过大量的随机模拟, 来达到/接近精确解的方法, 简单有效.
  它的一个最有名的例子, 就是模拟求解PI(圆周率), 在2*2的正方形中区域中, 随机生成大量的点, 最后PI满足如下公式:

圆面积/正方形面=圆内覆盖的点数/全部点=PI/4

  
  这边不再具体阐述了, 具体可以参考博文: 蒙特卡罗(Monte Carlo)方法计算圆周率π

胜率预估:
  手牌胜率预估, 我们假定一副牌(52张), 玩家数N(2~6)之间变化, 在经历足够多的模拟随机发牌后, 手牌的胜率趋于真实值.
  伪代码如下(炸金花没有平局, 这里把牌力相等, 认为输):

 # 假定随即模拟10000局, 其他玩家n个sim_n = 10000player_n = 其他玩家数hand_cards = 玩家自己的手牌  # 玩家胜利的次数win_n = 0 for i in range(sim_n):players <- 随机给n个玩家发牌if 玩家的手牌 > 所有其他玩家的手牌:win_n += 1# 这次概率值, 就接近真实的胜率return win_n / sim_n 

  是不是觉得非常的简单, ^_^.

各类牌型的胜率统计:
  这边选择了一些典型的牌型, 看看它在不同的对局用户数下, 胜率的变化:

牌型/几人桌 两人桌 三人桌 四人桌 五人桌 六人桌
[HK, SK, DK] 豹子  0.9997  0.9997  0.9995  0.9989 0.9988
[HA, HK, HQ] 同花顺  0.997 0.9949   0.9926  0.9894 0.989
[HA, HK, HT] 金  0.9951  0.9869  0.9805  0.976 0.9668
[HA, HK, SQ] 顺  0.9425  0.8928  0.8427  0.8006 0.7506
[H9, D9, ST] 对子  0.847  0.7113  0.605  0.5197 0.4335
[H9, DA, ST] 高牌  0.6644  0.4423  0.292  0.1901 0.1245

  由此可见, 拿到顺以上的牌, 胜率相当的高, 而且随人数变化小. 拿到对子也是不错的牌, 需要根据对子本身的大小和参与人数来做一个合理的评估.

真实代码:
  贴一下代码:

import random
import timeCARD_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)class Shoe(object):def __init__(self, deck_num=1):""":param deck_num: 几副牌, 默认为1副牌"""self.deck_num = deck_numself.cards = [Card(s+c) for s in "HDSC" for c in "A23456789TJQK"] * self.deck_numself.idx = 0def reshuffle(self):# 打散牌self.idx = 0random.shuffle(self.cards)def deal(self, exc_arr=[]):""":param exc_arr: 发牌需要过滤掉的牌, 避免重复:return:"""while self.idx < len(self.cards):card = self.cards[self.idx]self.idx = self.idx + 1if str(card) in exc_arr:continuereturn cardreturn None# 核心思路和德州一致, 把牌力映射为一个整数
# 牌力组成: 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 << 12class ThreeCardEvaluator(object):"""工具类"""@staticmethoddef win_prop(cards, n=2, sim_n=10000):"""胜率计算:param cards::param n: 玩家数(包含玩家自己):param sim_n: 模拟的轮数, 轮数越多越接近真实值:return:"""random.seed(time.time())shoe = Shoe(deck_num=1)exc_arr = [str(_) for _ in cards]owner_hand_value = ThreeCardEvaluator.evaluate(cards)# 胜利次数win_n = 0for _ in xrange(sim_n):# 打散牌谱shoe.reshuffle()player_cards = []for j in xrange(n - 1):player_cards.append([shoe.deal(exc_arr=exc_arr) for _ in range(3)])# 统计其他玩家中最大的手牌值max_hand_value = max([ThreeCardEvaluator.evaluate(_) for _ in player_cards])if owner_hand_value > max_hand_value:win_n += 1# 大量模拟后的胜率return win_n * 1.0 / sim_n@staticmethoddef evaluate(cards):"""牌力值计算:param cards: 三张牌构成的手牌:return:"""if not isinstance(cards, list):return -1if len(cards) != 3:return -1vals = [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)# 剩下的高highreturn HIGH_TYPE + (vals[2] << 8) + (vals[1] << 4) + vals[2]@staticmethoddef __leopard(cards, vals):if cards[0].rank == cards[1].rank and cards[1].rank == cards[2].rank:return True, cards[0].valuereturn False, 0@staticmethoddef __flush(cards, vals):if cards[0].suit == cards[1].suit and cards[1].suit == cards[2].suit:return True, valsreturn False, []@staticmethoddef __straight(cards, vals):# 顺子按序递增if vals[0] + 1 == vals[1] and vals[1] + 1 == vals[2]:return True, vals[2]# 处理特殊的牌型, A23if vals[0] == 2 and vals[1] == 3 and vals[2] == 14:return True, 3return False, 0@staticmethoddef __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, []

  测试代码:

# !/usr/bin/env python
# -*- coding:utf-8 -*-import sys
reload(sys)
sys.setdefaultencoding("utf-8")if __name__ == "__main__":card_cases = [[Card('HK'), Card('SK'), Card('DK')],      # 豹子[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:p = ThreeCardEvaluator.win_prop(case, n=6, sim_n=10000)card = ', '.join([str(_) for _ in case])print "[{}] = {}".format(card, p)

  测试结果:

[HK, SK, DK] = 0.9988
[HA, HK, HQ] = 0.989
[HA, HK, HT] = 0.9668
[HA, HK, SQ] = 0.7506
[H9, D9, ST] = 0.4335
[H9, DA, ST] = 0.1245

总结:
  本文是炸金花系列的第二篇, 后续要讲讲炸金花AI的编写, ^_^, 希望自己能坚持.

  

转载于:https://www.cnblogs.com/mumuxinfei/p/10300235.html

炸金花游戏(2)--炸金花游戏的胜率预估相关推荐

  1. 炸金花游戏(4)--炸金花AI基准测试评估

    前言: 本文将谈谈如何评估测试炸金花的AI, 其实这个也代表一类的问题, 德州扑克也是类似的解法. 本文将谈谈两种思路, 一种是基于基准AI对抗评估, 另一种是基于测试集(人工选定牌谱). 由于炸金花 ...

  2. 炸金花游戏(1)--炸金花游戏的模型设计和牌力评估

    前言: 好久没写博客了, 今天来补上一篇, 是关于炸金花游戏模型的设计和牌力评估. 其核心思想和之前谈到过的德州模型很像, 本文也想为炸金花游戏这个系列开个头, 希望后面能写写AI相关的文章. 相关文 ...

  3. dnf打团显示服务器即将关闭,DNF:游戏服务器炸锅?当天在打团的小伙伴,你们还好吗...

    原标题:DNF:游戏服务器炸锅?当天在打团的小伙伴,你们还好吗 在2.23号这一天许多的DNF玩家都发现游戏是彻底的登不上去,变成了名副其实的掉线城与勇士.可在这一天,不单单是地下城变成了掉线城,就连 ...

  4. 计算机特色的小游戏,宅家必备小游戏-steam小型游戏推荐

    宅家必备小游戏-steam小型游戏推荐 2020-01-28 16:34:29 18点赞 67收藏 2评论 创作立场声明:所有游戏都为自己或者女票购买且玩过的,部分游戏有十多小时的游戏时间. 这几天想 ...

  5. 游戏策划设计中关于游戏节奏的控制

      如同乐章一般,每一款游戏都有游戏自身的节奏,这种节奏有的是游戏类型本身所赋予的,而有的则是游戏设计所带来的.而玩家在游戏过程中则能够体会到这种潜在的节奏感,同时对于设计者来说,把握和控制游戏节奏, ...

  6. 基于cocos2d-x的快速的游戏开发--回合制游戏

    2019独角兽企业重金招聘Python工程师标准>>> #基于cocos2d-x的快速的游戏开发--回合制游戏 开发时间:3天 开发工具:cocos2d-x和cocostudio 开 ...

  7. 格斗类游戏和休闲类游戏不同

    前阵子我开发了Match3D, 一个可以把三维动画输出成为swf的工具, 而且实现了swf渲染的实时三维角色动画, 这可以说是我真正推出的第一个flash第三方软件, 其实这以前, 我曾经开发过几个其 ...

  8. 在通知栏上玩游戏,Steve iOS 游戏实现思路

    在通知栏上玩游戏,Steve iOS 游戏实现思路 最近有一款游戏特别的火爆,叫做Steve ,一种可以在通知中心直接玩的游戏.作者的脑洞也是非常的大,实在让人佩服.其实实现起来也简单,就是用到了iO ...

  9. cocos游戏源码怎么用_亲子游戏怎么玩?游戏方式用对了,才会事半功倍

    孩子的出生对于一个家庭来说都是至关重要的大事.如何将孩子抚育好也是父母最关注的问题.从早教到兴趣辅导班.我们变着法子让孩子可以发展的更全面更优秀,请最好的老师.上最好的学校.住着大房子.我们忙着给家人 ...

最新文章

  1. Unity UGUI获取鼠标在屏幕的准确点击位置
  2. Python是如何一步步成为热门编程语言的?
  3. IO多路复用之poll
  4. 【WinCE】SD card技术了解并WINCE下SDHC驱动开发(updated)
  5. bzoj29894170: 数列
  6. Mysql不能远程连接的解决方法
  7. cosnπ为什么是离散信号_奥本海姆《信号与系统》(第2版)笔记和课后习题(含考研真题)详解(下册)复习答案考研资料...
  8. 【数字信号调制】基于matlab GUI QPSK调制+解调【含Matlab源码 646期】
  9. Matlab里c2d命令,matlab中c2d如何将连续函数离散化
  10. 如何入门Python与机器学习
  11. AR涂涂乐⭐八、(add)优化原代码“7”、正方形识别图改为矩形识别图、增加BGM和App 图标
  12. javaserver_JavaServer页面简介
  13. C++程序实现自我复制
  14. 一个工厂有m条流水线
  15. 制作属于自己的QQ机器人,超详细
  16. 教你 IntelliJ IDEA 永久激活,建议收藏!(转)
  17. y=asin(wx+φ)的对称中心_函数y=Asin(wx+φ)图像和性质
  18. xml转pdf xsl取值问题 语法问题
  19. sklearn数据预处理(二)非线性转换
  20. 041 :魔法方法:构造和析构

热门文章

  1. 【IPFS周报-101】查看胡安、V神,诸多大佬的峰会演讲
  2. java写文件用二进制分割_java分割二进制文件
  3. 世界杯:用Python分析热门夺冠球队
  4. python爬取微信读书APP的书单
  5. 伦敦用人脸识别抓错人!专家:要结合DNA技术才行 | 研究
  6. 梅西百货公司 - Macy‘s - Shop Fashion Clothing Accessories
  7. sparksql insertinto 源码解析
  8. 程序员兼职接私活平台大全,兼职也能月薪上万
  9. 蓝桥杯JAVA答题技巧,第九届蓝桥杯大赛个人赛省赛(软件类)C/C++ 大学B组比赛心得(还在更新)...
  10. SpringBoot在线教育项目(十一)