21点游戏的设计能够充分体现C语言结构化程序设计的思想,如果你是C语言的初学者,本文对你一定有帮助.
21点游戏的规则如下:扑克牌A、2、3、4、、、、、、10、J、Q、K、大小鬼分别代表数字1、2、3、4、、、、、、10、11、12、13、21;人和电脑轮流坐庄,庄家先发牌。胜利条件:将所取牌点数相加凑成 "和"是21,谁先凑成21就胜出,如果没有人凑到"和"是21,就定"和"最接近21且小于21就胜出。每人至多可取5张扑克牌。要注意"和"超过21者谁先爆掉也就是输了,如果都爆了,点数小的胜;当点数相同时,庄家胜。
首先,理清游戏的运行思路是非常关键的。面对众多杂乱的规则,可以先去除无关紧要的细节,这样细节与游戏总体的思路没有关系,只是影响某些过程的处理细节.比如: 每人至多可取5张扑克牌,注意"和"超过21者谁先爆掉也就是输了,如果都爆了,点数小的胜;当点数相同时,庄家胜。可以概括为”取牌规则和判定规则”,这样整个游戏其实就变成了轮流取牌并计分的过程.只是一次取牌结束后可以自行退出,只是不能取5次以上,然后最终用特定的规则来判定胜负.这样理解的游戏的整体思路后,即使加入再多的细节也能轻松应对.
程序编码的第一步是什么?是先声明变量?写主函数?我个人喜欢的是先完成子函数模块的设计.把子模块完成之后,更高级的模块就变得容易了.就像盖楼房一样,先堆砌基础的模块总让我很放心,而一开始就从上层开始让我有一种悬于空中的感觉。我认为对于小型程序来说,自下而上的设计方式是高效的.
分析的时候从分析主体开始,没想到设计的时候从细节开始,但是,我小手一抖,就写出了几个子模块:
//根据牌点获取其数值
intvalue(int i)
{
if(i <= 52)
return i / 4 + 1; //花牌
return MAXNUM; //鬼牌
}
//电脑取牌
introbot_fetch()
{
if(robot_value >= AVG_POINT) //如果取得平均点,不再取牌
return AVG_POINT;
int id;
do{
id = rand() % SUM + 1; //随机选牌
}while(map[id]);
map[id] = 1; //标记为选择状态
return robot_value + value(id);
}
//玩家取牌
intplayer_fetch()
{
int id;
do{
id = rand() % SUM + 1; //随机选牌
}while(map[id]);
map[id] = 1; //标记为选择状态
int temp = value(id);
printf("\t\t你获得了%d 点, 现在您有 %d 点了\n", temp, player_value + temp);
return player_value + temp;
}
//判断胜负
voidjudge_win(int flag)
{
int winer;
if(robot_value > MAXNUM &&player_value > MAXNUM)
{
//同时大于21,少者取胜
if(flag == 1) //玩家是庄家
winer = (robot_value >=player_value);
else
winer = (robot_value >player_value);
}
else if(robot_value> MAXNUM)
winer = 1; //玩家赢了
else if(player_value> MAXNUM)
winer = 0; //电脑赢了
else
{
//同时小于21,多着取胜
if(flag == 1) //玩家是庄家
winer = (robot_value <=player_value);
else
winer = (robot_value <player_value);
}
printf("\n\t\t%s\n", winer ?"玩家赢了" : "电脑赢了");
}
在设计这些子模块的同时便决定了一些细节:
1. 用随机数模拟取牌过程
2. 用map数组记录牌的选中状态防止重选.
3. 扑克牌是按数字顺序排序的.
也在设计的过程中发现并使用了必要的变量和常量:
1. 玩家和电脑对于的点数
2. 对中间点数,总点数进行宏定义,增强程序通用性.
做出这些决定是非常自然了,你可以做出个性的不同的决定,比如用数组记录每张牌的点数,用局部变量的参数传递代替全局变量的使用等等.
子模块的设计是非常简单了,即使一个C语言的新手对这个游戏束手无策,仍然可以编出上面的程序,因为在设计的过程中不需要考虑其他的任何细节,只要在乎眼前的问题而已。这和面向对象的封装思想一样,C++的封装更优秀无非就是把子函数整理归类了.
最后就是用已有的模块搭建函数主题了,也同样可以轻松写出来:
int main()
{
srand((unsigned)time(NULL)); //随机种子
int flag = FLAG; //庄家标志
do
{
memset(map, 0,sizeof(map)); //初始所有的扑克牌没有被选中
player_value = robot_value= 0;
printf("\n\t\t游戏开始\n");
int count = 1;
do{
player_value =player_fetch(); //玩家取牌
robot_value =robot_fetch(); //电脑取牌
if(count >MAXFETVH || player_value > MAXNUM)
break;
count++;
printf("您要继续取牌吗?(Y / N)\n");
}while(tolower(get_sign())!= 'n');
printf("\n\t游戏结束, 您获得了 %d 点, 电脑获得了 %d 点\n",player_value, robot_value);
judge_win(flag);
flag = !flag; //交换庄家
printf("您要继续游戏吗?(Y / N)\n");
}while(tolower(get_sign()) != 'n');
return 0;
}
上面也有很大默认的规则,不过我想读者应该能够看出来,我就不多解释了.
程序仅用了150行,实现了基本的21点游戏功能,再此基础上继续增加新的功能非常简单.
如果电脑更加智能,可以根据对方的牌数和自己的点数来决定是否继续选牌,只要改变robot_fetch()函数的一部分就可以了.如果玩家是在赌博,加入金币制,只要再声明一个变量记录金币的流失与获得就可以了.就算从控制台界面转入windows窗体界面,也许全部的代码都要改动,但是流程和思路是不变的,只要懂得控件的使用就可以.