一、题目要求
24点游戏是经典的纸牌益智游戏
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。
用户初始生命值为一给定值,初始分数为0。随机生成4个代表扑克牌牌面的数字或字母,由用户输入包含这4个数字或字母的运算表达式(可包含括号),如果表达式计算结果为24则代表用户赢了此局。
1.程序风格良好(使用自定义注释模板)
2.使用计时器要求用户在规定时间内输入表达式,如果规定时间内运算正确则加分,超时或运算错误则进入下一题并减少生命值(不扣分)。
3.所有成绩均可记录在TopList.txt文件中。
二、问题分析
根据题目,我们可以用一个数组存储随机生成的四张牌。由于用户输入的表达式为前缀表达式,因此我们需要将其转化为后缀表达式进行计算。计算过程可以使用栈实现一个简单的计算器。而计时器的实现,我们可以使用简单方式获取开始时间和结束时间,控制二者之差进行时间控制。
三、调试
1.随机生成四张牌
2.表达式输入
四、测试
1.测试计算器功能
2.测试成绩录入文件功能
五、程序代码
#include<stdio.h>
#include<string.h>
#include<time.h>
#define MAX 100
#define MCHAR 20
typedef struct CharStack //字符栈
{
char data[MAX];
int top;
}cStack;
typedef struct DoubleStack //数据栈
{
int data[MAX];
int top;
}dStack;
struct use //用户的结构体
{
char id[MCHAR]; //用户名称
int HP; //生命值
int score; //分数
}user[MAX];
int unum;
void play();
int Isop(char ); //当前扫描元素优先级
int Inop(char ); //栈顶元素优先级
void Initc(cStack *); //初始化字符栈
int Pushc(cStack *,char); //字符栈压栈
char Gettopc(cStack *); //返回栈顶元素
char Popc(cStack *); //出栈
void Initd(dStack *); //初始化数据栈
int Pushd(dStack *,int); //数据压栈
int Popd(dStack *); //出栈
void Trans(char*s1,char*s2); //转化为后缀表达式
int Calculate(char *s2); //后缀表达式求值
void readTopfile();
void writeTopfile();
void grade(int t,int s);
int main()
{
readTopfile();
srand((unsigned)time(NULL)); //随机数
play();
return 0;
}
void play()
{
int j=unum;
int t=0,s=0;
int a[4];
int i;
int fun=0,choice=0;
int start,end;
char s1[MAX]; //用于存储前缀表达式
char s2[MAX]; //用于存储转换后的表达式
printf("24点游戏欢迎您!\n\n");
printf("请输入您的用户名:");
scanf("%s",&user[j].id);
while(1) //主界面
{
printf("请输入相应选项:1开始游戏,2结束游戏\n");
scanf("%d",&fun);
printf("您的初始生命值为100,答错或超时减10,每答对1次记10分\n");
while(fun==1)
{
printf("您抽到的四张牌是:");
for(i=0;i<4;i++)
{
a[i]=rand()%13+1; //获得随机数
if(a[i]==1)
printf("A ");
else if(a[i]==11)
printf("J ");
else if(a[i]==12)
printf("Q ");
else if(a[i]==13)
printf("K ");
else
printf("%d ",a[i]);
}
printf("您可以进行以下选择:1.作答 2.跳过\n");
scanf("%d",&choice);
while(choice==1)
{
printf("A=1,J=11,Q=12,K=13\n");
printf("请在60秒内完成作答。");
printf("请输入表达式:");
start=clock();
scanf("%s",s1);
Trans(s1,s2); //处理字符串,并转化为后缀表达式,存放在s2中
printf("\n计算结果为: %d",Calculate(s2)); //后缀表达式求值
printf("\n");
end=clock();
if(Calculate(s2)!=24)
{
printf("您输入的表达式运算不正确!\n");
t=t+1;
break;
}
else
{
if((end-start)>60000)
{
printf("您输入的表达式正确,不过很遗憾您已超时!");
t=t+1;
break;
}
else
{
printf("恭喜你,回答正确!\n");
s=s+1;
break;
}
}
}
if(t==10)
{
printf("您的生命值已为0,游戏结束!");
printf("您的成绩为:%d",s*10);
break;
}
else
{
printf("您可以选择:1.继续游戏 2.结束游戏\n") ;
scanf("%d",&fun);
if(fun==1)
continue;
else
{
grade(t,s);
printf("用户名:%s 生命值:%d 成绩:%d",user[j].id,user[j].HP,user[j].score);
break;
}
}
}
break;
}
printf("\n谢谢使用");
}
void grade(int t,int s)
{
int j=unum;
user[j].HP=100-t*10;
user[j].score=s*10;
unum++;
writeTopfile();
}
void readTopfile()
{
FILE *fp; //声明文件
int i=0; //定义变量
fp=fopen("TopList.txt","r"); //打开存放记错单词文件库
if(!fp) //如果文件不存在
{
printf("\n打开文件TopList.txt失败!");
}
while(fscanf(fp,"%s %d %d ",user[i].id,&user[i].HP,&user[i].score)==3)//读取下一个用户
{
i++;//计数器+1
}
unum=i;//记录用户总数
fclose(fp);//关闭文件
}
//向用户文件写入用户信息的函数
void writeTopfile(){
FILE *fp;//声明文件
int i=0;
fp=fopen("TopList.txt","w");//打开文件
if(!fp)//如果文件存在
{
printf("\n打开文件TopList.txt失败!");
}
for(i=0;i<unum;i++)
{
fprintf(fp,"\n%s %d %d ",user[i].id,user[i].HP,user[i].score);//fprintf(可以从一个文件流中格式化写入数据)
}
printf("\n");
fclose(fp);//关闭文件
}
//初始化
void Initc(cStack *s1)
{
s1->top=-1;
}
//字符栈压栈
int Pushc(cStack *c1,char op)
{
if(c1->top<MAX)
{
c1->data[++c1->top]=op;
return 1;
}
else return 0;
}
//GET栈顶元素
char Gettopc(cStack *c1)
{
return c1->data[c1->top];
}
//字符栈出栈
char Popc(cStack *c1)
{
return c1->data[c1->top--];
}
//初始化数据栈
void Initd(dStack *d1)
{
d1->top=-1;
}
//数据栈压栈
int Pushd(dStack *d1,int data)
{
if(d1->top<MAX)
{
d1->data[++d1->top]=data;
return 1;
}
else return 0;
}
//数据栈出栈
int Popd(dStack *d1)
{
return d1->data[d1->top--];
}
int Isop(char op) //当前扫描运算符优先级
{
switch(op)
{
case '(': return 6;
case '+': case '-': return 2;
case '*': case '/': return 4;
}
}
int Inop(char op) //当前扫描运算符优先级
{
switch(op)
{
case '(': return 1;
case '+': case '-': return 3;
case '*': case '/': return 5;
}
}
void Trans(char *s1,char *s2)
{
int i=0;
int j=0;
int flag1=-1; //flag1为0表示上次输出为数字,flag1为1表示上次输出为字符
int flag2=-1; //flag2为0表示上次扫描为数字,flag为1表示上次扫描为运算符,用于区分数字后加空格
cStack st1; //暂放运算符
Initc(&st1);
while(s1[i]!='\0')
{
if(flag1==0&&flag2==1) //若上次的输出为数字,上次循环扫描为字符,则表示该数字串结束,则在数字后加空格区分
{
s2[j++]=' ';
flag1=1;
}
if(s1[i]>='0'&&s1[i]<='9')
{
s2[j++]=s1[i];
flag2=0;
flag1=0;
}
else if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/'||s1[i]=='(')
{
flag2=1;
if(st1.top<0||Isop(s1[i])>Inop(Gettopc(&st1)))
{
Pushc(&st1,s1[i]);
}
else
{
while(st1.top>=0&&Isop(s1[i])<Inop(Gettopc(&st1))) //当前扫描字符优先级不断与栈顶字符优先级比较,当前字符 //小于栈顶字符时退栈并输出
{
s2[j++]=Popc(&st1);
flag1=1;
}
if(st1.top<0||Isop(s1[i])>Inop(Gettopc(&st1))) //当前字符优先级大于栈顶优先级或栈空时当前字符压入字符栈内
{
Pushc(&st1,s1[i]);
}
}
}
else if(s1[i]==')')
{
flag2=1;
if(Gettopc(&st1)!='(') //若括号仅包含数字则没有输出运算符
{
flag1=1;
}
while(Gettopc(&st1)!='(')
{
s2[j++]=Popc(&st1);
}
Popc(&st1); //将'('出栈
}
i++;
}
while(st1.top>=0) //将栈内剩余的运算符依次退栈输出
{
s2[j++]=Popc(&st1);
}
s2[j]='\0';
}
//表达式求值
int Calculate(char *s1)
{
int i=0;
int flag; //char类型转换为int类型数据标记
int data1,data2;
int sum;
dStack ds1;
Initd(&ds1);
while(s1[i]!='\0')
{
if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/') //若为运算符获取栈顶两个元素进行计算
{
data1=Popd(&ds1);
data2=Popd(&ds1);
if(s1[i]=='+') Pushd(&ds1,data2+data1);
else if(s1[i]=='-') Pushd(&ds1,data2-data1);
else if(s1[i]=='*') Pushd(&ds1,data2*data1);
else if(s1[i]=='/') Pushd(&ds1,data2/data1);
}
else
{
flag=0; //初始化为0为整数部分标记,1为小数部分标记
sum=0;
int divider=1;
while(s1[i]!=' '&&s1[i]!='+'&&s1[i]!='-'&&s1[i]!='*'&&s1[i]!='/')
{
if(flag==0)
sum=sum*10+(int)(s1[i]-'0');
else
{
divider=divider*10;
sum=sum+((int)(s1[i]-'0'))/divider;
}
i++;
}
if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*'||s1[i]=='/') i--;//转化成功一个数据,若下个字符为运算符,则i--,回到当前运算的数据位置
Pushd(&ds1,sum);
}
i++; //i++准备下一个字符的转换
}
return Popd(&ds1);
}
六、运行结果