C语言实现三子棋

通过一段时间的学习,我们已经能够较为熟练地使用分支语句,循环语句,创建函数,创建数组,创建随机数等。之前我们做过一个扫雷游戏,今天让我们再尝试创作一个三子棋游戏吧~

一、三子棋游戏的思路

三子棋的游戏规则很简单,游戏界面是一个3*3的棋盘,玩家双方依次向棋盘中放置棋子,我的棋子用'X'表示,对手的棋子用'O'表示,我们需要让自己的棋连在一起,同时还要阻拦对方的棋子组成连线,当有三个相同的棋子横向相连,纵向相连,或者交叉相连,那么这个棋子的持有者就获胜了

好了,那么既然我们已经了解了三子棋的游戏思路,那让我们试着分析一下,想要用C语言制作出三子棋的游戏,大体需要分成几个步骤?

1.游戏菜单

创造出一个比较美观的游戏菜单,可以选择开始游戏和退出游戏。

2.三子棋的棋盘

创造出一个3*3的三子棋游戏棋盘

3.双方下棋

玩家输入坐标 ,向三子棋棋盘中放置棋子

(玩家落子需要输入坐标,电脑落子需要srand函数创造随机数)

4.判定胜负

通过对当前棋盘中棋子的计算,来判定是 ,或者和棋

二、三子棋游戏的游戏文件

与之前的扫雷游戏一样,还是需要分成头文件game.h源文件game.c源文件test.c 三个文件。

他们的作用分别是:

  • game.h :作为头文件,它用来存放创造游戏所需要的各种类型的全局变量 ,并且也用来实现编写代码使所必需的函数说明
  • game.c: 存放各种实现游戏功能所需要的函数。
  • test.c:作为编译的主程序,存放主函数,编写实现游戏功能的主要思想。

(这里使用三个文件只是为了方便,并且修改代码和改进代码时会更快捷,但其实不使用三个也是可以的)

三、实现游戏的各种函数

1.创建游戏菜单

就像我们平常打一个游戏一样,我们自己做出的三子棋游戏也是需要有选择菜单的。如果没有选择菜单就无法明确如何游戏。

cs 复制代码
void GameInte()
{
	printf("**************************\n");
	printf("----- 三 子 棋 游 戏 -----\n");
	printf("**************************\n");
	printf("*******   1.play   *******\n");
	printf("*******   0.quit   *******\n");
	printf("**************************\n");
	printf("是否游玩?请输入>:");
}

我们需要将菜单函数GameInte在源文件game.c中进行创建和定义,然后再在头文件game.h中对函数进行声明。就像这个样子:

2.完成棋盘初始化

因为需要创建一个3*3的棋盘,所以我们可以创建一个char型的二维数组,boardROWCOL,(我们把ROW和COL创建成常量,因为是三子棋直接设置成ROW和COL的值都为3就好,如此一来,如果以后像制作五子棋之类的棋盘就会更加简单。像这样

我们只需要用 -- 和 | 组成一个3*3的框架,然后再把棋子依次放入里面就可以了。而放入棋子之前需要把棋子的char值设定为' ',这样才算是空,往里下入棋子后随之改变在棋盘中对应坐标的符号就好了。

cs 复制代码
char BoardCreate(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] = ' ';
		}
	}
}

这样就能将一个二维数组的所有制都初始化为' '啦。

3.打印棋盘

打印棋盘要注意,不能直接把二维数组的所有值都输出就算成功 ,因为二维数组的初始化我们把所有值都定义为' '了 ,所以打印出来是不显示的,我们可以选择用 | 和 -- 等符号进行辅助,来打印出一个美观且成型的棋盘。

cs 复制代码
void PrintBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	printf(" ----+---+----\n");
	for (i = 0; i < row; i++)
	{
		printf(" | %c | %c | %c | \n", board[i][0], board[i][1], board[i][2]);
		printf(" ----+---+----\n");
	}
}

这样就能打印出一个3*3的棋盘啦,并且能够成功的将所有的元素都放进去。

棋盘样例:

4.玩家下棋

(因为玩家下棋是自动输入,而电脑下棋是随机数输出,所以需要放到两个函数中更加清晰易懂并且容易后期改良)

我们需要定义一个函数,实现玩家能够输入坐标,就能改变棋盘上的格局,比如玩家输入1,1,就能够让棋盘上多一个玩家的棋子。并且我们需要限定输入坐标的位置,不能超过也不能少于这个范围,同时也不能下到已经有棋子的位置。

cs 复制代码
void PlyaChess(char board[ROW][COL], int row, int col)
{
	int a;
	int b;
	while (1)
	{
		printf("请输入下棋的坐标:>");
		scanf("%d %d", &a, &b);
		int m = a - 1;
		int n = b - 1;
		if(a<=0||a>=row+1||b<=0||b>=col+1)
		{
			printf("输入错误,请重新输入:>");
			continue;
		}
		else if(board[m][n]!=' ')
		{
			printf("你是想叠罗汉吗?\n");
			printf("请重新输入:>\n");
			continue;
		}
		board[m][n] = 'X';
		PrintBoard(board, ROW, COL);
		break;
	}
}

像这样设定两个分支条件语句就能分别控制,制止两种的发生。

5.电脑下棋

电脑下棋就需要用到我们之前所提到的srand()函数和rand来创造随机数,还是在主函数输入srand((unsigned int)time(NULL))这串代码来使随机数rand的种子随时间变化而变化,让它成为真正的随机数,然后使用rand()%rowrand()%col来创造出两个随机数,让电脑在棋子上下棋,遇到有棋子的地方就goto到上面重新生成随机数。

cs 复制代码
void ComputerChess(char board[ROW][COL], int row, int col)
{
	again:
	int m = rand() % row;
	int n = rand() % col;
	Sleep(1000);
	if (board[m][n] == ' ')
	{
		board[m][n] = 'O';
	}
	else 
	{
		goto again;
	}
	PrintBoard(board, ROW, COL);
}

(我们可以利用sleep函数添加一个让电脑思考的时间(只是假思考~),这样能让游戏更加带有真实性和参与的感觉。注:需要添加#include <windows.h>头文件

这就是成功的制作出让电脑下棋的步骤啦。

6.判断赢输与和棋

我们可以通过计算棋盘上的行,列,以及交叉的区域中,是否有相连的三个相同符号来确定这局三子棋的最终结果。而对于行,列的计算其实还是比较简单的,我们只需要用两个for循环的嵌套,因为两个嵌套来表示board数组的话,正好是一排一排定义的,所以我们可以第一次用for(i=0;i<row;i++),先在外层循环里定义两个初始值为0的变量分别作为'X'和'O'的计数器,内层嵌套for(j=0;j<col;j++),然后判定每一行的各个元素是否为'X'或'O',如果是'X'则对应的计数器+1,如果是'O'也同理,如果判定出有一行中都为'X'或'O'则宣布谁获胜,游戏结束。

(将两个计数器放在外层循环的目的是:每次外层循环,i变化时都会进入第二列/行的输出,此时需要重新从0开始记起)

接下来让我们再思考一下如何判定交叉区域的输赢:因为定义的是3*3的棋盘,所以只有1133坐标和1331这两种情况,我们仍然可以用for循环来解决。

思路有了,敲代码吧~

判断行获胜:

cs 复制代码
int WinChess(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)//判断行获胜
	{
		int Asum = 0;
		int Bsum = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == 'X')
			{
				++Asum;
				if (Asum == 3)
					return 1;
			}
			else if(board[i][j] == 'O')
			{
				++Bsum;
				if (Bsum == 3)
					return 2;
			}
		}
	}
	

判断列获胜:

cs 复制代码
for (i = 0; i < row; i++)//判断列获胜
	{
		int Asum = 0;
		int Bsum = 0;
		for (j = 0; j < col; j++)
		{
			if (board[j][i] == 'X')
			{
				++Asum;
				if (Asum == 3)
					return 1;
			}
			else if (board[j][i] == 'O')
			{
				++Bsum;
				if (Bsum == 3)
					return 2;
			}
		}
	}
	

判断交叉获胜:

cs 复制代码
int Asum = 0;
	int Bsum = 0;
	for (i = 0; i < row; i++)//判断交叉获胜
	{
			if ((board[i][i] == 'X') || (board[i][row - 1 - i] == 'X'))
			{
				++Asum;
				if (Asum == 3)
					return 1;
			}
			else if ((board[i][i] == 'O') || (board[i][row - 1 - i] == 'O'))
			{
				++Bsum;
				if (Bsum == 3)
					return 2;
			}
	}

那么接下来我们要来判断和棋的时候是什么情况,因为判断输赢已经使这个函数变得比较复杂了,所以我们再定义一个新的函数Full,用来判断棋盘是否已经满了,如果满了则返回1,没满则返回0。

cs 复制代码
int Full(char chessBoard[ROW][COL])
{
	for (int row = 0; row < ROW; row++)
	{
		for (int col = 0; col < COL; col++)
		{
			if (chessBoard[row][col] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

我们需要利用Full函数的返回值确定是否为满,并且在判断获胜或者失败之前,如果函数已为满,则先判断是否为和棋,这样能够防止程序因为判断不出获胜或失败则return0导致程序继续进行(而此时电脑已经没有地方可以下棋了,会导致游戏终止)

好啦~这次我们已经把所有的准备工作都完成啦,让我们来看一下主函数main是什么样子的吧~

cs 复制代码
#include"game.h"
int game()
{
	//棋盘初始化
	BoardCreate(board, ROW, COL);

	//打印棋盘
	PrintBoard(board, ROW, COL);

	while (1)
	{
		//玩家下棋
		PlyaChess(board, ROW, COL);
		if (Full(board) == 1)
		{
			if ((WinChess(board, ROW, COL)) == 1)
			{
				printf("恭喜玩家获胜!!!");
				break;
			}
			if ((WinChess(board, ROW, COL)) == 0)
			{
				printf("你和人机势均力敌,你是人机嘛");
				break;
			}
		}
		//电脑下棋
		ComputerChess(board, ROW, COL);
		if ((WinChess(board, ROW, COL)) == 1)
		{
			printf("恭喜玩家获胜!!!");
			break;
		}
		if ((WinChess(board, ROW, COL)) == 2)
		{
			printf("你被人机打败了!");
			break;
		}
	}
}
int main()
{
	srand((unsigned int)time(NULL));
	int a;
	do
	{
		//创造菜单
		GameInte();
		scanf("%d", &a);
		if (a == 1)
		{
			game();
			break;
		}
		else if (a == 0)
		{
			printf("退出游戏");
			break;
		}
		else 
		{
			printf("输入错误,请重新输入:\n");
		}
	} while (a);
}

注意,此时我们在主函数中在判断胜利或失败之前加入的,判断是否为和棋的代码中,又加入了判断玩家是否胜利,因为可能玩家最后下的这一枚棋正好导致了棋子填满并且玩家获胜。加入这段判断就不会导致明明在最后一刻棋子满的时候胜利了,却被说是和棋了。

好了,这次已经解决了所有的函数了,让我们运行一下代码看看也没有什么问题~

行相连胜利:

列相连胜利:

交叉相连胜利:

和棋:

落子最后一枚获胜:

嘿嘿,游戏运行完整无误呢,给大家分享一下完整代码吧~

四、游戏完整代码

1.game.h
cs 复制代码
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include <time.h>
#include <windows.h>
#define ROW 3
#define COL 3
char board[ROW][COL];

//创造菜单
void GameInte();

//棋盘初始化
void BoardCreate(char board[ROW][COL], int row, int col);

//打印棋盘
void PrintBoard(char board[ROW][COL], int row, int col);
 
//玩家下棋
void PlyaChess(char board[ROW][COL], int row, int col);

//电脑下棋
void ComputerChess(char board[ROW][COL],int row, int col);

//判断棋盘是否已满
int Full(char chessBoard[ROW][COL]);

//判断赢输与和棋
int WinChess(char board[ROW][COL], int row, int col);
2.game.c
cs 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void GameInte()
{
	printf("**************************\n");
	printf("----- 三 子 棋 游 戏 -----\n");
	printf("**************************\n");
	printf("*******   1.play   *******\n");
	printf("*******   0.quit   *******\n");
	printf("**************************\n");
	printf("是否游玩?请输入>:");
}

char BoardCreate(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 PrintBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	printf(" ----+---+----\n");
	for (i = 0; i < row; i++)
	{
		printf(" | %c | %c | %c | \n", board[i][0], board[i][1], board[i][2]);
		printf(" ----+---+----\n");
	}
}

void PlyaChess(char board[ROW][COL], int row, int col)
{
	int a;
	int b;
	while (1)
	{
		printf("请输入下棋的坐标:>");
		scanf("%d %d", &a, &b);
		int m = a - 1;
		int n = b - 1;
		if(a<=0||a>=row+1||b<=0||b>=col+1)
		{
			printf("输入错误,请重新输入:>");
			continue;
		}
		else if(board[m][n]!=' ')
		{
			printf("你是想叠罗汉吗?\n");
			printf("请重新输入:>\n");
			continue;
		}
		board[m][n] = 'X';
		PrintBoard(board, ROW, COL);
		break;
	}
}

int Full(char chessBoard[ROW][COL])
{
	for (int row = 0; row < ROW; row++)
	{
		for (int col = 0; col < COL; col++)
		{
			if (chessBoard[row][col] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}


void ComputerChess(char board[ROW][COL], int row,int col)
{
	again:
	int m = rand() % row;
	int n = rand() % col;
	int i = 0;
	int j = 0;
	int sum = 0;
	Sleep(500);
	if (board[m][n] == ' ')
	{
		board[m][n] = 'O';
	}
	else 
	{
		goto again;
	}
	PrintBoard(board, ROW, COL);
}

int WinChess(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)//判断行获胜
	{
		int Asum = 0;
		int Bsum = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == 'X')
			{
				++Asum;
				if (Asum == 3)
					return 1;
			}
			else if(board[i][j] == 'O')
			{
				++Bsum;
				if (Bsum == 3)
					return 2;
			}
		}
	}
	for (i = 0; i < row; i++)//判断列获胜
	{
		int Asum = 0;
		int Bsum = 0;
		for (j = 0; j < col; j++)
		{
			if (board[j][i] == 'X')
			{
				++Asum;
				if (Asum == 3)
					return 1;
			}
			else if (board[j][i] == 'O')
			{
				++Bsum;
				if (Bsum == 3)
					return 2;
			}
		}
	}
	int Asum = 0;
	int Bsum = 0;
	for (i = 0; i < row; i++)//判断交叉获胜
	{
			if ((board[i][i] == 'X') || (board[i][row - 1 - i] == 'X'))
			{
				++Asum;
				if (Asum == 3)
					return 1;
			}
			else if ((board[i][i] == 'O') || (board[i][row - 1 - i] == 'O'))
			{
				++Bsum;
				if (Bsum == 3)
					return 2;
			}
	}
	return 0;
}
3.test.c
cs 复制代码
#include"game.h"
int game()
{
	//棋盘初始化
	BoardCreate(board, ROW, COL);

	//打印棋盘
	PrintBoard(board, ROW, COL);

	while (1)
	{
		//玩家下棋
		PlyaChess(board, ROW, COL);
		if (Full(board) == 1)
		{
			if ((WinChess(board, ROW, COL)) == 1)
			{
				printf("恭喜玩家获胜!!!");
				break;
			}
			if ((WinChess(board, ROW, COL)) == 0)
			{
				printf("你和人机势均力敌,你是人机嘛");
				break;
			}
		}
		//电脑下棋
		ComputerChess(board, ROW, COL);
		if ((WinChess(board, ROW, COL)) == 1)
		{
			printf("恭喜玩家获胜!!!");
			break;
		}
		if ((WinChess(board, ROW, COL)) == 2)
		{
			printf("你被人机打败了!");
			break;
		}
	}
}
int main()
{
	srand((unsigned int)time(NULL));
	int a;
	do
	{
		//创造菜单
		GameInte();
		scanf("%d", &a);
		if (a == 1)
		{
			game();
			break;
		}
		else if (a == 0)
		{
			printf("退出游戏");
			break;
		}
		else 
		{
			printf("输入错误,请重新输入:\n");
		}
	} while (a);
}
相关推荐
蜜桃味女焊匠人2 小时前
焊接生产线优化思路:解决手工焊、机器人焊气体浪费问题
人工智能·经验分享·其他·机器人
Scott9999HH8 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
FairGuard手游加固11 小时前
2026年7月份国产游戏审批信息
游戏
马优晨11 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区13 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大13 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai13 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang