麻将基本牌型的胡牌检测算法,不包含小七对,针对小七对可以增加牌型检测,与此组成牌型树。
此类可以获得胡牌的牌型组合,取得后针对组合可以进行牌型名称检测。
先上一个,后面会持续更新各地特殊的胡牌算法,如癞子
/**
* 麻将基本胡牌 (不包含小七对)
*
* 此类检测是否胡牌,返回胡牌的组合, 你可以再将获得的胡牌组合,进行相应牌型检测获得牌型
* 不能用于小七对的胡牌检测
*
* @package
* @author sunshine
*
* $Id: HuAlgorithm.php 2017.12.05 $
*/
class HuAlgorithm
{
private $stack = []; # 牌组栈
private $cards = []; # 剩余手牌
private $pairs = 0; # 将对个数
private $raw = false; # 是否直接返牌组栈
private $number = [];# 牌堆
public function __construct($number = [], $raw = false)
{
$this->number = $number;
$this->raw = $raw;
}
public function clear($raw = false)
{
$this->stack = [];
$this->cards = [];
$this->pairs = 0;
$this->raw = $raw;
}
public function start($cards)
{
$this->cards = $cards;
sort($this->cards);
if ($this->try_hu())
{
if ($this->raw)
{
# 直接返回牌组栈
return $this->stack;
}
else
{
# 返回胡牌的排列
$win_cards = [];
foreach ($this->stack as $e)
{
$win_cards[] = $e;
}
return $win_cards;
}
}
else
{
return [];
}
}
# 将牌组压栈
private function push($group)
{
array_push($this->stack, $group);
}
# 牌组出栈
private function roll_back()
{
$group = array_pop($this->stack);
array_push($this->cards, $group);
sort($this->cards);
}
# 刻子,则将此组牌压栈
private function try_triplet($card)
{
if ($this->array_count($this->cards, $card) >= 3)
{
$this->cards = $this->array_remove($this->cards, $card, 3);
$this->push([$card, $card, $card]);
return true;
}
else
return false;
}
# 顺子,则将此组牌压栈
private function try_along($card)
{
if (!in_array($card, $this->number))
return false;
if (in_array($card, $this->cards) && in_array($card + 1, $this->cards) && in_array($card + 2, $this->cards))
{
$this->cards = $this->array_remove($this->cards, $card);
$this->cards = $this->array_remove($this->cards, $card + 1);
$this->cards = $this->array_remove($this->cards, $card + 2);
$this->push([$card, $card + 1, $card + 2]);
return true;
}
else
return false;
}
# 对子,对子只能有一个
private function try_sub($card)
{
if ($this->pairs == 1)
return false;
if ($this->array_count($this->cards, $card) >= 2)
{
$this->cards = $this->array_remove($this->cards, $card, 2);
$this->push([$card, $card]);
$this->pairs = 1;
return true;
}
else
return false;
}
# 回溯尝试组成顺子刻子对子
private function try_hu()
{
if (!$this->cards)
{
if ($this->pairs == 1)
return true;
else
return false;
}
$active_card = reset($this->cards);
if ($this->try_triplet($active_card))
{
if (!$this->try_hu())
$this->roll_back();
else
return true;
}
if ($this->try_sub($active_card))
{
if (!$this->try_hu())
{
$this->roll_back();
$this->pairs = 0;
}
else
return true;
}
if ($this->try_along($active_card))
{
if (!$this->try_hu())
$this->roll_back();
else
return true;
}
return false;
}
private function array_remove($cards, $card, $d = 1)
{
foreach ($cards as $k => $v)
{
if ($v == $card && $d)
{
$d--;
unset($cards[$k]);
}
}
return $cards;
}
private function array_count($cards, $card)
{
$rs = array_count_values($cards);
foreach ($rs as $k => $v)
{
if ($card == $k) return $v;
}
return 0;
}
}
##############################################################
# 牌
# 万 筒 条 东南西北 中发白 花
$wan = [17, 18, 19, 20 ,21, 22, 23, 24, 25];
$dot = [33, 34, 35, 36, 37, 38, 39, 40, 41];
$suo = [49, 50, 51, 52, 53, 54, 55, 56, 57];
$win = [61, 62, 63, 64];
$dragon = [71, 72, 73];
$flower = [];
$number = array_merge($wan, $dot, $suo);
##############################################################
# test 平胡
$cards = [17, 18, 19, 19, 19, 21, 22, 23, 35, 37, 36, 38, 36, 37];
# test 大对子
// $cards = [17, 17, 17, 19, 19, 19, 23, 23, 23, 36, 36, 36, 37, 37];
$ha = new HuAlgorithm($number);
$rs = $ha->start($cards);
print_r($rs);