前言:
这是一个简单的控制台五子棋小游戏,我目前的情况是刚学完C,C++尚未入门,在下面的代码中虽然用的是C++,但我的思考方式主要是C,所以新手不必害怕,耐心看完很简单的(●ˇ∀ˇ●)
数据及函数设计:
一、数据设计:
将二维数组作为棋盘,规定数组内数据为0表示空,1表示黑,2表示白
int map[19][19];
利用flag判断当前回合轮到谁落子
int flag;
// 表示当前回合数 偶数表示黑棋落子 奇数表示白棋落子
// 如: flag = 20 表示当前是第[20]次落子 由黑方落子
二、函数设计:(按照功能可以分为功能函数和菜单函数)
功能函数控制游戏的运行,菜单函数主要作用是刷新游戏界面和显示游戏结果。
1. 初始化游戏数据(棋盘重置)
将棋盘的值初始化为0
当前回合设为黑棋(flag设为0)
void init();
2. 胜负判断
根据传入的坐标(map对应位置)和flag值 判断落点后是否获胜
坐标(x,y): 依次先输入横坐标,再输入纵坐标
通过返回值确定游戏的结果(每次落子完成都要进行检查)
0表示没有获胜
1表示黑子胜利
2表示白子胜利
int isWin(int x, int y);
3. 落子/走子
在指定位置落子
如果map[x][y]是空地 则修改map[x][y]的值,否则不操作
x: 当前回合落子的x坐标
y: 当前回合落子的y坐标
返回值:
0表示落子失败 (棋盘已经有子)
1表示落子成功
int playerMove(int x, int y);
4. 主菜单(提供初始选项)
功能: 展示选项, 玩家可以在这里选择进入游戏, 进入设置或退出游戏
进入游戏: 调用游戏界面函数gameView();
进入设置: 敬请期待...
退出游戏: 调用exit(0);
void menuView();
5. 游戏界面(显示棋盘)
功能: 根据map数组 打印游戏棋盘
void gameView_ShowMap();
6. 显示结果
功能: 根据flag的值 打印游戏胜利界面 用户可以按任意键回到主菜单
void winView();
7. 游戏的整体逻辑(确保各函数按照一定规则被调用)
功能: 游戏界面整合
初始化游戏数据(调用函数init())
while(1){
打印游戏界面(调用函数gameView_ShowMap())
接收玩家坐标输入
落子(调用落子函数playerMove())
(如果落子失败 重新开始循环)
判断游戏是否胜利(调用胜利判断函数isWin())
(如果游戏胜利 调用胜利界面函数 然后结束当前界面)
切换玩家(修改flag值)
}
void gameView();
当然,游戏的运行逻辑也可以放在main函数中执行,这里为了方便就将其声明为一个单独的函数。
int main()
{
menuView();
return 0;
}
最终的代码:
下面的代码中添加了详细的注释,大家可以对照着阅读,应该比较容易理解(●ˇ∀ˇ●)
当然,如果对下面的代码存在什么问题的话也是可以在评论区提问的哦!
#include <iostream>
using namespace std;
int map[19][19];
int flag;
void init();
int isWin(int x, int y);
int playerMove(int x, int y);
void menuView();
void gameView_ShowMap();
void winView();
void gameView();
int main()
{
menuView();
return 0;
}
void init() {
//将棋盘的值初始化为0
// 五子棋棋盘大小为19x19
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
map[i][j] = 0;
}
}
//设置当前回合为黑棋
flag = 0;
}
int isWin(int x, int y) {
// 五子棋的胜利条件是:先在棋盘上形成五个相同颜色的棋子连成
// 一条线(横、竖、斜线皆可)的一方获胜
// ******************************************************************** 1. 横线(-)检查
// 变量count用来计数,count=5时则分出胜负
int count_1 = 1;
// 先看横线(y固定)
// 确定横坐标范围(x-4,x+4)
int left = x - 4;
int right = x + 4;
// 横坐标满足x - 4 >= 0, x + 4 <= 18
// 超出范围则按照边界计算
if (left < 0) left = 0;
if (right > 15) right = 18;
//往左数
int i = x - 1;
while (map[x][y] == map[i][y] && i >= left) {
count_1++;
i--;
}
//往右数
int j = x + 1;
while (map[x][y] == map[j][y] && j <= right) {
count_1++;
j++;
}
// 若第一种情况已经达到要求,则返回map[x][y]的值,得出结论
// 否则判断第二种情况
if (count_1 == 5) {
return map[x][y];
}
else if (count_1 != 5) {
// ******************************************************************** 2. 纵线(|)检查
// 变量count用来计数,count=5时则分出胜负
int count_2 = 1;
// 再看纵线(x固定)
// 确定纵坐标范围(y-4,y+4)
int up = y - 4;
int below = y + 4;
// 纵坐标满足y - 4 >= 0, y + 4 <= 18
// 超出范围则按照边界计算
if (up < 0) up = 0;
if (below > 19) below = 18;
//往上数
int i = y - 1;
while (map[x][y] == map[x][i] && i >= up) {
count_2++;
i--;
}
//往下数
int j = y + 1;
while (map[x][y] == map[x][j] && j <= below) {
count_2++;
j++;
}
// 若第二种情况已经达到要求,则返回map[x][y]的值,得出结论
// 否则判断第三种情况
if (count_2 == 5) {
return map[x][y];
}
else if (count_2 != 5) {
// ******************************************************************** 3. 正斜线(\)检查
// 变量count用来计数,count=5时则分出胜负
int count_3 = 1;
// 确定左上和右下坐标范围
int left_up_x = x - 4;
int left_up_y = y - 4;
int right_below_x = x + 4;
int right_below_y = y + 4;
// 横坐标满足x - 4 >= 0, x + 4 <= 18
// 超出范围则按照边界计算
// 纵坐标满足y - 4 >= 0, y + 4 <= 18
// 超出范围则按照边界计算
if (left_up_x < 0) left_up_x = 0;
if (right_below_x > 18) right_below_x = 18;
if (left_up_y < 0) left_up_y = 0;
if (right_below_y > 18) right_below_y = 18;
//往左上数
int i_x = x - 1;
int i_y = y - 1;
while (map[x][y] == map[i_x][i_y] && i_x >= left_up_x && i_y >= left_up_y) {
count_3++;
i_x--;
i_y--;
}
//往右下数
int j_x = x + 1;
int j_y = y + 1;
while (map[x][y] == map[j_x][j_y] && j_x <= right_below_x && j_y <= right_below_y) {
count_3++;
j_x++;
j_y++;
}
// 若第三种情况已经达到要求,则返回map[x][y]的值,得出结论
// 否则判断第三种情况
if (count_3 == 5) {
return map[x][y];
}
else if (count_3 != 5) {
// ******************************************************************** 4. 反斜线(/)检查
// 变量count用来计数,count=5时则分出胜负
int count_4 = 1;
// 确定左上和右下坐标范围
int left_below_x = x - 4;
int left_below_y = y + 4;
int right_up_x = x + 4;
int right_up_y = y - 4;
// 横坐标满足x - 4 >= 0, x + 4 <= 18
// 超出范围则按照边界计算
// 纵坐标满足y - 4 >= 0, y + 4 <= 18
// 超出范围则按照边界计算
if (left_below_x < 0) left_below_x = 0;
if (right_up_x > 18) right_up_x = 18;
if (left_below_y > 18) left_below_y = 18;
if (right_up_y < 0) right_up_y = 0;
//往右上数
int i_x = x + 1;
int i_y = y - 1;
while (map[x][y] == map[i_x][i_y] && i_x <= right_up_x && i_y >= right_up_y) {
count_4++;
i_x++;
i_y--;
}
//往左下数
int j_x = x - 1;
int j_y = y + 1;
while (map[x][y] == map[j_x][j_y] && j_x >= left_below_x && j_y <= left_below_y) {
count_4++;
j_x--;
j_y++;
}
// 若第四种情况已经达到要求,则返回map[x][y]的值,得出结论
// 否则没有胜利
if (count_4 == 5) {
return map[x][y];
}
else {
// **************************************************************** 5. 没有获胜
return 0;
}
}
}
}
}
int playerMove(int x, int y) {
// 如果(x, y)位置为空,则修改map[x][y]的值,并返回1
// 否则落子失败,返回0
if (map[x][y] == 0) {
map[x][y] = flag%2 + 1;
return 1;
}
else {
return 0;
}
}
void menuView() {
cout << "*************** >菜单< **************" << endl;
cout << "************ 1. 进入游戏 ************" << endl;
cout << "************ 2. 进入设置 ************" << endl;
cout << "************ 3. 退出游戏 ************" << endl;
cout << "请选择:";
// 玩家进行选择
int choice;
cin >> choice;
// 检查输入的有效性
while (choice <= 0 || choice > 3 || cin.fail()) {
cout << "无效输入,请重新选择:";
cin.clear(); // 清除错误标志
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略之前输入的内容
cin >> choice;
}
// 当输入1,2,3时,调用相关的函数或打印相关的文字
// 当输入不为1,2,3时,则要求重新输入一个有效值
if (choice == 1) gameView();
else if (choice == 2) cout << "敬请期待..." << endl;
else if (choice == 3) exit(0);
else {
cout << "选择无效,请重新选择(1,2,3)" << endl << endl;
menuView();
}
}
void gameView_ShowMap() {
//这是一个19x19的棋盘,共计361个可下棋的位置,也就是打印个225
for (int i = 0; i <= 18; i++) {
for (int j = 0; j <= 18; j++) {
cout << map[i][j] << " ";
}
cout << endl;
}
}
void winView() {
system("clear || cls"); // 清除画面
// 打印胜利游戏界面
gameView_ShowMap();
//flag 为偶数,表示黑棋落子后胜利
// 否则白棋胜利
if (flag % 2 == 0) {
cout << "********** 黑棋胜利! *********" << endl;
}
else {
cout << "********** 白棋胜利! *********" << endl;
}
printf("点击任意按钮返回主菜单\n");
// 判断是否有键盘输入
// cin.ignore()的作用在于清除输入缓冲区中的剩余字符,包括换行符,以确保下一个输入操作不受影响
// 否则,当用户输入(ENTER)时会立即结束程序
cin.ignore(); // 忽略之前的输入
cin.get(); // 获取一个字符
// 调用 menuView() 返回主菜单
menuView();
}
void gameView() {
//初始化游戏数据
init();
while (1) {
// 打印游戏界面
gameView_ShowMap();
cout << endl;
if (flag % 2 == 0) {
cout << "-->黑子回合:" << endl;
}
else
cout << "-->白子回合:" << endl;
// 接收玩家坐标输入, 坐标(i,j)
int i, j;
cout << " 输入横坐标x:";
cin >> i;
// 检查横坐标的有效性
while (i < 0 || i >= 18 || cin.fail()) {
cout << " 无效输入,请重新输入x:";
cin.clear(); // 清除错误标志
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略之前输入的内容
cin >> i;
}
cout << " 输入纵坐标y:";
cin >> j;
// 检查纵坐标的有效性
while (j < 0 || j >= 18 || cin.fail()) {
cout << " 无效输入,请重新输入y:";
cin.clear(); // 清除错误标志
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略之前输入的内容
cin >> j;
}
// 落子, 传入坐标(i,j)
// 利用playerMove(i, j)的返回值判断是否需要重新落子
// 1:成功
// 0: 失败
int control = playerMove(i, j);
while (control == 0) {
cout << "已有落子, 请重新输入。" << endl;
// 接收玩家坐标输入, 坐标(i,j)
int i, j;
cout << " 输入横坐标x:";
cin >> i;
// 检查横坐标的有效性
while (i < 0 || i >= 18 || cin.fail()) {
cout << " 无效输入,请重新输入x:";
cin.clear(); // 清除错误标志
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略之前输入的内容
cin >> i;
}
cout << " 输入纵坐标y:";
cin >> j;
// 检查纵坐标的有效性
while (j < 0 || j >= 18 || cin.fail()) {
cout << " 无效输入,请重新输入y:";
cin.clear(); // 清除错误标志
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略之前输入的内容
cin >> j;
}
control = playerMove(i, j);
}
// 判断玩家是否胜利, 利用value接受返回值,用于后续判断
int value = isWin(i, j);
// 游戏胜利时value = 1或2,此时调用胜利界面函数,然后结束当前界面
if (value) {
winView();
break;
}
// 切换玩家
flag += 1;
}
}