扑克牌编码与排序算法实现 - 面向对象编程
/**
* 题目说明及要求:
* - 请使用面向对象思维答题,且不要依赖枚举
* - 禁止出现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 著作权归作者所有。请勿转载和采集!