今天来一同做个小项目:五子棋;
不用说,大家都耳熟能详(还是要讲一下玩法的),双方分别使用黑白两色的棋子,轮流下在棋盘竖线与横线的交叉点上,先形成五子连线者获胜。
那么今天我们就来设计一个小棋盘,来实现一个简单的五子棋游戏。
首先,先来写一下项目流程(用写代码的方式比较好看,嗯):
项目流程{ 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; }看着还行,好了收工,下次见!
