目录
1.游戏准备
2.主函数
3.test函数
5.game函数
6.棋盘初始化
7.棋盘打印
8.玩家下棋
9.电脑下棋
10.判断输赢
11.游戏完整代码展示
11.1 game.h 游戏头文件
11.2 game.c 游戏逻辑实现
11.3 程序运行逻辑
前言:博主目前学习到了二维数组,结合网上的教程,实现了一个三子棋小游戏,虽然和我下棋的电脑憨憨的,让它赢都可能赢不了的那种。但是写程序的过程以及实现游戏后让人非常有成就感,同时也能很好的总结之前学的知识点(这个游戏运用了很多之前的知识,需要你对它们有透彻的理解)。
实现效果如图(故意让电脑赢的),由于博主水平有限,实现的样子简陋,希望不久的将来能写出图形化界面(等我回来修改它)
话不多说,我们开始吧。
要实现一个游戏我们会写很多的代码,为了不让代码看得混乱,同时也确保代码的整洁性,我把整个代码分成了三个部分。
game.c ---> 存放游戏相关函数
game.h ---> 游戏相关函数以及库函数的声明
test.c ---> 游戏的逻辑实现。
1.玩家下的棋子我们用 ' * ' 表示,电脑下的棋子我们用‘ # ’表示
2.下的棋子当然是要放进棋盘中,我们用二维数组代表棋盘
3.棋盘的大小我们用宏来定义,方便我们修改
4.在实现函数时,确保函数只实现一个功能,使其成为独立的代码块
int main()
{
srand((unsigned int)time(NULL));//设置随机数种子
test();
return 0;
}
为什么要使用srand函数后面就会知道了哈,等等我会解释
在函数中调用test函数,test实现全部代码的逻辑
void test()
{
int input = 0;
do
{
menu();//打印菜单,让玩家选择是否开始游戏
printf("请选择 > 1/0 \n");
scanf("%d", &input);//input为是否游戏的判断依据
switch (input)//switch语句进入不同分支
{
case 1:
game();//game函数实现游戏过程的逻辑
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);//循环,让玩家决定是否继续游戏
}
void menu()
{
printf("***********************\n");
printf("**** 1.开始游戏 ****\n");
printf("**** 0.退出游戏 ****\n");
printf("***********************\n");
}
接着是game函数的实现,game是游戏过程的逻辑:初始化棋盘 --> 打印棋盘 --> 玩家下棋 --> 电脑下棋 --> 判断谁赢得比赛,比赛有四种情况,我们用四种字符来表示
'#' --- 电脑胜利
'*' --- 玩家胜利
'C' --- 游戏继续
'p' --- 游戏平局
为什么要用字符表示游戏的状态?因为我们可以根据字符输入游戏结果,或者让游戏继续。这又是什么意思?别急,我们再来看看game函数。
game函数实现游戏的逻辑,根据我们梳理的逻辑先来试着理解一下游戏的实现过程
void game()
{
char board[ROW][COL];//棋盘的创建
InitBoard(board, ROW, COL);//初始化棋盘
DisplayBoard(board, ROW, COL);//给玩家展示棋盘
char con = 0;//判断游戏是否继续的变量
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//下完棋后展示棋盘
//每一次下完棋后都要判断双方谁赢
con = is_win(board, ROW, COL);//该函数会返回我所列出的四种情况所对应的四种字符
if (con != 'C')//根据is_win函数的返回值决定游戏是否继续
{
break;
}
ComMove(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);//展示棋盘
//每次下完棋的判断
con = is_win(board, ROW, COL);
if (con != 'C')
{
break;
}
}//注意我设置了一个死循环
//如果is_win函数返回继续游戏的字符,重复下棋的函数,
//下棋的动作会一直循环直到返回其他字符,跳出循环
//根据返回值打印游戏结果
if (con == '*')
{
printf("玩家获胜\n");
}
else if (con == '#')
{
printf("电脑获胜\n");
}
else if(con == 'Q')
{
printf("平局\n");
}
}
现在理解了吗?con为字符变量,它用来接收is_win函数返回的字符,程序再根据con的字符判断是否要继续游戏,还是要结束游戏,打印游戏结果。
由于每个函数的参数基本相同,代表的意义也类似,这里先说明每个参数的意思,之后不再赘述
char board[ROW][COL]:用二维数组接收我们创建的棋盘,行与列为宏定义的常量,方便我们对棋盘的修改
int row, int col :用row与col存放数组的行与列,在循环时我们会用到这两个变量
void InitBoard(char board[ROW][COL], int row, int col)
{
//用两层for循环遍历数组,把数组的每个元素初始化为空格
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ' ';
}
}
}
这样一个棋盘要如何打印?从一行来看,一个格子由空格、字符、空格组成,然后是一个 | 用来分割棋盘,当打印第三次空格、字符、空格时最后的 | 不打印。接着是三个 - 和一个 | ,当第三次打印---时 | 不打印(对于不打印我们用if语句来实现)。
void DisplayBoard(char board[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
//两层for循环打印
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
//一行中打印空格、字符、空格
printf(" %c ",board[i][j]);
//if语句限制'|'出现次数
if (j < col - 1)
{
printf("|");
}
}
printf("\n");//打印完一行记得要换行
//三个“---”只打印两次,这里也用if语句来实现
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)
{
//下棋前判断是否在棋盘范围内
while (1)//设置死循环
{
int i = 0;
int j = 0;
printf("玩家输入坐标 > ");
scanf("%d %d", &i, &j);
if (i > 0 && i <= ROW && j > 0 && j <= COL)
{
//下棋前判断格子是否被占用
//假设我们输入2 2,表示要在2行2列下棋,但数组的下标从0开始
//所以这里对输入的值减1才能正确访问数组
if (board[i - 1][j - 1] == ' ')
{
board[i - 1][j - 1] = '*';
break;//玩家下棋的操作成功时跳出死循环
}
else
{
printf("格子被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
这里让电脑产生两个随机数作为数组的行与列(用rand函数产生随机数,其用法我在之前的博客详细讲解过,想具体查看可以点击下方链接【c语言】rand,srand讲解(输出一个随机数)_.SacaJawea的博客-CSDN博客_输出随机数),但也要对坐标进行判断,看坐标是否被占用,而产生的随机数范围在0~8,所以不用对随机数减1
//电脑下棋用'#'表示
void ComMove(char board[ROW][COL], int row, int col)
{
//下棋的逻辑与玩家下棋类似
printf("电脑下棋\n");
while (1)
{
int i = rand() % ROW;
int j = rand() % COL;
//由于产生的是随机数,判断是否非法
if (i >= 0 && i < ROW && j >= 0 && j < COL)
{
//判断是否被占用
if (board[i][j] == ' ')
{
board[i][j] = '#';
break;
}
}
}
}
玩家或电脑每下一步棋后,都要对输赢进行判断,当三个相同棋子连成线表示一方获得胜利(当然我们还要确保棋子不是空格),判断哪一方胜利我们可以用if语句写,除了电脑与玩家胜利,还有谁都没赢的情况,在棋盘没下满时我们要继续游戏,棋盘下满时,游戏平局同时也结束游戏。
这里一共四种情况
'#' --- 电脑胜利
'*' --- 玩家胜利
'C' --- 游戏继续
'p' --- 游戏平局
将函数的返回类型设置成char,每种情况我们都返回一种字符,根据返回的字符判断游戏该怎么进行
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < ROW; i++)
{
//行的判断
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
return board[0][i];
}
for (i = 0; i < COL; i++)
{
//列的判断
if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[0][i] != ' ')
return board[i][0];
}
//对角线判断
if (board[0][0] == board[1][1] && board[2][2] == board[0][0] && board[0][0] != ' ')
{
return board[0][0];
}
else if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
else
{
return 'C';
}
//当没人分出胜负,并且棋盘下满的情况
if (is_full == 0)
{
return 'Q';
}
}
//判断棋盘是否下满的函数
int is_full(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++)
{
if (board[i][j] == ' ');
//如果没下满返回1
return 1;
}
}
//没有下满返回0
return 0;
}
#pragma once
#include
#include
//棋盘大小数据
#define ROW 3
#define COL 3
//打印游戏菜单
void menu();
//初始化棋盘
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 ComMove(char board[ROW][COL], int row, int col);
//判断棋盘是否被占满
int is_full(char board[ROW][COL], int row, int col);
//判断哪一方赢
char is_win(char board[ROW][COL], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//游戏菜单
void menu()
{
printf("***********************\n");
printf("**** 1.开始游戏 ****\n");
printf("**** 0.退出游戏 ****\n");
printf("***********************\n");
}
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
//用两层for循环遍历数组,把数组的每个元素初始化为空格
int i = 0;
int 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;
int j = 0;
//同样是用两层for循环打印
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
//一行中打印空格、字符、空格
printf(" %c ",board[i][j]);
//if语句限制'|'出现次数
if (j < col - 1)
{
printf("|");
}
}
printf("\n");//打印完一行记得要换行
//三个---只打印两次,这里也用if语句来实现
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)
{
//下棋前判断是否在棋盘范围内
while (1)//设置死循环
{
int i = 0;
int j = 0;
printf("玩家输入坐标 > ");
scanf("%d %d", &i, &j);
if (i > 0 && i <= ROW && j > 0 && j <= COL)
{
//下棋前判断格子是否被占用
//假设我们输入2 2,表示要在2行2列下棋,但数组的下标从0开始
//所以这里对输入的值减1才能正确访问数组
if (board[i - 1][j - 1] == ' ')
{
board[i - 1][j - 1] = '*';
break;//玩家下棋的操作成功时跳出死循环
}
else
{
printf("格子被占用,请重新输入\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
//电脑下棋
//电脑下棋用'#'表示
void ComMove(char board[ROW][COL], int row, int col)
{
//下棋的逻辑与玩家下棋类似
printf("电脑下棋\n");
while (1)
{
int i = rand() % ROW;
int j = rand() % COL;
//由于产生的是随机数,判断是否非法
if (i >= 0 && i < ROW && j >= 0 && j < COL)
{
//判断是否被占用
if (board[i][j] == ' ')
{
board[i][j] = '#';
break;
}
}
}
}
//判断棋盘是否下满的函数
int is_full(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++)
{
if (board[i][j] == ' ');
//如果没下满返回1
return 1;
}
}
//没有下满返回0
return 0;
}
//判断哪一方赢
char is_win(char board[ROW][COL], int row, int col)
{
int i = 0;
for (i = 0; i < ROW; i++)
{
//行的判断
if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')
return board[0][i];
}
for (i = 0; i < COL; i++)
{
//列的判断
if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[0][i] != ' ')
return board[i][0];
}
//对角线判断
if (board[0][0] == board[1][1] && board[2][2] == board[0][0] && board[0][0] != ' ')
{
return board[0][0];
}
else if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != ' ')
{
return board[0][2];
}
else
{
return 'C';
}
//当没人分出胜负,并且棋盘下满的情况
if (is_full == 0)//根据返回值判断棋盘是否下满
{
return 'Q';
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void game()
{
char board[ROW][COL];//棋盘的创建
InitBoard(board, ROW, COL);//初始化棋盘
DisplayBoard(board, ROW, COL);//给玩家展示棋盘
char con = 0;//判断游戏是否继续的变量
while (1)
{
PlayerMove(board, ROW, COL);//玩家下棋
DisplayBoard(board, ROW, COL);//下完棋后展示棋盘
//每一次下完棋后都要判断双方谁赢
con = is_win(board, ROW, COL);//该函数会返回我所列出的四种情况所对应的四种字符
if (con != 'C')//根据is_win函数的返回值决定游戏是否继续
{
break;
}
ComMove(board, ROW, COL);//电脑下棋
DisplayBoard(board, ROW, COL);//展示棋盘
//每次下完棋的判断
con = is_win(board, ROW, COL);
if (con != 'C')
{
break;
}
}//注意我设置了一个死循环
//如果is_win函数返回继续游戏的字符,重复下棋的函数,
//下棋的动作会一直循环直到返回其他字符,跳出循环
//根据返回值打印游戏结果
if (con == '*')
{
printf("玩家获胜\n");
}
else if (con == '#')
{
printf("电脑获胜\n");
}
else if(con == 'Q')
{
printf("平局\n");
}
}
void test()
{
int input = 0;
do
{
menu();//打印菜单,让玩家选择是否开始游戏
printf("请选择 > 1/0 \n");
scanf("%d", &input);//input为是否游戏的判断依据
switch (input)//switch语句进入不同分支
{
case 1:
game();//game函数实现游戏过程的逻辑
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);//循环,让玩家决定是否继续游戏
}
int main()
{
srand((unsigned int)time(NULL));//设置随机数种子
test();
return 0;
}
最后还要一个小小的注意点,就是我们可以把需要的头文件都放再game.h中,之后的源文件就只需要引用game.h啦(引用自己的源文件我们习惯用" "双引号,c语言提供的源文件我们用<> 单括号) 。