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

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

  • 扑克牌可以用字典或者列表来表示,但是这样的话会浪费很多的空间。因此,本项目将使用 Numpy 来实现,这样的话能保证性能的情况下减少存储成本。
  • 正常情况下,扑克牌的功能应该是:抽牌库顶牌、洗牌、放牌到牌库底、弃牌
  • API展示效果如下:
    请添加图片描述

1. 实现过程

  • 项目所需要的库为 import random as raimport 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) % 4def 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.librarydef 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 > 0def __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 popdef 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.librarydef 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)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2022年4月11日
下一篇 2022年4月11日

相关推荐