三子棋的简单实现与其中的模块化设计思想

目录

1.背景简介

2.模块化设计思想及三子棋实现思路

3.具体代码分析

                  4.完整代码

1.背景简介:

  相信大家小时候都应该和朋友玩过这个简单的游戏。三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。而今天我们将尝试用c语言初步实现这个简单的小游戏

2.模块化设计思想及三子棋实现思路

  所谓的模块化设计,简单地说就是将产品的某些要素组合在一起,构成一个具有特定功能的子系统,将这个子系统作为通用性的模块与其他产品要素进行多种组合,构成新的系统,产生多种不同功能或相同功能、不同性能的系列产品。而为什么要使用这种设计思想呢?一方面是增加程序的可读性,不至于所有的代码都塞入一个maiin函数内,使得代码变得晦涩难懂。另一方面,将各个功能封装成不同模块有利于后期的维护和更改,便于以后的各种工作交接。虽然在本次三子棋的简单实现中或许显得有些多余,但是这种思想在以后工作时会显得尤为重要,毕竟没有人能够脱离团队去完成项目,也没有人愿意对着一坨山一般的代码去维护和修改。

  基于上述理论,我们将本次的代码实现分为3个文件及若干个函数来完成。详见下图。

三子棋的简单实现与其中的模块化设计思想_第1张图片

3.具体代码分析

首先我们看到test.c的文件,这个文件主要包含了游戏的所有测试逻辑,可以说是整个游戏的框架,应该是我们在程序设计时首要想到实现的。其次大家在完成框架后也可以对我们的游戏逻辑进行初步的测试,避免后续出现bug更难以修复。

#include"game.h"//这个后续会展现
void menu()//简易菜单
{
	printf("*********************************************\n");
	printf("**************      1.play      *************\n");
	printf("**************      0.exit      *************\n");
	printf("*********************************************\n");
}
void game()
{
	char board[ROW][COL];//创建二维数组
	IntiBoard(board,ROW,COL);//初始化
	DisplayBoard(board, ROW, COL);//打印棋盘
	char ret = 0;
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家操作
		DisplayBoard(board, ROW, COL);
		ret = IsWin(board, ROW, COL);//判断胜利
		if (ret != 'C')
			break;

		ComputerMove(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");
	}
	DisplayBoard(board, ROW, COL);//结束后展示棋盘
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//随机值,控制电脑下棋
	do {
		menu();
		printf("请输入");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);//简易交互
	return 0;
}

可以看到此时我们已经按照上述的思维导图将程序最核心的框架所完成,而接下来就要在game.c文件内实现我们游戏的诸如初始化棋盘,玩家与电脑之间操作等的核心功能。这里先放一张效果图

三子棋的简单实现与其中的模块化设计思想_第2张图片

可以看到,我们这里首先要完成的就是对棋盘的初始化以及打印棋盘,所以我们创建一个二维数组便于接收后续玩家与电脑下的棋子,当然一开始得先用空格进行初始化。代码如下。

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

  这里有两点需要注意的。第一是对于函数中二维数组的传参需要写成二维数组的形式接收。切忌写成了char board的类型,这样接收的只是一个字符类型。第二是关于ROW与COL这个会在后面讲到,其实是在game.h中进行了声明的两个变量,这样的模块化处理在后续还会很常见,为的是便于代码后续维护。

  但聪明的你此时应该发现,光初始化棋盘是没什么用的,因为此时屏幕上还是没有出现任何东西,显然我们还得打印棋盘,函数实现如下。

void DisplayBoard(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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");//不是最后一行则打印分界符
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

可能只看代码许多人还是小小的眼睛,大大的迷惑。那下面我们结合图来讲解一下。


三子棋的简单实现与其中的模块化设计思想_第3张图片

 大家可以看到我们这里其实是把“|”以及“---”作为棋盘的两个组成部分,而在循环过程中水平分界和竖直分界只要打印n-1次即可,所以加入if判断语句。

  而接下来这部分代码我们将实现玩家下棋的这部分操作。这里我们采用从键盘上输入坐标的方式来让玩家下棋,并且以“*”作为玩家所下棋子。实现代码如下。

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] == ' ')//判断坐标是否被占用
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("坐标被占用\n");
			}
		}
		else
		{
			printf("坐标非法\n");
		}
	}
}

  这里有几点是我们需要注意的。第一,由于玩家认为的坐标和c语言中数组的有些差别,通常我们认为数组下标是从1开始,而C语言中数组下标从0开始,所以在处理玩家下棋输入的坐标时要进行减去1的处理。第二我们要注意输入坐标是否合法,比如是否超出棋盘范围,或者是否已被占用。

  接下来我们实现电脑下棋的函数。由于本人实力有限所以这里采用随机下棋的方式,如有大神有更好的方法可在评论区提出。代码如下。

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

  既然棋下完了,那总得分出个输赢,不能让这个棋局无限循环下去,此时就需要一个函数来判断这场游戏的状态。我们先简单分析一下有几种可能。1:玩家胜 2:电脑胜 3:平局 4:游戏继续这里我们用“*” “#” “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][1]!=' ')
				return board[i][1];
		}
		for (i = 0; i < col; i++)//判断列
		{
			if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
				return board[1][i];
		}
		if (board[1][1] == board[0][0] && 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];//判断副对角线
		}
		int ret = IsFull(board, row, col);//判断棋盘是否已满
		if (ret == 1)
		{
			return 'Q';
		}
		return 'C';
    }

  其中胜利的情况有三种,注意判断三点一线相同且不为空。但是编者这里判断胜利时采用的是列举的方法其实有些把代码写死了,期待有大神能提出更好的方法。其次需要注意的就是如果还未分出胜负则需判断棋盘是否已满,这个函数等下会实现。如果已满游戏结束,判断平局,如果未满则游戏继续。

  

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
	}

  到此我们的三子棋可以说基本已经完成,只差最后的game.h内的函数声明即可。具体如下。

#pragma once
#include
#include
#include
//符号定义
#define ROW 3//便于后续可修改成N子棋
#define COL 3
//函数声明
void IntiBoard(char board[ROW][COL], int row, int col);//初始化棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);//打印
void PlayerMove(char board[ROW][COL], int row, int col);//玩家下棋
void ComputerMove(char board[ROW][COL], int row, int col);//电脑下棋
char IsWin(char board[ROW][COL], int row, int col);//判断状况

这里可以看到我们不仅声明了自己完成的函数,同时也声明了我们需要使用的库函数。而此后我们在.c文件内都只引用了我们自己写的头文件,避免了重复引用的冗余。

4.完整代码

test.c

#include"game.h"
void menu()//简易菜单
{
	printf("*********************************************\n");
	printf("**************      1.play      *************\n");
	printf("**************      0.exit      *************\n");
	printf("*********************************************\n");
}
void game()
{
	char board[ROW][COL];//创建二维数组
	IntiBoard(board,ROW,COL);//初始化
	DisplayBoard(board, ROW, COL);//打印棋盘
	char ret = 0;
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家操作
		DisplayBoard(board, ROW, COL);
		ret = IsWin(board, ROW, COL);//判断胜利
		if (ret != 'C')
			break;

		ComputerMove(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");
	}
	DisplayBoard(board, ROW, COL);//结束后展示棋盘
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//随机值,控制电脑下棋
	do {
		menu();
		printf("请输入");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误\n");
			break;
		}
	} while (input);//简易交互
	return 0;
}

game.c

#include "game.h"
void IntiBoard(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] = ' ';
		}
	}
}
void DisplayBoard(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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}
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] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("坐标被占用\n");
			}
		}
		else
		{
			printf("坐标非法\n");
		}
	}
}

game.h

#pragma once
#include
#include
#include
//符号定义
#define ROW 3
#define COL 3
//函数声明
void IntiBoard(char board[ROW][COL], int row, int col);//初始化棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);//打印
void PlayerMove(char board[ROW][COL], int row, int col);//玩家下棋
void ComputerMove(char board[ROW][COL], int row, int col);//电脑下棋
char IsWin(char board[ROW][COL], int row, int col);//判断状况

   至此文章全部结束,感谢大家的观看。也希望大家对文中代码的不足积极提出自己的见解

三子棋的简单实现与其中的模块化设计思想_第4张图片

 

你可能感兴趣的:(c语言,开发语言,后端)