C语言小项目——井字棋游戏(升级版)

前言

Wassup guys

今天是我们的C语言小项目「井字棋升级版」

Let’s get it!
C语言小项目——井字棋游戏(升级版)_第1张图片


文章目录

  • 游戏说明
  • 游戏效果展示
  • 游戏代码
  • 游戏代码详解
    • 游戏菜单构建
    • 主函数构建
    • 三子棋的过程
      • 构建棋盘数组
      • 初始化棋盘数组
      • 打印棋盘
      • 玩家下棋
      • 电脑下棋
      • AI电脑算法升级
      • 判断输赢
      • 主函数判断输赢
    • AI算法思路二


游戏说明

井字棋,又名三子棋,英文名叫Tic-Tac-Toe;
 
是一种在3x3格子上进行的连珠游戏,和五子棋类似,格线排成井字故得名。
 
游戏需要的工具仅为纸和笔,然后由分别代表O和X的两个游戏者轮流在格子里留下标记(一般来说先手者为X),任意三个标记形成一条直线,则为获胜。
 
其游戏过程就像下面这样
C语言小项目——井字棋游戏(升级版)_第2张图片

游戏效果展示

游戏运行结果如下

C语言小项目——井字棋游戏(升级版)_第3张图片

游戏代码

我在函数这一篇总结中,讲过函数的声明和定义,所有这里我用三个文件来实现这个三子棋游戏
 
友友们可以将以下代码复制到自己的编译器当中运行:

game.h

游戏函数的声明

#pragma once
//游戏函数的声明
#include 
#include 
#include 

//棋盘的行数
#define ROW 3 

//棋盘的列数
#define COL 3


//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

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

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);

//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);

//电脑检查自己是否会赢
int CheckComputer(char board[ROW][COL], int row, int col);

//电脑检查玩家是否会赢
int CheckPlayer(char board[ROW][COL], int row, int col, int k);

//判断输赢函数
char Is_Win(char board[ROW][COL], int row, int col);
/*
1.玩家赢 ---> 返回'*'
2.电脑赢 ---> 返回'#'
3. 平局  ---> 返回'P'
4. 继续  ---> 返回'C'
*/

game.c

游戏函数的实现

#define _CRT_SECURE_NO_WARNINGS 1
//游戏函数的实现

#include "game.h"

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

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	//row = 3行
	//col = 3列
	for (i = 0; i < row; i++)
	{
		if (i == 0)
		{
			printf("┌----┬----┬----┐\n");
		}
		if ((i == 1) || (i == 2))
		{
			printf("├----┼----┼----┤\n");
		}
		for (int j = 0; j < col; j++)
		{
			printf("│  %c ", board[i][j]);
		}
		printf("│\n");
	}
	printf("└----┴----┴----┘\n");
}


void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("玩家下棋,请输入坐标:> ");
		scanf("%d %d", &x, &y);
		printf("你下棋的位置↓\n");
		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 ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	//z是判断变量
	int z = 0;

	z = CheckComputer(board, row, col);

	printf("电脑下棋的位置↓\n");
	while (0 == z)
	{
		x = rand() % row;
		y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = 'o';
			break;
		}
	}
}
//电脑检查自己能不能赢
int CheckComputer(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;

	//k用于返回值
	int k = 0;
	while (0 == k)
	{
		//电脑判断自己在三行上是否会赢
		for (i = 0; i < row; i++)
		{
			//如果第一个格子和第二个格子都是电脑的棋子,并且第三个格子是空的
			//那么电脑直接落子
			if (board[i][0] == board[i][1] && (board[i][0] == 'o' || board[i][1] == 'o') && board[i][2] == ' ')
			{
				board[i][2] = 'o';
				k = 1;
				break;
			}
			if (board[i][0] == board[i][2] && (board[i][0] == 'o' || board[i][2] == 'o') && board[i][1] == ' ')
			{
				board[i][1] = 'o';
				k = 1;
				break;
			}

			if (board[i][1] == board[i][2] && (board[i][1] == 'o' || board[i][2] == 'o') && board[i][0] == ' ')
			{
				board[i][0] = 'o';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//电脑判断自己在三列上是否会赢
		for (j = 0; j < col; j++)
		{
			if (board[0][j] == board[1][j] && (board[0][j] == 'o' || board[1][j] == 'o') && board[2][j] == ' ')
			{
				board[2][j] = 'o';
				k = 1;
				break;
			}

			if (board[0][j] == board[2][j] && (board[0][j] == 'o' || board[2][j] == 'o') && board[1][j] == ' ')
			{
				board[1][j] = 'o';
				k = 1;
				break;
			}
			if (board[1][j] == board[2][j] && (board[1][j] == 'o' || board[2][j] == 'o') && board[0][j] == ' ')
			{
				board[0][j] = 'o';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//使用while循环判断对角线是否会赢
		while (0 == k)
		{
			//左边对角线
			if (board[0][0] == board[1][1] && (board[0][0] == 'o' || board[1][1] == 'o') && board[2][2] == ' ')
			{
				board[2][2] = 'o';
				k = 1;
				break;
			}

			if (board[0][0] == board[2][2] && (board[0][0] == 'o' || board[2][2] == 'o') && board[1][1] == ' ')
			{
				board[1][1] = 'o';
				k = 1;
				break;
			}

			if (board[1][1] == board[2][2] && (board[1][1] == 'o' || board[2][2] == 'o') && board[0][0] == ' ')
			{
				board[0][0] = 'o';
				k = 1;
				break;
			}

			//右边对角线
			if (board[0][2] == board[1][1] && (board[0][2] == 'o' || board[1][1] == 'o') && board[2][0] == ' ')
			{
				board[2][0] = 'o';
				k = 1;
				break;
			}

			if (board[0][2] == board[2][0] && (board[0][2] == 'o' || board[2][0] == 'o') && board[1][1] == ' ')
			{
				board[1][1] = 'o';
				k = 1;
				break;
			}
			if (board[1][1] == board[2][0] && (board[1][1] == 'o' || board[2][0] == 'o') && board[0][2] == ' ')
			{
				board[0][2] = 'o';
				k = 1;
				break;
			}
			break;
		}

		k = CheckPlayer(board, row, col, k);

		return k;
	}

}
//检查玩家是否会赢
int CheckPlayer(char board[ROW][COL], int row, int col, int k)
{
	int i = 0;
	int j = 0;
	while (0 == k)
	{
		//判断玩家在三行上是否会赢
		for (i = 0; i < row; i++)
		{
			if (board[i][0] == board[i][1] && (board[i][0] == 'x' || board[i][1] == 'x') && board[i][2] == ' ')
			{
				board[i][2] = 'o';
				k = 1;
				break;
			}
			if (board[i][0] == board[i][2] && (board[i][0] == 'x' || board[i][2] == 'x') && board[i][1] == ' ')
			{
				board[i][1] = 'o';
				k = 1;
				break;
			}
			if (board[i][2] == board[i][1] && (board[i][2] == 'x' || board[i][1] == 'x') && board[i][0] == ' ')
			{
				board[i][0] = 'o';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//判断玩家在三列上是否会赢
		for (j = 0; j < col; j++)
		{
			if (board[0][j] == board[1][j] && (board[1][j] == 'x' || board[0][j] == 'x') && board[2][j] == ' ')
			{
				board[2][j] = 'o';
				k = 1;
				break;
			}

			if (board[0][j] == board[2][j] && (board[2][j] == 'x' || board[0][j] == 'x') && board[1][j] == ' ')
			{
				board[1][j] = 'o';
				k = 1;
				break;
			}

			if (board[1][j] == board[2][j] && (board[2][j] == 'x' || board[1][j] == 'x') && board[0][j] == ' ')
			{
				board[0][j] = 'o';
				k = 1;
				break;
			}
		}
		break;
	}

	//判断玩家在对角线上是否会赢
	while (0 == k)
	{
		//左边对角线
		if (board[0][0] == board[1][1] && (board[1][1] == 'x' || board[0][0] == 'x') && board[2][2] == ' ')
		{
			board[2][2] = 'o';
			k = 1;
			break;
		}
		if (board[0][0] == board[2][2] && (board[2][2] == 'x' || board[0][0] == 'x') && board[1][1] == ' ')
		{
			board[1][1] = 'o';
			k = 1;
			break;
		}
		if (board[1][1] == board[2][2] && (board[1][1] == 'x' || board[2][2] == 'x') && board[0][0] == ' ')
		{
			board[0][0] = 'o';
			k = 1;
			break;
		}

		//右边对角线
		if (board[0][2] == board[1][1] && (board[0][2] == 'x' || board[1][1] == 'x') && board[2][0] == ' ')
		{
			board[2][0] = 'o';
			k = 1;
			break;
		}

		if (board[0][2] == board[2][0] && (board[2][0] == 'x' || board[0][2] == 'x') && board[1][1] == ' ')
		{
			board[1][1] = 'o';
			k = 1;
			break;
		}

		if (board[1][1] == board[2][0] && (board[2][0] == 'x' || board[1][1] == 'x') && board[0][2] == ' ')
		{
			board[0][2] = 'o';
			k = 1;
			break;
		}
		break;
	}
	return k;
}

int is_full(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++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

char Is_Win(char board[ROW][COL], int row, int col)
{
	//1.判断输赢
	int i = 0;

	//三行判断
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
		{
			return board[i][1];
		}
	}

	//三列判断
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ')
		{
			return board[1][i];
		}
	}

	//对角线判断
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
	{
		return board[1][1];
	}

	//2.判断平局
	if (1 == is_full(board, row, col))
	{
		return 'P';
	}

	//3.游戏继续
	return 'C';
}

test.c

测试游戏

#define _CRT_SECURE_NO_WARNINGS 1
//测试游戏
#include "game.h"

void menu()
{
	printf("\n");
	printf("********三子棋小游戏********\n");
	printf("****************************\n");
	printf("*********  1.Play  *********\n");
	printf("*********  0.Exit  *********\n");
	printf("****************************\n");
}

void game()
{
	//定义一个棋盘数组
	char board[ROW][COL];

	//初始化棋盘
	InitBoard(board, ROW, COL);

	//打印棋盘
	DisplayBoard(board, ROW, COL);

	char ret = 0;
	while (1)
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = Is_Win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = Is_Win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}

	if (ret == 'x')
	{
		printf("恭喜你,赢得了游戏胜利\n");
	}
	else if (ret == 'o')
	{
		printf("不是吧,你连电脑都赢不了\n");
	}
	else
	{
		printf("居然和电脑打成了平手,你不行啊!\n");
	}
}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
		}
	} while (input);
	return 0;
}

游戏代码详解

游戏菜单构建

打印一个游戏菜单,有两种选择
 
1、玩游戏
 
0、退出游戏

代码实现

void menu()
{
	printf("\n");
	printf("********三子棋小游戏********\n");
	printf("****************************\n");
	printf("*********  1.Play  *********\n");
	printf("*********  0.Exit  *********\n");
	printf("****************************\n");
}

C语言小项目——井字棋游戏(升级版)_第4张图片

主函数构建

当运行这个游戏的时候,会首先生成一个菜单,让我们选择;
 
但是当我玩完一局过后,还想再玩,所以会用到循环

代码实现

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
		}
	} while (input);
	return 0;
}

C语言小项目——井字棋游戏(升级版)_第5张图片

三子棋的过程

game()函数,去实现三子棋的过程

构建棋盘数组

三子棋,顾名思义,我们要定义一个3x3的二维数组棋盘

void game()
{
	//定义一个棋盘数组
	char board[ROW][COL];
}

初始化棋盘数组

当棋盘数组定义好以后,我们就要初始化棋盘
 
可以看到,这个棋盘里面最开始都是空的,所以我们把棋盘定义成空的字符
C语言小项目——井字棋游戏(升级版)_第6张图片

首先我们要在 game.h头文件 里面去声明这个初始化函数

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);

然后再去 game.c游戏函数 里面实现

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

C语言小项目——井字棋游戏(升级版)_第7张图片

注意:后面的函数也是一样,要先去头文件里面声明,再到游戏函数里面去实现

打印棋盘

我们初始化好棋盘以后,要在屏幕上打印一下

C语言小项目——井字棋游戏(升级版)_第8张图片
聪明的朋友能猜到这个棋盘是怎么想出来的吗

代码实现

void DisplayBoard(char board[ROW][COL], int row, int col)
{

	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		if (i == 0)
		{
			printf("┌----┬----┬----┐\n");
		}
		if ((i == 1) || (i == 2))
		{
			printf("├----┼----┼----┤\n");
		}
		for (int j = 0; j < col; j++)
		{
			printf("│  %c ", board[i][j]);
		}
		printf("│\n");
	}
	printf("└----┴----┴----┘\n");
}

C语言小项目——井字棋游戏(升级版)_第9张图片

玩家下棋

接下来就是玩家开始下棋;
 
由于是个二维数组,并且数组下标是从0开始的,但是玩家可能不是程序员,所以不知道数组的下标;
 
所以这里把下标定义从1开始;
 
并且当玩家在棋盘落子以后,我们把棋盘打印出来。

代码实现

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("玩家下棋,请输入坐标:> ");
		scanf("%d %d", &x, &y);
		printf("你下棋的位置↓\n");
		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");
		}
	}
}

C语言小项目——井字棋游戏(升级版)_第10张图片

电脑下棋

电脑下棋的方法我们选择用时间戳来生成一个随机数,并把随机数的范围控制在0~3以内;
 
关于随机数的生成,这里简单说一下
C语言小项目——井字棋游戏(升级版)_第11张图片

代码实现

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋的位置↓\n");
	
	while (1)
	{
		x = rand() % row;
		y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = 'o';
			break;
		}
	}

}

注意,我们这里调用rand函数,必须在主函数里面设置一下srand函数
C语言小项目——井字棋游戏(升级版)_第12张图片
C语言小项目——井字棋游戏(升级版)_第13张图片

AI电脑算法升级

如果你运行了这个程序以后,会发现电脑的走法貌似不太聪明的样子;
 
因为我们用随机数生成,会造成玩家赢的很轻松;
 
那如果电脑有了AI的思想呢?它就知道如何进攻、防守。
 
那么来思考一下,你是怎么下三子棋的?
 
1、如果我们在三行、三列、对角线上,下了两个棋子后,我们会继续下第三个;
 
2、如果对方在三行、三列、对角线上有了相同两个棋子后,我们会堵截,不让它下第三个;
 
此时我们就有了思路
 
电脑需要的就是在:出现两个相同的棋子的地方下自己的棋子就好了
 

代码一

ComputerMove函数进行修改;
 
电脑下棋需要加一步,获取返回值,如果返回值为0,那么电脑只有乖乖的按照伪随机数来走啦

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;

	//z返回值
	int z = 0;

	z = CheckComputer(board, row, col);

	printf("电脑下棋的位置↓\n");
	while (0 == z)
	{
		x = rand() % row;
		y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = 'o';
			break;
		}
	}
}

代码二

定义一个函数:CheckComputer,作用是电脑检查自己是否会赢;
 
1、电脑在坐标(1,1)和坐标(1,2)的格子进行判断,如果(1,1)或者(1,2)的格子都为自己的棋子,那么直接在(1,3)的格子落子
C语言小项目——井字棋游戏(升级版)_第14张图片
2、电脑在坐标(1,1)和坐标(1,3)的格子进行判断,如果(1,1)或者(1,3)的格子都为自己的棋子,那么直接在坐标(1,2)的格子落子
C语言小项目——井字棋游戏(升级版)_第15张图片
3、电脑在坐标(1,2)和坐标(1,3)的格子进行判断,如果(1,2)或者(1,3)的格子都为自己的棋子,那么直接在坐标(1,1)的格子落子
C语言小项目——井字棋游戏(升级版)_第16张图片
此时我们只需要定义一个循环,三行都进行判断即可,同理,三列和对角线也是一样

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

	//k用于返回值
	int k = 0;
	while (0 == k)
	{
		//电脑判断自己在三行上是否会赢
		for (i = 0; i < row; i++)
		{
			//如果第一个格子和第二个格子都是电脑的棋子,并且第三个格子是空的
			//那么电脑直接落子
			if (board[i][0] == board[i][1] && (board[i][0] == 'o' || board[i][1] == 'o') && board[i][2] == ' ')
			{
				board[i][2] = 'o';
				k = 1;
				break;
			}
			if (board[i][0] == board[i][2] && (board[i][0] == 'o' || board[i][2] == 'o') && board[i][1] == ' ')
			{
				board[i][1] = 'o';
				k = 1;
				break;
			}

			if (board[i][1] == board[i][2] && (board[i][1] == 'o' || board[i][2] == 'o') && board[i][0] == ' ')
			{
				board[i][0] = 'o';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//电脑判断自己在三列上是否会赢
		for (j = 0; j < col; j++)
		{
			if (board[0][j] == board[1][j] && (board[0][j] == 'o' || board[1][j] == 'o') && board[2][j] == ' ')
			{
				board[2][j] = 'o';
				k = 1;
				break;
			}

			if (board[0][j] == board[2][j] && (board[0][j] == 'o' || board[2][j] == 'o') && board[1][j] == ' ')
			{
				board[1][j] = 'o';
				k = 1;
				break;
			}
			if (board[1][j] == board[2][j] && (board[1][j] == 'o' || board[2][j] == 'o') && board[0][j] == ' ')
			{
				board[0][j] = 'o';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//使用while循环判断对角线是否会赢
		while (0 == k)
		{
			//左边对角线
			if (board[0][0] == board[1][1] && (board[0][0] == 'o' || board[1][1] == 'o') && board[2][2] == ' ')
			{
				board[2][2] = 'o';
				k = 1;
				break;
			}

			if (board[0][0] == board[2][2] && (board[0][0] == 'o' || board[2][2] == 'o') && board[1][1] == ' ')
			{
				board[1][1] = 'o';
				k = 1;
				break;
			}

			if (board[1][1] == board[2][2] && (board[1][1] == 'o' || board[2][2] == 'o') && board[0][0] == ' ')
			{
				board[0][0] = 'o';
				k = 1;
				break;
			}

			//右边对角线
			if (board[0][2] == board[1][1] && (board[0][2] == 'o' || board[1][1] == 'o') && board[2][0] == ' ')
			{
				board[2][0] = 'o';
				k = 1;
				break;
			}

			if (board[0][2] == board[2][0] && (board[0][2] == 'o' || board[2][0] == 'o') && board[1][1] == ' ')
			{
				board[1][1] = 'o';
				k = 1;
				break;
			}
			if (board[1][1] == board[2][0] && (board[1][1] == 'o' || board[2][0] == 'o') && board[0][2] == ' ')
			{
				board[0][2] = 'o';
				k = 1;
				break;
			}
			break;
		}

		k = CheckPlayer(board, row, col, k);

		return k;
	}

}

三行
C语言小项目——井字棋游戏(升级版)_第17张图片
三列
C语言小项目——井字棋游戏(升级版)_第18张图片
左对角线
C语言小项目——井字棋游戏(升级版)_第19张图片
右对角线
C语言小项目——井字棋游戏(升级版)_第20张图片
完整代码

代码二

定义一个函数:CheckPlayer,作用是电脑检查玩家是否会赢;
 
如果玩家在三行、三列、对角线上有了相同两个棋子后,电脑会堵截,不让玩家下第三个
 
1、电脑在坐标(1,1)和坐标(1,2)的格子进行判断,如果(1,1)或者(1,2)的格子都为玩家的棋子,那么直接在(1,3)的格子落子堵截
C语言小项目——井字棋游戏(升级版)_第21张图片
2、电脑在坐标(1,1)和坐标(1,3)的格子进行判断,如果(1,1)或者(1,3)的格子都为玩家的棋子,那么直接在(1,2)的格子落子堵截
C语言小项目——井字棋游戏(升级版)_第22张图片
3、电脑在坐标(1,2)和坐标(1,3)的格子进行判断,如果(1,2)或者(1,3)的格子都为玩家的棋子,那么直接在(1,1)的格子落子堵截
C语言小项目——井字棋游戏(升级版)_第23张图片
此时我们只需要定义一个循环,三行都进行判断即可,同理,三列和对角线也是一样

int CheckPlayer(char board[ROW][COL], int row, int col, int k)
{
	int i = 0;
	int j = 0;
	while (0 == k)
	{
		//判断玩家在三行上是否会赢
		for (i = 0; i < row; i++)
		{
			if (board[i][0] == board[i][1] && (board[i][0] == 'x' || board[i][1] == 'x') && board[i][2] == ' ')
			{
				board[i][2] = 'o';
				k = 1;
				break;
			}
			if (board[i][0] == board[i][2] && (board[i][0] == 'x' || board[i][2] == 'x') && board[i][1] == ' ')
			{
				board[i][1] = 'o';
				k = 1;
				break;
			}
			if (board[i][2] == board[i][1] && (board[i][2] == 'x' || board[i][1] == 'x') && board[i][0] == ' ')
			{
				board[i][0] = 'o';
				k = 1;
				break;
			}
		}
		if (k != 0)
			break;

		//判断玩家在三列上是否会赢
		for (j = 0; j < col; j++)
		{
			if (board[0][j] == board[1][j] && (board[1][j] == 'x' || board[0][j] == 'x') && board[2][j] == ' ')
			{
				board[2][j] = 'o';
				k = 1;
				break;
			}

			if (board[0][j] == board[2][j] && (board[2][j] == 'x' || board[0][j] == 'x') && board[1][j] == ' ')
			{
				board[1][j] = 'o';
				k = 1;
				break;
			}

			if (board[1][j] == board[2][j] && (board[2][j] == 'x' || board[1][j] == 'x') && board[0][j] == ' ')
			{
				board[0][j] = 'o';
				k = 1;
				break;
			}
		}
		break;
	}

	//判断玩家在对角线上是否会赢
	while (0 == k)
	{
		//左边对角线
		if (board[0][0] == board[1][1] && (board[1][1] == 'x' || board[0][0] == 'x') && board[2][2] == ' ')
		{
			board[2][2] = 'o';
			k = 1;
			break;
		}
		if (board[0][0] == board[2][2] && (board[2][2] == 'x' || board[0][0] == 'x') && board[1][1] == ' ')
		{
			board[1][1] = 'o';
			k = 1;
			break;
		}
		if (board[1][1] == board[2][2] && (board[1][1] == 'x' || board[2][2] == 'x') && board[0][0] == ' ')
		{
			board[0][0] = 'o';
			k = 1;
			break;
		}

		//右边对角线
		if (board[0][2] == board[1][1] && (board[0][2] == 'x' || board[1][1] == 'x') && board[2][0] == ' ')
		{
			board[2][0] = 'o';
			k = 1;
			break;
		}

		if (board[0][2] == board[2][0] && (board[2][0] == 'x' || board[0][2] == 'x') && board[1][1] == ' ')
		{
			board[1][1] = 'o';
			k = 1;
			break;
		}

		if (board[1][1] == board[2][0] && (board[2][0] == 'x' || board[1][1] == 'x') && board[0][2] == ' ')
		{
			board[0][2] = 'o';
			k = 1;
			break;
		}
		break;
	}
	return k;
}

三行
C语言小项目——井字棋游戏(升级版)_第24张图片
三列
C语言小项目——井字棋游戏(升级版)_第25张图片
左对角线
C语言小项目——井字棋游戏(升级版)_第26张图片
右对角线
C语言小项目——井字棋游戏(升级版)_第27张图片
完整代码
C语言小项目——井字棋游戏(升级版)_第28张图片

判断输赢

当玩家或者电脑每下一步棋的时候,我们就定义一个函数,判断一下谁赢了;
 
当棋盘已经落满棋子的时候,并且谁也没有赢,我们就认定为平局;
 
当棋盘还没有落满棋子的时候,并且谁也没有赢,游戏就继续;
 
1、玩家赢 —> 返回x
 
2、电脑赢 —> 返回o
 
3、平局 —> 返回P
 
4、继续 —> 返回C
 
那么怎么来判断输赢呢?
 
其实很简单,你看这个棋盘
C语言小项目——井字棋游戏(升级版)_第29张图片
当三行或者三列,以及两条对角线都落满相同的棋子时,那就就赢了;

举个例子

如图,当第一行全部落满相同的棋子
C语言小项目——井字棋游戏(升级版)_第30张图片

那么我们就返回中间红色这个位置的棋盘,并拿去判断;
C语言小项目——井字棋游戏(升级版)_第31张图片
如果是x号,那么就是玩家胜利;
 
如果是o号,那么就是电脑胜利

代码示例

int is_full(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++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}
	return 1;
}

char Is_Win(char board[ROW][COL], int row, int col)
{
	//1.判断输赢
	int i = 0;

	//三行判断
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
		{
			return board[i][1];
		}
	}

	//三列判断
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ')
		{
			return board[1][i];
		}
	}

	//对角线判断
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
	{
		return board[1][1];
	}

	//2.判断平局
	if (1 == is_full(board, row, col))
	{
		return 'P';
	}

	//3.游戏继续
	return 'C';
}

三行判断
C语言小项目——井字棋游戏(升级版)_第32张图片
三列判断
C语言小项目——井字棋游戏(升级版)_第33张图片
对角线判断
C语言小项目——井字棋游戏(升级版)_第34张图片
判断平局
C语言小项目——井字棋游戏(升级版)_第35张图片
游戏继续
C语言小项目——井字棋游戏(升级版)_第36张图片

主函数判断输赢

玩家和电脑下棋的思路可以这样理解
 
当玩家每下一步棋时,就进行输赢判断;
 
当电脑每下一步棋时,也进行输赢判断;
 
但是玩家走一步,电脑走一步,必须要拿循环来实现;
 
那么跳出循环的条件是什么呢?
 
上面已经分析了判断输赢的四种情况;
 
如果当棋盘已经全部落子了,就表示游戏不能再继续了,所以就可以跳出循环;
 
跳出来以后,就把上面判断输赢函数返回的结果来进行比较;
 
如果是x,表示玩家赢了;
 
如果是o,表示电脑赢了;
 
如果是P,表示平局了。

void game()
{
	char ret = 0;
	while (1)
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = Is_Win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = Is_Win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}

	if (ret == 'x')
	{
		printf("恭喜你,赢得了游戏胜利\n");
	}
	else if (ret == 'o')
	{
		printf("不是吧,你连电脑都赢不了\n");
	}
	else
	{
		printf("居然和电脑打成了平手,你不行啊!\n");
	}
}

循环走棋
C语言小项目——井字棋游戏(升级版)_第37张图片
判断
C语言小项目——井字棋游戏(升级版)_第38张图片

AI算法思路二

其实上面的AI算法虽然说可以让电脑进行攻击和截堵,但还是有点缺陷,所以再给大家介绍一种。
 
这个算法很精妙,可以让玩家永远赢不了电脑,最多也就只能打成平局
 
首先第一步始终让电脑先落子,并且电脑落子的地方永远在坐标为(1,1)的格子里面
 
(注:#表示电脑,*表示玩家)
C语言小项目——井字棋游戏(升级版)_第39张图片
此时玩家落子就有剩余的8个格子可以选择
 
那么我们就遍历每一个格子,并推演出让电脑赢的方法(这里给大家演示一种)
 
第一步:首先电脑在坐标(1,1)落子;
 
第二步:玩家在坐标(1,2)落子;
C语言小项目——井字棋游戏(升级版)_第40张图片
第三步:电脑在坐标(3,1)落子;
 
第四步:玩家在坐标(2,1)落子;
C语言小项目——井字棋游戏(升级版)_第41张图片
第五步:电脑在坐标(3,3)落子;
C语言小项目——井字棋游戏(升级版)_第42张图片
第六步:此时玩家不管在棋盘剩余空格的任意位置落子,都已经输了
C语言小项目——井字棋游戏(升级版)_第43张图片
是不是很妙?其实五子棋的AI算法也类似,到时候会出一篇结合EasyX的图形化五子棋

结语

编程这件事情,你如果不练习,你只是学了语法而已,那是绝对不会写代码的

我是Edison,你知道的越多,你不知道越多,我们下期见!

你可能感兴趣的:(C的游戏世界,c语言,游戏,开发语言,c++)