用Python中的Numpy实现简单高效的扑克牌API(附代码)

用Python中的Numpy实现简单高效的扑克牌API

  • 扑克牌可以用字典或者列表来表示,但是这样的话会浪费很多的空间。因此,本项目将使用Numpy来实现,这样的话能保证性能的情况下减少存储成本。

  • 一般情况下,扑克牌应该具有的功能为: 抽牌库顶的牌、洗牌、将牌放进牌库底、丢弃某张牌

  • API展示效果如下:
    请添加图片描述



1. 实现过程

  • 项目所需要的库为
    import random as ra
    import numpy as np
    

1.1 类成员

  • 类成员的话比较简单,主要存储的是牌库以及他们的特殊字符。
  • 这里将特殊字符作为静态成员变量,牌库作为公共成员变量
    class PlayingCard(object):
        #\033[31m是颜色标识符,让字体变成红色的
        prefixs = ['\033[31m♦\033[0m','♣','\033[31m♥\033[0m','♠']
        suffixs = [str(i) for i in range(1,11)] + ['J','Q','K']
        specials = ["♚","\033[31m♚\033[0m"]
    
        def __init__(self) -> None:
            """
            构建牌库[x,y] 
            获取公式
            x = 54 // 4
            y = 54 % 4
            
            x \in [0,13],其中0~9代表数值1~10, 10/11/12代表J/Q/K, 13代表特殊牌(即王牌)
            y \in [0,3],其中0代表方块,1代表梅花,2代表桃心,3代表黑桃
            [13,0] 小王; [13,1] 大王
            """
            self.library = np.linspace(1,54,54).astype(np.int8) #一个一维向量作为牌库
    

1.2 索引转换与检索

  • 由于我们使用的是一个一维向量来进行存储,因此我们需要计算出一维向量和扑克牌之间的映射关系
    def card2index(self,index) -> tuple:
        #索引与卡牌的关系
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        index -= 1
        x,y = index//4,index % 4
        return (x,y)
    
    def card2str(self,index) -> str:
        #将索引转化为文本
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        x,y = self.card2index(index)
        return self.prefixs[y] + self.suffixs[x]
    
    def getPrefix(self,index) -> int:
        """
        得到扑克牌花色
        其中0代表方块,1代表梅花,2代表桃心,3代表黑桃
        """
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        return (index - 1) % 4
    
    def getNumber(self,index) -> int:
        #得到扑克牌数字
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        return (index - 1) //4
    

1.3 洗牌与重置牌库

  • 由于牌库是一维数组,因此可以直接使用一维数组打乱的方式实现洗牌
  • 对于重置牌库,则可以暴力的重新构造一个有序的一维向量
    def shuffle(self,seed = None) -> np.array:
        #洗牌
        if seed is None:
            seed = ra.randint(0,2^16)
        ra.seed(seed)
        ra.shuffle(self.library)
        return self.library
    
    def reload(self) -> np.array:
        #获得一副新牌
        self.library = np.linspace(1,54,54).astype(np.int8)
        return self.library
    
  • 注意到这里洗牌方法使用了seed参数,这样可以让用户自己设定随机种子来保证多次洗牌的结果是相同的

1.4 取卡牌、插卡牌与丢弃卡牌

  • 在很多个扑克游戏中,都喜欢在牌库顶抽走卡牌,在牌库底加入卡牌。因此设置这两个方法是有必要的。
  • 需要注意的是,在牌库中随机位置抽卡和随机位置加入卡牌可以通过“取卡/插卡 + 打乱”的组合来实现,因此不需要额外设计。
  • 丢弃卡片则是需要在牌库中丢弃掉某几张不需要的卡,这一点适用于某些规则之中。
  • 由于这几个操作需要判断卡牌是否在牌库中,所以特意加入了一个私有方法来判断卡牌是否在牌库里
    def _inLibrary(self,index) -> bool:
        #判断是否在卡里?
        return np.where(self.library == index)[0].size > 0
    
    def __len__(self):
        #重写len方法,即 len() 函数会返回牌库中剩余卡片数
        return len(self.library)
    
    def pop(self) -> int:
        #取出牌库顶牌
        if len(self) == 0:
            return 0
        pop = self.library[0]
        self.library = self.library[1:]
        return pop
    
    def insert(self,index:int) -> np.array:
        #在牌库底插入牌
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        assert not self._inLibrary(index), "[WARN] Card %d is already in library!" % index
        self.library = np.concatenate([self.library,np.array([index])])
        return self.library
    
    def drop(self,indexList) -> np.array:
        #丢弃某张牌
        for index in indexList if type(indexList) == list else [indexList]:
            assert self._inLibrary(index), "[WARN] Card %d is not in library!" % index
            self.library = np.delete(self.library,
                            np.where(self.library == index))
        return self.library
    
    
  • 由于是一维数组的缘故,因此用户在使用的时候需要先将卡牌转化为索引,随后再输入到drop方法中

1.5 卡牌可视化

  • 由于这个API是用一维数组实现的,因此很难直观地了解到里面到底有什么牌

  • 所以,特意设计了这么一个可视化的方法来进行查看

  • 这个方法通过重写__str__方法来实现,因此可以直接通过对实例使用print()来查看

    def __str__(self):
        string = "Card Summary: %d\n" % len(self)
        for x in range(13): #用于显示每一行(本来可以继续压缩的,但是怕可读性不行)
            string += "\t".join([self.prefixs[y] + self.suffixs[x] for y in range(4) 
                              if self._inLibrary(4 * x + y + 1)]) + "\n"
        string += "\t".join([self.specials[i] for i in [0,1] if self._inLibrary(53 + i)])
        return string
    
    

1.6 具体展示

  • 以下是一个简单的展示环节
    card = PlayingCard() #构建牌库
    print(card) #展示牌库
    
    card.shuffle(seed = 42) #随机打乱
    
    #抽出牌库顶前18张牌
    for i in range(18):
        card.pop()
    print(card)
    
    请添加图片描述

2. 全代码(太长不看版)

import random as ra
import numpy as np

class PlayingCard(object):
    prefixs = ['\033[31m♦\033[0m','♣','\033[31m♥\033[0m','♠']
    suffixs = [str(i) for i in range(1,11)] + ['J','Q','K']
    specials = ["♚","\033[31m♚\033[0m"]
    def __init__(self) -> None:
        """
        构建牌库[x,y] 
        获取公式
        x = 54 // 4
        y = 54 % 4
        
        x \in [0,13],其中0~9代表数值1~10, 10/11/12代表J/Q/K, 13代表特殊牌(即王牌)
        y \in [0,3],其中0代表方块,1代表梅花,2代表桃心,3代表黑桃
        [13,0] 小王; [13,1] 大王
        """
        self.library = np.linspace(1,54,54).astype(np.int8)

        
    
    def card2index(self,index) -> tuple:
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        index -= 1
        x,y = index//4,index % 4
        return (x,y)
    
    def card2str(self,index) -> str:
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        x,y = self.card2index(index)
        return self.prefixs[y] + self.suffixs[x]
    
    def shuffle(self,seed = None) -> np.array:
        #洗牌
        
        if seed is None:
            seed = ra.randint(0,2^16)
        ra.seed(seed)
        ra.shuffle(self.library)
        return self.library

    def _inLibrary(self,index) -> bool:
        #判断是否在卡里?
        return np.where(self.library == index)[0].size > 0
    
    def drop(self,indexList) -> np.array:
        #丢弃某张牌
        for index in indexList if type(indexList) == list else [indexList]:
            assert self._inLibrary(index), "[WARN] Card %d is not in library!" % index
            self.library = np.delete(self.library,
                            np.where(self.library == index))
        return self.library
    
    def reload(self) -> np.array:
        #获得一副新牌
        self.library = np.linspace(1,54,54).astype(np.int8)
        return self.library
    
    def getPrefix(self,index) -> int:
        """
        得到扑克牌花色
        其中0代表方块,1代表梅花,2代表桃心,3代表黑桃
        """
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        return (index - 1) % 4
    
    def getNumber(self,index) -> int:
        #得到扑克牌数字
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        return (index - 1) //4
    
    def pop(self) -> int:
        #取出牌库顶牌
        if len(self) == 0:
            return 0
        pop = self.library[0]
        self.library = self.library[1:]
        return pop
    
    def insert(self,index:int) -> np.array:
        #在牌库底插入牌
        assert not int((index-27)/27), "[WARN] Card %d is illegal!" % index
        assert not self._inLibrary(index), "[WARN] Card %d is already in library!" % index
        self.library = np.concatenate([self.library,np.array([index])])
        return self.library
    
    def __str__(self):
        string = "Card Summary: %d\n" % len(self)
        for x in range(13): #用于显示每一行(本来可以继续压缩的,但是怕可读性不行)
            string += "\t".join([self.prefixs[y] + self.suffixs[x] for y in range(4) 
                              if self._inLibrary(4 * x + y + 1)]) + "\n"
        string += "\t".join([self.specials[i] for i in [0,1] if self._inLibrary(53 + i)])
        return string
    
    def __len__(self):
        return len(self.library)
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值