24点游戏(C++)
新知识:
一.题目
24点游戏是经典的纸牌益智游戏。
常见游戏规则:
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。
基本要求: 随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式,用擅长的语言(C/C++/Java或其他均可)实现程序解决问题。
1.程序风格良好(使用自定义注释模板)
2.列出表达式无重复。
作业提示:
用穷举法列出四个数加上三个运算符号所构成的表达式所有可能的结果,或 实现一个简单的计算器判断用户的输入是否正确(用栈来实现)。
**二.分析
思路:
先把多元运算转化为两元运算,从四个数中取出两个数进行运算,然后把运算结果和第三个数进行运算,再把结果与第四个数进行运算。
优点:
避免了对括号的处理【括号的作用只是改变运算符的优先级】,四种运算符 “+,-,*,/”都为二元运算符,所以操作数都为2 。
主要步骤:
1. 将随机生成的4个整数放入数组中;
2 在数组中取两个数字的排列,共有 种排列;
3. 对每一个排列, 进行 – * / 运算;
4. 根据排列的两个数字和运算符,计算结果;
5. 改表数组:将此排列的两个数字从数组中去除掉,将 4中计算的结果放入数组中
对新的数组,重复4;
6.恢复数组:将此排列的两个数字加入数组中,将 4中计算的结果从数组中去除掉。
这是一个递归过程。 4 为递归函数。
当数组中只剩下一个数字的时候,这就是表达式的最终结果,此时递归结束。
注意:递归的现场保护和恢复,也就是递归调用之前与之后,现场状态应该保持一致。
三.流程图
四. 源代码
//纸牌益智游戏:24点游戏
//从扑克中每次取出4张牌,使用加减乘除,第一个能得出24者为赢
#include <iostream>//随机函数用于产生伪随机数,iostream头文件中有其函数strand()函数的定义,不需要引入stdlib.h,若用C,则需要引入
#include<time.h> //用于获取当前的系统时间,也用于随机数的生成,用日历时间作为种子
#define random(x) (rand()%x) //函数rand()生成一个0-x的随机数
#include <string>
#include <math.h>
using namespace std;
//使用const使COUNT,VALUE,LING的值不能被改变
const int COUNT = 4;
const int VALUE = 24;
const double LING = 1E-6;//1乘10的-6次幂,即0.000001
int num[COUNT]; //存放四个随机数的数组
string expression[COUNT]; //存放表达式的字符串数组
bool judge = false; //判断随机生成的4个数是否有解。
int count = 0;
void sum_24(int n)
{
if (n==1)
{
//求绝对值函数,在其头文件<math.h>中
if(fabs(num[0]-VALUE)<=LING)
{
cout << expression[0] << "\t\t";
judge = true;
count ++;
if((count % 3)==0) //使输出时每行三个表达式
cout<<endl;
}
else
{ }
}
for(int i=0;i<n;i++)//查找
{
for (int j=i+1;j<n;j++)//与其后面的查找进行计算
{
double a,b;
string expressiona, expressionb;
a=num[i];
b=num[j];
num[j]=num[n-1];
expressiona=expression[i];
expressionb=expression[j];
expression[j]=expression[n-1];
expression[i]= '('+expressiona+'+'+expressionb+')';
num[i]=a+b;
sum_24(n-1);
expression[i]='('+expressiona+'-' +expressionb+')';
num[i] =a-b;
sum_24(n-1);
expression[i] = '('+expressionb + '-' + expressiona + ')';
num[i]=b-a;
sum_24(n-1);
expression[i]= '('+ expressiona +'*'+ expressionb+ ')';
num[i]=a*b;
sum_24(n-1);
if (b!= 0)
{
expression[i] ='('+expressiona+'/' + expressionb + ')';
num[i] =a/b;
sum_24(n-1);
}
if (a != 0)
{
expression[i]='('+expressionb + '/'+ expressiona + ')';
num[i] = b / a;
sum_24(n-1);
}
num[i] =a;
num[j]=b;
expression[i] = expressiona;
expression[j] = expressionb;
}
}
}
int main()
{
int button;
cout<<"---------------------------------"<<endl;
cout<<" 是否开始游戏 "<<endl;
cout<<" 请选择按钮(1:开始,2:退出): "<<endl;
cin>>button;
if(button!=1)
cout<<"结束!"<<endl;
else
{
int ran_num[4];//存放4个随机数的数组
char ch[4]; //用来存放字符A,J,Q,K
//time()函数返回当前日历时间的秒数,返回值类型为time_t,函数原型为time_t time(time_t *)
//(int)time(0)前的(int)是把返回值类型强制转化为整型
//编译器对0和NULL作了隐式转换,所以time(0)等价于time(NULL)
srand((int)time(0)); //strand()函数是随机数发生器的初始化函数
cout<<"生成的四个随机数分别为:";
for(int x=0;x<4;x++)
{
ran_num[x]=random(14);
if(ran_num[x]==11)
{
ch[x]='J'; //'J'代表11
cout<<ch[x]<<"(11)"<<" ";
}
else if(ran_num[x]==12)
{
ch[x]='Q'; //'Q'代表12
cout<<ch[x]<<"12"<<" ";
}
else if(ran_num[x]==13)
{
ch[x]='K'; //'K'代表13
cout<<ch[x]<<"13"<<" ";
}
else if(ran_num[x]==1)
{
ch[x]='A'; //'A'代表1
cout<<ch[x]<<"(13)"" ";
}
else
{
cout<<ran_num[x]<<" ";
}
}
cout<<endl;
cout<<"请根据提示,依次输出输入随机生成的四个数:\n";
for (int i = 0; i < COUNT; i++)
{
char cha[20];
cout<<"第"<<i+1<<"个数:";
cin>>num[i];
itoa(num[i],cha, 10); //itoa()函数的作用是把第一个参数(数值)传送(转换)到第二个参数(字符串)中去,第三个参数(int型)是该数值在字符串里以什么进制存放。
expression[i]=cha;
}
cout<<endl;
sum_24(COUNT) ;
if(judge==true)
{
cout<<endl<<"成功!"<<endl;
cout<<"生成24的计算方法共有:"<<count<<endl;
}
else
{
cout << "随机生成的四个数不能组成24!" << endl;
}
}
return 0;
}
五.个人总结**
总的来收,我觉得这次的作业算是开学以来最有难度的一次作业了。但是,我们也收获的很多。24点游戏,我一共分了四个部分:(1)利用随即函数随机生成4个数;(2)把1换成A,11换成J,12换成Q,13换成K;(3)输出随机数能组成24的表达式;(4)设置页面。
随机机函数对我来说也算是一个新的小知识了,以前不怎么用,这次把它给搞清楚了。首先随机函数用于产生伪随机数,strand()函数是随机数发生器的初始化函数,iostream头文件中有其函数strand()函数的定义,所以不需要引入<stdlib.h>,若用C,则需要引入<stdlib.h>,其次<time.h>在随机数中用于随机数的生成,返回值类型为time_t,函数原型为time_t time(time_t *)用日历时间作为种子,最后,random(x) (rand()%x)中,函数rand()生成一个0-x的随机数。
接下来,就是本程序的核心部分,也就是最难的部分,列出所有能够得出24的表达式。其实我开始自己写的时候都是特别懵的,实在是想不出来。后来,在网上看了看,不外乎就是两种方法,第一种为穷举法,第二种就是先算两个数,结果再和第三数做运算,最后再和第四个数做运算。我觉得对我来说第二种更好一点,所以我用了第二种方法。这种方法递归调用,优点就是好理解,而且还避免了括号对优先级的影响。
最后,我认为,这次的作业还是挺好的,只要学到知识就是值得的,所以,不断学习吧!