题目描述
24点游戏是经典的纸牌益智游戏,其游戏规则如下:
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按要求编程解决24点游戏。
基本要求: 随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式
1.列出的表达式无重复
提高要求:用户初始生命值为一给定值(比如3),初始分数为0。随机生成4个代表扑克牌牌面的数字或字母,由用户输入包含这4个数字或字母的运算表达式(可包含括号),如果表达式计算结果为24则代表用户赢了此局。
1.使用计时器要求用户在规定时间内输入表达式,如果规定时间内运算正确则加分,超时或运算错误则进入下一题并减少生命值(不扣分)。
分析
自己在源程序的基础上建立了类的概念,在Card里设置了几个方法,因为这次的问题自己没太怎么懂,所以核心代码是在网上找的,在这里把原博主的分析过程拿过来,以便日后理解消化......
原博主博客:https://puffsun.iteye.com/blog/587136
因为能使用的4种运算符 + - * / 都是2元运算符,所以本文中只考虑2元运算符。2元运算符接收两个参数,输出计算结果,输出的结果参与后续的计算。
由上所述,构造所有可能的表达式的算法如下:
(1) 将4个整数放入数组中
(2) 在数组中取两个数字的排列,共有 P(4,2) 种排列。对每一个排列,
(2.1) 对 + - * / 每一个运算符,
(2.1.1) 根据此排列的两个数字和运算符,计算结果
(2.1.2) 改表数组:将此排列的两个数字从数组中去除掉,将 2.1.1 计算的结果放入数组中
(2.1.3) 对新的数组,重复步骤 2
(2.1.4) 恢复数组:将此排列的两个数字加入数组中,将 2.1.1 计算的结果从数组中去除掉可见这是一个递归过程。步骤 2 就是递归函数。当数组中只剩下一个数字的时候,这就是表达式的最终结果,此时递归结束。
在程序中,一定要注意递归的现场保护和恢复,也就是递归调用之前与之后,现场状态应该保持一致。在上述算法中,递归现场就是指数组,2.1.2 改变数组以进行下一层递归调用,2.1.3 则恢复数组,以确保当前递归调用获得下一个正确的排列。
括号 () 的作用只是改变运算符的优先级,也就是运算符的计算顺序。所以在以上算法中,无需考虑括号。括号只是在输出时需加以考虑。
附:程序源代码
/****************************************************************************
*文件名: 24PointGame.cpp
*内容简述:从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中J代表11,Q代表12,K代表13,A代表1),编程解决24点游戏。基本要求: 随机生成4个代表扑克牌牌面的数字字母,
程序自动列出所有可能算出24的表达式,要求列出的表达式无重复
*2019-04-12董怡馨创建该文件,增加文件头注释
*/
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include <math.h>//用到了求绝对值函数
#include<time.h>//利用时间戳保证每次运行产生的随机数都不同
using namespace std;
#define NUM 4
#define ZERO 1E-6
class Card{
private:
int i;
int j;
bool m_judge = false; //判断是否有解
int number[NUM]; //存放用户输入的4个随机数
char expression[NUM][20] ; //表达式
int cnt;//计数器,统计满足24点规则的表达式总数
public:
Card()
{
cnt=0;
}
void InitNumber()//初始化要输入的四个数
{
srand((unsigned int)time(NULL));//随机数种子
printf("随机产生的四个数: ");
for( i =0;i<NUM;i++)
{
char ch[20], s;
int a ;
cout<<"第"<<i+1<<"个数:";
number[i]=(rand()%10+1);//在1到10之间产生随机数
cout<<number[i]<<"\t";
a = number[i];
s = char(a + 48);
ch[0] = s ;
for (j = 1; j < 20; j++)
{
ch[j] = 0 ;
}
strcpy(expression[i], ch) ;
}
printf("\n") ;
}
void FindExpression(int num)
{
int i = 0, j = 0;
if (num == 1)//递归结束,输出
{
if ( fabs(number[0] - 24) <= ZERO)
{
for (i = 0; i < 20; i++)
{
printf("%c", expression[0][i]) ;
}
printf("\n") ;
m_judge = true;
cnt ++;
}
}
//递归查找
for(i = 0; i < num; i++)
{
for (j = i + 1; j < num; j++)//与其后面的查找进行计算
{
double a, b;
char expressiona[20], expressionb[20];
a = number[i];
b = number[j];
//数组前移删除number[j]元素,用number[num-1]补上
number[j] = number[num - 1];
strcpy(expressiona, expression[i]) ;
strcpy(expressionb, expression[j]) ;
//数组前移删除expression[j]元素,用expression[num-1]补上
strcpy(expression[j], expression[num - 1]) ;
sprintf(expression[i], "(%s+%s)", expressiona, expressionb) ;
//
number[i] = a + b;
FindExpression(num-1);
//减法
sprintf(expression[i], "(%s-%s)", expressiona, expressionb) ;
number[i] = a - b;
FindExpression(num-1);
//乘法
sprintf(expression[i], "(%s*%s)", expressiona, expressionb) ;
number[i] = a * b;
FindExpression(num-1);
//除法
if (b != 0)
{
sprintf(expression[i], "(%s/%s)", expressiona, expressionb) ;
number[i] = a / b;
FindExpression(num-1);
}
//恢复数组
number[i] = a; // 最后一层的Find(2)调用完成后将a重新赋值给number[i]即number[0]
number[j] = b; // 最后一层的Find(2)调用完成后将b重新赋值给nubmer[j]即number[1]
strcpy(expression[i], expressiona) ;
strcpy(expression[j], expressionb) ;
}
}
}
};
int main(void)
{
//创建一个纸牌类对象
Card card;
//初始化四个随机数,代表四张卡片
card.InitNumber();
//查找表达式
card.FindExpression(NUM);
}