信任的进化网址:>信任的进化 (dccxi.com)
本文是讲解如何用C语言实现《信任的进化》此游戏的逻辑算法(游戏有趣引人深思,建议游玩后再看本文章)
游戏规则:首先分别输入每种角色的数量,和比赛次数,之后每个角色都会和除自己之外的角色进行信任比赛,每场比赛后,硬币最少的将被删去,硬币最高的将会被复制一个并补上被删除的最低分角色的位置。整个游戏直到比赛次数到了才会停止,除此之外不会停下。
比赛规则:比赛的双方角色会进行信任选择,如果选择了合作,角色将消耗一个硬币。如果双方都选择了合作,则会获得3个硬币,除去消耗的硬币净收入为2个。如果有一方选择了欺骗,则被欺骗的那一方将什么都不会获得,而欺骗对方的角色将会获得3个硬币。
一.每种角色的初始化并打印菜单
首先,游戏中有五个角色,分别复读机,小红,油条,黑帮和福尔摩斯
五个角色行动逻辑分别为:
复读机:第一回合会合作,之后会一直模仿对方上一轮的行动。
小红:会一直和对方合作。
油条:会一直欺骗对方。
黑帮:一开始会和对方合作,如果遭受到欺骗则会一直欺骗对方。
福尔摩斯:前四轮分别会合作,欺骗,合作,合作。如果在前四轮之内遭受对方的欺骗,则四轮之后将会变成复读机,如果前四轮之内没有遭受欺骗,则四轮之后将会变成油条 。
所以,每个角色应该包含的数据有:名字,硬币数量,角色的选择,因为福尔摩斯前四轮和之后的轮数行动逻辑不一样,所以还应该加入轮数的数据。
因此,这里我选择用结构体来存储每个角色的数据。
首先,为了文件里的代码不特别杂乱,我先分别1个头文件用来声明函数
7个源文件,分别为执行文件,函数文件,和5个分别存储各自角色代码的文件。
如下图
编辑
在头文件中,我先定义了结构体
再在不同角色的文件里写下各自的初始化函数
struct player { int score;//硬币的数量 int choice;//每个人的选择,1为合作,0为欺骗 int n;//轮数 char name[100];//用来存储自己的名字 };
复读机
//初始化复读机 void InitFuduji(struct player* Fuduji) { Fuduji->choice = 1; Fuduji->score = 0; Fuduji->n = 0; strcpy(Fuduji->name, "Fuduji"); }
小红
//对小红进行初始化 void Initxiaohong(struct player* xiaohong) { xiaohong->choice = 1; xiaohong->score = 0; xiaohong->n = 0; strcpy(xiaohong->name, "xiaohong"); }
黑帮
//对黑帮进行初始化 void Initheibang(struct player* heibang) { heibang->choice = 1; heibang->score = 0; heibang->n = 0; strcpy(heibang->name, "heibang"); }
福尔摩斯
//初始化福尔摩斯 void Initfuer(struct player* fuer) { fuer->choice = 1; fuer->score = 0; fuer->n = 1;//福尔摩斯需要记录轮数,所以初始化轮数为1 strcpy(fuer->name, "fuer"); }
在执行文件xiangmu.c中,我首先定义了5种角色,并将其初始化,且输入每种角色的数量
//打印游戏的开头 printf("欢迎来到信任游戏模拟\n"); struct player Fuduji; struct player xiaohong; struct player youtiao; struct player heibang; struct player fuer; //初始化每种角色 InitFuduji(&Fuduji); Initxiaohong(&xiaohong); Inityoutiao(&youtiao); Initheibang(&heibang); Initfuer(&fuer); printf("注意,人数请不要输入负数,否则游戏会退出\n"); printf("请输入复读机的数量:>"); int Fuduji_num = 0; scanf("%d", &Fuduji_num); printf("请输入小红的数量:>"); int xiaohong_num = 0; scanf("%d", &xiaohong_num); printf("请输入油条的数量:>"); int youtiao_num = 0; scanf("%d", &youtiao_num); printf("请输入黑帮的数量:>"); int heibang_num = 0; scanf("%d", &heibang_num); printf("请输入福尔摩斯的数量:>"); int fuer_num = 0; scanf("%d", &fuer_num); //判断人数输入是否有误 if (fuer_num < 0 || xiaohong_num < 0 || youtiao_num < 0 || heibang_num < 0 || Fuduji_num < 0) { printf("输入人数错误,游戏退出\n"); exit(-1); } if (fuer_num==0 && xiaohong_num == 0 && youtiao_num==0 && heibang_num==0 &&Fuduji_num== 0) { printf("游戏结束!\n"); exit(-1); }
之后便开始打印游戏的菜单,显示每种角色的人数。
void menu(struct player* Fuduji, struct player* xiaohong, struct player* youtiao, struct player* heibang, struct player* fuer, int Fuduji_num, int xiaohong_num, int youtiao_num, int heibang_num, int fuer_num) { printf("------------------------------------------------------------\n"); printf("-----复读机:%d --------------------小红:%d----------------\n",Fuduji_num,xiaohong_num); printf("-----油条:%d ----------------------黑帮:%d------------------\n",youtiao_num,heibang_num); printf("-----福尔摩斯:%d -------------------------------------------\n",fuer_num); printf("------------------------------------------------------------\n"); }
二.比赛的逻辑
重头戏,就是比赛时的逻辑
首先我们要知道,每个角色都要和除自己之外的角色比赛,所以即使我们每种角色只初始化了一个人,也可以完成比赛的整个流程。
首先我们先写下比赛判断双方选择的函数
void isscore(struct player* p1, struct player* p2)//判断胜负的函数 { if (p1->choice == 1 && p2->choice == 0) { p2->score += 3; p1->score -= 1; } if (p2->choice == 1 && p1->choice == 0) { p1->score += 3; p2->score -= 1; } if (p1->choice == 1 && p2->choice == 1) { p2->score += 2; p1->score += 2; } }
之后,再写下双方比赛时如何选择的函数,首先,因为我们在初始化每种角色的时候将角色的对应名字定义到各自结构体的char name[]中,所以在双方比赛时我们可以通过比较结构体字符数组中的名字来认清各自的身份。
复读机,黑帮可以在先用isscore函数比较一次后,在进行判断,这样相当于第一轮比赛结束。
isscore(p1, p2); //实现复读机的函数 if (strcmp(p1->name, "Fuduji") == 0) { p1->choice = p2->choice; } if (strcmp(p2->name, "Fuduji") == 0) { p2->choice = p1->choice; } //实现黑帮老大的函数 if ((strcmp(p1->name, "heibang") == 0) && (p2->choice == 0)) { p1->choice = 0; } if ((strcmp(p2->name, "heibang") == 0) && (p1->choice == 0)) { p2->choice = 0; }
小红与油条可以一直不用变,直接比较就行。
而福尔摩斯才是最麻烦的,为了判断前四轮是否有反击,所以我们可以创建一个int变量,如果反击则为1,不反击则为0,因为要前四轮要有不同的选择,所以要在isscore函数之前就已经判断先,并在比赛结束之后将轮数加1。
所以,福尔摩斯的代码如下:>
//注:int fuerp=0;已经在比赛函数之外定义了,因为之后还有别的函数需要用到 //实现福尔摩斯函数 //判断是否反击 if ((strcmp(p1->name, "fuer") == 0) && ((p1->n == 2) || (p1->n == 3) || (p1->n == 4) || (p1->n == 1))) { if (p2->choice == 0) { fuerp = 1; } } if ((strcmp(p2->name, "fuer") == 0) && ((p2->n == 2) || (p2->n == 3) || (p2->n == 4) || (p2->n == 1))) { if (p1->choice == 0) { fuerp = 1; } } //在不同轮数做出不同选择 if ((strcmp(p1->name, "fuer") == 0) && p1->n == 2) { p1->choice = 0; } if ((strcmp(p2->name, "fuer") == 0) && p2->n == 2) { p2->choice = 0; } if ((strcmp(p2->name, "fuer") == 0) && ((p2->n == 3) || (p2->n == 4))) { p2->choice = 1; } if ((strcmp(p1->name, "fuer") == 0) && ((p1->n == 3) || (p1->n == 4))) { p1->choice = 1; } if ((strcmp(p2->name, "fuer") == 0) && (p2->n > 4)) { if (fuerp == 1) { p2->choice = p1->choice; } else if (fuerp == 0) { p2->choice = 0; } } if ((strcmp(p1->name, "fuer") == 0) && (p1->n > 4)) { if (fuerp == 1) { p1->choice = p2->choice; } else if (fuerp == 0) { p1->choice = 0; } } isscore(p1, p2); //将轮数加1 if (strcmp(p1->name, "fuer") == 0) { p1->n++; } if (strcmp(p2->name, "fuer") == 0) { p2->n++; }
因此,双方比赛选择且结果的完整函数如下
//两者比赛 void paly(struct player* p1, struct player* p2) { //实现福尔摩斯函数 //判断是否反击 if ((strcmp(p1->name, "fuer") == 0) && ((p1->n == 2) || (p1->n == 3) || (p1->n == 4) || (p1->n == 1))) { if (p2->choice == 0) { fuerp = 1; } } if ((strcmp(p2->name, "fuer") == 0) && ((p2->n == 2) || (p2->n == 3) || (p2->n == 4) || (p2->n == 1))) { if (p1->choice == 0) { fuerp = 1; } } if ((strcmp(p1->name, "fuer") == 0) && p1->n == 2) { p1->choice = 0; } if ((strcmp(p2->name, "fuer") == 0) && p2->n == 2) { p2->choice = 0; } if ((strcmp(p2->name, "fuer") == 0) && ((p2->n == 3) || (p2->n == 4))) { p2->choice = 1; } if ((strcmp(p1->name, "fuer") == 0) && ((p1->n == 3) || (p1->n == 4))) { p1->choice = 1; } if ((strcmp(p2->name, "fuer") == 0) && (p2->n > 4)) { if (fuerp == 1) { p2->choice = p1->choice; } else if (fuerp == 0) { p2->choice = 0; } } if ((strcmp(p1->name, "fuer") == 0) && (p1->n > 4)) { if (fuerp == 1) { p1->choice = p2->choice; } else if (fuerp == 0) { p1->choice = 0; } } isscore(p1, p2); if (strcmp(p1->name, "fuer") == 0) { p1->n++; } if (strcmp(p2->name, "fuer") == 0) { p2->n++; } //实现复读机的函数 if (strcmp(p1->name, "Fuduji") == 0) { p1->choice = p2->choice; } if (strcmp(p2->name, "Fuduji") == 0) { p2->choice = p1->choice; } //实现黑帮老大的函数 if ((strcmp(p1->name, "heibang") == 0) && (p2->choice == 0)) { p1->choice = 0; } if ((strcmp(p2->name, "heibang") == 0) && (p1->choice == 0)) { p2->choice = 0; } }
三.完成一次比赛之后的成员选择,轮数恢复
再双方进行完比赛后,例如复读机起初的选择是合作,如果和油条进行比赛后,复读机的选择将变成欺骗,所以每一次比赛后我们都应该把每个成员选,轮数恢复成初始的数据,这样有利于下一次与其他成员进行比赛时选择的正确性。
恢复的代码如下
void hireplayer(struct player* p1, struct player* p2) { if (strcmp(p1->name, "Fuduji") == 0) { p1->choice = 1; } if (strcmp(p2->name, "Fuduji") == 0) { p2->choice = 1; } if (strcmp(p2->name, "heibang") == 0) { p2->choice = 1; } if (strcmp(p1->name, "heibang") == 0) { p1->choice = 1; } if (strcmp(p1->name, "fuer") == 0) { p1->choice = 1; p1->n = 1; fuerp = 0; } if (strcmp(p2->name, "fuer") == 0) { p2->choice = 1; p2->n = 1; fuerp = 0; } }
四.一次比赛中,成员多次比赛后数据的恢复
当某一位成员完成了与自己之外的其他成员比赛时,除了这位成员,其他的成员的分数也会因此改变,所以此时将那位已经完成全部比赛的成员作为结构体指针返回,再将所有的成员全部初始化一遍,这样就可以做到多次比赛总数据的恢复
例如在文件xioahong.c中写的小红比赛后恢复的代码
struct player* playxiaohong(struct player* Fuduji, struct player* xiaohong, struct player* youtiao, struct player* heibang, struct player* fuer, int Fuduji_num, int xiaohong_num, int youtiao_num, int heibang_num, int fuer_num) { int i = 0; if (xiaohong_num == 0) { return xiaohong; } for (i = 0; i < Fuduji_num; i++) { paly(Fuduji, xiaohong); } hireplayer(Fuduji, xiaohong); for (i = 0; i < xiaohong_num - 1; i++) { paly(xiaohong, xiaohong); } hireplayer(xiaohong, xiaohong); for (i = 0; i < youtiao_num; i++) { paly(xiaohong, youtiao); } hireplayer(xiaohong, youtiao); for (i = 0; i < heibang_num; i++) { paly(xiaohong, heibang); } hireplayer(xiaohong, heibang); for (i = 0; i < fuer_num; i++) { paly(xiaohong, fuer); } hireplayer(xiaohong, fuer); return xiaohong; InitFuduji(&Fuduji); Initxiaohong(&xiaohong); Inityoutiao(&youtiao); Initheibang(&heibang); Initfuer(&fuer); }
其他成员也是类似的,最终的比赛完整版代码如下图
void Competition(struct player* Fuduji, struct player* xiaohong, struct player* youtiao, struct player* heibang, struct player* fuer, int Fuduji_num, int xiaohong_num, int youtiao_num, int heibang_num, int fuer_num) { int i = 0; int j = 0; struct player* newFuduji=playFuduji(Fuduji, xiaohong, youtiao, heibang, fuer, Fuduji_num, xiaohong_num, youtiao_num, heibang_num, fuer_num); struct player* newxiaohong = playxiaohong(Fuduji, xiaohong, youtiao, heibang, fuer, Fuduji_num, xiaohong_num, youtiao_num, heibang_num, fuer_num); struct player* newyoutiao = playyoutiao(Fuduji, xiaohong, youtiao, heibang, fuer, Fuduji_num, xiaohong_num, youtiao_num, heibang_num, fuer_num); struct player* newheibang = playheibang(Fuduji, xiaohong, youtiao, heibang, fuer, Fuduji_num, xiaohong_num, youtiao_num, heibang_num, fuer_num); struct player* newfuer = playfuer(Fuduji, xiaohong, youtiao, heibang, fuer, Fuduji_num, xiaohong_num, youtiao_num, heibang_num, fuer_num); Fuduji = newFuduji; xiaohong = newxiaohong; youtiao = newyoutiao; heibang = newheibang; fuer = newfuer; }
这样可以成功比赛一次
五.比赛一次后增多减少
当一次比赛完成之后,我们要将硬币最少的给删去,硬币最多的给增加,这时候,我们可以创建两个结构体,一个max结构体里面的硬币数量为0,一个min结构体里的硬币数量为2147483647,阅历每个成员的硬币数量并和max的硬币与min的硬币做比较,如果硬币数量大于max,max的硬币就会被赋值跟他一样多,同理,如果硬币小于min的,min的硬币数量也会被赋值跟它一样多,这样阅历之后,便可以找到最大与最小硬币的的成员。
如下图
struct player* max = (struct player*)malloc(sizeof(struct player)); struct player* min = (struct player*)malloc(sizeof(struct player)); max->score = -1000; min->score = 2147483647; //判断最大 if (Fuduji->score > max->score) { max->score= Fuduji->score; } if (xiaohong->score > max->score) { max->score = xiaohong->score; } if (youtiao->score > max->score) { max->score = youtiao->score; } if (heibang->score > max->score) { max->score = heibang->score; } if (fuer->score > max->score) { max->score = fuer->score; } //判断最小 if (Fuduji->score < min->score) { min->score = Fuduji->score; } if (xiaohong->score < min->score) { min->score = xiaohong->score; } if (youtiao->score < min->score) { min->score = youtiao->score; } if (heibang->score < min->score) { min->score = heibang->score; } if (fuer->score < min->score) { min->score = fuer->score; } //找最大最小 //找最小 if (Fuduji->score == min->score) { *Fuduji_num-=1; } if (xiaohong->score == min->score) { *xiaohong_num -= 1; } if (heibang->score == min->score) { *heibang_num -= 1; } if (fuer->score == min->score) { *fuer_num -= 1; } if (youtiao->score == min->score) { *youtiao_num -= 1; } //找最大 if ((Fuduji->score == max->score) && (*Fuduji_num != 0)) { *Fuduji_num+=1; } if ((xiaohong->score == max->score) && (*xiaohong_num != 0)) { *xiaohong_num += 1; } if ((heibang->score == max->score) && (*heibang_num != 0)) { *heibang_num+=1; } if (fuer->score == max->score) { *fuer_num += 1; } if ((youtiao->score == max->score) && (*youtiao_num != 0)) { *youtiao_num += 1; } }
六.比赛过后打印菜单与游戏的最终效果
之后,可以用menu将菜单再重新打印一遍
最终游戏效果如下图
编辑
总代码文件已经上传到gitee上面了,有需要的朋友可以自己去下载
gitee网址:C-practice-10-13 · Elysian剑彧/text.c - 码云 - 开源中国 (gitee.com)
能力有限,如有错误还请海涵。