远程笔试题目-使用面向对象的方式实现扑克牌排序

admin 2023-05-12 00:36:15 1748

有些公司需要你先答题,后面试,比如这家公司,主要做海外游戏的,所以出题难度也确实很大,请看下面

<?php

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

class Card
{
/**
* @param int $cardNum 即扑克牌编码,如17 29 63 等
*/
public function __construct(int $cardNum)
{
//todo code
}
}

/**
* 示例:
* 输入: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副牌的全部编码
*/
function getAllCardNumSet(int $n, bool $useJoker): array
{
//todo code
return [];
}

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

$all = getAllCardNumSet(2, true);
var_dump($all);

shuffle($all);

var_dump(sortCards(array_slice($all, 0, rand(10, 14))));

答案->上代码:

<?php

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

class Card
{
    public int $cardNum;

    /**
     * @param int $cardNum 即扑克牌编码,如17 29 63 等
     */
    public function __construct(int $cardNum)
    {
        $this->cardNum = $cardNum;
    }

    /**
     * 返回当前扑克是否为通配牌
     *
     * @return bool
     */
    function isWildcard(): bool
    {
        return in_array($this->getIndex(), [2, 63, 64]);
    }

    /**
     * 返回当前扑克的花色
     *
     * @return int 1黑桃,2红桃,3梅花,4方块,0错误
     */
    function getColor(): int
    {
        $n = floor(($this->cardNum - 1) / 64);
        $cardNum = $this->cardNum - $n * 64;

        for ($color = 1; $color <= 4; $color++) { // 四个花色
            if ($color * 16 > $cardNum) {
                return $color;
            }
        }
        return 0;
    }

    /**
     * 返回当前扑克的大小王
     *
     * @return int 1小王,2大王,0不是大小王,
     */
    function getKing(): int
    {
        $n = floor(($this->cardNum - 1) / 64);
        $index = $this->cardNum - $n * 64;
        if ($index == 63) {
            return 1;
        }
        if ($index == 64) {
            return 2;
        }
        return 0;
    }

    /**
     * 返回当前扑克的的索引
     *
     * @return int A~K对应的1~13
     */
    function getIndex(): int
    {
        $n = floor(($this->cardNum - 1) / 64);
        $index = $this->cardNum - $n * 64;
        $n = floor(($index - 1) / 16);
        return $index - $n * 16;
    }

    function __toString(): string
    {
        static $kings = ['?', '?'];
        static $colors = ['♠️', '♥️', '♣️', '♦️'];
        static $indexs = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];

        $king = $this->getKing();
        if ($king) {
            return $kings[$king - 1];
        }
        $color = $this->getColor();
        $index = $this->getIndex();
        return $colors[$color - 1] . $indexs[$index - 1];
    }

    function __debugInfo(): array
    {
        return [$this->cardNum => (string)$this];
    }
}

// 断言测试
assert(new Card(1) == '♠️A');
assert(new Card(2) == '♠️2');
assert(new Card(63) == '?');
assert(new Card(64) == '?');
assert(new Card(17) == '♥️A');
assert(new Card(18) == '♥️2');

/**
 * 示例:
 *  输入: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副牌的全部编码
 */
function getAllCardNumSet(int $n, bool $useJoker): array
{
    $set = [];
    for ($num = 1; $num <= $n; $num++) { // 几副牌
        for ($color = 1; $color <= 4; $color++) { // 四个花色
            $offset = ($color - 1) * 16 + ($num - 1) * 64;
            $set[] = range($offset + 1, $offset + 13); // A~K
        }
        if ($useJoker) {
            $offset = ($num - 1) * 64;
            $set[] = [$offset + 63, $offset + 64]; // 大小王
        }
    }
    $set = array_merge(...$set); // 合并数组
    return $set;
}

/**
 * 说明:
 * 输入可能有任意花色,需要返回最长的花色的结果,如最长长度的花色有多个,则随机返回一个
 * 相同花色的排序规则:按照牌值(即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 返回最长同花色排序结果的一维数组
 */
function sortCards(array $input): array
{
    /** @var Card[] $wildcards 所有的通配牌 */
    $wildcards = [];

    /** @var Card[][] $cards 所有的同花牌(不含通配牌) */
    $cards = [];

    foreach ($input as $k => $v) {
        $card = new Card($v);
        if ($card->isWildcard()) {
            $wildcards[] = $card;
        } else {
            $cards[$card->getColor()][] = $card;
        }
    }

    // 同花长度排序 将长度从大到小排序
    uasort($cards, fn ($a, $b) => count($b) <=> count($a));

    // 通配从小到大
    usort($wildcards, fn ($a, $b) => $a->getIndex() <=> $b->getIndex());

    // 当前花色(长度最大的花色)
    $color = array_key_first($cards);
    if ($color) {
        // 将与当前花色相同的牌放前面
        usort($wildcards, function ($a, $b) use ($color) {
            if ($a->getColor() == $color) {
                return -1;
            }
            if ($b->getColor() == $color) {
                return 1;
            }
            return 0;
        });
    }

    /** @var Card[] 最长的同花牌 */
    $lists = $cards[$color] ?? [];

    // 从小到大排序
    usort($lists, fn ($a, $b) => $a->getIndex() <=> $b->getIndex());

    // A(1):如果只有一张A作为1处理,如果大于1张,将其中的一张作为K的下一张牌处理,即排序规则为:A23456789,10,J,Q,K,A
    $a = [];
    foreach ($lists as $k => $card) {
        if ($card->getIndex() == 1) {//把所有的A放a里
            $a[$k] = $card;
        }
    }
    if (count($a) > 1) {
        unset($a[array_key_first($a)]);//删掉存在的第一个a
        foreach ($a as $k => $card) {//处理剩下的a,并将其放数组最后
            unset($lists[$k]);
            $lists[] = $card;
        }
    }
    $lists = array_values($lists);

    // 通配牌:
    //  只能充当不存在的那张牌(下称缺口),同一缺口只能使用一张通配牌,比如已有135,则缺口为2467...等,且不能有两张通配牌同时作为2(由于所有2是通配牌,所以2这个位置必然是缺口)
    //  如已有的通配牌数量不足以插入所有缺口,则忽略剩余缺口,只要保证从小到大的顺序即可,如大于缺口数量,则舍弃剩余通配牌
    //  优先使用同花色通配牌插入缺口,优先使用同花色2插入2这个缺口(非必须要求,加分项)
    //  其他牌:如果有多张一样牌值的如3,则排在一起,并且这两张牌排序不分前后

    $inserts = [];

    foreach ($lists as $i => $current) {
        if (!$wildcards) {
            break;
        }

        $prev = $lists[$i - 1] ?? null;

        if ($prev === null) {
            $diff = $current->getIndex() - 1;//第一张牌时
        } else {
            $diff = $current->getIndex() - $prev->getIndex() - 1;
        }

        if ($diff < 1) {
            continue;
        }
        // 找到lists需要插入通配牌的位置,并把$wildcards分割到找到的位置处(key为位置)
        $inserts[$i] = array_splice($wildcards, 0, $diff);
    }

    krsort($inserts);
    foreach ($inserts as $pos => $_wildcards) {//从大到小插入统配牌
        array_splice($lists, $pos, 0, $_wildcards);
    }

    foreach ($wildcards as $card) {//剩余的统配牌
        $lists[] = $card;
    }

    // 还原为数字
    $new = [];
    foreach ($lists as $card) {
        $new[] = $card->cardNum;
    }
    return $new;
}

echo '<pre>';
// 输入:[3,2,1,66,65,67,17,18,10,11,19,36,53] 输出:[1,2,3,67,66,18,10,11,65]
var_dump(implode(',', sortCards([3, 2, 1, 66, 65, 67, 17, 18, 10, 11, 19, 36, 53])));

// $r = [1, 2, 3, 67, 66, 18, 10, 11, 65];
// foreach ($r as $k => $v) {
//     $r[$k] = new Card($v);
// }
// var_dump($r);

// $all = getAllCardNumSet(2, true);
// var_dump($all);
//
// shuffle($all);
//
// var_dump(sortCards(array_slice($all, 0, rand(10, 14))));
可爱猫?Telegram电报群 https://t.me/ikeaimao

社区声明 1、本站提供的一切软件、教程和内容信息仅限用于学习和研究目的
2、本站资源为用户分享,如有侵权请邮件与我们联系处理敬请谅解!
3、本站信息来自网络,版权争议与本站无关。您必须在下载后的24小时之内,从您的电脑或手机中彻底删除上述内容
最新回复 (0)

您可以在 登录 or 注册 后,对此帖发表评论!

返回