三子棋游戏的思路分享

三子棋游戏的思路分享

今天,我很高兴和大家分享一下我练习三子棋的感受与一些思路。

  1. 首先我们先建一个工程,这次与往常不一样,为了把代码细分清楚,建立一个头文件(game.h)(用于一些头文件的包含,以及一些函数的声明),再建立两个源文件(game.c和test.c)(一个是游戏代码即一些函数部分,一个是测试部分主要是一些外部框架)。
  2. 然后先搭建一个框架,如下图
    在这里插入图片描述
    这个菜单部分我们可以通过一个menu()来实现,代码如下:
void menu()
{
    
    
	printf("******************\n");
	printf("***   1.play   ***\n");
	printf("***   0.exit   ***\n");
	printf("******************\n");
}

此外,我们通过输入的数字来进行参与游戏,是1则参与游戏,是0则结束游戏,如果输入其他数字则打印输入错误,重新输入。大家看到这个根据输入的数字来进行不同的操作,有没有想到switch语句呢?我们就通过这个来实现它,test.c源文件中的代码如下:

int main()
{
    
    
	int input = 0;
	do
	{
    
    
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
    
    
		case 1:
			game();//三子棋的游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

让我们先试一下,看看能不能实现这个功能。
在这里插入图片描述

大家看到这个是不是还是觉得很简单呢,让我们继续挖深吧!
3.现在就是game()内部这个大工程了。
(1)因为我们设计的是三子棋,所以我们看成是一个字符数组,三行三列,因为这是棋盘,我们直接把数组名写成board,board[ROW][COL],
我们在这里不直接将数组的行列数直接写出来,可以进行宏定义,这样有助于兼容性,如果下次做五子棋或者其他形式,可以直接在宏定义中修改一下信息。我们在game.h头文件中的代码如下:

#define ROW 3
#define COL 3

我们只需要在各个源文件中加上

#include "game.h"

即可。
(2)我们对棋盘数组进行初始化,令每一位都是空格。在game.h我们写出一个函数InitBoard()声明

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

然后我们就可以在game.c中实现这个函数了。

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] = ' ';
		}
	}
}

(3)初始化棋盘后,我们先打印一下棋盘,和上面的初始化棋盘类似,先声明一下

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

再实现一下

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

这种代码虽然实现了棋盘的打印,但是兼容性不好,如果不打印三子棋棋盘,改成五子棋等等,则这个代码就不行了。所以现在对代码进行修改,将打印改成循环形式

void DisplayBoard(char board[ROW][COL], int row, int col)
{
    
    
	int i = 0;
	for (i = 0; i < row; i++)
	{
    
    
		//打印数据
		//printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		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)
		{
    
    
			//printf("---|---|---\n");
			for (j = 0; j < col; j++)
			{
    
    
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

在这里插入图片描述
棋盘打印就出来了,无论是三子棋还是五子棋还是其他形式,只要改变一下宏定义就可以打印出正确的棋盘。
(4)现在我们就可以进行下棋环节了。因为这个是人工对战,所以我们搞出两个函数玩家下棋和电脑下棋,代码如下:

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

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

(5)现在我们进行PlayerMove()函数的代码书写,我们通过读取输入的坐标进行对棋盘数组元素的变化,同时也要防止一些特殊情况的输入,因此我们要进行对输入的坐标进行判断,坐标是否在字符串内,同时该坐标下的元素是空格(同时要注意由于C语言中的数组开头是从0开始的,所以进行该坐标下的数组元素判断的时候,记得行列数分别减1!

void PlayerMove(char board[ROW][COL], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	printf("玩家走:>\n");
	while (1)
	{
    
    
		printf("请输入一个坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			if (board[x-1][y-1] != ' ')
			{
    
    
				printf("坐标被占用,请重新输入!\n");
			}
			else
			{
    
    
				board[x-1][y-1] = '*';
				break;
			}
		}
		else
		{
    
    
			printf("坐标非法,请重新输入!\n");
		}
	}
}

(6)进行电脑下棋,为了确保随机下棋的进行,我们通过rand()形式产生随机数,再通过时间戳来作为随机数产生的起点,因此我们在main函数中进行一次随机数起点的进行,因此我们利用下面进行随机数起点

srand((unsigned int)time(NULL));

同时在game.h头文件中包含头文件

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

电脑下棋代码如下:

void ComputerMove(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] = '#';
			break;
		}
	}
}

(7)现在可以下棋了,最后进行判断输赢情况:
(1)一种是输赢出来了
(2)一种是还没有结束,继续比赛
(3)最后就是打成平局
通过一个字符变量ret来控制判断输赢情况。
这里我们依旧使用一个函数CheckWin()来进行,函数声明就不写下了,代码如下:

//判断输赢
char CheckWin(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 < row; 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) == 1)
	{
    
    
		return 'Q';
	}
	return 'C';
}

在判断平局的时候,通过引用IsFull()函数来判断是否应该结束游戏

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

经过以上的代码设计,game()内部基本上达成

void game()
{
    
    
	char ret = '0';
	//数组的存储是二维数组
	char board[ROW][COL];//初始化为空格
	//初始化棋盘
	InitBoard(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	while (1)
	{
    
    
		//玩家下棋
		PlayerMove(board,ROW,COL);
		ret = CheckWin(board, ROW, COL);
		if (ret != 'C')
		{
    
    
			break;
		}
		DisplayBoard(board, ROW, COL);
		//电脑下棋
		ComputerMove(board,ROW,COL);//随机下棋
		ret = CheckWin(board, ROW, COL);
		if (ret != 'C')
		{
    
    
			break;
		}
		DisplayBoard(board, ROW, COL);
	}
	if (ret == '*')
		printf("玩家赢了\n");
	else if (ret == '#')
		printf("电脑赢了\n");
	else if (ret == 'Q')
		printf("平局\n");
	DisplayBoard(board, ROW, COL);
}

现在我们进行一次三子棋游戏吧!
在这里插入图片描述
经过与电脑进行一番PK,终于在棋盘填满的时候赢得胜利!()
感谢大家的观看,作为小白,写的不是很透彻,希望大家多多包涵指点,谢谢大家!

猜你喜欢

转载自blog.csdn.net/weixin_51548091/article/details/113092325