麻将的和牌、听牌以及一向听(即能否打一张牌进行立直)的算法。

感觉网上关于麻将的源码资源很少,一般这种算法都是用递归,把牌堆分解成若干子牌堆然后针对2-3张牌的情形给出一个出口。

和牌算法比较常见,毕竟只要是麻将编程都要用到,后面两种虽然普通的麻将编程用不到,但是要编写AI对策以及某些特殊规则(例如日本麻将)就有用了,尤其一向听的算法。

三种算法原理差不多,都是先分析牌数较少的情形,然后牌数较多的情形通过牌堆分解后,对提取剩余牌堆调用自身。

 

本源码使用C++,分MDeck和MDeckExtract类。这两个类定义如下:

 

typedef int MTile;
class MDeck;
class MDeckExtract;
class MDeck
{
public:
	MTile tile[200];
	int length;
};
class MDeckExtract
{
public:
	int methodCount;
	MDeck extracted[10];
	MDeck remained[10];
};

 

 

其中MDeck类实际上就是一个由MTile(int)组成的数组和牌堆长度,跟CArray类很像。

而MDeckExtract类则用于存储牌堆的提取结果。其中methodCount是提取方案的数目,extracted存储提取方案中可以提取的目标牌堆,remain则存储剩余牌堆。举个简单的例子:

例如一个牌堆为 3,3,4,5,6,若对其进行顺子提取操作,则MDeckExtract应该是这样的:

methodCount 为 2,即两种提取方案。

extracted[0]为3,4,5,remain[0]为3,6;extracted[1]为4,5,6,remain[1]为3,3。

接下来是提取方法(其中有的较为简单的方法的定义例如牌堆排序、创建不包含重复的牌的副本、牌堆连接、加入等方法就不帖进来了,很容易实现)的源码。

 
MDeckExtract MDeck::extractDuizi(){
	sortTile();
	int i = 0;
	MDeck dr = removeRepeatedTile();
	MDeck dt;
	MDeck de;
	MDeckExtract e;
	MTile t;
	for(i = 0;i<dr.length;i++){
		t = dr.tile[i];
		dt = (*this);
		de.length = 0;
		if(dt.countTile(t)>1){
			dt.removeTile(t);
			dt.removeTile(t);
			de.pushTile(t);
			de.pushTile(t);
			e.extracted[e.methodCount] = de;
			e.remained[e.methodCount] = dt;
			e.methodCount ++;
		}
	}
	return e;
}

MDeckExtract MDeck::extractShunzi(){
	sortTile();
	int i = 0;
	MDeck dr = removeRepeatedTile();
	MDeck dt;
	MDeck de;
	MDeckExtract e;
	MTile t;
	for(i = 0;i<dr.length;i++){
		t = dr.tile[i];
		dt = (*this);
		de.length = 0;
		if(dt.countTile(t+1)>0 &a
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
红中麻将听牌算法可以分为两个步骤:1.计算所有可能的型;2.排除不可能的型,得到所有可能的听牌。 Step 1: 计算所有可能的型 首先,需要将手、已打出的和红中合并成一个数组,然后对数组进行排序。接着,可以将数组按照顺序分成三个部分:雀头、顺子和刻子。其中,雀头必须是一对相同的,顺子必须是三个相邻的,刻子必须是三个相同的。如果出现了四个相同的,那么可以将其拆成一个刻子和一个顺子。 接下来,可以对顺子进行扩展。如果手中有相邻的可以与顺子形成一个新的顺子,那么就将其合并。这里需要注意的是,如果将顺子和型中的其他拆开后,可以形成新的顺子或刻子,那么就需要将其加入到型中。最后,将所有型保存到一个数组中。 Step 2: 排除不可能的型,得到所有可能的听牌 在得到所有可能的型后,需要对每个进行分析,排除不可能的听牌。具体来说,可以对每个型中的每张进行分析,看看这张是否可以作为雀头或是将来形成一个顺子或刻子。如果不可以,就可以将这张作为可能的听牌。 下面是Lua的代码示例: ```lua -- 手、已打出的和红中 local hand = {1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 19} local discard = {10, 11, 12, 13, 14} local hongzhong = {19} -- 合并数组并排序 local tiles = {} for _, v in ipairs(hand) do table.insert(tiles, v) end for _, v in ipairs(discard) do table.insert(tiles, v) end for _, v in ipairs(hongzhong) do table.insert(tiles, v) end table.sort(tiles) -- 计算型 local function calculate_patterns(tiles) local patterns = {} local function dfs(index, jiang) if index > #tiles then table.insert(patterns, jiang) return end for i = index + 1, #tiles do if tiles[i] == tiles[i - 1] then if not jiang and i - index >= 2 then dfs(i + 1, {tiles[i], tiles[i]}) end if jiang and tiles[i] == jiang[1] then dfs(i + 1, jiang) end elseif tiles[i] == tiles[i - 1] + 1 then if jiang and tiles[i] == jiang[1] + 1 then dfs(i + 1, jiang) end if i + 1 <= #tiles and tiles[i + 1] == tiles[i] then dfs(i + 2, jiang) end elseif tiles[i] == tiles[i - 1] + 2 then if i + 1 <= #tiles and tiles[i + 1] == tiles[i] then dfs(i + 2, jiang) end else if not jiang then dfs(i, {tiles[i], tiles[i]}) end end end end dfs(1, nil) return patterns end local patterns = calculate_patterns(tiles) -- 计算听牌 local function calculate_ting(patterns) local ting = {} for _, p in ipairs(patterns) do for i = 1, #p do local t = p[i] if i == 1 or p[i] ~= p[i - 1] then -- 判断是否可以作为雀头 if t == p[i + 1] then goto continue end -- 判断是否可以形成顺子 if t <= 7 and table.indexof(p, t + 1) and table.indexof(p, t + 2) then goto continue end -- 判断是否可以形成刻子 if table.count(p, t) >= 2 then goto continue end -- 可以作为听牌 table.insert(ting, t) end ::continue:: end end return ting end local ting = calculate_ting(patterns) -- 输出结果 print("手: ", table.concat(hand, " ")) print("已打出的: ", table.concat(discard, " ")) print("红中: ", table.concat(hongzhong, " ")) print("听牌: ", table.concat(ting, " ")) ``` 这段代码首先将手、已打出的和红中合并成一个数组,并对数组进行排序。接着,调用`calculate_patterns`函数计算所有可能的型。最后,调用`calculate_ting`函数计算所有可能的听牌。 `calculate_patterns`函数使用深度优先搜索算法计算所有可能的型。具体来说,它首先将数组按照顺序分成三个部分:雀头、顺子和刻子。然后,对顺子进行扩展,并将所有型保存到一个数组中。 `calculate_ting`函数遍历所有可能的型,并对每个进行分析,排除不可能的听牌。具体来说,它对每个型中的每张进行分析,看看这张是否可以作为雀头或是将来形成一个顺子或刻子。如果不可以,就可以将这张作为可能的听牌。 最后,输出所有可能的听牌
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值