目录
Hello,大家好,今天我们用c语言简单写一个三子棋,话不多说,我们开始构思整体思路。
在此之前先看看代码的效果
思路构思:
1.定义一个game.h头文件,放函数声明,game.c放实现游戏的代码,test.c放主函数,调试游戏代码
2.输出棋盘
3.玩家跟电脑下棋
4.判断输赢,是否平局
5.结束
1.输出三子棋的棋盘
如上图,我们要输出棋盘,就要先定义一个3x3的二维数组,并且里面要放的值是空格,并且想要在棋盘中插入需要的棋子,那就定义为char类型,并且全部初始为空格。先创建game,h,game.c,test.c文件
头文件中放入 要包含的头文件,.c文件中包含头文件
//game.h
#include<stdio.h>
//game.c
#include "game.h"
//test.c
#include "game.h"
这样子,就可以在game.c跟test,c中愉快的使用库文件的函数了。
想好后高高兴兴打开代码一顿噼里啪啦,最后输出的效果如下
输出棋盘:
版本1
game.c
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 DisplayBoard(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("%c", board[i][j]);
}
printf("\n");
}//输出3x3棋盘
与预想的三子棋棋盘完全不一样,为什么会这样?仔细想想,前面我们初始化棋盘里面全是空格,因此,我们把空格改成任意字符(如X)再看看效果
OMG,原来已经把棋盘输出来了,但是还是没有达到我们预期的效果,动动小脑袋再思考,我们要达到想要的效果,看最开始的棋盘的第一行,先输出(空格+空格+竖线),第二行(横线+横线+横线+竖线),其中,边框是没有线条的,恍然大雾!!!
抄起键盘又是一顿啪啪啪。于是,版本二就诞生了!
game.c
void DisplayBoard(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(" %c |", board[i][j]);
}
printf("\n");
printf("---|---|---\n");
}
}//输出3x3棋盘
心想,不就是简简单单的棋盘吗,不就是在输出棋盘的时候,先打印(空格+初始化棋盘内容+空格+竖线)-->( %c |),这还不简单,其次就是到下面的竖线,只需要在他打印完第一行就直接输出---|---|---不就ok?然而你在运行代码时
WTF? 怎么边边也有横线跟竖线啊,跟我想的棋盘不一样啊,
看了半天,原来是循环里面,我们只需要打印两次竖线跟横线就可以了,而且在优化一下代码,我们先打印棋盘内容,在打印边边角角,并且给边边角角订上规则,你只能打印两次。
抄起键盘开干
game.c
void DisplayBoard(char board[ROW][COL], int row, int col) {
int i = 0;
int j = 0;
for (i = 0; i < row; i++) {
//1.打印数据
for (j = 0; j < col; j++) {
printf(" %c ", board[i][j]);
if (j < col - 1)打印两次竖线
printf("|");
}
printf("\n");
//2.打印分割线
if (i < row - 1) {
//printf("---|---|---\n");
for (j = 0; j < col; j++) {
printf("---");
if (j < col - 1)//打印两次分割线
printf("|");
}
printf("\n");
}
}//版本三
}//打印棋盘
ohohohoh!!!棋盘打印出来了 ,再把初始化棋盘内容改成空格,就可以达到预期效果了
2.玩家跟电脑下棋
棋盘做好了,那也得有人来下啊,因此我们可以先跟电脑solo一下。
思考:
1.玩家的坐标是怎么样的?(1~3)
2.如何让玩家在正确的位置下棋,下错地方了会怎么样?
3.如何判断改地方已经有棋子了,不能在下?
4.玩家可以下多少次?
玩家下棋
game.c
void PlayerMove(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) {//限定为1~3的坐标
if (board[x - 1][y - 1] == ' ') {
board[x - 1][y - 1] = 'X';
break;
}//要落子的地方都必须是棋盘初始化为空格的键盘内
//玩家棋子为'X'
//在输入数组的坐标中落子
else {
printf("坐标被占用,不能落子,请重新输入坐标\n");
//如果不是空格,就是该坐标已经有棋子了
//没有这个条件就会造成落子重复
}
}//坐标合法
else {
printf("坐标错误,重新输入!\n");
}//坐标非法
}//循环的目的是让玩家可以一直落子
}//玩家落子
test.c
void game() {
char board[ROW][COL] = { 0 };
char ret = 0;
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1) {
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
}
}//游戏代码
int main() {
int input = 0;
srand((unsigned int)time(NULL));
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新输入\n");
break;
}
} while (input);
}//主函数测试游戏代码
OK兄弟们,辛辛苦苦写完的代码测试一下
Nice!!!没有问题进行下一步
电脑下棋
电脑下棋嘛,由于是实现简单的三子棋,主打就就是随意下,因此有什么方法可以实现生成随机坐标呢,在c语言中,生成动态的随机数,可以使用rand()函数跟srand()函数,对于这两个函数的用法不多bb。在写之前,我们先想想电脑如何下棋
1.电脑的坐标跟玩家的坐标是否一致?
2.电脑下棋能不能随便乱下,是不是跟我家鸽鸽随地下蛋一样?
3.三子棋的坐标就是3x3的,怎么不让电脑生成的随机坐标不超过棋盘坐标?
4.电脑是不是可以多下几次?
game,c
void ComputerMove(char board[ROW][COL], int row, int col) {
printf("电脑下棋:>\n");
int x = 0;//0~row-1
int y = 0;//0~col-1
while (1) {
x = rand() % row;//求余最大随机值小于row,符号数组下标
y = rand() % col;
if (board[x][y] == ' ') {
board[x][y] = 'O';//定义电脑的棋子为'O'
break;
}//可以落子
}//不能落子就继续返回生成新的随机坐标
}//循环实现电脑落子
OK兄弟们,目光向代码看齐
void game() {
char board[ROW][COL] = { 0 };
char ret = 0;
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1) {
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
}
}
芜湖!!,有惊无险,终于把玩家跟电脑下棋给完成了 ,但是我都赢了,怎么没有判断我赢啊???少年(少女),你想得太简单了,你认为的赢,是电脑认为的赢吗?
电脑不认得你所谓的赢啊。因此,我们要写一些条件让电脑判断谁赢了,或者平局了。
3.判断输赢,或者平局
写之前,先思考一下
1.三子棋中一共有多少个条件能赢?
2.平局的条件是什么?
3.如何将输赢,或者平局让电脑知道
game.c
int IsFull(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] == ' ')
return 0;
}
}//判断棋盘是否全有空格,如果有说明棋盘没有满
return 1;//没有满就返回1
}//判断棋盘是否满了
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];
}
//if内条件错误示范:board[i][0] == board[i][1] && board[i][1]== 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[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[0][2];
//平局
//平局返回'Y'
//否则游戏继续返回'C'
if (IsFull(board, row, col) == 1)
return 'Y';
return 'C';//没有赢或平局继续
}
test.c
void game() {
char board[ROW][COL] = { 0 };
char ret = 0;
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1) {
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C') {
break;
}//判断游戏是否结束
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C') {
break;
}
}
if (ret == 'X')
printf("玩家赢!\n");
else if (ret == 'O')
printf("电脑赢!\n");
else
printf("平局!\n");//巧妙运用返回值来判断输赢
}
这里,我们巧妙运用了返回值的关系,如果判断为赢则在调用游戏函数void game()中用ret接收返回值,其中每下一个棋就要判断是否继续,如果不等于‘C’,就直接退出循环判断输赢还有平局,这种函数调用跟返回值的关系,还有很多巧妙的运用关系,我们还需要不断努力学习才能达到预期效果。
源代码
//game.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3
//头文件声明函数
//菜单
void menu();
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int low);
//输出棋盘
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'
//电脑赢 返回 'O'
//平局 返回'Y'
//继续 返回'C'
char IsWin(char board[ROW][COL], int row, int col);
//判断棋盘是否满了
int IsFull(char board[ROW][COL], int row, int col);
//game.c
#pragma warning (disable:4996)
#include "game.h"
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;
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 (i = 0; i < row; i++) {
//1.打印数据
for (j = 0; j < col; j++) {
printf(" %c ", board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
//2.打印分割线
if (i < row - 1) {
//printf("---|---|---\n");
for (j = 0; j < col; j++) {
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}//版本三
}//打印棋盘
void PlayerMove(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) {//限定为1~3的坐标
if (board[x - 1][y - 1] == ' ') {
board[x - 1][y - 1] = 'X';
break;
}//要落子的地方都必须是棋盘初始化为空格的键盘内
//玩家棋子为'X'
//在输入数组的坐标中落子
else {
printf("坐标被占用,不能落子,请重新输入坐标\n");
//如果不是空格,就是该坐标已经有棋子了
//没有这个条件就会造成落子重复
}
}//坐标合法
else {
printf("坐标错误,重新输入!\n");
}//坐标非法
}//循环的目的是让玩家可以一直落子
}
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col) {
printf("电脑下棋:>\n");
int x = 0;//0~row-1
int y = 0;//0~col-1
while (1) {
x = rand() % row;//求余最大随机值小于row,符号数组下标
y = rand() % col;
if (board[x][y] == ' ') {
board[x][y] = 'O';
break;
}//可以落子
}
}
int IsFull(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] == ' ')
return 0;
}
}//判断棋盘是否全有空格,如果有说明棋盘没有满
return 1;//没有满就返回1
}
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];
}
//if内条件错误示范:board[i][0] == board[i][1] && board[i][1]== 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[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[0][2];
//平局
//平局返回'Y'
//否则游戏继续返回'C'
if (IsFull(board, row, col) == 1)
return 'Y';
return 'C';//没有赢或平局继续
}
//test.c
#pragma warning (disable:4996)
#include "game.h"
void game() {
char board[ROW][COL] = { 0 };
char ret = 0;
//初始化棋盘
InitBoard(board, ROW, COL);
//打印棋盘
DisplayBoard(board, ROW, COL);
while (1) {
//玩家下棋
PlayerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C') {
break;
}//判断游戏是否结束
//电脑下棋
ComputerMove(board, ROW, COL);
DisplayBoard(board, ROW, COL);
//判断输赢
ret = IsWin(board, ROW, COL);
if (ret != 'C') {
break;
}
}
if (ret == 'X')
printf("玩家赢!\n");
else if (ret == 'O')
printf("电脑赢!\n");
else
printf("平局!\n");//巧妙运用返回值来判断输赢
}
int main() {
int input = 0;
srand((unsigned int)time(NULL));
do {
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新输入\n");
break;
}
} while (input);
}
最后乱杀电脑
结语
总结:代码的可读性还是挺好的,当然博主在写的时候,遇到了某些致命的低级错误,找了一个多小时,总之,代码基础还是不够好,还需要不断继续练习。