麻将查表法(2)

参考

查表思路(无自成顺,无牌的位置)
一开始以为这是个坑(指视频),思路和lua其实都不错,代码上有学习和借鉴

对比 查表法1

查表1 查表2
生成key值方式 把手牌抽象压缩转换 先对手牌颜色分类,然后再对手牌抽象转换
缺点 过于抽象,特别是要找到最大的百搭,还有字成顺,一个文件数据量过大 ——
优点 —— 分而治之,减少单个文件量,更简单的满足一些需求

生成表的结构(Key,Value)

在这里插入图片描述

  • Key
    同种颜色牌的数量组成
  • Value (借鉴 查表1)
    用uint32_t 存储,记录一些我需要知道的牌的信息
    - 开始bit位 占据的bit位 值的范围
    对子的数量 0 4 (0~1)
    对子的值 4 4 (0~9)
    最大的百搭值 8 4 (0~9)
    百搭的值1 12 4 (0~9)
    百搭的值2 16 4 (0~9)
    百搭的值3 20 4 (0~9)
    百搭的值3 24 4 (0~9)

例:
能胡的手牌 (0x12,0x12,0x12),(0x21,0x22,0x23,0x27,0x28,0x29),(0x32,0x33,0x34),(0x43,0x43),

1 2 3 4 5 6 7 8 9 key value
0 3 0 0 0 0 0 0 0 30,000,000 0
1 1 1 0 0 0 1 1 1 111,000,111 0
0 1 1 1 0 0 0 0 0 11,100,000 0
0 0 2 0 0 0 0 - - 20,000 0011 0001(49)

生成所有能胡表

只提三个有代表的生成表方法,其它都能依次类推或者修改得到
获取生成代码和表

1.基础胡表(除了字牌)

1.首先不管对子,胡牌也就是4个刻子(3) 或者 顺子(1,1,1)
2.对9个空位递归 加刻子 然后是 顺子,找出 除对子外牌的所有可能

# 生成基础胡表
def get_base_table():
    e_list = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    gen_table_sub(e_list,0,0)
#生成所有胡牌可能
def gen_table_sub(e_list,count,value):
    for i in range(0,16):
        if i < 9:
            if e_list[i] > 1 : continue
            e_list[i] += 3
        else:
            index = i - 9 # 大于9 的用来加顺子
            if e_list[index] >= 4 or e_list[index + 1] >= 4 or e_list[index + 2] >= 4:
                continue
            e_list[index] += 1
            e_list[index + 1] += 1
            e_list[index + 2] += 1

        add_table(e_list,value)
        
        if count < 3:
            gen_table_sub(e_list,count + 1,value)

        if i < 9:
            e_list[i] -= 3
        else:
            index = i - 9
            e_list[index] -= 1
            e_list[index + 1] -= 1
            e_list[index + 2] -= 1

3.用add_table()加入表中

base_table = {}
eye_base_table = {}

def add(key,value):
    if key % 3 == 0:
        if key not in base_table:
            base_table.setdefault(key, [])
        base_table[key] = 0
    else:
        if key not in eye_base_table:
            eye_base_table.setdefault(key, [])
        eye_base_table[key] = value


def add_table(e_list,value):
    key_num = 0
    for i in range(0,9):
        key_num = key_num * 10 + e_list[i]
    #控制重复
    if key_num in ttt:
        return
    ttt.setdefault(key_num, True)
    
    add(key_num,value)

4.再依次对9个空格加上对子

# 生成带对子的基础胡表
def gen_eye_base_table():
    e_list = [0, 0, 0, 0, 0, 0, 0, 0, 0]

    for i in range(0,9):
        e_list[i] = 2
        value = 1 + ((i + 1) << 4) + (0 << 8)
        add_table(e_list,value)
        gen_table_sub(e_list,0,value)
        e_list[i] = 0

2.百搭基础胡牌表

1.对基础胡牌每一种胡牌结果进行减牌,少一张牌就是多一个癞子
也就是对add_table() 方法进行修改,再之前的基础上再减牌,改为parse_table()

def parse_table(cards,eye,value,value_count):
    if not check_add(cards,0,eye,value):
        return
    parse_table_sub(cards,1,eye,value,value_count)

2.parse_table_sub() 递归找到所有百搭的情况(# 注释的是计算最大百搭值)

def parse_table_sub(cards,baida_num,eye,value,value_count):
    # tmp_value = copy.deepcopy(value)
    # tmp_value_count = copy.deepcopy(value_count)
    for i in range(0,9):
        if cards[i] == 0:
            continue

        cards[i] -= 1
        tmp_value[0] |= (i + 1) << value_count[0]
        tmp_value_count[0] += 4

        if not check_add(cards,baida_num,eye,tmp_value):
            # flag, last_value = get_last_key_value(cards, baida_num, eye)
            # if flag == True:
            #     tt_value = copy.deepcopy(tmp_value)
            #     maxBaiDaCard = 0
            #     for j in range(0, 4):
            #         baiDaCard = (tt_value[0] >> (12 + j * 4)) & 0xF
            #         if baiDaCard > maxBaiDaCard:
            #             maxBaiDaCard = baiDaCard
            #
            #     tt_value[0] |= (maxBaiDaCard << 8)
            #     last_max_value = last_value >> 8 & 0xF
            #     max_value = tt_value[0] >> 8 & 0xF
            #     if max_value > last_max_value:
            #         add_not_first_time(cards, baida_num, eye, tt_value)
            cards[i] += 1
            tmp_value = copy.deepcopy(value)
            tmp_value_count = copy.deepcopy(value_count)
            continue

        if baida_num < 4:
            parse_table_sub(cards,baida_num + 1,eye,tmp_value,tmp_value_count)

        cards[i] += 1
        # tmp_value = copy.deepcopy(value)
        # tmp_value_count = copy.deepcopy(value_count)

3.用check_add() 存入表中(# 注释的是计算最大百搭值)

def add_key(key,baida_num,eye,value):
    if eye == True:
        if key not in eye_table[baida_num]:
            eye_table[baida_num].setdefault(key, [])
        eye_table[baida_num][key] = value[0]
    else:
        if key not in table[baida_num]:
            table[baida_num].setdefault(key, [])
        table[baida_num][key] = value[0]


def check_add(cards,baida_num,eye,value):
    key = 0
    for i in range(0,9):
        key = key * 10 + cards[i]

    if key == 0:
        return False

    nor_table = {}

    if eye == False:
        nor_table = baida_table[baida_num]
    else:
        nor_table = baida_eye_table[baida_num]

    if key in nor_table:
        return False

    nor_table.setdefault(key,True)

    # for i in range(0,9):
    #     if cards[i] > 4:
    #         return True
    #
    # tmp_value = copy.deepcopy(value)
    # maxBaiDaCard = 0
    # for i in range(0, 4):
    #     baiDaCard = (tmp_value[0] >> (12 + i * 4)) & 0xF
    #     if baiDaCard > maxBaiDaCard:
    #         maxBaiDaCard = baiDaCard
    #
    # tmp_value[0] |= (maxBaiDaCard << 8)

    add_key(key,baida_num,eye,tmp_value)
    return True

3.字成顺胡牌表

这个思路跟其他的不一样
1.把字牌[0,0,0,0,0,0,0]拆成两部分[0,0,0,0],[0,0,0]
2.前面4个是风牌的,后面是箭牌
3.获得箭牌一个顺子的可能或者单个顺子的可能 同理可得 风牌

# 风牌
base_feng_tb = [
    [0,0,0,0],
    [0,1,1,1],
    [1,0,1,1],
    [1,1,0,1],
    [1,1,1,0],
    [3,0,0,0],
    [0,3,0,0],
    [0,0,3,0],
    [0,0,0,3]
]
# 箭牌
base_jian_tb = [
    [0,0,0],
    [1,1,1],
    [3,0,0],
    [0,3,0],
    [0,0,3]
]

4.对base_feng_tb 排列组合,我们可以就拿一个列表项,两个,也可以全部相加,可以把问题转换成 9个拿一个的可能,9个拿二个的可能 一直到 9个拿9个的可能,这样就得到所有胡牌可能的风牌
(同理得到 所有胡牌可能的箭牌)
5.根据不能超过4,把胡牌可能的风牌和箭牌拼凑成一个key即可

判断是否是胡牌

用伪代码来演示

游戏初始化时候读取表,只读一次
生成手牌数量列表
if 没有百搭 :
	if 字牌的key不存在:
		return False
	elif 万牌的key不存在:
		return False
	elif 筒牌的key不存在:
		return False
	elif 条牌的key不存在: 
		return False
	return True
else:
	对子数量dd
	万牌的key需要a个百搭,有对子dd+=1
	筒牌的key需要b个百搭,有对子dd+=1
	条牌的key需要c个百搭,有对子dd+=1
	字牌的key需要d个百搭,有对子dd+=1
	总共的百搭为n
	if dd == 0:#没有将
		if (a + b + c + d) + 2 <= n:
			return True
		return False
	elif dd == 1: # 有将
		if (a + b + c + d) <= n:
			return True
		return False
	else : # 有多个对子,需要 对子 - 1 个百搭
		if (a+b+c+d) + dd - 1 <= n:
			return True
		return False
发布了39 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zbbzb/article/details/90516141