使用python来玩24点游戏
游戏规则
从一副去掉大王、小王、J、Q、K的扑克牌中随机抽取四张牌
判断这四张牌能否通过四则运算(+-*/)得到24
若能,输入具体的四则运算表达式,若不能则输入'f'
游戏代码
import random
def gameCal24():
print('-'*15,' 24点游戏 ','-'*15)
print('\t从一副去掉大王、小王、J、Q、K的扑克牌中随机抽取四张牌')
print('\t判断这四张牌能否通过四则运算(+-*/)得到24,')
print('\t若能,输入表达式,若不能则输入f,')
# 生成一个极小数用于解决python中浮点数的精度问题,即两个浮点数的差小于这个极小数则认为相等
# 例如:特殊情况[3,8,3,8] -> 8/(3-8/3) = 24 ,但是python中计算结果为23.999999999999996
EPSILON = 1e-6
while True:
print('-'*40)
# 生成4个随机数
nums = [random.randint(1, 10) for i in range(4)]
# 生成4个随机运算符
print('\t四个随机数为:', ' '.join([str(i) for i in nums]))
expersion = input('\t请输入四个数的运算表达式(若不能组成24则输入f):')
# 判断给定的数组能否通过四组运算拼出24
try:
if expersion == 'f' and not judgePoint24(nums):
print('\t给定数组确实不能组成24')
continue
elif expersion == 'f' and judgePoint24(nums):
print('\t给定数组可以组成24,回答错误')
continue
# 比较表达式中所用的数字和给定的数组是否一一匹配
ans_nums =[int(i) for i in re.findall(r'\d+', expersion)]
nums.sort()
ans_nums.sort()
# 计算表达式的结果
result = eval(expersion)
# 判断表达式中的数字与给定的数字是否一一匹配
if nums != ans_nums:
print('\t表达式中的数字与给定的数字不匹配')
continue
# 判断计算结果是否为24
if abs(result - 24) < EPSILON:
print('\t解答正确')
else:
print('\t计算错误')
except:
print('\t表达式错误')
def judgePoint24(nums):
TARGET = 24
EPSILON = 1e-6
cal = {0:lambda x,y:x+y, 1:lambda x,y:x*y, 2:lambda x,y:x-y, 3:lambda x,y:x/y if abs(y) > EPSILON else None}
def solve(nums):
if not nums:
return False
if len(nums) == 1:
return abs(nums[0] - TARGET) < EPSILON
for i, x in enumerate(nums):
for j, y in enumerate(nums):
if i != j:
# 构造新的数字列表,排除已选的x和y
newNums = [nums[k] for k in range(len(nums)) if k != i and k != j]
for k in range(4):
# 避免在两个操作数相同的情况下重复计算
if k < 2 and i > j:
continue
result = cal[k](x, y)
if result is not None: # 检查返回结果是否有效
newNums.append(result)
if solve(newNums):
return True,newNums
newNums.pop()
return False
return solve(nums)
测试用例
import unittest
import random
from re import findall
# 这里我们定义了一个辅助函数,用于模拟用户的输入
def mock_input(*args):
return args[0]
# 测试用例类
class TestGameCal24(unittest.TestCase):
def test_correct_expression(self):
nums = [4, 1, 6, 9]
with self.subTest(msg="Test with correct expression"):
self预期输出 = "解答正确"
self.assertEqual(gameCal24_output(nums, "(6-(4-1*9))"), self预期输出)
def test_cannot_form_24(self):
nums = [1, 2, 3, 4]
with self.subTest(msg="Test with expression that cannot form 24"):
self预期输出 = "给定数组确实不能组成24"
self.assertEqual(gameCal24_output(nums, "1+2+3+4"), self预期输出)
def test_invalid_expression(self):
nums = [5, 7, 8, 3]
with self.subTest(msg="Test with invalid expression"):
self预期输出 = "表达式错误"
self.assertEqual(gameCal24_output(nums, "(5+7)/8)3"), self预期输出)
def test_input_f(self):
nums = [1, 3, 7, 9]
with self.subTest(msg="Test input 'f' to check if can form 24"):
self预期输出 = "给定数组可以组成24,回答错误"
self.assertEqual(gameCal24_output(nums, "f"), self预期输出)
def test_mismatch_numbers(self):
nums = [2, 3, 7, 9]
with self.subTest(msg="Test with mismatch numbers in expression"):
self预期输出 = "表达式中的数字与给定的数字不匹配"
self.assertEqual(gameCal24_output(nums, "(9-(7-3*2))"), self预期输出)
def test_float_expression(self):
nums = [1, 2, 3, 4]
with self.subTest(msg="Test with float numbers in expression"):
result = gameCal24_output(nums, "(1+2.0)*3-4")
if abs(eval("(1+2.0)*3-4") - 24) < 1e-6:
self预期输出 = "解答正确"
else:
self预期输出 = "计算错误"
self.assertEqual(result, self预期输出)
# 辅助函数,用于模拟gameCal24的输入过程
def gameCal24_output(nums, expression):
random.seed(0) # 固定随机数种子,确保每次测试的随机数相同
mock_input_calls = [expression]
with mock.patch('builtins.input', side_effect=mock_input_calls):
gameCal24()
return gameCal24_output Captured Output
# 运行测试
if __name__ == '__main__':
unittest.main()
代码详解
1. `gameCal24` 函数-游戏的主函数,负责游戏的流程控制
- 首先,打印游戏的标题和简介,告知玩家游戏的规则。
- 然后,进入一个无限循环,每次循环尝试生成一组随机数,并要求玩家输入一个表达式。
- 在每次循环的开始,打印一个分隔符,增加可读性。
- 使用 `random.randint(1, 10)` 生成四个1到10之间的随机整数,作为游戏的输入数字。
- 打印这四个随机生成的数字。
- 调用 `input` 函数,提示玩家输入一个表达式,如果玩家认为无法通过四则运算得到24,则输入 'f'。
- 使用 `try...except` 块来捕获可能发生的异常,如输入的表达式无法正确求值等。
- 如果玩家输入的是 'f',则调用 `judgePoint24` 函数判断当前的数字是否真的无法组成24。如果判断结果为真,提示玩家继续;如果判断结果为假,指出玩家的错误。
- 如果玩家输入的是一个表达式,则首先使用正则表达式 `re.findall` 提取表达式中的所有数字,并与原始数字列表进行排序和比较,确保玩家没有改变数字的顺序或添加额外的数字。
- 使用内置函数 `eval` 计算表达式的值。
- 判断计算结果与24的差的绝对值是否小于预设的极小数 `EPSILON`,如果是,则认为玩家解答正确;否则,提示计算错误。
- 如果输入的表达式有误(例如语法错误),则捕获异常并提示玩家表达式错误。
2. `judgePoint24` 函数-判断一组数字是否能够通过四则运算得到24。
- 首先,定义目标值 `TARGET` 为24和极小数 `EPSILON`,用于判断结果是否足够接近24。
- 定义一个字典 `cal`,它包含了四种运算的函数式,注意除法运算检查除数是否为0,以避免除以0的错误。
- 定义一个内部函数 `solve`,它使用递归来尝试所有可能的运算组合。
- 如果 `nums` 列表为空,说明已经没有数字可以进行运算,返回 `False`。
- 如果 `nums` 列表只有一个元素,检查这个数字与目标值的差的绝对值是否小于 `EPSILON`,如果是,则返回 `True`。
- 遍历 `nums` 列表中的每个数字,两两组合,并尝试进行四种运算。
- 为了避免重复计算,当 `k < 2` 且 `i > j` 时跳过当前循环。
- 对于每次运算,如果结果有效(即 `cal[k](x, y)` 不是 `None`),将其加入到新的数字列表 `newNums` 中,并递归调用 `solve` 函数。
- 如果递归调用返回 `True`,则当前组合可以得出24,返回 `True`;否则,移除该结果并继续尝试其他组合。
- 如果所有可能的组合都尝试过且都无法得到24,则最终返回 `False`。