#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <ctype.h>
#include <conio.h>
#define CARD_COUNT 52
#define CARD_SUM 4
#define GOAL_SCORE 24
#define HEART 0
#define SPADE 1
#define DIAMOND 2
#define CLUB 3
/*结构体:卡牌*/
typedef struct
{
unsigned char color:2;
unsigned char face:4;
}Card;
/*结构体:牌叠*/
typedef struct
{
int top;
Card *cards;
}Pile;
/*结构体:表达式计算时使用的栈*/
typedef struct
{
int depth_op;
int depth_num;
char *operators;
float *numbers;
}Cal_Stack;
/*结构体:回溯时使用的树节点*/
typedef struct
{
char parent;
char lchild;
char rchild;
char op;
float val;
}Cal_Tree;
/*过程:调试时使用,将目标字符串输出到trace.txt*/
void trace(char* text)
{
FILE *file=fopen("trace.txt","at+");
fwrite(text,strlen(text)*sizeof(char),1,file);
fclose(file);
}
/*cls函数:来自MSDN*/
#define PERR(bSuccess, api){if(!(bSuccess)) printf("%s:Error %d from %s on line %d\n", __FILE__, GetLastError(), api, __LINE__);}
void cls( HANDLE hConsole )
{
COORD coordScreen = { 0, 0 }; /* here's where we'll home the cursor */
BOOL bSuccess;
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
DWORD dwConSize; /* number of character cells in the current buffer */
/* get the number of character cells in the current buffer */
bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
PERR( bSuccess, "GetConsoleScreenBufferInfo" );
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
/* fill the entire screen with blanks */
bSuccess = FillConsoleOutputCharacter( hConsole, (TCHAR) ' ',
dwConSize, coordScreen, &cCharsWritten );
PERR( bSuccess, "FillConsoleOutputCharacter" );
/* get the current text attribute */
bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
PERR( bSuccess, "ConsoleScreenBufferInfo" );
/* now set the buffer's attributes accordingly */
bSuccess = FillConsoleOutputAttribute( hConsole, csbi.wAttributes,
dwConSize, coordScreen, &cCharsWritten );
PERR( bSuccess, "FillConsoleOutputAttribute" );
/* put the cursor at (0, 0) */
bSuccess = SetConsoleCursorPosition( hConsole, coordScreen );
PERR( bSuccess, "SetConsoleCursorPosition" );
return;
}
/*过程:初始化牌叠*/
void initCards(Pile *pile)
{
int i,j;
pile->top=0;
pile->cards=(Card*)malloc(sizeof(Card)*CARD_COUNT);
for(i=0,j=HEART-1;i<CARD_COUNT;i++)
{
if(i%13==0)j=(j+1)%4;
pile->cards[i].color=j;
pile->cards[i].face=(i%13)+1;
}
}
/*过程:删除牌叠*/
void delCards(Pile *pile)
{
free(pile->cards);
free(pile);
}
/*过程:洗牌*/
void shuffleCards(Pile *pile)
{
int i,j;
srand((int)time(0));
for(i=0;i<CARD_COUNT;i++)
{
j=rand()%CARD_COUNT;
if(i==j)continue;
pile->cards[i].color^=pile->cards[j].color;
pile->cards[j].color^=pile->cards[i].color;
pile->cards[i].color^=pile->cards[j].color;
pile->cards[i].face^=pile->cards[j].face;
pile->cards[j].face^=pile->cards[i].face;
pile->cards[i].face^=pile->cards[j].face;
}
pile->top=0;
}
/*过程:发牌*/
void dealCards(Pile *pile,Card *cards,int n)
{
int i;
if(CARD_COUNT-pile->top<n)
shuffleCards(pile);
for(i=0;i<n;i++,pile->top++)
{
cards[i].color=pile->cards[pile->top].color;
cards[i].face=pile->cards[pile->top].face;
}
}
/*过程:展示牌*/
void showCards(Card *cards,int n)
{
int i,j;
COORD c1,c2;/*存储光标位置*/
CONSOLE_SCREEN_BUFFER_INFO bInfo;/*存储控制台窗口信息(包括光标位置)*/
/*显示牌的框架*/
for(i=0;i<n;i++)
printf("┏━━┓");
printf("\n");
for(j=0;j<3;j++)
{
for(i=0;i<n;i++)
printf("┃ ┃");
printf("\n");
}
for(i=0;i<n;i++)
printf("┗━━┛");
printf("\n");
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &bInfo);/*获取控制台窗口信息*/
/*获取并修改光标位置*/
c1.X=bInfo.dwCursorPosition.X;
c1.Y=bInfo.dwCursorPosition.Y;
c2.Y=c1.Y-3;
c2.X=c1.X+3;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c2);/*定位光标到上面*/
/*显示牌的花色与点数*/
for(i=0;i<n;i++)
{
switch(cards[i].color)
{
case HEART:
printf("\3");
break;
case SPADE:
printf("\6");
break;
case DIAMOND:
printf("\4");
break;
case CLUB:
printf("\5");
break;
}
switch(cards[i].face)
{
case 13:
printf("K");
break;
case 12:
printf("Q");
break;
case 11:
printf("J");
break;
case 1:
printf("A");
break;
default:
printf("%d",cards[i].face);
break;
}
c2.X=c2.X+8;/*平移光标*/
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c2);
}
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),c1);/*复位光标位置*/
}
/*过程:初始化表达式计算工作空间*/
void initCal(Cal_Stack *cal,int n)
{
cal->depth_op=0;
cal->depth_num=0;
cal->numbers=(float*)malloc(sizeof(float)*n);
cal->operators=(char*)malloc(sizeof(char)*n);
}
/*过程:删除表达式计算工作空间*/
void delCal(Cal_Stack *cal)
{
free(cal->numbers);
free(cal->operators);
free(cal);
}
/*过程:数据入栈*/
void push_num(Cal_Stack *cal,float number)
{
cal->numbers[cal->depth_num++]=number;
}
/*过程:操作符入栈*/
void push_op(Cal_Stack *cal,char op)
{
cal->operators[cal->depth_op++]=op;
}
/*过程:数据出栈*/
void pop_num(Cal_Stack *cal)
{
if(cal->depth_num>0)
cal->depth_num--;
}
/*函数:取得栈顶数据*/
float top_num(Cal_Stack *cal)
{
return cal->numbers[cal->depth_num-1];
}
/*过程:操作符出栈*/
void pop_op(Cal_Stack *cal)
{
if(cal->depth_op>0)
cal->depth_op--;
}
/*函数:取得栈顶操作符*/
char top_op(Cal_Stack *cal)
{
return cal->operators[cal->depth_op-1];
}
/*函数:根据字符返回操作符索引*/
int get_idx(char op)
{
switch(op)
{
case '+':
return 0;
case '-':
return 1;
case '*':
return 2;
case '/':
return 3;
case '(':
return 4;
case ')':
return 5;
default:
return 6;
}
}
/*函数:根据操作符索引取得操作符优先级*/
int get_priority(int idx,int in_stack)
{
int t;
switch(idx)
{
case 0:
case 1:
t=2;
in_stack=in_stack==0?0:1;
break;
case 2:
case 3:
t=4;
in_stack=in_stack==0?0:1;
break;
case 4:
t=6;
in_stack=in_stack==0?0:-5;
break;
case 5:
t=6;
in_stack=in_stack==0?-5:0;
break;
default:
return 0;
}
return t+in_stack;
}
/*函数:根据数据及操作符计算值*/
float figure(float a,float b,char op)
{
switch(op)
{
case 0:
return a+b;
case 1:
return a-b;
case 2:
return a*b;
case 3:
if(b!=0)
return a/b;
default:
return a;
}
}
/*函数:取得绝对值*/
float Abs(float n)
{
if(n<0)
return -n;
return n;
}
/*函数:判断浮点数是否足够相近*/
int equal(float a,float b)
{
if(Abs(a-b)<1e-4)
return 1;
return 0;
}
/*函数,格式化字符串,失败返回-1*/
int format(char* buf)
{
int offset=0;
unsigned long q=0;//栈,存储未完成匹配的左括号
while(*buf!=10)
{
while(*buf==' ')
{
offset++;
buf++;
}
if(!(isdigit(*buf)))
{
switch(*buf)
{
case '+':
case '-':
case '*':
case '/':
break;
case '(':/*对于左括号,入栈*/
q|=(q<<1)+1;
break;
case ')':/*每遇到一个右括号,出栈一个左括号*/
if((q&0X01)==0X01)
q>>=1;
else/*栈空,返回出错*/
return -1;
break;
default:
return -1;
}
}
*(buf-offset)=*buf;
buf++;
}
if(q!=0)/*栈没有完全清空(左括号过多),返回出错*/
return -1;
*(buf-offset)=10;
return 0;
}
/*函数:分析表达式与发出的牌,返回表达式正确性,正确返回1,错误返回0,表达式格式错误返回-1*/
int analysis(char* buf,Card *cards,int n)
{
Cal_Stack* cal;
float sum=0,a,b,*num;
int flag=1,p1,p2,count=0,i;/*flag保存正负号信息*/
char op;
if(format(buf)==-1)
{
return -1;
}
cal=(Cal_Stack*)malloc(sizeof(Cal_Stack));
initCal(cal,1024);
push_op(cal,get_idx('#'));
num=(float*)malloc(sizeof(float)*n);
if(*buf=='-')
{
flag=0;
buf++;
}
while(*buf!=10||top_op(cal)!=6)/*字符串未被读完或操作符栈未被清空*/
{
if(isdigit(*buf))/*读入数据*/
{
sum=0;
if(flag!=0)
{
while(isdigit(*buf))
{
sum=sum*10+*buf-'0';
buf++;
}
}
else
{
while(isdigit(*buf))
{
sum=sum*10-*buf-'0';
buf++;
}
}
push_num(cal,sum);
num[count++]=flag==0?-sum:sum;/*表达式中出现的数字全部记录下来,以便校验表达式合法性*/
flag=1;
}
else/*读入操作符*/
{
p1=get_priority(top_op(cal),1);
p2=get_priority(get_idx(*buf),0);
if(p1>p2)/*根据优先级决定要执行的操作*/
{
b=top_num(cal);
pop_num(cal);
a=top_num(cal);
pop_num(cal);
op=top_op(cal);
pop_op(cal);
push_num(cal,figure(a,b,op));
}
else if(p1<p2)
{
push_op(cal,get_idx(*buf));
if(*buf!=10)
buf++;
}
else
{
pop_op(cal);
if(*buf!=10)
buf++;
}
}
}
for(i=0,a=1,b=1;i<n;i++)/*计算校验码*/
{
a*=cards[i].face;
b*=num[i];
}
if(equal(a,b)==0)/*校验码不一致*/
{
printf("错误,必须使用给定的卡牌\n");
}
else if(equal(top_num(cal),GOAL_SCORE)==1)
{
printf("%d点!正确!\n",GOAL_SCORE);
free(num);
delCal(cal);
return 1;
}
else
printf("%.0f点,错误~\n",top_num(cal));
free(num);
delCal(cal);
return 0;
}
/*过程:初始化树*/
void initTree(Cal_Tree *tree,Card *cards,int n)
{
int i;
for(i=0;i<n;i++)
{
tree[i].lchild=-1;
tree[i].rchild=-1;
tree[i].parent=-1;
tree[i].op=-1;
tree[i].val=cards[i].face;
}
for(i=2*n-2;i>=n;i--)
{
tree[i].lchild=-1;
tree[i].rchild=-1;
tree[i].parent=-2;
tree[i].op=-1;
tree[i].val=0;
}
}
/*过程:合并两个子树*/
void catTree(Cal_Tree *tree,int l,int r,int p,char op)
{
tree[p].lchild=l;
tree[p].rchild=r;
tree[p].op=op;
tree[p].val=figure(tree[l].val,tree[r].val,get_idx(op));
tree[p].parent=-1;
tree[l].parent=p;
tree[r].parent=p;
}
/*过程:分离二叉树*/
void splitTree(Cal_Tree *tree,int n)
{
tree[tree[n].lchild].parent=-1;
tree[tree[n].rchild].parent=-1;
tree[n].parent=-2;
}
/*过程:输出答案到缓冲区*/
void printAnswer(Cal_Tree *tree,char *buf,int *buf_size,int n)
{
char f=0;/*记录是否需要加上括号*/
if(*buf_size-strlen(buf)<1024)/*自动扩大缓冲区*/
{
realloc(buf,*buf_size*2);
*buf_size+=*buf_size;
}
if(tree[tree[n].lchild].op=='+'||tree[tree[n].lchild].op=='-')
f=1;
if(f)
sprintf(buf,"%s(",buf);
if(tree[tree[n].lchild].lchild!=-1)
{
printAnswer(tree,buf,buf_size,tree[n].lchild);
}
else
{
sprintf(buf,"%s%.0f",buf,tree[tree[n].lchild].val);
}
if(f)
{
sprintf(buf,"%s)",buf);
f=0;
}
sprintf(buf,"%s%c",buf,tree[n].op);
if(tree[tree[n].rchild].op=='+'||tree[tree[n].rchild].op=='-')
f=1;
if(f)
sprintf(buf,"%s(",buf);
if(tree[tree[n].rchild].lchild!=-1)
{
printAnswer(tree,buf,buf_size,tree[n].rchild);
}
else
{
sprintf(buf,"%s%.0f",buf,tree[tree[n].rchild].val);
}
if(f)
sprintf(buf,"%s)",buf);
}
/*过程:穷举+回溯,找到合理的解,并调用输出函数*/
void tryAnswer(Cal_Tree *tree,char *buf,int *buf_size,int n,int max,int goal)
{
int i,j;
if(n==max)
if(equal(tree[n].val,goal))
{
printAnswer(tree,buf,buf_size,max);
sprintf(buf,"%s\n",buf);
}
for(i=0;i<n;i++)
{
if(tree[i].parent!=-1)
continue;
for(j=i+1;j<=n;j++)
{
if(tree[j].parent!=-1)
continue;
catTree(tree,i,j,n+1,'+');
tryAnswer(tree,buf,buf_size,n+1,max,goal);
splitTree(tree,n+1);
catTree(tree,i,j,n+1,'*');
tryAnswer(tree,buf,buf_size,n+1,max,goal);
splitTree(tree,n+1);
catTree(tree,i,j,n+1,'-');
tryAnswer(tree,buf,buf_size,n+1,max,goal);
splitTree(tree,n+1);
catTree(tree,j,i,n+1,'-');
tryAnswer(tree,buf,buf_size,n+1,max,goal);
splitTree(tree,n+1);
if(equal(tree[j].val,0))
{
catTree(tree,i,j,n+1,'/');
tryAnswer(tree,buf,buf_size,n+1,max,goal);
splitTree(tree,n+1);
}
if(equal(tree[i].val,0))
{
catTree(tree,j,i,n+1,'/');
tryAnswer(tree,buf,buf_size,n+1,max,goal);
splitTree(tree,n+1);
}
}
}
}
/*函数:回溯入口,返回存储答案的缓冲区*/
char* giveAnswer(Card *cards,int n)
{
Cal_Tree *tree=(Cal_Tree*)malloc(sizeof(Cal_Tree)*(n*2-1));
char *buf=(char*)malloc(sizeof(char)*65536);
int *buf_size=(int*)malloc(sizeof(int));
*buf_size=1024;
initTree(tree,cards,n);
strcpy(buf,"");
tryAnswer(tree,buf,buf_size,n-1,n*2-2,GOAL_SCORE);
free(tree);
free(buf_size);
return buf;
}
int main()
{
Pile *pile=(Pile*)malloc(sizeof(Pile));
Card cards[CARD_SUM];
char buf[1024]={'\0'},*tmp;
int flag;
initCards(pile);
shuffleCards(pile);
printf("############################\n");
printf("##--------二十四点--------##\n");
printf("## V1.02 By.ZaneYork ##\n");
printf("############################\n");
while(1)
{
flag=0;
if(buf[0]!='d')
{
printf("按任意键开始发牌\n");
_getch();
}
else
free(tmp);
cls(GetStdHandle(STD_OUTPUT_HANDLE));
while(1)/*对于无解的组合,重新发牌*/
{
dealCards(pile,cards,CARD_SUM);
tmp=giveAnswer(cards,CARD_SUM);
if(strcmp(tmp,"")>0)
break;
}
showCards(cards,CARD_SUM);
while(flag!=1)
{
printf("请输入算式:(输入:d.换牌 a.答案 c.清屏 q.退出)\n");
fgets(buf,1024,stdin);
if(buf[0]=='d'||buf[0]=='q'||buf[0]=='a')
break;
else if(buf[0]=='c')
{
cls(GetStdHandle(STD_OUTPUT_HANDLE));
showCards(cards,CARD_SUM);
continue;
}
flag=analysis(buf,cards,CARD_SUM);
if(flag==-1)
printf("表达式格式错误\n");
}
if(buf[0]=='a')
{
printf("%s",tmp);
free(tmp);
}
else if(buf[0]=='q')
break;
}
free(tmp);
delCards(pile);
return 0;
}