【C语言】N子棋小游戏♔

目录

前言

一、何为N子棋游戏?

二、游戏思路

三、游戏实现

3.1 模块化

3.2 游戏棋盘

3.3 下棋操作

3.3.1 玩家下棋

3.3.2 电脑下棋

3.4 判断输赢

总结


前言

三子棋小游戏相信大家都玩过吧,类似的5子琪等等,这篇文章将带着大家从0到1实现一个简易版的N子棋小游戏,当然,是不带图形界面的。通过本文,可以让初学者对C语言的基础知识点的了解更进一步。


一、何为N子棋游戏?

鉴于文章的严谨性,可能有的老铁没有玩过N子棋类的游戏,这里有个网页版的游戏,可以去体验体验:三子棋(井字棋)

N代表的连线的棋子,如五子棋等等。


二、游戏思路

实现该游戏大致分为以下几步:

  1. 分模块进行,三个文件,分别为游戏测试逻辑、游戏逻辑、游戏声明。
  2. 棋盘准备。
  3. 玩家下棋。
  4. 判断输赢
  5. 电脑下棋
  6. 判断输赢

三、游戏实现

我们规定:

  • * 为玩家下棋的字符标志
  • # 为电脑下棋的字符标志

3.1 模块化

C语言中,对于一个完整的项目来说,不可能只在一个源文件下实现,往往需要分模块进行,在C语言标准中:

  • .h头文件放置函数声明和头文件包含
  • 专门一个.c源文件用于进行测试逻辑,也就是用于调用函数。
  • 专门一个.c源文件用于函数的实现(定义)
  • .....

在该游戏中,我们要用到三个文件,分别是:

  • 游戏测试逻辑(.c文件)
  • 游戏实现逻辑(.c文件)
  • 游戏声明(.h文件)

下面我带着大家进行创建:

游戏测试逻辑
游戏测试逻辑

游戏实现逻辑
游戏实现逻辑

游戏说明
游戏声明

3.2 游戏棋盘

该类游戏都会有一个棋盘,类似于三子棋:

既然是下棋,那就需要构建一个棋盘,可以看出,棋盘由行和列组成,那么我们可以用二维数组来表示,因为二维数组就是以行和列组成的,当然,为了游戏的完整性,我们可以先打印一个菜单供用户选择。

test.c 文件
#include "game.h" //引入头文件
void mnue()
{
	printf("*******************\n");
	printf("*****1、play*******\n");
	printf("*****0、exit*******\n");
	printf("*******************\n");
}

int main()
{

	int input = 0;
	//一进来就打印菜单,让用户选择,用do...while最合适
	do
	{
		mnue();
		printf("请选择:>"); 
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			break;
		}
		
	} while (input);
	return 0;
}

game.h文件
#include <stdio.h>

因为用户一进游戏,无论用户是否选择开始游戏,都会进行一次打印菜单,因此这里用do...while结构非常合适。


菜单准备完毕,下面开始准备棋盘:

定义一个二维数组来存放下棋数据,也相当于棋盘:

char board[ROW][COL] = { 0 };

这里的数组大小不能写死,因为我们做的游戏是N子棋,对于数组大小,我们用#define标识符定义:

game.h文件
//棋盘大小
#define ROW 3
#define COL 3

数组是否要进行初始化?要进行的,并且要全部初始化为空格,为什么呢?我们可以观察一下棋盘:

我们发现,在没有下棋时,格子的位置明显是一个空格进行占位,因此我们将数组初始化为空格,进行占位。

test.c 文件
void game()
{
	//1、定义一个二维数组来存放下棋数据,也相当于棋盘
	//让棋盘大小可变,定义#define标识符
	char board[ROW][COL] = { 0 };
	//2、初始化棋盘,让数组内容均为空格,这样做是为了占位
	Initialize_Board(board, ROW, COL);
}

game.h 文件
#pragma once
#include<stdio.h>
#include<string.h>
//棋盘大小
#define ROW 3
#define COL 3
//初始化棋盘
void Initialize_Board(char board[ROW][COL],int row, int col);

game.c 文件
#include "game.h"
//初始化棋盘
void Initialize_Board(char board[ROW][COL], int row,int col)
{
	//方法1:嵌套for循环
	/*for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}*/

	//方法二:memset库函数,修改执行字节大小的值
	// 三个参数:起始地址,修改内容,字节个数
	memset(&board[0][0], ' ', row*col *sizeof(board[0][0]));

}

进行初始化操作有两个方法:

  • 嵌套for循环(常规)
  • memset库函数,制定修改n字节的内容,具体可看文档:memset

初始化完毕后,接下来就该构造出棋盘了,其实就是打印棋盘操作,基本思路如下:

  • 棋盘组成:一行内容|,一行---|
  • 边界条件:一行内容|中的|为COL-1列;一行---|中---为ROW-1行,|为COL-1列。
test.c 文件
void game()
{
	//1、定义一个二维数组来存放下棋数据,也相当于棋盘
	//让棋盘大小可变,定义#define标识符
	char board[ROW][COL] = { 0 };
	//2、初始化棋盘,让数组内容均为空格,这样做是为了占位
	Initialize_Board(board, ROW, COL);
    //3、打印棋盘
	Print_Board(board, ROW, COL);
}

game.h 文件
#pragma once
#include<stdio.h>
#include<string.h>
//棋盘大小
#define ROW 3
#define COL 3
//初始化棋盘
void Initialize_Board(char board[ROW][COL],int row, int col);
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col);

game.c 文件
#include "game.h"
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col)
{
	//两部分:
	//1、内容+|
	//2、--- + |
	for (int i=0; i<row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			//如3×3棋盘,那就打印两列|就可以了
			if (j<col-1)
			{
				printf("|");
			}
		}
		//给内容+| 和 ---|之间换行
		printf("\n");
		//这里打印---|
		//也一样,只打印两行---即可
		if (i<row-1)
		{
			//为每一列打印---|
			for (int i = 0; i < col; i++)
			{
				printf("---");
				//同样的,只打印2列|
				if (i<col-1)
				{
					printf("|");
				}
			}
		}
		//换行,表示这一行打印完成
		printf("\n");
	}
}


棋盘准备完毕,我们运行看看效果:


3.3 下棋操作

棋盘完成后,进入下棋逻辑,下棋我们要考虑几个方面:

  • 下棋后应打印出最新的棋盘。
  • 判断输赢

3.3.1 玩家下棋

玩家根据棋盘的坐标进行下棋,这里的逻辑有几个注意点:

  • 玩家眼中的坐标与数组表示的下标不同。
  • 输入的坐标有边界条件:x>=1 && x<=row && y>=1 && y<=col
  • 判断输入的坐标中是否已下过棋
test.c 文件
void game()
{
	//1、定义一个二维数组来存放下棋数据,也相当于棋盘
	//让棋盘大小可变,定义#define标识符
	char board[ROW][COL] = { 0 };
	//2、初始化棋盘,让数组内容均为空格,这样做是为了占位
	Initialize_Board(board, ROW, COL);
    //3、打印棋盘
	Print_Board(board, ROW, COL);
    //4、玩家下棋
	Root_Play(board, ROW, COL);
    //5、打印棋盘
	Print_Board(board, ROW, COL);
}

game.h 文件
#pragma once
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
//棋盘大小
#define ROW 3
#define COL 3
//初始化棋盘
void Initialize_Board(char board[ROW][COL],int row, int col);
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col);
//玩家下棋
void Root_Play(char board[ROW][COL], int row, int col);

game.c 文件
#include "game.h"
//玩家下棋
// * 为玩家下棋的字符标志
// # 为电脑下棋的字符标志
void Root_Play(char board[ROW][COL], int row, int col)
{
	//根据坐标下棋
	int x = 0;
	int y = 0;
	
	//边界条件
	//最小不能为x!=0,y!=0
	//最大x>row+1,y>col+1
	//边界条件为:x ==0 || y == 0 || x > row || y > col
	//还要判断输入的坐标中是否已下过棋
	while (1)
	{
		printf("请玩家输入下棋坐标:>");
		scanf("%d %d", &x, &y);
		//如果超出边界条件,那就提示一下,然后重新输入
		if (x ==0 || y == 0 || 
			x>row || y>col)
		{
			printf("坐标超出范围...\n");
		}
		//如果输入的坐标中已下过棋,则重新下棋
		else if (board[x - 1][y - 1] == '*' || 
			board[x - 1][y - 1] == '#')
		{
			printf("该坐标已下过棋...\n");
		}
		else
		{
			//为坐标棋盘赋值
			//x-1,y-1是因为计算机中下标是从0开始
			//但用户不懂这些,按照正常坐标,因此要-1
			board[x - 1][y - 1] = '*';
			system("cls");
			break;
		}
	}

}

在下棋时,需要将坐标-1,因为用户理解的坐标是常规的,但在编写代码时,数组以下标进行访问,因此坐标-1。我们看看效果:

在玩家下完棋后,使用system("cls");清空屏幕,这样更加好看。

因为下完棋打印最新的棋盘就是打印棋盘的逻辑,这里就不讲了,只需要调用打印棋盘函数即可。


3.3.2 电脑下棋

关于电脑下棋这一功能,我们用随机数生成坐标即可。与玩家下棋不同,电脑下棋通过生成随机坐标,范围是由我们定义的,因此坐标也符合我们的想法,这里就不用添加边界条件了,但要判断输入的坐标中是否已下过棋。

test.c 文件
void game()
{
	//1、定义一个二维数组来存放下棋数据,也相当于棋盘
	//让棋盘大小可变,定义#define标识符
	char board[ROW][COL] = { 0 };
	//2、初始化棋盘,让数组内容均为空格,这样做是为了占位
	Initialize_Board(board, ROW, COL);
    //3、打印棋盘
	Print_Board(board, ROW, COL);
    //4、玩家下棋
	Root_Play(board, ROW, COL);
    //5、打印棋盘
	Print_Board(board, ROW, COL);
    //4、电脑下棋
	Ai_Play(board, ROW, COL);
	//5、打印棋盘
	Print_Board(board, ROW, COL);
}
int main()
{

	int input = 0;
	//定义随机数生成棋,一次就够
	srand((unsigned int)time(NULL));
	//一进来就打印菜单,让用户选择,用do...while最合适
	do
	{
		mnue();
		printf("请选择:>"); 
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			break;
		}
		
	} while (input);
	return 0;
}

game.h 文件
#pragma once
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
//棋盘大小
#define ROW 3
#define COL 3
//初始化棋盘
void Initialize_Board(char board[ROW][COL],int row, int col);
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col);
//玩家下棋
void Root_Play(char board[ROW][COL], int row, int col);
//电脑下棋
void Ai_Play(char board[ROW][COL], int row, int col);


game.c 文件
#include "game.h"
//电脑下棋
// * 为玩家下棋的字符标志
// # 为电脑下棋的字符标志
void Ai_Play(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] = '#';
			Sleep(500);
			system("cls");
			break;
		}
	}
}

电脑下棋中,关键点在于生成随机坐标,生成随机数使用srand和rand就可以了,自定义随机数的范围: int x = rand() % row; int y = rand() % col; 最后我们看看效果:

电脑下棋时,有短暂的暂停效果,这是因为加入了:Sleep(500); 睡眠。同样的,在电脑下完棋后,调用打印棋盘函数。


3.4 判断输赢

不管是玩家下完棋还是电脑下完棋,都应该进行输赢的判断,因此写一个函数即可。

判断输赢的条件如下:

  • 任意一行连成线
  • 任意一列连成线
  • 任意一条对角线连成线

不同的几种结果:

  • 玩家胜利(1)
  • 电脑胜利(-1)
  • 平局(2)
  • 继续(0)

不可能一次下棋就能判断出结果,大多情况是继续,因此下棋操作一个循环。

判断输赢函数:

//判断输赢
/*
	获胜条件:
	1、任意一行连成线
	2、任意一列连成线
	3、两条对角线任一连成线
    返回结果:
	1、玩家胜利返回1
	2、电脑胜利返回-1
	3、平局返回2
	4、继续返回0
*/
//判断是否平局
int Isdraw(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			//当数组还要空格,说明还没结束棋局
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	//此时棋局结束,平局
	return 2;
}
int Judge_Wins_And_Losers(char board[ROW][COL], int row, int col)
{
	
	//任意一行连成线
	for (int i = 0; i < row; i++)
	{
		int flag1 = 0;//玩家赢
		int flag2 = 0;//电脑赢
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == '*')
			{
				flag1++;
			}
			else if(board[i][j] == '#')
			{
				flag2++;
			}
		}
		if (flag1 == ROW)
		{
			return 1;
		}
		else if (flag2 == ROW)
		{
			return -1;
		}
	}
	//任意一列连成线
	for (int i = 0; i < col; i++)
	{
		int flag1 = 0;//玩家赢
		int flag2 = 0;//电脑赢
		for (int j = 0; j < row; j++)
		{
			if (board[j][i] == '*')
			{
				flag1++;
			}
			else if (board[j][i] == '#')
			{
				flag2++;
			}
		}
		if (flag1 == ROW)
		{
			return 1;
		}
		else if (flag2 == ROW)
		{
			return -1;
		}
	}

	//任意一对角线连成线
	//左边到右边对角线
	int flag1 = 0;//玩家赢
	int flag2 = 0;//电脑赢
	int j = 0;
	int k = 0;
	for (int i = 0; i < col; i++)
	{
		if (board[j][i] == '*')
		{
			j++;
			flag1++;
		}
		else if (board[k][i] == '#')
		{
			k++;
			flag2++;
		}
		if (flag1 == ROW)
		{
			return 1;
		}
		else if (flag2 == ROW)
		{
			return -1;
		}
	}

	//右边到左边对角线
	flag1 = 0;//玩家赢
	flag2 = 0;//电脑赢
	j = 0;
	k = 0;
	for (int i = col-1; i >= 0; i--)
	{
		if (board[j][i] == '*')
		{
			j++;
			flag1++;
		}
		else if (board[k][i] == '#')
		{
			k++;
			flag2++;
		}
		if (flag1 == ROW)
		{
			return 1;
		}
		else if (flag2 == ROW)
		{
			return -1;
		}
	}
	//谁都没有胜利,直接返回
	//是否平局
	if (Isdraw(board, row, col))
	{
		return 2;
	}
	//继续
	return 0;
}

对于判断输赢函数逻辑,关键在于判断棋盘中是否有构成一条线的情况,我们用两个计数变量进行统计。


主函数中接收返回值,并进行判断输赢:

void game()
{
	//1、定义一个二维数组来存放下棋数据,也相当于棋盘
	//让棋盘大小可变,定义#define标识符
	char board[ROW][COL] = { 0 };
	//2、初始化棋盘,让数组内容均为空格,这样做是为了占位
	Initialize_Board(board, ROW, COL);
	//3、打印棋盘
	Print_Board(board, ROW, COL);
	int num = 0;
	while (1)
	{
		//4、玩家下棋
		Root_Play(board, ROW, COL);
		//5、打印棋盘
		Print_Board(board, ROW, COL);
		//6、判断输赢
		//1---玩家胜利
		//-1---电脑胜利
		//2---平局
		//0---继续
		num = Judge_Wins_And_Losers(board, ROW, COL);
		if (num == 1 || num == -1 || num == 2)
		{
			break;
		}
		//4、电脑下棋
		Ai_Play(board, ROW, COL);
		//5、打印棋盘
		Print_Board(board, ROW, COL);
		//6、判断输赢
		num = Judge_Wins_And_Losers(board, ROW, COL);
		if (num == 1 || num == -1 || num == 2)
		{
			break;
		}
	}

	//打印获胜方
	if (num == 1)
	{
		printf("玩家胜利\n");

	}
	else if (num == -1)
	{
		printf("电脑胜利\n");

	}
	else
	{
		printf("平局\n");

	}
}

当出现不是继续的情况,则break跳出循环,然后再进行输赢判断。


对应的游戏声明代码:

#pragma once
/*
	游戏逻辑
	用于存放头文件和函数的声明
*/
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define ROW 3
#define COL 3
//初始化棋盘
void Initialize_Board(char board[ROW][COL],int row, int col);
//打印棋盘
void Print_Board(char board[ROW][COL], int row, int col);
//玩家下棋
void Root_Play(char board[ROW][COL], int row, int col);
//判断输赢
int Judge_Wins_And_Losers(char board[ROW][COL], int row, int col);
//电脑下棋
// * 为玩家下棋的字符标志
// # 为电脑下棋的字符标志
void Ai_Play(char board[ROW][COL], int row, int col);

运行演示一下吧:

N子棋游戏运行测试


总结

这就是关于C语言实现N子棋小游戏,希望对您有所帮助,关注我,干货满满!!源代码自取。

  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值