今天来一同做个小项目:五子棋;
不用说,大家都耳熟能详(还是要讲一下玩法的),双方分别使用黑白两色的棋子,轮流下在棋盘竖线与横线的交叉点上,先形成五子连线者获胜。
那么今天我们就来设计一个小棋盘,来实现一个简单的五子棋游戏。
首先,先来写一下项目流程(用写代码的方式比较好看,嗯):
项目流程{
1.立项与项目开发计划{
用C++实现:五子棋{
我们来用二维数组去表示的棋盘;
}
}
2.可行性分析{
因为今天要这个基本的小旗,所以会用到数组,自定义函数,对齐函数setw(),别忘写相应的头文件#include<iomanip>等;
同时要记着定义变量时的声明{
写清楚初始值、数组作用还有自定义函数的作用;
函数怎样去实现功能;
}
}
3.项目设计{
我们等会写个代码如何实现(自定义函数){
先去大致结构实现,再测试去揪一些细节;
}我们就用正常的输出界面就行(可视化我也不会‘-’);
}
4.架构搭建{
#include<iostream>
#include<iomanip>
#include<cstring> //好像也用不到呀6
int 数组();
int 初始值 = xx;
函数声明;
int main(){
xxxxxxxx;
xxxxxxxx;
return 0;
}
类型 函数名(){
}
类型 函数名(){
}
类型 函数名(){
}
类型 函数名(){
}
类型 函数名(){
}
and so on........;
}
5.开发{
C++进度:函数、数组
定义一个全局变量,比如:初始值(不按要求就退出或回到初始值);
不要重复命名,要注意;
先完成大致,再揪其他;
}
6.测试{
这的话,玩就行了;
}
}
好了,看完项目流程,我们就开始干吧‘ - ’:
我们先来定义一个数组名叫map的19*19的小棋盘,用二维数组方便一些
int map[19][19];再定义个用于记数的全局变量:
int flag;我用‘0’表示空地,‘1’表示黑棋,用‘2’来表示白棋,所以,可以用 flag%2,来进行判断,当 flag&2 == 0时,(能整除),当前为黑棋的回合,那么 flag%2 == 1时为白棋的回合,况且再每次落子后,flag++;
接着来写最重要的自定义函数功能:
先定义一个无参数类型的 inin() 函数,我让它的功能表现在:初始化游戏数据
将棋盘的值初始化为0
当前回合设为黑棋(flag设为0)
参数: void
返回值: void
void init(){
int i,j;
for(i=0;i<19;++i){
for(j=0;j<19;++j){
map[i][j] = 0;
}
}
flag = 0;
}
先定义 i,j 分别用来控制循环行号与列号,因为定义数组是从0到18(一般数组索引从0开始),所以在循环中将 map[i][j] 设置为0,来表示初始为空,再将 flag 赋予0值。
再定义一个int类型的 isWin() 函数(这还是个难点,因为要控制方向!),让它的功能表现在:根据传入的坐标(map对应位置)和flag值 判断落点后是否获胜
参数:
x: 当前回合落子的x坐标
y: 当前回合落子的y坐标
返回值:
0表示没有获胜
1表示黑子胜利
2表示白子胜利
int isWin(int x,int y){
int flag_2 = (flag%2 == 0) ? 1:2;
int count = 1;
int i,j;
for(i=y+1;i<19 && map[x][i] == flag_2;++i){
count++;
}
for(i=y-1;i>=0 && flag_2 == map[x][i];--i){
count++;
}
if(count>5) return flag_2;
count = 1;
for(i=x+1;i<19 && map[i][y] == flag_2;++i){
count++;
}
for(i=x-1;i>=0 && map[i][y] == flag_2;--i){
count++;
}
if(count>5) return flag_2;
count = 1;
for(i=x+1,j=y+1;i<19 && j<19 && map[i][j] == flag_2;++i,++j){
count++;
}
for(i=x-1,j=y-1;i>=0 && j>=0 && map[i][j] == flag_2;--i,--j){
count++;
}
count = 1;
for(i=x+1,j=y-1;i<19 && j>=0 && map[i][j] == flag_2;++i,--j){
count++;
}
for(i=x-1,j=y+1;i>=0 && j<19 && map[i][j] == flag_2;--i,++j){
count++;
}
if(count>5) cout<<flag_2;
return 0;
}
对于方向来讲,可以从水平、竖直、主对角线和副对角线来判断:
先说水平方向:从当前列 y 向右检查,统计连续相同棋子的数量;从当前列 y 向左检查,统计连续相同棋子的数量;如果棋子连续的数量超过5,则返回 flag_2 表示当前棋子颜色获胜。
再来看竖直方向:从当前行 x 向下检查,统计连续相同棋子的数量;从当前行 x 向上检查,统计连续相同棋子的数量;同样如果棋子连续的数量超过5,则返回 flag_2 表示当前棋子颜色获胜。
那么,对于主对角线方向:从当前位置 (x, y) 向右上方检查,统计连续相同棋子的数量;从当前位置 (x, y) 向左下方检查,统计连续相同棋子的数量;当棋子连续的数量超过5,则返回 flag_2 表示当前棋子颜色获胜。
同样,对于副对角线方向:、从当前位置 (x, y) 向右下方检查,统计连续相同棋子的数量;从当前位置 (x, y) 向左上方检查,统计连续相同棋子的数量;当棋子连续的数量超过5,则返回 flag_2 表示当前棋子颜色获胜。
接着定义一个int 类型的 playerMove() 函数,让它的功能表现在:在指定位置落子
如果map[x][y]是空地 则修改map[x][y]的值:改为相应颜色(flag对应颜色)
否则不操作
参数:
x: 当前回合落子的x坐标
y: 当前回合落子的y坐标
返回值:
0表示落子失败 (棋盘已经有子)
1表示落子成功
int playerMove(int x, int y){
if(x<0 || x>=19 || y<0 || y>=19) return 0; //在指定位置落子
if(map[x][y] != 0) return 0;
// if(flag%2 == 0) map[x][y] = 1;
// else map[x][y] = 2;
int flag_2 = (flag%2 == 0) ? 1:2;
map[x][y] = flag_2;
flag++;
return 1; //1表示落子成功
}
对于这个函数来说,落子是在棋盘的指定位置放置当前玩家的棋子。返回 0 表示落子失败(位置非法或已有棋子),而返回 1 表示落子成功。
if(x<0 || x>=19 || y<0 || y>=19) return 0;
检查 (x, y) 是否在棋盘范围内,如果不在范围内,返回 0 表示落子失败。
if(map[x][y] != 0) return 0;
检查 (x, y) 位置是否已经有棋子,如果已经有棋子,返回 0 表示落子失败。
int flag_2 = (flag%2 == 0) ? 1:2;
根据 flag 的值确定当前落子的棋子颜色,flag_2 用于表示当前棋子的颜色:1 表示黑棋,2 表示白棋。
map[x][y] = flag_2; //在 (x, y) 位置放置当前玩家的棋子。
flag++; //增加 flag 的值,表示回合数增加,准备进入下一回合。
随后再定义一个写有主菜单的函数 menuView() :功能显而易见,展示选项, 玩家可以在这里选择进入游戏, 进入设置或退出游戏
while(1){
1. 展示选项
2. 用户输入
3. 根据输入进行对应处理
进入游戏: 调用游戏界面函数gameView();
进入设置: 打印敬请期待... 重新循环
退出游戏: 调用exit(0);
}
参数: void
返回值: void
void menuView(){
while(1){
cout<<"=======五子棋======="<<endl;
cout<<"1.进入游戏"<<endl;
cout<<"2.设置"<<endl;
cout<<"3.退出游戏"<<endl;
cout<<"===================="<<endl;
cout<<"请选择:"<<endl;
int choice;
cin>>choice;
switch(choice){
case 1:
gameView();
break;
case 2:
cout<<"打印敬请期待..."<<endl;
continue;
case 3:
exit(0);
default:
cout<<"请重选:"<<endl;
break;
}
}
}
这个,没啥好说,就是 switch case 语句要写在循环里,不然会出bug。
这里定义的是gameView_ShowMap()函数,它的功能吗,就是根据map数组 打印游戏棋盘
void gameView_ShowMap(){
int i,j;
// cout<<" ";
for(j=0;j<19;++j)
// cout<<j%10<<" "; //对齐
cout<<setw(2)<<j<<" ";
cout<<endl;
for(i=0;i<19;++i){
//cout<<i%10<<" "; //只显示各位
cout<<setw(2)<<i<<" ";
for(j=0;j<19;++j){
// if(map[i][j] == 0) cout<<0<<" "; //空
// else if(map[i][j] == 1) cout<<1<<" "; //黑
// else if(map[i][j] == 2) cout<<2<<" "; //白
cout<<setw(2)<<map[i][j]<<" ";
}
cout<<endl;
}
}
要点就是行,列对齐,所以我在循环里加个setw(2),(用这个时别忘加个头文件:#include<iomanip>,不然又会报错)
cout<<setw(2)<<j<<" ";来保证列对齐,行对齐也这样写
cout<<setw(2)<<i<<" ";打注释的代码是我之前写的,但功能不完善的,有些在之前写的函数中用法重复了。
接着定义的是 winView() 函数,其功能是: 根据flag的值,打印游戏胜利界面,用户可以按任意键回到主菜单。
void winView(){
if(flag%2 == 0) cout<<"黑棋赢了"<<endl;
else cout<<"白棋赢了"<<endl;
cout<<"用户按任意键回到主菜单"<<endl;
cin.get();
menuView();
}
cin.get();get()用于任意输入,但它不会自动回到menuView,所以我要在调用一次 menuView,来进行手动返回。
最后一个函数 gameView用于实现游戏界面整合:
初始化游戏数据(调用函数init())
while(1){
打印游戏界面(调用函数gameView_ShowMap())
接收玩家坐标输入
落子(调用落子函数playerMove())
(如果落子失败 重新开始循环)
判断游戏是否胜利(调用胜利判断函数isWin())
(如果游戏胜利 调用胜利界面函数 然后结束当前界面)
切换玩家(修改flag值)
}
参数: void
返回值: void
void gameView(){
init();
// while(1){
// gameView_ShowMap();
// int x,y;
// cin>>x>>y;
// // int flag_1 = (flag%2 == 0) ? 1:2;
// if(playerMove(x,y)){
// if(isWin(x,y)){
// gameView_ShowMap();
// winView();
// return;
// }
// // flag++;
// }else cout<<"无效,重输入"<<endl;
// }
while(1){
gameView_ShowMap();
int x,y;
cout<<"请输入坐标(x y,范围0-18):";
while(!(cin>>x>>y)){// 检查输入是否有效
cin.clear(); // 清除错误状态
cout<<"无效输入,请重新输入坐标(x y,范围0-18): ";
}
if(x<0 || x>=19 || y<0 || y>=19){
cout<<"坐标超出范围,请输入0-18之间的数字!"<<endl;
continue;
}
if(playerMove(x,y)){
if(isWin(x,y)){
gameView_ShowMap();
winView();
return;
}
}else{
cout<<"该位置已有棋子,请重新输入!"<<endl;
}
}
}
因为19*19 不能访问下标,所以在输入19要给与不可访问提醒,并加个判断x和y大于0小于19,不加可能会报错,但我之前写的时没给提示。
if(x<0 || x>=19 || y<0 || y>=19){
cout<<"坐标超出范围,请输入0-18之间的数字!"<<endl;
continue;
}
最最后,再写上主函数,并调用函数就行:
int main(){
menuView();
return 0;
}
至此,所有功能已全部写好,我们来一起看下完整代码:#include<iostream>
#include<cstring> //也用不到
#include<iomanip>
using namespace std;
int map[19][19];
int flag; // 如: flag = 20,表示当前是第[20]次落子,由黑方落子
void gameView(); //函数声明
void init(){
int i,j;
for(i=0;i<19;++i){
for(j=0;j<19;++j){
map[i][j] = 0;
}
}
flag = 0;
}
int isWin(int x,int y){
int flag_2 = (flag%2 == 0) ? 1:2;
int count = 1;
int i,j;
for(i=y+1;i<19 && map[x][i] == flag_2;++i){
count++;
}
for(i=y-1;i>=0 && flag_2 == map[x][i];--i){
count++;
}
if(count>5) return flag_2;
count = 1;
for(i=x+1;i<19 && map[i][y] == flag_2;++i){
count++;
}
for(i=x-1;i>=0 && map[i][y] == flag_2;--i){
count++;
}
if(count>5) return flag_2;
count = 1;
for(i=x+1,j=y+1;i<19 && j<19 && map[i][j] == flag_2;++i,++j){
count++;
}
for(i=x-1,j=y-1;i>=0 && j>=0 && map[i][j] == flag_2;--i,--j){
count++;
}
count = 1;
for(i=x+1,j=y-1;i<19 && j>=0 && map[i][j] == flag_2;++i,--j){
count++;
}
for(i=x-1,j=y+1;i>=0 && j<19 && map[i][j] == flag_2;--i,++j){
count++;
}
return 0;
}
int playerMove(int x,int y){
if(x<0 || x>=19 || y<0 || y>=19) return 0; //在指定位置落子
if(map[x][y] != 0) return 0;
// if(flag%2 == 0) map[x][y] = 1;
// else map[x][y] = 2;
int flag_2 = (flag%2 == 0) ? 1:2;
map[x][y] = flag_2;
flag++;
return 1; //1表示落子成功
}
void menuView(){
while(1){
cout<<"=======五子棋======="<<endl;
cout<<"1.进入游戏"<<endl;
cout<<"2.设置"<<endl;
cout<<"3.退出游戏"<<endl;
cout<<"===================="<<endl;
cout<<"请选择:"<<endl;
int choice;
cin>>choice;
switch(choice){
case 1:
gameView();
break;
case 2:
cout<<"打印敬请期待..."<<endl;
continue;
case 3:
exit(0);
default:
cout<<"请重选:"<<endl;
break;
}
}
}
void gameView_ShowMap(){
int i,j;
// cout<<" ";
for(j=0;j<19;++j)
//cout<<j%10<<" "; //对齐
cout<<setw(2)<<j<<" ";
cout<<endl;
for(i=0;i<19;++i){
//cout<<i%10<<" "; //只显示个位
cout<<setw(2)<<i<<" ";
for(j=0;j<19;++j){
// if(map[i][j] == 0) cout<<0<<" "; //空
// else if(map[i][j] == 1) cout<<1<<" "; //黑
// else if(map[i][j] == 2) cout<<2<<" "; //白
cout<<setw(2)<<map[i][j]<<" ";
}
cout<<endl;
}
}
void winView(){
if(flag%2 == 0) cout<<"黑棋赢了"<<endl;
else cout<<"白棋赢了"<<endl;
cout<<"用户按任意键回到主菜单"<<endl;
cin.ignore();
cin.get();
menuView();
}
void gameView(){
init();
// while(1){
// gameView_ShowMap();
// int x,y;
// cin>>x>>y;
// // int flag_1 = (flag%2 == 0) ? 1:2;
// if(playerMove(x,y)){
// if(isWin(x,y)){
// gameView_ShowMap();
// winView();
// return;
// }
// // flag++;
// }else cout<<"无效,重输入"<<endl;
// }
while(1){
gameView_ShowMap();
int x,y;
cout<<"请输入坐标(x ,y,范围0-18):";
while(!(cin>>x>>y)){// 检查输入是否有效
cin.clear(); // 清除错误状态
cout<<"无效输入,请重新输入坐标(x y,范围0-18): ";
}
if(x<0 || x>=19 || y<0 || y>=19){
cout<<"坐标超出范围,请输入0-18之间的数字!"<<endl;
continue;
}
if(playerMove(x,y)){
if(isWin(x,y)){
gameView_ShowMap();
winView();
return;
}
}else{
cout<<"该位置已有棋子,请重新输入!"<<endl;
}
}
}
int main(){
menuView()
return 0;
}
看着还行,好了收工,下次见!


京公网安备 11010502036488号