扑克牌游戏编码及排序算法 - 面向对象实现
扑克牌游戏编码及排序算法 - 面向对象实现
本文将使用面向对象编程,实现扑克牌编码和排序算法,根据给定的编码规则,生成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;
}
}
代码说明
-
Card类: 每个Card对象代表一张扑克牌,包含以下方法:__construct: 初始化Card对象,并设置其编码cardNum。getAllCardNumSet: 生成 n 副牌的编码,可以选择是否包含王牌。sortCards: 对给定的牌进行排序,返回最长的花色,并按照规则排序。getSuit: 获取牌的花色。getValue: 获取牌的点数。isJoker: 判断牌是否是王牌。isWildcard: 判断牌是否是通配牌。getCardNum: 获取牌的编码。
-
sortCards方法:- 将所有牌按照花色分组。
- 找到最长的花色。
- 对最长花色的牌进行排序,按照以下规则:
- A(1):如果只有一张 A,则作为 1 处理;如果大于 1 张,则将其中一张作为 K 的下一张牌处理。
- 通配牌:只能充当缺口,同一缺口只能使用一张通配牌,优先使用同花色通配牌。
- 其他牌:如果有多张相同牌值的牌,则排在一起,并且排序不分前后。
总结
本文通过面向对象编程实现了一个扑克牌编码和排序算法,并详细解释了代码实现和逻辑。希望本文能够帮助读者更好地理解和应用面向对象编程,解决实际问题。
原文地址: https://www.cveoy.top/t/topic/ntCX 著作权归作者所有。请勿转载和采集!