万字详解:C语言三子棋进阶 + N子棋递归动态判断输赢

本文介绍C语言学习阶段的经典项目:三子棋(N子棋)。

文章主要以代码的形式呈现,并附上必要的说明(说明主要以代码注释的风格呈现),力求正确、简洁、清晰。


文章目录

前言

**解压缩的文件夹及其内容预告

一、游戏简要介绍 -- 三子棋

规则

功能实现

二、代码呈现 -- 按功能实现函数接口

test.c

game.h

game.c

三、功能优化:会堵棋的电脑角色

四、 功能优化:递归实现 N 子棋动态输赢判断

1. 思路

递归思路

2. 代码展示

is_Same.c

test_is_Win.c 

五、总结

六、课设大作业资源


前言

三子棋游戏设计的核心是对二维数组的把握和运用。

本文分步骤呈现三子棋(N子棋)游戏程序设计代码,以介绍与解析为主。文末另附一份压缩文件,为C语言三子棋课设实验报告(博主本人在学校写的,当初选择了三子棋作为课设项目),需要用作课设参考的同学可以直接下载现成的。希望多诸位读者有所帮助。

**解压缩的文件夹及其内容预告

\C语言课设作业--三子棋

\C语言课设作业--三子棋\课设大作业

此外,本文也对原有的最简单的三子棋游戏进行了优化,增加了电脑“会堵棋”的代码版本以及 N 子棋的输赢判断实现。希望对大家开拓思路有所帮助。


 一、游戏简要介绍 -- 三子棋

规则

游戏模式为人机对决。玩家在主菜单界面选择是否要开始一盘游戏。当游戏开始后,由玩家这一方先开局下棋。通过坐标输入的方式将棋子放入玩家想要下的位置。玩家落子后电脑方立刻下棋。双方轮流下棋,直到有一方有3颗棋子连成一线,则率先达到3子连线的一方获得胜利。然后再由玩家决定是否要再开一把游戏。

功能实现

  1. 在菜单界面玩家可选择开始游戏或退出游戏。
  2. 创建一个新的棋盘,并初始化。
  3. 将棋盘打印在屏幕上。
  4. 玩家先开局,通过输入行列坐标的方式来落子,用×表示玩家落子。
  5. 玩家落子后轮到电脑落子,电脑在棋盘随机位置落子,用〇表示电脑落子。
  6. 判定胜负,输或赢或和棋,用q表示和棋。率先连成3子的一方获胜。
  7. 回到步骤1,循环以上步骤。

二、代码呈现 -- 按功能实现函数接口

我们以以上的游戏的功能实现为线索,进行代码编写。下列代码的实验环境为Visual Studio 2022


1. 在菜单界面玩家可选择开始游戏或退出游戏。

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

2. 创建一个新的棋盘,并初始化。

//初始化棋盘--将二维数组每个元素赋值为空格
//空格:可以仅占位而不显示,展示出“空白棋盘”的效果

void init_board(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] = ' ';
		}
	}
}

3. 将棋盘打印在屏幕上。

静态打印棋盘

void print_chessBoard(char chessBoard[ROW][COL])
{
	printf("棋盘打印\n");
	printf("+(0)+(1)+(2)+\n");
	printf("+---+---+---+\n");
	for (int row = 0; row < ROW; row++) 
    {
		printf("| %c | %c | %c |(%d)\n", chessBoard[row][0],chessBoard[row][1], chessBoard[row][2],row);
		printf("+---+---+---+\n");
	}
}

有些人认为静态打印不如动态打印好,因为棋盘是固定的。但我个人觉得差不太多,因为动态打印棋盘则必须搭配动态判断输赢的办法,而现在能搜到的大部分三子棋代码并不配备动态判断输赢的算法,依旧是以类似于结果枚举的静态效果来判断输赢的。仅仅对于简单的3×3棋盘的三子棋而言,静态也完全够用。

但动态打印的逻辑是非常重要的,它需要同学们对二维数组的使用有清晰的思路和正确的把控,要是把握不好了会打印出歪瓜裂枣。因此动态打印思路也是一定需要介绍的。 

动态判断输赢在文章偏后部分进行说明。

动态打印棋盘 

void show_board(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++)
		{
			printf(" %c ", board[i][j]);
			if(j<col-1)
				printf("|");
		}
		printf("\n");

		//---|---|---
        //打印横线列
		if (i < row - 1)
		{
			//printf("---|---|---\n");
			for (j = 0; j < col; j++)
			{
				printf("---");
				if(j<col-1)
					printf("|");
			}
			printf("\n");
		}
	}
}

说明:该处代码较长。如何动态打印棋盘是一个难点。在思考逻辑时,建议大家先以“行”为单位思考每一行内代码要做什么事,实现什么效果;再同样以列为单位进行思考。

注意其中标注“打印格子列”“打印横线列”的代码。此处的思路如下:

1. 打印时,每一行要做哪些事?

——打印二维数组数据,打印分隔竖线"|";打印分隔横线。

2. 数据格子和横线在行中交替出现,怎么解决?

——要做到一行打印格子、一行打印横线交替,不太方便实现,可以转变思路

——将一个格子与它下面的横线看作同一行,如下图是三行,每一行(除最后一行外)包括一层空白格子和一层横线。(一层在上、一层在下用'\n'实现即可)

3. 最后一行没有横线,最后一列也没有竖线,怎么办?

——用if语句控制,只在最后一行和最后一列之前打印横线和竖线。

4. 我想不到 / 想不清楚怎么办?

——此处运用到的思路详述在另一篇文章中,链接如下:

“盒子思路”解决循环嵌套icon-default.png?t=N7T8http://t.csdn.cn/kZVwx

另一个版本的棋盘打印代码如下,也许会更贴合上述逻辑(其实整合后,就是上面所贴出的代码):

//打印棋盘
void show_board(char board[ROW][COL], int row, int col) {
	//打印行
	for (int i = 0; i < row; i++)
	{
		if (i < row - 1) {
			//打印格子列
			for (int j = 0; j < col; j++)
			{
				if (j < col - 1)
				{
					printf(" %c ", board[i][j]);
					printf("|");
				}
				else
				{
					printf(" %c ", board[i][j]);
				}
			}
			
			printf("\n");

			//打印横线列
			for (int j = 0; j < col; j++)
			{
				if (j < col - 1)
				{
					printf("---");
					printf("|");
				}
				else
				{
					printf("---");
				}
			}
			printf("\n");
		}
		else
		{
			for (int j = 0; j < col; j++)
			{
				if (j < col - 1)
				{
					printf(" %c ", board[i][j]);
					printf("|");
				}
				else
				{
					printf(" %c ", board[i][j]);
				}
			}
			printf("\n");
		}
	}
}

棋盘效果展示

 5*5

10*10

 更改用宏定义:

#define ROW 10
#define COL 10

 变长数组应该也可以?但我没试过,因为vs 2022仍不支持C99。

4. 玩家先开局,通过输入行列坐标的方式来落子,用 '*' 表示玩家落子。

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

	printf("玩家下棋:>\n");

	while (1)
	{
		printf("请输入要下棋的坐标:>");
		scanf("%d %d", &x, &y);
		//1.坐标的合法性
		//2.坐标是否被占用
		if (x >= 1 && x <= row && y >= 1 && y <= col)    //用x而不用x-1:默认玩家玩游戏时,按照习惯从“1”开始计行数和列数,因此不用减一
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

说明:注意此处判断非法的逻辑。有些人用if-else if-else将三个判断条件串起来,这时不对的,如下图:

上图代码中,最后一个else永远也不会被执行到。原本我们想要表达的是,如果输入的坐标超出棋盘范围(数组越界),跳出提示:输入位置无效。而超出棋盘范围这一状况,同样满足第二个 else-if (数组坐标越界,该坐标所代表的元素在c语言中并不是不存在,它的内容是脏数据,只是没有被我们当作棋盘打印出来)。

写出这样的代码,是因为思路不清晰。我们进行合法性判断的步骤是这样的:

1. 如果下的棋位置在棋盘上,而且又是空位置,那就落子;

2. 如果下的在棋盘上,但是不是空位置,那就提示该位置已经下过了,落子失败重新输入;

3. 如果下的不在棋盘上,那就提示数组越界,落子失败重新输入。

所以,代码逻辑是这样的:

在棋盘上吗?

        - 在,那棋盘上这个位置是不是空的呢?

                -- 是 ---> 下棋。

                -- 不是 ---> 重新输。

        -不在,重新输。

因而,谁和谁共用同一层if - else,显而易见。

5. 玩家落子后轮到电脑落子,电脑在棋盘随机位置落子,用 '#'表示电脑落子。

void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:>\n");
    
	while (1)
	{
		int x = rand() % row;    //row为3时,取模运算结果为 0~2 ,刚好满足下标规律,所以这里不用像玩家一样用x-1
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

说明:此时我们用到了rand()函数来获取电脑要落子的位置。注意:播种随机种子应在main函数内,播种一次即可;另外,要包含头文件time.h(时间戳)与stdlib.h

6. 判定胜负,输或赢或和棋,用Q表示和棋。率先连成3子的一方获胜。

棋盘满了,还未分出胜负——和棋。 

//判断棋盘是否已满
int is_full(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 is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
    
    //判断横三行
	for (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 (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][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[1][1] != ' ')
	{
		return board[1][1];
	}

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

	//继续
	return 'C';
}

 7. 回到步骤1,循环以上步骤。

在控制游戏流程时,加上死循环while,并用break控制循环的结束。这是C语言设计简单游戏时的常用操作,从需求分析的角度解释为:玩家体验一次可能不过瘾,还想接着再来一句,因而这时while可以实现由玩家自己决定是否还要再来一局(而不是直接结束程序)。

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

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

void game() {
	printf("游戏开始!\n\n");
	char board[ROW][COL] = { 0 };

	//1.初始化棋盘
	init_board(board, ROW, COL);
	//test_print(board, ROW, COL);	//打印二维数组

	//2.打印棋盘
	show_board(board, ROW, COL);

	//3.下棋
	char ret;
	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		show_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}

		//电脑下棋
		computer_move(board, ROW, COL);
		show_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}

	//4.判断胜负
	if (ret == '*') {
		printf("玩家获胜!\n");
	}
	else if (ret == '#') {
		printf("电脑获胜!\n");
	}
	else if (ret == 'Q')
	{
		printf("平局!\n");
	}

}


int main() {
	int input = 0;
	srand((unsigned)time(NULL));

	//主体部分写在循环内
	//玩不过瘾还可以接着玩:实现体验多次游戏
	do
	{
		menu();
		printf("请输入正确的选项>:");
		scanf("%d", &input);
		switch (input)
		{
		case 1: {	//进入游戏
			game();
			printf("\n");
			break;
		}
		case 0: {	//退出游戏
			printf("退出游戏!\n");
			break;
		}
		default:	//输入错误,需要重新输入
			printf("输入错误,请重新输入!\n");
			break;
		}
		
	} while (input);

	return 0;
}

game.h

#pragma once

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

#define ROW 3
#define COL 3



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

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

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

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

//判断输赢
char is_win(char board[ROW][COL], int row, int col);

//判断棋盘是否已满
int is_full(char board[ROW][COL], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void init_board(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++)
		{
			board[i][j] = ' ';
		}
	}
}

//void display_board(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (i = 0; i < row; i++)
//	{
//		//数据
//		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//		//---|---|---
//		if(i<row-1)
//			printf("---|---|---\n");
//	}
//}


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

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

	printf("玩家下棋:>\n");

	while (1)
	{
		printf("请输入要下棋的坐标:>");
		scanf("%d %d", &x, &y);
		//1.坐标的合法性
		//2.坐标是否被占用
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

//
//电脑随机下棋
//
void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:>\n");
	//0~32726
	//%3-->0~2
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//如果棋盘满了,返回1
//不满,返回0
static int is_full(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++)
		{
			if (' ' == board[i][j])
			{
				return 0;
			}
		}
	}

	return 1;
}

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (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 (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][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[1][1] != ' ')
	{
		return board[1][1];
	}

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

	//继续
	return 'C';
}

三、功能优化:会堵棋的电脑角色

说明见代码注释。 

void update_computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋!\n");
	int x = y = 0;
	int i = j = 0;
	
	//检查每行,判断是否需要堵棋
	for (i = 0; i < row; i++)
	{
		int count = 0;	//用于标记棋数
		
		//数每一行玩家下了多少个棋子
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == '*')
				count++;
		}
		
		//当某一行玩家已经下了两个棋-->要堵棋
		if (count == 2)
		{
			for (j = 0; j < col; j++)
			{
				if (board[i][j] == ' ')
				{
					board[i][j] = '#';
					return;		//落子,函数结束
				}
			}
		}
	}
	
	//检查每列,判断是否需要堵棋
	//思路与上面完全相同
	for (j = 0; j < col; j++)
	{
		int count = 0;
		for (i = 0; i < row; i++)
		{
			if (board[i][j] == '*')
				count++;
		}
		
		//若一列已经有两个棋-->堵棋
		if (count == 2)
		{
			for (i = 0; i < row; i++)
			{
				if (board[i][j] == ' ')
				{
					board[i][j] = '#';
					return;		//落子,函数结束
				}
			}
		}
	}
	
	int diagonal_1 = 0;		//标记主对角线
	//检查主对角线,判断是否需要堵棋
	for (i = 0; i <row; i++)
	{
		if (board[i][i] == '*')
			diagonal_1++;
	}
	if (diagonal_1 == 2)
	{
		for (i = 0; i <row; i++)
		{
			if (board[i][i] == ' ')
			{
				board[i][i] = '#';
				return;		//落子,函数结束
			}
		}
	}
	
	int diagonal_2 = 0;		//标记副对角线
	//检查副对角线,判断是否需要堵棋
	for (i = 0, j = col - 1; i < row && j >= 0; i++, j--)
	{
		if (board[i][j] == '*')
			diagonal_2++;
	}
	if (diagonal_2 == 2)
	{
		for (i = 0, j = col - 1; i < row && j >= 0; i++, j--)
		{
			if (board[i][j] == ' ')
			{
				board[i][j] = '#';
				return;
			}
		}
	}
	
	//若不符合堵棋的条件-->随机落子
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x - 1][y - 1] == ' ')
		{
			board[x - 1][y - 1] = '#';
			return;
		}
	}
}

四、 功能优化:递归实现 N 子棋动态输赢判断

从三子棋到N子棋(N行N列N子),所要作出的调整无非只有两个:

1. 动态打印棋盘(见上,通过改变符号常量ROW和COL来更改棋盘大小)

2. 动态判断输赢(不用if 一一列出最终棋盘上可能的情况再一一判断谁胜谁负)


1. 思路

示意图如下(忽略图画得很丑这个事实),向八个方向进行递归,检查并统计该棋子在递归方向有多少个。

我们可以通过创建并定义符号常量NUMBER,来作为判断是否胜利的标准。如三子棋中,令NUMBER为3,则这八个方向中有任意一个方向达成3子连珠,则连珠的这个棋子所代表的玩家获胜。

之所以要用递归解决,是因为我们每次有落子后,都需要判断是否有NUMBER个连着的相同棋子。以向左递归,symbol = '*'  为例,思路如下:

判断该棋子左侧的棋子是否是'*' ?

        -不是,return  symbol_count(结束函数)

        -是,计数器 symbol_count++。判断再左边的棋子是否是'*'?(……不断重复该问题,以此类推)

递归思路

找重复:不断向左遍历判断该位子上的棋子是否是symbol

找变化:棋子的坐标(不断向左移动)

找出口:发现第一个棋盘上不是该棋子的位置(return symbol_count)

 最终判断某一方向上的symbol_count是否等于NUMBER,若等于,则该棋子胜出。

2. 代码展示

is_Same.c

//symbol  棋子符号,'*'为玩家,'#'为电脑
//x、y为坐标参数

//左
int is_Same_left(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((y-1 >= 0) && (board[x][y-1] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_left(board, symbol, x, y-1);
	}
	return symbol_count;
}

//右
int is_Same_right(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((y+1 < COL) && (board[x][y+1] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_right(board, symbol, x, y+1);
	}
	return symbol_count;
}

//上
int is_Same_over(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((x-1 >= 0) && (board[x-1][y] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_over(board, symbol, x-1, y);
	}
	return symbol_count;
}

//下
int is_Same_down(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((x+1 < ROW) && (board[x+1][y] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_down(board, symbol, x+1, y);
	}
	return symbol_count;
}


//主对角线上半部分
int is_Same_diagonal1_over(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((x-1 >= 0) && (y-1 >= 0) && (board[x-1][y-1] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_diagonal1_over(board, symbol, x-1, y-1);
	}
	return symbol_count;
}

//主对角线下半部分
int is_Same_diagonal1_down(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((x+1 < ROW) && (y+1 < COL) && (board[x+1][y+1] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_diagonal1_down(board, symbol, x+1, y+1);
	}
	return symbol_count;
}

//副对角线上半部分
int is_Same_diagonal2_over(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((x+1 < ROW) && (y-1 >= 0) && (board[x+1][y-1] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_diagonal2_over(board, symbol, x+1, y-1);
	}
	return symbol_count;
}

//副对角线下半部分
int is_Same_diagonal2_down(char board[ROW][COL], char symbol, int x, int y)
{
	int symbol_count = 0;
	if((x-1 >= 0) && (y+1 < COL) && (board[x-1][y+1] == symbol))
	{
		symbol_count ++;
		symbol_count += is_Same_diagonal2_down(board, symbol, x-1, y+1);
	}
	return symbol_count;
}

test_is_Win.c 

int is_Win(char borad[ROW][COL], char symbol, int x, int y)
{
	int cnt1,cnt2,cnt3,cnt4;
	
	cnt1 = cnt2 = cnt3 = cnt4 = 1;
	
	//左右
	cnt1 += is_Same_left(borad, symbol, x, y);
	cnt1 += is_Same_right(borad, symbol, x, y);
	
	//上下
	cnt2 += is_Same_over(borad, symbol, x, y);
	cnt2 += is_Same_down(borad, symbol, x, y);
	
	//主对角线
	cnt3 += is_Same_diagonal1_over(borad, symbol, x, y);
	cnt3 += is_Same_diagonal1_down(borad, symbol, x, y);
	
	//副对角线
	cnt4 += is_Same_diagonal2_over(borad, symbol, x, y);
	cnt4 += is_Same_diagonal2_down(borad, symbol, x, y);

	//判断胜利
	//NUMBER 为自己定义的符号常量,用于设置多少个棋子算赢
	if ((cnt1 == NUMBER) || (cnt2 == NUMBER) || (cnt3 == NUMBER) || (cnt4 == NUMBER))
	{
		return 2;	//返回2,代表赢
	}
	else
	{
		return is_full(borad, ROW, COL);	//判断是否平局了
	}
}

五、总结

二维数组的使用;for 循环嵌套;递归判断棋子是否一样

(懒得写了,自己总结)


六、课设大作业资源

已上传为资源,有需要者可点击链接下载资源。

资源下载 :C课设大作业 -- 三子棋icon-default.png?t=N7T8https://download.csdn.net/download/wyd_333/86265917

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值