不积跬步,无以至千里;不积小流,无以成江海。
1.三子棋
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。
2.设计思路
既然是棋,那么他就具有棋类一般具有的规则和落子的棋盘,从图中我们不难看出三子棋是一个9宫格类型的棋盘,所以在代码设计棋盘上我们很容易联想到将棋盘用一个3x3的二维数组来模拟储存落子点。其次就是游戏所具有的菜单、AI下棋和输赢的判断。
所以我们可以粗略的总结一下三子棋小游戏设计所需要实现的功能:
1.游戏菜单
2.我们需要一个空的3x3的棋盘--------棋盘初始化3.玩家落子
4.电脑落子
5.打印更新后棋盘
6.判断输赢
另外,为了代码逻辑性和复用性,一些代码分模块编写。
game.c
game.h
test.c
具体内容后面呈现。
3. 代码实现
3.1 游戏菜单
因为三子棋游戏本身就比较简单单一,所以我们菜单也并没有那么复杂,简单的呈现开始和退出就行了。
3.2 初始化棋盘
棋盘的形状是可以根据自己兴趣去设计的,只要是9宫格就行。比如:
形状并非固定,有兴趣可以自行去设计。
以我这个棋盘为例,其中每一个格子内并非空空如也,程序的初始化时就已经将存储落子的二位数组内的每一位,即每一个方格中都初始化为一个空格‘ ’,表示此处未落子。
3.3 玩家落子
玩家落子,顾名思义,即我们先下第一步,具体思路呢,就是利用坐标的形式,毕竟是一个3x3知道具体大小的棋盘游戏,那么我们就可以根据每一个方格的横纵坐标去将我们的落子存入到二维数组中,⭐️ps:数组中的下标从0开始,我们设计输入时候最好改成从1开始哦。
左上角就是我们人为操作落下的一颗子,用 * 表示,实质上就是输入了坐标之后将数组这个位置的元素改变成了 * 。由于在已有落子上不可重复落子,则我们应该有判空操作。
3.4 电脑落子
三子棋中电脑落子其实可以不用写的很复杂,毕竟三子棋游戏就不复杂,所以我们利用随机数,让电脑在九宫格内随机找一个空位落子即可。当然,这里牵扯到棋盘空余位置的判断问题。
棋盘空余的判断其实质上就是判断二维数组中该落子的地方是否为‘ ’。
3.5 更新棋盘
这一步和最开始打印棋盘一样,将每一次玩家落子后和电脑落子后的棋盘情形重复打印出来。
3.6 判断输赢
当玩家落子或者电脑落子后游戏可能的运行情况:
1.玩家落子后,玩家三子连线,玩家胜利,游戏结束。
2.电脑落子后,玩家三子连线,电脑胜利,游戏结束。
3.玩家或者电脑落子后,棋盘已满,平局。
4.玩家或者电脑落子后,棋盘未满且无一方胜出,游戏继续。
在处理游戏输赢的情况时候,我们又可以分为几个小过程:
1.当某一行连成线,一方胜出。
2.当某一列连成线,一方胜出。
3.当左上到右下的斜线连成线,一方胜出。
4.当右上到左下的斜线连成线,一方胜出。
5.以上情况均不发生,但棋盘已满,则平局。
6.以上情况均不发生,游戏继续。
有没有感觉清晰很多了呢。
①当某一行连成线时(返回值为连成线的棋子样式):
思路就是两两相比,相等的时候就让一个变量自增,最后判断如果等于2则表示一行连成线。
②当某一列连成线时(返回值为连成线的棋子样式):
思路和行一样。
③当左上到右下的斜线连成线,即“\”时(返回值为连成线的棋子样式):
④ 当右上到左下的斜线连成线,即“/”时(返回值为连成线的棋子样式):
⑤ 以上情况均不发生,但棋盘已满时:
⑥ 以上情况均不发生:
直接返回‘C’:表示游戏继续
4.完整代码实现
4.1 game.h
#pragma once
#define ROW 3
#define COL 3
#include <stdio.h>
#include <Windows.h>
//初始化
void Initgame(char board[ROW][COL], int row, int col);
//更新棋盘
void Displaygame(char board[ROW][COL],int row,int col);
//玩家下棋
void Ourplay(char board[ROW][COL], int row, int col);
//电脑下棋
void pcplay(char board[ROW][COL], int row, int col);
//判断输赢
char iswin(char board[ROW][COL], int row, int col);
4.2 game.c
#include "game.h"
//初始化
void Initgame(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void Displaygame(char board[ROW][COL], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j != col-1)
{
printf("|");
}
}
printf("\n");
if (i != row - 1)
{
for (int j = 0; j < col; j++)
{
printf("---");
if (j != col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
//玩家下棋
void Ourplay(char board[ROW][COL], int row, int col)
{
int x, y;
while (1)
{
scanf_s("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (board[x-1][y-1] == ' ')
{
board[x-1][y-1] = '*';
break;
}
else
{
printf("此处已有棋子,请重新落子\n");
}
}
else
{
printf("落子非法,请重新落子\n");
}
}
}
//电脑下棋
void pcplay(char board[ROW][COL], int row, int col)
{
while (1)
{
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
//判断输赢
char iswin(char board[ROW][COL], int row, int col)
{
//1.玩家赢
//2.电脑赢
//3.平局
//4.继续
//行
int i,j;
for (i = 0; i < row; i++)
{
int flag = 0;
for (j = 0; j < col-1; j++)
{
if ((board[i][j] == board[i][j + 1] && board[i][j] != ' '))
{
flag++;
}
}
if (flag==2) return board[i][j-1];
}
//列
for (i = 0; i < col-1 ; i++)
{
int flag = 0;
for (j = 0; j < row ; j++)
{
if ((board[j][i] == board[j+1][i] && board[j][i] != ' '))
{
flag++;
}
}
if (flag==2) return board[j-1][i];
}
//右斜(\)
int flag=0;
for (int i = 0; i < row-1; i++)
{
if ((board[i][i] == board[i + 1][i + 1] && board[i][i] != ' '))
{
flag++;
}
}
if (flag==2) return board[0][0];
//左斜(/)
flag = 0;
j = col - 1;
for (int i = 0; i<row-1;i++)
{
for (; j >= 0;)
{
if ((board[i][j] == board[i + 1][j - 1] && board[i][j] != ' '))
{
flag++;
}
j--;
break;
}
}
if (flag==2) return board[0][2];
flag = 1;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
if (board[i][j] == ' ')
{
flag = 0;
break;
}
}
}
if (flag) return'Q';
return 'C';
}
4.3 test.c
#include "game.h"
//游戏主菜单
void menu()
{
printf("************************\n");
printf("********1.开始游戏******\n");
printf("********0.退出游戏******\n");
printf("************************\n");
}
void game(char board[ROW][COL],int row,int col)
{
//棋盘初始化
Initgame(board, row, col);
//打印棋盘
Displaygame(board, row, col);
while (1)
{
printf("玩家回合,请输入落棋坐标=>\n");
//玩家下棋
Ourplay(board, row, col);
//判断输赢
char ch = iswin(board, row, col);
if (ch == '*')
{
system("cls");
printf("玩家胜利\n");
Displaygame(board, row, col);
break;
}
else if (ch == 'Q')
{
system("cls");
printf("平局\n");
Displaygame(board, row, col);
break;
}
else{ }
printf("电脑回合=>\n");
//电脑下棋
pcplay(board, row, col);
ch = iswin(board, row, col);
if (ch == '#')
{
system("cls");
printf("电脑胜利\n");
Displaygame(board, row, col);
break;
}
else if (ch == 'Q')
{
system("cls");
printf("平局\n");
Displaygame(board, row, col);
break;
}
else {
system("cls");
Displaygame(board, row, col); }
}
}
int main()
{
int input = 0;
srand((unsigned)time(NULL));
char board[ROW][COL]={0};
do
{
menu();
printf("请输入您的选择:\n");
scanf_s("%d", &input);
switch (input)
{
case 1:
//游戏入口
system("cls");
game(board, ROW, COL);
break;
case 0:
printf("游戏退出\n");
break;
default:
printf("非法输入,请重新选择。\n");
break;
}
} while (input);
return 0;
}
5.总结⭐️
本次代码虽然功能实现没什么问题,但是代码比较冗余,更待优化。在撰写本文的过程中,可能未能传达我所有的初衷与见解,这主要归咎于我个人在表达上的局限性。为此,希望每一位读者能够理解和包容。若您在阅读过程中发现任何表述不清或词不达意之处,非常欢迎您在评论区留下宝贵的问题、意见或建议。同时,对于文中涉及的代码部分,我也持开放态度接受大家的审视。如果您发现任何潜在的错误、改进空间或优化建议,请不吝赐教。我们共同的学习与讨论,将推动知识的进步与共享😆😆。
感谢你们的关注与支持,期待与你们在评论区展开更加丰富的交流与互动。
代码已上传Gitee,如有需要可以下载哦。