C语言学会数组就可以做五子棋 - 快来试试吧(包含源码)

本篇给大家带来两个玩家对战的五子棋,棋盘大小和玩家所下的棋子可以很容易的更改,读者如有更好的建议,可以在评论区留言

代码模块将分为3个部分,游戏页面逻辑书写写在 test.c 文件中,游戏所需要的声明书写在 game.h 文件中,实现游戏的函数写在 game.c 文件中


一、test.c文件

1. 游戏界面的书写

如果小伙伴不喜欢该游戏界面,可以自行更改菜单 mnue 函数中的内容

#include "game.h";		//需要用到 game.h 中的声明

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

为了让玩家有更好的游戏体验,游戏玩了一次之后我们可以选择再来一把,或者退出游戏,这里采用 do while 循环

int main()
{
	int input = 0;
	do
	{
		//菜单
		mnue();
		printf("请输入:>");
		scanf("%d", &input);
		system("cls");			//将屏幕已经打印的内容清理掉,
								//需要头文件是 stdlib.h

		//用户选择
		switch (input)
		{
		case 1:			//用户选择 1 开始游戏,进入到 game 函数中
			printf("欢迎来到五子棋,游戏开始\n");
			game();
			break;
		case 0:			//用户选择 0 退出游戏
			printf("退出游戏\n");
			break;
		default :		//如果选择的不是 1 则需要重新输入
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);	//选择 0 条件不成立会退出循环,已结束游戏
						//选择 1 或输入了其他数字,判断为真
						//进入循环,打印菜单,让用户重新选择

	return 0;
}

运行截图

2. 游戏逻辑的书写

用户选择 1 之后来到 game 函数

void game()
{
	char ret = 0;
	char board[ROW][COL];			//用于存储棋盘落子的信息
	init_board(board, ROW, COL);    //初始化棋盘落子的信息
									//用' '表示还没有落子
	print_board(board, ROW, COL);	//打印棋盘

	//提醒用户本游戏是如何落子
	printf("********************\n");
	printf("本游戏通过坐标来下棋\n");
	printf("********************\n");

	//玩家下棋是一个重复的过程
	//这里将玩家下棋封装成函数,其中第四个参数用来代表不同的玩家
	//每次落子之后,通过函数的返回值,来判断游戏是否结束
	while(1)
	{
		printf("玩家1 请下棋:>");
		ret = play_board(board, ROW, COL, PIECE_ONE);
		if (ret != 'C')
			break;

		system("cls");
		print_board(board, ROW, COL);	//下棋之后,打印棋盘

		printf("玩家2 请下棋:>");
		ret = play_board(board, ROW, COL, PIECE_TWO);	
		if (ret != 'C')
			break;

		system("cls");
		print_board(board, ROW, COL);	//下棋之后,打印棋盘
	}
	
	//判断是谁赢了,以及是否平局
	if (ret == PIECE_ONE)
	{
		printf("恭喜玩家1,获胜\n");
	}
	else if(ret == PIECE_TWO)
	{
		printf("恭喜玩家2,获胜\n");
	}
	else
	{
		printf("很遗憾,没有决出胜负\n");
	}

	Sleep(5000);	//让输出结果停留 5s 需要的头文件是 windows.h
	system("cls");
}

运行截图


二、game.h 文件

用来存放函数的声明,需要的头文件,以及棋盘的大小和棋子的字符,在该文件可以更改棋盘大小和棋子的符号

#define _CRT_SECURE_NO_WARNINGS 1	//在VS编译器用 scanf 等函数
									//需要定义该符号

//需要的包含的头文件
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

//需要更改棋盘的大小或棋子的符号,改这里就可以了
#define ROW 12					//棋盘的行数
#define COL 15					//棋盘的列数
#define PIECE_ONE '*'			//玩家1 的棋子
#define PIECE_TWO '#'			//玩家2 的棋子

//初始化
void init_board(int board[ROW][COL], int row, int col);

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

//玩家下棋,并判断游戏是否结束,用返回值带回判断的结果
//第四个参数通过传递玩家的棋子,用来代表不同的玩家
char play_board(char board[ROW][COL],
				 int row, int col,
				 char piece);

三、game.c 文件

1. 设计棋盘

棋盘的格子采用空格字符空格的形式

  • 一行中两个格子之间采用 | 分格
  • 相邻两行的格子中间采用连续的三个下划线
  • 两个连续的下划线之间也采用 | 分格

棋盘

2. 初始化棋盘

根据棋盘格子的设计,棋盘最初是会打印字符的,为了可以达到视觉的效果,将数组全部初始化成空格

#include "game.h"			//需要用到 game.h 中的声明

void init_board(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. 打印棋盘

打印棋盘

 print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;

	//打印列号
	printf("  ");		//为了对齐,打印行号时,会占用两个空格
	for (i = 1; i <= COL; i++)
		printf(" %x  ", i);		//因为10以上的数字会占用两个位置
								//这里采用十六进制的形式打印

	printf("\n");

	for (j = 0; j < row; j++)
	{
		//打印行号
		printf("%x ", j + 1);		//采用十六进制的形式打印

		//打印 * | * | * 	(这里 * 号表示字符)
		for (i = 0; i < col; i++)
		{
			printf(" %c ", board[j][i]);

			//不打印最后一列的竖线
			if (i < col - 1)
				printf("|");
		}
		printf("\n");

		//为了对齐
		printf("  ");
		
		//打印___|___|___ ,最后一行打印   |   |   
		for (i = 0; i < col; i++)
		{
			//将最后一行的3个下划线换成3个空格
			j == row - 1 ? printf("   ") : printf("___");

			//不打印最后一列的竖线
			if (i < col - 1)
				printf("|");
		}
		printf("\n");
	}
}

4. 玩家下棋

(1)玩家落子

char play_board(char board[ROW][COL], 
				int row, int col, 
				char piece)
{
	int x = 0;
	int y = 0;
	
	while (1)
	{
		scanf("%d %d", &x, &y);
		//坐标合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//坐标是否被占用,玩家并不知道要从 0 开始,所以坐标应当 -1
			if (board[x - 1][y - 1] != PIECE_ONE && 
				board[x - 1][y - 1] != PIECE_TWO)
			{
				board[x - 1][y - 1] = piece;

				//判断是否结束游戏,注意这里传参传的是调整好的坐标
				//最后一个参数是用来代表玩家的
				return is_win(board, row, col, x - 1, y - 1, piece);		
			}
			else
			{
				printf("输入错误,请重新输入:>");
			}
		}
		else
		{
			printf("坐标非法,请重新输入:>");
		}
	}
}

(2)判断玩家落子后是否游戏结束

玩家在下当前这棵棋子时,如果游戏结束,只可能是包含这棵棋子的横线五颗,竖线五颗,主斜线五颗,以及副斜线五颗

应当注意:在传参时已经将坐标调整为是从 0 开始的

这里我们以横线五颗为例说明,其他只需要改变判断的坐标,其思路是类似的

横线五颗

static char is_win(char board[ROW][COL], 
					int row, int col, 
					int x, int y, 
					char piece)
{
	int i = 0;
	int j = 0;
	int k = 0;
	
	//记录当前棋盘的总棋子数
	static int count = 0;
	count++;
		
	//判断竖线
	for (i = x - 4; i <= x; i++)
	{
		//坐标越界
		if (i < 0 || i + 4 >= row)
			continue;
		
		//判断是否有连续相等的五颗
		for (j = 0; j < 5; j++)
		{
			if (board[i + j][y] != piece)
				break;
		}

		//判断是否获胜
		if (j == 5)
			return  piece;
	}

	//判断横线
	for (i = y - 4; i <= y; i++)
	{
		//坐标越界
		if (i < 0 || i + 4 >= col)
			continue;

		//判断是否有连续相等的五颗
		for (j = 0; j < 5; j++)
		{
			if (board[x][i + j] != piece)
				break;
		}

		//判断是否获胜
		if (j == 5)
			return  piece;
	}

	//判断正斜线
	for (i = x - 4, j = y - 4; i <= x; i++, j++)
	{
		//坐标越界
		if (i < 0 || j < 0 || i + 4 >= row || j + 4 >= col)
			continue;
		
		//判断是否有连续相等的五颗
		for (k = 0; k < 5; k++)
		{
			if (board[i + k][j + k] != piece)
				break;
		}

		//判断是否获胜
		if (k == 5)
			return  piece;
	}

	//判断副斜线
	for (i = x + 4, j = y - 4; i <= x; i++)
	{
		//坐标越界
		if (i >= row || j < 0 || i + 4 < 0 || j + 4 >= col)
			continue;
			
		//判断是否有连续相等的五颗
		for (k = 0; k < 5; k++)
		{
			if (board[i - k][j + k] != piece)
				break;
		}

		//判断是否获胜
		if (k == 5)
			return  piece;
	}
	
	//判断是否平局
	if (count == row * col)
		return 'Q';
		
	return 'C';
}

四、源码

test.c 文件

#include "game.h";

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

void game()
{
	char ret = 0;
	char board[ROW][COL];
	init_board(board, ROW, COL);		//初始化
	print_board(board, ROW, COL);		//打印棋盘

	printf("********************\n");
	printf("本游戏通过坐标来下棋\n");
	printf("********************\n");

	while(1)
	{
		printf("玩家1 请下棋:>");
		ret = play_board(board, ROW, COL, PIECE_ONE);
		if (ret != 'C')
			break;

		system("cls");
		print_board(board, ROW, COL);

		printf("玩家2 请下棋:>");
		ret = play_board(board, ROW, COL, PIECE_TWO);	
		if (ret != 'C')
			break;

		system("cls");
		print_board(board, ROW, COL);
	}

	if (ret == PIECE_ONE)
	{
		printf("恭喜玩家1,获胜\n");
	}
	else if(ret == PIECE_TWO)
	{
		printf("恭喜玩家2,获胜\n");
	}
	else
	{
		printf("很遗憾,没有决出胜负\n");
	}

	Sleep(5000);
	system("cls");
}

int main()
{
	int input = 0;
	do
	{
		//菜单
		mnue();
		printf("请输入:>");
		scanf("%d", &input);
		system("cls");

		//用户选择
		switch (input)
		{
		case 1:
			printf("欢迎来到五子棋,游戏开始\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default :
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

game.h 文件

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>

#define ROW 8
#define COL 8
#define PIECE_ONE '*'
#define PIECE_TWO '#'

//初始化
void init_board(int board[ROW][COL], int row, int col);

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

//玩家下棋,并判断游戏是否结束
char play_board(char board[ROW][COL], int row, int col, char piece);

game.c 文件

#include "game.h"

void init_board(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 print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;

	//打印列号
	printf("  ");		//为了对齐
	for (i = 1; i <= COL; i++)
		printf(" %x  ", i);

	printf("\n");

	for (j = 0; j < row; j++)
	{
		//打印行号
		printf("%x ", j + 1);

		//打印 * | * | * 
		for (i = 0; i < col; i++)
		{
			printf(" %c ", board[j][i]);

			if (i < col - 1)
				printf("|");
		}
		printf("\n");

		//为了对齐
		printf("  ");
		//打印___|___|___ ,最后一行打印   |   |   
		for (i = 0; i < col; i++)
		{
			j == row - 1 ? printf("   ") : printf("___");

			if (i < col - 1)
				printf("|");
		}
		printf("\n");
	}
}


static char is_win(char board[ROW][COL], int row, int col, int x, int y, char piece)
{
	int i = 0;
	int j = 0;
	int k = 0;

	//记录当前棋盘的总棋子数
	static int count = 0;
	count++;

	//判断竖线
	for (i = x - 4; i <= x; i++)
	{
		if (i < 0 || i + 4 >= row)
			continue;

		for (j = 0; j < 5; j++)
		{
			if (board[i + j][y] != piece)
				break;
		}

		//判断是否获胜
		if (j == 5)
			return  piece;
	}

	//判断横线
	for (i = y - 4; i <= y; i++)
	{
		if (i < 0 || i + 4 >= col)
			continue;

		for (j = 0; j < 5; j++)
		{
			if (board[x][i + j] != piece)
				break;
		}

		//判断是否获胜
		if (j == 5)
			return  piece;
	}

	//判断正斜线
	for (i = x - 4, j = y - 4; i <= x; i++, j++)
	{
		if (i < 0 || j < 0 || i + 4 >= row || j + 4 >= col)
			continue;

		for (k = 0; k < 5; k++)
		{
			if (board[i + k][j + k] != piece)
				break;
		}

		//判断是否获胜
		if (k == 5)
			return  piece;
	}

	//判断副斜线
	for (i = x + 4, j = y - 4; i <= x; i++)
	{

		if (i >= row || j < 0 || i + 4 < 0 || j + 4 >= col)
			continue;

		for (k = 0; k < 5; k++)
		{
			if (board[i - k][j + k] != piece)
				break;
		}

		//判断是否获胜
		if (k == 5)
			return  piece;
	}

	//判断是否平局
	if (count == row * col)
		return 'Q';

	return 'C';
}

char play_board(char board[ROW][COL], int row, int col, char piece)
{
	int x = 0;
	int y = 0;
	
	while (1)
	{
		scanf("%d %d", &x, &y);
		//坐标合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//坐标是否被占用
			if (board[x - 1][y - 1] != PIECE_ONE && board[x - 1][y - 1] != PIECE_TWO)
			{
				board[x - 1][y - 1] = piece;

				return is_win(board, row, col, x - 1, y - 1, piece);		//判断是否结束游戏
			}
			else
			{
				printf("输入错误,请重新输入:>");
			}
		}
		else
		{
			printf("坐标非法,请重新输入:>");
		}
	}
}

本代码只提供了两个玩家对战的模式,读者有兴趣可以开发人机对战模式
代码亲测有效,如有建议,可以评论留言

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值