/**
 * 题目说明及要求:
 * - 请使用面向对象思维答题,且不要依赖枚举
 * - 禁止出现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
    {
        // todo code
    }

    /**
     * 说明:
     * 输入可能有任意花色,需要返回最长的花色的结果,如最长长度的花色有多个,则随机返回一个
     * 相同花色的排序规则:按照牌值(即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
    {
        // todo code
    }
}

请根据Card类现有代码及注释完善该Card类内容:class Card
{
    private array $cardMap = [
        1 => ['name' => 'A', 'value' => 1],
        2 => ['name' => '2', 'value' => 2],
        3 => ['name' => '3', 'value' => 3],
        4 => ['name' => '4', 'value' => 4],
        5 => ['name' => '5', 'value' => 5],
        6 => ['name' => '6', 'value' => 6],
        7 => ['name' => '7', 'value' => 7],
        8 => ['name' => '8', 'value' => 8],
        9 => ['name' => '9', 'value' => 9],
        10 => ['name' => '10', 'value' => 10],
        11 => ['name' => 'J', 'value' => 11],
        12 => ['name' => 'Q', 'value' => 12],
        13 => ['name' => 'K', 'value' => 13],
        63 => ['name' => '小王', 'value' => 14],
        64 => ['name' => '大王', 'value' => 15],
    ];

    /**
     * 这个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
    {
        $cardSet = [];
        for ($i = 1; $i <= $n; $i++) {
            $cardSet = array_merge($cardSet, range(($i - 1) * 64 + 1, $i * 64 - ($useJoker ? 0 : 2)));
        }
        return $cardSet;
    }

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

        // 对每个花色的牌进行排序
        $sortedCards = [];
        foreach ($cardColor as $color => $cards) {
            $sortedCards[$color] = $this->sortSingleColorCards($cards);
        }

        // 找到最长的花色
        $maxColor = null;
        $maxCount = 0;
        foreach ($sortedCards as $color => $cards) {
            if (count($cards) > $maxCount) {
                $maxColor = $color;
                $maxCount = count($cards);
            }
        }

        // 随机选择一种最长花色
        $result = $sortedCards[$maxColor];
        shuffle($result);
        return $result;
    }

    /**
     * 获取牌的花色
     * @param int $num 牌的编码
     * @return int 牌的花色,1-4分别代表黑桃、红桃、梅花、方块
     */
    private function getCardColor(int $num): int
    {
        return (int)ceil($num / 16);
    }

    /**
     * 对一种花色的牌进行排序
     * @param array $cards 牌的编码数组
     * @return array 排序后的牌的编码数组
     */
    private function sortSingleColorCards(array $cards): array
    {
        // 牌值(即A-K)从小到大排序
        sort($cards);
        $count = count($cards);

        // 牌值相同的牌排在一起
        for ($i = 0; $i < $count - 1; $i++) {
            $j = $i + 1;
            while ($j < $count && $this->getCardValue($cards[$j]) == $this->getCardValue($cards[$i])) {
                $j++;
            }
            if ($j > $i + 1) {
                $sameValueCards = array_slice($cards, $i, $j - $i);
                $cards = array_merge(
                    array_slice($cards, 0, $i),
                    $this->sortSameValueCards($sameValueCards),
                    array_slice($cards, $j)
                );
                $count -= $j - $i - 1;
                $i--;
            }
        }

        // A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理
        // 通配牌:只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌
        $wildCardCount = 0; // 当前花色的通配牌数量
        $cardsMap = []; // 牌值 => 编码 的映射
        foreach ($cards as $card) {
            $value = $this->getCardValue($card);
            if ($value == 1 && isset($cardsMap[13])) {
                // 如果已有K,则将A作为K的下一张牌处理
                $cardsMap[14] = $card;
            } elseif ($value == 2 && $wildCardCount == 0) {
                // 如果是2,且还没有通配牌,则将该2作为缺口
                $cardsMap[99] = $card;
            } elseif ($value == 2 || $value == 14 || $value == 15) {
                // 如果是通配牌,则记录通配牌数量,并将该通配牌作为缺口
                $wildCardCount++;
                if ($value == 2) {
                    $cardsMap[99] = $card;
                } else {
                    $cardsMap[$value + 100] = $card;
                }
            } else {
                // 其他牌直接加入到映射中
                $cardsMap[$value] = $card;
            }
        }

        // 将缺口填上通配牌
        if ($wildCardCount > 0) {
            ksort($cardsMap);
            $wildCardUsed = 0; // 已经使用的通配牌数量
            $wildCardColorMap = []; // 每个缺口对应的通配牌的花色,缺口 => 花色
            foreach ($cardsMap as $value => $card) {
                if ($value == 99) {
                    // 遇到缺口,尝试使用同花色的通配牌填充
                    $color = $this->getCardColor($card);
                    if (isset($wildCardColorMap[$value])) {
                        // 如果已经有同一缺口的通配牌了,则跳过
                        continue;
                    }
                    if (isset($cards[$color + 100]) && $wildCardUsed < $wildCardCount) {
                        // 如果有同花色的通配牌,则使用同花色的通配牌填充该缺口
                        $wildCardUsed++;
                        $wildCardColorMap[$value] = $color;
                        $cardsMap[$value] = $cards[$color + 100];
                    } elseif ($wildCardUsed < $wildCardCount) {
                        // 否则使用任意通配牌填充该缺口
                        $wildCardUsed++;
                        $cardsMap[$value] = $cards[$wildCardCount + $value];
                    } else {
                        // 如果通配牌数量已用完,则跳出循环
                        break;
                    }
                }
            }
        }

        // 按牌值排序
        ksort($cardsMap);

        return array_values($cardsMap);
    }

    /**
     * 获取牌的牌值
     * @param int $num 牌的编码
     * @return int 牌的牌值,1-15分别代表A、2、3、4、5、6、7、8、9、10、J、Q、K、小王、大王
     */
    private function getCardValue(int $num): int
    {
        if ($num == 63 || $num == 64) {
            return $this->cardMap[$num]['value'];
        }
        return $num % 16 == 0 ? 13 : $num % 16 - 1;
    }

    /**
     * 对同一牌值的牌进行排序
     * @param array $cards 牌的编码数组
     * @return array 排序后的牌的编码数组
     */
    private function sortSameValueCards(array $cards): array
    {
        // A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理
        // 通配牌:只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌
        $wildCardCount = 0; // 当前牌值的通配牌数量
        $cardsMap = []; // 花色 => 编码 的映射
        foreach ($cards as $card) {
            $color = $this->getCardColor($card);
            $value = $this->getCardValue($card);
            if ($value == 1 && isset($cardsMap[$color]['K'])) {
                // 如果已有K,则将A作为K的下一张牌处理
                $cardsMap[$color]['A'] = $card;
            } elseif ($value == 2 && $wildCardCount == 0) {
                // 如果是2,且还没有通配牌,则将该2作为缺口
                $cardsMap[$color][99] = $card;
            } elseif ($value == 2 || $value == 14 || $value == 15) {
                // 如果是通配牌,则记录通配牌数量,并将该通配牌作为缺口
                $wildCardCount++;
                if ($value == 2) {
                    $cardsMap[$color][99] = $card;
                } else {
                    $cardsMap[$color][$value + 100] = $card;
                }
            } else {
                // 其他牌直接加入到映射中
                $cardsMap[$color][$value] = $card;
            }
        }

        // 将缺口填上通配牌
        if ($wildCardCount > 0) {
            foreach ($cardsMap as $color => &$cards) {
                ksort($cards
扑克牌编码及排序算法实现

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

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