C语言实现简单三子棋游戏

三子棋游戏介绍

三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。

基本思路

  1. 游戏至少要先进行一次
  2. 需要提供一个菜单供用户选择
  3. 棋盘布局
  4. 玩家下棋对应的坐标
  5. 电脑下棋是随机的
  6. 判断输赢

代码实现过程

由于代码过多,堆在一个文件中会比较复杂,所以我们将分成
test.c --------游戏的整体框架
game.c -------过程实现
game.h -------函数的声明
三个文件来实现整个游戏的逻辑。

需要源代码的可以去我GitHub上面下载源代码

test.c

我们把主函数写在test.c文件中,并且在这个文件创建了一个游戏框架,我们知道,游戏至少会进行一次,所以用do while循环比较合适,利用switch判断来作为游戏继续或者退出的条件。

void game()
{
    
    
	int ret = 0;
	//数组储存是个二维数组
	char board[ROW][COL];
	//把棋盘初始化成空格
	Initboard(board, ROW, COL);//在game.h声明,game.c中实现
	//打印棋盘
	Displayboard(board, ROW, COL);//在game.h声明,game.c中实现
	while (1)
	{
    
    
		//以下所有函数都在在game.h声明,game.c中实现
		//玩家下棋
		PlayMove(board, ROW, COL);
		//每下完一个棋子,打印一次棋盘
		//Sleep(500);
		Displayboard(board, ROW, COL);
		//判断输赢
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
		{
    
    
			break;
		}
		//电脑下棋,随机下棋
		CompuMove(board, ROW, COL);
		//电脑下完一个棋子,打印一次棋盘
		//Sleep(500);
		Displayboard(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
		{
    
    
			break;
		}
	}
	if (ret == '*')
	{
    
    
		system("cls");
		printf("玩家获胜\n");
		Displayboard(board, ROW, COL);
		system("pause");
	}
	else if (ret == '#')
	{
    
    
		system("cls");
		printf("电脑获胜\n");
		Displayboard(board, ROW, COL);
		system("pause");
	}
	else
	{
    
    
		system("cls");
		printf("平局\n");
		Displayboard(board, ROW, COL);
		system("pause");
	}
}

void menu()
{
    
    
	printf("**************************\n");
	printf("***   1.play   0.exit  ***\n");
	printf("**************************\n");
}

int main()
{
    
    
	srand((unsigned int)time(NULL));//使用系统时间作为随机值的种子
	int input = 0;
	char ch = 0;
	do
	{
    
    
		system("cls");
		menu();//菜单
		printf("请选择:>");
		scanf("%d", &input);
		system("cls");
		while (ch != '\n')
		{
    
    
			ch = getchar();
		}
		switch (input)
		{
    
    
		case 1:
			printf("开始游戏\n");
			game();//实现游戏过程的函数
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while(input);//输入的选择作为循环的条件
	return 0;
}

我们看到test.c文件中的game函数是用来实现游戏过程的,调用了很多自定义函数,这些函数并没有声明,所以这时候就要我们引用创建的game.h头文件了,引用自己创建的头文件是用" "引用的。

#include"game.h"

game.h

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
#define ROW 3//行
#define COL 3//列

//声明函数
void Initboard(char arr[ROW][COL], int row, int col);//初始化棋盘
void Displayboard(char arr[ROW][COL], int row, int col);//打印棋盘
void PlayMove(char arr[ROW][COL],int row,int col);//玩家下棋过程
void CompuMove(char arr[ROW][COL],int row,int col);//电脑下棋过程
char Iswin(char arr[ROW][COL], int row, int col);//判断输赢

现在我们一一来实现这些函数的功能

game.c

首先我们在game.c文件种也要调用"game.h"头文件。

#include"game.h"

我们看到在test.c文件中游戏刚开始的时候需要打印一个空棋盘到屏幕上去,所以我们用两个函数来实现这两个功能

//初始化棋盘
Initboard(board, ROW, COL);
//打印棋盘
Displayboard(board, ROW, COL);

InitBoard()

游戏开始前我们需要将整个棋盘初始化成空格,方便后面打印出来更加美观。代码实现如下

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

Displayboard()

初始化棋盘后,我们需要将棋盘打印成以下的形状
在这里插入图片描述

我们可以把这个图形分解成三个对象,横线有N行就打印N-1行,有N列就打印N列,而竖线就是有N行就打印N行,有N列就打印N-1列,字符是N行N列就打印N行N列个
在这里插入图片描述

代码实现

//打印棋盘
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]);
			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");
		}
	}
}

当空棋盘打印在屏幕面前时,接下来就开始进行下棋操作了,下棋操作分为两种,玩家下棋和电脑下棋,并且,每下完一步棋,就将棋盘给打印出来,我们也将其封装成两个函数来实现,下棋过程是个循环过程,所以我们用while循环来控制这下棋过程

while(1)
{
    
    
	PlayMove(board, ROW, COL);//玩家下棋过程
	Displayboard(board, ROW, COL);//每下完一个棋子,打印一次棋盘
	CompuMove(board, ROW, COL);//电脑下棋
	Displayboard(board, ROW, COL);//每下完一个棋子,打印一次棋盘
}

PlayMove()

在玩家下棋的过程,玩家要输入坐标,我们知道数组对应的坐标应该是这样的
在这里插入图片描述
可有的玩家不一定接触过编程,站在他们的角度想,对应的坐标应该是这样的
在这里插入图片描述
所以在下棋时,我们要将玩家输入的坐标-1就是对应的我们数组真实的坐标,并且在此之前需要判断该坐标是否合法,并检测该坐标有没有被占用,那么这个过程也是个循环,在下子成功后退出循环。

//玩家下棋
void PlayMove(char board[ROW][COL], int row, int col)
{
    
    
	char ch = 0;
	int x = 0;
	int y = 0;
	while (1)
	{
    
    
		printf("请输入要下棋的坐标:>");
		scanf("%d%d", &x, &y);
		while (ch != '\n')
		{
    
    
			ch = getchar();
		}
		if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
		{
    
    
			if (board[x - 1][y - 1] == ' ')//判断真实坐标是否被下过棋子
			{
    
    
				board[x - 1][y - 1] = '*';
				system("cls");
				break;//下子成功立马跳出循环
			}
			else
			{
    
    
				printf("坐标被占用请重新输入!!!\n");
			}
		}
		else
		{
    
    
			printf("坐标不合法,请重新输入!!!\n");
		}
		
	}

}

CompuMove()

电脑下棋过程就比较简单了,电脑是随机下棋,只需要判断电脑随机数的坐标有没有被占用就行了,这里注意的是rand()这个(伪)随机值函数是需要srand()函数生成随机数起点的种子,我们这里用系统时间来当作随机数的种子,而种子只需要生成一次就行,所以我们将srand()函数放在了主函数那里

srand((unsigned int)time(NULL));//使用系统时间作为随机值的种子
//电脑下棋
void CompuMove(char board[ROW][COL], int row, int col)
{
    
    
	system("cls");
	printf("电脑走:>");
	while (1)
	{
    
    
		int x = rand() % row;//产生0-row的随机坐标
		int y = rand() % col;//产生0-row的随机坐标
		if (board[x][y] == ' ' && board[x][y] != '*')
		{
    
    
			printf("%d %d\n", x + 1, y + 1);
			board[x][y] = '#';
			break;
		}
	}
}

游戏总会结束的,所以我们接下来用Iswin()函数来判断游戏状态,并在每次玩家或电脑下完棋后判断一次。

	while (1)
	{
    
    
		//以下所有函数都在在game.h声明,game.c中实现
		//玩家下棋
		PlayMove(board, ROW, COL);
		//每下完一个棋子,打印一次棋盘
		//Sleep(500);
		Displayboard(board, ROW, COL);
		//判断输赢
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
		{
    
    
			break;
		}
		//电脑下棋,随机下棋
		CompuMove(board, ROW, COL);
		//电脑下完一个棋子,打印一次棋盘
		//Sleep(500);
		Displayboard(board, ROW, COL);
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
		{
    
    
			break;
		}
	}	

Iswin()

在这个函数里给我们返回4种状态

  1. 玩家赢 --返回’*’
  2. 电脑赢 --返回’#’
  3. 平局 --返回’Q’
  4. 游戏继续 --返回’C’
    在这里我们把所有的获胜的条件都列出来,横三行,竖三列,和对角线
    在这里插入图片描述
    代码实现,如果满足上面获胜条件,则返回其中一个坐标对应的字符,如果不满足,则转到判断平局,在判断平局的函数种遍历一遍棋盘,如果棋盘还有空格,就说明棋盘没满,否则返回‘Q’。以上所有条件不满足时在返回字符’C’
//判断平局
int IsFull(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 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][1] != ' ')
			return board[i][0];
	}
	//竖三列
	for (i = 0; i < col; i++)
	{
    
    
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
			return board[0][i];
	}
	//对角线 左上->右下
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[2][2] != ' ')
		return board[1][1];
	//对角线 左下->右上
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[0][2] != ' ')
		return board[1][1];
	//判断平局
	if(IsFull(board,ROW,COL)==1)
		return 'Q';
	return 'C';
}

如果返回的不是‘C’,则跳出整个游戏的循环,在输出对应的结果

if (ret == '*')
	{
    
    
		system("cls");
		printf("玩家获胜\n");
		Displayboard(board, ROW, COL);
		system("pause");
	}
	else if (ret == '#')
	{
    
    
		system("cls");
		printf("电脑获胜\n");
		Displayboard(board, ROW, COL);
		system("pause");
	}
	else
	{
    
    
		system("cls");
		printf("平局\n");
		Displayboard(board, ROW, COL);
		system("pause");
	}

运行截图

在这里插入图片描述
谢谢大家的观看,如果有什么写的不对,欢迎指出。

猜你喜欢

转载自blog.csdn.net/qq_45270751/article/details/113108272