用python玩牛牛


牛牛

牛牛游戏,本程序中模拟2名玩家参与。游戏的目标是通过比较手中的牌点数来决定胜负。下面是详细的游戏规则说明:

游戏规则

  • 牌的点数规则
    A(Ace):1点
    2-10:牌面点数
    J(Jack)、Q(Queen)、K(King):10点
  • 牌的花色规则
    花色大小顺序为:黑桃(♠)> 红桃(♥)> 梅花(♣)> 方块(♦)
    游戏流程

python代码

import random
from itertools import combinations

# 定义牌的数值
NUM_VALUES = {'A': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 'J': 11, 'Q': 12, 'K': 13}
# 定义花色的顺序
FLOWER_ORDER = {'♠': 4, '♥': 3, '♣': 2, '♦': 1}


def gameNiuNiu():
    """
    运行牛牛游戏的主要函数。
    """
    # ... 游戏逻辑代码 ...
    print('-'*15 + ' 牛牛游戏 ' + '-'*15)
    print('\t每人发5张牌,若其中三张被10整除的话比较剩余两张跟10取余的大小,')
    print('\t若都不能被10整除则取最大的一张牌跟对方比较大小,点数大的赢,若点数相同则比较点数最大牌的花色大小')
    print('\tA为1点,JQK为10点,其余按牌面大小计算点数,花色大小为:♠>♥>♣>♦')
    print('\t若点数较大的一方点数大于5点,每多一个点数分数加1')
    print('\t例如:玩家是牛9,系统是牛6,则玩家得分4(9-5)')
    score = 0
    cards = generate_deck()
    print('-' * 40)
    n = get_shuffle_position()
    cards = shuffle_cards(cards, n)
    player, sysNPC = deal_cards(cards)
    print('玩家的牌是:', ' '.join(player))
    print('系统的牌是:', ' '.join(sysNPC))
    is_win, player_point, sysNPC_point, score = compare_points(player, sysNPC)
    print('玩家的结果是:牛', player_point if player_point != 10 else '牛')
    print('系统的结果是:牛', sysNPC_point if sysNPC_point != 10 else '牛')
    if is_win:
        print(f'玩家赢了,得分{score}')
    else:
        print('系统赢了,得分0')
    print('-'*40)


def generate_deck():
    """
    生成一副标准的扑克牌。
    
    :return: 包含所有牌的列表,每张牌由花色和数字组成。
    """
    flowers = ['♠', '♥', '♣', '♦']
    numbers = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
    return [flower + number for flower in flowers for number in numbers]


def shuffle_cards(cards, n):
    """
    洗牌函数,根据给定的起始位置打乱牌序。
    
    :param cards: 一副牌的列表。
    :param n: 开始发牌的位置(整数)。
    :return: 根据给定的起始位置重新排列的牌。
    """
    random.shuffle(cards)
    return cards[n-1:] + cards[:n-1]


def get_shuffle_position():
    """
    获取用户输入的洗牌起始位置。
    
    :return: 用户输入的有效位置。
    """
    while True:
        try:
            n = int(input('请输入开始发牌的位置(必须在1到52之间):'))
            if 1 <= n <= 52:
                return n
            else:
                print('输入的位置不在1到52之间,请重新输入。')
        except ValueError:
            print('输入无效,请输入一个整数。')


def deal_cards(cards):
    """
    发牌函数,将一副牌分成玩家和系统的手牌。
    
    :param cards: 一副牌的列表。
    :return: 包含两个元素的元组,第一个元素是玩家的手牌,第二个元素是系统的手牌。
    """
    player = cards[0:10:2]
    sysNPC = cards[1:10:2]
    return player, sysNPC


def get_point(cards):
    """
    计算给定手牌的牛牛点数。
    
    :param cards: 玩家或系统的手牌列表。
    :return: 计算得到的牛牛点数。
    """
    point_list = [NUM_VALUES[card[1:]] if card[1:] in NUM_VALUES else int(card[1:]) for card in cards]
    for comb in combinations(point_list, 3):
        if sum(comb) % 10 == 0:
            remaining_nums = [x for x in point_list if x not in comb]
            return sum(remaining_nums) % 10 if sum(remaining_nums) % 10 != 0 else 10
    return 0


def compare_points(player, sysNPC):
    """
    比较玩家和系统的牛牛点数,并计算得分。
    
    :param player: 玩家的手牌列表。
    :param sysNPC: 系统的手牌列表。
    :return: 包含四个元素的元组:是否赢,玩家点数,系统点数,得分。
    """
    player_point = get_point(player)
    sysNPC_point = get_point(sysNPC)
    score = 0
    is_win = player_point > sysNPC_point
    if is_win and player_point > 5:
        score = player_point - 5
    elif player_point == sysNPC_point:
        score, is_win = compare_hands(player, sysNPC)
    return is_win, player_point, sysNPC_point, score


def compare_hands(hand1, hand2):
    """
    比较两手牌的大小,用于在点数相同的情况下确定胜者。
    
    :param hand1: 第一手牌列表。
    :param hand2: 第二手牌列表。
    :return: 包含两个元素的元组:比较结果(True为第一手牌大),最大牌的牌面。
    """
    combined_dict = {card: NUM_VALUES[card[1:]] + FLOWER_ORDER[card[0]] for card in hand1 + hand2}
    max_card1 = max(combined_dict.values())
    max_card2 = max(combined_dict.values(), key=lambda x: (-x, combined_dict.index(x)))
    if combined_dict[max_card1] > combined_dict[max_card2]:
        return True, max_card1
    else:
        return False, max_card2

# ... 测试用例代码 ...
if __name__ == '__main__':
    gameNiuNiu()  # 运行游戏

模块功能说明

1. gameNiuNiu()

这个函数是游戏的入口点,它负责启动游戏流程。首先,它打印出游戏的基本信息和规则,然后生成一副牌,洗牌,发牌,并比较玩家和系统(NPC)的牌点数,最后输出游戏结果。

2. generate_deck()

这个函数生成一副标准的扑克牌,包括所有花色和数值的组合。

flowers = ['♠', '♥', '♣', '♦']  # 定义花色
numbers = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']  # 定义数值
return [flower + number for flower in flowers for number in numbers]  # 通过列表推导式生成所有牌的组合

3. shuffle_cards(cards, n)

这个函数用于洗牌,它接受一副牌和一个起始位置,然后根据这个位置对牌进行随机打乱。

random.shuffle(cards)  # 随机打乱牌的顺序
return cards[n-1:] + cards[:n-1]  # 根据起始位置重新排列牌

4. get_shuffle_position()

这个函数获取用户输入的洗牌起始位置,并确保这个位置是有效的(即在1到52之间)。

while True:  # 无限循环直到获得有效输入
    try:
        n = int(input('请输入开始发牌的位置(必须在1到52之间):'))  # 读取用户输入并尝试转换为整数
        if 1 <= n <= 52:  # 检查输入是否在有效范围内
            return n  # 如果是,返回这个位置
        else:
            print('输入的位置不在1到52之间,请重新输入。')  # 如果不是,提示用户重新输入
    except ValueError:  # 如果输入不是整数,则捕获异常
        print('输入无效,请输入一个整数。')  # 提示用户输入一个有效的整数

5. deal_cards(cards)

这个函数负责发牌,将一副牌分成玩家和系统的手牌,每人5张。

player = cards[0:10:2]  # 给玩家发牌,取索引为偶数的牌
sysNPC = cards[1:10:2]  # 给系统发牌,取索引为奇数的牌
return player, sysNPC  # 返回玩家和系统的手牌

6. get_point(cards)

这个函数计算给定手牌的牛牛点数,根据牛牛游戏的规则,如果三张牌的数值之和能被10整除,则这三张牌的数值之和为10点,剩余两张牌的数值之和为剩余点数。

point_list = [NUM_VALUES[card[1:]] if card[1:] in NUM_VALUES else int(card[1:]) for card in cards]  # 将牌转换为数值
for comb in combinations(point_list, 3):  # 遍历所有三张牌的组合
    if sum(comb) % 10 == 0:  # 如果三张牌的数值之和能被10整除
        remaining_nums = [x for x in point_list if x not in comb]  # 获取剩余的牌
        return sum(remaining_nums) % 10 if sum(remaining_nums) % 10 != 0 else 10  # 返回剩余牌的数值之和,如果不能被10整除
return 0  # 如果没有三张牌的数值之和能被10整除,则返回0

7. compare_points(player, sysNPC)

这个函数比较玩家和系统的牛牛点数,并计算得分。

player_point = get_point(player)  # 计算玩家的点数
sysNPC_point = get_point(sysNPC)  # 计算系统的点数
score = 0  # 初始化得分为0
is_win = player_point > sysNPC_point  # 比较点数,确定是否赢
if is_win and player_point > 5:  # 如果玩家赢并且点数超过5点
    score = player_point - 5  # 计算得分
elif player_point == sysNPC_point:  # 如果点数相同
    score, is_win = compare_hands(player, sysNPC)  # 比较牌面大小和花色
return is_win, player_point, sysNPC_point, score  # 返回比较结果

8. compare_hands(hand1, hand2)

这个函数在点数相同的情况下比较两手牌的大小,并确定胜者。

combined_dict = {card: NUM_VALUES[card[1:]] + FLOWER_ORDER[card[0]] for card in hand1 + hand2}  # 将牌转换为一个综合数值(数值+花色)
max_card1 = max(combined_dict.values())  # 获取第一手牌中数值最大的牌
max_card2 = max(combined_dict.values(), key=lambda x: (-x, combined_dict.index(x)))  # 获取两手牌中数值最大的牌
if combined_dict[max_card1] > combined_dict[max_card2]:  # 比较最大牌的数值
    return True, max_card1  # 如果第一手牌的大,返回True和最大牌
else:
    return False, max_card2  # 否则返回False和第二手牌的最大牌

9. if __name__ == '__main__':

这是Python中的一个常用模式,用于判断当前脚本是否作为主程序运行。如果是,则执行gameNiuNiu()函数启动游戏。

if __name__ == '__main__':
    gameNiuNiu()  # 运行游戏

这个脚本通过模拟发牌、计算点数和比较牌的大小来模拟牛牛游戏的过程。用户可以输入开始发牌的位置,然后程序会根据这个位置洗牌、发牌,并输出游戏结果。

测试用例

import unittest
from unittest.mock import patch
from itertools import combinations
import random

# 引入游戏模块的代码

class TestGameNiuNiu(unittest.TestCase):
    def setUp(self):
        # 在每个测试用例之前设置测试环境
        self.deck = generate_deck()
        self.shuffle_pos = 27  # 假设用户输入的洗牌起始位置为27
        self.cards = shuffle_cards(self.deck, self.shuffle_pos)
        self.player, self.sysNPC = deal_cards(self.cards)

    def test_generate_deck(self):
        # 测试 generate_deck 函数
        deck = generate_deck()
        self.assertEqual(len(deck), 52)
        for card in deck:
            self.assertIn(card[0], '♠♥♣♦')
            self.assertIn(card[1], 'A23456789TJQK')

    def test_shuffle_cards(self):
        # 测试 shuffle_cards 函数
        original_deck = self.deck.copy()
        shuffled_deck = shuffle_cards(original_deck, self.shuffle_pos)
        self.assertNotEqual(shuffled_deck, original_deck)
        self.assertEqual(len(shuffled_deck), 52)

    def test_get_shuffle_position(self):
        # 测试 get_shuffle_position 函数
        with patch('builtins.input', return_value='27'):
            self.assertEqual(get_shuffle_position(), 27)
        with patch('builtins.input', side_effect=(ValueError, 'a', '53', '27')):
            self.assertEqual(get_shuffle_position(), 27)

    def test_deal_cards(self):
        # 测试 deal_cards 函数
        player, sysNPC = deal_cards(self.cards)
        self.assertEqual(len(player), 5)
        self.assertEqual(len(sysNPC), 5)

    def test_get_point(self):
        # 测试 get_point 函数
        test_cases = [
            (['A', 'A', 'A', '5', 'K'], 0),
            (['A', 'A', '10', '5', 'K'], 0),
            (['2', '3', '4', '5', '6'], 1),
            (['9', '10', 'J', 'Q', 'K'], 10),
            (['K', 'Q', 'J', '10', 'A'], 1)
        ]
        for hand, expected in test_cases:
            self.assertEqual(get_point(hand), expected)

    def test_compare_points(self):
        # 测试 compare_points 函数
        test_cases = [
            ([], [], 0, False),  # 无效输入
            (['A', 'A', 'A', '5', 'K'], ['A', '2', '3', '4', '5'], 0, True),  # 玩家赢
            (['A', 'A', '10', '5', 'K'], ['A', 'A', 'A', 'K', 'J'], 1, False),  # 系统赢
            (['A', 'A', 'A', '5', 'K'], ['A', 'A', 'A', '5', 'K'], 0, False)  # 点数相同
        ]
        for player_hand, sys_hand, expected_score, expected_win in test_cases:
            player_point = get_point(player_hand)
            sys_point = get_point(sys_hand)
            win, score, _, _ = compare_points(player_hand, sys_hand)
            self.assertEqual(win, expected_win)
            self.assertEqual(score, expected_score)

    def test_compare_hands(self):
        # 测试 compare_hands 函数
        test_cases = [
            (['10', 'J', 'A'], ['A', '2', '3'], True, 'A'),  # 玩家最大牌A大于系统2
            (['10', 'J', 'A'], ['A', 'K', 'Q'], False, 'A'),  # 玩家最大牌A小于系统A
            (['10', 'J', 'A'], ['A', 'A', 'A'], True, 'A')  # 玩家和系统最大牌相同,但玩家有J
        ]
        for hand1, hand2, expected_win, expected_max_card in test_cases:
            win, max_card = compare_hands(hand1, hand2)
            self.assertEqual(win, expected_win)
            self.assertEqual(max_card, expected_max_card)

if __name__ == '__main__':
    unittest.main()

更多问题,可咨询

Cos机器人

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值