首页 > 其他分享 >C语言工程实践实现完整的五子棋项目一

C语言工程实践实现完整的五子棋项目一

时间:2024-08-18 14:23:24浏览次数:9  
标签:head cur int 五子棋 实践 pnew C语言 player printf

五子棋第一章

玩家信息部分

五子棋


前言

接下来的几篇文章我将带领大家实现一个工程实践的项目–五子棋。它是一个比较完整的项目,主要包括了三个部分:
1.玩家信息部分–主要实现对玩家信息的相关操作,包括玩家信息的注册、登录、保存、查看排名等功能的实现。
2.游戏内容部分–主要为五子棋游戏的内容逻辑,即五子棋游戏的实现。
3.人机对战部分–该部分主要实现五子棋游戏中的人机对战部分,我们在这里实现的人机对战不是通过随机数来实现电脑下棋的呆呆鸟人机,而是能真正赢下对局的智慧人机。
本项目完整的代码实现有1600多行(不含注释),对大家的C语言水平有一定的考验,需要大家熟练掌握C语言的语法以及使用,但我相信这个项目一定会让大家有很大的收获。
本次我将先讲解玩家信息部分的实现。


一、所需技术

本次我们使用C语言来实现这个五子棋项目,需要我们能够熟练掌握C语言的语法以及使用,同时还需要有一定的数据结构的基础,因为我们在这个项目将使用带头双向循环链表来存储玩家的信息,同时还会涉及到排序的思想和运用,再加上分文件编写的模式,我们才能很好地完成这个项目。

二、玩家信息部分实现

1.头文件部分

本次由于项目是一个比较完整的项目,所以我们不止一个头文件。我在这里定义了两个头文件,一个test.h和一个game.h。其中test.h里面主要放的就是我们的菜单以及玩家信息功能部分的函数声明和常规的头文件。game.h里面放的主要就是关于五子棋游戏内容部分实现所需的函数的声明
game.h

#define X 16
#define Y 16

typedef struct player
{
	char account[15];
	char password[15];
	char id[10];
	int win;
	int lose;
	int tie;
	int score;
	struct player* prev;
	struct player* next;
}player;

由于我们在实现玩家信息部分的时候需要用到这个定义玩家的结构体,所以我们先在game.h里面定义一个名为player的结构体来表示一个玩家,里面包含玩家的基本信息–账号,密码,ID,输赢场次,同时还有两个指针来实现双向链表。当然,棋盘的大小我们也可以顺便在这里宏定义出来。


test.h

#include"game.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<time.h>

player* ListInit();
void menu();
void rule();
void setcolor();
void log_in1(player* head, int num);
player* log_in2(player* head);
int checkaccount(player* head, char* pnew);
int checkid(player* head, char* pnew);
void register_account(player* head, int* psum);
void watch(player* p);
void modify(player* head);
void game(player* p, player* head, int num);
void sort(player* head, int num);
void rank(player* head, int num);
void destory(player** phead);

这个头文件包含了菜单以及玩家信息功能部分的函数声明和常规的头文件。由于是比较正式的项目,所以我们在这里的命名也没有用简单的拼音来命名,而是采用了比较标准的英文翻译来命名,所以每个函数的作用通过名字就能大致知晓。

2.菜单部分

int main()
{
	enum
	{
		toexit,
		torule,
		tologin,
		toregister,
		torank,
		tosetcolor
	};
	int num = 0;
	player* head = ListInit();
	//load(head, &num);
	int input = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case torule:
			rule();
			break;
		case tologin:
			log_in1(head, num);
			break;
		case toregister:
			register_account(head, &num);
			break;
		case torank:
			rank(head, num);
			break;
		case tosetcolor:
			setcolor();
			break;
		case toexit:
			sort(head, num);
			//save(head);
			destory(&head);
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

void menu()
{
	printf("-------------------------------------\n");
	printf("-----------------五子棋--------------\n");
	printf("---------------1.查看规则------------\n");
	printf("---------------2.登录游戏------------\n");
	printf("---------------3.注册账号------------\n");
	printf("---------------4.查看排名------------\n");
	printf("---------------5.颜色设置------------\n");
	printf("---------------0.退出游戏------------\n");
	printf("-------------------------------------\n");
}

这里没啥好说的,就是非常常规的do…while语句嵌套switch语句做目录,循环调用menu()函数并让玩家进行选择。
head为带头双向链表的头结点,num为存储的玩家信息数量。
然后通过枚举的方法来代替case后面的1,2等数字来提高代码可读性
这里我们先注释了两个函数load()和save(),这是最后将玩家信息存储在文件中的函数,但是这个是最后才需考虑的事情,所以我们就先注释掉了,最后才进行编写。


rule()函数,这个函数负责打印五子棋的规则。

void rule()
{
	printf("五子棋的基本规则如下:\n");
	printf("1.棋盘和棋子。五子棋的棋盘一般为15x15的棋盘,两名玩家分别使用黑棋(*)和白棋(#)对弈;\n");
	printf("2.游戏开始:游戏由黑棋(*)先下,然后双方轮流落子;\n");
	printf("3.落子规则:每次只能下一枚棋子,且必须落在棋盘的空白处。一旦棋子落在棋盘上,就不能再移动或拿掉;\n");
	printf("4.胜利条件:目标是成为第一个将自己的五个棋子在水平、垂直或对角线上连成一条直线的玩家,这可以是横、竖、斜方向上的连接,形成“五连“即可获得胜利。\n");
}

setcolor()函数,负责设置界面的背景颜色。

在所需要改变窗口颜色的位置调用函数 system(“color xx”) xx分别指的是背景颜色和文字(前景)颜色。x为一位16进制数,即1-f都可以使用。可以随意组合。
0 = Black (黑) 8 = Gray (灰)
1 = Blue (蓝) 9 = Light Blue (淡蓝)
2 = Green (绿) A = Light Green (淡绿)
3 = Aqua (水绿) B = Light Aqua
4 = Red (红) C = Light Red
5 = Purple (紫) D = Light Purple
6 = Yellow (黄) E = Light Yellow
7 = White (白) F = Bright White (亮白)

这里只是一个小技巧,与项目整体关系不大,所以我们在这里就不多加赘述了,如果感兴趣的可以自己去研究一下。

void setcolor()
{
	int n = 0;
	do
	{
		enum
		{
			exit,
			setred,
			setblue,
			setgreen,
			setyellow,
			setcyan,
			setwhite,
			setblack
		};
		printf("-------------------------------------\n");
		printf("-------------请选择背景颜色----------\n");
		printf("---------1.红色          2.蓝色------\n");
		printf("---------3.绿色          4.黄色------\n");
		printf("---------5.青色          6.白色------\n");
		printf("--------------7.默认黑色-------------\n");
		printf("--------------0.设置完成-------------\n");
		printf("-------------------------------------\n");
		scanf("%d", &n);
		switch (n)
		{
		case setred:
			system("color 40");
			break;
		case setblue:
			system("color 90");
			break;
		case setgreen:
			system("color 20");
			break;
		case setyellow:
			system("color E0");
			break;
		case setcyan:
			system("color B0");
			break;
		case setwhite:
			system("color F0");
			break;
		case setblack:
			break;
		case exit:
			system("cls");
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (n);
}

3.玩家信息部分

ListInit()函数,进行链表的初始化操作。这里没有传二级指针,是通过给返回值的方式来改变实参。

player* ListInit()
{
	//初始化
	player* phead = (player*)malloc(sizeof(player));
	if (phead == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

destory(player** phead)函数,进行链表的销毁操作。这里要传二级指针,因为这样才能通过形参的解引用来改变实参,将head也进行销毁并设置为空。

void destory(player** phead)
{
	assert(phead);
	player* cur = *phead;
	while (cur != *phead)
	{
		player* next = cur->next;
		free(cur);
		cur = next;
	}
	free(*phead);
	*phead = NULL;
}

checkaccount(player* head, char* pnew),checkid(player* head, char* pnew)函数,顾名思义,检查玩家的账号和检查玩家ID是否存在的函数。通过对链表进行遍历检查是否存在,存在返回0,不存在返回1。

int checkaccount(player* head, char* pnew)
{
	player* cur = head->next;
	while (cur != head)
	{
		if (strcmp(cur->account, pnew) == 0)
		{
			return 0;
		}
		cur = cur->next;
	}
	return 1;
}
int checkid(player* head, char* pnew)
{
	player* cur = head->next;
	while (cur != head)
	{
		if (strcmp(cur->id, pnew) == 0)
		{
			return 0;
		}
		cur = cur->next;
	}
	return 1;
}

register_account(player* head, int* pnum)函数,进行玩家信息的注册操作。这里就需要调用上面的checkaccount(player* head, char* pnew)和checkid(player* head, char* pnew)函数检查账号和ID是否存在,如果没有存在,即不重复才能成功注册。成功注册之后对玩家的各项信息进行初始化,然后进行带头双向链表的头插,这里就是数据结构的相关知识了。

void register_account(player* head, int* pnum)
{
	assert(head);
	player* pnew = (player*)malloc(sizeof(player));
	if (pnew == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	while (1)
	{
		printf("请输入您的账号(15个字符以内):\n");
		scanf("%s", pnew->account);
		int a = checkaccount(head, pnew->account);
		if (a == 0)
		{
			printf("账号重复,请重新输入!\n");
		}
		else
		{
			break;
		}
	}
	printf("请输入您的密码(15个字符以内):\n");
	scanf("%s", pnew->password);
	while (1)
	{
		printf("请输入您的昵称(10个字符以内):\n");
		scanf("%s", pnew->id);
		int b = checkid(head, pnew->id);
		if (b == 0)
		{
			printf("昵称重复,请重新输入!\n");
		}
		else
		{
			break;
		}
	}
	printf("恭喜您注册账号成功\n");
	pnew->win = 0;
	pnew->lose = 0;
	pnew->tie = 0;
	pnew->score = 0;
	//头插
	player* next = head->next;
	head->next = pnew;
	pnew->prev = head;
	pnew->next = next;
	next->prev = pnew;
	(*pnum)++;
}

log_in1(player* head, int num)函数,这个函数实现1号玩家进行登录的操作。即输入账号和密码,然后遍历链表进行确认是否正确。顺便一提,这里可以加一个密码保护功能,即输密码时将密码内容变为***,有兴趣的可以自主实现一下。如果账号和密码都正确,这里就调用game()函数进入游戏部分的菜单

void log_in1(player* head, int num)
{
	assert(head);
	printf("请输入您的账号:\n");
	char a[15] = { 0 };
	char b[15] = { 0 };
	scanf("%s", a);
	printf("请输入您的密码:\n");
	scanf("%s", b);
	player* cur = head->next;
	while (cur != head)
	{
		if (strcmp(cur->account, a) == 0 && strcmp(cur->password, b) == 0)
		{
			//game(cur, head, num);
			return;
		}
		cur = cur->next;
	}
	printf("账号或密码错误\n");
}

log_in2(player* head)函数,这个函数实现2号玩家进行登录的操作。为后面的玩家对战部分做好铺垫。

player* log_in2(player* head)
{
	assert(head);
	printf("请二号玩家登录\n");
	printf("请输入您的账号:\n");
	char a[15] = { 0 };
	char b[15] = { 0 };
	scanf("%s", a);
	printf("请输入您的密码:\n");
	scanf("%s", b);
	player* cur = head->next;
	while (cur != head)
	{
		if (strcmp(cur->account, a) == 0 && strcmp(cur->password, b) == 0)
		{
			return cur;
		}
		cur = cur->next;
	}
	printf("账号或密码错误,返回前页\n");
	return NULL;
}

总结

本次五子棋项目的第一章就先到这里了,我们在这一章完成了基本目录,玩家信息的登录,注册等相关功能,下一章预计将讲解游戏部分的菜单以及对链表的内容进行排序等内容。

如需源码,可在我的gitee上找到,下面是链接。
五子棋源码

每文推荐

后弦–画风
任然–山外小楼夜听雨
邓紫棋–唯一

标签:head,cur,int,五子棋,实践,pnew,C语言,player,printf
From: https://blog.csdn.net/2401_86557724/article/details/141292522

相关文章

  • 【C语言篇】数组和函数的实践:扫雷游戏(附源码)
    文章目录前言扫雷游戏的分析和设计扫雷游戏的功能说明游戏的分析和设计文件结构设计扫雷游戏的代码实现初始化棋盘打印棋盘布置雷排查雷扫雷游戏的拓展前言源码在最后扫雷游戏的分析和设计经典扫雷游戏扫雷游戏的功能说明使⽤控制台实现经典的扫雷游戏游戏可......
  • 【C语言】内存函数
    memcpy使用和模拟实现void*memcpy(void*destination,constvoid*source,size_tnum);介绍:•函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。•这个函数在遇到‘\0’的时候并不会停下来。•如果source和destinati......
  • C语言学习————常量和宏、初识指针
    #define定义常量和宏define是一个预处理指令用途:1.define定义符号#defineMAX1000intmain(){ printf("%d\n",MAX); return0;}2.define定义宏#defineADD(X,Y)((X)+(Y))intmain(){ printf("%d\n",ADD(2,3)); return0;}指针内存内存是计算机上特......
  • C语言 错误 C4716 “printstar”: 必须返回一个值
    错误:解决办法: 此类代码显然不需要返回值1.点击“项目”——“属性”2.点击“C/C++”中的“高级”——“禁用特定警告”中输入“4716”——点击“确定” 3.重新运行即可成功 ......
  • c语言(8.18)
    补一下前几天练习的代码#include<stdio.h>intmain(){ intn=0; //scanf("%d",&n); intsum=0; for(intj=1;j<=10;j++) { intmul=1;//mul应该放在里面才行,每一次新的阶乘mul都是1 for(inti=1;i<=j;i++) { mul=mul*i; }......
  • 【时时三省】(C语言基础)调试技巧2
    山不在高,有仙则名。水不在深,有龙则灵。             ----CSDN时时三省多多动手,尝试调试,才能有进步。•一定要熟练掌握调试技巧。•初学者可能80%的时间在写代码,20%的时间在调试。但是一个程序员可能20%的时间在写程序,但是80%的时间在调试。•我......
  • 【C语言初阶】掌握C语言调试技巧,迈向高效编程的阶梯
    ......
  • 油管视频《编程思维》中的题目,使用C语言编写出来,第十集,世界上的机器
    题目:假设首先我拥有大量的机器人,从迷宫中心的水晶出发,其次我拥有取之不尽的线轴,这些线非常结实耐用,可以在必要时刻切断线,现在面对一个迷宫,迷宫以水晶为中心,哪里有许多条死胡同,但没有一条会绕回到起点,我只有一次机会,可以在机器人们跳入迷宫,寻找出口前,发送一条简单的指令,请问什么......
  • “Datawhale x魔搭 AI夏令营”-AIGC方向-Day3从零入门AI生图原理&实践
    学习内容提要从通过代码实现AI文生图逐渐进阶,教程偏重图像工作流、微调、图像优化等思路,最后会简单介绍AIGC应用方向、数字人技术(选学)Task03:进阶上分-实战优化具体Datawhale教程学习内容见链接:https://linklearner.com/activity/14/10/37框架资源网站名称链接地......
  • C语言程序设计-[22] 数组应用
    1、简单插入排序算法根据以上分析,代码与结果如下:#include"stdio.h"intmain(){ intcount,j,x,a[101];scanf("%d",&x);if(x<=0) return;else a[1]=x;count=1;scanf("%d",&x);while(x>0){ ......