用C语言创建五子棋游戏

CCBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在这个项目中,我们将使用C编程语言创建一个简单的基于文本的五子棋游戏。五子棋是一种两人策略棋盘游戏,目标是率先在水平、垂直或对角线上连续获得五颗棋子。我们将使用一个 15x15 的游戏棋盘来开发这个游戏。

👀 预览

五子棋游戏

🎯 任务

在这个项目中,你将学习:

  • 如何在C语言中使用二维数组设计和实现棋盘。
  • 如何创建一个主函数来控制游戏流程。
  • 如何实现初始化游戏、打印棋盘以及允许玩家轮流下棋的函数。
  • 如何开发一个函数来检查游戏中的获胜条件。
  • 如何编译和运行程序。

🏆 成果

完成这个项目后,你将能够:

  • 在C语言中使用二维数组。
  • 使用函数设计和实现游戏流程。
  • 检查游戏中的获胜条件。
  • 编译和运行C程序。

创建项目文件

首先,创建一个名为 gomoku.c 的新文件,并在你喜欢的代码编辑器中打开它。

cd ~/project
touch gomoku.c
✨ 查看解决方案并练习

设计棋盘

使用 'O' 和 'X' 表示棋子

首先,我们需要一个棋盘(15 * 15)来记录棋盘上每个位置的“状态”。因此,我们可以定义一个数组 chessboard[16][16]。为什么不是 [15][15] 呢?这是因为这样数组的坐标就能与棋盘的行和列精确对应,便于编写后续代码。

#include <stdio.h>

#define N 15

// 定义一个数组,并将每个元素初始化为0。
int chessboard[N + 1][N + 1] = { 0 };
✨ 查看解决方案并练习

主函数

在开始编写主函数之前,让我们简要思考一下游戏的典型流程。首先,我们进入游戏的主界面,然后点击开始按钮进入游戏,接着显示游戏画面,判断胜负,最后结束游戏。那么,像五子棋这样的游戏流程是怎样的呢?

首先,我们进入游戏的欢迎界面,然后输入Y开始游戏(非Y则退出游戏),显示游戏棋盘,接着两位玩家轮流落子,之后判断胜负(是否有五个棋子连成一线)。

// 用于记录轮到玩家1还是玩家2,奇数表示轮到玩家1,偶数表示轮到玩家2。
int whoseTurn = 0;

int main(void)
{
	// 一个自定义函数,用于初始化游戏,即显示欢迎界面并进入游戏显示棋盘。
	initGame();

	// 这个循环用于两位玩家轮流操作。
	while (1)
	{
		// 每次循环加1,这样两人就能轮流操作。
		whoseTurn++;

		// 一个自定义函数,用于执行落子操作。
		playChess();
	}

	return 0;
}
✨ 查看解决方案并练习

initGame 函数

在这个函数中,我们要实现的功能有:

  • 显示一个简单的欢迎界面。
  • 要求输入 'Y',输入后显示棋盘。
void initGame(void)
{
	char c;

	printf("请输入'y'进入游戏:");
	c = getchar();
	if ('y'!= c && 'Y'!= c)
		exit(0);

	// 清屏
	system("clear");

	// 这里再次调用一个自定义函数,其功能是打印出棋盘。
	printChessboard();
}

initGame 函数中,我们使用了 exitsystem 函数,所以需要在程序顶部包含头文件 stdlib.h

#include <stdlib.h>
✨ 查看解决方案并练习

函数 printChessboard

在这个函数中,我们的目标是:

  • 打印出行号和列号,并打印出棋盘。
  • 如果数组元素的值为 0,打印一个星号(*),表示该位置为空。
  • 如果数组元素的值为 1,打印一个实心圆(X),代表玩家 1 的棋子。
  • 如果数组元素的值为 2,打印一个空心圆(O),代表玩家 2 的棋子。
void printChessboard(void)
{
	int i, j;

	for (i = 0; i <= N; i++)
	{
		for (j = 0; j <= N; j++)
		{
			if (0 == i)		// 这会打印出列号。
				printf("%3d", j);
			else if (j == 0)	// 打印行号。
				printf("%3d", i);
			else if (1 == chessboard[i][j])
				printf("  X");
			else if (2 == chessboard[i][j])
				printf("  O");
			else
				printf("  *");
		}
		printf("\n");
	}
}
✨ 查看解决方案并练习

playChess 函数

在这个函数中,我们想要实现以下几点:

  • 提示玩家输入放置棋子的位置。
  • 如果当前轮到玩家 1 行动,将数组中相应元素的值设为 1。
  • 如果当前轮到玩家 2 行动,将数组中相应元素的值设为 2。
  • 每一步棋之后,判断当前玩家是否获胜。
void playChess(void)
{
	int i, j, winner;

	// 判断是玩家 1 的回合还是玩家 2 的回合,然后将值赋给数组中的相应元素。
	if (1 == whoseTurn % 2)
	{
		printf("轮到玩家 1,请输入位置,格式为行号 + 空格 + 列号: ");
		scanf("%d %d", &i, &j);
		chessboard[i][j] = 1;
	}
	else
	{
		printf("轮到玩家 2,请输入位置,格式为行号 + 空格 + 列号: ");
		scanf("%d %d", &i, &j);
		chessboard[i][j] = 2;
	}

	// 再次打印棋盘。
	system("clear");
	printChessboard();	// 再次调用此函数。

	/*
	* 以下部分调用自定义函数(判断函数)。
	* 用于判断当前玩家这一步棋是否获胜。
	*/
	if (judge(i, j, whoseTurn))
	{
		if (1 == whoseTurn % 2)
			printf("获胜者是玩家 1!\n");
		else
			printf("获胜者是玩家 2!\n");
	}
}
✨ 查看解决方案并练习

judge 函数

函数参数:

  • x:当前落子的行号
  • y:当前落子的列号

返回值:

  • 1 或 0。1 表示当前玩家落子后获胜。
int judge(int x, int y)
{
    int i, j;
    int t = 2 - whoseTurn % 2;
    const int step[4][2]={{-1,0},{0,-1},{1,1},{1,0}};
    for(int i=0;i<4;++i)
    {
        const int d[2]={-1,1};
        int count=1;
        for(int j=0;j<2;++j)
            for(int k=1;k<=4;++k){
                int row=x+k*d[j]*step[i][0];
                int col=y+k*d[j]*step[i][1];
                if( row>=1 && row<=N && col>=1 && col<=N &&
                    chessboard[x][y]==chessboard[row][col])
                    count+=1;
                else
                    break;
            }
        if(count>=5)
            return 1;
    }
    return 0;
}

judge 函数中有 3 个嵌套的 for 循环,其目的是判断是否存在连续五个棋子的连线。

五个棋子的连线可以是水平、垂直或对角线方向。这里,我们将采用试错法,在水平、垂直和对角线方向上搜索连续的棋子。举个例子:

五子棋游戏

在上面的棋盘上,我们将根据坐标 (9, 10) 解释判断是否存在五个棋子连线的算法。

首先,我们检查从 (9, 10) 开始是否存在一条五个棋子的对角线。过程如下:

  • 从 (9, 10) 开始,我们向左上方搜索。满足条件的坐标是 (8, 9)、(7, 8) 和 (6, 7)。由于 (5, 6) 不满足条件,我们进入下一步。
  • 然后,我们向右下方搜索,找到 (10, 11),这是唯一满足条件的坐标。
  • 我们找到了一条直线上的五个点,所以玩家 2 获胜。

如果在对角线方向上没有五个棋子的连线,我们接着检查垂直和水平方向。如果它们都不满足获胜条件,这意味着当前玩家不能获胜,游戏将继续。

✨ 查看解决方案并练习

我的棋子被“吃掉”了

我不知道大家有没有注意到,在我们的五子棋游戏中,即使某个位置已经被占用,我们在放置自己的棋子时仍然可以“吃掉”原来的棋子。

这是因为我们在编写 playChess 函数时,没有检查我们放置棋子的位置。我们可以像这样修改代码:

void playChess(void)
{
	int i, j, winner;
	if (1 == whoseTurn % 2)
	{
		printf("轮到玩家 1,请输入位置,格式为行号 + 空格 + 列号: ");
		scanf("%d %d", &i, &j);
		//调试
		while(chessboard[i][j]!= 0)
		{
			printf("这个位置已经被占用,请重新输入位置: ");
			scanf("%d %d",&i, &j);
		}
		//调试
		chessboard[i][j] = 1;
	}
	else
	{
		printf("轮到玩家 2,请输入位置,格式为行号 + 空格 + 列号: ");
		scanf("%d %d", &i, &j);
		//调试
		while(chessboard[i][j]!= 0)
		{
			printf("这个位置已经被占用,请重新输入位置: ");
			scanf("%d %d",&i, &j);
		}
		//调试
		chessboard[i][j] = 2;
	}

	system("clear");
	printChessboard();

	if (judge(i, j))
	{
		if (1 == whoseTurn % 2)
			printf("获胜者是玩家 1!\n");
		else
			printf("获胜者是玩家 2!\n");
	}
}

我们在函数中添加了一个循环,所以当位置已经被占用时,它会给出提示并要求输入新的位置。

✨ 查看解决方案并练习

为什么我永远赢不了?

当出现五个棋子连成一线,并提示“玩家 1”或“玩家 2”获胜时,接着又提示“轮到玩家 * 行动,请输入你的棋子位置……”。是不是很让人沮丧?实际上我们只需要添加一行代码!

	if (judge(i, j))
	{
		if (1 == whoseTurn % 2)
		{
			printf("获胜者是玩家 1!\n");
			exit(0);	//调试
		}
		else
		{
			printf("获胜者是玩家 2!\n");
			exit(0);	//调试
		}
	}
}

除了在玩家获胜后提示获胜者之外,使用 exit(0) 退出游戏。

✨ 查看解决方案并练习

编译与运行

执行 gcc 命令进行编译:

cd ~/project
gcc -o gomoku gomoku.c
./gomoku
五子棋游戏
✨ 查看解决方案并练习

总结

恭喜你!你已经使用 C 语言创建了一个简单的五子棋游戏。玩家可以轮流在一个 15x15 的棋盘上放置棋子,当有玩家连成五个连续的棋子时,程序将宣布其为获胜者。和你的朋友们尽情享受这个基于文本的游戏吧!

您可能感兴趣的其他 C 教程