24点游戏 python_Python破解24点游戏,从此打扑克就没输过

文源网络,仅供学习之用,如有侵权请联系删除。

24点游戏

对于任意给定的四张扑克牌,计算是否有赢得24点游戏的方法(即使用加、减、乘、除四则运算凑成24的方法);如果有的话,列出所有可能的方法。

在大小王以外的52张牌中,任意抽取其中4张牌。

如果通过加、减、乘、除四则运算(可加括号)的方法,将抽到的4张牌算成24,则为胜利;每张牌都必须使用,且只能使用一次。

第一种解法

依据游戏规则,我们可以想到如下解决思路:使用枚举的方法,将所有的计算方法都枚举出来,将四张扑克牌的数字代入到所有的计算方法中得出结果,如果结果为24则为解。

由此,我们得到了第一种解法。

在具体实现中:将所有可能的四则运算组合和所有可能的括号组合合并在一起,由此生成所有可能的算式组合。计算某一个牌组时,先计算所有该牌组所有可能的组合方式,并将所有的组合方式带入所有可能的算式组合求解。

import itertools

class CardGaming:

def __init__(self):

self.formula_list = list() # 存储所有可能的算式

for marks in itertools.product(["+", "-", "*", "/"], repeat=3):

for bracket in ["{0}%s{1}%s{2}%s{3}", "({0}%s{1})%s{2}%s{3}", "({0}%s{1}%s{2})%s{3}",

"{0}%s({1}%s{2})%s{3}",

"{0}%s({1}%s{2}%s{3})", "({0}%s{1})%s({2}%s{3})", "{0}%s{1}%s({2}%s{3})"]:

self.formula_list.append((bracket % marks))

def solve(self, card_probability):

answer = []

for card_order in set(itertools.permutations(card_probability, 4)): # 遍历所有可能的不同卡牌顺序(最多24种可能)

for formula in self.formula_list: # 遍历所有可能的算式(448种可能)

final_formula = formula.format(*card_order)

try:

if round(eval(final_formula), 3) == 24:

answer.append(final_formula)

except ZeroDivisionError:

continue

return answer

if __name__ == "__main__":

print(CardGaming().solve((3, 3, 8, 8))) # 输出: 8/(3-8/3)

当前代码在计算每一个牌组的答案时,都需要遍历4^3*7=448种算式和最多A44=24种卡牌顺序,即处理最多448*24=10752种可能性。使用这个解法计算所有可能的扑克牌组合(共计13^4=28561种解法),需要1906秒(I7 7700,8GB)。

第二种解法

在第一种解法中,计算每一个牌组的答案时,处理的可能性中有很多重复的情况,例如“A+B-C+D”、“D-C+B+A”、“D+A-C+B“等。这就极大地拖累了我们的运算速度。

但是,要在第一种解法的基础上来合并这些不同的情况,需要同时考虑符号、括号和卡牌顺序,十分复杂。

因此,我们可以从另外一个角度来解决这个问题。

通过观察我们可以发现,无论什么算式,本质上都是按着一定的顺序,对4张扑克牌的数值进行三次运算;而每一次运算,都是从尚未用过的扑克牌以及之前的运算结果中选择2个进行运算。所以,我们可以将所有算式归纳为:

从4张牌中任意抽取2个进行任意运算,将未抽取的2张牌和运算结果组合成包含3个数值的新列表;在新列表中任意抽取2个进行任意运算,将未抽取的1张牌和运算结果组成包含2个数值的新列表;对新列表中的2个数值进行任意运算得出结果,如果结果为24则为解。

由此,我们得到了第二种算法。在具体实现中,我们主要注意如下几点:

因为不再枚举算式,所以我们也不再需要使用低效的eval()函数运行算式。

因为如果在运算过程中生成算式,会增加很多运算量,所以我们只在求出解后反向生成解的算式(哪怕这样生成算式会更困难,但是需要生成的次数大大减少)。

def solve(card_probability):

card_probability = list(card_probability) # 生成临时列表

answer = []

for combine_1 in set(itertools.combinations(card_probability, 2)): # 在临时列表的4个数中任意抽取2个数

for answer_1 in all_maybe_count_answer(combine_1[0], combine_1[1]):

card_list_1 = copy.deepcopy(card_probability)

card_list_1.remove(combine_1[0]) # 从临时列表移除抽到的数1

card_list_1.remove(combine_1[1]) # 从临时列表移除抽到的数2

card_list_1.append(answer_1) # 添加计算结果到临时列表

for combine_2 in set(itertools.combinations(card_list_1, 2)): # 在临时列表的3个数中任意抽取2个数

for answer_2 in all_maybe_count_answer(combine_2[0], combine_2[1]):

card_list_2 = copy.deepcopy(card_list_1)

card_list_2.remove(combine_2[0]) # 从临时列表移除抽到的数1

card_list_2.remove(combine_2[1]) # 从临时列表移除抽到的数2

card_list_2.append(answer_2) # 添加计算结果到临时列表

for combine_3 in set(itertools.combinations(card_list_2, 2)): # 抽取临时列表剩下的2个数

for answer_3 in all_maybe_count_answer(combine_3[0], combine_3[1]):

if round(answer_3, 3) == 24:

answer.append(total_formula(card_probability, combine_1, answer_1, combine_2,

answer_2, combine_3, answer_3)) # 生成完整算式

return answer

if __name__ == "__main__":

start_time = time.time()

for cards in list(itertools.product([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], repeat=4)):

solve(cards)

print("计算时间:", time.time() - start_time)

其中all_maybe_count_answer函数计算两个参数进行四则运算的所有可能结果;total_formula函数依据中间变量生成完整计算公式)

运行结果:

计算时间: 136.27595901489258

这种接法在第一次四则运算时,有种C24=6抽取结果,有6种运算结果(减法和除法因顺序不同有2个结果);在第二次四则运算时,有种C23=3抽取结果,有6种运算结果;在第三次四则运算时,有C22=1种抽取结果,有6种运算结果。

因此,这种算法在求一个扑克牌组的解时,仅需要考虑C24*6*C23*6*C22*6=3888种可能性。使用这个解法计算所有可能的扑克牌组合,需要136秒(I7 7700,8GB),比第一种解法快了10倍以上。

在学习Python的道路上肯定会遇见困难,别慌,我这里有一套学习资料,包含40+本电子书,800+个教学视频,涉及Python基础、爬虫、框架、数据分析、机器学习等,不怕你学不会!

https://shimo.im/docs/JWCghr8prjCVCxxK/ 《Python学习资料》

关注公众号【Python圈子】,优质文章每日送达。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Python 21点小游戏,供参考: ```python import random # 定义扑克牌的点数和花色 card_values = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'] card_suits = ['♠', '♥', '♦', '♣'] # 创建一副扑克牌 deck = [(value, suit) for value in card_values for suit in card_suits] # 计算一张牌的点数 def card_value(card): if card[0] in ['J', 'Q', 'K']: return 10 elif card[0] == 'A': return 11 else: return int(card[0]) # 计算一手牌的点数 def hand_value(hand): value = sum(card_value(card) for card in hand) num_aces = len([card for card in hand if card[0] == 'A']) while value > 21 and num_aces: value -= 10 num_aces -= 1 return value # 发牌 def deal(deck, n): hand = [] for i in range(n): card = deck.pop() hand.append(card) return hand # 打印一手牌 def print_hand(hand): for card in hand: print(card[0] + card[1], end=' ') print() # 主游戏逻辑 def play_game(): # 初始化 deck = [(value, suit) for value in card_values for suit in card_suits] random.shuffle(deck) player_hand = deal(deck, 2) dealer_hand = deal(deck, 2) # 玩家回合 while True: print('你的牌是:') print_hand(player_hand) print('总点数是:', hand_value(player_hand)) if hand_value(player_hand) > 21: print('你爆了!') return False elif hand_value(player_hand) == 21: print('21点,你赢了!') return True else: choice = input('你要继续要牌吗?(y/n)') if choice.lower() == 'y': player_hand.append(deck.pop()) else: break # 庄家回合 while hand_value(dealer_hand) < 17: dealer_hand.append(deck.pop()) # 结束游戏 print('你的牌是:') print_hand(player_hand) print('总点数是:', hand_value(player_hand)) print('庄家的牌是:') print_hand(dealer_hand) print('总点数是:', hand_value(dealer_hand)) if hand_value(dealer_hand) > 21: print('庄家爆了,你赢了!') return True elif hand_value(dealer_hand) > hand_value(player_hand): print('庄家赢了!') return False elif hand_value(player_hand) > hand_value(dealer_hand): print('你赢了!') return True else: print('平局!') return False # 开始游戏 while True: print('欢迎来玩21点!') if play_game(): print('你赢了!') else: print('你输了!') choice = input('你要再来一局吗?(y/n)') if choice.lower() == 'n': break ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值