c语言实现三子棋游戏

举报
luming_02 发表于 2023/12/05 13:28:27 2023/12/05
【摘要】 1.三子棋游戏规则在设计游戏前,我们先得知道最基础的游戏规则,以实现我们最基本的设计需求:1、人机双方轮流在格子里落子棋子,先连成三棋一线者视为胜利2、棋盘被摆满棋子仍未分出胜利,视为平局2.分析需求        其实设计程序的难点就在于,把抽象的逻辑概念转化为计算机语言可识别的逻辑语句,再简单的设计需求,都要用科学的分析方法,先分析需求,再针对性的设计,不可想当然的拿起手就开始写代码,也...

1.三子棋游戏规则

在设计游戏前,我们先得知道最基础的游戏规则,以实现我们最基本的设计需求:

1、人机双方轮流在格子里落子棋子,先连成三棋一线者视为胜利

2、棋盘被摆满棋子仍未分出胜利,视为平局

2.分析需求

        其实设计程序的难点就在于,把抽象的逻辑概念转化为计算机语言可识别的逻辑语句,再简单的设计需求,都要用科学的分析方法,先分析需求,再针对性的设计,不可想当然的拿起手就开始写代码,也就是我们始终强调的编程思维。要用工程化的方法解决实际问题。

        首先,一个游戏是必须有菜单界面的,供以玩家选择是否开始游戏或者退出游戏,同时我们也要考虑游戏多次游玩的情况,需要用一个循环语句将菜单界面封装起来,可以让玩家在游玩后再次进入菜单选择是否开始游戏;其次是游戏的初始化问题,我们需要一个数组来保存玩家和电脑下棋时落子的位置,并且要在屏幕上打印出来,在这里我们就得考虑落子的问题,玩家落子很简单,用一个数组保存就行了,但是电脑的落子应该是随机的,我们可以考虑使用rand函数和time函数来做到随机生成的功能,,并且在落子的时候我们得考虑输入是否合理,不能越界输入,不能重复在同一个位置落子;解决了以上的问题后,我们要思考,怎么设计一个判断输赢的逻辑,怎么样是玩家赢,怎么样是电脑赢,怎么样算是平局。

        在有了以上大致的规划和思考后,我们就可以正式的开始设计了。

        在开始设计之前,我们首先得明确,这个程序是由多个功能的函数语句共同实现的,因此在创建工程时,我们就应该提前做好创建多个文件的工作,不同的文件实现不同的功能和逻辑,以保证程序的健康性和完整性,也方便我们日后更改。我们可以分为一下三个文件实现,game.h用于包含程序所需要的头文件和函数的申明,chessboard.c用于实现棋盘上具体的初始化和判断逻辑,game.c文件为主要的主程序所在的文件,是程序的主体,也是各个函数的集合实现。


3.菜单界面设计

        首先需要在屏幕上打印菜单,然后让玩家选择,若玩家输入不正确或者想在游玩后再次游玩的话,则需要个循环来实现,因此,这里需要的是先执行循环体再判断循环条件的语句,也就是do...whlie...语句,以下是代码实现

void screen()
{
	printf("--------------------------\n");
	printf("---------1.Play ----------\n");
	printf("--------------------------\n");
	printf("---------2.Esc  ----------\n");
	printf("--------------------------\n");
}

int input = 0;//记录玩家的输入选择
	printf("请进行输入确认是否开始游戏\n");
	do
	{
		screen();//调用屏幕打印菜单的函数
 		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			printf("三子棋\n");
			game();
			break;
		case 2:
			printf("退出游戏\n");
			break;
		default:
			printf("非法输入,请重新输入\n");
			break;
		}
	} while (input != 2);

4.游戏主体设计(棋盘的初始化部分)

        在这里,为了方便以后对棋盘大小进行更改,我们可以在头文件中进行宏定义,我们用ROWCOL分别来代表行和列

#define ROW 3 //定义 行为3
#define COL 3 //定义 列为3

        然后我们需要用一个3*3的数组去对我们的棋盘的数据进行保存,并对其进行初始化,将其封装成一个函数,并且放在chessboard.c文件中实现,需要传入的参数是一个数组和行以及列。我们对数组中每一个赋值为空格。

char board[ROW][COL] = { 0 };
	initialize(board, ROW, COL);//初始化棋盘
void initialize(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

        在完成了初始化之后,我们需要打印棋盘到屏幕上供玩家看见,但是此时打印出来的全是空格,玩家是看不见什么有意义的信息的,所以我们要制定一个棋盘的框架,既要实现需求,也要保证美观,所以需要打印部分分割线

displayboard(board, ROW, COL);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			//打印分割列
			if (j < col - 1)
				printf("|");
		}
		//打印分割行
		if (i < row - 1)
			printf("\n---|---|---\n");
	}
	printf("\n");
}

我们可以运行着测试一下

5.游戏主体设计(玩家下棋部分)

         在拥有完整的棋盘后,我们就可以开始着手设计玩家下棋和电脑下棋的部分了,首先是玩家下棋的部分,我们可以用之前定义的board数组来防止玩家落子的结果,可以设计如下的封装函数。使用while循环来保证玩家落子是真确合法的,否则就一直进行循环,直到输入正确的落子坐标。

palyer_move(board, ROW, COL);//玩家下棋
void palyer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("\n----玩家下棋----\n");//提示语句

	while (1)
	{
		printf("请输入落子的坐标:");
		scanf("%d %d", &x, &y);
		if (x <= 0 || x > 3 || y <= 0 || y > 3)//判断输入是否越界
		{
			printf("非法输入,请重新输入\n");
		}
		else if (board[x-1][y-1] == ' ')
		{
			board[x-1][y-1] = '*';//用*表示玩家落子
			break;
		}
		else
			printf("输入位置被占用,请重新输入\n");
	}
}

6.游戏主体(电脑下棋部分)

        电脑下棋,最需要的就是随机,不然每一次电脑都是一个下法,游戏就不能正常游玩了,在这里,我们可以使用rand函数来生成随机数,使用srand函数来作为生成随机数的种子,为了保证随机数的动态变化,我们使用time函数来获取时间戳供以srand函数作为参数

computer_move(board, ROW, COL);//电脑下棋
srand((unsigned int)time(NULL));//初始化随机数的种子
void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("\n----电脑下棋----\n");//提示语句

	while (1)
	{
		int x = rand() % ROW;//使得随机数大小为1--3
		int y = rand() % COL;//使得随机数大小为1--3
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';//用#表示电脑落子
			break;
		}
	}
}

        在这里的while循环是为了避免不合理的落子,比如在以及有玩家落子的地方是不允许再一次落子的,在不满足这样的条件下会再一次生成新的随机数,直到可以有正确的落子坐标为止

7.判断输赢逻辑逻辑设计

        在玩家和电脑落子都设计完成后,我们需要思考,怎么样的情况下算是玩家赢,怎么样的情况下算是电脑赢,怎么样算是平局,在玩家或者电脑赢了后怎么样实现立即停止游戏。

        我们可以设计一个函数,用其返回值来表示谁赢的了游戏的胜利,谁先完成三子连成一线就返回谁的棋子符号,在这里可以用俩个for循环判断3行和3列是否已经达成三子一线的情况,并且不能是三个空格,如果满足条件,则返回满足条件的元素符号,也就是*或者#,在用俩个if语句去判断俩条对角线是否满足条件,都没完成就继续游戏,并且设计一个函数判断棋盘是否满了,当棋盘下满后返回Q表示棋盘已经下满了,游戏平局,否则返回C表示游戏继续正常进行

//玩家赢 - '*'
//电脑赢 - '#'
//平局 --- 'Q'
//游戏继续-'C'
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];
		}
	}
	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[1][1] != ' ')
	{
		return board[1][1];
	}

	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	//判断平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}

	return 'C';
}

其中的判断棋盘是否下满的函数具体实现如下

static 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;//判断有空位,返回0
		}
	}
	return 1;//判断棋盘已满,返回1
}

        并且在游戏的主程序中,玩家每下一次棋以及电脑每下一次棋,都进行一次判断,用一个while循环,当某一方胜利或者平局时,也就是ret拿到的返回值只要不是C(游戏继续),就执行if语句,跳出循环,具体实现如下,在跳出循环后我们要对结果进行打印,也就是使用if...else if...else的多分支语句进行判断

while (1)
	{
		palyer_move(board, ROW, COL);//玩家下棋
		displayboard(board, ROW, COL);//打印棋盘
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

		computer_move(board, ROW, COL);//电脑下棋
		displayboard(board, ROW, COL);//打印棋盘
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
	{
		printf("平局\n");
	}

综上完成了各个模块的设计后,我们就可以进行组装,最后完成程序的设计

8.游戏完整代码

1.头文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 3 //定义 行为3
#define COL 3 //定义 列为3

//打印屏幕菜单
void screen();

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

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

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

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

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

2.棋盘模块代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void screen()
{
	printf("--------------------------\n");
	printf("---------1.Play ----------\n");
	printf("--------------------------\n");
	printf("---------2.Esc  ----------\n");
	printf("--------------------------\n");
}

void initialize(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

void displayboard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			//打印分割列
			if (j < col - 1)
				printf("|");
		}
		//打印分割行
		if (i < row - 1)
			printf("\n---|---|---\n");
	}
	printf("\n");
}

void palyer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("\n----玩家下棋----\n");

	while (1)
	{
		printf("请输入落子的坐标:");
		scanf("%d %d", &x, &y);
		if (x <= 0 || x > 3 || y <= 0 || y > 3)
		{
			printf("非法输入,请重新输入\n");
		}
		else if (board[x-1][y-1] == ' ')
		{
			board[x-1][y-1] = '*';
			break;
		}
		else
			printf("输入位置被占用,请重新输入\n");
	}
}

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("\n----电脑下棋----\n");

	while (1)
	{
		int x = rand() % ROW;//使得随机数大小为1--3
		int y = rand() % COL;//使得随机数大小为1--3
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

static 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;
}

//玩家赢 - '*'
//电脑赢 - '#'
//平局 --- 'Q'
//游戏继续-'C'
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];
		}
	}
	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[1][1] != ' ')
	{
		return board[1][1];
	}

	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	//判断平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}

	return 'C';
}

3.游戏主函数文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void game()
{
	//打印棋盘
	void screen();
	char ret = 0;

	char board[ROW][COL] = { 0 };
	initialize(board, ROW, COL);//初始化棋盘
	displayboard(board, ROW, COL);//打印棋盘

	while (1)
	{
		palyer_move(board, ROW, COL);//玩家下棋
		displayboard(board, ROW, COL);//打印棋盘
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

		computer_move(board, ROW, COL);//电脑下棋
		displayboard(board, ROW, COL);//打印棋盘
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
	{
		printf("平局\n");
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	printf("请进行输入确认是否开始游戏\n");
	do
	{
		screen();
 		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("开始游戏\n");
			printf("三子棋\n");
			game();
			break;
		case 2:
			printf("退出游戏\n");
			break;
		default:
			printf("非法输入,请重新输入\n");
			break;
		}
	} while (input != 2);

	return 0;
}


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。