三子棋小游戏实现

目录

简易版

主函数部分

游戏部分

初识化棋盘

打印棋盘

玩家输入

电脑输入

 判断输赢

优化版

主函数部分

游戏部分

一:初始化数组

二:打印棋盘

三:玩家输入

四:电脑输入

五:判断输赢


简易版

主函数部分

我们先创建两个头文件,一个test.c, 一个game.c, 一个源文件game.h。

我们的主函数和游戏实现的函数引用放再test.c。

函数的声明放在game.h。

函数的实现放在game.c。

第一步主函数:

要实现游戏我们要先让他运行一次,然后我们输入选择玩还是不玩。

打印一个菜单,然后选择1  /   0,选择错误,提示一下,然后重新选择。

 这样写一个是方便别人看,有时候代码太长了阅读感不是很好,所以分模块封装,一个是也可以当知识产权的保护。

游戏部分

主函数部分写完了下面就是游戏部分的实现。

首先我们创建一个数组字符数组char board[3][3],因为我们下棋里面要放*和#,所以使用字符数组,然后三子棋嘛,就创建一个3 * 3 的格子。

然后将这个二维数组全部初识化为空格,再把棋盘打印出来。

初识化棋盘

打印棋盘

 代码实现

玩家输入

玩家要输入两个值,所以先创建整型的X   Y变量,然后用scanf读取。

玩家习惯的应该第一个坐标是1  1,但是数组的坐标是0  0, 所以再读取完后我们再给他加个1。

然后判断玩家输入的是不是再1~3之间,也就是判断合法性。

再判断玩家输入的这个坐标是否为空格,也就是是否被占用。

这些判断如果不成立就需要重新输入,所以这里应该是一个循环

电脑输入

电脑输入就要生成随机数,我们会用到srand,只要调用一次,所以我们在主函数里面加上。

生成的是0~2的数字,表示数组代表的坐标,所以不用判断合法性。

这里也要判断是否被占用。

判断条件失败的话要重新产生随机数,所以这里也是个循环。

 判断输赢

下完一次棋无非有四种情况:

  • 玩家赢了
  • 电脑赢了
  • 平局
  • 游戏继续

我们写一个函数,判断玩家赢了输出*。

                                   电脑赢了输出#。

                                   平局输出“Q”。

                                   游戏继续输出“C”。

判断输赢:

三行是否相等:board[0][0] 和 board[0][1] 和 board[0][2] 这是第一行,用for循环3次,把三行都遍历一遍。

三列是否相等:board[0][0] 和 board[1][0] 和board[2][0] 这是第一列,用for循环3次,把三列都遍历一遍。

对角线是否相等: board[0][0] 和 board[1][1] 和 board[2][2]这是正对角,board[0][2] 和 board[1][1]

和board[2][0] 这是反对角是否相等。

上面判断完相等之后并且不能等于空格。

判断平局:

将整个数组遍历一遍,看看数组里面是否还有空格,没有了就是平局。

 

如果以上三个都不成立就是游戏继续。

我们在玩家下完后和电脑下完后都判断一下,输出的是C就继续游戏,不是C就跳出循环,打印结果。

当判断结果是C的时候,要继续游戏,所以应该是个循环。

完整代码如下:

test.c部分

#include "game.h"

void game()
{
	char ret = 0;
	char board[3][3];
	initialization(board, 3, 3);   //初识化数组
	print(board, 3, 3);            //打印棋盘
	while (1)
	{
		player_move(board, 3, 3);  //玩家输入
		ret = judge_win_or_lose_board(board, 3, 3);  //判断输赢
		if (ret != 'C')
		{
			break;
		}
		computer_move(board, 3, 3);  //电脑输入
		ret = judge_win_or_lose_board(board, 3, 3);  //判断输赢
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢了!\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢了!\n");
	}
	else if (ret == 'Q')
	{
		printf("平局!\n");
	}
}	

game.c部分

#define  _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void meun()
{
	printf("*******************\n");
	printf("****  1. play  ****\n");
	printf("****  0. exit  ****\n");
	printf("*******************\n");
}

void initialization(char board[3][3], int row, int col)  
{
	for (int i = 0; i < 3; i++)  //通过两层for循环遍历整个数组,然后全部初识化为空格
	{
		for (int j = 0; j < 3; j++)
		{
			board[i][j] = ' ';
		}
	}
}


void print(char board[3][3], 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)      //最后一个 | 不用打印, 所以减1
				{
					printf("|");
				}
			}
			printf("\n");            //第一行打印完换行
		}
	}
}

void player_move(char board[3][3], int row, int col)
{
	int x, y;   //创建两个变量用来储存要输入的值
	while (1)
	{
		printf("(第一个坐标为1 1,后面依次类推) 玩家输入:<\n");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3)  //判断合法性
		{
			if (board[x - 1][y - 1] == ' ') // 判断坐标是否被占用
			{
				board[x - 1][y - 1] = '*';
				break;                     //替换成功跳出循环
			}
			else
			{
				printf("此坐标被占用,请重新选择!\n");  //被占用提示一下重新输入
			}
		}
		else
		{
			printf("输入错误,请重新输入!\n");             //坐标不合法提示一下,重新输入
		}
	}
}


void computer_move(char board[3][3], int row, int col)
{
	printf("电脑输入!\n");
	while (1)
	{
		int x = rand() % row;  //产生0~2的数字
		int y = rand() % col;  //产生0~2的数字
		if (board[x][y] == ' ')//判断是否被占用
		{
			board[x][y] = '#';
			break;
		}
	}
}

int already_expired(char board[3][3], 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
}

char judge_win_or_lose_board(char board[3][3], 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][2] != ' ')
		{
			return board[i][0];
		}
	}
	for (i = 0; i < row; i++)   //判断三列
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ')
		{
			return board[0][i];
		}
	}

	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
		return board[1][1];     //判断对角

	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
		return board[1][1];     //判断对角

	if (already_expired(board, row, col) == 1)
	{
		return 'Q';            //判断平局
	}
	return 'C';               //游戏继续
}

game.h部分

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

//打印菜单
void meun();


//初始化数组
void initialization(char board[3][3], int row, int col);


//打印棋盘
void print(char board[3][3], int row, int col);


//玩家下棋
void player_move(char board[3][3], int row, int col);


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


//判断输赢
char judge_win_or_lose_board(char board[3][3], int row, int col);


//判断平局
int already_expired(char board[3][3], int row, int col);

优化版

主函数部分

主函数部分和基础版的一样,其中肯定也要产生随机数和菜单,我们直接将基础版的复制过来。

我们要优化的部分是玩家不仅仅限于三个格子,还可以产生更大的格子,所以我们的棋盘要改为可以变换的,判断方式也要改。

 

主函数部分还是一样。

游戏部分

第一步肯定也是创建一个字符数组,然后初识化,我们的数组不能直接写死,所以在game.h部分定义两个常量,来决定数组的大小。

 

 

然后我们创建数组的时候把数组创大一点,在ROW的基础上加个4,到判断的时候用的上。

一:初始化数组

这个和基础版也是一样,将整个数组都初识化为空格。

 

就是注意在游戏传参的时候要传ROW+4这样才是整个数组。

二:打印棋盘

 

黑色部分是我们需要用到的格子,红色部分是我们留着后面判断用的,所以我们要把棋盘打印出来,并且要将对应的数组放入格子中,我们在基础版上要进行一定的更改。

可以看出我们第一个格子的位置是2-2, 最后一个是6-6,在基础版上我们是从0开始打印的,所以在这里我们要从二开始,然后传进去的是5,但是最后一个是6,所以后面要在ROW的基础上加个2

其他的打印空格+字符+空格+ | 是一行的第一部分不变,--- + |是第一行的第二部分也不变。

三:玩家输入

玩家输入的第一个肯定是1 1,我们的下标指向的却是2 2,所以我们在玩家输入的基础上还要加上一个1,。

在判断他的合法性,是否在2 到 ROW+1中。

在判断是否是空格。

 

输入错误或者被占用要重新输入,所以也是个循环。

四:电脑输入

电脑输入我们让他产生随机数是2~6就行,所以合法性不用判断,只需要判断是否被占用。

 

五:判断输赢

这次我们不能像之前一样把把所有可以赢的格子都表示出来,那么我们怎么判断呢。

我们可以从玩家和电脑输入的坐标去找,上下左右对角是否相等。如图:

大上下左右:

大对角:

 

小上下左右:

小对角:

 

 

然后用if语句把他们都写进去就行,那么问题来了,怎么知道玩家和电脑下的坐标呢,出来电脑和玩家的输入函数就消失了,那么我们直接写一个函数,然后再输入函数内调用他,那么我们的输入函数不能再使用void类型了,要改为char型。

上面是玩家输赢,接下来就是平局了,和之前一样把整个数组遍历一遍看看有没有空格。

以上情况都不是就是平局。

 

然后再玩家和电脑输入后都判断一下,将判断结果返回。所以改完之后变这样。

 

后面再判断输出的是 # 、 * 、 Q 、C中的哪一个,然后进行选择。

完整代码如下:

test.c部分:

#define  _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void game()
{
	char ret = 0;
	char board[ROW + 4][COL + 4];
	init_board(board, ROW + 4, COL + 4);  //初识化整个数组
	print_borad(board, ROW, COL);         //打印棋盘
	while (1)
	{
		ret = player_move(board, ROW, COL);      //玩家输入
		if (ret != 'C')
		{
			break;
		}
		ret = computer_move(board, ROW, COL); //电脑输入
		if (ret != 'C')
		{
			break;
		}

	}
	if (ret == '#')
	{
		printf("电脑赢了!!\n");
		print_borad(board, ROW, COL);
	}
	else if (ret == '*')
	{
		printf("玩家赢了!!\n");
		print_borad(board, ROW, COL);
	}
	else if (ret == 'Q')
	{
		printf("平局!!\n");
		print_borad(board, ROW, COL);
	}
}


int main()
{
	int input = 0;
	srand((unsigned int)(time(NULL)));
	do {
		meun();
		printf("请选择是否游戏\n");
		scanf("%d", &input);
		printf("\n");
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏");
			break;
		default:
			printf("选择错误请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

game.c部分:

#define  _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

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

void init_board(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++)
		{
			board[i][j] = ' ';
		}
	}
}

void print_borad(char board[ROW][COL], int row, int col)
{
	int i = 2;
	int j = 2;
	for (i = 2; i < row + 2; i++)  //传入的是5,下标从2开始放入,到7停止,
	{
		//打印第一行第一部分
		for (j = 2; j < col + 2; j++)
		{
			printf(" %c ", board[i][j]);//此时第一个下标为2 - 2
			if (j < col + 1)     //跟基础版一样,最后一列的 | 不用打印,本来+2现在-1就变成+1.
			{
				printf("|");
			}
		}
		printf("\n");
		if (i < row + 1)  //跟基础版一样,最后一行的 --- 不用打印,本来+2现在-1就变成+1.
		{
			//打印第一行第二部分--- + | 
			for (j = 2; j < col + 2; j++)
			{
				printf("---");
				if (j < col + 1)  //同上
				{
					printf("|");
				}
			}
			printf("\n");
		}
	}
}


int already_expired(char board[ROW][COL], int row, int col)
{
	int i = 2;
	int j = 2;
	//遍历的数组是我们使用的数组,不是整个,所以是从2-ROW+2中
	for (i = 2; i < row + 2; i++)
	{
		for (j = 2; j < col + 2; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
char judge(char board[ROW][COL], int x, int y)
{
	if (board[x][y] == board[x][y + 1] && board[x][y] == board[x][y + 2] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x][y - 1] && board[x][y] == board[x][y - 2] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x + 1][y] && board[x][y] == board[x + 2][y] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x - 1][y] && board[x][y] == board[x - 2][y] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x + 1][y + 1] && board[x][y] == board[x + 2][y + 2] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x - 1][y - 1] && board[x][y] == board[x - 2][y - 2] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x - 1][y + 1] && board[x][y] == board[x - 2][y + 2] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x + 1][y - 1] && board[x][y] == board[x + 2][y - 2] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x - 1][y] && board[x][y] == board[x + 1][y] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x][y - 1] && board[x][y] == board[x][y + 1] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x + 1][y - 1] && board[x][y] == board[x - 1][y + 1] && board[x][y] != ' ')
		return board[x][y];
	if (board[x][y] == board[x - 1][y - 1] && board[x][y] == board[x + 1][y + 1] && board[x][y] != ' ')
		return board[x][y];
	if (already_expired(board, ROW, COL))
	{
		return 'Q';
	}
	return 'C';
}

char player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋:<");  //提示玩家可以下棋了
	int x = 0;
	int y = 0;
	while (1)
	{
		scanf("%d %d", &x, &y);
		x = x + 1;  //在玩家输入的基础上+1
		y = y + 1;
		//判断玩家输入的是否正确
		if (x >= 2 && x <= col + 1 && y >= 2 && y <= col + 1)
		{
			//判断玩家输入是否被占用
			if (board[x][y] == ' ')
			{
				board[x][y] = '*';
				break;
			}
			else
			{
				printf("此坐标被占用,请重新选择!\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	}
	print_borad(board, ROW, COL);  //输入完打印一下棋盘。
	return judge(board, x, y);
}


char computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑输入!\n");
	while (1)
	{
		x = rand() % row + 2;  //+2表示产生从0-4的数字变成2-6。
		y = rand() % col + 2;
		//判断是否被占用
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
	print_borad(board, ROW, COL);  //输入完打印一下棋盘。
	return judge(board, x, y);
}

game.h部分:

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

#define ROW 5
#define COL 5

void meun();

void init_board(char board[ROW][COL], int row, int col);

void print_borad(char board[ROW][COL], int row, int col);

int already_expired(char board[ROW][COL], int row, int col);

char judge(char board[ROW][COL], int x, int y);

char player_move(char board[ROW][COL], int row, int col);

char computer_move(char board[ROW][COL], int row, int col);

评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值