【C语言】三子棋游戏【数组与函数】

文章目录

    • 一、实现思路
    • 二、源码以及效果展示
      • game.h//函数头文件的引用以及功能函数的声明
      • test.c//游戏主逻辑的实现
      • game.c//函数功能函数的实现
    • 三、项目学习感想

一、实现思路

  • 首先,进入游戏界面我们肯定需要一个菜单吧——打印菜单吧
  • 然后,下棋需要有棋盘吧——打印棋盘
  • 然后,我们【人】下棋需要选择位置吧——人下棋的函数
  • 再然后,电脑下棋——电脑下棋的函数
  • 另外,当我们下完棋,或者电脑下完棋,需要进行判断是人赢了还是电脑赢了,还是平局了,亦或是人都没有赢谁也没有输还可以接着下——判断游戏状态的函数

需要补充的一点是,每次人/电脑下完棋,我们都肯定是想要看到效果的,所以每次下完棋,我们都需要打印一次棋盘,更新一次棋盘。

并且开始打印的棋盘都是视觉上的空棋盘,但实际上是我们用空格填充的。

棋盘,我们可以用一个二维数组来表示。

下边我们来具体看看怎么实现吧~

1.【打印菜单】

菜单里边肯定有我们选择到底是玩还是不玩吧,所以一定有两个选项.

下边提供一种较为简单的方法:

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

2.【打印棋盘】

打印棋盘分为两个阶段,一个是还没有开始下棋的时候,另一个就是已经下的时候。

下边就是两种情况,我们来分析一下。
【C语言】三子棋游戏【数组与函数】_第1张图片
【C语言】三子棋游戏【数组与函数】_第2张图片

大体思路分析:

1.我们整体可以分为两个部分,一个是分割信息部分,另一个是非分隔信息部分。

2.很显然我们可以把它看成几个重复的小单元,通过for循环实现

【C语言】三子棋游戏【数组与函数】_第3张图片

实现的时候注意几个点:①换行②打印的条件——边界都是没有竖行/横线的,所以需要有一个打印分割信息的时候都需要有一个判断条件

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

	}
}

其实未下棋与下棋是只有一个区别,是棋盘数据元素是空格还是代表电脑或者人的棋子的特殊符号。

所以在一开始打印棋盘的时候,我们还需要另外定义一个初始化棋盘的函数

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

在人/电脑下棋的时候将下棋位置的坐标修改成其他的元素,然后在此调用打印棋盘的函数,就能够得到想要的打印的结果了。

3.【人下棋】

人下棋是通过选择位置位置坐标来实现的。所以这个函数的实现我们只需要输入坐标,然后修改对应的元素值即可。

但是这个位置可不是随便修改的,有以下三种情况不能够选择,第一,是人原来已经占的位置,第二,是电脑原来占得位置,第三,是超过棋盘所能表示范围的位置。

只有当我们输入的坐标位置的元素是空格的时候,我们才能完成这次操作。

因此,这里我们可以通过一个while的死循环循环来实现,当发现输入的位置表示的元素是空格时我们修改并且跳出循环,否则一直选择位置。

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, 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");
		}
	}
}

4.【电脑下棋】

电脑下棋与人下棋类似,只不过电脑下棋的位置是我们用随机数产生的特定范围的坐标。

说明:随机数的种子,程序已经一开始种下

void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:>\n");
	int x = 0, y = 0;
	while (1)
	{
		x = rand() % row;//0~2
		y = rand() % col;//0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

5.【判断游戏状态】

我们通过列举将可能回赢的情况一一列举出来,然后进行判断即可。

不过在进行此函数代码书写之前,我们还需要进行判断棋盘是否满函数的定义,为判断是否平局的情况做准备。

char IsWin(char board[ROW][COL], int row, int col)
{
	//先判断行
	//判断列
	//判断对角线
	int i, j;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			//其实这里判断的就有些局限了,只能够判断三子棋了
			//判断行
			if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
			{
				return board[i][0];
			}
			//判断列
			if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
			{
				return board[0][j];
			}
			//判断对角线
			if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' '
				|| board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
			{
				return board[1][1];
			}
		}
		//没人赢,就要平局——棋盘满了
		if (IsFull(board, row, col))
		{
			return 'Q';
		}
		else
		{
			return 'C';//继续
		}
	}
}
int IsFull(char board[ROW][COL], int row, int col)
{
	int i, j, tag = 1;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

二、源码以及效果展示

game.h//函数头文件的引用以及功能函数的声明

test.c//游戏主逻辑的实现

game.c//函数功能函数的实现

//game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS

#include 
#include 
#include 

//方便以后更改棋盘大小
#define ROW 3
#define COL 3

//初始化棋盘
void InitBoard(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);

//判断输赢——其实还是在弄数组元素值得判断
//玩家赢-‘*’
//电脑赢-‘#’
//平局-'Q'【以上三种都不能够再玩了】
//继续-'C'
char IsWin(char board[ROW][COL], int row, int col);
//test.c
//菜单
//打印棋盘
//下棋
#include "game.h"
void menu()
{
	printf("******************************\n");
	printf("****    1. play  0.exit   ****\n");
	printf("******************************\n");
}
void game()
{
	int ret;
	//3*3的数组存放棋盘的信息
	// 打印棋盘——下棋了打印空格,不下棋打印符号
	//玩家下棋
	// 电脑下棋
	//判断输赢的过程
	char board[ROW][COL] = { 0 }; 
	//初始化棋盘的功能
	InitBoard(board,ROW,COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
	//下棋
	while (1)
	{
		//玩家部分
		PlayerMove(board, ROW, COL);//其实就是对数组的操作
		//判断输赢
		ret= IsWin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		DisplayBoard(board, ROW, COL);
		//电脑部分
		ComputerMove(board, ROW, COL);
		//判断输赢
		ret = IsWin(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");
}
int main()
{
	srand((unsigned int)time(NULL));//设置随机数的生成起点
	int input;
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			printf("开始游戏\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
//game.c
#include "game.h"
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i, j;
	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, j;
	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)
		{
			int k = 0;
			for (k = 0;k < col;k++)
			{
				printf("---");
				if(k<col-1)
					printf("|");
			}
			printf("\n");
		}

	}
}

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, 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");
		}
	}
}

//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:>\n");
	int x = 0, y = 0;
	while (1)
	{
		x = rand() % row;//0~2
		y = rand() % col;//0~2
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断输赢
//判断行
//判断列
//判断对角线
int IsFull(char board[ROW][COL], int row, int col)
{
	int i, j, tag = 1;
	for (i = 0;i < row;i++)
	{
		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, j;
	for (i = 0;i < row;i++)
	{
		for (j = 0;j < col;j++)
		{
			//其实这里判断的就有些局限了,只能够判断三子棋了
			//判断行
			if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
			{
				return board[i][0];
			}
			//判断列
			if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
			{
				return board[0][j];
			}
			//判断对角线
			if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' '
				|| board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
			{
				return board[1][1];
			}
		}
		//没人赢,就要平局——棋盘满了
		if (IsFull(board, row, col))
		{
			return 'Q';
		}
		else
		{
			return 'C';
		}
	}
}

【C语言】三子棋游戏【数组与函数】_第4张图片

三、项目学习感想

1.利用宏定义定义棋盘的大小,方便后续的更改

2.分文件编写,.h文件里边放头文件的引用和功能函数的声明,test.c文件里边是游戏整体逻辑的实现,game.c里边放的是游戏函数的定义。

3.棋盘如何打印——分成小块循环控制,还要特别注意边界的控制

4.判断输赢的操作如何实现,同时如何减少代码冗余——分类

5.如何实现人下棋和电脑下棋的操作

你可能感兴趣的:(项目学习,C/C++专题练习总结,c语言,游戏)