导读:
demo内容来源网课贪心科技AI学社的网课《人工智能Python编程特训营》
本篇博客的主要内容是写一个21点(blackjack)的python程序。
作业
构建《豪赌21点》游戏,完成开发。
先说一下这次demo完成的情况,可以支持玩家和一名电脑进行PK。电脑提供两种要牌模式,一种是点数比玩家小就要牌,一种是设定一个阈值,小于阈值就要牌。
缺点:没有可视化、不是用面向对象写的代码的可扩展性比较差难以拓展到多个玩家
21点游戏规则与流程
- 玩家共两个角色:电脑和人类,电脑是庄家
- 游戏开始时,先给人类和电脑每个玩家分别发两张牌作为底牌,庄家底牌只漏一张
- 判断双方底牌是否直接为21点,如果其中一方为21点则直接判胜利,并在总分上加一分。如果双方都是21点,那就是平局,不得分。
- 当初始牌面上,没有直接出现21点,人类玩家根据自己的牌面大小决定是否继续要牌。如果要牌,那就在牌堆中抽一张,再次询问是否要牌。
如果人类牌面的总点数超过了21点,那就直接判输 - 如果人类玩家停止要牌了,并且没有因为超过21点而被判输的情况下,则电脑要牌。电脑要牌这里,可以自己设计一个规则:
5.1 比如电脑一直要牌,直到比人类玩家大才停止要牌。
5.2 根据牌堆中剩余牌的数量,计算赢的概率,然后设置一个阈值,超过阈值就要,低于就不要。 - 电脑要完牌之后,直接判断人类玩家和电脑玩家的牌面大小判断胜负。
- 完成一轮游戏的时候,可由人类玩家决定,是否继续玩下一轮
- 牌堆中剩余的牌数不够玩一轮游戏的时候,游戏自动结束。
- 计算规则: 2、3、4、5、6、7、8、9、10分别是正常的点数,J、Q、K都是10点
- A比较特殊,首先把A当做1来计算,牌面总分数如果小于21,那么再把A当做11再计算一次,如果这个时候仍然小于21,
那么A就当11算,如果这个时候牌面总分数大于了21,那么A就当1算。
代码部分
我们首先得理清我们的程序需要哪一些函数
"""
程序的功能模块:
洗牌:将排进行随机排列 random_card
发牌:1.初始化时发牌,一下发两张 start_game_init_two_poker
2.要牌,一下发一张 get_one_poker
计分:2-10就是正常点数,JQK都是10,要考虑A的特殊性 score_count
胜负判断:比较电脑和玩家手中的分数谁大,并记录 who_win
是否要牌 if_get_next_poker
继续还是退出 continue_or_quit
每一轮 every_round
"""
接着引用相关的包及定义一些需要用到的变量
# 洗牌函数 shuffle 作用是随机打乱列表
from random import shuffle
import random
# 为什么要用numpy ,原因是numpy数组提供对应位置相加,不用我们自己计算
# 用于多轮游戏的分数统计
import numpy as np
# exit 函数就是退出程序
from sys import exit
# 初始化扑克牌
playing_cards = {
"♠ A": 1, "♠ 2": 2, "♠ 3": 3, "♠ 4": 4, "♠ 5": 5, "♠ 6": 6, "♠ 7": 7,
"♠ 8": 8, "♠ 9": 9, "♠10": 10, "♠ J": 10, "♠ Q": 10, "♠ K": 10,
"♥ A": 1, "♥ 2": 2, "♥ 3": 3, "♥ 4": 4, "♥ 5": 5, "♥ 6": 6, "♥ 7": 7,
"♥ 8": 8, "♥ 9": 9, "♥ 10": 10, "♥ J": 10, "♥ Q": 10, "♥ K": 10,
"♦ A": 1, "♦ 2": 2, "♦ 3": 3, "♦ 4": 4, "♦ 5": 5, "♦ 6": 6, "♦ 7": 7,
"♦ 8": 8, "♦ 9": 9, "♦ 10": 10, "♦ J": 10, "♦ Q": 10, "♦ K": 10,
"♣ A": 1, "♣ 2": 2, "♣ 3": 3, "♣ 4": 4, "♣ 5": 5, "♣ 6": 6, "♣ 7": 7,
"♣ 8": 8, "♣ 9": 9, "♣ 10": 10, "♣ J": 10, "♣ Q": 10, "♣ K": 10,
}
poker_name = list(playing_cards.keys()) # 扑克牌名
poker_count = 1 # 几副扑克
poker_list = poker_name * poker_count # 参与游戏的所有扑克牌
# 用于判断手中的牌是否有A,根据分数来进行选择A的分值是0还是1
four_a = {"♠ A", "♥ A", "♦ A", "♣ A"}
# 计分器, 玩家:电脑 初始的分数都是0
total_score = np.array([0, 0])
# 记录游戏是第几个回合
game_round = 1
洗牌函数:只需要调用shuffle函数即可
"""
洗牌:重新对扑克牌进行随机排列
不需要return,因为传入的值是引用
"""
def random_card(poker_name_temp):
shuffle(poker_name_temp)
计分函数:计算手里牌的分数
"""
计分:计算手里牌的分数
传进来的参数是一个列表
"""
def score_count(hand_poker):
# 声明一个变量,这个变量用来记录牌的总分数
poker_score = 0
# 标记 是否有A的标记,默认没有
have_a = False
# 计算手中牌的分数
for k in hand_poker:
poker_score += playing_cards[k]
# 判断手中的牌是否有A,然后再根据A的规则进行分数计算
# 有A就结束循环,没有A就继续循环直到遍历完成
for i in hand_poker:
if i in four_a:
have_a = True
break
else:
continue
if have_a: # 如果有A,且总分不超过21,则A记为11
if poker_score + 10 <= 21:
poker_score = poker_score + 10
return poker_score
判断输赢函数:
"""
判断输赢的函数
"""
def who_win(your_score, pc_score):
if your_score > 21 and pc_score > 21:
print("平局了")
return np.array([1, 1])
elif your_score > 21 and pc_score <= 21:
print("对不起,玩家输了")
return np.array([0, 1])
elif your_score <= 21 and pc_score > 21:
print("恭喜,玩家赢了")
return np.array([1, 0])
elif your_score <= 21 and pc_score <= 21:
if your_score < pc_score:
print("对不起,玩家输了")
return np.array([0, 1])
elif your_score > pc_score:
print("恭喜你,玩家赢了")
return np.array([1, 0])
else:
print("平局了")
return np.array([1, 1])
决定是否继续要牌:
"""
是否继续要牌
"""
def if_get_next_poker():
if_continue = input("是否继续要下一张牌?(Y/N)>>>>>:")
if if_continue.upper() == "Y":
return get_one_poker() # 返回一张牌
elif if_continue.upper() == "N":
print("玩家停止叫牌")
return False # 返回False
else:
print("输入错误,请重新输入")
return if_get_next_poker() # 递归调用,也可以用while True
发牌函数:包括发一张牌和初始化
"""
发牌:要牌的时候需要从牌堆随机抽取一张牌
"""
def get_one_poker():
# 发一张牌,必须要在牌堆里面删掉这张牌 pop 删除并且返回
return poker_list.pop(random.randint(0, len(poker_list) - 1))
"""
开局初始化牌,自动给电脑和玩家发两张牌
"""
def start_game_init_two_poker(poker_database):
return [poker_database.pop(random.randint(0, len(poker_database) - 1)),
poker_database.pop(random.randint(0, len(poker_database) - 1))]
继续游戏或结束游戏:
"""
一轮游戏结束后,询问玩家是继续还是结束
"""
def continue_or_quit():
if_next_round = input("你还想要玩下一局吗?(Y/N)>>>>>:")
if if_next_round.upper() == "Y":
if len(poker_list) < 15:
print("剩余扑克牌太少了,不能玩了,游戏结束")
exit(1)
else:
return True
elif if_next_round.upper() == "N":
print("游戏结束,玩家不玩了")
exit(1)
else:
print("输入错误,请再输入一次")
continue_or_quit()
每一轮游戏函数:在注释部分提供了两种电脑叫牌的规则,一种感受比玩家小就叫牌,一种是取某个阈值,小于阈值就叫牌
"""
每一次游戏的流程
"""
def every_round(poker_list):
# 声明一个变量,代表我们手里的变量
your_hand_poker = []
# 声明一个变量,代表电脑手里的变量
computer_hand_poker = []
# 一个回合的游戏,首先要自动的在牌堆里抽取两张牌
your_init_poker = start_game_init_two_poker(poker_list)
computer_init_poker = start_game_init_two_poker(poker_list)
print("玩家所获得的扑克牌是{}和{}".format(your_init_poker[0], your_init_poker[1]))
print(f"电脑所获得的的扑克牌是{computer_init_poker[0]},?\n")
# 荷官同志把牌放到我们手里
# extend是添加一个序列,append是添加一个对象
your_hand_poker.extend(your_init_poker)
computer_hand_poker.extend(computer_init_poker)
# 计算初始牌面的分数
score = np.array([score_count(your_hand_poker), score_count(computer_hand_poker)])
# 首先判断一下初始化牌面分数的大小,如果有等于21 ,那么直接调用判断输赢函数
if score[0] == 21 or score[1] == 21:
print("初始化牌面分数有21点")
return who_win(score[0], score[1])
# 否则,判断自己手里的分数是否小于21点,如果小于21点,那么询问是否要下一张牌
else:
while score[0] <= 21:
get_new_poker = if_get_next_poker()
if get_new_poker:
# 把新的扑克牌放到我的手里
your_hand_poker.append(get_new_poker)
print("你手里的扑克牌是:{}".format(your_hand_poker))
score[0] = score_count(your_hand_poker)
# 判断分数大小
if score[0] > 21:
print("你手里的扑克牌已经超过了21点")
print("电脑手里的扑克牌是:{}".format(computer_hand_poker))
return who_win(score[0], score[1])
else:
continue
# 玩家不要牌了,电脑开始叫牌
elif not get_new_poker:
# # 电脑叫牌的规则1:只要分数比玩家低就一直叫,直到大于等于玩家的分数才停止
# while score[1] < score[0]:
# computer_poker = get_one_poker()
# computer_hand_poker.append(computer_poker)
# pc_score = score_count(computer_hand_poker)
# score[1] = pc_score
# 电脑叫牌的规则2:只要分数低于阈值18就继续叫牌
threshold = 18
while score[1] < threshold:
computer_poker = get_one_poker()
computer_hand_poker.append(computer_poker)
pc_score = score_count(computer_hand_poker)
score[1] = pc_score
print("电脑手里的扑克牌是:{}".format(computer_hand_poker))
return who_win(score[0], score[1])
else:
continue
开始游戏:先选择扑克牌数,然后进入游戏
# 开始玩游戏
input("游戏开始,祝你好运。按'回车'游戏正式开始 ")
while True:
poker_count = input("请输入要几副牌?(1-6)>>>>>")
if poker_count.isdigit() and 1 <= int(poker_count) <= 6:
poker_list = poker_name * int(poker_count)
break
else:
print("输入错误,请重新输入!")
while True:
print("游戏是第{}轮".format(game_round))
# 洗牌
random_card(poker_list)
# 开始游戏
every_score = every_round(poker_list)
# 计算总分数
total_score = np.add(total_score, every_score)
print("本轮结束,总比分是玩家{}:电脑{}".format(total_score[0], total_score[1]))
# 轮数+1
game_round += 1
# 判断继续或退出
continue_or_quit()