Part 1 三子棋游戏基础实现:
1、操作界面实现:
即实现供用户选择的的菜单:我们定义为menu函数
void menu(){
printf("****************\n");
printf("*****1.play*****\n");
printf("*****0.exit*****\n");
printf("****************\n");
}
我们希望达到的目的是用户可以不断选择,直至按0退出游戏,于是我们在主函数中需要搭建框架:
int main(){
srand((unsigned int)time(NULL));//为后函数中rand提供起点
int input = 0;
do{
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch(input){
case 1:
printf("游戏开始\n");
game();//游戏的核心函数
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);//巧妙利用input实现中止循环
return 0;
}
2、游戏核心函数实现game()函数:
在进入游戏时我们需要的变量有board二维数组,和ret变量(判断输赢)
我们需要一下几个函数:
1、InitBoard函数在每次game()函数开始初始化棋盘
2、DisplayBoard函数 可视化棋盘
3、PlayerMove函数即玩家输入坐标实现下棋操作
4、ComputerMove函数电脑随机生成坐标下棋
5、CheckWin函数,无论玩家或是电脑走棋后,判断当前输赢
void game(){
char board[ROW][COL];//ROW,COL通过宏定义,三子棋中即为3行3列
char ret = 0;//判断输赢
InitBoard(board, ROW, COL);//初始化棋盘
DisplayBoard(board, ROW, COL);//可视化棋盘
while(1){
PlayerMove(board, ROW, COL);//玩家输入坐标实现下棋操作
ret = CheckWin(board, ROW, COL);//玩家或是电脑走棋后,判断当前输赢
if(ret !='C'){
break;
}
DisplayBoard(board, ROW, COL);
ComputerMove(board, ROW, COL);//电脑随机生成坐标下棋
ret = CheckWin(board, ROW, COL);
if(ret !='C'){
break;
}
DisplayBoard(board, ROW, COL);
}
if(ret =='*'){
printf("恭喜您赢了!\n");
}else if(ret =='#'){
printf("遗憾计算机赢了!\n");
}else if(ret == 'Q'){
printf("平手!\n");
}
DisplayBoard(board, ROW, COL);
}
3、以上提及函数的实现:
(1)InitBoard函数在每次game()函数开始初始化棋盘:将二维数组board中的元素全部清空为空格
void InitBoard(char board[ROW][COL], int row, int col){
int i = 0,j=0;
for (i = 0; i < ROW;i++){
for (j = 0; j < COL;j++){
board[i][j] = ' ';//遍历二维数组后清空
}
}
}
(2)DisplayBoard函数 可视化棋盘:
我们希望实现的棋盘如下图:
为保留三子棋的游戏推广性,我们如下编写代码:
void DisplayBoard(char board[ROW][COL], int row, int col){
int i = 0, j = 0;
for (i = 0; i < row;i++){
for (j = 0; j < col;j++){
printf(" %c ", board[i][j]);
if(j<col-1){
printf("|");//通过判断语句 避免多打印横线
}
}
printf("\n");
if(i<row-1){
for (j = 0; j < col;j++){
printf("---");
if(j<col-1){
printf("|");
}
}
printf("\n");
}
}
}
(3)PlayerMove函数即玩家输入坐标实现下棋操作:
void PlayerMove(char board[ROW][COL], int row, int col){
printf("玩家开始:>\n");
while(1){
int x, y;
scanf("%d %d", &x, &y);
if(x<=row && x>=1 && y<=col && y>=1){
if(board[x-1][y-1] ==' '){
board[x - 1][y - 1] = '*';
break;//核心不可丢
}else{
printf("当期棋盘已被占有,请重新输入!\n");//防止重复输入
}
}else{
printf("输入不在棋盘当中!\n");//控制合理合理输入
}
}
}
(4)ComputerMove函数电脑随机生成坐标下棋
void ComputerMove(char board[ROW][COL], int row, int col){
printf("电脑开始:>\n");
while(1)
{
int x = rand() % row;//利用rand生成行
int y = rand() % col;//利用rand生成列
if(board[x][y] == ' '){
board[x][y] = '#';
break;
}
}
}
(5)CheckWin函数,无论玩家或是电脑走棋后,判断当前输赢:
三子棋的规则要求同一行同一列,或是对角线达到3颗同样棋子都可以获胜于是我们便有了判断获胜的条件。
但是平局的问题如何解决?我们需要另一个函数IsFull 来判断是否9个格子全部下满了,去判断是否达到平局。
int IsFull(char board[ROW][COL], int row, int col){
int i = 0, j = 0;
for (i = 0; i < row;i++){
for (j = 0; j < col;j++){
if(board[i][j] == ' '){
return 0;
}
}
}
return 1;
}
char CheckWin(char board[ROW][COL], int row, int col){
int i = 0;
for (i = 0; i < row;i++){
if(board[i][0]==board[i][1] && board[i][1]==board[i][2] && board[i][0] != ' '){
return board[i][0];//判断行,返回值恰好可以代表玩家或电脑,(#为电脑,*为玩家),下同
}
}
for (i = 0; i < col;i++){
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];//判断列
}
}
if(board[0][0]==board[1][1] && board[1][1] == board[2][2] && board[0][0]!= ' '){
return board[0][0];//判断对角线1
}
if(board[0][2]==board[1][1] && board[1][1]==board[2][0] && board[0][2] !=' '){
return board[0][2];//判断对角线2
}
if(IsFull(board,row,col)==1){
return 'Q';//平局传递符号
}
return 'C';//联系上文的主函数
}
至此三子棋游戏已经实现,下面贴上全部代码:
#include <stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 3
#define COL 3
void menu();
void game();
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL], int row, int col);
void ComputerMove(char board[ROW][COL], int row, int col);
int AI(char board[ROW][COL], int row, int col);
char CheckWin(char board[ROW][COL], int row, int col);
int IsFull(char board[ROW][COL], int row, int col);
int main(){
srand((unsigned int)time(NULL));
int input = 0;
do{
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch(input){
case 1:
printf("游戏开始\n");
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
void menu(){
printf("****************\n");
printf("*****1.play*****\n");
printf("*****0.exit*****\n");
printf("****************\n");
}
void InitBoard(char board[ROW][COL], int row, int col){
int i = 0,j=0;
for (i = 0; i < ROW;i++){
for (j = 0; j < COL;j++){
board[i][j] = ' ';
}
}
}
void DisplayBoard(char board[ROW][COL], int row, int col){
int i = 0, j = 0;
for (i = 0; i < row;i++){
for (j = 0; j < col;j++){
printf(" %c ", board[i][j]);
if(j<col-1){
printf("|");
}
}
printf("\n");
if(i<row-1){
for (j = 0; j < col;j++){
printf("---");
if(j<col-1){
printf("|");
}
}
printf("\n");
}
}
}
void PlayerMove(char board[ROW][COL], int row, int col){
printf("玩家开始:>\n");
while(1){
int x, y;
scanf("%d %d", &x, &y);
if(x<=row && x>=1 && y<=col && y>=1){
if(board[x-1][y-1] ==' '){
board[x - 1][y - 1] = '*';
break;
}else{
printf("当期棋盘已被占有,请重新输入!\n");
}
}else{
printf("输入不在棋盘当中!\n");
}
}
}
void ComputerMove(char board[ROW][COL], int row, int col){
printf("电脑开始:>\n");
while(1)
{
int x = rand() % row;
int y = rand() % col;
if(board[x][y] == ' '){
board[x][y] = '#';
break;
}
}
}
int IsFull(char board[ROW][COL], int row, int col){
int i = 0, j = 0;
for (i = 0; i < row;i++){
for (j = 0; j < col;j++){
if(board[i][j] == ' '){
return 0;
}
}
}
return 1;
}
char CheckWin(char board[ROW][COL], int row, int col){
int i = 0;
for (i = 0; i < row;i++){
if(board[i][0]==board[i][1] && board[i][1]==board[i][2] && board[i][0] != ' '){
return board[i][0];
}
}
for (i = 0; i < col;i++){
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
if(board[0][0]==board[1][1] && board[1][1] == board[2][2] && board[0][0]!= ' '){
return board[0][0];
}
if(board[0][2]==board[1][1] && board[1][1]==board[2][0] && board[0][2] !=' '){
return board[0][2];
}
if(IsFull(board,row,col)==1){
return 'Q';
}
return 'C';
}
void game(){
char board[ROW][COL];
char ret = 0;
InitBoard(board, ROW, COL);
DisplayBoard(board, ROW, COL);
while(1){
PlayerMove(board, ROW, COL);
ret = CheckWin(board, ROW, COL);
if(ret !='C'){
break;
}
DisplayBoard(board, ROW, COL);
ComputerMove(board, ROW, COL);
ret = CheckWin(board, ROW, COL);
if(ret !='C'){
break;
}
DisplayBoard(board, ROW, COL);
}
if(ret =='*'){
printf("恭喜您赢了!\n");
}else if(ret =='#'){
printf("遗憾计算机赢了!\n");
}else if(ret == 'Q'){
printf("平手!\n");
}
DisplayBoard(board, ROW, COL);
}
Part 2 AI实现的设想:
我们发现电脑随机生成,会造成玩家赢得太轻松;AI实现其实就是人脑思维在计算机上的实现,我们来思考一下,我们是怎么下三子棋的,进攻策略上当在行、列、对角线上如果出现了两个我方棋子,我们会继续下第三个;防守策略上,敌方在行、列、对角线上有了两个棋子,我们会堵截;进攻、防守策略上相同的点便出现了电脑需要的就是在出现两个相同的棋子的地方下自己的棋子就好了(不管是己方还是敌方):
于是代码如下:
int AI(char board[ROW][COL], int row, int col){
int i = 0, j = 0;
for (i = 0; i < row;i++){
if(board[i][0]==board[i][1] && board[i][0]!=' '&& board[i][2]==' ')//此处需要注意判断堵截的棋子是否未下过
{
board[i][2] = '#';
return 1;//返回值为判断AI执行
}else if(board[i][1]==board[i][2] && board[i][1]!=' '&& board[i][0]==' '){
board[i][0] = '#';
return 1;
}else if(board[i][2]==board[i][0] && board[i][0]!=' '&& board[i][1]==' '){
board[i][1] = '#';
return 1;
}
}
for (i = 0; i < col;i++){
if(board[0][i]==board[1][i] && board[1][i]!=' ' && board[2][i]==' '){
board[2][i] = '#';
return 1;
}
if(board[1][i]==board[2][i] && board[1][i]!=' '&& board[0][i]==' '){
board[0][i] = '#';
return 1;
}
if(board[0][i]==board[2][i] && board[0][i]!=' '&& board[1][i]==' '){
board[1][i] = '#';
return 1;
}
}
if(board[0][0]==board[1][1] && board[1][1]!=' '&& board[2][2]==' '){
board[2][2] = '#';
return 1;
}
if(board[2][2]==board[1][1] && board[1][1]!=' '&& board[0][0]==' '){
board[0][0] = '#';
return 1;
}
if(board[0][0]==board[2][2] && board[0][0]!=' '&& board[1][1]==' '){
board[1][1] = '#';
return 1;
}
if(board[0][2]==board[1][1] && board[1][1]!=' '&& board[2][0]==' '){
board[2][0] = '#';
return 1;
}
if(board[2][0]==board[1][1] && board[1][1]!=' '&& board[0][2]==' '){
board[0][2] = '#';
return 1;
}
if(board[2][0]==board[0][2] && board[2][0]!=' '&& board[1][1]==' '){
board[1][1] = '#';
return 1;
}
return 0;//返回值为判断AI未执行
}
我们还需要修改一下啊ComputerMove函数
void ComputerMove(char board[ROW][COL], int row, int col){
printf("电脑开始:>\n");
while(1)
{
if(AI(board,row,col)==1){
break;
}//此处修改,若不需要执行AI,则正常的随机生成坐标
int x = rand() % row;
int y = rand() % col;
if(board[x][y] == ' '){
board[x][y] = '#';
break;
}
}
}
总结:
AI函数的代码依赖于三子棋的可供选择空间较小,当然思路模仿了人下棋的正常思维。
Part 3 五子棋的推广:
三子棋毕竟是小游戏,我们希望实现在很大的棋盘上,下五子棋,观察先前的函数,唯一需要修改的是CheckWin函数,和宏定义里的ROW和COL(这个只控制棋盘的大小)
char CheckWin(char board[ROW][COL], int row, int col){
int i = 0,j=0;
for (i = 0; i < row;i++){
for (j = 0; j < row - 4;j++){
if (board[i][j] == board[i][j+1] && board[i][j+1] == board[i][j+2] && board[i][j+2] == board[i][j+3] && board[i][j+3] == board[i][j+4] && board[i][j] != ' ')
{
return board[i][j];//此处寻找每一行的是否获胜
}
}
}
for (i = 0; i < col;i++){
for (j = 0; j < row - 4;j++){
if(board[j][i]==board[j+1][i]&&board[j+2][i]==board[j+1][i]&&board[j+2][i]==board[j+3][i]&&board[j+4][i]==board[j+3][i]&&board[j][i]!=' '){
return board[j][i];//此处此处利用循环寻找每一列的是否获胜
}
}
}
for (j = 0; j < row - 4;j++){
for (i = 0; i < row - 4; i++)
{
if (board[i+j][i] == board[i + j + 1][i + 1] && board[i + j + 1][i + 1] == board[i +j+ 2][i + 2] && board[i + j+ 2][i + 2] == board[i +j+ 3][i + 3] && board[i +j+ 3][i + 3] == board[i +j+ 4][i + 4] && board[i+j][i] != ' ')
{
return board[i+j][i];//我们发现斜线获胜逐渐变得复杂,需要两层循环来控制;此时我们需要思考如何平移斜线(右平移和下平移)
}
if (board[i][i+j] == board[i + 1][i + j + 1] && board[i + 1][i + j + 1] == board[i + 2][i +j+ 2] && board[i + 2][i + j+ 2]== board[i + 3][i +j+ 3] && board[i + 3][i +j+ 3] == board[i + 4][i +j+ 4] && board[i][i+j] != ' ')
{
return board[i][i+j];
}
}
}
for (j = 0; j < row - 4;j++){
for (i = 0; i < row - 4; i++)
{
if (board[i][row - i - 1-j] == board[i + 1][row - i - 2-j] && board[i + 1][row - i - 2-j] == board[i + 2][row - i - 3-j] && board[i + 2][row - i - 3-j] == board[i + 3][row - i - 4-j] && board[i + 3][row - i - 4-j] == board[i + 4][row - i - 5-j] && board[i][row - i - 1-j] != ' ')
{
return board[i][row - i - 1-j];//同上思路
}
if (board[i+j][row - i - 1-j] == board[i + 1+j][row - i - 2-j] && board[i + 1+j][row - i - 2-j] == board[i + 2+j][row - i - 3-j] && board[i + j+2][row - i - 3-j] == board[i +j+ 3][row - i - 4-j] && board[i +j+ 3][row - i - 4-j] == board[i + j+4][row - i - 5-j] && board[i+j][row - i - 1-j] != ' ')
{
return board[i+j][row - i - 1-j];
}
}
}
if (IsFull(board, row, col) == 1)
{
return 'Q';
}
return 'C';
}
有了这种方法棋盘可以无限扩大,从而使得五子棋变得更有意思。
当然此时可以再利用Part 2的思路使得计算机变得更聪明,但这种算法在无限扩大的棋盘上,显得过于臃肿,但限于作者水平,并没有演示出来
Part 4 总结:
三子棋游戏是c语言利用数组这一模块知识可以写出的较为基础的小游戏,但是其背后似乎也蕴含着类似Alpha Go的AI算法思想,作者也在不断编写的过程中,收获了更加广阔的思路。同时三子棋不是局限,类似初高中学习,我们可以通过改进来实现从特殊到一般的过程。