用1-9 一万到九万 10-18 一筒到九筒 19-27 一条到九条 这里就无视缺门了,缺门的问题很简单就不用我多说了。
代码:
<?php
//1-9 一万到九万 10-18 一筒到九筒 19-27 一条到九条
$paiArr = [1,1,1,1,2,2,2,2,3,3,3,3,4,4]; //123 123 123 123 44
$paiArr1 = [1,2,2,2,3,3,3,4,4,10,10,10,11,11];// 123 234 234 101010 1111
$paiArr2 = [7,7,7,8,8,8,9,9,9,10,10,11,11,11];//789 789 789 111111 1010
$paiArr3 = [1,2,3,4,5,6,7,8,9,10,11,12,13,13];//123 456 789 101112 1313
asort($paiArr);//每个都要从小到大排序的
asort($paiArr1);//每个都要从小到大排序的
asort($paiArr2);//每个都要从小到大排序的
asort($paiArr3);//每个都要从小到大排序的
echo checkHu($paiArr);
echo checkHu($paiArr1);
echo checkHu($paiArr2);
echo checkHu($paiArr3);
function checkHu($arr){//主函数
$jiangArr = checkJiang($arr);
foreach ($jiangArr as $key=>$index){
$tmpArr = $arr;
unset($tmpArr[$index]);//去除一对将
unset($tmpArr[$index+1]);//去除一对将
$tmpArr = array_values($tmpArr);
for ($i =1;$i<=4;$i++){//最多4个123型的牌,四次循环
$count1 = count($tmpArr);
$tmpArr = check123Pai($tmpArr);
$count2 = count($tmpArr);
if ($count2 == $count1)break;//如果已经找不到123型的牌就结束
}
for ($i =1;$i<=4;$i++){//最多4个111型的牌,四次循环
$count1 = count($tmpArr);
$tmpArr = check111Pai($tmpArr);
$count2 = count($tmpArr);
if ($count2 == $count1)break;//如果已经找不到111型的牌就结束
}
if (count($tmpArr)==0){
return 1;
}
}
return 0;
}
function check123Pai($arr){//找出123类型牌,并将这些牌去除(从第一个牌开始算,看看后面有没有比它大1的,然后再找有没有比它大2的,都有的话就把三张牌剔除,此时数组key变化了,需要array_values一下)
$count = count($arr);
for ($i=0;$i<=$count-3;$i++){
$minPai = $arr[$i];
$max = $minPai>18? 27 : ($minPai>9? 18:9);//这里保证9 10 11这种牌不算(九万一筒二筒不能算的)
for ($j=$i+1;$j<=$count-2;$j++){
if ($arr[$j]<$max&&$arr[$j]==$arr[$i]+1 ){
for ($k=$j+1;$k<=$count-1;$k++){
if ($arr[$k]<=$max&&$arr[$k]==$arr[$j]+1 ){
unset($arr[$i]);
unset($arr[$j]);
unset($arr[$k]);
return array_values($arr);
}
}
}
}
}
return $arr;
}
function check111Pai($arr){//找出111类型牌并将这些牌去除(从第一个牌开始算,每次加3,保证三张一样就行,这里有人会问那要是1111这种牌怎么算。这里就涉及到算法的思想了,我先剔除了将,又剔除了123型的牌,如果还有1111型的牌,那肯定胡不了)
$count = count($arr);
for ($i=0;$i<=$count-3;$i=$i+3){
if ($arr[$i]==$arr[$i+1]&&$arr[$i]==$arr[$i+2]){
unset($arr[$i]);
unset($arr[$i+1]);
unset($arr[$i+2]);
return array_values($arr);
}
}
return $arr;
}
function checkJiang($arr){//返回一对将的offset
$count = count($arr);
$jiangOffset = [];
//里面会有重复的
for ($i=0;$i<=$count-2;$i++){
if ($arr[$i] ==$arr[$i+1]){
$jiangOffset[] = $i;
}
}
//去重
$repeatArr = [];
$tmpCount = count($jiangOffset)-2;
for ($i=0;$i<=$tmpCount;$i++){
if ($arr[$jiangOffset[$i]] ==$arr[$jiangOffset[$i+1]]){
$repeatArr[] = $jiangOffset[$i+1];
}
}
foreach ($jiangOffset as $key=>$value){
if (in_array($value,$repeatArr)){
unset($jiangOffset[$key]);
}
}
return array_values($jiangOffset);
}
function tingPai($arr){//返回听牌的数据
$mineArr = [];
asort($arr);
for ($i=0;$i<=count($arr)-4;$i+=4){
if ($arr[$i] ==$arr[$i+3]){
$mineArr[] = $arr[$i];
}
}
$tingPaiArr = [];
for ($i=1;$i<=27;$i++){
if (in_array($i,$mineArr)){
continue;
}
$arr1 = $arr;
$arr1[] = $i;
asort($arr1);
if (checkHu($arr1)){
$tingPaiArr[] = $i;
}
}
return $tingPaiArr;
}
思路
规则:麻将四坎牌一对将代表基本胡,坎牌比如一万二万三万,三筒三筒三筒。一对将就相同的两张牌比如一条一条,类似于斗地主里的对子。基本胡的基础上才衍生出对对胡,清一色啥的。
首先找出牌里的一对将,当然程序不知道哪对将才能胡牌,这里找出所有的将进行遍历。然后先把将去掉,剩下的判断111型的牌与123型的牌。这里问题来了,先判断哪个类型?这里必须先判断123型,再判断111型,具体原因是笔者实践得出来的。其他算法可能不一样,笔者的算法就是这个顺序。判断能胡牌了,然后再判断什么天胡地胡啥的就方便了。