python24点游戏

使用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`。

更多问题,可咨询

Cosplay机器人

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值