/**
 * 题目说明及要求:
 * - 请使用面向对象思维答题,且不要依赖枚举
 * - 禁止出现notice,请自行修改php.ini,不要屏蔽错误输出
 * 扑克牌编码规则如下:
 * - 使用从1开始的十进制整数为每张扑克牌进行编码
 * - 花色顺序:黑桃,红桃,梅花,方块
 * - 每副牌占用64个数字,如1-64,65-128,以此类推;每个花色占用16个数字,但实际使用了13个,如1-13是黑桃A到K,17-29是红桃A到K,以此类推;最后两个数字为大小王,如63,64
 * - 通配牌:大王、小王、所有牌值是2的牌,可以作为任意花色任意牌值
 *
 * 请根据提示完善以下功能
 */
class Card
{
    /**
     * 这个cardNum是题目中提到的 '使用从1开始的十进制整数' 如17 64 等
     */
    public function __construct(private int $cardNum)
    {

    }

    /**
     * 示例:
     *  输入:1,false;输出:array_merge(range(1,13), range(17,29), range(33,45), range(49,61))
     * @param int $n 几副牌 大于0的正整数
     * @param bool $useJoker 是否有王
     * @return array 根据参数返回n副牌的全部编码
     */
    public function getAllCardNumSet(int $n, bool $useJoker): array
    {
        $cardNumSet = [];
        for ($i = 0; $i < $n; $i++) {
            for ($j = 0; $j < 4; $j++) {
                $start = $i * 64 + $j * 16 + 1;
                $cardNumSet = array_merge($cardNumSet, range($start, $start + 12));
            }
        }
        if ($useJoker) {
            $cardNumSet[] = 63;
            $cardNumSet[] = 64;
            $cardNumSet[] = 2;
        }
        return $cardNumSet;
    }

    /**
     * 说明:
     * 输入可能有任意花色,需要返回最长的花色的结果,如最长长度的花色有多个,则随机返回一个
     * 相同花色的排序规则:按照牌值(即A-K)从小到大排序,排序规则分为三种,A(1),通配牌,其他牌
     *  - A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理,即排序规则为:A23456789,10,J,Q,K,A
     *  - 通配牌:
     *      只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌,比如已有135,则缺口为2467...等,且不能有两张通配牌同时作为2(由于所有2是通配牌,所以2这个位置必然是缺口)
     *      如已有的通配牌数量不足以插入所有缺口,则忽略剩余缺口,只要保证从小到大的顺序即可,如大于缺口数量,则舍弃剩余通配牌
     *      优先使用同花色通配牌插入缺口,优先使用同花色2插入2这个缺口(非必须要求,加分项)
     *  - 其他牌:如果有多张一样牌值的如3,则排在一起,并且这两张牌排序不分前后
     * 示例:
     *  输入:[3,2,1,66,65,67,17,18,10,11,19,36,53] 输出:[1,2,3,67,66,18,10,11,65]
     *
     * @param array $input 不重复整数的一维数组,即getAllCardNumSet()的子集
     * @return array 返回最长同花色排序结果的一维数组
     */
    public function sortCards(array $input): array
    {
        // 先将牌按照花色分类
        $cardsBySuit = [];
        foreach ($input as $cardNum) {
            $suit = $this->getSuit($cardNum);
            if (!isset($cardsBySuit[$suit])) {
                $cardsBySuit[$suit] = [];
            }
            $cardsBySuit[$suit][] = $cardNum;
        }

        // 对于每个花色,将牌按照牌值排序
        foreach ($cardsBySuit as &$cards) {
            usort($cards, function ($card1, $card2) {
                $value1 = $this->getCardValue($card1);
                $value2 = $this->getCardValue($card2);
                if ($value1 === $value2) {
                    return 0;
                }
                if ($value1 === 1) {
                    return -1;
                }
                if ($value2 === 1) {
                    return 1;
                }
                if ($value1 === 2) {
                    return -1;
                }
                if ($value2 === 2) {
                    return 1;
                }
                return ($value1 < $value2) ? -1 : 1;
            });

            // 将通配牌插入缺口
            $wildcardCount = $this->getWildcardCount($cards);
            if ($wildcardCount > 0) {
                $missingCards = $this->getMissingCards($cards);
                $wildcardUsed = 0;
                foreach ($missingCards as $missingCard) {
                    if ($this->isWildcard($missingCard)) {
                        $cards[] = $missingCard;
                        $wildcardUsed++;
                        if ($wildcardUsed === $wildcardCount) {
                            break;
                        }
                    }
                }
                foreach ($missingCards as $missingCard) {
                    if (!$this->isWildcard($missingCard)) {
                        $cards[] = $missingCard;
                    }
                }
            }
        }

        // 找到最长的花色,如果有多个,则随机返回一个
        $maxSuitLength = 0;
        $maxSuit = null;
        foreach ($cardsBySuit as $suit => $cards) {
            if (count($cards) > $maxSuitLength) {
                $maxSuitLength = count($cards);
                $maxSuit = $suit;
            }
        }
        $maxSuits = [];
        foreach ($cardsBySuit as $suit => $cards) {
            if (count($cards) === $maxSuitLength) {
                $maxSuits[] = $suit;
            }
        }
        $resultSuit = $maxSuit;
        if (count($maxSuits) > 1) {
            $randIndex = array_rand($maxSuits);
            $resultSuit = $maxSuits[$randIndex];
        }

        // 将最长的花色的牌按照顺序排列
        $resultCards = $cardsBySuit[$resultSuit];
        usort($resultCards, function ($card1, $card2) {
            $value1 = $this->getCardValue($card1);
            $value2 = $this->getCardValue($card2);
            if ($value1 === 1 && $value2 === 1) {
                return 0;
            }
            if ($value1 === 1) {
                return -1;
            }
            if ($value2 === 1) {
                return 1;
            }
            if ($value1 === 2) {
                return -1;
            }
            if ($value2 === 2) {
                return 1;
            }
            return ($value1 < $value2) ? -1 : 1;
        });

        return $resultCards;
    }

    /**
     * 获取牌的花色
     * @param int $cardNum
     * @return int
     */
    private function getSuit(int $cardNum): int
    {
        return (int)(($cardNum - 1) / 16);
    }

    /**
     * 获取牌的牌值
     * @param int $cardNum
     * @return int
     */
    private function getCardValue(int $cardNum): int
    {
        $value = ($cardNum - 1) % 16 + 1;
        if ($value === 1) {
            return 14;
        }
        if ($value === 2) {
            return 15;
        }
        return $value;
    }

    /**
     * 判断牌是否是通配牌
     * @param int $cardNum
     * @return bool
     */
    private function isWildcard(int $cardNum): bool
    {
        return ($cardNum === 2 || $this->getCardValue($cardNum) === 2);
    }

    /**
     * 获取牌中缺少的牌
     * @param array $cards
     * @return array
     */
    private function getMissingCards(array $cards): array
    {
        $missingCards = [];
        for ($i = 1; $i <= 13; $i++) {
            $found = false;
            foreach ($cards as $card) {
                if ($this->getCardValue($card) === $i) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                $missingCards[] = $i;
            }
        }
        return $missingCards;
    }

    /**
     * 获取牌中通配牌的数量
     * @param array $cards
     * @return int
     */
    private function getWildcardCount(array $cards): int
    {
        $wildcardCount = 0;
        foreach ($cards as $card) {
            if ($this->isWildcard($card)) {
                $wildcardCount++;
            }
        }
        return $wildcardCount;
    }
}
扑克牌编码与排序算法实现 - 面向对象编程

原文地址: https://www.cveoy.top/t/topic/ntBX 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录