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

五子棋第三章

游戏内容部分


前言

本篇文章为完整工程实践五子棋项目的第二章。
如未看过前面的章节:
五子棋第一章
五子棋第二章
本篇文章我们将讲解游戏内容部分的实现。


一、头文件部分

game.h

#include<windows.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;

void gameprocess(player* p, player* q);
void init(char board[X][Y], int x, int y);
void print(char board[X][Y], int x, int y);
int play1(char board[X][Y], int x, int y);
int play2(char board[X][Y], int x, int y);
int judge(char board[X][Y], int x, int y, int a, int b);

我们在之前就只在game.h这个头文件里面定义了一个player的结构体,但现在我们要进行游戏内容部分的编写,所以我们在这个头文件里面还需要放入我们实现游戏内容部分所需的函数的声明。然后我们游戏内容部分的代码就不在之前的menu.c里面进行编写了,我们直接新建一个game.c的源文件,然后先#include"test.h"包含头文件,再在里面进行游戏内容部分代码的编写。

特别注意

我们这个项目有两个头文件test.h和game.h,然后我们在test.h里面用#include"game.h"包含game.h。这里由于我们在游戏部分还会用到windows.h这个头文件里面的内容,所以还需要包含这个头文件,但如果我们直接在test.h里面使用#include<windows.h>包含这个头文件会报错。经过我的研究发现,当我们需要包含windows.h这个头文件时,我们需要把它放到所有头文件中最前面的那一个。在这里即是我们将#include<windows.h>这一语句放到game.h里面,然后再在test.h里面用#include"game.h"包含game.h同时也包含windows.h,这样写就不会报错

二、游戏内容部分

1、游戏进程

gameprocess(player* p,player* q)函数

void gameprocess(player* p,player* q)
{	
	player* f = NULL;
	player* s = NULL;
	srand((unsigned int)time(NULL));
	int r = rand();
	if (r % 2 == 0)
	{
		printf("玩家%s先手\n", p->id);
		Sleep(3000);
		f = p;
		s = q;
	}
	else
	{
		printf("玩家%s先手\n", q->id);
		Sleep(3000);
		f = q;
		s = p;
	}
	char board[X][Y] = { 0 };
	init(board, X, Y);
	print(board, X, Y);
	while (1)
	{
		int sum = 0;
		printf("轮到%s玩家(*)下棋\n", f->id);
		int ret1 = play1(board, X, Y);
		if (ret1 == 1)
		{
			f->win++;
			f->score = (f->win - f->lose);
			s->lose++;
			s->score = (s->win - s->lose);
			print(board, X, Y);
			printf("\n");
			printf("恭喜玩家%s获胜\n", f->id);
			printf("\n");
			Sleep(5000);
			break;
		}
		sum++;
		if (sum == 113)
		{
			printf("平局\n");
			f->tie++;
			s->tie++;
			print(board, X, Y);
			printf("\n");
			Sleep(5000);
			break;
		}
		print(board, X, Y);
		printf("轮到%s玩家(#)下棋\n", s->id);
		int  ret2 = play2(board, X, Y);
		if (ret2 == 2)
		{
			s->win++;
			s->score = (s->win - s->lose);
			f->lose++;
			f->score = (f->win - f->lose);
			print(board, X, Y);
			printf("\n");
			printf("恭喜玩家%s获胜\n", s->id);
			printf("\n");
			Sleep(5000);
			break;
		}
		print(board, X, Y);
	}
}

这个函数控制整体游戏的进程。这里p代表1号玩家(当前登录玩家),q代表2号玩家;f代表first即先手的玩家;s代表second即后手的玩家。在这个函数里面我们先用随机数随机确定哪个玩家是先手,然后将先手的玩家(player类型的变量)赋值给f,后手的玩家(player类型的变量)赋值给s。接着init(board, X, Y)函数初始化棋盘,print(board, X, Y)函数打印棋盘,紧接着就是死循环里面控制游戏进程,play1(board, X, Y)函数进行先手玩家下棋,play2(board, X, Y)函数进行后手玩家下棋。当游戏结束时,就对胜利的一方的胜利次数(win),失败次数(lose),分数(score)进行更新并跳出死循环


2、初始化及打印

init(char board[X][Y],int x,int y)函数

void init(char board[X][Y],int x,int y)
{
	int i = 0;
	for (i = 0; i < x; i++)
	{
		int j = 0;
		for (j = 0; j < y; j++)
		{
			board[i][j] = ' ';
			if (i == 0)
			{
				board[i][j] = j;
			}
			if (j == 0)
			{
				board[i][j] = i;
			}
		}
	}
}

这个函数进行五子棋游戏棋盘的初始化操作,由于五子棋棋盘大小为15*15,所以在这里为了方便玩家下棋,我们宏定义的X,Y均为16,为的就是在这里在外面多一圈数字标识几排几列,所以我们在这里初始化时除了把棋盘部分设置为空字符,还要把外面一圈的标识数字初始化上去


print(char board[X][Y], int x, int y)函数

void print(char board[X][Y], int x, int y)
{
	int i = 0;
	for (i = 0; i < x; i++)
	{
		int j = 0;
		for (j = 0; j < y; j++)
		{
			if (i == 0 || j == 0)
			{
				printf("%3d", board[i][j]);
			}
			else
			{
				printf(" %c ", board[i][j]);
			}
			printf("|");
		}
		printf("\n");
		for (j = 0; j < y; j++)
		{
			printf("---|");
		}
		printf("\n");
	}
}

这个函数负责打印棋盘,这个函数和init(char board[X][Y],int x,int y)函数完成初始化后打印出来的棋盘就应该如下:
初始化棋盘


3、玩家下棋

play1(char board[X][Y], int x, int y)函数

int play1(char board[X][Y], int x, int y)
{
	int a = 0;
	int b = 0;
	while (1)
	{
		printf("请输入下棋坐标:");
		scanf("%d %d", &a, &b);
		if (a >= 1 && a < x && b >= 1 && b < y)
		{
			if (board[a][b] == ' ')
			{
				board[a][b] = '*';
				int ret = judge(board, X, Y, a, b);
				return ret;
			}
			else
			{
				printf("该位置已有棋子,请重新选择下棋坐标\n");
			}
		}
		else
		{
			printf("下棋坐标选择错误,请重新输入\n");
		}
		
	}
}

play2(char board[X][Y], int x, int y)函数

int play2(char board[X][Y], int x, int y)
{
	int a = 0;
	int b = 0;
	while (1)
	{
		printf("请输入下棋坐标:");
		scanf("%d %d", &a, &b);
		if (a >= 1 && a < x && b >= 1 && b < y)
		{
			if (board[a][b] == ' ')
			{
				board[a][b] = '#';
				int ret = judge(board, X, Y, a, b);
				return ret;
			}
			else
			{
				printf("该位置已有棋子,请重新选择下棋坐标\n");
			}
		}
		else
		{
			printf("下棋坐标选择错误,请重新输入\n");
		}

	}
}

play1(char board[X][Y], int x, int y)和play2(char board[X][Y], int x, int y)基本完全一样。唯一的区别就是下的棋子不一样,这里我们定义先手的玩家下的棋子是*,后手的玩家下的棋子是#。当确定好下棋位置后,我们就调用judge(board, X, Y, a, b)函数进行判断游戏是否结束


4、判断胜负

judge(char board[X][Y], int x, int y,int a,int b)函数

int judge(char board[X][Y], int x, int y,int a,int b)
{
	char z = board[a][b];
	while (1)
	{
		int n = -1;         
		int i = a;
		while (i >= 1)
		{       
			if (board[i][b] == z)
			{
				n++;
				if (n == 5)
				{  
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i--;
			}
			else
			{
				break;
			}
		}
		i = a;
		while (i < x)
		{
			if (board[i][b] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i++;
			}
			else
			{
				break;
			}
		}
		break;
	}
	while (1)
	{
		int n = -1;
		int i = b;
		while (i >= 1)
		{
			if (board[a][i] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i--;
			}
			else
			{
				break;
			}
		}
		i = b;
		while (i < y)
		{
			if (board[a][i] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i++;
			}
			else
			{
				break;
			}
		}
		break;
	}
	while (1)
	{
		int n = -1;
		int i = a;
		int j = b;
		while (i >= 1 && j >= 1)
		{
			if (board[i][j] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i--;
				j--;
			}
			else
			{
				break;
			}
		}
		i = a;
		j = b;
		while (i < x && j < y)
		{
			if (board[i][j] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i++;
				j++;
			}
			else
			{
				break;
			}
		}
		break;
	}
	while (1)
	{
		int n = -1;
		int i = a;
		int j = b;
		while (i >= 1 && j < y)
		{
			if (board[i][j] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i--;
				j++;
			}
			else
			{
				break;
			}
		}
		i = a;
		j = b;
		while (i < x && j >=1)
		{
			if (board[i][j] == z)
			{
				n++;
				if (n == 5)
				{
					if (z == '*')
					{
						return 1;
					}
					else if (z == '#')
					{
						return 2;
					}
				}
				i++;
				j--;
			}
			else
			{
				break;
			}
		}
		break;
	}
	return 0;
}

这个函数负责判断游戏是否结束,可以说是游戏进程里面最重要的一个函数。我们在这里就采用最常规的思路进行判断,分别用循环对四个方向进行判断。由于四个方向的思路都是一致的,所以接下来我就只对一个方向的代码进行讲解。

while (1)
{
	int n = -1;         
	int i = a;
	while (i >= 1)
	{       
		if (board[i][b] == z)
		{
			n++;
			if (n == 5)
			{  
				if (z == '*')
				{
					return 1;
				}
				else if (z == '#')
				{
					return 2;
				}
			}
			i--;
		}
		else
		{
			break;
		}
	}
	i = a;
	while (i < x)
	{
		if (board[i][b] == z)
		{
			n++;
			if (n == 5)
			{
				if (z == '*')
				{
					return 1;
				}
				else if (z == '#')
				{
					return 2;
				}
			}
			i++;
		}
		else
		{
			break;
		}
	}
	break;
}

int judge(char board[X][Y], int x, int y,int a,int b),首先我们要明白每个参数的意义,x,y就是我们宏定义的X,Y即棋盘大小;a,b即是我们当前进行下棋的位置。我们上来先用了一个变量z来记录当前位置的棋子。然后这一部分的代码是对水平方向的这一条线进行检查。在while (i >= 1)这一个循环语句里面,我们先对该位置左边的棋子进行检查,如果等于我们一开始记录的棋子z,我们就让其数量n++,如果不等于,就说明那一边被对面的棋子堵住了或者为空,我们就跳出该循环;而while (i < x)这一个循坏语句就是同理地检查右边的棋子。当然,在这两个循环中如果n==5时就说明已经5颗连上了,我们这时就判断z代表的棋子为先手(*)还是后手(#),先手返回1,后手返回2,这样一来水平方向的棋子我们就检查完毕了。这里有一点需要注意,因为我们从a行b列这个位置开始分别对两边进行检查,所以我们的n一开始需要设置成-1,不然这个位置会被多算一次。然后同理再对四个方向的棋子进行进行检查。在最后的时候我们返回0,代表游戏没有结束,还需继续


总结

本次五子棋项目的第三章就先到这里了,我们在这一章完成了游戏内容部分,实现了五子棋游戏的完整逻辑,到这里其实已经算是一个比较完整的工程实践项目了,下一章预计将讲解五子棋游戏人机对战逻辑的具体实现。

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

如对您有所帮助,可以来个三连,感谢大家的支持。

每文推荐

锤娜丽莎–我太笨
弦子–天空之外
邵雨薇–星月

学技术学累了时可以听歌放松一下

  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值