网站建设哪里有,flashfxp上传网站模板,网站后台登陆验证码不对,请勿直接解析到ip否则我司不能保证您的网站能正常运行!"三子棋 1. 前言2. 准备工作3. 使用二维数组存储下棋的数据4. 初始化棋盘为全空格5. 打印棋盘6. 玩家下棋7. 电脑下棋8. 判断输赢9. 效果展示10. 完整代码 1. 前言
大家好#xff0c;我是努力学习游泳的鱼#xff0c;今天我们会用C语言实现三子棋。所谓三子棋#xff0c;就是… 三子棋 1. 前言2. 准备工作3. 使用二维数组存储下棋的数据4. 初始化棋盘为全空格5. 打印棋盘6. 玩家下棋7. 电脑下棋8. 判断输赢9. 效果展示10. 完整代码 1. 前言
大家好我是努力学习游泳的鱼今天我们会用C语言实现三子棋。所谓三子棋就是三行三列的棋盘玩家可以和电脑下棋率先连成三个的获胜。话不多说我们开始吧。
2. 准备工作
我们可以在一个项目中创建三个文件分别是
test.c测试游戏的逻辑。game.c游戏的实现。game.h函数声明符号的定义。
测试这个游戏时我们玩一把肯定不过瘾所以需要使用do while循环每次可以选择继续玩或者退出游戏。先把大致的框架搭出来。
#include stdio.hvoid menu()
{printf(****************************\n);printf(******** 1. play ******\n);printf(******** 0. exit ******\n);printf(****************************\n);
}int main()
{int input 0;do{menu();printf(请选择(1/0):);scanf(%d, input);switch (input){case 1:printf(三子棋\n);break;case 0:printf(退出游戏\n);break;default:printf(选择错误\n);break;}} while (input);return 0;
}当然我们的游戏不可能只是打印“三子棋”这三个字这么简单具体的实现我们会封装成一个函数暂且取名为game。
3. 使用二维数组存储下棋的数据
当我们下三子棋的时候需要把下棋的数据存起来。由于三子棋的棋盘是3×3的我们就需要一个三行三列的数组来存储下棋的数据。char board[3][3] { 0 };
4. 初始化棋盘为全空格
当我们还没开始下棋时棋盘上应该啥都没有但是真的是啥都没有吗事实上如果我们打印棋盘时能打印出“没有棋子”的效果数组里应该是全空格。所以我们需要写一个函数初始化棋盘为全空格。InitBoard(board, 3, 3);这个函数的声明我们会放在game.h里。具体的实现我们会放在game.c里。以下的函数同理。该函数的声明void InitBoard(char board[3][3], int row, int col);以下的函数均省略声明。该函数的实现只需遍历这个二维数组全部赋值为空格。
void InitBoard(char board[3][3], int row, int col)
{int i 0;for (; i row; i){int j 0;for (; j col; j){board[i][j] ;}}
}有没有发现这个程序中到处都要用到数组“三行三列”这个特点。如果我们想要改变这一点比如改成五行五列就需要改很多地方非常麻烦。怎么解决这个问题呢我们可以在game.h里定义两个常量ROW和COL这样每次只需要改变#define后的值就行了。
#define ROW 3
#define COL 3这样以上出现的所有3都可以用ROW和COL替代了此处省略。注意如果想使用game.h里的符号我们需要在game.c和test.c里引用这个头文件。引用自己写的头文件要用双引号。#include game.h而对于其他头文件如stdio.h我们不需要在game.c和test.c里包含两次只需在game.h里包含就行了。
5. 打印棋盘
我们初始化棋盘后会想要看一看棋盘长啥样所以接下来写一个打印棋盘的函数DisplayBoard(board, ROW, COL); 如果你认为打印出数组的数据就行了从而这样写
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){int j 0;for (; j col; j){printf(%c, board[i][j]);}printf(\n);}
}实际运行时你会发现打印了但没完全打印。 事实上此时打印的是一堆空格非常难看。如果我们想打印得好看点可以考虑加上一些横向和竖向的分割。比如我设想了这样一种打印的效果 | |
---|---|---| |
---|---|---| | 假设把 | |
---|---|---当成一组总共需要打印3组为什么呢因为有3行。 每一组里面又分为数据行和分割行。我们需要先打印数据行再打印分割行。 | | // 数据行
---|---|--- // 分割行所以一个简单的想法是这么写
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){// 打印数据printf( %c | %c | %c \n, board[i][0], board[i][1], board[i][2]);// 打印分割的行printf(---|---|---\n);}
}效果如下 我们发现多打印了一行分割行所以打印分割行时要加一条判断不是最后一行才打印分割行。
if (i row - 1)printf(---|---|---\n);这样子打印就好看多了。 但是这么写的话就相当于一行一定要打印三列后面就没有改变的可能了。比如我把ROW和COL都改成5效果如下 所以这种写法还是不够好。最好的写法是再用一层循环控制列的打印。比如对于数据行 | | 我们可以把 | 当成一组数据打印三组最后一组就不用加上|了同上一种写法用一个if语句来控制。 对于分割行
---|---|---我们可以把
---|当成一组数据打印三组最后一组就不用加上|了还是用if语句来控制。 实现如下
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){// 打印数据int j 0;for (; 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);}}
}效果也没问题 若把ROW和COL改成10打印出来的效果如下 这样写出来的代码就比较通用具有比较强的可维护性。
6. 玩家下棋
接下来写玩家下棋的函数。player_move(board, ROW, COL); 先让玩家输入坐标若x和y都在1到3之间则输入的坐标合法在数组对应的位置是board[x-1][y-1]若该位置仍然是空格则这个位置没有被下过就把数组的这个元素改成*。由于若玩家输入的坐标非法或者被占用时需要重新输入故需要一个循环。
void player_move(char board[ROW][COL], int row, int col)
{int x 0;int y 0;printf(玩家下棋\n);while (1){printf(请输入坐标:);scanf(%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);}}
}玩家下棋后再把棋盘打印一下DisplayBoard(board, ROW, COL);效果如下
7. 电脑下棋
玩家下完棋后就轮到电脑下棋computer_move(board, ROW, COL); 我们让电脑随机下棋只需生成两个随机的坐标x和y若该位置是空格就下在这个位置如果是空格那就再次产生随机坐标可见这也需要一个循环来实现。
void computer_move(char board[ROW][COL], int row, int col)
{int x 0;int y 0;printf(电脑下棋\n);while (1){x rand() % row; // 0~2y rand() % col; // 0~2if (board[x][y] ){board[x][y] #;break;}}
}使用rand函数前需要使用srand函数来设置随机数生成器的起点。我们需要给srand函数传一个时间戳这就要用到time函数。srand((unsigned int)time(NULL));使用rand和srand都需要引用头文件stdlib.h使用time函数需要引用头文件time.h。 我们用一个循环就能实现玩家和电脑轮流下棋的效果。
while (1)
{// 玩家下棋player_move(board, ROW, COL);DisplayBoard(board, ROW, COL);// 电脑下棋computer_move(board, ROW, COL); // 随机下棋DisplayBoard(board, ROW, COL);
}8. 判断输赢
什么时候游戏就结束了呢如果玩家赢了或者电脑赢了或者平局游戏就结束了否则游戏继续。 我们来设计一个is_win函数来判断棋局是上面四种状态的哪一种。我们这么设计is_win函数的返回值
玩家赢 - *电脑赢 - #平局 - Q继续 - C
当棋局状态不是C时说明游戏结束了就跳出循环接着根据不同情况打印不同的结果。
char ret 0;
while (1)
{// 玩家下棋player_move(board, ROW, COL);DisplayBoard(board, ROW, COL);// 判断输赢ret is_win(board, ROW, COL);if (ret ! C){break;}// 电脑下棋computer_move(board, ROW, COL); // 随机下棋DisplayBoard(board, ROW, COL);ret is_win(board, ROW, COL);if (ret ! C){break;}
}
if (ret *)
{printf(玩家赢\n);
}
else if (ret #)
{printf(电脑赢\n);
}
else
{printf(平局\n);
}如何实现is_win函数呢只需判断每行每列每条对角线是否有三个同样的棋子就行了。 先判断行由于玩家赢和电脑赢都是返回对应的棋子——*和#所以直接把对应的数组元素返回就行了。
int i 0;
for (; 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 col; 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];
}如果有人赢了在前面的代码中就会返回。如果没人赢再来判断是否平局。如果棋盘没有空格了那就是平局。我们可以写一个is_full函数来判断棋盘是否满了。如果满了就返回1没满就返回0。
if (is_full(board, row, col) 1)
{return Q;
}由于is_full函数只是写给is_win函数的只需要在game.c这个文件内使用所以加上static。具体的实现只需要遍历数组若发现空格说明棋盘没满就返回0否则返回1。
static int is_full(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){int j 0;for (; j col; j){if (board[i][j] ){return 0; // 没有满}}}return 1; // 满了
}而如果没人赢也不是平局则游戏继续return C;即可。
9. 效果展示
写到这我们就把三子棋程序写完啦。接下来看看效果 玩家赢 电脑赢 平局
10. 完整代码
下面是完整的代码 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 DisplayBoard(char board[ROW][COL], int row, int col);// 玩家下棋
void player_move(char board[ROW][COL], int row, int col);// 电脑下棋
void computer_move(char board[ROW][COL], int row, int col);// 判断输赢
char is_win(char board[ROW][COL], int row, int col);game.c
#define _CRT_SECURE_NO_WARNINGS 1#include game.hvoid InitBoard(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){int j 0;for (; j col; j){board[i][j] ;}}
}//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i 0;
// for (; i row; i)
// {
// int j 0;
// for (; j col; j)
// {
// printf(%c, board[i][j]);
// }
// printf(\n);
// }
//}//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
// int i 0;
// for (; i row; i)
// {
// // 打印数据
// printf( %c | %c | %c \n, board[i][0], board[i][1], board[i][2]);
// // 打印分割的行
// if (i row - 1)
// printf(---|---|---\n);
// }
//}void DisplayBoard(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){// 打印数据int j 0;for (; 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 player_move(char board[ROW][COL], int row, int col)
{int x 0;int y 0;printf(玩家下棋\n);while (1){printf(请输入坐标:);scanf(%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 computer_move(char board[ROW][COL], int row, int col)
{int x 0;int y 0;printf(电脑下棋\n);while (1){x rand() % row; // 0~2y rand() % col; // 0~2if (board[x][y] ){board[x][y] #;break;}}
}static int is_full(char board[ROW][COL], int row, int col)
{int i 0;for (; i row; i){int j 0;for (; j col; j){if (board[i][j] ){return 0; // 没有满}}}return 1; // 满了
}char is_win(char board[ROW][COL], int row, int col)
{int i 0;// 判断行for (; 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 col; 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];}// 判断平局if (is_full(board, row, col) 1){return Q;}// 继续return C;
}test.c
#define _CRT_SECURE_NO_WARNINGS 1#include game.hvoid menu()
{printf(****************************\n);printf(******** 1. play ******\n);printf(******** 0. exit ******\n);printf(****************************\n);
}void game()
{// 三子棋小游戏的具体实现char ret 0;// 存放下棋的数据char board[ROW][COL] { 0 };// 初始化棋盘为全空格InitBoard(board, ROW, COL);// 打印棋盘DisplayBoard(board, ROW, COL);while (1){// 玩家下棋player_move(board, ROW, COL);DisplayBoard(board, ROW, COL);// 判断输赢ret is_win(board, ROW, COL);if (ret ! C){break;}// 电脑下棋computer_move(board, ROW, COL); // 随机下棋DisplayBoard(board, ROW, COL);ret is_win(board, ROW, COL);if (ret ! C){break;}}if (ret *){printf(玩家赢\n);}else if (ret #){printf(电脑赢\n);}else{printf(平局\n);}//DisplayBoard(board, ROW, COL);
}//
// 什么时候游戏就结束了
// 玩家赢 - *
// 电脑赢 - #
// 平局 - Q
// 继续 - C
//
int main()
{int input 0;srand((unsigned int)time(NULL));do{menu();printf(请选择(1/0):);scanf(%d, input);switch (input){case 1:game();break;case 0:printf(退出游戏\n);break;default:printf(选择错误\n);break;}} while (input);return 0;
}