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

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

用Python中的Numpy实现简单高效的扑克牌API
  • 扑克牌可以用字典或者列表来表示,但是这样的话会浪费很多的空间。


    因此,本项目将使用Numpy来实现,这样的话能保证性能的情况下减少存储成本。


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

  • API展示效果如下:


文章目录
  • 用Python中的Numpy实现简单高效的扑克牌API
  • 1. 实现过程
    • 1.1 类成员
    • 1.2 索引转换与检索
    • 1.3 洗牌与重置牌库
    • 1.4 取卡牌、插卡牌与丢弃卡牌
    • 1.5 卡牌可视化
    • 1.6 具体展示
  • 2. 全代码(太长不看版)


1. 实现过程
  • 项目所需要的库为
    import random as ra
    import numpy as np
    
1.1 类成员
  • 类成员的话比较简单,主要存储的是牌库以及他们的特殊字符。


  • 这里将特殊字符作为静态成员变量,牌库作为公共成员变量
    class PlayingCard(object):
        #3[31m是颜色标识符,让字体变成红色的
        prefixs = ['3[31m♦3[0m','♣','3[31m♥3[0m','♠']
        suffixs = [str(i) for i in range(1,11)] + ['J','Q','K']
        specials = ["♚","3[31m♚3[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 = ['3[31m♦3[0m','♣','3[31m♥3[0m','♠']
    suffixs = [str(i) for i in range(1,11)] + ['J','Q','K']
    specials = ["♚","3[31m♚3[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)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/589695.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-13
下一篇 2022-04-13

发表评论

登录后才能评论

评论列表(0条)

保存