一.问题描述
24点游戏是经典的纸牌益智游戏。
常见游戏规则:
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。
随机生成4个代表扑克牌牌面的数字字母,由答题者答题,判断是否等于24,根据答题者的选择列出所有可能算出24的表达式。
二.算法设计思路
24点游戏主要就是用穷举法,列出四个数字加减乘除和括号所有出现的结果的。
定义一个游戏类。
通过调用srand()和rand()函数,随机显示出4张纸牌,这四张牌从1~K这13张牌中随机调出。
定义两个栈分别存入算数符号和数值。
定义函数比较运算符的优先级。
函数计算玩家的表达式,来判断答案是否为24,
定义函数用枚举法,要列出所有符合要求的表达式,
函数输出所有符合条件表达式。
三.代码实现
#include<iostream>
#include<ctime>
#include<string>
#include<stack>
#include<cmath>
using namespace std;
#define SIZE 20
#define MAXSIZE 40
const int count=4;//四个数字
const int answar=24;//最后的答案,定义为const
int life=3;//生命值
int score=0;//分数
//字符节点定义
struct node
{
char *base;
char *top;
int size;
}m;
//数值结点定义
struct list
{
float *base;
float *top;
int size;
}n;
class game
{
public:
double num[count]; //数值
string exp[count];//主要存A,J,Q,K
string ans[20];//答案
bool flag;
int shu,find;
void InitNum(list& S); //初始化数值栈
void InitSymbol(node& S); //初始化字符栈
void PushNum(list& S,float e); //数值入栈
void PushSymbol(node& S,char e); //字符入栈
float PopNum(list& S); //数值出栈
char PopSymbol(node& S); //字符出栈
int Prior(char c);//比较优先级
int Prior2(char c);
void card();//随机生成四个数字
float card(char* str); //计算表达式
void judge();//判断答案是否正确,调用card(char* str)函数
void Find(int n);
void out();//输出答案
};
void game::InitNum(list& S)
{
S.base=(float *)malloc(SIZE *sizeof(float)); //动态分配空间
if(!S.base)
{
cout<<"errer1"<<endl;
exit(1);
}
S.top=S.base;
S.size=20;
}
void game::InitSymbol(node& S)
{
S.base=(char *)malloc(SIZE *sizeof(char));
if(!S.base)
{
cout<<"errer2"<<endl;
exit(1);
}
S.top=S.base;
S.size=30;
}
void game::PushNum(list& S,float e)
{
*S.top++=e; //将节点的值存入数组中
}
void game::PushSymbol(node& S,char e)
{
*S.top++=e; //将下一个字符存储为当前
}
float game::PopNum(list& S)
{
if(S.base==S.top) cout<<"整数栈为空,不能输出";
float x= *(--S.top);
return x;
}
char game::PopSymbol(node& S)
{
if(S.base==S.top) cout<<"字符栈为空,不能输出";
char x= *(--S.top);
return x;
}
int game::Prior(char c)
{
int r;
switch(c)
{
case ';':r=0;break;
case '(':r=1;break;
case '*':
case '/':r=5;break;
case '+':
case '-':r=3;break;
case ')':r=8;break;
default:cout<<"errer prior1"<<endl;
}
return r;
}
int game::Prior2(char c)
{
int w;
switch(c)
{
//在此处W相当于权重
case ';':w=0;break;
case '(':w=8;break;
case '*':
case '/':w=4;break;
case '+':
case '-':w=2;break;
case ')':w=1;break;
default:cout<<"errer prior2"<<endl;
}
return w;
}
void game::card()
{
int i,k;
srand((unsigned)time(NULL)); //随机函数种子
for(i=0;i<count;i++)
{
char ch[20];
num[i]=rand()%13+1; //随机生成1~13其中一个数字
itoa(num[i],ch,10); //将这个十进制的整数转换为字符型
exp[i]=ch; //将字符存储在符号数组中
}
cout<<"游戏数字是:";
for(i=0;i<count;i++)
{
k=num[i];
switch(k)
{
case 1:
cout<<"A"<<" ";
break;
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
cout<<num[i]<<" ";
break;
case 11:
cout<<"J"<<" ";
break;
case 12:
cout<<"Q"<<" ";
break;
case 13:
cout<<"K"<<" ";
break;
}
}
cout<<endl;
}
float game::card(char* str)
{
int i=0;
float x; //存储计算结果
PushSymbol(m,';'); //初始化字符栈,中只有一个元素';',整数栈为空栈
while(str[i]&&i<SIZE-1) //处理中缀表达式循环 表达式没有到最后且数组没有越界
{
x=0;
if(str[i]==' ') //去掉无效字符
{
i++;
continue;
}
while(str[i]>=48&&str[i]<=57)
{
x=x*10+str[i]-48; //把字符的ASCILL码变成数字的
i++;
}
if(x!=0)
PushNum(n,x);
else
{
int a=Prior2(str[i]); //处理栈外字符
int b=Prior(*(m.top-1)); //处理栈内字符,成员变量是字符栈中的栈顶元素
if(a>b) //栈外运算符优先级高于栈内运算符优先级
{
PushSymbol(m,str[i]);i++;} //将其插入到字符栈中
else
switch(PopSymbol(m)) //优先级相反,括号里面的参数变量是字符栈内的首元素
{
case '+':
x=PopNum(n);
x+=PopNum(n); //从整数栈中抛出两个数值,进行加运算
PushNum(n,x);
break; //计算完之后结果重新入栈
case '-':
x=PopNum(n);
x-=PopNum(n); //从整数栈中抛出两个字符,进行减运算
PushNum(n,x); //计算之后结果存入栈中
break;
case '*':
x=PopNum(n);
x*=PopNum(n);
PushNum(n,x);
break;
case '/':
x=PopNum(n);
if(x!=0.0)
{
x=PopNum(n)/x;
PushNum(n,x);
}
else
{
cout<<"零不能做除数"<<endl;
i=SIZE-1;
}
break;
case '(':
i++;
break;
case ';':
i=SIZE-1;
break;
default:cout<<"输入错误!"<<endl;
}
}
}
x=PopNum(n); //将计算结果返回
return x;
}
void game::judge()
{
char str[32];
char a[SIZE];
InitSymbol(m);
InitNum(n);
cout<<"输入你的答案,并以;结束:";
cin>>str; //输入一个24点的表达式存放在数组中
float z=card(str); //将这个表达式进行计算
cout<<"结果为:"<<z<<endl; //输出结果
if(24==(int)z)
{
cout<<"回答正确的!"<<endl;
score+=10;
cout<<"分数"<<score<<endl;
}
else
{
cout<<"回答错误的!"/*<<"你还有"<<(life--)<<"次机会"*/<<endl;
life--;
}
}
void game::Find(int n)
{
if(n==1) //n为1时,给出相应的处理
{
if(fabs(num[0]-answar)<=0.0) //判断数值是否是24
{
ans[find]=exp[0];
flag=true; //结果标识
shu++;
find++; //计算表达式个数
}
}
for(int i=0;i<n;i++) //递归循环开始,使用穷举法的方法
{
for(int j=i+1;j<n;j++)
{
double a,b;
string expa,expb; //计算的表达式
a=num[i]; //把两个随机数赋给a和b,随机数放在了数组中
b=num[j];
num[j]=num[n-1];
expa=exp[i];
expb=exp[j];
exp[j]=exp[n-1];
exp[i]='('+expa+'+'+expb+')';
num[i]=a+b;
Find(n-1);
exp[i]='('+expa+'-'+expb+')';
num[i]=a-b;
Find(n-1);
exp[i]='('+expb+'-'+expa+')';
num[i]=b-a;
Find(n-1);
exp[i]='('+expa+'*'+expb+')';
num[i]=a*b;
Find(n-1);
//除数为0
if(b!=0)
{
exp[i]='('+expa+'/'+expb+')';
num[i]=a/b;
Find(n-1);
}
if(a!=0)
{
exp[i]='('+expb+'/'+expa+')';
num[i]=b/a;
Find(n-1);
}
num[i]=a; //递归调用的回溯,为了不影响下一次调用
num[j]=b;
exp[i]=expa;
exp[j]=expb;
}
}
}
void game::out()
{
char a;
cout<<"是否想看答案?";
cout<<"输入y,或n;";
cin>>a;
Find(count);
if(a=='y')
{
if(flag==true)
{
cout<<"\n成功!"<<endl;
cout<<"总共的方法有:"<<shu<<endl;
for(int i=0;i<find+1;i++)
{
cout<<ans[i]<<endl;
}
}
else
cout<<"无正确的解!"<<endl;
}
}
void menu()
{
cout<<"*********欢迎来到24点游戏***************\n";
cout<<" 1.开始游戏\n";
cout<<" 2.游戏规则\n";
cout<<" 3.退出游戏\n";
cout<<"****************************************\n";
}
void rule()
{
cout<<"游戏说明:"<<endl;
cout<<" 从扑克中每次取出4张牌。使用加减乘除,"<<endl;
cout<<" 第一个能得出24者为赢.(其中,J代表11,Q代表12,K代表13,A代表1)"<<endl;
cout<<"答题者如果碰到如J、Q、K对应成11,12,13;"<<endl;
}
int main()
{
game g24;
g24.flag = false;
g24.shu= 0;
g24.find= 0;
int option; //选项
int flag; //标志
int score;
char a;
do
{
menu();
cout<<"请输入你的选项:";
cin>>option;
if(option>=1&&option<=3)
{
flag=1;
break;
}
else
{
flag=0;
cout<<"输入有误,请重新输入!"<<endl;
}
}while(flag==0); //如果输入的选项不符合要求,重新输入
while(flag==1) //输入正确选项
{
switch(option)
{
case 1:
g24.card();
g24.judge();
g24.out();
break;
case 2:
rule();
break;
case 3:
exit(0);
break;
}
if(life>0)
{
menu();
cout<<"请输入你的选项:";
cin>>option;
}
else
exit(0);
}
}
四.总结收获:
程序还有点缺陷,比如不能判断答题者用的是不是系统给定的四个数字,只要结果为24它都判对,显然是不合理的,还没有解决这个问题。还有之前的数据结构与算法设计学的不好,很多知识也还没有很好的理解,这次使用栈也比较吃力,参考了很多24点的例子,也结合自己的理解,在完成基础要求,还实现了计算玩家表达式的功能,还有玩家的生命值,在编程过程中也有很多问题,定义类完后没在大括号后加括号,导致很多错误,还是自己不够细心,关于栈的知识也是这周恶补,理解不是很深,这次代码不是那么完美,但我有很多收获,对这次编程结果还是比较满意的。这次编程锻炼了我的逻辑思维能力,提高解决问题的能力,也让我看到自己的能力还是不够,而且算法结构还比较重要,要好好理解,还要注重实践,之后还要优化程序,使它更完美。