如何在玩家之间随机洗牌?

Posted

技术标签:

【中文标题】如何在玩家之间随机洗牌?【英文标题】:How to randomly shuffle a deck of cards among players? 【发布时间】:2018-11-19 01:13:41 【问题描述】:

我在使用另一种功能发牌时遇到问题。这是我到目前为止所拥有的。

import random as rand 

def create(): 
     ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']   
     suites = ['H', 'C', 'D', 'S'] 
     deck = [[r + s] for s in suites for r in ranks]    
     return deck   

def cards_dealt (num_cards, num_players, deck): 
     rand.shuffle(deck) 


print(cards_dealt(5, 3, deck)) 

我了解该功能不完整。我需要 num_cards 是每个玩家收到的卡片数量,num_players 是玩家数量,deck 是函数 create() 中的卡片字符串列表。 例如,打印语句将显示三位玩家每人从已洗牌的列表牌组中获得的 5 张牌。我的问题是,每当我写东西时,它都会说没有定义甲板。

【问题讨论】:

deck 是函数create 中的局部变量。它在函数之外是不可见的。您必须将其设为全局或将其返回。回到你的 Python 101。 但我确实退货了?这就是我感到困惑的原因。 @DyZ createreturn deck 结尾。 cards_dealt 有一个名为 deck 的参数。缺少的是代码中某处对create 的调用。 不幸的是你没有调用你的 create() 函数,你只是定义它,在打印之前添加一个简单的deck = create()。但我必须注意,不鼓励在函数内部和外部使用相同的变量名,因为这可能会导致稍后在变量范围方面产生混淆,但在您的示例中会这样做。 作为如何解决下一个问题的提示:发牌基本上只是将牌组分成大小相等的块(除了一些剩余部分)。在 Stack Overflow 上已经有很多问题有很好的答案,而且应该很容易将其中一个问题应用到您的程序中。 【参考方案1】:

忘记你的两个函数,因为它们都是正确的,看看你的***代码:

import random as rand
def ...
def ...
print(cards_dealt(5, 3, deck)) 

deck 来自哪里?无处。因此例外。

很明显你打算从哪里来——你有一个以return deck结尾的create函数。你只是没有在任何地方调用它。

所以你想要:

import random as rand
def ...
def ...
deck = create()
print(cards_dealt(5, 3, deck)) 

…或者,如果您想更清楚地说明全局变量与两个局部变量无关:

import random as rand
def ...
def ...
mydeck = create()
print(cards_dealt(5, 3, mydeck)) 

…或者,如果你想让事情更简洁:

import random as rand
def ...
def ...
print(cards_dealt(5, 3, create())) 

除了这个问题,cards_dealt 没有return,所以它只返回None。当你的函数完成后,可能会有一些值得打印出来的东西,但是现在,当你调试你目前所拥有的东西时,它并不是很有用。

同时,如果您想打印洗牌后的牌组,这可能很有用,您可以这样做:

import random as rand
def ...
def ...
mydeck = create()
cards_dealt(5, 3, mydeck)
print(mydeck)

当您稍后完成该函数以返回,例如,包含 5 张卡片的 3 个列表的元组时,您可能希望存储这些返回值以供以后使用并打印它们,因此它可能会如下所示:

import random as rand
def ...
def ...
mydeck = create()
hands = cards_dealt(5, 3, mydeck)
for hand in hands:
    print(hand)

【讨论】:

它们几乎都是正确的,cards_dealt 没有返回值。 或者只是限定该语句。我喜欢你解释它的方法。 看起来不错,如果您想添加它,这就是我为 card_dealt 输入的内容。 rand.shuffle(deck) 然后return [deck[i:i+5] for i in range(0, num_cards*num_players, num_cards)] @Zev 我不想为他们编写所有 OP 的代码,只是展示如何解决他们询问的问题。【参考方案2】:

让我建议一种面向对象的方法,我们将在其中定义类 CardDeckPlayer

使用对象而不是卡片列表将提供一个简洁的 API 来实现游戏。当您实现游戏逻辑时,还可以更轻松地保持单一事实来源,了解每张牌在哪里以及哪些玩家拥有每张牌。

甲板API

import random


class Card:
    def __init__(self, kind, rank, deck):
        self._kind = kind
        self._rank = rank
        self.deck = deck
        self.where = None

    def __repr__(self):
        return 'Card(kind=, rank='.format(self.kind, self.rank)

    @property
    def kind(self):
        return self._kind

    @property
    def rank(self):
        return self._rank


class Deck:
    def __init__(self):
        ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
        kinds = ['H', 'C', 'D', 'S']
        self.cards = [Card(kind, rank, self) for kind in kinds for rank in ranks]

    def deal(self, players, n=None):
        if any(p.deck is not self for p in players):
            raise RuntimeError('Player  is not playing the deck'.format(p.id))

        n = n if n is not None else len(self.cards)
        random.shuffle(self.cards)

        for i, card in enumerate(self.cards[:n * len(players)]):
            card.where = players[i % len(players)]


class Player:
    def __init__(self, id, deck):
        self.id = id
        self.deck = deck

    @property
    def hand(self):
        return [card for card in deck.cards if card.where is self]

发牌

deck = Deck()
players = [Player(id, deck) for id in range(3)]

deck.deal(players, n=4)

for p in players:
    print(p.hand)

输出

[Card(kind=D, rank=A), Card(kind=D, rank=2), Card(kind=S, rank=5), Card(kind=S, rank=K)]
[Card(kind=S, rank=9), Card(kind=D, rank=5), Card(kind=C, rank=A), Card(kind=C, rank=Q)]
[Card(kind=C, rank=9), Card(kind=S, rank=J), Card(kind=D, rank=3), Card(kind=H, rank=9)]

打牌

card.where 属性可以更新以指示卡片的位置。由于它是卡片位置的唯一真实来源,因此会更新player.hand 属性。

deck = Deck()
players = [Player(id, deck) for id in range(2)]

deck.deal(players, n=1)

players[0].hand[0].where = players[1]

for p in players:
    print(p.hand)

输出

[]
[Card(kind=H, rank=K), Card(kind=D, rank=2)]

更多功能

上述 API 提供了发牌和手牌移动的基础知识,但可以扩展以实现新功能。

【讨论】:

嗯,为什么?这显然比目前的 OP 先进得多 当然,但仅使用列表和字典来实现纸牌游戏几乎总是会因为有多个事实来源而难以追踪错误。虽然它比 OP 更先进,但标题如何在玩家之间随机洗牌? 可能会很好地吸引其他正在寻找纸牌实施想法的用户。此外,在 SO 上,常见的类似旁注的问题位于答案堆的底部,但仍然可以作为解决外围问题的旁注。【参考方案3】:

当然还有其他方法可以做到这一点,但您可以这样完成您的功能,以返回您的玩家及其手牌的字典:

def cards_dealt(num_cards, num_players, deck):
    rand.shuffle(deck)
    return player:[deck.pop() for _ in range(num_cards)] for player in range(num_players)

然后,创建你的牌组(这就是你的“牌组未定义”问题出现的地方)并调用你的函数:

my_deck = create()

print(cards_dealt(5, 3, my_deck))

返回:

0: [['10S'], ['8S'], ['7D'], ['6S'], ['4C']], 
 1: [['JD'], ['AC'], ['QD'], ['2D'], ['JS']], 
 2: [['6D'], ['4H'], ['AS'], ['4S'], ['9S']]

等效的 cards_dealt 函数,但使用循环而不是 dict 和列表推导:

def cards_dealt (num_cards, num_players, deck): 
     rand.shuffle(deck)
     player_hands = i:[] for i in range(num_players)
     for player in range(num_players):
         for num in range(num_cards):
             player_hands[player].append(deck.pop())
     return player_hands

【讨论】:

为什么它返回字典而不是列表列表? IMO,这是一种比列表更简洁的组织方式 嗯,好的。当它不在列表中时,我只是不知道如何完成其​​余部分。我会努力解决的。 如果你指定player_hands = cards_dealt(5,3,my_deck),你可以访问给定玩家的牌,比如玩家1:player_hands[1]

以上是关于如何在玩家之间随机洗牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何在洗牌的连续整数数组中找到重复元素?

如何在 C# 中以相同的方式随机化/洗牌两个数组

如何随机洗牌一个数组

Ionic 2 - *ngFor 值之间的随机洗牌

如何在 Unity C# 中的对象之间传输信息?

随机洗牌算法Knuth Shuffle和错排公式