小猫钓鱼代码实现

  • 说明
  • 运行结果
  • 前言
  • 实现分析
  • 具体实现
      • 首先来搭建一个框架:
      • 数据结构初始化
      • 轮流出牌判断输赢
      • 谁赢得了游戏
    • 代码思考
  • 完整代码
  • 总结

说明

纸牌小游戏使用纯C语言实现,使用VS2015编译测试,涉及的主要数据结构为 栈和队列 及C语言基础语法,这个是适合数据结构的初学者作为练习的小程序,其原型来自《啊哈!算法》,我对其进行了一点改进,增加了相应的输入检查,区分了函数以及其他的一些细节。解释和注释我都写的非常详细了,希望可以帮助理解~ o( ̄▽ ̄)ブ

运行结果

这是我修改后的代码的运行结果:

前言

先简单介绍一下这个简单的纸牌游戏——小猫钓鱼,就是我们将一副扑克牌(不含大小王)平均分成两份,两人每人一份,我们叫这两人分别为 甲 和 乙 吧,可以让 甲 先拿出手中的第一张扑克牌放到桌上,然后 乙 也拿出手中的第一张扑克牌,放在 甲 打出的扑克牌的上面,这样两人交替出牌,在出牌时,如果某人打出的牌,与桌面上的某张牌的牌面相同,即可将两张相同的牌及中间所夹的牌全部取走,并依次放到自己手牌的末尾,当任意一人手中的牌全部出完时,获得胜利,游戏结束。

实现分析

其实游戏规则很简答,下面我们来想一下,该怎样用代码来逐个实现游戏功能,我们来分析一下每个的基本操作:

比如以甲为例,它主要会有两种操作,出牌和赢牌,如果要抽象成我们可以解决的代码,我们就需要一个合适的“容器”来实现可以动态的表示 甲 手中牌的变化,不难想到,用队列*** 这个数据结构可以很好地实现我们所需要的功能,出牌就是出队*,赢牌就是入队,简直是完美符合游戏要求。
下面就是桌面上的牌,该用什么来实现呢,我们需要做的是将相同的牌匹配,涉及到匹配,当然是用 来实现啦,匹配括号算法还记得吗?在这里,每打出一张牌放到桌子上就相当于一个元素入栈,当有人赢牌从桌子上拿牌的时候,就相当于元素出栈。
最后我们来想一下,如何实现赢牌的操作呢,一个比较容易想到的就是枚举桌面上的每一张牌,与之进行比较,如果一样,则判定为赢牌,可以取走对应的牌数,(我感觉这个应该比较好实现,所以我在下面代码中使用下面这种方法)如果进一步想一下的话,我们也可以使用数组来记录牌的信息,毕竟牌是有限的嘛,如果桌面上有了一张牌,我们就将数组对应的值设置为1,如果某张牌被取走了,我们就将其设置为0,这样我们就不需要遍历数组了,而只是使用一个if条件判断就可以实现了,(这个思想是不是有点熟悉,和我之前写的一个排序算法思想很相似最简单的排序—桶排序的实现)当然了,要试下这个条件判断,我们还得有个对应规则:将牌与数组对应起来(当然是用纯数组容易实现,如果非要使用字符也不是不可以,就是在结果匹配那里会不如数组方便),下面是我的对应规则:

好了,截止目前我们就已经分析清楚,我们要实现这个功能所需要的具体材料了:两个队列,一个栈,还有一个数组,下面我们就可以着手搭建了!

具体实现

  1. 首先来搭建一个框架:

我们需要一个简单的操作界面,显示一些简单的信息,然后构造出我们需要的主要数据结构——两个队列和一个栈,
下面是一个简单的显示界面:

queue q1, q2;  //甲 和 乙 手上的牌stack s;  //桌子上的牌int i = 0;printf(" ==============================\n");printf(" =======小猫钓鱼游戏开始=======\n");printf(" ==============================\n");

下面是需要用到的数据结构:关于代码的含义我在注释中都有比价详细的解释了,当然了,为了代码以后的可读性和修改度,我将一部分常量使用define定义了,关于大小我也在代码注释中解释了,这个思想其实在之前的最简单的排序—桶排序的实现中,就很好理解了,当然了在这里也很好理解( ̄︶ ̄*))

#define q_size 1000
#define s_size 14
typedef struct queue
{int data[q_size];  //存储队列中的元素,这里主要是为了防止越界,故设置的比较大int head; //存储队头int tail;  //存储队尾
}queue;typedef struct stack
{int data[s_size];//存储栈中的元素,这里设置为14,因为只有13中不同的牌面,所以桌上最多有13张牌,14就足够了int top; //存储栈顶
}stack;

在实现基本的框架之后,就要进行简单的初始化了,关于初始化,我是采用不同的函数来实现的,这样可以很好的进行模块化设计。

  1. 数据结构初始化

先想一下我们需要初始化的量,首先是两个用户的队列,其次是桌面的栈

void init_q(queue *p);  //初始化牌
void init_s(stack *p)
void init_b(int p[],int *max_size);   //初始化

下面是具体实现的三个函数,这个其实算是在学习数据结构时必须要掌握的内容了,而且关于初始化,我们一定是要传递定义好变量的地址,否则的话是不能做到顺利初始化的(因为传递值的在离开被调函数时就直接被系统销毁了,根本不会回到main函数)。
当然了在下面的函数中可以注意到一点,每个初始时函数我都注释了一个 初始化完毕的 输出语句,其实我个人感觉,类似这样的输出语句其实在调试代码中很有用处,可以很好的知道每个函数的执行情况,是个很不错的宏观调试方法了(。・∀・)ノ゙

void init_q(queue *p)
{p->head = 1;p->tail = 1;//初始化队列为空,此时两人手中还没有牌//printf("队列初始化完毕!\n");}void init_s(stack *p)
{p->top = 0;//初始化栈为空,此时桌面上还没有牌//printf("栈初始化完毕!\n");
}
void init_b(int p[],int *max_size)
{for (int i = 0; i <= *max_size; i++){p[i] = 0;}//printf(" 桌面初始化完毕!\n");}

在初始化完成之后,我们就可以对其进行输入数据了,我在这里是把需要在屏幕上显示的框架类的内容放在main函数中,其具体的代码我都会选择封装在函数中调用,下面是向其中输入数据的main函数中的部分:

 int max_size = 6;printf(" 初始时每人手中的牌数: ");scanf("%d", &max_size);while (max_size <= 1 || max_size > 26){printf(" 牌数设置错误(2-26中间的数字),请重新设置每人手中的牌数:");scanf("%d", &max_size);}
//人物摸牌printf(" 请输入甲手中的牌:\n");read_s(&q1, &max_size);printf(" 请输入乙手中的牌:\n");read_s(&q2, &max_size);printf(" 展示甲手中的牌:");show(&q1);printf(" 展示乙手中的牌:");show(&q1);

下面是上面涉及的函数的实现:

void read_s(queue *p, int *max_size)
{for (int i = 1; i <=*max_size; i++){printf(" 请输入ta的第%d张牌:", i);scanf("%d", &p->data[p->tail]);while (p->data[p->tail]<0||p->data[p->tail]>13){printf(" 输入数据有误(1-13),请重新输入:");scanf("%d", &p->data[p->tail]);}p->tail++;}printf("\n");printf("\n");
}
void show(queue *p)
{for (int i = p->head; i <= p->tail - 1; i++){printf("%d\t", p->data[i]);}printf("\n");
}

好了,截止现在,我们关于游戏前期所有的准备工作都已经实现了:我们已经有了合适的 数据结构来表示各种数据,两个人手中已经有了可以打出的手牌,下面就是两个人轮流出牌,然后判断输赢的过程了。

  1. 轮流出牌判断输赢

我们要判断输赢,下面我用甲来作为一个例子说明一下,乙赢的情形和甲是一样的,只需要把甲的代码的所有信息都变成乙的信息就可以了。我们可以假设 甲 先出牌,用一个临时变量t 来表示甲出的牌,这个就作为q1 队列的队首,同时当这张牌被打出的时候,我们就可以使用桌面这个数组来存储它了,
还得判断甲打出的牌是否可以赢得桌子上的牌,就是判断桌子上的牌与t 是否有相同的,我所采用的是和数组(book[ ],这里的book是有记录、登记的意思,当然使用mark等也可以,主要是为了代码的可读性要选用易于理解的变量名)中是否有一样的来判断是否可以拿走桌子上的牌。
下面来简单解释一下这个数组的使用:如果桌面上有一个牌面为3的牌,那就需要把数组book[3] 的值设置为1,这样就表示桌面上牌面为 3 的值就已经有了,如果这张牌面为3 的牌被拿走,我们就需要及时的把book[3] 重新设置为0,这样就表示桌面上已经没有牌面为3 的值了,所以我们如果要判断甲打出的某张牌在桌面上有没有,我们只需要一个简单的if 条件判断就可以实现了,下面就是具体的实现代码:

while (q1.head < q1.tail && q2.head < q2.tail) //只要一方手中还有牌,则游戏就继续{int t = q1.data[q1.head];  // 这里用t来表示 甲 要打出的牌if (book[t] == 0)  //桌面上没有牌面为t的牌{//甲没有赢牌q1.head++; //把打出的牌出列s.top++; s.data[s.top] = t; //打出的牌放到桌子上,即入栈book[t] = 1;  //标记桌子上已经出现的牌}else{// 甲 赢牌q1.head++; //把打出的牌出列q1.data[q1.tail] = t; //因为赢牌了,所以要把该牌依次放到后面去用q1.tail++;while (s.data[s.top] !=t)  //把桌子上赢得的牌 统统依次放到手牌后面去{book[s.data[s.top]] = 0;  //取消标记q1.data[q1.tail] = s.data[s.top];  //放入队尾q1.tail++;s.top--;  //桌子上的牌减少了,所以要减1}}

乙赢牌和甲赢牌是一样的,就不再这里贴出了,我会把所有实现代码都放在最后的总体代码中。
出牌阶段实现完成之后,我们就可以思考游戏如何结束了,我所想的是只要有一个人的牌用完了,游戏就结束了,因此我在外面使用了一个while循环。

  1. 谁赢得了游戏

要判断谁赢得了游戏,我们就只需要判断谁没有牌了,然后输出胜利者的手牌,并显示一下桌面的手牌,下面是实现代码,详细注释在代码中:

if (q2.head==q2.tail){printf(" 甲赢");printf(" 甲手中的牌为:");show(&q1);if (s.top > 0){show_table(&s);}else{printf("\n 桌上已经没有牌了");printf("\n");}}else{printf(" 乙赢");printf(" 乙手中的牌为:");show(&q2);if (s.top > 0){show_table(&s);}else{printf("\n 桌上已经没有牌了");printf("\n");}}

下面是上面涉及的show_table函数的具体实现,其实还是打印内容,只要遍历就可以了,下面是实现代码:

void show_table(stack *p)
{printf("\n 桌上的牌为:");for (int i = 1; i <= p->top; i++){printf("%d\t", p->data[i]);}printf("\n");printf("\n");
}

好了,截止现在,关于小猫钓鱼这个具体实现我们就已经全部都实现完成了。

代码思考

我们在这个程序实现中,关于数据输入时的健壮性还是不错的,可以对输入数据进行有效的检查,但是如果实验足够多的数据的话,其实代码还是存在一个问题的,会导致游戏可能会永远运行下去,谁都无法赢得对方。比如我们如果输入这个数据,就会出现错误的结果:
这显然是错误的结果,这个问题还有待后续解决。

完整代码

下面是完整的具体实现代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define q_size 1000
#define s_size 14
//#define max_size //每日初始化时,手中的牌typedef struct queue
{int data[q_size];  //存储队列中的元素,这里主要是为了防止越界,故设置的比较大int head; //存储队头int tail;  //存储队尾
}queue;typedef struct stack
{int data[s_size];//存储栈中的元素,这里设置为14,因为只有13中不同的牌面,所以桌上最多有13张牌,14就足够了int top; //存储栈顶
}stack;void init_q(queue *p);  //初始化牌
void init_s(stack *p);
void read_s(queue *p, int *max_size);  //摸牌
void init_b(int p[],int *max_size);   //初始化
void show(queue *p);
void show_table(stack *p);int main()
{queue q1, q2;  //甲 和 乙 手上的牌stack s;  //桌子上的牌int i = 0;printf(" ==============================\n");printf(" =======小猫钓鱼游戏开始=======\n");printf(" ==============================\n");int max_size = 6;printf(" 初始时每人手中的牌数: ");scanf("%d", &max_size);while (max_size <= 1 || max_size > 26){printf(" 牌数设置错误(2-26中间的数字),请重新设置每人手中的牌数:");scanf("%d", &max_size);}//初始化元素init_q(&q1); init_q(&q2);init_s(&s);  //人物摸牌printf(" 请输入甲手中的牌:\n");read_s(&q1, &max_size);printf(" 请输入乙手中的牌:\n");read_s(&q2, &max_size);printf(" 展示甲手中的牌:");show(&q1);printf(" 展示乙手中的牌:");show(&q1);printf("\n");//桌牌记录int book[s_size];init_b(book,&max_size);printf(" 游戏运行中,下面是游戏运行结果:\n");printf("\n");//游戏开始while (q1.head < q1.tail && q2.head < q2.tail) //只要一方手中还有牌,则游戏就继续{int t = q1.data[q1.head];  // 这里用t来表示 甲 要打出的牌if (book[t] == 0)  //桌面上没有牌面为t的牌{//甲没有赢牌q1.head++; //把打出的牌出列s.top++; s.data[s.top] = t; //打出的牌放到桌子上,即入栈book[t] = 1;  //标记桌子上已经出现的牌}else{// 甲 赢牌q1.head++; //把打出的牌出列q1.data[q1.tail] = t; //因为赢牌了,所以要把该牌依次放到后面去用q1.tail++;while (s.data[s.top] !=t)  //把桌子上赢得的牌 统统依次放到手牌后面去{book[s.data[s.top]] = 0;  //取消标记q1.data[q1.tail] = s.data[s.top];  //放入队尾q1.tail++;s.top--;  //桌子上的牌减少了,所以要减1}}t = q2.data[q2.head];  //乙出一张牌//判断是否能赢牌if (book[t] == 0)  //桌面上没有牌面为t的牌{//乙没有赢牌q2.head++; //把打出的牌出列s.top++;s.data[s.top] = t; //打出的牌放到桌子上,即入栈book[t] = 1;  //标记桌子上已经出现的牌}else{// 乙 赢牌q2.head++; //把打出的牌出列q2.data[q2.tail] = t; //因为赢牌了,所以要把该牌依次放到后面去用q2.tail++;while(s.data[s.top] != t)  //把桌子上赢得的牌 统统依次放到手牌后面去{book[s.data[s.top]] = 0;  //取消标记q2.data[q2.tail] = s.data[s.top];  //放入队尾q2.tail++;s.top--;  //桌子上的牌减少了,所以要减1}}}if (q2.head==q2.tail){printf(" 甲赢");printf(" 甲手中的牌为:");show(&q1);if (s.top > 0){show_table(&s);}else{printf("\n 桌上已经没有牌了");printf("\n");}}else{printf(" 乙赢");printf(" 乙手中的牌为:");show(&q2);if (s.top > 0){show_table(&s);}else{printf("\n 桌上已经没有牌了");printf("\n");}}return 0;
}void init_q(queue *p)
{p->head = 1;p->tail = 1;//初始化队列为空,此时两人手中还没有牌//printf("队列初始化完毕!\n");}void init_s(stack *p)
{p->top = 0;//初始化栈为空,此时桌面上还没有牌//printf("栈初始化完毕!\n");
}void read_s(queue *p, int *max_size)
{for (int i = 1; i <=*max_size; i++){printf(" 请输入ta的第%d张牌:", i);scanf("%d", &p->data[p->tail]);while (p->data[p->tail]<0||p->data[p->tail]>13){printf(" 输入数据有误(1-13),请重新输入:");scanf("%d", &p->data[p->tail]);}p->tail++;}printf("\n");printf("\n");
}void init_b(int p[],int *max_size)
{for (int i = 0; i <= *max_size; i++){p[i] = 0;}//printf(" 桌面初始化完毕!\n");}void show(queue *p)
{for (int i = p->head; i <= p->tail - 1; i++){printf("%d\t", p->data[i]);}printf("\n");
}void show_table(stack *p)
{printf("\n 桌上的牌为:");for (int i = 1; i <= p->top; i++){printf("%d\t", p->data[i]);}printf("\n");printf("\n");
}

总结

好了关于小猫钓鱼的完整实现就到这里了,其中对于队列和栈的使用,函数传递,数组思想的运行等都有不同程度的深入,这对于我们编程的提高有很大的帮助,当然了,这个程序现在也还有遗憾的地方,就是不能及时纠错退出,我会继续更新然后实现这个更完美的效果的!(>ω・* )ノ

感谢观赏,一起提高,慢慢变强。

简单的纸牌游戏——小猫钓鱼(详细解释实现)相关推荐

  1. 啊哈算法之纸牌游戏小猫钓鱼

    简述 本算法摘选自啊哈磊所著的<啊哈!算法>第二章第三节的题目--纸牌游戏小猫钓鱼.文中代码使用C语言编写,但是仔细看了一遍发现原书中有个细节是错误的,也就是说按照算法题目意思,原书中作者 ...

  2. 算法学习 2.3纸牌游戏——小猫钓鱼

    这是一章对前面的队列,栈的操作进行运用的一个实例.玩家A和玩家B玩纸牌游戏小猫钓鱼,游戏规则如下: 一副纸牌,平均分成两份,每人拿一份(这里就用数字代替了). 玩家A先拿出一张纸牌放在桌子上,玩家B再 ...

  3. 纸牌游戏——小猫钓鱼

    一.游戏规则 将一副扑克牌平均分成两份,每人拿一份.小哼先拿出手中的第一张扑克牌放在桌上,然后小哈也拿出手中的第一张扑克牌,并放在小哼刚打出的扑克牌的上面,就像这样两人交替出牌.出牌时,如果某人打出的 ...

  4. 纸牌游戏-----小猫钓鱼

    游戏规则 将一副扑克牌平均分成两份,每人拿一份.小哼先拿出手中的第一张扑克牌放桌上,然后小哈也拿出来手中的第一张扑克牌,并放在小哼刚打出来的扑克牌上边,就像这样两人交替出牌.出牌时,如果某人打出的牌与 ...

  5. 《啊哈算法》纸牌游戏---小猫钓鱼

    扑克游戏: 将一副扑克牌平均分成两份,每人拿一份.小哼先拿出手中的第一张扑克牌放在桌上,然后小哈也拿出手中的第一张放在桌上,就这样两人交替出牌. 出牌时,若某人打出的牌与桌面上的某张牌面相同,即将两张 ...

  6. 运用简单队列、栈实现简单纸牌游戏

    案例分析 我们知道栈和队列能实现很多算法. 但是作为初学者,想要实现那些复杂.高深的算法其实挺难的. 那我们能不用用简单队列和栈做一些简单的程序呢? 我们做一个简单的纸牌游戏. 现在假设有玩家A和玩家 ...

  7. python编程剪刀石头布思路_Python制作简单的剪刀石头布游戏

    关于程序相关的 您可以反复玩游戏,直到选择停止为止. 该程序跟踪获胜情况. 大小写无关紧要(即ROCK与Rock相同). 如果您输入的内容无效,程序会一直提示您,直到您输入有效的内容. 对项目进行编码 ...

  8. 【GDKOI训练】纸牌游戏(card)

    [GDKOI训练]纸牌游戏(card) 题目描述 Bessie 是一头非常喜欢纸牌的奶牛,虽然她没有大拇指,但她对纸牌有近乎痴迷的喜 爱.不幸的是,牛群中的其他牛都不是好的对手.他们的水平真的很差.他 ...

  9. C语言游戏---小猫钓鱼

    A和B两个同学玩简单的纸牌游戏,每人手里有n张牌,两人轮流出牌并依次排列在桌面上,每次出掉手里的第1张牌,出牌后如果发现桌面上有跟刚才打出的牌的数字相同的牌,则把从相同的那张牌开始的全部牌按次序放在自 ...

  10. python制作剪刀石头布_Python制作简单的剪刀石头布游戏

    关于程序相关的 您可以反复玩游戏,直到选择停止为止. 该程序跟踪获胜情况. 大小写无关紧要(即rock与rock相同). 如果您输入的内容无效,程序会一直提示您,直到您输入有效的内容. 对项目进行编码 ...

最新文章

  1. 哈希表的分类,创建,查找 以及相关问题解决
  2. PTA 基础程序编程集 7-2 然后是几点 C语言
  3. 池州市计算机专科,2021年3月安徽省池州市计算机等级考试时间
  4. 'utf-8' codec can't decode byte 0xff in position 0:
  5. php控件不显示,解决控件遮挡问题:关于有窗口元素和无窗口元素
  6. JavaScript 读取、写入Txt文档
  7. 使用 rocketmq-spring-boot-starter 来配置、发送和消费 RocketMQ 消息
  8. linux 网卡配置不一致,linux环境下,双网卡配置不同网段后,路由问题
  9. 南理工计算机博士 年薪_计算机专业的女博士毕业后,进入211大学当讲师,年薪曝光...
  10. Java Vector Capacity()方法与示例
  11. R in action读书笔记(3)-第六章:基本图形
  12. 【Thread】java类Thread中提供了检测线程是否中断的方法,说一说你的了解?
  13. 创作高水准的 3D 作品,苹果Mac设计师必备的4款3D软件
  14. oracle 自治事务异常不回滚,ORA-06519: 检测到活动的自治事务处理,已经回退
  15. c语言表达式优先级最高的是,C语言运算符优先级表
  16. 任务栏图标消失怎么办?三种方法教你快速恢复
  17. SQLyog安装地址
  18. 如何使用Matlab合并Excel表格
  19. 海康设备通过SDK获取和设置设备网络参数
  20. Android 判断手机设备是否是小米,华为,魅族设备

热门文章

  1. 量子计算与PKS信创体系首次融合,实现算力跨越
  2. UltraCompare Professional Version 7.20.0.1009 注册码
  3. 雷课堂(THUnderClass)——清华大学2020C++大作业个人项目记录与总结
  4. C语言绘制单项正弦电压波形图,正弦交流电压波形图为例讲解“五点法”画波形图的方法...
  5. 点击箭头 切图 html,css写箭头
  6. Linux从删库到跑路--常用命令
  7. 怎样在Word中添加批注?分享干货!word如何加入批注?
  8. bing 搜索引擎 无法访问 bug
  9. Edge地址栏搜索引擎换成Bing
  10. (休息几天)读米什金之货币银行学——金融市场工具