再分享一个德州扑克的算法

自行脑补类似现在直播玩的"猫鼠游戏“类的小游戏。送上一个简单的算法,求各位指正啦。

<?php
/**
 * 德州扑克
 * Class Texas
 */
class Texas
{
    protected static $cards = [
        //数字,花色
        [1, 4], [1, 3], [1, 2], [1, 1],
        [2, 4], [2, 3], [2, 2], [2, 1],
        [3, 4], [3, 3], [3, 2], [3, 1],
        [4, 4], [4, 3], [4, 2], [4, 1],
        [5, 4], [5, 3], [5, 2], [5, 1],
        [6, 4], [6, 3], [6, 2], [6, 1],
        [7, 4], [7, 3], [7, 2], [7, 1],
        [8, 4], [8, 3], [8, 2], [8, 1],
        [9, 4], [9, 3], [9, 2], [9, 1],
        [10, 4], [10, 3], [10, 2], [10, 1],
        [11, 4], [11, 3], [11, 2], [11, 1],
        [12, 4], [12, 3], [12, 2], [12, 1],
        [13, 4], [13, 3], [13, 2], [13, 1],
    ];
    /**
     * @var array 玩家牌
     */
    protected $playerCards = [];
    /**
     * @var array 公共牌
     */
    protected $publicCards;
    /**
     * @var array 剩余牌堆
     */
    protected $nowLeftCards;
    public function __construct()
    {
        $this->nowLeftCards = self::$cards;
        //洗牌
        shuffle($this->nowLeftCards);
    }
    /**
     * 获取牌面数字
     * @param $card
     * @return mixed
     */
    protected function getCardNumber($card)
    {
        return $card[0];
    }
    /**
     * 获取所有牌的牌面
     * @param $cards
     * @return array
     */
    protected function getCardNumbers($cards)
    {
        $numbers = [];
        foreach ($cards as $card) {
            $numbers[] = $this->getCardNumber($card);
        }
        return $numbers;
    }
    /**
     * 获取花色
     * @param $card
     * @return mixed
     */
    protected function getCardColor($card)
    {
        return $card[1];
    }
    /**
     * 获取所有牌的花色
     * @param $cards
     * @return array
     */
    protected function getCardColors($cards)
    {
        $colors = [];
        foreach ($cards as $card) {
            $colors[] = $this->getCardColor($card);
        }
        return $colors;
    }
    /**
     * 获取公共牌
     * @return array
     */
    public function getPublicCards()
    {
        return $this->publicCards;
    }
    /**
     * 设置公共牌
     * @param $publicCards
     * @return mixed
     */
    public function setPublicCards($publicCards)
    {
        return $this->publicCards = $publicCards;
    }
    /**
     * 获取玩家牌
     * @param string $player 玩家 key
     * @return array
     */
    public function getPlayersCards($player = null)
    {
        return is_null($player) ? $this->playerCards : (isset($this->playerCards[$player]) ? $this->playerCards[$player] : []);
    }
    /**
     * 随机生成玩家牌 & 公共牌
     * @param int  $playerNumbers 玩家数量
     * @param bool $needPublic 是否生成公共牌
     */
    public function generate($playerNumbers = 2, $needPublic = true)
    {
        $needPublic and $this->publicCards = array_splice($this->nowLeftCards, 0, 5);
        $beginAt = count($this->playerCards);
        for ($i = 1; $i <= $playerNumbers; $i++) {
            $player = "player_" . ($beginAt + $i);
            $this->playerCards[$player] = array_splice($this->nowLeftCards, 0, 2);
        }
    }
    /**
     * @param array $player1
     * @param array $player2
     * @return array 'result' = 0 相同,1 前者小,-1 后者小
     */
    public function comparePlayer(array $player1, array $player2)
    {
        $player1Biggest = $this->getBiggestCardFromPlayerAndPublic($player1);
        $player2Biggest = $this->getBiggestCardFromPlayerAndPublic($player2);
        $type1 = $this->judge($player1Biggest);
        $type2 = $this->judge($player2Biggest);
        $result = $type1 < $type2 ? 1 : -1;
        if ($type1 == $type2) {
            $result = $this->compareTwoEqualType($type1, $player1Biggest, $player2Biggest);
        }
        return [
            'result' => $result,
            'player_1' => [
                'cards' => $player1Biggest,
                'type' => $type1
            ],
            'player_2' => [
                'cards' => $player2Biggest,
                'type' => $type2
            ],
        ];
    }
    /**
     * 从玩家和公共牌的组合中选出最优解
     * @param array $player
     * @param array $public
     * @return mixed
     */
    public function getBiggestCardFromPlayerAndPublic(array $player, array $public = [])
    {
        $public = empty($public) ? $this->publicCards : $public;
        $combinations = $this->getUniqueCombinationWithSort(array_merge($player, $public));
        usort($combinations, function ($first, $next) {
            //先比较牌型
            $firstType = $this->judge($first);
            $nextType = $this->judge($next);
            if ($firstType == $nextType) {
                return $this->compareTwoEqualType($firstType, $first, $next);
            }
            return $firstType < $nextType ? 1 : -1;
        });
        return reset($combinations);
    }
    /**
     * 相同牌型的比较的牌的大小
     * @param int $type
     * @param array $cards1
     * @param array $cards2
     * @return int 0 相同,1 前者小,-1 后者小
     */
    public function compareTwoEqualType($type, array $cards1, array $cards2)
    {
        switch ($type) {
            case 10://皇家同花顺
                return 0;
            case 9://同花顺
            case 5://顺子
                return $this->compareFlushNumber($this->getCardNumbers($cards1), $this->getCardNumbers($cards2));
            case 8://四条
                $same = 4;
            case 7://葫芦
                $same = isset($same) ? $same : 3;
                $left = 5 - $same;
                $cards1SameCount = $cards2SameCount = [];
                $this->checkSame($this->getCardNumbers($cards1), $same, $cards1SameCount);
                $this->checkSame($this->getCardNumbers($cards2), $same, $cards2SameCount);
                $_temp1 = array_flip($cards1SameCount);
                $_temp2 = array_flip($cards2SameCount);
                //先比较4/3张牌是否相同,如果相同,就比较最后1/2张牌的大小
                if ($_temp1[$same] == $_temp2[$same]) {
                    return $_temp1[$left] == $_temp2[$left] ? 0 : ($_temp1[$left] < $_temp2[$left] ? 1 : -1);
                }
                return $_temp1[$same] < $_temp2[$same] ? 1 : -1;
            case 6://同花
            case 1://高牌
                return $this->compareNumber($this->getCardNumbers($cards1), $this->getCardNumbers($cards2));
            case 4://三条
                $same = 3;
            case 2://一对
                $same = isset($same) ? $same : 2;
                $cards1SameCount = $cards2SameCount = [];
                $this->checkSame($this->getCardNumbers($cards1), $same, $cards1SameCount);
                $this->checkSame($this->getCardNumbers($cards2), $same, $cards2SameCount);
                $_temp1 = array_flip($cards1SameCount);
                $_temp2 = array_flip($cards2SameCount);
                //先比较3/2张牌是否相同,如果相同,就比较最后2张牌的大小
                if ($_temp1[$same] == $_temp2[$same]) {
                    return $this->compareNumber(array_unique($this->getCardNumbers($cards1)), array_unique($this->getCardNumbers($cards2)));
                }
                return $_temp1[$same] < $_temp2[$same] ? 1 : -1;
            case 3://两对
                $cards1SameCount = $cards2SameCount = [];
                $this->checkSame($this->getCardNumbers($cards1), 2, $cards1SameCount);
                $this->checkSame($this->getCardNumbers($cards2), 2, $cards2SameCount);
                $two1 = $two2 = [];
                $one1 = $one2 = [];
                foreach ($cards1SameCount as $key1 => $value1) {
                    if ($value1 == 1){
                        $one1[] = $key1;
                    } elseif ($value1 == 2) {
                        $two1[] = $key1;
                    }
                }
                foreach ($cards2SameCount as $key2 => $value2) {
                    if ($value2 == 1){
                        $one2[] = $key2;
                    } elseif ($value2 == 2) {
                        $two2[] = $key2;
                    }
                }
                //先比较2对的大小
                if (0 == $compare2Res = $this->compareNumber($two1, $two2)) {
                    //如果相同,再比较单牌的大小
                    return $this->compareNumber($one1, $one2);
                }
                return $compare2Res;
        }
    }
    /**
     * 比较非顺子以外牌型的 2个牌面的大小
     * @param array $numbers1
     * @param array $numbers2
     * @return int  0 相同,1 前者小,-1 后者小
     */
    public function compareNumber(array $numbers1, array $numbers2)
    {
        //做一个大小的映射, A最大,2最小
        $order = array_flip([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1]);
        //排序
        $orderClosure = function ($first, $second) use ($order) {
            return $order[$first] == $order[$second] ? 0 : ($order[$first] < $order[$second] ? 1 : -1);
        };
        usort($numbers1, $orderClosure);
        usort($numbers2, $orderClosure);
        foreach ($numbers1 as $key => $value) {
            if ($order[$value] < $order[$numbers2[$key]]) {
                return 1;
            } elseif ($order[$value] > $order[$numbers2[$key]]) {
                return -1;
            }
        }
        return 0;
    }
    /**
     * 比较顺子的 2个牌面的大小
     * @param array $numbers1
     * @param array $numbers2
     * @return int  0 相同,1 前者小,-1 后者小
     *
     * A在做顺子牌型的时候,只会出现在顺首和顺尾,并且会有截然不同的效果。比如下面的例子:
     * A、K、Q、J、10 在此牌型中“A”为顺尾,它作为最大的牌使用。
     * 5、4、3、2、A 在此牌型中“A”为顺首,它作为最小的牌使用。
     * 比如: 5、4、3、2、A 就比 6、5、4、3、2 要小。
     *
     */
    public function compareFlushNumber(array $numbers1, array $numbers2)
    {
        $orderClosure = function ($first, $second) {
            return $first == $second ? 0 : ($first < $second ? 1 : -1);
        };
        usort($numbers1, $orderClosure);
        $numbers1 == [13, 12, 11, 10, 1] and $numbers1 = [14, 13, 12, 11, 10];
        usort($numbers2, $orderClosure);
        $numbers2 == [13, 12, 11, 10, 1] and $numbers2 = [14, 13, 12, 11, 10];
        foreach ($numbers1 as $key => $value) {
            if ($value < $numbers2[$key]) {
                return 1;
            } elseif ($value > $numbers2[$key]) {
                return -1;
            }
        }
        return 0;
    }
    /**
     * 判断牌型
     * @param array $cards 从大到小排序过的牌
     * @return int 10:皇家同花顺 | 9:同花顺 | 8:四条 | 7:葫芦 | 6:同花 | 5:顺子 | 4:三条 | 3:两对 | 2:一对 | 1:高牌
     */
    public function judge(array $cards)
    {
        $numbers = $this->getCardNumbers($cards);
        $color = $this->getCardColors($cards);
        //皇家同花顺
        if ($numbers == [13, 12, 11, 10, 1]) {
            return 10;
        }
        //同花顺
        if ($this->checkStraight($numbers) && $this->checkFlush($color)) {
            return 9;
        }
        //四条
        if ($this->checkSame($numbers, 4) == 1) {
            return 8;
        }
        //葫芦, 有1个3条 和 2个2条
        if ($this->checkSame($numbers, 3) == 1 && $this->checkSame($numbers, 2) == 2) {
            return 7;
        }
        //同化
        if ($this->checkFlush($color)) {
            return 6;
        }
        //顺子
        if ($this->checkStraight($numbers)) {
            return 5;
        }
        //三条
        if ($this->checkSame($numbers, 3) == 1) {
            return 4;
        }
        //两对
        if ($this->checkSame($numbers, 2) == 2) {
            return 3;
        }
        //一对
        if ($this->checkSame($numbers, 2) == 1) {
            return 2;
        }
        //高牌
        return 1;
    }
    /**
     * 检查是否相同牌面,可判断 4条,3条,2条
     * @param array $numbers
     * @param int   $same
     * @param array $sameCounts 相同的结果
     * @return int
     */
    public function checkSame(array $numbers, $same = 4, array &$sameCounts = [])
    {
        // 桶方法
        foreach ($numbers as $number) {
            if (!isset($sameCounts[$number])) {
                $sameCounts[$number] = 1;
            } else {
                $sameCounts[$number]++;
            }
        }
        $sameNumber = 0;
        foreach ($sameCounts as $key => $sameCount) {
            if (($ceil = (int)($sameCount / $same)) > 0) {
                $sameNumber += $ceil;
            }
        }
        return $sameNumber;
    }
    /**
     * 检查是不是 顺子
     * @param $numbers
     * @return bool
     */
    public function checkStraight($numbers)
    {
        $isStraight = true;
        foreach ($numbers as $key => $number) {
            $nextKey = $key + 1;
            if (isset($numbers[$nextKey]) && ($number - $numbers[$nextKey] != 1)) {
                $isStraight = false;
                break;
            }
        }
        return $isStraight;
    }
    /**
     * 检查是不是 同花
     * @param $color
     * @return bool
     */
    public function checkFlush($color)
    {
        return count(array_unique($color)) == 1;
    }
    /**
     * 获取唯一的排列
     * @param array $cards
     * @param int   $number
     * @return array
     */
    public function getUniqueCombinationWithSort(array $cards, $number = 5)
    {
        $combinations = $this->arrangement($cards, $number);
        $result = [];
        foreach ($combinations as $combination) {
            $unique = true;
            $this->sortCard($combination);
            foreach ($result as $item) {
                if ($item == $combination) {
                    $unique = false;
                    break;
                }
            }
            if ($unique) {
                $result[] = $combination;
            }
        }
        return $result;
    }
    /**
     * 根据牌面,花色的大小从大到小排序
     * @param array  $cards 牌
     * @param bool   $sortColor 是否比较花色
     * @param string $sort 降序(desc),还是升序(asc)
     */
    protected function sortCard(array &$cards, $sortColor = true, $sort = 'desc')
    {
        $desc = strtolower($sort) == 'desc';
        usort($cards, function ($value, $next) use ($sortColor, $desc) {
            $number = $this->getCardNumber($value);
            $nextNumber = $this->getCardNumber($next);
            if ($number == $nextNumber) {
                // 比较花色
                if ($sortColor) {
                    return $this->getCardColor($value) < $this->getCardColor($next) ? ($desc ? 1 : -1) : ($desc ? -1 : 1);
                }
                return 0;
            }
            return $number < $nextNumber ? ($desc ? 1 : -1) : ($desc ? -1 : 1);
        });
    }
    /**
     * 排列
     * @param $array
     * @param $number
     * @return array
     */
    public function arrangement($array, $number = 5)
    {
        $result = [];
        $count = count($array);
        if ($number <= 0 || $number > $count) {
            return $result;
        }
        for ($i = 0; $i < $count; $i++) {
            $_temp = $array;
            //每次取一个数字,并从数组中删除
            $single = array_splice($_temp, $i, 1);
            if ($number == 1) {
                $result[] = $single;
            } else {
                $deep = $this->arrangement($_temp, $number - 1);
                foreach ($deep as $deepItem) {
                    $result[] = array_merge($single, $deepItem);
                }
            }
        }
        return $result;
    }
}

用法

$texas = new Texas();
//生成2个玩家的牌 和 公共牌
$texas->generate(2);
//输出玩家牌 和 公共牌
var_dump($texas->getPlayersCards(), $texas->getPublicCards());
//输出比较结果
echo "<pre>";
print_r($texas->comparePlayer($texas->getPlayersCards('player_1'), $texas->getPlayersCards('player_2')));

最近闲着无聊仿照各直播平台的小游戏来谢谢其中的游戏逻辑哦,求 star
https://github.com/goodspb/poker-algorithm

《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!