一、题目分析
基本要求:随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式,用擅长的语言(C/C++/Java或其他均可)实现程序解决问题。
1.程序风格良好(使用自定义注释模板)
2.列出表达式无重复。
提高要求:用户初始生命值为一给定值(比如3),初始分数为。随机生成4个代表扑克牌牌面的数字或字母,由用户输入包含这4个数字或字母的运算表达式(可包含括号),如果表达式计算结果为24则代表用户赢了此局。
1. 程序风格良好(使用自定义注释模板)
2.使用计时器要求用户在规定时间内输入表达式,如果规定时间内运算正确则加分,超时或运算错误则进入下一题并减少生命值(不扣分)。
3.所有成绩均可记录在TopList.txt文件中。
二、算法构造
1.基本要求:
①构造子函数,将加减乘除四则运算和算式表达式的几种可能分别列举出来;
②在子函数get24()中使用穷举法输出算式表达式的几种可能形式;
2.提高要求:
①操作数栈:构造一个存数据的栈并判断栈是否为空,若为空则插入栈顶元素,若不为空则返回栈顶元素;
②操作符栈:构造一个存操作符的栈并判断栈是否为空,下面操作与数栈一样;
③运算操作:包括将表达式标准化、算数表达式求值、判断c是否是一个操作符、判断op1和op2优先级的高低,返回'>','<','='、对操作数a,b进行theta运算、获得以*c开始的操作数,返回后c为操作符;
3.主函数:
提示用户先选择游戏种类:
①若选择产生随机算式,则产生随机数,并调用基本要求里面的子函数,输出得数为24的所有可能;
②若选择手动输入算式表达式,则用户有三次机会根据产生的随机数在规定的时间范围内输入得数为24的算式,用户赢分数+1,如果,超时输入或输入为NULL,则失败,并且生命值减1,当生命值为0时游戏结束;
三、算法实现
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define BUFFERSIZE 256
char op[5]={'#','+','-','*','/',};
float yunsuan(float x,float y,int op)
{
switch(op)
{
case 1:return x+y;
case 2:return x-y;
case 3: return x*y;
case 4: return x/y;
}
}
//列出算式表达式的五种可能
float count1(float i,float j,float k,float t,int op1,int op2,int op3)
{
float sum1,sum2,sum3;
sum1 = yunsuan(i,j,op1);
sum2 = yunsuan(sum1,k,op2);
sum3 = yunsuan(sum2,t,op3);
return sum3;
}
float count2(float i,float j,float k,float t,int op1,int op2,int op3)
{
float sum1,sum2,sum3;
sum1 = yunsuan(j,k,op2);
sum2 = yunsuan(i,sum1,op1);
sum3 = yunsuan(sum2,t,op3);
return sum3;
}
float count3(float i,float j,float k,float t,int op1,int op2,int op3)
{
float sum1,sum2,sum3;
sum1 = yunsuan(k,t,op3);
sum2 = yunsuan(j,sum1,op2);
sum3 = yunsuan(i,sum2,op1);
return sum3;
}
float count4(float i,float j,float k,float t,int op1,int op2,int op3)
{
float sum1,sum2,sum3;
sum1 = yunsuan(j,k,op2);
sum2 = yunsuan(sum1,t,op3);
sum3 = yunsuan(i,sum2,op1);
return sum3;
}
float count5(float i,float j,float k,float t,int op1,int op2,int op3)
{
float sum1,sum2,sum3;
sum1 = yunsuan(i,j,op1);
sum2 = yunsuan(k,t,op3);
sum3 = yunsuan(sum1,sum2,op2);
return sum3;
}
get24(int i,int j,int k,int t)
{
int op1,op2,op3;
int flag=0;
//使用穷举法输出算式表达式
for(op1=1;op1<=4;op1++)
for(op2=1;op2<=4;op2++)
for(op3=1;op3<=4;op3++)
{
if(count1(i,j,k,t,op1,op2,op3)==24)
{
printf("((%d%c%d)%c%d)%c%d=24\n",i,op[op1],j,op[op2],k,op[op3],t);//算式表达式为((ab)c)d
flag = 1;
}
if(count2(i,j,k,t,op1,op2,op3)==24)
{
printf("(%d%c(%d%c%d))%c%d=24\n",i,op[op1],j,op[op2],k,op[op3],t);//算式表达式为(a(bc))d
flag = 1;
}
if(count3(i,j,k,t,op1,op2,op3)==24)
{
printf("%d%c(%d%c(%d%c%d))=24\n",i,op[op1],j,op[op2],k,op[op3],t);//算式表达式为a(b(cd))
flag = 1;
}
if(count4(i,j,k,t,op1,op2,op3)==24)
{
printf("%d%c((%d%c%d)%c%d)=24\n",i,op[op1],j,op[op2],k,op[op3],t);//算式表达式为a((bc)d)
flag = 1;
}
if(count5(i,j,k,t,op1,op2,op3)==24)
{
printf("(%d%c%d)%c(%d%c%d)=24\n",i,op[op1],j,op[op2],k,op[op3],t);//算式表达式为(ab)(cd)
flag = 1;
}
}
return flag;
}
typedef int Status; //函数返回状态
typedef int opndElem; //操作数元素类型
typedef struct{//操作数栈结构定义
opndElem *base;
opndElem *top;
int stacksize;
}OpndStack;
typedef char optrElem;//操作符元素类型
typedef struct{//操作符栈结构定义
optrElem *base;
optrElem *top;
int stacksize;
}OptrStack;
//==========操作数栈=============//
Status InitStack_OPND(OpndStack *S);
//构造一个空栈S
Status GetTop_OPND(OpndStack S,opndElem *e);
//若栈不为空,则用e返回S的栈顶元素,并返回OK;否则返回FALSE
Status Push_OPND(OpndStack *S,opndElem e);
//插入元素e为新的栈顶元素
Status Pop_OPND(OpndStack *S,opndElem *e);
//若栈S不为空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
//==========操作符栈=============//
Status InitStack_OPTR(OptrStack *S);
//构造一个空栈S
optrElem GetTop_OPTR(OptrStack S);
//若栈不为空,则用e返回S的栈顶元素,并返回OK;否则返回FALSE
Status Push_OPTR(OptrStack *S,optrElem e);
//插入元素e为新的栈顶元素
Status Pop_OPTR(OptrStack *S,optrElem *e);
//若栈S不为空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
//============运算操作================//
void Standard(char *expression);
//将表达式标准化
opndElem EvalueateExpression(const char *expression);
//算数表达式求值
Status Isoperator(char c);
//判断c是否是一个操作符
char Precede(char op1,char op2);
//判断op1和op2优先级的高低,返回'>','<','='
opndElem operate(opndElem a,optrElem theta,opndElem b);
//对操作数a,b进行theta运算
const char *getOpnd(const char *c,opndElem *op);
//获得以*c开始的操作数,返回后c为操作符
//==========操作数栈===========//
Status InitStack_OPND(OpndStack *S){
//构造一个空操作数栈S
S->base=(opndElem *)malloc(STACK_INIT_SIZE*sizeof(opndElem));
if(!S->base)//分配失败
{
printf("分配内存失败.\n");
exit(0);
}
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
return OK;
}
Status GetTop_OPND(OpndStack S,opndElem *e){
//若操作数栈不为空,则用e返回S的栈顶元素,并返回OK;否则返回FALSE
if(S.top==S.base){
printf("栈为空.\n");
return FALSE;
}else{
*e=*(S.top-1);
return OK;
}
}
Status Push_OPND(OpndStack *S,opndElem e){
//插入元素e为新的栈顶元素
if(S->top-S->base>=S->stacksize){//栈已满,追加存储空间
S->base=(opndElem *)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(opndElem));
if(!S->base)
{
printf("重新申请空间失败.\n");
exit(0);
}
S->top=S->base+S->stacksize;//更改栈顶指针
S->stacksize+=STACKINCREMENT;
}
*S->top++=e;
return OK;
}
Status Pop_OPND(OpndStack *S,opndElem *e){
//若栈S不为空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
if(S->top==S->base){//栈为空
printf("栈为空.\n");
return ERROR;
}
*e=*(--S->top);
return OK;
}
//==========操作符栈===========//
Status InitStack_OPTR(OptrStack *S){
//构造一个空操作数栈S
S->base=(optrElem *)malloc(STACK_INIT_SIZE*sizeof(optrElem));
if(!S->base)//分配失败
{
printf("分配内存失败.\n");
exit(0);
}
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
return OK;
}
optrElem GetTop_OPTR(OptrStack S){
//若操作数栈不为空,则返回S的栈顶元素,并返回OK;否则返回FALSE
optrElem e;
if(S.top==S.base){
printf("栈为空.\n");
}else{
e=*(S.top-1);
}
return e;
}
Status Push_OPTR(OptrStack *S,optrElem e){
//插入元素e为新的栈顶元素
if(S->top-S->base>=S->stacksize){//栈已满,追加存储空间
S->base=(optrElem *)realloc(S->base,(S->stacksize+STACKINCREMENT)*sizeof(optrElem));
if(!S->base)
{
printf("重新申请空间失败.\n");
exit(0);
}
S->top=S->base+S->stacksize;//更改栈顶指针
S->stacksize+=STACKINCREMENT;
}
*S->top++=e;
return OK;
}
Status Pop_OPTR(OptrStack *S,optrElem *e){
//若栈S不为空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
if(S->top==S->base){//栈为空
printf("栈为空.\n");
return ERROR;
}
*e=*(--S->top);
return OK;
}
//============运算操作================//
opndElem EvalueateExpression(const char *expression){
//对只有四则运算符的算数表达式 expression 求值
//OPTR:操作符栈,OPND:操作数栈
const char *c=expression;
OpndStack OPND;
OptrStack OPTR;
optrElem x,theta;
opndElem a,b,num,result;
InitStack_OPTR(&OPTR);//初始化操作符栈
InitStack_OPND(&OPND);//初始化操作数栈
Push_OPTR(&OPTR,'#');//首先将匹配符号'#'入栈
while(*c!='#'||GetTop_OPTR(OPTR)!='#'){
//printf("getchar=%c\n",*c);
if(*c=='\0')//遇到回车退出
break;
if(FALSE==Isoperator(*c)){
c=getOpnd(c,&num);
Push_OPND(&OPND,num);
}
else
switch(Precede(GetTop_OPTR(OPTR),*c)){
case '<':
Push_OPTR(&OPTR,*c);
c++;
break;
case '=':
Pop_OPTR(&OPTR,&x);
c++;
break;
case '>':
Pop_OPTR(&OPTR,&theta);
Pop_OPND(&OPND,&b);
Pop_OPND(&OPND,&a);
result=operate(a,theta,b);
//printf("temp result is:%d\n",result);
Push_OPND(&OPND,result);
break;
default:
//printf("Precede:%c",Precede(GetTop_OPTR(OPTR),*c));
break;
}//switch
}//while
GetTop_OPND(OPND,&result);
return result;
}
void Standard(char *expression){
//将字符串表达式标准化为算术表达式
char *p=expression,*q;
while(*p!='\0'){//遍历字符串
if(*p==' '){//如果是空格,删除
q=p;
do{
*q=*(q+1);
q++;
}while(*q!='\0');
}
p++;
}
*p++='#';
*p='\0';
}
const char *getOpnd(const char *c,opndElem *op){
//获得以*c开始的操作数,返回后c为操作符
int sum=0,tmp;
while(FALSE==Isoperator(*c)){//当c不是操作符
tmp=*c-'0';
sum=sum*10+tmp;
//printf("tmp=%d\n",tmp);
c++;
}
*op=sum;
//printf("getOpnd:%d\n",*op);
return c;
}
Status Isoperator(char c){
//判断c是否是一个运算操作符
switch(c){
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
case '#':
return TRUE;
break;
default:
return FALSE;
break;
}
}
char Precede(char op1,char op2){
//判断op1和op2优先级的高低,返回'>','<','='
switch(op1){
case '+':
switch(op2){
case '*':
case '/':
case '(':
return '<';
break;
default:
return '>';
break;
}
break;
case '-':
switch(op2){
case '*':
case '/':
case '(':
return '<';
break;
default:
return '>';
break;
}
break;
case '*':
switch(op2){
case '(':
return '<';
break;
default:
return '>';
break;
}
break;
case '/':
switch(op2){
case '(':
return '<';
break;
default:
return '>';
break;
}
break;
case '(':
switch(op2){
case ')':
return '=';
break;
default:
return '<';
break;
}
break;
case ')':
switch(op2){
default:
return '>';
break;
}
break;
case '#':
switch(op2){
case '#':
return '=';
break;
default:
return '<';
break;
}
break;
default:
return '<';
break;
}
}
opndElem operate(opndElem a,optrElem theta,opndElem b){
//对操作数a,b进行theta运算,并返回运算结果
//theta只能是四则运算符号
int rs_i;
switch(theta){
case '+':
rs_i=a+b;
break;
case '-':
rs_i=a-b;
break;
case '*':
rs_i=a*b;
break;
case '/':
if(b==0){
printf("errror:除数为0.");
exit(0);
}
rs_i=a/b;
break;
default:
printf("Is not a operator.\n");
break;
}
//printf("%d %c %d = %d\n",a,theta,b,rs_i);
return rs_i;
}
int main()
{
int a[4];
int j,i;
int choose;
printf("*****欢迎进入24点游戏*****\n");
printf("\n");
printf("提示:当数字为1、11、12或13时表示A,J,Q,K\n");
printf("\n");
printf("****请选择游戏种类****\n");
printf("0******随机产生算式\n");
printf("1******手动输入算式\n");
scanf("%d",&choose);
if(choose==0)
{
srand(time(NULL));
for(j=0;j<4;j++)
a[j]=rand()%13+1; //a[i]=rand()%13+1;//产生随机数并且避免产生数字0
/*printf("*****欢迎进入24点游戏*****\n");
printf("\n");
printf("提示:当数字为1、11、12或13时表示A,J,Q,K\n");
printf("\n");*/
printf("本轮产生的随机数为:\n");
for(j=0;j<4;j++)
printf("%d\t",a[j]);
printf("\n");
if(get24(a[0],a[1],a[2],a[3]));
else
{
printf("对不起,不能得到数字24\n");
}
}
else if(choose==1)
{
opndElem result=0;
char *expression=(char*)malloc(sizeof(char)*BUFFERSIZE);
if(expression==NULL)
{
printf("对不起,内存初始化错误:\n");
exit(0);
}
int total=3,score=0;
int tag=1;
/* printf("*****欢迎进入24点游戏*****\n");
printf("游戏规则: 玩家有三条命 每4张牌有30秒钟的时间写出算式或写出NULL\n");
printf("提示:当数字为1、11、12或13时表示A,J,Q,K\n");
printf("\n");*/
clock_t start,finish; //定义时间的开始与结束
double totaltime;
while(tag)
{
if(total>0)
{
opndElem result;
char expression[20];
if(expression==NULL){
printf("对不起,内存初始化错误\n");
exit(0);
}
srand((unsigned)time(NULL));
for(i=0;i<4;i++)
a[i]=rand()%13+1; //a[i]=rand()%13+1;//产生随机数并且避免产生数字0
printf("\n");
printf("本轮产生的随机数为:\n");
for(j=0;j<4;j++)
printf("%d\t",a[j]);
printf("\n");
start=clock();
printf("请使用随机数输入算式表达式:\n");
//gets(expression);
scanf("%s",&expression);
//printf("Before standard:%s\n",expression);
Standard(expression);//标准化
//printf("Standard expression:%s\n",expression);
result=EvalueateExpression(expression);
long i=10000000L;
while(i--)
finish=clock();
totaltime=(double)(finish-start)/CLOCKS_PER_SEC; //程序运行时间,算差值
if(totaltime>60)//时间限制
{
printf("超时,生命值减1\n");
total--;
printf("生命值还有%d\n",total);
continue;
}
if(result==24)//判断得数是否为24
{
printf("结果是: %d\n",result);
printf("恭喜你答对了,你真厉害!\n");
printf("还有生命值%d\n",total);
score++;
printf("本次成绩为:%d\n",score);
continue;
}
else
{
printf("失败,继续加油!\n");
total--;
printf("还有生命值%d\n",total);
continue;
}
}
else{tag=0;}
}
printf("本轮游戏总成绩为:%d\n",score);
//将score存入文件夹里面
FILE *fp= fopen("TopList.txt", "w");//打开记录分数的记事本
if(!fp)
{
printf("\n打开文件TopList.txt失败!");
fprintf(fp, "%d", score);
}
else {
fprintf(fp, "%d", score);
}
fclose(fp);//关闭文件
}
//fp = NULL;
return 0;
}
四、测试及调试截屏
1.测试截屏
①基本要求
②提高要求
2.调试
①基本要求
②提高要求
五、总结
本次作业中基本要求随机生成4个代表不可拍牌面的数字,程序自动列出所有可能算出24的算式表达式,这里需要随机函数产生随机数,最重要的是要把算式表达式的几种可能都在子函数中写出来,在输出函数里面判断得数是否为24,符合条件的用穷举法输出,在主函数中调用。提高要求要使用到两个栈,分别是运算符栈和数据栈,这就联系到了上学期学的数据结构,让我们又巩固了栈的知识,要实现的功能还有输入算式表达式有时间限制,这个功能在之前的作业中使用过,因此,也算是有复习了一遍怎样计算时间。将数据存入文件中在之前的作业中也用到过很多次,所以没有什么难度。