C语言三子棋(井字棋)与电脑难度提升


大家好,这一篇博客讲的是我对 C语言三字棋 的理解。以下代码使用的是 VS2022环境下的 C语言,如果有错误,还请读者大大们指出。

开始之前准备好 game.h、game.c、test.c文件。

三子棋基础内容实现

界面代码

在game.h中:

#pragma once

#define _CRT_SECURE_NO_WARNINGS

#define ROW 3 // 方便扩展
#define COL 3

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

在test.c中:

#include "game.h"

void menu()
{
	printf("*************************\n");
	printf("**** 1.play   0.exit ****\n");
	printf("*************************\n");
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL)); // 为后续的电脑随机下棋准备
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			//game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误!\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

初始化与棋盘打印代码

在game.h中:

void initBoard(char board[ROW][COL], int row, int col);

void display(char board[ROW][COL], int row, int col);

在game.c中:

#include "game.h"

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

void display(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1) // 打印纵分割线
				printf("|");
		}
		printf("\n");
		if (i < row - 1) // 打印横分割线
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}

玩家操作与游戏框架代码

在game.h中:

void playerMove(char board[ROW][COL], int row, int col);

在game.c中:

void playerMove(char board[ROW][COL], int row, int col)
{
	int y, x;
	printf("玩家下棋:>\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &y, &x);
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3 && board[y - 1][x - 1] == ' ') // 限定范围
		{
			board[y - 1][x - 1] = '*'; // 这里要对应数组下标
			break;
		}
		else
			printf("坐标非法或被占用,请重新输入\n");
	}
}

在test.c中:

void game()
{
	char board[ROW][COL] = { 0 };
	initBoard(board, ROW, COL);
	display(board, ROW, COL);
}

电脑操作代码

在game.h中:

void computerMove(char board[ROW][COL], int row, int col);

在game.c中:

void computerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int y, x;
	while (1) // 这里使用了随机下棋
	{
		y = rand() % row;
		x = rand() % col;
		if (board[y][x] == ' ') // 是空格才下
		{
			board[y][x] = '#';
			break;
		}
	}
}

游戏输、赢、平局判定

在game.h中:

char IsWin(char board[ROW][COL], int row, int col);

在game.c中:

int IsFull(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 1;
}

char IsWin(char board[ROW][COL], int row, int col)
{// 检测行、列、对角线棋子情况
	for (int 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 (int j = 0; j < col; j++) // 列
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
			return board[1][j];// 若当列三子相同,返回其棋子

	// 对角线
	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[1][1] != ' ')
		return board[1][1];

	if (IsFull(board, row, col))
		return 'Q'; // 字符Q表示棋盘满了
	return 'C'; // 字符C表示下棋正常进行
}

在test.c中:

void game()
{
	char board[ROW][COL] = { 0 };
	char ret = 0; // 接收棋盘下棋的信息
	initBoard(board, ROW, COL);
	display(board, ROW, COL);
	while (1) // 使用循环
	{
		playerMove(board, ROW, COL);
		ret = IsWin(board, ROW, COL); // 接收信息
		display(board, ROW, COL);
		if (ret != 'C') // 若不为正常情况,跳出循环
			break;
		computerMove(board, ROW, COL);
		ret = IsWin(board, ROW, COL);
		display(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*') // 跳出循环时判断下棋之后的情况
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else if (ret == 'Q')
		printf("平局\n");
}

整体源代码

在game.h中:

#pragma once

#define _CRT_SECURE_NO_WARNINGS

#define ROW 3 // 方便扩展
#define COL 3

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

void initBoard(char board[ROW][COL], int row, int col);

void display(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);

char IsWin(char board[ROW][COL], int row, int col);

在game.c中:

#include "game.h"

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

void display(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1) // 打印纵分割线
				printf("|");
		}
		printf("\n");
		if (i < row - 1) // 打印横分割线
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}

void computerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int y, x;
	while (1) // 这里使用了随机下棋
	{
		y = rand() % row;
		x = rand() % col;
		if (board[y][x] == ' ') // 是空格才下
		{
			board[y][x] = '#';
			break;
		}
	}
}

void playerMove(char board[ROW][COL], int row, int col)
{
	int y, x;
	printf("玩家下棋:>\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &y, &x);
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3 && board[y - 1][x - 1] == ' ') // 限定范围
		{
			board[y - 1][x - 1] = '*'; // 这里要对应数组下标
			break;
		}
		else
			printf("坐标非法或被占用,请重新输入\n");
	}
}

int IsFull(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 1;
}

char IsWin(char board[ROW][COL], int row, int col)
{// 检测行、列、对角线棋子情况
	for (int 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 (int j = 0; j < col; j++) // 列
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
			return board[1][j];// 若当列三子相同,返回其棋子

	// 对角线
	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[1][1] != ' ')
		return board[1][1];

	if (IsFull(board, row, col))
		return 'Q'; // 字符Q表示棋盘满了
	return 'C'; // 字符C表示下棋正常进行
}

在test.c中:

#include "game.h"

void menu()
{
	printf("*************************\n");
	printf("**** 1.play   0.exit ****\n");
	printf("*************************\n");
}

void game()
{
	char board[ROW][COL] = { 0 };
	char ret = 0; // 接收棋盘下棋的信息
	initBoard(board, ROW, COL);
	display(board, ROW, COL);
	while (1) // 使用循环
	{
		playerMove(board, ROW, COL);
		ret = IsWin(board, ROW, COL); // 接收信息
		display(board, ROW, COL);
		if (ret != 'C') // 若不为正常情况,跳出循环
			break;
		computerMove(board, ROW, COL);
		ret = IsWin(board, ROW, COL);
		display(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*') // 跳出循环时判断下棋之后的情况
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else if (ret == 'Q')
		printf("平局\n");
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL)); // 为后续的电脑随机下棋准备
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误!\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

以上是C语言三子棋的基础内容实现。

电脑难度提升

这里使用的是 暴力枚举 的方法,也就是使用循环加判断的方式将各种情况进行分析。
不过,在三子棋3×3对称且有下棋方法的棋盘中大部分枚举的情况是可以省略的

先创建 computerDifficult.c源文件

三子连接防御

三子连接防御:当玩家下出两个连接的棋子,再下一颗连接成三子就胜利,这里电脑需要阻拦玩家连接成三个棋子。
列的防御
行的防御
对角线的防御
绿色圆为玩家棋子,红色菱形为电脑棋子。这意味着我们需要将所有的行、列、对角线的每个位置进行判断
在computerDifficult.c中:

#include "game.h"

int difficult(char board[ROW][COL], char set1, char set2)
{
	for (int i = 0; i < 3; i++)
	{
		// 行的防御
		if (board[i][0] == board[i][1] && board[i][0] == set1 && board[i][2] == set2) // 行尾
		{
			board[i][2] = '#';
			return 1;
		}
		if (board[i][0] == board[i][2] && board[i][0] == set1 && board[i][1] == set2) // 行中
		{
			board[i][1] = '#';
			return 1;
		}
		if (board[i][1] == board[i][2] && board[i][1] == set1 && board[i][0] == set2) // 行头
		{
			board[i][0] = '#';
			return 1;
		}

		// 列的防御
		if (board[0][i] == board[1][i] && board[0][i] == set1 && board[2][i] == set2) // 列尾
		{
			board[2][i] = '#';
			return 1;
		}
		if (board[0][i] == board[2][i] && board[0][i] == set1 && board[1][i] == set2) // 列中
		{
			board[1][i] = '#';
			return 1;
		}
		if (board[1][i] == board[2][i] && board[1][i] == set1 && board[0][i] == set2) // 列头
		{
			board[0][i] = '#';
			return 1;
		}
	}

	// 对角线的防左斜
	if (board[0][0] == board[1][1] && board[1][1] == set1 && board[2][2] == set2) // 对角线左斜下
	{
		board[2][2] = '#';
		return 1;
	}
	if (board[0][0] == board[2][2] && board[0][0] == set1 && board[1][1] == set2) // 对角线左斜中
	{
		board[1][1] = '#';
		return 1;
	}
	if (board[1][1] == board[2][2] && board[1][1] == set1 && board[0][0] == set2) // 对角线左斜上
	{
		board[0][0] = '#';
		return 1;
	}

	// 对角线的防右斜
	if (board[0][2] == board[1][1] && board[1][1] == set1 && board[2][0] == set2) // 对角线右斜下
	{
		board[2][0] = '#';
		return 1;
	}
	if (board[0][2] == board[2][0] && board[0][2] == set1 && board[1][1] == set2) // 对角线右斜中
	{
		board[1][1] = '#';
		return 1;
	}
	if (board[1][1] == board[2][0] && board[1][1] == set1 && board[0][2] == set2) // 对角线右斜上
	{
		board[0][2] = '#';
		return 1;
	}
	return 0;
}

在game.h中:

int difficult(char board[ROW][COL], char set1, char set2);

在game.c中:

void computerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int y, x;

	if (difficult(board, '*', ' ')); // 当防御时返回1,否则返回0。
	else
	{
		while (1) // 这里使用了随机下棋
		{
			y = rand() % row;
			x = rand() % col;
			if (board[y][x] == ' ') // 是空格才下
			{
				board[y][x] = '#';
				break;
			}
		}
	}
}

三子连接攻击

三子连接攻击:当电脑下出两个连接的棋子,再下一颗连接成三子就胜利

这里只需要将 difficult函数 的参数更改一下。
三子连接攻击
不过要注意,三子连接攻击的优先度应高于三子连接防御,如:
优先度
若到电脑下棋,此时应下3行3列获得胜利。
在game.c中:

void computerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int y, x;
	//        三子连接攻击				 三子连接防御
	if (difficult(board, '#', ' ') || difficult(board, '*', ' ')); // 当防御时返回1,否则返回0。
	else
	{
		while (1) // 这里使用了随机下棋
		{
			y = rand() % row;
			x = rand() % col;
			if (board[y][x] == ' ') // 是空格才下
			{
				board[y][x] = '#';
				break;
			}
		}
	}
}

二子连接

二子连接:当电脑下出一个棋子时,对其进行连接。
通过观察将棋子下在不同位置,其优劣是不同的:
在这里插入图片描述
在中心有四条线可以连接成三子,在角有三条线可以连接成三子,在边有二条线可以连接成三子。
我们可以继续使用 difficult函数 ,不过需要更改函数中的一些代码 和 下棋位置的优先度
在computerDifficult.c中:

#include "game.h"

int difficult(char board[ROW][COL], char set1, char set2)
{
	// 对角线的防与攻左斜
	if (board[0][0] == board[1][1] && board[1][1] == set1 && board[2][2] == set2) // 对角线左斜下
	{
		board[0][0] == ' ' ? (board[0][0] = '#') : (board[2][2] = '#');
		return 1;
	}
	if (board[0][0] == board[2][2] && board[0][0] == set1 && board[1][1] == set2) // 对角线左斜中
	{
		board[0][0] == ' ' ? (board[0][0] = '#') : (board[1][1] = '#');
		return 1;
	}
	if (board[1][1] == board[2][2] && board[1][1] == set1 && board[0][0] == set2) // 对角线左斜上
	{
		board[2][2] == ' ' ? (board[2][2] = '#') : (board[0][0] = '#');
		return 1;
	}

	// 对角线的防与攻右斜
	if (board[0][2] == board[1][1] && board[1][1] == set1 && board[2][0] == set2) // 对角线右斜下
	{
		board[0][2] == ' ' ? (board[0][2] = '#') : (board[2][0] = '#');
		return 1;
	}
	if (board[0][2] == board[2][0] && board[0][2] == set1 && board[1][1] == set2) // 对角线右斜中
	{
		board[0][2] == ' ' ? (board[0][2] = '#') : (board[1][1] = '#');
		return 1;
	}
	if (board[1][1] == board[2][0] && board[1][1] == set1 && board[0][2] == set2) // 对角线右斜上
	{
		board[2][0] == ' ' ? (board[2][0] = '#') : (board[0][2] = '#');
		return 1;
	}

	for (int i = 0; i < 3; i++)
	{
		// 行的防御
		if (board[i][0] == board[i][1] && board[i][0] == set1 && board[i][2] == set2) // 行尾
		{
			board[i][0] == ' ' ? (board[i][0] = '#') : (board[i][2] = '#');
			return 1;
		}
		if (board[i][0] == board[i][2] && board[i][0] == set1 && board[i][1] == set2) // 行中
		{
			board[i][0] == ' ' ? (board[i][0] = '#') : (board[i][1] = '#');
			return 1;
		}
		if (board[i][1] == board[i][2] && board[i][1] == set1 && board[i][0] == set2) // 行头
		{
			board[i][2] == ' ' ? (board[i][2] = '#') : (board[i][0] = '#');
			return 1;
		}

		// 列的防御
		if (board[0][i] == board[1][i] && board[0][i] == set1 && board[2][i] == set2) // 列尾
		{
			board[0][i] == ' ' ? (board[0][i] = '#') : (board[2][i] = '#');
			return 1;
		}
		if (board[0][i] == board[2][i] && board[0][i] == set1 && board[1][i] == set2) // 列中
		{
			board[0][i] == ' ' ? (board[0][i] = '#') : (board[1][i] = '#');
			return 1;
		}
		if (board[1][i] == board[2][i] && board[1][i] == set1 && board[0][i] == set2) // 列头
		{
			board[2][i] == ' ' ? (board[2][i] = '#') : (board[0][i] = '#');
			return 1;
		}
	}
	return 0;
}

在game.c中:

void computerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int y, x;
	//        三子连接攻击				 三子连接防御					   二子连接
	if (difficult(board, '#', ' ') || difficult(board, '*', ' ') || difficult(board, ' ', '#')); // 当防御时返回1,否则返回0。
	else
	{
		while (1) // 这里使用了随机下棋
		{
			y = rand() % row;
			x = rand() % col;
			if (board[y][x] == ' ') // 是空格才下
			{
				board[y][x] = '#';
				break;
			}
		}
	}
}

当执行二子连接时,此代码优先考虑棋盘的四个角

开局处理

根据上一部分我们可知电脑下棋位置的优先度应为中心、角、边。
开局时若中心没有棋子,则电脑优先选择,反之选择四个角的其中一个
在computerDifficult.c中:

int first(char board[3][3])
{
	if (board[1][1] == ' ') // 开局占中心
	{
		board[1][1] = '#';
		return 1;
	}
	else// 开局占四角
	{
		int arr[2] = { 0, 2 };
		int i = arr[rand() % 2];
		int j = arr[rand() % 2];
		if (board[i][j] == ' ')
		{
			board[i][j] = '#';
			return 1;
		}
	}
	return 0;
}

在game.h中:

int first(char board[3][3]);

在game.c中:

void computerMove(char board[ROW][COL], int row, int col, int* firstMove)
{
	printf("电脑下棋\n");
	int y, x;
	//        三子连接攻击				 三子连接防御					   二子连接
	if (difficult(board, '#', ' ') || difficult(board, '*', ' ') || difficult(board, ' ', '#')); // 当防御时返回1,否则返回0。
	else if (*firstMove && first(board)) // 开局 
		*firstMove = 0;
	else
	{
		while (1) // 这里使用了随机下棋
		{
			y = rand() % row;
			x = rand() % col;
			if (board[y][x] == ' ') // 是空格才下
			{
				board[y][x] = '#';
				break;
			}
		}
	}
}

在test.c中:

void game()
{
	int firstMove = 1; // 判断是否是开局
	char board[ROW][COL] = { 0 };
	char ret = 0; // 接收棋盘下棋的信息
	initBoard(board, ROW, COL);
	display(board, ROW, COL);
	while (1) // 使用循环
	{
		playerMove(board, ROW, COL);
		ret = IsWin(board, ROW, COL); // 接收信息
		display(board, ROW, COL);
		if (ret != 'C') // 若不为正常情况,跳出循环
			break;
		computerMove(board, ROW, COL, &firstMove);
		ret = IsWin(board, ROW, COL);
		display(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*') // 跳出循环时判断下棋之后的情况
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else if (ret == 'Q')
		printf("平局\n");
}

穷举特殊情况

上面针对提升的电脑难度的代码,已经可以保证大部分情况电脑不会输给玩家
不过还有些情况没有覆盖到,这里我们可以穷举出来

同边情况

如图:
同边情况
当玩家先手且下出同边情况,由于电脑下棋的优先度导致 1-4 情况无法防御,所以我们需要单独列出。

边角情况

如图:
边角情况
当玩家先手且下出边角情况,由于电脑下棋的优先度并不能防御 2-1、2-2、2-3、2-4 这四种情况,所以我们需要单独列出。

对角情况

如图:
对角情况
当玩家先手且下出对角情况,电脑第二颗棋下在 角 本身就一定防御不住,所以我们需要单独列出。

代码实现

在computerDifficult.c中:

#include "game.h"

int defense(char board[ROW][COL])
{
	// 同边情况
	if (board[2][1] == '*' && board[1][2] == '*')
	{
		board[2][2] = '#';
		return 1;
	}
	// 边角情况
	if ((board[1][0] == '*' && board[2][2] == '*') || (board[2][1] == '*' && board[0][0] == '*'))
	{
		board[2][0] = '#';
		return 1;
	}
	if ((board[2][1] == '*' && board[0][2] == '*') || (board[1][2] == '*' && board[2][0] == '*'))
	{
		board[2][2] = '#';
		return 1;
	}
	// 对角情况
	if ((board[0][0] == '*' && board[2][2] == '*') || (board[0][2] == '*' && board[2][0] == '*'))
	{
		board[0][1] = '#';
		return 1;
	}
	return 0;
}

int difficult(char board[ROW][COL], char set1, char set2)
{
	if (set1 == ' ' && set2 == '#') // 当为二子连接时
		if (defense(board)) // 对特殊情况穷举
			return 1;
	// 对角线的防与攻左斜
	if (board[0][0] == board[1][1] && board[1][1] == set1 && board[2][2] == set2) // 对角线左斜下
	{
		board[0][0] == ' ' ? (board[0][0] = '#') : (board[2][2] = '#');
		return 1;
	}
	if (board[0][0] == board[2][2] && board[0][0] == set1 && board[1][1] == set2) // 对角线左斜中
	{
		board[0][0] == ' ' ? (board[0][0] = '#') : (board[1][1] = '#');
		return 1;
	}
	if (board[1][1] == board[2][2] && board[1][1] == set1 && board[0][0] == set2) // 对角线左斜上
	{
		board[2][2] == ' ' ? (board[2][2] = '#') : (board[0][0] = '#');
		return 1;
	}
	// 对角线的防与攻右斜
	if (board[0][2] == board[1][1] && board[1][1] == set1 && board[2][0] == set2) // 对角线右斜下
	{
		board[0][2] == ' ' ? (board[0][2] = '#') : (board[2][0] = '#');
		return 1;
	}
	if (board[0][2] == board[2][0] && board[0][2] == set1 && board[1][1] == set2) // 对角线右斜中
	{
		board[0][2] == ' ' ? (board[0][2] = '#') : (board[1][1] = '#');
		return 1;
	}
	if (board[1][1] == board[2][0] && board[1][1] == set1 && board[0][2] == set2) // 对角线右斜上
	{
		board[2][0] == ' ' ? (board[2][0] = '#') : (board[0][2] = '#');
		return 1;
	}
	for (int i = 0; i < 3; i++)
	{
		// 行的防御
		if (board[i][0] == board[i][1] && board[i][0] == set1 && board[i][2] == set2) // 行尾
		{
			board[i][0] == ' ' ? (board[i][0] = '#') : (board[i][2] = '#');
			return 1;
		}
		if (board[i][0] == board[i][2] && board[i][0] == set1 && board[i][1] == set2) // 行中
		{
			board[i][0] == ' ' ? (board[i][0] = '#') : (board[i][1] = '#');
			return 1;
		}
		if (board[i][1] == board[i][2] && board[i][1] == set1 && board[i][0] == set2) // 行头
		{
			board[i][2] == ' ' ? (board[i][2] = '#') : (board[i][0] = '#');
			return 1;
		}
		// 列的防御
		if (board[0][i] == board[1][i] && board[0][i] == set1 && board[2][i] == set2) // 列尾
		{
			board[0][i] == ' ' ? (board[0][i] = '#') : (board[2][i] = '#');
			return 1;
		}
		if (board[0][i] == board[2][i] && board[0][i] == set1 && board[1][i] == set2) // 列中
		{
			board[0][i] == ' ' ? (board[0][i] = '#') : (board[1][i] = '#');
			return 1;
		}
		if (board[1][i] == board[2][i] && board[1][i] == set1 && board[0][i] == set2) // 列头
		{
			board[2][i] == ' ' ? (board[2][i] = '#') : (board[0][i] = '#');
			return 1;
		}
	}
	return 0;
}

提升难度后的整体源代码

在game.h中:

#pragma once

#define _CRT_SECURE_NO_WARNINGS

#define ROW 3 // 方便扩展
#define COL 3

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

void initBoard(char board[ROW][COL], int row, int col);

void display(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);

char IsWin(char board[ROW][COL], int row, int col);

int difficult(char board[ROW][COL], char set1, char set2);

int first(char board[3][3]);

在game.c中:

#include "game.h"

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

void display(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1) // 打印纵分割线
				printf("|");
		}
		printf("\n");
		if (i < row - 1) // 打印横分割线
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}

void computerMove(char board[ROW][COL], int row, int col, int* firstMove)
{
	printf("电脑下棋\n");
	int y, x;
	//        三子连接攻击				 三子连接防御					   二子连接
	if (difficult(board, '#', ' ') || difficult(board, '*', ' ') || difficult(board, ' ', '#')); // 当防御时返回1,否则返回0。
	else if (*firstMove && first(board)) // 开局 
		*firstMove = 0;
	else
	{
		while (1) // 这里使用了随机下棋
		{
			y = rand() % row;
			x = rand() % col;
			if (board[y][x] == ' ') // 是空格才下
			{
				board[y][x] = '#';
				break;
			}
		}
	}
}

void playerMove(char board[ROW][COL], int row, int col)
{
	int y, x;
	printf("玩家下棋:>\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &y, &x);
		if (x >= 1 && x <= 3 && y >= 1 && y <= 3 && board[y - 1][x - 1] == ' ') // 限定范围
		{
			board[y - 1][x - 1] = '*'; // 这里要对应数组下标
			break;
		}
		else
			printf("坐标非法或被占用,请重新输入\n");
	}
}

int IsFull(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 1;
}

char IsWin(char board[ROW][COL], int row, int col)
{// 检测行、列、对角线棋子情况
	for (int 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 (int j = 0; j < col; j++) // 列
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')
			return board[1][j];// 若当列三子相同,返回其棋子

	// 对角线
	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[1][1] != ' ')
		return board[1][1];

	if (IsFull(board, row, col))
		return 'Q'; // 字符Q表示棋盘满了
	return 'C'; // 字符C表示下棋正常进行
}

在test.c中:

#include "game.h"

void menu()
{
	printf("*************************\n");
	printf("**** 1.play   0.exit ****\n");
	printf("*************************\n");
}

void game()
{
	int firstMove = 1; // 判断是否是开局
	char board[ROW][COL] = { 0 };
	char ret = 0; // 接收棋盘下棋的信息
	initBoard(board, ROW, COL);
	display(board, ROW, COL);
	while (1) // 使用循环
	{
		playerMove(board, ROW, COL);
		ret = IsWin(board, ROW, COL); // 接收信息
		display(board, ROW, COL);
		if (ret != 'C') // 若不为正常情况,跳出循环
			break;
		computerMove(board, ROW, COL, &firstMove);
		ret = IsWin(board, ROW, COL);
		display(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*') // 跳出循环时判断下棋之后的情况
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else if (ret == 'Q')
		printf("平局\n");
}

void test()
{
	int input = 0;
	srand((unsigned int)time(NULL)); // 为后续的电脑随机下棋准备
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误!\n");
		}
	} while (input);
}

int main()
{
	test();
	return 0;
}

在computerDifficult.c中:

#include "game.h"

int defense(char board[ROW][COL])
{
	// 同边情况
	if (board[2][1] == '*' && board[1][2] == '*')
	{
		board[2][2] = '#';
		return 1;
	}
	// 边角情况
	if ((board[1][0] == '*' && board[2][2] == '*') || (board[2][1] == '*' && board[0][0] == '*'))
	{
		board[2][0] = '#';
		return 1;
	}
	if ((board[2][1] == '*' && board[0][2] == '*') || (board[1][2] == '*' && board[2][0] == '*'))
	{
		board[2][2] = '#';
		return 1;
	}
	// 对角情况
	if ((board[0][0] == '*' && board[2][2] == '*') || (board[0][2] == '*' && board[2][0] == '*'))
	{
		board[0][1] = '#';
		return 1;
	}
	return 0;
}

int difficult(char board[ROW][COL], char set1, char set2)
{
	if (set1 == ' ' && set2 == '#') // 当为二子连接时
		if (defense(board)) // 对特殊情况穷举
			return 1;
	// 对角线的防与攻左斜
	if (board[0][0] == board[1][1] && board[1][1] == set1 && board[2][2] == set2) // 对角线左斜下
	{
		board[0][0] == ' ' ? (board[0][0] = '#') : (board[2][2] = '#');
		return 1;
	}
	if (board[0][0] == board[2][2] && board[0][0] == set1 && board[1][1] == set2) // 对角线左斜中
	{
		board[0][0] == ' ' ? (board[0][0] = '#') : (board[1][1] = '#');
		return 1;
	}
	if (board[1][1] == board[2][2] && board[1][1] == set1 && board[0][0] == set2) // 对角线左斜上
	{
		board[2][2] == ' ' ? (board[2][2] = '#') : (board[0][0] = '#');
		return 1;
	}
	// 对角线的防与攻右斜
	if (board[0][2] == board[1][1] && board[1][1] == set1 && board[2][0] == set2) // 对角线右斜下
	{
		board[0][2] == ' ' ? (board[0][2] = '#') : (board[2][0] = '#');
		return 1;
	}
	if (board[0][2] == board[2][0] && board[0][2] == set1 && board[1][1] == set2) // 对角线右斜中
	{
		board[0][2] == ' ' ? (board[0][2] = '#') : (board[1][1] = '#');
		return 1;
	}
	if (board[1][1] == board[2][0] && board[1][1] == set1 && board[0][2] == set2) // 对角线右斜上
	{
		board[2][0] == ' ' ? (board[2][0] = '#') : (board[0][2] = '#');
		return 1;
	}
	for (int i = 0; i < 3; i++)
	{
		// 行的防御
		if (board[i][0] == board[i][1] && board[i][0] == set1 && board[i][2] == set2) // 行尾
		{
			board[i][0] == ' ' ? (board[i][0] = '#') : (board[i][2] = '#');
			return 1;
		}
		if (board[i][0] == board[i][2] && board[i][0] == set1 && board[i][1] == set2) // 行中
		{
			board[i][0] == ' ' ? (board[i][0] = '#') : (board[i][1] = '#');
			return 1;
		}
		if (board[i][1] == board[i][2] && board[i][1] == set1 && board[i][0] == set2) // 行头
		{
			board[i][2] == ' ' ? (board[i][2] = '#') : (board[i][0] = '#');
			return 1;
		}
		// 列的防御
		if (board[0][i] == board[1][i] && board[0][i] == set1 && board[2][i] == set2) // 列尾
		{
			board[0][i] == ' ' ? (board[0][i] = '#') : (board[2][i] = '#');
			return 1;
		}
		if (board[0][i] == board[2][i] && board[0][i] == set1 && board[1][i] == set2) // 列中
		{
			board[0][i] == ' ' ? (board[0][i] = '#') : (board[1][i] = '#');
			return 1;
		}
		if (board[1][i] == board[2][i] && board[1][i] == set1 && board[0][i] == set2) // 列头
		{
			board[2][i] == ' ' ? (board[2][i] = '#') : (board[0][i] = '#');
			return 1;
		}
	}
	return 0;
}

int first(char board[3][3])
{
	if (board[1][1] == ' ') // 开局占中心
	{
		board[1][1] = '#';
		return 1;
	}
	else// 开局占四角
	{
		int arr[2] = { 0, 2 };
		int i = arr[rand() % 2];
		int j = arr[rand() % 2];
		if (board[i][j] == ' ')
		{
			board[i][j] = '#';
			return 1;
		}
	}
	return 0;
}

结语

以上针对电脑难度提升的代码,个人觉得电脑应该不会出现输的情况了。当然,若读者测试出此电脑还是有问题,可以私信或评论区告知我。

谢谢阅读

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值