-
扑克牌可以用字典或者列表来表示,但是这样的话会浪费很多的空间。
因此,本项目将使用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
- 类成员的话比较简单,主要存储的是牌库以及他们的特殊字符。
- 这里将特殊字符作为静态成员变量,牌库作为公共成员变量
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) #一个一维向量作为牌库
- 由于我们使用的是一个一维向量来进行存储,因此我们需要计算出一维向量和扑克牌之间的映射关系
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
- 由于牌库是一维数组,因此可以直接使用一维数组打乱的方式实现洗牌
- 对于重置牌库,则可以暴力的重新构造一个有序的一维向量
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参数,这样可以让用户自己设定随机种子来保证多次洗牌的结果是相同的
- 在很多个扑克游戏中,都喜欢在牌库顶抽走卡牌,在牌库底加入卡牌。
因此设置这两个方法是有必要的。
- 需要注意的是,在牌库中随机位置抽卡和随机位置加入卡牌可以通过“取卡/插卡 + 打乱”的组合来实现,因此不需要额外设计。
- 丢弃卡片则是需要在牌库中丢弃掉某几张不需要的卡,这一点适用于某些规则之中。
- 由于这几个 *** 作需要判断卡牌是否在牌库中,所以特意加入了一个私有方法来判断卡牌是否在牌库里
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方法中
-
由于这个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
- 以下是一个简单的展示环节
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)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)