Skip to content

Latest commit

 

History

History
242 lines (209 loc) · 7.46 KB

三公核心算法:算牌.md

File metadata and controls

242 lines (209 loc) · 7.46 KB

三公核心算法: 算牌

本文章使用php编写三公的算牌逻辑,不引用外面的库,全部手写,拿来即用。
如有疑问可以联系我。

<?php

class POKER_NUM
{
    const POKER_NUM_A = 1;    // A
    const POKER_NUM_2 = 2;    // 2
    const POKER_NUM_3 = 3;    // 3
    const POKER_NUM_4 = 4;    // 4
    const POKER_NUM_5 = 5;    // 5
    const POKER_NUM_6 = 6;    // 6
    const POKER_NUM_7 = 7;    // 7
    const POKER_NUM_8 = 8;    // 8
    const POKER_NUM_9 = 9;    // 9
    const POKER_NUM_T = 10;    // 10
    const POKER_NUM_J = 11;    // J
    const POKER_NUM_Q = 12;    // Q
    const POKER_NUM_K = 13;    // K
}

class POKER_TYPE
{
    const POKER_TYPE_BLACK = 4; // 黑桃
    const POKER_TYPE_RED = 3; // 红桃
    const POKER_TYPE_CLUB = 2; // 梅花
    const POKER_TYPE_DIAMOND = 1; // 方片
}

class POKER_RESULT
{
    const SG_INVALID = -1;      // 非法牌型
    const SG_POINT_0 = 0;       // 0点
    const SG_POINT_1 = 100;     // 1点
    const SG_POINT_2 = 200;     // 2点
    const SG_POINT_3 = 300;     // 3点
    const SG_POINT_4 = 400;     // 4点
    const SG_POINT_5 = 500;     // 5点
    const SG_POINT_6 = 600;     // 6点
    const SG_POINT_7 = 700;     // 7点
    const SG_POINT_8 = 800;     // 8点
    const SG_POINT_9 = 900;     // 9点
    const SG_THREE_MAN = 1000;  // 三公
    const SG_BOMB = 1100;       // 炸弹 KKK(1113) > QQQ > JJJ > TTT > 999 > 888 > 777 > 666 > 555 > 444 > 222 > AAA(1101)
    const SG_BAOJIU = 1333;     // 爆玖 (333)
}

const POKER_RESULT_TIMES = [
    POKER_RESULT::SG_INVALID => 0,
    POKER_RESULT::SG_POINT_0 => 1,
    POKER_RESULT::SG_POINT_1 => 1,
    POKER_RESULT::SG_POINT_2 => 1,
    POKER_RESULT::SG_POINT_3 => 1,
    POKER_RESULT::SG_POINT_4 => 1,
    POKER_RESULT::SG_POINT_5 => 1,
    POKER_RESULT::SG_POINT_6 => 1,
    POKER_RESULT::SG_POINT_7 => 2,
    POKER_RESULT::SG_POINT_8 => 2,
    POKER_RESULT::SG_POINT_9 => 2,
    POKER_RESULT::SG_THREE_MAN => 3,
    POKER_RESULT::SG_BOMB => 4,
    POKER_RESULT::SG_BAOJIU => 5
];


class Poker_Style_t
{
    public $num;    // 牌
    public $type;   // 花色

    public function __construct($num, $type)
    {
        $this->num = $num;
        $this->type = $type;
    }
}

const SGTags = [
    POKER_RESULT::SG_INVALID => '非法牌',
    POKER_RESULT::SG_POINT_0 => '0点',
    POKER_RESULT::SG_POINT_1 => '1点',
    POKER_RESULT::SG_POINT_2 => '2点',
    POKER_RESULT::SG_POINT_3 => '3点',
    POKER_RESULT::SG_POINT_4 => '4点',
    POKER_RESULT::SG_POINT_5 => '5点',
    POKER_RESULT::SG_POINT_6 => '6点',
    POKER_RESULT::SG_POINT_7 => '7点',
    POKER_RESULT::SG_POINT_8 => '8点',
    POKER_RESULT::SG_POINT_9 => '9点',
    POKER_RESULT::SG_THREE_MAN => '三公',
    POKER_RESULT::SG_BOMB => '炸弹',
    POKER_RESULT::SG_BAOJIU => '爆玖'
];


class PokerResult
{
    public $cards;  // 牌
    public $rank;   // 评分
    public $style;  // 牌型
    public $message;// 牌型的文字描述

    public function __construct($style, $rank = -1, $message = '非法牌')
    {
        $this->rank = $rank;
        $this->style = $style;
        $this->message = $message;
    }
}

class Hand
{
    private $pokers = [];    // 牌型 [{num, type}]
    private $result;    // 结果

    public function __construct(Array $pokers)
    {
        $this->pokers = $pokers;

        $this->calcPoint();
        $this->calcRank();

        $this->result->cards = $pokers;
    }

    /**
     *  算点数
     */
    public
    function calcPoint()
    {
        // 过滤非法牌型, 比如排的数目不是五张,牌的花色有问题等等. todo
        if (count($this->pokers) != 3) {
            $this->result = new PokerResult(POKER_RESULT::SG_INVALID);
            return;
        }

        // 判断是否有重复的牌
        $arr = [];
        for ($i = 0; $i < count($this->pokers); $i++) {
            $index = $this->pokers[$i]->num * 100 + $this->pokers[$i]->type;
            if (isset($arr[$index]) && $arr[$index] == 1) {
                $this->result = new PokerResult(POKER_RESULT::SG_INVALID);
                return;
            }
            $arr[$index] = 1;
        }

        // 是不是爆玖
        if ($this->pokers[0]->num == POKER_NUM::POKER_NUM_3 &&
            $this->pokers[1]->num == POKER_NUM::POKER_NUM_3 &&
            $this->pokers[2]->num == POKER_NUM::POKER_NUM_3) {

            $this->result = new PokerResult(POKER_RESULT::SG_BAOJIU);
        } else if ($this->pokers[0]->num == $this->pokers[1]->num &&
            $this->pokers[1]->num == $this->pokers[2]->num &&
            $this->pokers[0]->num != POKER_NUM::POKER_NUM_3) {  // 是不是炸弹

            $this->result = new PokerResult(POKER_RESULT::SG_BOMB);
        } else if ($this->pokers[0]->num >= POKER_NUM::POKER_NUM_J &&
            $this->pokers[1]->num >= POKER_NUM::POKER_NUM_J &&
            $this->pokers[2]->num >= POKER_NUM::POKER_NUM_J) {  // 是不是三公

            $this->result = new PokerResult(POKER_RESULT::SG_THREE_MAN);
        } else { // 点数牌
            $a0 = $this->pokers[0]->num > POKER_NUM::POKER_NUM_T ? POKER_NUM::POKER_NUM_T : $this->pokers[0]->num;
            $a1 = $this->pokers[1]->num > POKER_NUM::POKER_NUM_T ? POKER_NUM::POKER_NUM_T : $this->pokers[1]->num;
            $a2 = $this->pokers[2]->num > POKER_NUM::POKER_NUM_T ? POKER_NUM::POKER_NUM_T : $this->pokers[2]->num;

            $point = ($a0 + $a1 + $a2) % 10;
            $this->result = new PokerResult($point * 100);   // 点数 * 100 = 牌型
        }

        return;
    }

    /**
     * 算分
     */
    public
    function calcRank()
    {
        $maxPoker = $this->_getMaxPokerNum();

        // 计算公仔牌的数量
        $threeManNum = 0;
        for ($i = 0; $i < 3; $i++) {
            if ($this->pokers[$i]->num > POKER_NUM::POKER_NUM_T) {
                $threeManNum++;
            }
        }

        // 针对不同牌型计算rank
        if ($this->result->style == POKER_RESULT::SG_BAOJIU) {
            // 爆玖 rank = 1333 写死 !!!
            $this->result->rank = POKER_RESULT::SG_BAOJIU;
        } else if ($this->result->style == POKER_RESULT::SG_BOMB) {
            // 炸弹 rank = 炸弹底分 + 牌面值
            $this->result->rank = $this->result->style + $maxPoker['num']; // + number_format($maxPoker['type'] / 10, 1);
        } else if ($this->result->style == POKER_RESULT::SG_THREE_MAN) {
            // 三公 rank = 三公底分 + 最大牌面值 + 最大牌花色
            $this->result->rank = $this->result->style + $maxPoker['num'] + number_format($maxPoker['type'] / 10, 1);
        } else {
            // 点数 rank = 点数底分 + 三公数量 * 30 + 最大牌面值 + 最大牌花色
            $this->result->rank = $this->result->style + 30 * $threeManNum +
                $maxPoker['num'] + number_format($maxPoker['type'] / 10, 1);
        }

        $this->result->message = SGTags[$this->result->style];
    }

    public
    function getResult()
    {
        return $this->result;
    }

    /**
     * 获取最大牌面大小
     */
    private
    function _getMaxPokerNum()
    {
        $num = $this->pokers[0]->num;
        $type = $this->pokers[0]->type;
        for ($i = 1; $i < count($this->pokers); $i++) {
            if ($this->pokers[$i]->num > $num ||
                ($this->pokers[$i]->num == $num && $this->pokers[$i]->type > $type)) {
                $num = $this->pokers[$i]->num;
                $type = $this->pokers[$i]->type;
            }
        }

        return ['num' => $num, 'type' => $type];
    }

}