当前位置:   article > 正文

C语言经典项目实战——三子棋_c语言项目实战

c语言项目实战

三子棋,又称井字棋,是我们经常爱玩的游戏,今天来学习,如何用C语言来实现一个简单的三子棋小游戏。

一、分析三子棋项目需求

首先对我们要实现的三子棋项目,分析它的需求。为了了解它需要哪些函数,我们先模拟一遍下棋的过程、

/*
	1.初始化棋盘
	
	2、玩家落子
	2.1 展示棋盘
	2.2 判断胜负
	
	3、电脑落子
	3.1 展示棋盘
	3.2 判断胜负
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这个步骤中,我们看到,要实现三子棋,起码要实现,棋盘创建,期盼展示,玩家落子,电脑落子,胜负判断等功能的函数。由此就可以开展,三子棋的实现。

二、各个功能实现

首先在play.h,这个头文件中,写下棋盘的初始化信息,如棋盘为三行三列

#ifndef _PLAY_H
#define _PLAY_H

#define ROW 3
#define COL 3

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:这里的#ifdef 后面加的名字可以是任意的,但一般都是自己的大写文件名,这一句代码的作用是,防止在头文件中重复申明了多个函数,或者是结构体

然后进行函数申明,(这里我们还没有写主函数,也没有进行任何一个变量定义,但是没有关系,我们先进行函数的申明,然后再play.c文件中,进行函数定义)


void menu();//菜单
void InitBoard(char board[ROW][COL]);//初始化棋盘
void ShowBoard(char board[ROW][COL]);//展示棋盘
void Player(char board[ROW][COL]);//玩家下棋
void Computer(char board[ROW][COL]);//电脑下棋
char IsWin(char board[ROW][COL]);//判断胜负
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

根据上一步的需求分析,我们知道主要实现的就是,初始化棋盘,棋盘展示,玩家下棋,电脑下棋,判断胜负,这几个函数,所以我们先将其定义出来,其中的参数我们都向其中传入棋盘board。

接下来,进行函数实现,注意,接下来的函数定义,都在play.c中实现。
首先看菜单函数的实现。
下棋的时候,我要知道按什么按键开始游戏,按什么按键退出游戏,所以需要一个菜单函数

1.menu()函数

void menu()
{
	printf("请开始游戏吧!\n");
	printf("---------------------------------\n");
	printf("-----1.开始--------0.退出--------\n");
	printf("---------------------------------\n");

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个函数向玩家提示,按1开始游戏,按0退出。
来看它的显示效果

在这里插入图片描述

2.InitBoard()函数

接下来实现棋盘的初始化功能


void InitBoard(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			board[i][j] = ' ';
		}
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

我们遍历整个二维数组,将其中的每个单元都赋值为‘ ’(空格),先对其进行初始化。

然后实现棋盘展示功能
实现这个功能的时候,并不是把这个二维数组简单的按顺序打印(当然也可以,只要你不嫌简陋) 这里,我们想个我们的棋盘加上一些边界,让棋盘看上去美观一点、就像这样:
在这里插入图片描述
在这些空格中放入棋子。

3.ShowBoard()函数

接下来看,如何打印这个棋盘。

void ShowBoard(char board[ROW][COL])
{
	for (int i = 0; i < ROW * 2 + 1; i++)
	{
		if (i % 2 == 0)
		{
			for (int j = 0; j < COL; j++)
			{
				printf("----");
			}
			printf("\n");
		}
		else
		{
			for (int j = 0; j < COL * 2 + 1; j++)
			{
				if (j % 2 == 0)
				{
					printf("|");
				}
				else
				{
					printf(" %c ", board[i / 2][j / 2]);
				}
			}
			printf("\n");
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

注意:我上面给的只是一开始没下棋时候的棋盘,而我们用这个函数的时候,不仅要展示棋盘的边界,还要展示其中的棋子。所以,我们将整个棋盘打印的时候看成是7行7列的一个数组,然后去打印。
观察棋盘,我们发现横线只在0,2,4,6行出现,数据在1,3,5行出现,竖线在0,2,4,6列出现,数据在1,3,5列出现。由此就可以打印出我们期待的棋盘的样子。

4.Player()函数

然后实现玩家下棋


void Player(char board[ROW][COL])
{
	while (1)
	{
		printf("请输入你的座标:");
		int x, y;
		scanf("%d,%d", &x, &y);
		if (x>=1 && x<=ROW && y>=1 && y<=COL)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = 'X';
				break;
			}
			else
			{
				printf("该位置已经有棋子!\n");
			}
		}
		else
		{
			printf("输入错误。\n");
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在这个函数中,要注意使用了while函数,而且还是给了判断条件1,也就是说,如果在这个函数中不使用break的话,就陷入了死循环。为什么要这么设计呢,因为,我们要预防用户输入的棋子坐标不正确 或者 此位置上有其他棋子的情况,需要重新输入,只有当输入正确时,才结束循环,否则一直输入。
看一下函数效果

在这里插入图片描述

5.Computer()函数

下面来看电脑下棋功能的实现
和玩家下棋类似,只不过这一次,不需要我们在手动输入,而是采用随机数,进行随机位置下棋。


void Computer(char board[ROW][COL])
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'O';
			break;
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

我们让随机数对行数和列数取模,这样就不会有越界的情况,同时使用while循环,防止电脑下在了已经有棋子的地方,来保证电脑棋子的正确性。
下面来看函数效果、
在这里插入图片描述
不管是电脑下棋,还是玩家下棋,下过一步之后都要进行胜负判断,判断是否应该结束游戏

7.IsWin()函数

所以下面来实现,判断胜负函数:


char IsWin(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	for (int j = 0; j < COL; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
		{
			 return board[0][j];
		}
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}
	if (IsFull == 0)
	{
		return 'Q';
	}
	return 'n';
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

在这里我们给函数定义一个返回值,目的是通过返回值来判断是否出现赢家,还是应该继续游戏。三子棋判断胜利的条件十分简单,只要判断每一行是否全为相同字母,或者每一列,或者每一个对角线,是否有三个相同的棋子,如果有,则返回这个棋子,这样我们就能在调用它的时候知道,哪一方获得胜利,这里有个小技巧,就是我们不用设置一个字母来放置判断条件然后在最后统一返回,而是在每一次判断后,如果成功就直接返回,这样可以减少代码量。
在判断完是否有赢家之后,如果这个函数还没有结束,就说明没有赢家,那我们就要判断棋盘是否已经满了,如果满了,但是没有出现赢家,说明应该按照平局处理,如果没有赢家,棋盘也没满,则说明结果正常,游戏继续。
观察代码,如果玩家胜利的话,就返回’X’,电脑胜利就返回’O’,平局的话就返回’Q’,如果游戏继续的话就返回’n’,这里我们把返回’n’看作正常情况,那么只要这个函数返回的不是字符n,就说明要么玩家胜利,要么电脑胜利,要么平局,不论如何,游戏应该结束了。利用这个特征,对我们之后调用这个函数,非常有帮助。

同时,判断棋盘有没有满的函数也很简单,只要遍历一遍棋盘,如果发现有空格,说明没满,返回1,如果遍历完成之后,也没有找到空格,说明棋盘满了,返回0;
下面来看具体实现:


int IsFull(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
			{
				return 1;
			}
		}
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

到此处为止,三子棋项目所需要的所有函数已经完成,之后要进行的就是对于这些函数的调用问题。我们仍然设置一个叫RunGame的函数来进行游戏的调用,目的就是为了保证主函数的简短和可读性。

三、RunGame函数和main函数的实现

为了对主函数进行化简,我们设置了RunGame函数,在三子棋这个项目中,因为本身游戏所用到的函数并不多,也不需要复杂的调用,所以这个RunGame函数是可以省略的,直接在主函数中实现,但是需要知道,在其他大一点的项目中,最好添加上这一项,保证可读性。
同时,添加上这个函数,还有一个好处,就是这个函数代表了一次游戏的执行过程,这样我们就能实现,让玩家结束一局游戏之后,选择继续游戏,还是退出游戏。
下面来看具体实现:


void RunGame()
{
	char board[ROW][COL];
	InitBoard(board);
	ShowBoard(board);
	char temp;
	while (1)
	{
		Player(board);
		ShowBoard(board);
		temp = IsWin(board);
		if (temp != 'n')
		{
			break;
		}
		Computer(board);
		ShowBoard(board);
		temp = IsWin(board);
		if (temp != 'n')
		{
			break;
		}
	}
	if (temp == 'X'){
		printf("玩家胜利!\n");
	}
	else if (temp == 'O')
	{
		printf("电脑胜利!\n");
	}
	else if (temp == 'Q')
	{
		printf("平局!\n");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

既然是游戏的一次执行过程,那么在游戏的最开始,我们需要先定义一个二维数组当作我们的棋盘,然后调用InitBoard函数来初始化,之后向玩家展示这个初始化好的空棋盘。对于一个游戏来说,只要游戏没有结束,那么我们就应该一直执行它,所以,需要在一个死循环中来执行游戏,当if判断应该结束游戏时,才跳出循环。
这里巧妙地运用了IsWin函数的返回值,用temp来接收函数返回值,每次判断不需要,逐个字符去判断是否相等,而是判断是否为’n’,如果为’n’,说明游戏继续,如果不为’n’,不管它是什么,都说明游戏应该结束,这时跳出循环,在循环体外,判断temp,来说明到底是什么情况。

由于我们之前已经做了很多的铺垫,所以主函数就会十分简短,下面来看主函数的调用


srand((unsigned)time(NULL));
	int input;
	do{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			RunGame();
			break;
		case 0:
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

第一句的sand语句,是为了种下一个随机种子,我们在Computer函数中,要求电脑用随机数生成数据,但是,其实生成的是伪随机数,我们重复运行函数就会发现,电脑下的棋都会出现在固定位置,为了解决这个问题,我们引入随机种子,这个种子的位置可以随意,但一定要在调用Computer函数之前。
之后是一个do~while循环,通过我们输入的数据,如果输入1,则开始游戏,输入0则退出游戏。

四、项目的整体分析

到这里,整个三子棋游戏也就完成了,下面来对我们实现的三子棋游戏进行总结。
首先我们完成了真正的三子棋的大部分必备功能,包括棋盘初始化,玩家和电脑相会对弈,游戏判断胜负等等,它搭建起来一个三子棋游戏的框架,就算是真正的的三子棋游戏,它的内核实现,也是这样的。当然,这个项目也存在问题,就是,我们虽然实现了电脑下棋,但是真正运行就会发现,我们设置的这个电脑玩家总是“笨笨的”,它不知道往哪里下棋可以赢得比赛,这是因为,我们给电脑的只是一个随机数,我们可以保证这个随机数一定是符合要求的,但是不能保证这个随机数会帮助电脑取得胜利,所以和这样的电脑下棋是无聊的。
但是,前面提到,这个项目已经明确了三子棋的框架结构,我们还可以设置两个真人玩家互相对弈,只需要把玩家下棋的代码复制一份,给玩家2,然后调用的时候,让玩家2取代电脑的位置就可以实现,甚至,在掌握网络有关知识后,还可以实现,两个人联网对弈,这都是基于我们目前实现的这个简易的三子棋框架的。

五、完整代码

play.h

#ifndef _PLAY_H
#define _PLAY_H

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

#define ROW 3
#define COL 3

void menu();//菜单
void RunGame();
void InitBoard(char board[ROW][COL]);//初始化棋盘
void ShowBoard(char board[ROW][COL]);//展示棋盘
void Player(char board[ROW][COL]);//玩家下棋
void Computer(char board[ROW][COL]);//电脑下棋
char IsWin(char board[ROW][COL]);//判断胜负
int IsFull(char board[ROW][COL]);


#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

play.c


#define _CRT_SECURE_NO_WARNINGS 1
#include"play.h"
void menu()
{
	printf("请开始游戏吧!\n");
	printf("---------------------------------\n");
	printf("-----1.开始--------0.退出--------\n");
	printf("---------------------------------\n");

}

void InitBoard(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			board[i][j] = ' ';

		}
	}
}

void ShowBoard(char board[ROW][COL])
{
	for (int i = 0; i < ROW * 2 + 1; i++)
	{
		if (i % 2 == 0)
		{
			for (int j = 0; j < COL; j++)
			{
				printf("----");
			}
			printf("\n");
		}
		else
		{
			for (int j = 0; j < COL * 2 + 1; j++)
			{
				if (j % 2 == 0)
				{
					printf("|");
				}
				else
				{
					printf(" %c ", board[i / 2][j / 2]);
				}
			}
			printf("\n");
		}
	}

}

void Player(char board[ROW][COL])
{
	while (1)
	{
		printf("请输入你的座标:");
		int x, y;
		scanf("%d,%d", &x, &y);
		if (x>=1 && x<=ROW && y>=1 && y<=COL)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = 'X';
				break;
			}
			else
			{
				printf("该位置已经有棋子!\n");
			}
		}
		else
		{
			printf("输入错误。\n");
		}
	}
}

void Computer(char board[ROW][COL])
{
	while (1)
	{
		int x = rand() % ROW;
		int y = rand() % COL;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'O';
			break;
		}
	}
}

char IsWin(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	for (int j = 0; j < COL; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
		{
			 return board[0][j];
		}
	}
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[0][0];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
	{
		return board[0][2];
	}
	if (IsFull == 0)
	{
		return 'Q';
	}
	return 'n';
}

int IsFull(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')
			{
				return 1;
			}
		}
	}
	return 0;
}

void RunGame()
{
	char board[ROW][COL];
	InitBoard(board);
	ShowBoard(board);
	char temp;
	while (1)
	{
		Player(board);
		ShowBoard(board);
		temp = IsWin(board);
		if (temp != 'n')
		{
			break;
		}
		Computer(board);
		ShowBoard(board);
		temp = IsWin(board);
		if (temp != 'n')
		{
			break;
		}
	}
	if (temp == 'X'){
		printf("玩家胜利!\n");
	}
	else if (temp == 'O')
	{
		printf("电脑胜利!\n");
	}
	else if (temp == 'Q')
	{
		printf("平局!\n");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176

test.c


#define _CRT_SECURE_NO_WARNINGS 1

#include"play.h"

int main()
{
	
	/*
	1.初始化棋盘
	2、玩家落子
	2.1 展示棋盘
	2.2 判断胜负
	3、电脑落子
	3.1 展示棋盘
	3.2 判断胜负
	*/
	
	srand((unsigned)time(NULL));
	int input;
	do{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			RunGame();
			break;
		case 0:
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/135229
推荐阅读
相关标签
  

闽ICP备14008679号