您现在的位置是:首页 >技术交流 >C语言:简单的三子棋练习网站首页技术交流
C语言:简单的三子棋练习
文章目录
前言
三子棋练习对前面所学的知识进行运用与检验,三子棋这里就需要进行对函数进行封装成功能函数,因为三子棋代码较长肯定不能全部放在main函数中,要是全部放在main函数中代码很难重复使用,可读性也不好。
只要把函数封装成功能函数,代码的可读会也会提高,因为只要在函数前加注释就能很轻松的找到所需要的功能函数,也可以重复使用。
一、总体思路
需要完成的效果图
接下来介绍一下需要完成的功能
这里打错字了为了节省时间暂时先不改,
先来一起思考大概的制作流程
第一步需要打印开始与结束游戏,这一步可想而知就是直接用printf打印出来就行了。第二步这里需要完成选择开始的效果,这里用switch就能完成选择的效果。
第三步就是打印棋盘,先观察棋盘,棋盘里面一共有三种字符 空格跟分隔符与斜杠,只要用多层循环就能实现,对不对还得自己动手试。
第四步需要人机交互,这里可以先把人机交互功能分给两个功能函数实现。
第五步用常用的输入库函数scanf实现。
这里在补充一点,人机交互肯定是需要重复执行,所以这里直接用死循环
二、头文件
头文件比较重要还是单独拿出来讲解
什么是头文件,在我理解看来,一个项目中有多个源文件,打个比方A文件与B文件他们直接是独立存在的,要是A文件想调用B文件的函数,就得在头文件中声明B文件的函数就行了,话说回来头文件就是让两个独立的源文件直接存在联系。
三子棋头文件完整代码
#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 playerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断输赢
//玩家 - 'x'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C'
char IsWin(char board[ROW][COL], int row, int col);
三、实现流程
说了这么还是得动手实操,来跟我一起深入浅出的学习吧!
1、第一步
文件目录
game.h头文件用来声明函数
game.c游戏功能的实现
test游戏主体,从这个开始调用game.c
以上后面会细讲
这里是函数主体布局完整代码,我会分几步讲解
代码如下(示例):
int main()
{
int input = 0;
do
{
menu(); //开始界面打印
printf("请选择>");
scanf("%d", &input); //输入坐标
switch (input) //开始与结束选项
{
case 1:
printf("三子棋游戏
");
game(); //三子棋内部的实现
break;
case 0:
printf("退出游戏
");
default:
printf("选择错误,请重新输入
");
break;
}
} while (input);
return 0;
}
打印开始界面其实很简单用printf就能实现。
1.0、do-while补充说明
这里在对do-while循环不熟的人补充说明一下,对这个人就是我。do-while先执行在判断,判断结果为假直接结束,所以在这里运行do-while非常合适。
do
{
}
while();
1.1、menu()
开始界面打印
代码如下(示例):
void menu()
{
printf("*********************************
");
printf("********* 1.play ************
");
printf("********* 0.exit ************
");
printf("*********************************
");
}
**打印开始界面其实很简单用printf就能实现。
**
二、game函数三子棋核心功能
1.0game函数内部讲解
game()函数是三子棋核心函数
switch (input)
{
case 1:
printf("三子棋游戏
");
game(); //game()函数是三子棋核心函数
break;
case 0:
printf("退出游戏
");
default:
printf("选择错误,请重新输入
");
break;
}
完整game功能函数
void game()
{
//存储数据 - 二维数组
char board[ROW][COL];
//初始化棋盘 - 初始化
InitBoard(board, ROW, COL);
//打印一下棋盘 - 本质是打印数组的内容
DisplayBoard(board, ROW, COL);
char ret = 0;
while (1)
{
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C')
break;
//玩家下棋
playerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret=IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("玩家赢!
");
else if (ret == '#')
printf("电脑赢!
");
else
printf("平局!
");
}
1.1、InitBoard(初始化棋盘)
InitBoard完整代码
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i< row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
补充#define知识
#define预处理指令又称宏定义,可以定义常量往深入讲也可以定义函数,在三子棋中#define用来定义常量
#define ROW 3
#define COL 3
//二维数组用来存储棋盘数据
char board[ROW][COL]; //这种定义的好处是可以让三子棋变成五子棋
//初始化棋盘 - 初始化
InitBoard(board, ROW, COL); //1.把数组传入,2.初始化棋盘时用循环时肯定需要一个限定值,那就得把行跟列传过去
void InitBoard(char board[ROW][COL], int row, int col) //函数传参,char board[ROW][COL]这里传了个二维数组,后面的传的就是行跟列
{
int i = 0;
for (i = 0; i< row; i++) //初始化行
{
int j = 0;
for (j = 0; j < col; j++) //初始化列
{
board[i][j] = ' '; //把初始化的空格传给二维数组
}
}
}
1.2、DisplayBoard(打印棋盘完整代码)
DisplayBoard完整代码
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++) //遍历行
{
int j = 0;
for (j = 0; j < col; j++) //遍历列
{
printf(" %c ", board[i][j]); //数据的打印,%c两边还有两空格让打印的数据更加美观
if (j < col - 1) //
printf("|");
}
printf("
");
if (i < row - 1)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("
");
}
}
}
1.2.1、数据与分隔符的打印
为了让代码变得更加灵活,可以先遍历行跟列,让数据一个一个打印,在这里只需要改变行跟列的值就可以让棋盘变成n子棋,在这里打印的循序是先打印数据在打印|然后在打印—,
for (i = 0; i < row; i++) //遍历行
{
int j = 0;
for (j = 0; j < col; j++) //遍历列
{
printf(" %c ", board[i][j]); //数据的打印,%c两边还有两空格让打印的数据更加美观
if (j < col - 1) //col - 1为了美观行会少打印最后一列
printf("|");
}
printf("
"); //第一行数据跟分隔符打印完换行
if (i < row - 1) //少打印最后一行---
{
int j = 0;
for (j = 0; j < col; j++) //换行后在打印---
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("
");
}
1.3、playerMove(玩家下棋)
playerMove完整代码
//玩家下棋
void playerMove(char board[ROW][COL], int row, int col)
{
//定义x,y坐标
int x = 0;
int y = 0;
printf("玩家走:>
");
while (1) //死循环
{
printf("请输入下棋坐标:>");
scanf("%d %d", &x, &y);
//判断坐标合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//下棋
//坐标是否被占用
if (board[x - 1][y - 1] == ' ') //数组下标是从0开始计算,正常人思维是从1开始计算,所以要-1
{
//x-1是之前x>1判断坐标是否被占用
//玩家下棋
board[x - 1][y - 1] = '*';
break; //下完一次退出
}
else
{
printf("坐标被占用,请重新输入
");
}
}
else
{
printf("坐标非法,请重新输入
");
}
}
}
1.4、ComputerMove(电脑下棋)
ComputerMove完整代码
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
int x = 0;
int y = 0;
printf("电脑下棋:>
");
//给电脑随机下棋坐标
while (1)
{
//电脑自动下棋核心部分
x = rand() % row; //限制电脑x的坐标,假如row值为3,行随机范围为0-3,y同理
y = rand() % col;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
1.5、重难点判断输赢
判断输赢
玩家 - ‘x’
电脑赢 - ‘#’
平局 - ‘Q’
继续 - ‘C’
IsWin完整代码
//判断输赢
char IsWin(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 < row; 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[1][1] != ' ')
return board[1][1];
if(board[0][2] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[1][1];
//平局
if (IsFull(board, row, col)==1)
{
return 'Q';
}
//继续
return 'C';
}
1.5.1、行
加张图有利于理解
用循环遍历每行下棋情况,还有就是不能等于空格
//行
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]; //返回第一列遍历每行的值
}
}
1.5.2、列
加张图有利于理解
跟行同理
//列
for (i = 0; i < row; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
{
return board[0][i];
}
}
1.5.3、对角线
加张图有利于理解
//对角线
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][2] && board[1][1] != ' ')
return board[1][1];
1.5.4、平局
棋盘man了返回Q
if (IsFull(board, row, col)==1)
{
return 'Q';
}
IsFull完整代码
棋盘为空格返回0,棋盘满了返回1
int IsFull(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
if (board[i][j]==' ')
{
return 0;
}
}
}
return 1;
}
1.5.5、继续
不是以上任何一种情况直接返回C
//继续
return 'C';
三、结尾函数的嵌套调用
玩家先后的下棋顺序可以调换顺序,最后的输赢打印放在while循环外就行就可以不需要重复打印
char ret = 0;
while (1)
{
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C') //只要不等于C结束游戏
break;
//玩家下棋
playerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret=IsWin(board, ROW, COL);
if (ret != 'C')
break;
}
if (ret == '*')
printf("玩家赢!
");
else if (ret == '#')
printf("电脑赢!
");
else
printf("平局!
");
总结
三子棋代码比较长,难度适中能很好的检验前面所学知识,有理解不好的地方还请大佬指教!
字算比较多有打错字的地方可以在评论区指正
希望能帮到各位