【原创博文,转载请注明出处!】
在分析麻将的排序算法之前我们先大致了解一下麻将玩法中的一些专业名词以及其国际化代称👉。麻将的专有词汇表。
有了上面这些词汇基础,我们开始今天的分享主题----->麻将排序。我将会从两个方面入手:
①:全部桌牌的排序(例如一副麻将144张牌按规则排序)。
②:玩家的手牌排序。
首先说明的是①相对②要容易很多,我们先分析①。
不同地方的玩法,实际需要的总牌数不一样,我们暂且按144张这种,反正大同小异,对我们的算法思路没有影响。所谓的“规则”,无非就是先将麻将按照牌的大类(也就是“花色”)排序,例如“万牌”、“条牌”、“筒牌”、“箭牌”......,从玩法的专业词汇中,我们知道一副麻将有下面7大类牌:
1. 'CHARACTER',(万牌)
2. 'BAMBOO',(条牌)
3. 'DOT',(筒牌)
4. 'WIND',(东南西北风牌)
5. 'DRAGON',(红 发 白板)
6. 'FLOWER',(梅兰竹菊)
7. 'SEASON',(春夏秋冬)
大类排序好以后再对大类里面的牌按照一定顺序排序(如万字牌按 “一万”、“二万”......“九万”)。接下来我们就开始对整副牌排序啦,完成开头提到的任务①。
Step One:
将大类规则按顺序存入数组_defaultRules中👇:
_defaultRules : [
'CHARACTER',
'BAMBOO',
'DOT',
'WIND',
'DRAGON',
'FLOWER',
'SEASON',
],
Step Two:
小类所有的牌按顺序存入小类数组管理器中,提供getDot()、 getCharacter()、getBamboo()、getWind()、getDragon()、 getSeason()、 getFlower()七个API。
getDot(){
var arr = [
'DOT_1',
'DOT_2',
'DOT_3',
'DOT_4',
'DOT_5',
'DOT_6',
'DOT_7',
'DOT_8',
'DOT_9'
];
return arr;
},
getCharacter(){
......
},
getBamboo(){
......
},
getWind(){
......
},
getDragon(){
......
},
getSeason(){
var arr = [
'SEASON_SPRING',
'SEASON_SUMMER',
'SEASON_AUTUMN',
'SEASON_WINTER'
];
return arr;
},
getFlower(){
var arr = [
'FLOWER_PLUM',
'FLOWER_ORCHID',
'FLOWER_BAMBOO',
'FLOWER_CHRYSANTHEMUM'
];
return arr;
},
完成上面👆操作后,我们声明定义一个空数组_allMjWithRule :[],然后将上面七个API的结果依次存入_allMjWithRule中,嗯,这样一副牌(共有42种不同的牌)就按照_defaultRules中的规则依次盛放在_allMjWithRule数组中。[具体实现如下所示👇]
/**
*
* @param {排序规则的数组} ruleArr
*/
getAllMjWithRules(ruleArr = this._defaultRules){
this._allMjWithRule = [];
if (ruleArr instanceof Array) {
ruleArr.forEach(element => {
switch (element) {
case 'CHARACTER':{
this.getCharacter().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
case 'BAMBOO':{
this.getBamboo().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
case 'DOT':{
this.getDot().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
case 'WIND':{
this.getWind().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
case 'DRAGON':{
this.getDragon().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
case 'FLOWER':{
this.getFlower().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
case 'SEASON':{
this.getSeason().forEach(element => {
this._allMjWithRule.push(element);
});
}
break;
default:
break;
}
});
return this._allMjWithRule;
}else{
console.log('传入的排序规则错误');
return;
}
},
排好序的牌结果如下👇:
Simulator: JS: CHARACTER_1,CHARACTER_2,CHARACTER_3,CHARACTER_4,CHARACTER_5,CHARACTER_6,CHARACTER_7,CHARACTER_8,CHARACTER_9,BAMBOO_1,BAMBOO_2,BAMBOO_3,BAMBOO_4,BAMBOO_5,BAMBOO_6,BAMBOO_7,BAMBOO_8,BAMBOO_9,DOT_1,DOT_2,DOT_3,DOT_4,DOT_5,DOT_6,DOT_7,DOT_8,DOT_9,WIND_EAST,WIND_SOUTH,WIND_WEST,WIND_NORTH,DRAGON_RED,DRAGON_GREEN,DRAGON_WHITE,FLOWER_PLUM,FLOWER_ORCHID,FLOWER_BAMBOO,FLOWER_CHRYSANTHEMUM,SEASON_SPRING,SEASON_SUMMER,SEASON_AUTUMN,SEASON_WINTER
嗯,上面👆讲到任务①不难。简单地组装一下就好了,毕竟是一副完整的牌嘛,每张牌都在,对号入座罢了。但是针对任务②如何处理呢?一般手牌视麻将玩法而定,我所知道的“沈家门”和“定海”的手牌有14张,那么可能是“ BAMBOO_9”、“ BAMBOO_5”、“ DOT_8”、“ CHARACTER_4”、“ BAMBOO_1”、“ WIND_WEST”、“ DRAGON_RED”...可以有重复的牌,并且某些大类的牌可能没有,即便有的话也不连贯☹️,是吧,怎么排序呢???
其实我们任务①所做的工作也是为任务②做铺垫的!任务①我们已经将所有的牌排好序了,这样我们知道“ BAMBOO_9”、“ BAMBOO_5”、“ DOT_8”、“ CHARACTER_4”、“ BAMBOO_1”、“ WIND_WEST”、“ DRAGON_RED”...这些毫无规律的牌,每一张在_allMjWithRule中的位置了吧?那简单了,我们将手牌依次去_allMjWithRule数组中映射出自己的位置并设置为自己的localId,代码如下所示:tempModelArr指的是自己的手牌对象(如:name、serverId、LocalId…)数组,
tempModelArr.forEach(element => {
for (const key in this._allMjWithRule) {
//如果未排序的手牌与已经排好序的全部牌相同,则设置未排序手牌的本地id(这个id后面重新设置)
if (element.name === this._allMjWithRule[key]) {
element.localId = key;
}
}
});
这样,我们将映射完成后的手牌对象放入一个数组中,根据localId的大小进行排序,嘿嘿,是不是就完成了?
tempModelArr.sort(function(a,b){
return a.localId - b.localId;
});
上面就是我最近在游戏开发中处理的麻将排序算法的分享。