《追逐胜利:编程之路上的三子棋游戏实践》

文章目录


前言

大家好!我是艾老虎尤!今天我很高兴来和大家分享我最近的一个项目------用C语言实现的三子棋小游戏。作为一个编程爱好者,我对游戏开发也比较感兴趣,而通过写这篇博客,我想向大家展示我在这个小游戏中的实践经验和收获,为了增加自己的描述和方便大家的理解,一些地方我也会画图为大家展示,话不多说,我们直接开始!


一、三子棋游戏规则

  1. 三子棋是一种在3x3的棋盘上进行的井字游戏。游戏开始时,棋盘上没有任何棋子。
  2. 两名玩家轮流执黑白棋子,黑子先行。
  3. 游戏过程中,每个玩家可以在空余的格子上放置自己的棋子。
  4. 每个玩家的目标是在棋盘上形成三颗自己的棋子连成一条线,可以是横向、纵向或对角线。
  5. 若某个玩家达成了连接三颗棋子的目标,则游戏结束,该玩家获胜。
  6. 若棋盘上的格子被填满且没有任何玩家成功连成一条线,则游戏以平局结束。

二、步骤详解

1.游戏菜单的实现

一个好的游戏菜单不光可以提高游戏的观赏性,更关键的是提示玩家如何开始游戏等等,所以我们第一步先把游戏菜单编辑出来,这一部分代码比较简单,我们只需要把游戏菜单打印在屏幕上,告知玩家选1的话就开始游戏,选0就结束游戏,选其他的数字就提示他选择错误,先看代码。

代码如下:

c 复制代码
#include<stdio.h>

void menu()
{
	printf("********************\n");
	printf("***  1.开始游戏  ***\n");
	printf("***  0.退出游戏  ***\n");
	printf("********************\n");

}

void test()
{
	int input = 0;//输入值
	do
	{
		menu();//打印菜单
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("实现游戏\n");
			break;
		case 0:
			printf("退出游戏成功!\n");
			break;
		default:
			printf("不好意思,选择错误,请重新选择!\n");
			break;
		}
	} while (input);
}

int main()
{
	test();//测试逻辑
	return 0;
}

这里我们使用test函数实现打印菜单和选择游戏开始的逻辑,我们再使用menu函数打印游戏的菜单,再利用do while和swith实现游戏开始的选择,这里非常巧妙地是input这个变量,它不但控制了do whiel和switch的表达式,还控制了switch的语句项,可以说一举三得。

效果展示:

2.棋盘的实现

在游戏的介绍中也说到过, 三子棋是一种在3x3的棋盘上进行的井字游戏,所以我们使用一个二维数组表示即可,刚好对应了棋盘上的九个位置,这时候我们就需要在上面代码中的case 1写一个函数game,我们将会在这个函数里面实现游戏。

代码如下:

c 复制代码
void game()
{
	char arr[3][3];
}

为了方便,我们使用#define定义一个行ROW再定义一个列COL,它们的值为3。

然后我们就需要初始化棋盘,使里面的元素全部为空格,初始化后我们打印棋盘,看看效果。

c 复制代码
#define ROW 3
#define COL 3

void game()
{
	char board[ROW][COL];
	//初始化棋盘全为空格
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);
}

2.1 初始化棋盘

初始化棋盘代码如下:

c 复制代码
//初始化棋盘
void init_board(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] = ' ';
		}
	}
}

2.2 打印棋盘

当把棋盘初始化后,我们就可以打印棋盘了

打印棋盘代码如下:

c 复制代码
//打印棋盘
void print_board(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]);
		}
		printf("\n");
	}
}

逻辑是这样没错,但是我们在这个函数之前把棋盘初始化成空格了,所以我们打印出来棋盘什么都看不见,下面我们就要来实现打印出,如下图

想要打印,有一个方法很简单,我们把它拆分以下,如下图



代码运行效果:

当我们实现完代码后,发现最后面的|多了,所以我们要对它额外进行判断,就是让它少打印一次。

修改后的代码:

c 复制代码
//打印棋盘
void print_board(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]);
			if(j<col-1)
				printf("|");
		}
		printf("\n");
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

运行效果:


3.游戏逻辑实现

当棋盘部分设计好之后,我们就需要开始游戏逻辑的实现。

3.1 玩家下棋

假设游戏中玩家是先手,我们接下来写一个函数player_move(board, ROW, COL);来实现玩家下棋的动作。

首先我们需要一条指令提醒玩家可以开始下棋了,直接用printf即可

然后让玩家输入要下棋的坐标,因为我们程序员知道数组的下标是从0开始的,但是玩家不一定知道,如下图:

当玩家输入坐标时,我们还要判断坐标的合法性

判断的规则就是横坐标要大于等于1,小于等于3
纵坐标要大于等于1,小于等于3

只有这样,输入的坐标才有用

具体看下面的代码和注释

c 复制代码
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋\n");
	//死循环,防止坐标被占用和非法坐标
	while(1)
	{
		printf("请输入下棋位置坐标\n");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3)
		{
			if (board[x - 1][y - 1] == ' ')//判断输入的坐标为不为空
			{
				board[x - 1][y - 1] = 'O';//玩家下棋用O表示
				break;//跳出循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}

然后输入完我们再调用一下打印棋盘的函数

效果展示:

注意,我们这里把玩家下棋,打印棋盘的函数放到了while循环里面,让让它死循环


3.2 电脑下棋

假设游戏中电脑是后手,我们接下来写一个函数computer_move(board, ROW, COL);来实现带电脑下棋的动作。

电脑下棋的逻辑就是随机生成坐标,看下面代码和注释

c 复制代码
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	while (1)
	{
		//生成随机坐标
		int x = rand() % row;
		int y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = 'X';
			break;
		}
	}
}

效果展示

生成随机数的解释可以参考我的这篇文章:C语言实现猜数字小游戏


4.判断输赢

我们的逻辑是玩家下棋,下完了打印棋盘,然后电脑下棋,然后再打印棋盘

c 复制代码
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		//打印棋盘
		print_board(board, ROW, COL);
		//电脑下棋
		computer_move(board, ROW, COL);
		//打印棋盘
		print_board(board, ROW, COL);
	}

于是我们在玩家下完棋之后写一个判断输赢的函数win,在电脑下完棋之后再调用这个函数判断输赢,而判断输赢就是看横纵坐标和斜着的是不是三个相连,最后不光要判断输赢,还要看是玩家赢了还是电脑赢了,还是平局了,如果都不是,程序继续。

假设玩家赢了返回:O

电脑赢了返回:X

平局返回:Q

游戏继续返回:C

c 复制代码
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		//打印棋盘
		print_board(board, ROW, COL);
		//判断输赢
		ret = win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		computer_move(board, ROW, COL);
		//打印棋盘
		print_board(board, ROW, COL);
		//判断输赢
		ret = win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == 'O')
		printf("恭喜你!你赢了!\n");
	else if (ret == 'X')
		printf("电脑获胜,你失败了!\n");
	else if (ret == 'Q')
		printf("平局");
}

4.1 win函数实现

这里我们需要判断每一行和每一列,还有两个对角是不是三个是相同的元素。

c 复制代码
//判断平局
int full(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;
}

//判断输赢
char win(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 < 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[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}
	//平局
	if (full(board, row, col) == 1)
	{
		return 'Q';
	}
	//游戏继续
	return 'C';
}

5.完整代码

test.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void menu()
{
	printf("********************\n");
	printf("***  1.开始游戏  ***\n");
	printf("***  0.退出游戏  ***\n");
	printf("********************\n");

}

void game()
{
	char ret = 0;
	char board[ROW][COL];
	//初始化棋盘全为空格
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);

	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		//打印棋盘
		print_board(board, ROW, COL);
		//判断输赢
		ret = win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		computer_move(board, ROW, COL);
		//打印棋盘
		print_board(board, ROW, COL);
		//判断输赢
		ret = win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == 'O')
		printf("恭喜你!你赢了!\n");
	else if (ret == 'X')
		printf("电脑获胜,你失败了!\n");
	else if (ret == 'Q')
		printf("平局");
}


void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;//输入值
	do
	{
		menu();//打印菜单
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 2:
			printf("退出游戏成功!\n");
			break;
		default:
			printf("不好意思,选择错误,请重新选择!\n");
			break;
		}
	} while (input);
}

int main()
{
	test();//测试逻辑
	return 0;
}

game.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//函数的实现

//初始化棋盘
void init_board(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] = ' ';
		}
	}
}

//打印棋盘
void print_board(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]);
			if(j<col-1)
				printf("|");
		}
		printf("\n");
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋\n");
	//死循环,防止坐标被占用和非法坐标
	while(1)
	{
		printf("请输入下棋位置坐标\n");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = 'O';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	//生成随机坐标
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = 'X';
			break;
		}
	}
}

//判断平局
int full(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;
}

//判断输赢
char win(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 < 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[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}
	//平局
	if (full(board, row, col) == 1)
	{
		return 'Q';
	}
	//游戏继续
	return 'C';
}

game.h

c 复制代码
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 3
#define COL 3

//函数的声名

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

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

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

//判断输赢
char win(char board[ROW][COL], int row, int col);

运行效果

总结

在这篇博客中,我们详细介绍了使用C语言实现三子棋游戏的过程。首先,我们了解了三子棋游戏的规则和目标。接着,我们详细讨论了程序的设计和实现过程。我们使用了多个函数来处理不同的功能,如打印游戏板、获取玩家输入、判断胜负等。并调用合适的函数来执行游戏的不同步骤,我们成功地实现了一个简单而实用的三子棋游戏,通过这个项目,我不仅深化了我对于C语言的理解和运用,还提高了我的编程技巧。在实现三子棋小游戏的过程中,我进一步加强了我的算法思维和逻辑分析能力,并通过反复调试和优化来提升程序的性能。这个项目让我得到了满满的成就感,也增加了我的编程兴趣。我相信这次实践对于未来我的编程之路会产生积极的影响,我会继续探索和挑战更多有趣且有意义的项目,希望我的文章能给大家带来帮助。

相关推荐
PC端游爱好者3 小时前
如何使用VR眼镜串流玩游戏?VR眼镜串流玩游戏教程
游戏·智能手机·电脑·vr·玩游戏
电报号dapp11911 小时前
链游系统定制化开发:引领游戏产业的新时代
游戏·机器人·去中心化·区块链
敲敲敲-敲代码12 小时前
游戏设计:推箱子【easyx图形界面/c语言】
c语言·开发语言·游戏
神仙别闹21 小时前
基本MFC类框架的俄罗斯方块游戏
c++·游戏·mfc
erxij1 天前
【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看
c++·经验分享·游戏·3d·游戏引擎
单音GG1 天前
推荐一个基于协程的C++(lua)游戏服务器
服务器·c++·游戏·lua
erxij2 天前
【游戏引擎之路】登神长阶(十三)——Vulkan教程:讲个笑话:离开舒适区
c++·经验分享·游戏·3d·游戏引擎
无敌最俊朗@2 天前
unity3d————Sprite(精灵图片)
学习·游戏·unity·c#·游戏引擎
IDC02_FEIYA3 天前
游戏服务器和普通服务器的区别
运维·服务器·游戏