#灵感来源于两篇文章关于其的解法,本文想进一步说明一些东西#
题目1:
背景:两个人每人发3张牌(各从一副牌中),每张牌包括花色(红桃(Heart)>黑桃(Spade)>方块(Diamond)>梅花(Club))和大小(从小到大依次是:2-10、J、Q、K、A),谁手上有最大的一张或多张(如果有相同的牌)牌谁获胜。
输入:A的3张牌(未排序)和B的3张牌(未排序)。(参见用例)
输出:A的3张牌的排序后的输出和B的3张牌的排序后的输出,以及A和B谁获胜。(参见用例)
思路:将相关的花色和数字大小进行量化,有利于后续的比较和排序,此处采用的是冒泡排序
#include<stdio.h>
int main()
{
//一是对于花色和大小的赋值处理,二是对花色和大小的权重处理,三是对'10'的单独处理
char A[3][3],B[3][3];
int A_score[3][2],B_score[3][2],A_sum[3],B_sum[3];
int i,j,temp;
char ctemp1,ctemp2;
//输入中只有10是特殊的,所以我们希望能将所有的输入都转变成两位的形式,这样方便后续的处理和比较
//注意:输入中无10,第三位为空格;若有10,则将第二位转变为'9'+1,第三位转变为0,并补上空格
for(i=0;i<3;i++)
{
scanf("%c%c%c",&A[i][0],&A[i][1],&A[i][2]);
if(A[i][1]=='1'&&A[i][2]=='0')
{
A[i][1]='9'+1;
A[i][2]=0;
if(i!=2)
scanf(" ");
}
}
scanf("\n");
for(i=0;i<3;i++)
{
scanf("%c%c%c",&B[i][0],&B[i][1],&B[i][2]);
if(B[i][1]=='1'&&B[i][2]=='0')
{
B[i][1]='9'+1;
B[i][2]=0;
if(i!=2)
scanf(" ");
}
}
//对于花色和大小进行赋值。由于花色的优先级更高,故我们将花色权重设置到百位,这样对大小的赋值不会影响到花色
//A_sum,B_sum即代表整体的分数,后面可以直接拿这个作比较
for(i=0;i<3;i++)
{
switch(A[i][0])
{
case 'H':
A_score[i][0]=400;
break;
case 'S':
A_score[i][0]=300;
break;
case 'D':
A_score[i][0]=200;
break;
case 'C':
A_score[i][0]=100;
break;
default:
printf("Input Error!\n");
return 0;
}
switch(A[i][1])
{
case '2':
A_score[i][1]=1;
break;
case '3':
A_score[i][1]=2;
break;
case '4':
A_score[i][1]=3;
break;
case '5':
A_score[i][1]=4;
break;
case '6':
A_score[i][1]=5;
break;
case '7':
A_score[i][1]=6;
break;
case '8':
A_score[i][1]=7;
break;
case '9':
A_score[i][1]=8;
break;
case '9'+1:
A_score[i][1]=9;
break;
case 'J':
A_score[i][1]=10;
break;
case 'Q':
A_score[i][1]=11;
break;
case 'K':
A_score[i][1]=12;
break;
case 'A':
A_score[i][1]=13;
break;
default:
printf("Input Error!\n");
return 0;
}
A_sum[i]=A_score[i][0]+A_score[i][1];
switch(B[i][0])
{
case 'H':
B_score[i][0]=400;
break;
case 'S':
B_score[i][0]=300;
break;
case 'D':
B_score[i][0]=200;
break;
case 'C':
B_score[i][0]=100;
break;
default:
printf("Input Error!\n");
return 0;
}
switch(B[i][1])
{
case '2':
B_score[i][1]=1;
break;
case '3':
B_score[i][1]=2;
break;
case '4':
B_score[i][1]=3;
break;
case '5':
B_score[i][1]=4;
break;
case '6':
B_score[i][1]=5;
break;
case '7':
B_score[i][1]=6;
break;
case '8':
B_score[i][1]=7;
break;
case '9':
B_score[i][1]=8;
break;
case '9'+1:
B_score[i][1]=9;
break;
case 'J':
B_score[i][1]=10;
break;
case 'Q':
B_score[i][1]=11;
break;
case 'K':
B_score[i][1]=12;
break;
case 'A':
B_score[i][1]=13;
break;
default:
printf("Input Error!\n");
return 0;
}
B_sum[i]=B_score[i][0]+B_score[i][1];
}
//有了前面的铺垫,这里就是一个简单的冒泡排序,需要关注的就是对于数字和字符的分离但同步的处理
for(i=0;i<2;i++)
{
if(A_sum[i]==A_sum[i+1]||B_sum[i]==B_sum[i+1])
{
printf("Input Error!\n");
return 0;
}
for(j=0;j<2-i;j++)
{
if(A_sum[j]<A_sum[j+1])
{
temp=A_sum[j];
A_sum[j]=A_sum[j+1];
A_sum[j+1]=temp;
ctemp1=A[j][0];
ctemp2=A[j][1];
A[j][0]=A[j+1][0];
A[j][1]=A[j+1][1];
A[j+1][0]=ctemp1;
A[j+1][1]=ctemp2;
}
if(B_sum[j]<B_sum[j+1])
{
temp=B_sum[j];
B_sum[j]=B_sum[j+1];
B_sum[j+1]=temp;
ctemp1=B[j][0];
ctemp2=B[j][1];
B[j][0]=B[j+1][0];
B[j][1]=B[j+1][1];
B[j+1][0]=ctemp1;
B[j+1][1]=ctemp2;
}
}
}
if(A_sum[0]==B_sum[0])
{
if(A_sum[1]==B_sum[1])
{
if(A_sum[2]==B_sum[2])
{
printf("Winner is X!\n");
}
else if(A_sum[2]>B_sum[2])
{
printf("Winner is A!\n");
}
else
{
printf("Winner is B!\n");
}
}
else if(A_sum[1]>B_sum[1])
{
printf("Winner is A!\n");
}
else
{
printf("Winner is B!\n");
}
}
else if(A_sum[0]>B_sum[0])
{
printf("Winner is A!\n");
}
else if(A_sum[0]<B_sum[0])
{
printf("Winner is B!\n");
}
//对第一步处理的还原,将'9'+1还原为'10'
for(i=0;i<3;i++)
{
if(A[i][1]=='9'+1)
{
A[i][1]='1';
A[i][2]='0';
}
if(B[i][1]=='9'+1)
{
B[i][1]='1';
B[i][2]='0';
}
}
//输出结果
printf("A:");
for(i=0;i<3;i++)
{
if(A[i][2]=='0')
printf(" %c%c%c",A[i][0],A[i][1],A[i][2]);
else
printf(" %c%c",A[i][0],A[i][1]);
}
printf("\n");
printf("B:");
for(i=0;i<3;i++)
{
if(B[i][2]=='0')
printf(" %c%c%c",B[i][0],B[i][1],B[i][2]);
else
printf(" %c%c",B[i][0],B[i][1]);
}
printf("\n");
return 0;
}
题目2(进阶版):
背景:两个人每人发3张牌(各从一副牌中),每张牌包括花色(红桃(Heart)>黑桃(Spade)>方块(Diamond)>梅花(Club))和大小(从小到大依次是:2-10、J、Q、K、A),胜负规则如下:同花顺(3张同花色的连牌,先比大小,再比花色,后同)>炸弹(3张相同大小的牌)>连牌(3张不同花色的连牌)>对子(两张相同大小的牌)>单牌。例如,红桃QKA>黑桃QKA>梅花567>方块234>AAA(红桃、方块、梅花)>AAA(黑桃、方块、梅花)>JQK(红桃、红桃、方块)>JQK(黑桃、红桃、方块)>AA2(梅花黑桃梅花)>QQJ(红桃梅花方块)>JQA(红桃红桃红桃)。注:A23不算连牌。
输入:A的3张牌(未排序)和B的3张牌(未排序)。(参见用例)
输出:A的3张牌的排序后的输出和B的3张牌的排序后的输出,以及A和B谁获胜。(参见用例)
代码如下,并非原作,仅学习讨论(稍作排版和解读)
#include<stdio.h>
#include<stdlib.h>
int check1(char);
int check2(char);
int cmp(char a[2],char b[2]);
int cmp2(char a[],char b[]);
void sort2(char a[3][3]);
void restore(char a[2]);
void change(char a[3][3]);
int card_type(char a[3][3]);
enum kind {straight_flush,three,straight,pair,single};
int main()
{
char a[3][3]={0},b[3][3]={0};
int i=0,j=0,error=0,flag;
for(i=0;i<3;i++)//input and check
{
scanf("%c%c%c",&a[i][0],&a[i][1],&a[i][2]);
if(a[i][2]=='0')
{
a[i][1]='9'+1;
a[i][2]=0;
if(i!=2)
scanf(" ");
}//输入有10,化为两位
if(check1(a[i][0])&&check2(a[i][1]))
continue;
else
{
error=1;
break;
}
}
scanf("\n");
for(i=0;i<3;i++)
{
scanf("%c%c%c",&b[i][0],&b[i][1],&b[i][2]);
if(b[i][2]=='0')
{
b[i][1]='9'+1;
b[i][2]=0;
if(i!=2)
scanf(" ");
}
if(check1(b[i][0])&&check2(b[i][1]))
continue;
else
{
error=1;
break;
}
}
for(i=0;i<2;i++)//检查重复
{
for(j=i+1;j<3;j++)
{
if((a[i][0]==a[j][0]&&a[i][1]==a[j][1])||(b[i][0]==b[j][0]&&b[i][1]==b[j][1]))
error=1;
}
}
if(error)
printf("Input Error!\n");
else//valid input
{
change(a);
change(b);//adjust the point
sort2(a);
sort2(b);//sort
if(card_type(a)<card_type(b))
flag=1;//a牌型大
else if(card_type(a)>card_type(b))
flag=3;//a牌型小
else//同种牌型
{
for(i=0;i<3;i++)
{
if(cmp2(a[i],b[i])==1)
{
flag=1;
break;
}
else if(cmp2(a[i],b[i])==3)
{
flag=3;
break;
}
else
{
flag=2;
continue;
}
}
}
if(flag==1)
{
printf("Winner is A!\nA:");
}
else if(flag==2)
{
//printf("Winner is X!\nA:");
printf("Draw!\nA:");
}
else if(flag==3)
{
printf("Winner is B!\nA:");
}
for(i=0;i<3;i++)//恢复
{
restore(a[i]);
restore(b[i]);
}
for(i=0;i<3;i++)
{
if(a[i][2]=='0')
printf(" %c%c%c",a[i][0],a[i][1],a[i][2]);
else
printf(" %c%c",a[i][0],a[i][1]);
}
printf("\nB:");
for(i=0;i<3;i++)
{
if(b[i][2]=='0')
printf(" %c%c%c",b[i][0],b[i][1],b[i][2]);
else
printf(" %c%c",b[i][0],b[i][1]);
}
putchar(10);
}
system("pause");
return 0;
}
int check1(char card)//check the suits
{
if(card!='H'&&card!='S'&&card!='D'&&card!='C')
return 0;//ERROR
else
return 1;
}
int check2(char card)//check the points
{
if(('2'<=card&&card<='9'+1)||card=='J'||card=='Q'||card=='K'||card=='A')
return 1;
else
return 0;
}
int cmp(char a[2],char b[2])//花色优先//if the first is bigger,return 1;else if equal,return 2;else return 3
{
if(a[0]=='H')a[0]='T';if(b[0]=='H')
b[0]='T';//给红桃升格
if(a[0]>b[0])
return 1;
else if(a[0]==b[0])//the suit is the same
{
if(a[1]>b[1])
return 1;
else if(a[1]==b[1])
return 2;
else
return 3;
}
else
return 3;
}
int cmp2(char a[],char b[])//点数优先
{
if(a[0]=='H')a[0]='T';if(b[0]=='H')
b[0]='T';//给红桃升格
if(a[1]>b[1])
return 1;
else if(a[1]<b[1])
return 3;
else
{
if(a[0]>b[0])
return 1;
else if(a[0]<b[0])
return 3;
else
return 2;
}
}
void change(char a[3][3])//adjust the point
{
int i,j;
for(i=0;i<3;i++)
{
//if(a[i][1]=='1'&&a[i][2]=='0'){a[i][1]='9'+1;a[i][2]=0;}
if(a[i][1]=='J')
a[i][1]='9'+2;
else if(a[i][1]=='Q')
a[i][1]='9'+3;
else if(a[i][1]=='K')
a[i][1]='9'+4;
else if(a[i][1]=='A')
a[i][1]='9'+5;
}
}
void sort2(char a[3][3])
{
int i,j;char t1,t2;
for(i=0;i<2;i++)
{
for(j=0;j<2-i;j++)
{
if(cmp2(a[j],a[j+1])==3)
{
t1=a[j][0];
t2=a[j][1];
a[j][0]=a[j+1][0];
a[j][1]=a[j+1][1];
a[j+1][0]=t1;
a[j+1][1]=t2;
}
}
}
}
void restore(char a[])//由于传递指针改变原值应恢复
{
if(a[0]=='T')
a[0]='H';//给红桃升格
if(a[1]=='9'+1)
{
a[1]='1';
a[2]='0';
}
else if(a[1]=='9'+2)
a[1]='J';
else if(a[1]=='9'+3)
a[1]='Q';
else if(a[1]=='9'+4)
a[1]='K';
else if(a[1]=='9'+5)
a[1]='A';
}
int card_type(char a[3][3])//排序后判断牌型
{
if(a[0][0]==a[1][0]&&a[1][0]==a[2][0]&&a[0][1]==a[1][1]+1&&a[1][1]==a[2][1]+1)//同花顺
return 0;
else if(a[0][1]==a[1][1]&&a[1][1]==a[2][1])
return 1;//three of a kind
else if(a[0][1]==a[1][1]+1&&a[1][1]==a[2][1]+1)
return 2;//straight
else if(a[0][1]==a[1][1]||a[0][1]==a[2][1]||a[1][1]==a[2][1])
return 3;//pair
else
return 4;//single
}
-
main
函数:- 定义了两个3x3的字符数组
a
和b
,分别用于存储两副牌的花色和点数。 - 通过循环读取用户输入的牌,并对输入进行校验。
- 如果输入有误,输出错误信息;如果输入有效,继续进行牌型的调整、排序和比较。
- 根据比较结果输出胜负信息,并恢复牌的原始表示形式后输出。
- 定义了两个3x3的字符数组
-
check1
函数:- 检查输入的花色是否合法(只能是'H'、'S'、'D'、'C')。
-
check2
函数:- 检查输入的点数是否合法(2-10, J, Q, K, A)。
-
cmp
函数:- 比较两个牌的花色和点数,返回1表示第一个牌大,2表示相等,3表示第二个牌大。
-
cmp2
函数:- 类似于
cmp
函数,但是以点数为优先比较。
- 类似于
-
change
函数:- 将牌的点数调整为一个统一的范围内,例如将J、Q、K、A分别转换为11、12、13、14。
-
sort2
函数:- 对牌进行排序,使得点数大的牌在前。
-
restore
函数:- 将调整后的牌恢复到原始的表示形式。
-
card_type
函数:- 判断牌型,返回不同的值表示不同的牌型(同花顺、三条、顺子、对子、散牌)。
以下是一些不太成熟的个人意见:
使用循环来简化代码: 在change和restore函数中,可以使用循环来简化代码,避免重复的if-else语句
增加输入验证: 在读取输入后,可以再次验证输入的牌是否在合法的范围内,例如花色是否为'H', 'S', 'D', 'C',点数是否为'2'到'9','T', 'J', 'Q', 'K', 'A'。
使用更健壮的比较函数: cmp和cmp2函数可以合并为一个函数,使用一个参数来决定是优先比较花色还是点数。
增加异常处理: 在读取输入时,可以增加异常处理,例如使用try-catch块来捕获可能的输入输出错误。
于是我对代码进行了如下修改(不一定对、也不到一定好,只是分享一下不成熟的想法而已)
合并 cmp 和 cmp2 函数:将创建一个新的函数 compare_cards,它将接受额外的参数来决定是优先比较花色还是点数。
使用标准库函数:我们将使用 strcmp 来比较字符串,但在这个特定的例子中,由于我们比较的是单字符的花色和点数,strcmp 并不适用。不过,可以简化 change 和 restore 函数,使用查找表来转换点数。
简化 change 和 restore 函数:使用查找表来简化这些函数
#include <stdio.h>
#include <stdlib.h>
// 定义点数和花色的枚举
typedef enum
{
CLUBS, DIAMONDS, HEARTS, SPADES
} Suit;
typedef enum
{
TWO = 2, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK = 11, QUEEN = 12, KING = 13, ACE = 14
} Value;
// 函数声明
int check_card(char suit, char point);
void adjust_cards(char a[3][3]);
void restore_cards(char a[3][3]);
int compare_cards(char a[2], char b[2], int compare_suit_first);
int determine_hand_rank(char a[3][3]);
void sort_cards(char a[3][3]);
int main() {
char a[3][3] = {0}, b[3][3] = {0};
int i = 0, j = 0, error = 0, flag;
// 输入和检查
for (i = 0; i < 3; i++)
{
scanf("%c%c%c", &a[i][0], &a[i][1], &a[i][2]);
if (!check_card(a[i][0], a[i][1]))
{
error = 1;
break;
}
if (a[i][2] == '0')
{
a[i][1] = '9' + 1;
a[i][2] = 0;
}
}
scanf("\n");
for (i = 0; i < 3; i++)
{
scanf("%c%c%c", &b[i][0], &b[i][1], &b[i][2]);
if (!check_card(b[i][0], b[i][1]))
{
error = 1;
break;
}
if (b[i][2] == '0')
{
b[i][1] = '9' + 1;
b[i][2] = 0;
}
}
// 检查重复
for (i = 0; i < 2; i++)
{
for (j = i + 1; j < 3; j++)
{
if ((a[i][0] == a[j][0] && a[i][1] == a[j][1]) || (b[i][0] == b[j][0] && b[i][1] == b[j][1]))
{
error = 1;
}
}
}
if (error)
{
printf("Input Error!\n");
}
else
{
adjust_cards(a);
adjust_cards(b);
sort_cards(a);
sort_cards(b);
flag = determine_hand_rank(a) - determine_hand_rank(b);
if (flag < 0)
printf("Winner is A!\nA:");
else if (flag > 0)
printf("Winner is B!\nA:");
else
{
printf("Draw!\nA:");
flag = 2; // Draw
}
for (i = 0; i < 3; i++)
{
restore_cards(a);
restore_cards(b);
if (a[i][2] == '0')
printf(" %c%c%c", a[i][0], a[i][1], a[i][2]);
else
printf(" %c%c", a[i][0], a[i][1]);
}
printf("\nB:");
for (i = 0; i < 3; i++)
{
if (b[i][2] == '0')
printf(" %c%c%c", b[i][0], b[i][1], b[i][2]);
else
printf(" %c%c", b[i][0], b[i][1]);
}
putchar(10);
}
system("pause");
return 0;
}
int check_card(char suit, char point)
{
if (suit != 'H' && suit != 'S' && suit != 'D' && suit != 'C')
return 0;
if ((point >= '2' && point <= '9') || point == 'T' || point == 'J' || point == 'Q' || point == 'K' || point == 'A')
return 1;
return 0;
}
void adjust_cards(char a[3][3])
{
for (int i = 0; i < 3; i++)
{
switch (a[i][1])
{
case 'T': a[i][1] = '9' + 2; break;
case 'J': a[i][1] = '9' + 3; break;
case 'Q': a[i][1] = '9' + 4; break;
case 'K': a[i][1] = '9' + 5; break;
case 'A': a[i][1] = '9' + 6; break;
}
}
}
void restore_cards(char a[3][3])
{
for (int i = 0; i < 3; i++)
{
switch (a[i][1])
{
case '9' + 2: a[i][1] = 'T'; break;
case '9' + 3: a[i][1] = 'J'; break;
case '9' + 4: a[i][1] = 'Q'; break;
case '9' + 5: a[i][1] = 'K'; break;
case '9' + 6: a[i][1] = 'A'; break;
}
}
}
int compare_cards(char a[2], char b[2], int compare_suit_first)
{
if (compare_suit_first)
{
if (a[0] == b[0])
{
return (a[1] > b[1]) - (a[1] < b[1]);
}
else
{
return (a[0] > b[0]) - (a[0] < b[0]);
}
}
else
{
if (a[1] == b[1])
{
return (a[0] > b[0]) - (a[0] < b[0]);
}
else
{
return (a[1] > b[1]) - (a[1] < b[1]);
}
}
}
void sort_cards(char a[3][3])
{
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2 - i; j++)
{
if (compare_cards(a[j], a[j + 1], 1) == 3)
{
char temp[3];
temp[0] = a[j][0];
temp[1] = a[j][1];
temp[2] = a[j][2];
a[j][0] = a[j + 1][0];
a[j][1] = a[j + 1][1];
a[j][2] = a[j + 1][2];
a[j + 1][0] = temp[0];
a[j + 1][1] = temp[1];
a[j + 1][2] = temp[2];
}
}
}
}
int determine_hand_rank(char a[3][3])
{
int values[3] = {0};
for (int i = 0; i < 3; i++)
{
switch (a[i][1])
{
case '2': values[i] = 2; break;
case '3': values[i] = 3; break;
case '4': values[i] = 4; break;
case '5': values[i] = 5; break;
case '6': values[i] = 6; break;
case '7': values[i] = 7; break;
case '8': values[i] = 8; break;
case '9': values[i] = 9; break;
case 'T': values[i] = 10; break;
case 'J': values[i] = 11; break;
case 'Q': values[i] = 12; break;
case 'K': values[i] = 13; break;
case 'A': values[i] = 14; break;
}
}
// 检查同花顺、三条、顺子、对子、散牌
// 这里需要实现具体的牌型判断逻辑
return 0; // 返回牌型的排名
}
这段代码是一个用于比较两副扑克牌大小的程序。它包含了以下几个关键部分:
-
输入和验证:
- 程序首先读取用户输入的两副牌,每副牌由三张牌组成。
- 使用
check_card
函数验证每张牌的花色和点数是否合法。 - 检查是否有重复的牌。
-
调整牌的点数:
adjust_cards
函数将牌的点数从字符形式转换为数值形式,以便进行比较。例如,将 'J' 转换为 '11'。
-
排序:
sort_cards
函数对每副牌进行排序,以便从大到小比较。
-
比较牌型:
compare_cards
函数比较两张牌的大小,可以指定是先比较花色还是点数。determine_hand_rank
函数判断牌型的等级,如同花顺、三条等。
-
恢复牌的原始形式:
restore_cards
函数将牌的点数从数值形式转换回字符形式,以便于输出。
-
输出结果:
- 根据比较结果,输出哪副牌获胜或者是否为平局。
- 输出牌的原始形式。
-
错误处理:
- 如果输入无效,程序会输出错误信息并终止。
这个程序的改进点包括:
- 合并了
cmp
和cmp2
函数为compare_cards
,增加了灵活性。 - 使用查找表简化了
adjust_cards
和restore_cards
函数。 - 增加了对输入完整性和合法性的检查。
- 提供了更详细的错误信息。
请注意,这个程序是一个简化的示例,实际的扑克牌比较程序可能需要更复杂的逻辑来处理所有的牌型和比较规则。此外,determine_hand_rank
函数需要实现具体的牌型判断逻辑,这部分代码在这里没有详细实现。