扑克牌游戏编码及排序算法 - 面向对象实现

本文将使用面向对象编程,实现扑克牌编码和排序算法,根据给定的编码规则,生成n副牌的编码,并对任意花色扑克牌进行排序。

编码规则

  • 使用从1开始的十进制整数为每张扑克牌进行编码。
  • 花色顺序:黑桃,红桃,梅花,方块。
  • 每副牌占用64个数字,如1-64, 65-128, 以此类推;每个花色占用16个数字,但实际使用了13个,如1-13是黑桃A到K,17-29是红桃A到K,以此类推;最后两个数字为大小王,如63, 64。
  • 通配牌:大王、小王、所有牌值是2的牌,可以作为任意花色任意牌值。

代码实现

<?php
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
    {
        $cardNums = [];
        $maxCardNum = $n * 64;
        for ($i = 1; $i <= $maxCardNum; $i++) {
            if ($useJoker) {
                if ($i == $maxCardNum - 1 || $i == $maxCardNum) {
                    $cardNums[] = $i;
                    continue;
                }
                if ($i % 16 == 0 || $i % 16 == 2) {
                    $cardNums[] = $i;
                    continue;
                }
            }
            if ($i % 16 <= 13 && $i <= $n * 64) {
                $cardNums[] = $i;
            }
        }
        return $cardNums;
    }

    /**
     * 说明:
     * 输入可能有任意花色,需要返回最长的花色的结果,如最长长度的花色有多个,则随机返回一个
     * 相同花色的排序规则:按照牌值(即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
    {
        $cards = [];
        foreach ($input as $cardNum) {
            $card = new Card($cardNum);
            $cards[$card->getSuit()][] = $card;
        }
        arsort($cards);
        $maxSuit = array_key_first($cards);
        $maxCards = $cards[$maxSuit];
        usort($maxCards, function ($a, $b) {
            if ($a->getValue() == $b->getValue()) {
                return 0;
            }
            if ($a->getValue() == 1) {
                return -1;
            }
            if ($b->getValue() == 1) {
                return 1;
            }
            if ($a->isJoker() && !$b->isJoker()) {
                return 1;
            }
            if (!$a->isJoker() && $b->isJoker()) {
                return -1;
            }
            if ($a->isWildcard() && !$b->isWildcard()) {
                return 1;
            }
            if (!$a->isWildcard() && $b->isWildcard()) {
                return -1;
            }
            return $a->getValue() < $b->getValue() ? -1 : 1;
        });
        $result = [];
        foreach ($maxCards as $card) {
            $result[] = $card->getCardNum();
        }
        return $result;
    }

    /**
     * 获取此牌的花色
     * @return int
     */
    public function getSuit(): int
    {
        if ($this->cardNum <= 0 || $this->cardNum > 64) {
            throw new InvalidArgumentException('Invalid card num.');
        }
        if ($this->cardNum % 16 == 0) {
            return 4;
        }
        return ($this->cardNum - 1) % 16;
    }

    /**
     * 获取此牌的点数
     * @return int
     */
    public function getValue(): int
    {
        if ($this->cardNum <= 0 || $this->cardNum > 64) {
            throw new InvalidArgumentException('Invalid card num.');
        }
        $value = ($this->cardNum - 1) / 4 + 1;
        if ($value > 13) {
            $value -= 13;
        }
        return $value;
    }

    /**
     * 判断此牌是否是王
     * @return bool
     */
    public function isJoker(): bool
    {
        if ($this->cardNum == 63 || $this->cardNum == 64) {
            return true;
        }
        return false;
    }

    /**
     * 判断此牌是否是通配牌
     * @return bool
     */
    public function isWildcard(): bool
    {
        if ($this->getValue() == 2) {
            return true;
        }
        $suit = $this->getSuit();
        foreach (range(1, 13) as $value) {
            $cardNum = ($value - 1) * 4 + ($suit - 1);
            if ($cardNum != $this->cardNum && new Card($cardNum)->getValue() == $value) {
                return false;
            }
        }
        return true;
    }

    /**
     * 获取此牌的编码
     * @return int
     */
    public function getCardNum(): int
    {
        return $this->cardNum;
    }
}

代码说明

  1. Card 类: 每个 Card 对象代表一张扑克牌,包含以下方法:

    • __construct 初始化 Card 对象,并设置其编码 cardNum
    • getAllCardNumSet 生成 n 副牌的编码,可以选择是否包含王牌。
    • sortCards 对给定的牌进行排序,返回最长的花色,并按照规则排序。
    • getSuit 获取牌的花色。
    • getValue 获取牌的点数。
    • isJoker 判断牌是否是王牌。
    • isWildcard 判断牌是否是通配牌。
    • getCardNum 获取牌的编码。
  2. sortCards 方法:

    • 将所有牌按照花色分组。
    • 找到最长的花色。
    • 对最长花色的牌进行排序,按照以下规则:
      • A(1):如果只有一张 A,则作为 1 处理;如果大于 1 张,则将其中一张作为 K 的下一张牌处理。
      • 通配牌:只能充当缺口,同一缺口只能使用一张通配牌,优先使用同花色通配牌。
      • 其他牌:如果有多张相同牌值的牌,则排在一起,并且排序不分前后。

总结

本文通过面向对象编程实现了一个扑克牌编码和排序算法,并详细解释了代码实现和逻辑。希望本文能够帮助读者更好地理解和应用面向对象编程,解决实际问题。

扑克牌游戏编码及排序算法 - 面向对象实现

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

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