24点游戏
1.题目分析
题目要求:本次作业有关24点游戏,从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。
基本要求: 随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式,用擅长的语言(C/C++/Java或其他均可)实现程序解决问题。
提高要求:给一用户初始生命值和初始分数。随机生成4个代表扑面的数字或字母,由用户输入包含这4个数字或字母的运算表达式(可包含括号),如果表达式计算结果为24则代表用户赢了此局。使用计时器要求用户在规定时间内输入表达式,如果规定时间内运算正确则加分,超时或运算错误则进入下一题并减少生命值(不扣分)。最后所有成绩均可记录在TopList.txt文件中。
具体题目分析:①本次游戏要求生成的四张牌是随机的,所以会用到随机数rand()随机生成四个数,②四个数产生值为24的不同表达式重点在于表达式符号的类型以及位置,所以应该列出四个数组成的表达式的可能情况。③提高要求涉及表达式的计算,所以需要用到数据结构中栈对表达式进行求解。(此处涉及栈的相关操作例如栈的初始化,如何放置表达式,如何将表达式从栈中取出)④在一定时间内执行某个操作会涉及系统函数kbhit(),所以我查阅资料了解了关于这个函数的用法。⑤将最终的得分写入文件,需注意文件的读写方式(Fprintf)以及文件路径是否正确。
2.算法构造
①列出生成随机数值为24的表达式。需注意表达式符号的类型以及位置。四个数组成的表达式总共有五种类型。{第一种表达式:a
b
b
bc
d
=
第
二
种
表
达
式
(
a
d=第二种表达式(a
d=第二种表达式(a(b
c
)
)
c))
c))d=第三种表达式a
(
b
(b
(b(c
d
)
)
=
第
四
种
表
达
式
a
d))= 第四种表达式a
d))=第四种表达式a((b
c
)
c)
c)d)= 第五种表达式(a
b
)
b)
b)(cKaTeX parse error: Expected 'EOF', got '}' at position 4: d)=}̲(代表某个具体运算符,a,b,c,d代表随机产生的四个操作数)。
②栈对表达式求值算法。(此算法参考相关资料)建立两个栈,一个栈存放运算数,另一个存放操作符。设置一个数组cmp[7][7] 用来进行比较运算符优先级的矩阵,3代表’=’,2代表’>’,1是代表’<’,0代表不可比,对两个栈进行初始化操作,输入表达式如果没有结束标识符#,将不会输出结果,或出现错误结果。具体操作函数{void InitStack(SeqStack *S)/初始化运算符栈/;void InitStacknum(numSeqStack *S)/初始化运算数栈/int IsEmpty(SeqStack *S)/判操作符栈为空栈时返回值为真,反之为假/int IsFull(SeqStack *S)/判断符号栈S为满栈时返回值为真,反之为假/void Push(SeqStack *S, char x)/运算符栈入栈函数, '#'为结束标志/void Pushnum(numSeqStack *S, int x)/运算数入栈函数/int Pop(SeqStack *S)/运算符栈出栈函数/int Isoperator(char ch) /判断输入字符是否为运算符函数,是返回TRUE,不是返回FALSE/等。(本算法重点是判断运算符运算顺序,以及分清表达式中的运算符和算数)。
③控制时间算法。运用系统函数setlocale(LC_ALL,“chs”);sleep(3000);用kbhit()判断玩家若不在三秒内输入表达式,将减少生命值,开始新的一轮。
④写入文件算法:fprintf(文件指针,写入文件类型,变量)。
3.算法实现
``#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<windows.h>
#include<conio.h>
#include<locale.h>
#include<math.h>
int i,j,k,life=10,score=0,num=0; //定义循环变量,用户生命值以及得分
#define Stack_increment 20
#define Stack_Size 100
char ops[7] = { '+', '-', '*', '/', '(', ')', '#' };/*运算符数组*/
int cmp[7][7] = { /*用来进行比较运算符优先级的矩阵,3代表'=',2代表'>',1代表'<',0代表不可比*/
{ 2, 2, 2, 2, 2, 1, 1 },
{ 2, 2, 2, 2, 2, 1, 1 },
{ 1, 1, 2, 2, 2, 1, 1 },
{ 1, 1, 2, 2, 2, 1, 1 },
{ 2, 2, 2, 2, 3, 1, 0 },
{ 1, 1, 1, 1, 0, 1, 1 },
{ 2, 2, 2, 2, 2, 0, 3 } };
typedef struct/*运算符栈的定义*/
{
int stacksize;
int *top;
int *base;
}SeqStack;
typedef struct/* 运算数栈的定义*/
{
int stacksize;
int *top;
int *base;
}numSeqStack;
void InitStack(SeqStack *S)/*初始化运算符栈*/
{
S->base = (int *)malloc(Stack_Size * sizeof(int));
S->top = S->base;
S->stacksize = Stack_Size;
}
void InitStacknum(numSeqStack *S)/*初始化运算数栈*/
{
S->base = (int *)malloc(Stack_Size * sizeof(int));
S->top = S->base;
S->stacksize = Stack_Size;
}
int IsEmpty(SeqStack *S)/*判操作符栈为空栈时返回值为真,反之为假*/
{
return(S->top == S->base ? 1 : 0);
}
int IsEmptynum(numSeqStack *S)/*判断栈S为空栈时返回值为真,反之为假*/
{
return(S->top == S->base ? 1 : 0);
}
int IsFull(SeqStack *S)/*判断符号栈S为满栈时返回值为真,反之为假*/
{
return (*S->top == Stack_Size - 1 ? 1 : 0);
}
int IsFullnum(numSeqStack *S)/*判断操作数栈为满栈时返回值为真,反之为假*/
{
return (*S->top == Stack_Size - 1 ? 1 : 0);
}
void Push(SeqStack *S, char x)/*运算符栈入栈函数, '#'为结束标志*/
{
if (* (S -> top) == Stack_Size - 1)
{
printf("Stack is full\n");
S->base = (int *)realloc(S->base, (S->stacksize + Stack_increment) * sizeof(int));
S->top = S->base + S->stacksize;
S->stacksize = S->stacksize + Stack_increment;
}
else if (x == '#')
{
return;
}
else
{
*S->top++ = x;
}
}
void Pushnum(numSeqStack *S, int x)/*运算数入栈函数*/
{
if (*S->top == Stack_Size - 1)
{
S->base = (int *)realloc(S->base, (S->stacksize + Stack_increment) * sizeof(int));
S->top = S->base + S->stacksize;
S->stacksize = S->stacksize + Stack_increment;
}
else if ((char)x == '#')
{
return;
}
else
{
*(S -> top) = x;
S -> top++;
}
}
int Pop(SeqStack *S)/*运算符栈出栈函数*/
{
if (S->top == S->base)
{
return 0;
}
else
{
--(S->top);
return 1;
}
}
int Popnum(numSeqStack *S)/*运算数栈出栈函数*/
{
if (S -> top == S -> base)
{
return 0;
}
else
{
--(S -> top);
return 1;
}
}
char GetTop(SeqStack *S)/*运算符栈取栈顶元素函数*/
{
return (*(S->top - 1));
}
int GetTopnum(numSeqStack *S)/*运算数栈取栈顶元素函数*/
{
if (S -> top == S -> base)
{
return (*(S -> top ));
}
return (* (S -> top - 1));
}
int Isoperator(char ch) /*判断输入字符是否为运算符函数,是返回TRUE,不是返回FALSE*/
{
int i;
for (i = 0; i < 7; i++)
{
if (ch == ops[i])
return (int)1;
}
return (int)0;
}
char Compare(char ch1, char ch2)/*比较运算符优先级函数*/
{ /*保存优先级比较后的结果'>'、'<'、'='*/
int i, m = 0, n = 0;
int priority;
for (i = 0; i < 7; i++)
{
if (ch1 == ops[i])
m = i;
if (ch2 == ops[i])
n = i;
}
priority = cmp[m][n];
switch (cmp[m][n])
{
case 1:
return (char)('<');
case 2:
return (char)('>');
case 3:
return (char)('=');
default:
printf("表达式错误!\n");
break;
}
}
int Execute(int a, char op, int b)/*运算函数*/
{
switch (op)
{
case '+':
return (b + a);
break;
case '-':
return (b - a);
break;
case '*':
return (b * a);
break;
case '/':
return (b / a);
break;
}
}
int expression()/*输入一个简单算术表达式并计算其值。optr和operand分别为运算符栈和运算数栈,OPS为运算符集合*/
{
int a, b, v;
int sum = 0;
char ch, op, temp;
char str[100];
numSeqStack operand;
SeqStack optr;
InitStack(&optr); /*初始化,没有元素*/
InitStacknum(&operand);
printf("请输入表达式(#为结束符):\n"); /*表达式输入*/
scanf("%s",&str); /*取得一行表达式至字符串中*/
int i = 0;
for (i; i < (strlen(str) + 1); )
{
ch = str[i];
if (ch == '#')
{
if (IsEmpty(&optr))
{
v = GetTopnum(&operand);
return v;
}
else
{
a = GetTopnum(&operand);/*运算数栈取栈顶元素函数*/
Popnum(&operand);//运算数栈出栈函数
b = GetTopnum(&operand);
Popnum(&operand);
op = GetTop(&optr);//调用运算符栈取栈顶元素函数
Pop(&optr);//调用运算符栈出栈函数
v = Execute(a, op, b);//调用运算函数
Pushnum(&operand, v);//将运算结果放入运算数栈中
if (ch == ')')//如果碰到右括号
{
Pop(&optr);//继续调用运算符出栈函数
}
}
}
else if (!Isoperator(ch))/*判断输入字符是否为运算符,是返回TRUE,不是返回FALSE*/
{
int dis = 0;
int j = i, j1 = i, j2 = i, j3 = i;
int k; /*记录原始值*/
for (k = i; k < (strlen(str) + 1); )
{
if (!Isoperator(ch))
{
i++;
ch = str[i];
k++;
dis = i - j; /*记录差距的位数*/
j1 = i;
j2 = i;
j3 = i;
}
else if (Isoperator(ch))
{
break;
}
}
for (j1 = j1 - dis; j1 < j2 ; j1++)
{
int temp;
temp = (int)(str[j1] - '0');
sum = sum + temp * pow(10, (j2 - j1 - 1)); /*求和*/
if (j2 - j1 < 0)
{
break;
}
}
Pushnum(&operand, sum);//运算结果入栈
sum = 0;
dis = 0;
}
else if (Isoperator(ch))
{
if (optr.base == optr.top)//直到运算符栈为空
{
Push(&optr, ch);
i++;
}
else
{
char buf ;
temp = GetTop(&optr);
buf = Compare(temp, ch);
if (buf == '>')
{
Push(&optr, ch);
i++;
}
else if (buf == '=')
{
Pop(&optr);
}
else
{
a = GetTopnum(&operand);
Popnum(&operand);
b = GetTopnum(&operand);
Popnum(&operand);
op = GetTop(&optr);
Pop(&optr);
v = Execute(a, op, b);
Pushnum(&operand, v);
if (ch == ')')
{
Pop(&optr);
}
else
{
Push(&optr, ch);
}
i++;
}
}
}
}
}
int player()//玩家开始游戏,当玩家生命值为0时,停止游戏
{int b[4];
int s;
srand((unsigned)time(NULL));
for(i=0;i<4;i++)
b[i]=rand()%13+1;
printf("生成的随机数为:");
for(j=0;j<4;j++)
printf("%d\t",b[j]);
printf("\n");
printf("请输入算术表达式(随机由生成的数组成),并以#结束\n");
setlocale(LC_ALL,"chs");
printf("如果在3秒钟内不输入表达式,则未得分\n");
Sleep(3000);
s=expression();
printf("结果是: %d\n",s);
if(kbhit())//判断玩家是否在3秒内开始输入
life-=5;
else
{
if(s==24)
{score=score+5;}//玩家在3秒内输入表达式,并且表达式的值为24,分数加1
else
life-=5;
}
printf("本轮得分%d,生命值剩余%d(生命值为0时停止游戏)\n",score,life);
return score;
}
float count(int tag,float a,float b)//写出具体运算法则
{switch(tag)
{case 1: return(a+b);break;
case 2:return(a-b);break;
case 3:return(a*b);break;
case 4:return(a/b);break;
}
}
char print(int m)//判断操作符
{char s;
switch(m)
{case 1:s='+';break;
case 2:s='-';break;
case 3:s='*';break;
case 4:s='/';break;
}
return s;
}
void turn1(int i,int j,int k,float b[])//第一种表达式:a$b$c$d=
{float a1,a2,a3;
a1=count(i,b[0],b[1]);
a2=count(j,a1,b[2]);
a3=count(k,a2,b[3]);
if(a3==24)
{printf("((%f %c %f) %c %f) %c %f)=24\n",b[0],print(i),b[1],print(j),b[2],print(k),b[3]);//输出满足24点的表达式
num++;}
}
void turn2(int i,int j,int k,float b[])//第二种表达式(a$(b$c))$d=
{float a1,a2,a3;
a1=count(j,b[1],b[2]);
a2=count(i,b[0],a1);
a3=count(k,a2,b[3]);
if(a3==24)
{printf("(%f%c(%f%c%f))%c %f=24\n",b[0],print(i),b[1],print(j),b[2],print(k),b[3]);
num++;}
}
void turn3(int i,int j,int k,float b[])//第三种表达式a$(b$(c$d))=
{float a1,a2,a3;
a1=count(k,b[2],b[3]);
a2=count(j,b[1],a1);
a3=count(i,b[0],a2);
if(a3==24)
{printf("(%f%c(%f%c(%f%c%f ))=24\n",b[0],print(i),b[1],print(j),b[2],print(k),b[3]);
num++;}
}
void turn4(int i,int j,int k,float b[])//第四种表达式a$((b$c)$d)=
{float a1,a2,a3;
a1=count(j,b[1],b[2]);
a2=count(k,a1,b[3]);
a3=count(i,a2,b[0]);
if(a3==24)
{printf("%f%c((%f%c%f)%c%f)=24\n",b[0],print(i),b[1],print(j),b[2],print(k),b[3]);
num++;
}
}
void turn5(int i,int j,int k,float b[])//第五种表达式(a$b)$(c$d)=
{float a1,a2,a3;
a1=count(i,b[0],b[1]);
a2=count(k,b[2],b[3]);
a3=count(j,a1,a2);
if(a3==24)
{printf("(%f%c%f)%c(%f%c%f)=24\n",b[0],print(i),b[1],print(j),b[2],print(k),b[3]);
num++;}
}
void dianshu()//穷举法求出所有表达式
{float a[4];
int tag,sign=1;
srand((unsigned)time(NULL));
for(i=0;i<4;i++)
a[i]=rand()%13+1;//随机产生四张扑克牌
for(j=0;j<4;j++)
printf("%f\t",a[j]);
printf("\n");
for(i=1;i<5;i++)//穷举法列出所有表达式
for(j=1;j<5;j++)
for(k=1;k<5;k++)
{
turn1(i,j,k,a);
turn2(i,j,k,a);
turn3(i,j,k,a);
turn4(i,j,k,a);
turn5(i,j,k,a);
}
}
void main()
{int sign=1,tag;
FILE* p;//定义文件指针
p=fopen("TopList.txt","w");
while(sign)//进行功能选择
{
printf("请进行功能选择\n**1----列出生成的随机数所有满足24点的表达式----***\n**2----------------玩家开始游戏----------------***\n**0----------退出--------**\n");
scanf("%d",&tag);
switch(tag)
{
case 1:
{dianshu();
printf("满足24点的表达式有%d个\n",num);
}break;
case 2:
{
while(life)
{
player();
fprintf(p,"本轮得分%d\n",score);//将本轮得分写入文件
}
fclose(p);//关闭文件
}break;
case 0:sign=0;break;
}
}
}
- 调试、测试及运行结果。
①调试结果
②运行结果
显示随机生成四个数值为24的表达式
玩家进行游戏,以及写入文件结果
5.经验归纳
1)错误分析:
本次实验主要内容是24点游戏。我编写程序时遇到的困难如下:①
列出满足24点表达式是未考虑不够全面,以至于结果只输出了部分表达式。②运用栈对表达式值求解时,对栈用之前未进行初始化,以至于数据无法存储进去。经过和同学的讨论最终解决。③在输入表达式式我定义的是数组存放表达式,输入应该用SCanf(“%d”,&s);我用成了gets(),导致表达式输入错误未能进行求值运算。
2)个人心得:
通过这次实验我得到的收获是:算符优先法是教材上有关栈的应用的一个具体实例,考虑到算符优先法中对于运算符的操作是先入先出的,正好符合栈这种结构的存储使用规则,于是我们便可以利用栈来实现算。遇到困难时的心态要平和,冷静地去查找问题并解决问题。注意知识的总结和积累。最近,其实不止是本次程序实验带给我的收获,就是作为一个合格的程序员,要注意对知识的积累和知识体系的梳理,每隔一段时间就应该对自己近期的工作有个小结和反思,只有这样,才能不断进步,因此,我在CSDN开通了自己的博客,关于本次实验写的知识点总结在里面都有。