一、题目要求
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
#include
#include
#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&#61;0;i<4;i&#43;&#43;)
{
a[i]&#61;rand()%13&#43;1; //获得随机数
if(a[i]&#61;&#61;1)
printf("A ");
else if(a[i]&#61;&#61;11)
printf("J ");
else if(a[i]&#61;&#61;12)
printf("Q ");
else if(a[i]&#61;&#61;13)
printf("K ");
else
printf("%d ",a[i]);
}
printf("您可以进行以下选择&#xff1a;1.作答 2.跳过\n");
scanf("%d",&choice);
while(choice&#61;&#61;1)
{
printf("A&#61;1,J&#61;11,Q&#61;12,K&#61;13\n");
printf("请在60秒内完成作答。");
printf("请输入表达式:");
start&#61;clock();
scanf("%s",s1);
Trans(s1,s2); //处理字符串&#xff0c;并转化为后缀表达式,存放在s2中
printf("\n计算结果为: %d",Calculate(s2)); //后缀表达式求值
printf("\n");
end&#61;clock();
if(Calculate(s2)!&#61;24)
{
printf("您输入的表达式运算不正确&#xff01;\n");
t&#61;t&#43;1;
break;
}
else
{
if((end-start)>60000)
{
printf("您输入的表达式正确&#xff0c;不过很遗憾您已超时&#xff01;");
t&#61;t&#43;1;
break;
}
else
{
printf("恭喜你&#xff0c;回答正确&#xff01;\n");
s&#61;s&#43;1;
break;
}
}
}
if(t&#61;&#61;10)
{
printf("您的生命值已为0&#xff0c;游戏结束&#xff01;");
printf("您的成绩为&#xff1a;%d",s*10);
break;
}
else
{
printf("您可以选择&#xff1a;1.继续游戏 2.结束游戏\n") ;
scanf("%d",&fun);
if(fun&#61;&#61;1)
continue;
else
{
grade(t,s);
printf("用户名&#xff1a;%s 生命值&#xff1a;%d 成绩&#xff1a;%d",user[j].id,user[j].HP,user[j].score);
break;
}
}
}
break;
}
printf("\n谢谢使用");
}
void grade(int t,int s)
{
int j&#61;unum;
user[j].HP&#61;100-t*10;
user[j].score&#61;s*10;
unum&#43;&#43;;
writeTopfile();
}
void readTopfile()
{
FILE *fp; //声明文件
int i&#61;0; //定义变量
fp&#61;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)&#61;&#61;3)//读取下一个用户
{
i&#43;&#43;;//计数器&#43;1
}
unum&#61;i;//记录用户总数
fclose(fp);//关闭文件
}
//向用户文件写入用户信息的函数
void writeTopfile(){
FILE *fp;//声明文件
int i&#61;0;
fp&#61;fopen("TopList.txt","w");//打开文件
if(!fp)//如果文件存在
{
printf("\n打开文件TopList.txt失败!");
}
for(i&#61;0;i
fprintf(fp,"\n%s %d %d ",user[i].id,user[i].HP,user[i].score);//fprintf&#xff08;可以从一个文件流中格式化写入数据&#xff09;
}
printf("\n");
fclose(fp);//关闭文件
}
//初始化
void Initc(cStack *s1)
{
s1->top&#61;-1;
}
//字符栈压栈
int Pushc(cStack *c1,char op)
{
if(c1->top
c1->data[&#43;&#43;c1->top]&#61;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&#61;-1;
}
//数据栈压栈
int Pushd(dStack *d1,int data)
{
if(d1->top
d1->data[&#43;&#43;d1->top]&#61;data;
return 1;
}
else return 0;
}
//数据栈出栈
int Popd(dStack *d1)
{
return d1->data[d1->top--];
}
int Isop(char op) //当前扫描运算符优先级
{
switch(op)
{
case &#39;(&#39;: return 6;
case &#39;&#43;&#39;: case &#39;-&#39;: return 2;
case &#39;*&#39;: case &#39;/&#39;: return 4;
}
}
int Inop(char op) //当前扫描运算符优先级
{
switch(op)
{
case &#39;(&#39;: return 1;
case &#39;&#43;&#39;: case &#39;-&#39;: return 3;
case &#39;*&#39;: case &#39;/&#39;: return 5;
}
}
void Trans(char *s1,char *s2)
{
int i&#61;0;
int j&#61;0;
int flag1&#61;-1; //flag1为0表示上次输出为数字&#xff0c;flag1为1表示上次输出为字符
int flag2&#61;-1; //flag2为0表示上次扫描为数字&#xff0c;flag为1表示上次扫描为运算符&#xff0c;用于区分数字后加空格
cStack st1; //暂放运算符
Initc(&st1);
while(s1[i]!&#61;&#39;\0&#39;)
{
if(flag1&#61;&#61;0&&flag2&#61;&#61;1) //若上次的输出为数字&#xff0c;上次循环扫描为字符&#xff0c;则表示该数字串结束&#xff0c;则在数字后加空格区分
{
s2[j&#43;&#43;]&#61;&#39; &#39;;
flag1&#61;1;
}
if(s1[i]>&#61;&#39;0&#39;&&s1[i]<&#61;&#39;9&#39;)
{
s2[j&#43;&#43;]&#61;s1[i];
flag2&#61;0;
flag1&#61;0;
}
else if(s1[i]&#61;&#61;&#39;&#43;&#39;||s1[i]&#61;&#61;&#39;-&#39;||s1[i]&#61;&#61;&#39;*&#39;||s1[i]&#61;&#61;&#39;/&#39;||s1[i]&#61;&#61;&#39;(&#39;)
{
flag2&#61;1;
if(st1.top<0||Isop(s1[i])>Inop(Gettopc(&st1)))
{
Pushc(&st1,s1[i]);
}
else
{
while(st1.top>&#61;0&&Isop(s1[i])
s2[j&#43;&#43;]&#61;Popc(&st1);
flag1&#61;1;
}
if(st1.top<0||Isop(s1[i])>Inop(Gettopc(&st1))) //当前字符优先级大于栈顶优先级或栈空时当前字符压入字符栈内
{
Pushc(&st1,s1[i]);
}
}
}
else if(s1[i]&#61;&#61;&#39;)&#39;)
{
flag2&#61;1;
if(Gettopc(&st1)!&#61;&#39;(&#39;) //若括号仅包含数字则没有输出运算符
{
flag1&#61;1;
}
while(Gettopc(&st1)!&#61;&#39;(&#39;)
{
s2[j&#43;&#43;]&#61;Popc(&st1);
}
Popc(&st1); //将&#39;(&#39;出栈
}
i&#43;&#43;;
}
while(st1.top>&#61;0) //将栈内剩余的运算符依次退栈输出
{
s2[j&#43;&#43;]&#61;Popc(&st1);
}
s2[j]&#61;&#39;\0&#39;;
}
//表达式求值
int Calculate(char *s1)
{
int i&#61;0;
int flag; //char类型转换为int类型数据标记
int data1,data2;
int sum;
dStack ds1;
Initd(&ds1);
while(s1[i]!&#61;&#39;\0&#39;)
{
if(s1[i]&#61;&#61;&#39;&#43;&#39;||s1[i]&#61;&#61;&#39;-&#39;||s1[i]&#61;&#61;&#39;*&#39;||s1[i]&#61;&#61;&#39;/&#39;) //若为运算符获取栈顶两个元素进行计算
{
data1&#61;Popd(&ds1);
data2&#61;Popd(&ds1);
if(s1[i]&#61;&#61;&#39;&#43;&#39;) Pushd(&ds1,data2&#43;data1);
else if(s1[i]&#61;&#61;&#39;-&#39;) Pushd(&ds1,data2-data1);
else if(s1[i]&#61;&#61;&#39;*&#39;) Pushd(&ds1,data2*data1);
else if(s1[i]&#61;&#61;&#39;/&#39;) Pushd(&ds1,data2/data1);
}
else
{
flag&#61;0; //初始化为0为整数部分标记&#xff0c;1为小数部分标记
sum&#61;0;
int divider&#61;1;
while(s1[i]!&#61;&#39; &#39;&&s1[i]!&#61;&#39;&#43;&#39;&&s1[i]!&#61;&#39;-&#39;&&s1[i]!&#61;&#39;*&#39;&&s1[i]!&#61;&#39;/&#39;)
{
if(flag&#61;&#61;0)
sum&#61;sum*10&#43;(int)(s1[i]-&#39;0&#39;);
else
{
divider&#61;divider*10;
sum&#61;sum&#43;((int)(s1[i]-&#39;0&#39;))/divider;
}
i&#43;&#43;;
}
if(s1[i]&#61;&#61;&#39;&#43;&#39;||s1[i]&#61;&#61;&#39;-&#39;||s1[i]&#61;&#61;&#39;*&#39;||s1[i]&#61;&#61;&#39;/&#39;) i--;//转化成功一个数据&#xff0c;若下个字符为运算符&#xff0c;则i--&#xff0c;回到当前运算的数据位置
Pushd(&ds1,sum);
}
i&#43;&#43;; //i&#43;&#43;准备下一个字符的转换
}
return Popd(&ds1);
}
六、运行结果