当前位置:网站首页>“纯C”实现——三子棋小游戏

“纯C”实现——三子棋小游戏

2022-08-02 10:48:00 InfoQ



文章目录

  • 三子棋实现思路
  • 棋盘实现
  • 玩家下棋
  • 电脑下棋
  • 判断输赢
  • 小结语

text.c文件
game.c文件
game.h


三子棋实现思路

三子棋游戏就是玩家和电脑之间轮流下棋,
首先它的棋盘为3X3,我们玩家先下棋,然后到电脑,只要其中一方下的棋子连成了一条线即为胜利
。 那么要实现游戏的前提首先是得有一个棋盘显示出来,然后把我们每一次下的棋到显示在屏幕上直到一方胜利或者和局。所以

我们第一步是要打印棋盘:

打印棋盘的前提是有数据可以打印,所以我们需要用到一个
3X3的二维数组
,刚开始里面没有数据,我们
先初始化成空格
,这样打印出来就是空白的形式,为了方便区分玩家和电脑下棋,我们可以规定玩家下棋就用符号" *"填补电脑下棋就用符号"# "填补

那么第二步:

棋盘打印出来屏幕后,就是
玩家下棋
,那么我们需要一个玩家下棋的独立函数,用来接收玩家输入的坐标,而且要判断坐标是否合法

第三步:

玩家下完棋后,就是
电脑下棋
,电脑下棋我们可以用
随机值
进行下棋此时我们又可以实现一个电脑下棋的独立函数

第四步:

玩家和电脑每下一次棋,我们都需要判断一下是否有一方已经胜利了,或者棋盘是否满了,所以我们又需要一个独立函数用来判断

第五步:

把上述的所有函数用循环连接起来即可,下面直接看我操作

null

棋盘实现

下面的图就是我们要实现的棋盘,首先要创建一个
3X3的二维数组

#define ROW 3 //行
#define COL 3 //列
char board[ROW][COL]

我们用宏定义来定义行列,方便以后要打印成其他4X4,10X10的棋盘
而且我们要把它初始化成
空格

//初始化成空格
void InitBoard(char board[ROW][COL], int row, int col)
{
 int i = 0;
 int j = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col; j++)
 {
 board[i][j] = ' ';
 }
 }
}

可以看一个下的棋下面都有三个“-”符号,因为我们的“ # ”,和“ * ”都只占一个位置,所以我们打印的时候要用
空格
隔开

null
用宏定义的好处,如果把ROW和COL改成10,那么它可以直接打印成下面的10X10格子 注意我们下面是基于3X3格子进行实现的三子棋,只适用于3x3格子

null
下面我们就实现一下打印棋盘的代码实现代码:

//打印棋盘
void BoardPrint(char board[ROW][COL], int row, int col)
{
 int i = 0;
 int j = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col; j++)
 {
 printf(&quot; %c &quot;,board[i][j]);
 //可以看到图片里的最后一列是没有竖杆的,所以这里要判断一下
 //如果是最后一列就不必打印竖杆
 if(j<col-1)
 printf(&quot;|&quot;);
 }
 printf(&quot;\n&quot;);
 for (j = 0; j < col; j++)
 {
 printf(&quot;---&quot;);
 //这里跟上面用一个意思
 if (j < col - 1)
 printf(&quot;|&quot;);
 }
 printf(&quot;\n&quot;);
 }
}

玩家下棋

玩家下棋的方式是输入坐标 ,所以需要接收玩家输入的数据,我们要考虑到
用户友好性

因为我们使用的是二维数组,
下标都是从0开始的,而玩家不一定是程序员,是不知道的这件事的,所以用户输入的数据时要注意处理

null
处理用户输入的数据只需将它-1即可,
比如玩家输入坐标&quot;3 3&quot;,在后台改变数组的时候就将它减1变成“2 2”同时还有判断当前坐标是否已经被占用了,如果被占用了还需要提醒玩家重新输入,直到输入正确为止,所以我们要使用到循环结构 
坐标没有被占用,我们就可以将数组对应的位置放字符“ * ”

void PlayerMove(char board[ROW][COL], int row, int col)
{
 while (1)
 {
 printf(&quot;请输入坐标>:&quot;);
 int x = 0;
 int y = 0;
 scanf(&quot;%d %d&quot;, &x, &y);
 //判断坐标是否合法
 if (x <= 3 && x > 0 && y <= 3 && y > 0)
 {
 if (board[x - 1][y - 1] == ' ')
 {
 board[x - 1][y - 1] = '*';
 break;
 }
 else
 {
 printf(&quot;坐标被占用,不能下棋,请选择其他位置\n&quot;);
 }
 }
 else
 {
 printf(&quot;坐标非法,请重新输入\n&quot;);
 }
 }
}

电脑下棋

电脑下棋使用
rand函数获取随机值
,将它坐标按行和列取模,比如随机值是100,那么取模行row之后,就为1,
电脑下棋就不用判断坐标是否合法了,只需要确定它取模后的数字范围在0~2之间即可
还需要注意获取的坐标是否被占用即可

因为我们是使用随机值的方式让电脑下棋,使用这样的方式下棋,电脑是很“笨”的,不会对我们下的棋进行拦截,简而言之就是不够“智能”,当然如果, 你感兴趣的话,可以对它进行一些算法处理,比如穷举一些玩家可能性下的棋,对它进行预判等等,让它变得更加“智能”一些,但本文章就不做优化了,留给你们自行处理!

//电脑下棋
void ComputerMove(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;
 }
 }
}

null

判断输赢

判断输赢大概就以下几种,我们可以使用枚举的方式,也可以使用循环的方式去判断,下面我给出两个版本,
一个是枚举的,一个是循环的
我们规定玩家赢返回“ * ”,电脑赢返回“ # ”,平局返回“Q”,进行游戏返回“0”

null
枚举

//版本1.0枚举
//玩家赢返回&quot;*&quot;,电脑赢返回&quot;&quot;#,棋盘满了返回&quot;Q&quot;
char DetermineEnd1(char board[ROW][COL])
{
 //玩家赢的情况
 if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='*')
 || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '*')
 || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '*')
 || (board[0][0] == board[1][0] && board[1][0] == board[2][0]&&board[0][0] == '*')
 || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '*')
 || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '*')
 || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '*')
 || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] == '*'))
 {
 
 return '*';
 }
 //电脑赢的情况
 else if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='#')
 || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '#')
 || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '#')
 || (board[0][0] == board[1][0] && board[1][0] == board[2][0]&&board[0][0] == '#')
 || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '#')
 || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '#')
 || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '#')
 || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][02] == '#'))
 {
 
 return '#';
 }
 else
 {
 //判断棋盘是否满了
 for (int i = 0; i < ROW; i++)
 {
 for (int j = 0; j < COL; j++)
 {
 if (board[i][j] == ' ')
 return '0';
 }
 }
 }
 //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局
 return 'Q';
}

其实循环和枚举在这里都是大同小异。

循环

//版本2.0循环
// 判断游戏是否结束 玩家赢返回&quot;*&quot;,电脑赢返回&quot;#&quot;,棋盘满了返回&quot;Q&quot;
char DetermineEnd2(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][1] != ' '))
 {
 return board[i][1];
 }
 }
 //判断列
 for (i = 0; i < row; i++)
 {
 if ((board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' '))
 {
 return board[1][i];
 }
 }
 //判断对角
 if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
 {
 return board[1][1];
 }

 if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
 {
 return board[1][1];
 }
 //判断棋盘是否满了
 for (int i = 0; i < ROW; i++)
 {
 for (int j = 0; j < COL; j++)
 {
 //其中有一个空格就说明还没满,返回&quot;0&quot;
 if (board[i][j] == ' ')
 return '0';
 }
 }
 //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局
 return 'Q';
}

小结语

其实不管是什么小游戏,看似代码很大,其实如果理解其中的思想,就知道他们都有一个共同点,都是一些基本操作组装起来。

接下来才是最关键的一步,就是将它们组装起来,形成一个可以运行的程序我把它实现在text.c文件中。

null

text.c文件

text.c为工程测试文件

#define _CRT_SECURE_NO_WARNINGS

#include&quot;game.h&quot;
void menu()
{
 printf(&quot;******************************\n&quot;);
 printf(&quot;******* 1. play 0. exit*****\n&quot;);
 printf(&quot;******************************\n&quot;);
}

void game()
{
 
 char board[ROW][COL] = { 0 };
 //先初始化棋盘
 InitBoard(board, ROW, COL);
 //打印棋盘让玩家下棋
 BoardPrint(board, ROW, COL);
 char ret;
 while (1)
 {
 printf(&quot;玩家下棋\n&quot;);
 PlayerMove(board, ROW, COL);
 ret= DetermineEnd2(board,ROW,COL);
 if (ret != '0')
 {
 break;
 }
 BoardPrint(board, ROW, COL);
 printf(&quot;电脑下棋\n&quot;);
 ComputerMove(board, ROW, COL);
 ret = DetermineEnd2(board, ROW, COL);
 if (ret != '0')
 {
 break;
 }
 BoardPrint(board, ROW, COL);
 }
 if (ret == '*')
 {
 printf(&quot;玩家赢\n&quot;);
 }
 else if (ret == '#')
 {
 printf(&quot;电脑赢\n&quot;);
 }
 else
 {
 printf(&quot;平局\n&quot;);
 }
 BoardPrint(board, ROW, COL);

}
int main()
{
 //初始化随机值
 srand((unsigned int)time(NULL));
 //接收用户选择开始游戏,或者退出游戏的变量
 int input = 0;
 do 
 {
 menu();
 printf(&quot;请选择>:&quot;);
 scanf(&quot;%d&quot;, &input);
 switch (input)
 {
 case 1:
 game();
 break;
 case 0:
 printf(&quot;退出成功\n&quot;);
 break;
 default:
 printf(&quot;无此选项,请重新选择\n&quot;);
 break;
 }

 } while (input);
 return 0;
}

game.c文件

game.c为工程函数实现文件

#define _CRT_SECURE_NO_WARNINGS

#include&quot;game.h&quot;


//初始化成空格
void InitBoard(char board[ROW][COL], int row, int col)
{
 int i = 0;
 int j = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col; j++)
 {
 board[i][j] = ' ';
 }
 }
}

//打印棋盘
void BoardPrint(char board[ROW][COL], int row, int col)
{
 int i = 0;
 int j = 0;
 for (i = 0; i < row; i++)
 {
 for (j = 0; j < col; j++)
 {
 printf(&quot; %c &quot;,board[i][j]);
 if(j<col-1)
 printf(&quot;|&quot;);
 }
 printf(&quot;\n&quot;);
 for (j = 0; j < col; j++)
 {
 printf(&quot;---&quot;);
 if (j < col - 1)
 printf(&quot;|&quot;);
 }
 printf(&quot;\n&quot;);
 }
}


void PlayerMove(char board[ROW][COL], int row, int col)
{
 while (1)
 {
 printf(&quot;请输入坐标>:&quot;);
 int x = 0;
 int y = 0;
 scanf(&quot;%d %d&quot;, &x, &y);
 //判断坐标是否合法
 if (x <= 3 && x > 0 && y <= 3 && y > 0)
 {
 if (board[x - 1][y - 1] == ' ')
 {
 board[x - 1][y - 1] = '*';
 break;
 }
 else
 {
 printf(&quot;坐标被占用,不能下棋,请选择其他位置\n&quot;);
 }
 }
 else
 {
 printf(&quot;坐标非法,请重新输入\n&quot;);
 }
 }
}

//电脑下棋
void ComputerMove(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;
 }
 }
//}
////版本1.0
////玩家赢返回&quot;*&quot;,电脑赢返回&quot;&quot;#,棋盘满了返回&quot;Q&quot;
//char DetermineEnd1(char board[ROW][COL])
//{
// //玩家赢的情况
// if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='*')
// || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '*')
// || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '*')
// || (board[0][0] == board[1][0] && board[1][0] == board[2][0]&&board[0][0] == '*')
// || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '*')
// || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '*')
// || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '*')
// || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] == '*'))
// {
// 
// return '*';
// }
// //电脑赢的情况
// else if ((board[0][0] == board[0][1] && board[0][1] == board[0][2]&&board[0][0]=='#')
// || (board[1][0] == board[1][1] && board[1][1] == board[1][2] && board[1][0] == '#')
// || (board[2][0] == board[2][1] && board[2][1] == board[2][2] && board[2][0] == '#')
// || (board[0][0] == board[1][0] && board[1][0] == board[2][0]&&board[0][0] == '#')
// || (board[0][1] == board[1][1] && board[1][1] == board[2][1] && board[0][1] == '#')
// || (board[0][2] == board[1][2] && board[1][2] == board[2][2] && board[0][2] == '#')
// || (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] == '#')
// || (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][02] == '#'))
// {
// 
// return '#';
// }
// else
// {
// //判断棋盘是否满了
// for (int i = 0; i < ROW; i++)
// {
// for (int j = 0; j < COL; j++)
// {
// if (board[i][j] == ' ')
// return '0';
// }
// }
// }
// //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局
// return 'Q';
//}

//版本2.0
// 判断游戏是否结束 玩家赢返回&quot;*&quot;,电脑赢返回&quot;#&quot;,棋盘满了返回&quot;Q&quot;
char DetermineEnd2(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][1] != ' '))
 {
 return board[i][1];
 }
 }
 //判断列
 for (i = 0; i < row; i++)
 {
 if ((board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' '))
 {
 return board[1][i];
 }
 }
 //判断对角
 if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
 {
 return board[1][1];
 }

 if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
 {
 return board[1][1];
 }
 //判断棋盘是否满了
 for (int i = 0; i < ROW; i++)
 {
 for (int j = 0; j < COL; j++)
 {
 //其中有一个空格就说明还没满,返回&quot;0&quot;
 if (board[i][j] == ' ')
 return '0';
 }
 }
 //程序走到这里就说明没有分出胜负,而且棋盘已经满了,故为和局
 return 'Q';
}

game.h

game.h为工程的函数和变量声明的头文件

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 3 //行
#define COL 3 //列

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void BoardPrint(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);

// 版本1.0 判断游戏是否结束 玩家赢返回&quot;*&quot;,电脑赢返回&quot;#&quot;,棋盘满了返回&quot;Q&quot;
char DetermineEnd1(char board[ROW][COL]);


//版本2.0 判断游戏是否结束 玩家赢返回&quot;*&quot;,电脑赢返回&quot;#&quot;,棋盘满了返回&quot;Q&quot;
char DetermineEnd2(char board[ROW][COL],int row,int col);


原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://xie.infoq.cn/article/0e612987f9cb8238591650933

随机推荐