当前位置: 首页 > news >正文

人工智能三子棋-人机对弈-人人对弈,谁会是最终赢家?

在这里插入图片描述

✅作者简介:大家好我是原始豌豆,感谢支持。
🆔本文由 原始豌豆 原创 CSDN首发🐒 如需转载还请通知⚠
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
📣系列专栏:C语言项目实践

前言

    相信大家都玩过三子棋或者五子棋游戏,但你会通过编程完美实现吗,本篇将带领你真正学会AI智能三子棋,让程序拥有人工智能,可以实现堵棋,赢棋,自由落子的功能。玩家VS电脑,玩家VS玩家。谁会是最终赢家?

文章目录

一、项目背景与目标
二、最终成果展示
三、模块化编程
四、整体结构组成
五、代码及思路讲解
     (1)控制台属性设置函数
     (2)颜色控制函数
     (3)背景音乐函数
     (4)光标函数
     (5)三子棋字幕动画函数
     (6)主菜单界面player vs computer 动画展示函数
     (7)主菜单函数
     (8)初始化棋盘函数
     (9)打印棋盘函数
     (10)玩家下棋函数
     (11)玩家下棋提示框函数
     (12)AI人工智能下棋函数
     (13)AI人工智能三连赢棋函数
     (14)AI人工智能堵棋函数
     (15)先手后手函数
     (16)遍历棋盘函数
     (17)电脑下棋提示框函数
     (18)玩家VS玩家下棋函数
     (19)人机对战中玩家赢或电脑赢或平局的提示框函数
     (20)游戏主体函数
     (21)游戏说明函数
     (22)开发人员菜单函数
     (23)退出游戏函数
     (24)主函数

六.全部源码
七.项目总结

一、项目背景与目标

  三子棋,又称为井字棋,是一种古老而简单的两人对弈游戏。在3x3的棋盘上,两位玩家轮流在空格上放置自己的标记(通常为X和#),先连成三子一线(横、竖或斜)的玩家获胜。本项目旨在使用C语言实现一个能够与人类玩家进行对战并具有一定智能水平的三子棋AI。在人机对战中这个AI会尝试找到一个胜利的模式或阻止对手胜利的模式。如果没有找到,它将会选择其余可以干扰到玩家的位置下棋。除此之外该程序还具备人人对战的功能。

二、最终成果展示

话不多说,先给大家看下做好后的最终效果!!!

三子棋游戏最终效果图GIF展示(带背景音乐)

在这里插入图片描述

三、模块化编程

实现智能三子棋代码逻辑前,我们来说说什么是模块化编程吧?

什么是模块化编程?
  传统方式编程:所有的函数均放在主函数main.c里,一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路。
   模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include
"XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。

四、整体结构组成

游戏代码主要由四部分组成。
beautify.c
   该文件内主要存放三子棋游戏的控制台设置函数,颜色控制函数,音乐播放函数,光标定位函数,游戏开场动画函数,菜单界面动画函数,主菜单界面,打印玩家赢或电脑赢或平局的提示框函数,游戏规则说明函数,开发人员菜单函数,打印玩家下棋对应的提示框函数,打印电脑下棋坐标提示框的函数,打印退出游戏动态效果
game.c
    该文件内主要存放初始化棋盘函数 ,打印棋盘函数 ,玩家下棋函数,电脑下棋函数,分别调用AI智能三连赢棋函数,AI智能堵棋函数,按比重权值范围随机落子,实现人工智能下棋,判断棋盘是否为空函数,AI人工智能三连赢棋函数,AI人工智能三连赢棋函数,玩家VS玩家下棋函数 ,判断输赢函数
test.c
   该文件内主要存放人机对战中选择玩家先手或电脑先手的函数,游戏主体函数,主函数

sanziqi.h 头文件
   声明游戏内容中所包含的所有函数

在这里插入图片描述

五、代码及思路讲解

(1)控制台属性设置函数

  智能三子棋是基于C语言编写的控制台应用程序,我们首先要把控制台的各种参数设置好,控制台显示的宽度和高度,控制台缓冲区的大小和控制台标题的设置。

//控制台属性设置
void ConsoleSet()
{system("mode con cols=141 lines=50");//窗口宽度和高度HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//使用GetStdHandle函数来获取标准输出句柄(STD_OUTPUT_HANDLE),通常用于控制台应用程序。COORD bufferSize = { 141, 500 }; // 缓冲区大小为141*500字符  缓冲区的大小以字符宽和高为单位。//设置的控制台缓冲区大小要超过控制台大小SetConsoleScreenBufferSize(hOut, bufferSize);system("title 智能三子棋");//界面美化
}

(2)颜色控制函数

  智能三子棋游戏里使用了很多不同颜色的动态效果展示和界面,所以我们先编写一个color函数用来控制程序的各个界面的颜色,让程序运行看上去更加美观、鲜明。

//颜色控制函数
void color(short x)
{if (x >= 0 && x <= 15)SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);elseSetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}

(3)背景音乐函数

   我们的游戏是带有背景音乐的,所以这里我们创建一个背景音乐函数BackgroundMusic,通过调用windows api来实现音乐的播放和音效的实现,“AI-sanziqi.wav”,引号内部的内容为音频文件的名称(音频文件必须是wav格式的,音频文件放在代码对应的release文件底下程序才能有声音),在这里给
BackgroundMusic函数设置了参数x,通过参数用来控制音乐的打开或关闭。传参数e时音乐停止,音频文件我会放到本文的最后。

void BackgroundMusic(char x) //参数传s代表start,开始播放音乐,参数传e代表end,终止播放音乐
{if (x == 's'){PlaySound(TEXT("AI-sanziqi.wav"), NULL, SND_FILENAME | SND_LOOP | SND_ASYNC);//播放音频文件,循环播放,异步播放  }if (x == 'e'){PlaySound(NULL, NULL, SND_PURGE); // 停止所有声音 }
}

(4)光标函数

   从本文最开始的最终效果展示中可以看到,部分图案和文字的的打印定位是通过光标位置到指定坐标,然后开始打印的,所以我们编写一个光标控制函数。

//定位光标位置到指定坐标
void goto_xy(int x, int y)
{HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);;COORD pos = { x,y };SetConsoleCursorPosition(handle, pos);
}

(5)三子棋字幕动画函数

   游戏开场动画,用字符数组存储并通过循环搭配Sleep函数和光标函数实现显示动画和动画清除的效果,本函数有两个参数,参数传p打印图案,传c清屏图案。

//游戏开场动画,三子棋字幕显示(函数内部不带颜色)
void sanziqi(char print_or_cls)//参数传p打印图案,传c清屏图案
{char str[] ="######################################################################\n\
#                      #                      #     ##   ###   ###   #\n\
#                      #    ##                #     ##   ###   ###   #\n\
#   ###############    #    ##############    #     ##  ####   ####  #\n\
#   ###############    #    ##############    #     ##  ###########  #\n\
#                      #            ####      #  ##################  #\n\
#                      #          #####       #  #######  ##   ###   #\n\
#                      #         ####         #  #######  ########   #\n\
#                      #         ###          #    ###    ########   #\n\
#     ##        ##     # ###################  #    #####  ##   ###   #\n\
#     ############     # ###################  #   ######  ########   #\n\
#     ############     # ###################  #   ####### ########   #\n\
#                      #          ##          #  #######  ##   ###   #\n\
#                      #          ##          # ### ## ## ##   ##### #\n\
#                      #          ##          #  ## ## ############# #\n\
#                      #          ##          #     ## ############# #\n\
#  ##             ###  #          ##          #     ##    ### ####   #\n\
#  ##################  #       #####          #     ##   ###  #####  #\n\
#  ##################  #       #####          #     ## ####     #### #\n\
#                      #       ####           #     ##  ##       ##  #\n\
#                      #                      #                      #\n\
#                      #                      #                      #\n\
######################################################################";if (print_or_cls == 'p')	//参数传p代表打印图案printffor (int i = 0; i < strlen(str); i++){putchar(str[i]);if (str[i] == '\n'){Sleep(50);}}else if (print_or_cls == 'c')  //参数传c代表清除图案cls{goto_xy(0, 0);   //将坐标传到(0,0),再依次按行打印空格,以达到清屏效果for (int i = 0; i < strlen(str); i++){putchar(' ');if (str[i] == '\n'){Sleep(50);}}}
}

效果展示
在这里插入图片描述

(6)主菜单界面player vs computer 动画展示函数

   从本文最开始的最终效果展示中可以看到,主菜单界面的左侧是有player vs computer 这个动态效果的展示的,所以我们编写一个player vs computer动画展示函数,用字符数组存储图案,并通过循环搭配Sleep函数和光标函数实现显示动画和动画清除的效果,我将图案分成了三部分,并设置了红,绿,蓝三种颜色对一部分每一部图案。

//游戏主菜单界面左侧player vs computer 动画展示(函数内部自带颜色,红,绿,蓝)
void player_vs_computer()
{int line = 0;//控制player vs computer在goto_xy函数操纵下的的行数位置char ch1[] ="        _  \n"" _ __  | |   __ _   _   _    ___   _ __  \n""| '_ \\ | |  / _` | | | | |  / _ \\ | '__|\n""| |_)| | | | (_| | | |_| | |  __/ | |   \n""|.__./ |_|  \\__,_|  \\__, |  \\___| | _|\n""|_|                 |___/ \n";char ch2[] =" __   __  ___ \n"" \\ \\ /  // __|\n""  \\ V /  \\__ \\\n""   \\_/   |___/\n";char ch3[] ="                                        _\n""  ___   ___   _ __ ___   _ __   _   _  | |_    ___   _ __ \n""/ __|  / _ \\ | '_ ` _ \\ | '_ \\ | | | | | __|  / _ \\ | '__|\n""| (__ | (_) || | | | | || |_) || |_| | | |_  |  __/ |  | \n""\\___ | \\___/ |_| |_| |_|| .__ / \\__,_|  \\___| \\___| | _|\n""                        |_| \n";color(4);//红色goto_xy(5, 0);for (int i = 0; i < strlen(ch1); i++){printf("%c", ch1[i]);if (ch1[i] == '\n'){Sleep(30);goto_xy(5, ++line);}}color(10);//绿色goto_xy(15, ++line);for (int i = 0; i < strlen(ch2); i++){printf("%c", ch2[i]);if (ch2[i] == '\n'){Sleep(30);goto_xy(15, ++line);}}color(3);//蓝色goto_xy(0, ++line);for (int i = 0; i < strlen(ch3); i++){printf("%c", ch3[i]);if (ch3[i] == '\n'){Sleep(30);goto_xy(0, ++line);}}
}

效果展示
在这里插入图片描述

(7)主菜单函数

  我们既然做游戏,就得有一个菜单,供玩家选择开始游戏或退出游戏等功能。我们先做一个Menu菜单函数并写上每个功能,使用时在主函数里调用就可以展示给玩家了。
  菜单函数也可以设计的很精美,可以参考我的设计样式.最后的goto_xy(75, 16);是为了将输入处的光标定位在括号里面

//游戏主菜单界面菜单界面(函数内部不带颜色)
void Menu()
{goto_xy(60, 5);printf("________________________\n");  Sleep(70);goto_xy(60, 6);printf("|       1.开始游戏      |\n");  Sleep(70);goto_xy(60, 7);printf("|_______________________|\n"); Sleep(70);goto_xy(60, 8);printf("|       2.人人对战      |\n");  Sleep(70);goto_xy(60, 9);printf("|_______________________|\n"); Sleep(70);goto_xy(60, 10);printf("|       3.游戏说明      |\n"); Sleep(70);goto_xy(60, 11);printf("|_______________________|\n"); Sleep(70);goto_xy(60, 12);printf("|       4.开发人员      |\n"); Sleep(70);goto_xy(60, 13);printf("|_______________________|\n"); Sleep(70);goto_xy(60, 14);printf("|       5.退出游戏      |\n"); Sleep(70);goto_xy(60, 15);printf("|_______________________|\n"); Sleep(70);goto_xy(60, 16);printf("请输入选项->【   】");goto_xy(75, 16);//将输入处的光标定位在括号里面
}

效果展示
在这里插入图片描述

(8)初始化棋盘函数

  菜单函数写好以后,游戏拥有的基本功能框架也就定位好了,接下来我们要做的就是实现菜单函数里所希望实现的所有功能,先来实现人机对战。
  创建棋盘并进行初始化,因为我们是要把符号输入到二维数组中,结合我们构造出来的图案形成三子棋的模样,所以我们需要创建一个二维数组并进行初始化(因为棋盘最开始为空白的,所以二维数组初始化的内容为空格),三子棋的棋盘一共三行三列,共有九个空可以下棋,这用一个三行三列的数组来存放每个空的数据,为了代码的灵活性,这里不要把行和列的值写死,用define定义的常量来写,方便以后代码的更新。

//初始化棋盘函数  
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] = ' ';//将棋盘的每个位置初始化为空格}}
}

(9)打印棋盘函数

  我们创建并初始化棋盘后,需要打印出三子棋的棋盘供玩家进行观察和下棋,我们同样不要把行和列写死,而是采用define定义的常量ROW和COL,这样玩家如果不想玩3*3格的三子棋,或者想把三子棋改为五子棋,只需在头文件中改变define定义的常量值,棋盘的大小也会随之改变。

//打印棋盘函数  (亮黄色棋盘)
int DisplayBoard(char Board[ROW][COL], int row, int col)
{color(14);//黄色棋盘int k = 0;  //goto_xy函数的列坐标goto_xy(50, ++k);  //棋盘打印的初始坐标for (int i = 1; i <= col; i++)//打印棋盘列序号{printf("   %d", i);}goto_xy(52, ++k);for (int i = 0; i < col; i++){printf("____");}for (int i = 0; i < row; i++)//控制行数{goto_xy(50, ++k);//k不断自增以对应棋盘不同的大小,起到换行后居中的效果printf("%d|", i + 1);//打印棋盘行序号和最左侧边框for (int j = 0; j < col; j++){printf(" %c |", Board[i][j]);//将"   |"看作一组,根据col打印}goto_xy(50, ++k);//k不断自增以对应棋盘不同的大小,起到换行后居中的效果printf(" |");//打印棋盘无序号行和最左侧边框for (int j = 0; j < col; j++){printf("---|");//将"---|"看作一组,根据col打印}}printf("\n\n");
}

三子棋棋盘ROW和COL都为3效果图:
在这里插入图片描述
例如将ROW和COL都改成9:效果图
在这里插入图片描述

(10)玩家下棋函数

  在玩家下棋这部分中,我们定义玩家通过输入坐标来进行下棋。需要注意的是:数组所定义的下标是从0开始的,而玩家认知的坐标是从1开始的(例如:玩家想输入第一行第一列的坐标是,输入的是(1,1),而不是(0,0),这里需要将玩家输入的横、纵坐标减1,转换成数组的坐标。

   在玩家输入完坐标后,我们还需要判断该坐标是否合法,如果玩家输入的坐标超过棋盘界限,或者该坐标已经下过棋了,那么需要重新输入坐标。所以我们采用while循环结构,直到玩家下棋成功,跳出循环。
这里玩家下的棋,用 X表示。定义PlayerMove用来实现玩家下棋的这部分。

//玩家下棋函数  (玩家下棋显示红色提示框)
void PlayerMove(char Board[ROW][COL], int row, int col)
{color(4);//玩家下棋显示红色提示框int i = 0, j = 0;//定义了两个整数变量i和j,用于存储玩家输入的坐标。int ch = 0;PrintPlayerMove(1);while (1)//开始一个无限循环,这样程序会持续等待玩家的输入,直到玩家输入有效的坐标并下棋break。{scanf("%d%d", &i, &j);while ((ch = getchar()) != '\n' && ch != EOF);//对玩家输入数据进行处理,防止字符造成死循环。if ((i >= 1 && i <= row) && (j >= 1 && j <= col))//如果坐标在棋盘范围内(即1到行数和1到列数之间),则执行以下代码:{if (Board[i - 1][j - 1] == ' ')//检查该坐标位置是否为空。如果为空,表示这个位置可以被下棋{Board[i - 1][j - 1] = 'X';break;//成功下棋后跳出循环}else{PrintPlayerMove(2);}}else//如果坐标不在棋盘范围内,重新输入。{PrintPlayerMove(3);}}
}

(11)玩家下棋提示框函数

   玩家下棋回合得有提示框提示玩家到下棋回合了,或者玩家坐标输入错误,或者坐标不合法都会有对应的提示框,我们单独做一个玩家下棋提示框函数来显示这些,并通过不同的参数判断该打印哪条提示框信息。上面写的玩家下棋函数就可以调用玩家提示框函数,因为后续还会有人人对战模式,所以我们这里直接把人人对战的提示框也写好。

//打印玩家下棋对应的提示框函数  (函数内部不带颜色)
void PrintPlayerMove(int n)
{if (n == 1){printf(" __________________________________\n");printf("|       玩家下棋回合, 棋子‘X’    |\n");printf("|__________________________________|\n");printf(" 输入坐标:");}else if (n == 2){printf(" __________________________________\n");printf("|      坐标被占用, 请重新输入      |\n");printf("|__________________________________|\n");printf(" 输入坐标:");//如果该位置已被占据,程序会提示玩家重新输入:}else if (n == 3)//如果坐标不在棋盘范围内,重新输入。{printf(" __________________________________\n");printf("|      坐标不合法,请重新输入       |\n");printf("|__________________________________|\n");printf(" 输入坐标:");}else if (n == 4){printf(" __________________________________\n");printf("|       玩家一下棋回合, 棋子‘X’  |\n");printf("|__________________________________|\n");printf(" 玩家一输入坐标:");}else if (n == 5){printf(" __________________________________\n");printf("|       玩家二下棋回合, 棋子‘#’  |\n");printf("|__________________________________|\n");printf(" 玩家二输入坐标:");}}

效果展示图
在这里插入图片描述

(12)AI人工智能下棋函数
看了CSDN大部分文章写的三子棋,都是没有智能的,玩家轻松就能击败电脑,那么如何赋予电脑智能呢?让电脑可以击败玩家或立于不败之地呢?其实并不复杂。

   电脑实现AI人工智能下棋分为三个部分:赢棋、堵棋、按权值自由落子。

   这里的优先级是赢棋>堵棋>自由落子,在写代码的时候要遵循:能赢优先赢,不能赢则优先堵,以上情况都不符合的时候自由落子。(自由落子不等于随机下棋,找到以自身坐标为中心,周围8个坐标有玩家棋子的位置进行落子,可以对玩家形成一定的干扰)

定义三个函数:

   智能三连赢棋函数:AI_WinChess(如果函数执行并落子,返回’s’;否则返回’f’)

   智能堵棋函数: AI_BlockingChess(如果函数执行并落子,返回1;否则返回0)

   自由落子函数:ComputerMove(构建整体框架,根据前两个函数的返回值来判断是否落子)

//电脑下棋函数,分别调用AI智能三连赢棋函数,AI智能堵棋函数,按比重权值范围随机落子,实现人工智能下棋
void ComputerMove(char Board[ROW][COL], int row, int col)
{int x, y;//定义了两个整数变量x和y,用于存储电脑下棋的坐标。//1.智能三连赢棋函数优先级最高,//如果智能三连赢棋函数没有成功找到可以三连的棋子则返回f,然后进入内层运行智能堵棋函数if (AI_WinChess(Board, ROW, COL) == 'f'){//智能堵棋函数优先级第二,//如果智能堵棋函数没有成功找到可以堵棋的棋子则返回0,然后进入内层进行随机落子if (AI_BlockingChess(Board, ROW, COL) == 0){//分支判断电脑是否是先手且第一步下棋,是则在中心位置下棋,if (TraverseBoard(Board, ROW, COL) == 'e'){color(15);Board[row / 2][col / 2] = '#';AI_Coordinate(row / 2, col / 2);}else if (TraverseBoard(Board, ROW, COL) == 'n')//否则则找到以自身为中心,其余八个坐标内有玩家棋子的位置进行随机下棋{while (1){color(11);//随机下棋湖蓝色x = rand() % row;//在循环内,使用rand()函数%row和col,生成一个坐标值不会越界的随机的坐标(x, y)y = rand() % col;if (Board[x][y] == ' ' && (Board[x - 1][y - 1] == 'X' || Board[x - 1][y] == 'X' || Board[x - 1][y - 1] == 'X' || Board[x][y - 1] == 'X' || Board[x][y + 1] == 'X' || Board[x + 1][y - 1] == 'X' || Board[x + 1][y] == 'X' || Board[x + 1][y + 1] == 'X')){Board[x][y] = '#';AI_Coordinate(x, y);break;}}}}}
}

(13)AI人工智能三连赢棋函数

对玩家的的落子进行分析,实现智能赢棋 (优先级最高) ,电脑下棋用‘#’表示,该函数在AI人工智能下棋函数中被调用。

1 判断所有行里是否有某行出现 #  #  空 ,  #  空  # ,空  #  #,三种情况进行智能赢棋,
2.判断所有列里是否有某列出现
  #   #   空
  #   空  #
  空  #   #
3.判断一条角线(\)里是否有出现
#     #     空
  #    空     #
    空     #     #
4.判断另一条角线(/)里是否有出现
   #     #     空
  #     空       #
空     #      #

// AI人工智能三连赢棋函数(对玩家的的落子进行分析,实现智能赢棋  (优先级最高) (亮白色提示框)
char AI_WinChess(char Board[ROW][COL], int row, int col)// 返回值s代表success电脑三连赢棋成功,返回值f代表failure电脑未找到能够三连的的棋子{int i = 0;int j = 0;color(15);//1.判断所有行里是否有某行出现 ##空 #空# 空##,三种情况进行智能赢棋for (i = 0; i < row; i++){for (j = 0; j < col - 2; j++){if (Board[i][j] == Board[i][j + 1] && Board[i][j] == '#' && Board[i][j + 2] == ' '){Board[i][j + 2] = '#';AI_Coordinate(i, j + 2);//将下棋坐标传入AI_Coordinate函数return 's';}if (Board[i][j] == Board[i][j + 2] && Board[i][j] == '#' && Board[i][j + 1] == ' '){Board[i][j + 1] = '#';AI_Coordinate(i, j + 1);//将下棋坐标传入AI_Coordinate函数return 's';}if (Board[i][j + 1] == Board[i][j + 2] && Board[i][j + 1] == '#' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数return 's';}}}//2.判断所有列里是否有某列出现  // #   #   空// #   空   #// 空   #   #//以上三种情况,进行智能三连赢棋for (j = 0; j < col; j++){for (i = 0; i < row - 2; i++){if (Board[i][j] == Board[i + 1][j] && Board[i][j] == '#' && Board[i + 2][j] == ' '){Board[i + 2][j] = '#';AI_Coordinate(i + 2, j);//将下棋坐标传入AI_Coordinate函数return 's';}if (Board[i][j] == Board[i + 2][j] && Board[i][j] == '#' && Board[i + 1][j] == ' '){Board[i + 1][j] = '#';AI_Coordinate(i + 1, j);//将下棋坐标传入AI_Coordinate函数return 's';}if (Board[i + 1][j] == Board[i + 2][j] && Board[i + 1][j] == '#' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数return 's';}}}//3.判断一条角线(\)里是否有出现//#           #              空//  #           空              #//    空           #               #//以上三种情况,进行智能赢棋for (i = 0; i < row - 2; i++){for (j = 0; j < col - 2; j++){if (Board[i][j] == Board[i + 1][j + 1] && Board[i][j] == '#' && Board[i + 2][j + 2] == ' '){Board[i + 2][j + 2] = '#';AI_Coordinate(i + 2, j + 2);return 's';}if (Board[i][j] == Board[i + 2][j + 2] && Board[i][j] == '#' && Board[i + 1][j + 1] == ' '){Board[i + 1][j + 1] = '#';AI_Coordinate(i + 1, j + 1);return 's';}if (Board[i + 1][j + 1] == Board[i + 2][j + 2] && Board[i + 1][j + 1] == '#' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);return 's';}}}//4.判断一条角线(/)里是否有出现//       #           #           空//     #           空           #//  空           #            #//以上三种情况,进行智能赢棋for (i = 0; i < row - 2; i++){for (j = col - 1; j > 1; j--){if (Board[i][j] == Board[i + 1][j - 1] && Board[i][j] == '#' && Board[i + 2][j - 2] == ' '){Board[i + 2][j - 2] = '#';AI_Coordinate(i + 2, j - 2);return 's';//返回这个棋子的字符}if (Board[i][j] == Board[i + 2][j - 2] && Board[i][j] == '#' && Board[i + 1][j - 1] == ' '){Board[i + 1][j - 1] = '#';AI_Coordinate(i + 1, j - 1);return 's';//返回这个棋子的字符}if (Board[i + 1][j - 1] == Board[i + 2][j - 2] && Board[i + 1][j - 1] == '#' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);return 's';//返回这个棋子的字符}}}return 'f';
}

(14)AI人工智能堵棋函数

(对玩家的的落子进行分析,实现智能堵棋)(优先级第二),该函数在AI人工智能下棋函数中被调用。
  堵棋的优先度在下新棋之上,在赢棋的优先度之下。

堵棋共有四种堵法:横着堵、竖着堵、左斜堵、右斜堵。
  将堵棋的大致框架写出,并且为了代码的适用性,这里的代码也不要写死,既要适用三行三列的棋盘,也要同样适用于更大的棋盘。(五行五列、十行十列等等)
  当棋盘中不需要堵棋时,返回0;而只要当其中一种情况成立,电脑便落子堵棋,返回1。

//AI人工智能堵棋函数(对玩家的的落子进行分析,实现智能堵棋)(优先级第二) (紫色提示框)
int AI_BlockingChess(char Board[ROW][COL], int row, int col)
{color(5);//紫色int i = 0;int j = 0;//判断所有行里是否有某行出现 XX空 X空X 空XX,三种情况进行智能堵棋for (i = 0; i < row; i++){for (j = 0; j < col - 2; j++){if (Board[i][j] == Board[i][j + 1] && Board[i][j] == 'X' && Board[i][j + 2] == ' '){Board[i][j + 2] = '#';AI_Coordinate(i, j + 2);//将下棋坐标传入AI_Coordinate函数return 1;}if (Board[i][j] == Board[i][j + 2] && Board[i][j] == 'X' && Board[i][j + 1] == ' '){Board[i][j + 1] = '#';AI_Coordinate(i, j + 1);//将下棋坐标传入AI_Coordinate函数return 1;}if (Board[i][j + 1] == Board[i][j + 2] && Board[i][j + 1] == 'X' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数return 1;}}}//判断所有列里是否有某列出现  // X   X   空// X   空   X// 空   X   X//以上三种情况,进行智能堵棋for (j = 0; j < col; j++){for (i = 0; i < row - 2; i++){if (Board[i][j] == Board[i + 1][j] && Board[i][j] == 'X' && Board[i + 2][j] == ' '){Board[i + 2][j] = '#';AI_Coordinate(i + 2, j);//将下棋坐标传入AI_Coordinate函数return 1;}if (Board[i][j] == Board[i + 2][j] && Board[i][j] == 'X' && Board[i + 1][j] == ' '){Board[i + 1][j] = '#';AI_Coordinate(i + 1, j);//将下棋坐标传入AI_Coordinate函数return 1;}if (Board[i + 1][j] == Board[i + 2][j] && Board[i + 1][j] == 'X' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数return 1;}}}//判断一条角线(\)里是否有出现//X           X              空//  X           空              X//    空           X               X//以上三种情况,进行智能堵棋for (i = 0; i < row - 2; i++){for (j = 0; j < col - 2; j++){if (Board[i][j] == Board[i + 1][j + 1] && Board[i][j] == 'X' && Board[i + 2][j + 2] == ' '){Board[i + 2][j + 2] = '#';AI_Coordinate(i + 2, j + 2);//将下棋坐标传入AI_Coordinate函数return 1;}if (Board[i][j] == Board[i + 2][j + 2] && Board[i][j] == 'X' && Board[i + 1][j + 1] == ' '){Board[i + 1][j + 1] = '#';AI_Coordinate(i + 1, j + 1);//将下棋坐标传入AI_Coordinate函数return 1;}if (Board[i + 1][j + 1] == Board[i + 2][j + 2] && Board[i + 1][j + 1] == 'X' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数return 1;}}}//判断一条角线(/)里是否有出现//       X           X           空//     X           空           X//  空           X            X//以上三种情况,进行智能堵棋for (i = 0; i < row - 2; i++){for (j = col - 1; j > 1; j--){if (Board[i][j] == Board[i + 1][j - 1] && Board[i][j] == 'X' && Board[i + 2][j - 2] == ' '){Board[i + 2][j - 2] = '#';AI_Coordinate(i + 2, j - 2);//将下棋坐标传入AI_Coordinate函数return 1;//返回这个棋子的字符}if (Board[i][j] == Board[i + 2][j - 2] && Board[i][j] == 'X' && Board[i + 1][j - 1] == ' '){Board[i + 1][j - 1] = '#';AI_Coordinate(i + 1, j - 1);//将下棋坐标传入AI_Coordinate函数return 1;//返回这个棋子的字符}if (Board[i + 1][j - 1] == Board[i + 2][j - 2] && Board[i + 1][j - 1] == 'X' && Board[i][j] == ' '){Board[i][j] = '#';AI_Coordinate(i, j);//将下棋坐标传入AI_Coordinate函数return 1;//返回这个棋子的字符}}}return 0;
}

(15)先手后手函数

人机对战中选择玩家先手或电脑先手的函数

//人机对战中选择玩家先手或电脑先手的函数
int Xianshou_Houshou()
{int control = 0;while (1){printf(" _____________________________________\n"); Sleep(30);printf("|          1.玩家先手(简单)           |\n");  Sleep(30);printf("|          2.电脑先手(困难)           |\n");  Sleep(30);printf("|_____________________________________|\n"); Sleep(30);printf(" 请选择>:");scanf("%d", &control);while (getchar() != '\n');if (control == 1)return 1;else if (control == 2)return 2;else{printf("选择错误,重新选择");Sleep(750);system("cls");}}
}

效果图展示
在这里插入图片描述

(16)遍历棋盘函数

  智能三子棋游戏中,我们设置人机对战中玩家可以选择电脑先手下棋或玩家先手下棋,如果电脑先手下棋,棋盘开始时应该是空的,所以我们做一个遍历棋盘函数,根据返回值判断电脑第一步下棋时应该落子的坐标,该函数在AI人工智能下棋函数中被调用。

//遍历棋盘判断棋盘是否为空,电脑先手且棋盘为空的情况下,返回e,非空返回n。
char TraverseBoard(char Board[ROW][COL], int row, int col)
{int flag = 0;for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){if (Board[i][j] != ' '){flag = 'n';    //nonempty 非空return flag;}}}return 'e'; //empty 空
}

(17)电脑下棋提示框函数

   玩家下棋回合有玩家提示框,那么电脑下棋也得有电脑下棋提示框,该函数还可以显示电脑下棋的坐标,在智能三连赢棋函数AI_WinChess,智能堵棋函数:AI_BlockingChess, 自由落子函数ComputerMove被调用。

//打印电脑下棋坐标提示框的函数,参数xy为电脑下棋位置 (函数内部不带颜色)
void AI_Coordinate(int x, int y)
{printf(" __________________________________\n");printf("|  电脑回合,棋子‘#’电脑思考中... |\n");printf("|__________________________________|\n");Sleep(100);printf(" __________________________________\n");printf("|       电脑下棋坐标(%d , %d)        |\n", x + 1, y + 1);//将电脑下棋的坐标展示给玩家。展示时横纵坐标+1后展示。printf("|__________________________________|\n");Sleep(400);
}

效果图展示
在这里插入图片描述
在这里插入图片描述

(18)玩家VS玩家下棋函数

   智能三子棋游戏中除了人机对战还有人人对战的功能,所以应该写一个玩家VS玩家下棋函数 ,来实现人人对战。

//玩家VS玩家下棋函数  (玩家一下棋显示淡绿色提示框)(玩家二下棋显示淡蓝色提示框)
void Player1_VS_Player2(char Board[ROW][COL], int row, int col, int x)
{int i = 0, j = 0;//定义了两个整数变量i和j,用于存储玩家输入的坐标。int ch = 0;if (x == 1)//玩家一{color(10);//淡绿色PrintPlayerMove(4);}if (x == 2)//玩家二{color(9);//淡蓝色PrintPlayerMove(5);}while (1)//开始一个无限循环,这样程序会持续等待玩家的输入,直到玩家输入有效的坐标并下棋break。{scanf("%d%d", &i, &j);while ((ch = getchar()) != '\n' && ch != EOF);//对玩家输入数据进行处理,防止字符造成死循环。if ((i >= 1 && i <= row) && (j >= 1 && j <= col))//如果坐标在棋盘范围内(即1到行数和1到列数之间),则执行以下代码:{if (Board[i - 1][j - 1] == ' ')//检查该坐标位置是否为空。如果为空,表示这个位置可以被下棋{if (x == 1)        //人人对战专用,玩家一回合传参x=1{Board[i - 1][j - 1] = 'X';break;//成功下棋后跳出循环}else if (x == 2)   //人人对战专用,玩家二回合传参x=2{Board[i - 1][j - 1] = '#';break;//成功下棋后跳出循环}}else{PrintPlayerMove(2);//坐标被占用提示框}}else//如果坐标不在棋盘范围内,重新输入。{PrintPlayerMove(3);}}
}

效果图展示
在这里插入图片描述

在这里插入图片描述
三子棋的输赢判定方式分为五种,一种是横向三个,一种是纵向三个,一种是右下倾斜(\),一种是左下倾斜(/),还有就是是否平局。也就是棋盘下满且双方皆未达成棋子三连的结果。
将框架写好,随后在for循环的内部加入判定部分即可。

//判断输赢函数
char Judgment(char Board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++)//判断i行里是否有棋子连成一行的情况出现{for (j = 0; j < col - 2; j++){if (Board[i][j] == Board[i][j + 1] && Board[i][j + 1] == Board[i][j + 2] && Board[i][j] != ' ')return Board[i][j];//返回这个棋子的字符}}for (j = 0; j < col; j++)//判断j列里是否有棋子连成一列的情况出现{for (i = 0; i < row - 2; i++){if (Board[i][j] == Board[i + 1][j] && Board[i + 1][j] == Board[i + 2][j] && Board[i][j] != ' ')return Board[i][j];//返回这个棋子的字符}}//判断斜线上是否有三个棋子连线的情况出现 (\)for (i = 0; i < row - 2; i++){for (j = 0; j < col - 2; j++){if (Board[i][j] == Board[i + 1][j + 1] && Board[i + 1][j + 1] == Board[i + 2][j + 2] && Board[i][j] != ' ')return Board[i][j];//返回这个棋子的字符}}//判断斜线上是否有三个棋子连线的情况出现  (/)for (i = 0; i < row - 2; i++){for (j = col - 1; j > 1; j--){if (Board[i][j] == Board[i + 1][j - 1] && Board[i + 1][j - 1] == Board[i + 2][j - 2] && Board[i][j] != ' ')return Board[i][j];//返回这个棋子的字符}}//上述四种情况都未发生的情况下,说明此时无论玩家或电脑双方皆未赢,程序才会运行到此,此时只剩下继续或平局两种情况for (i = 0; i < row; i++)//通过遍历棋盘,判断棋盘上是否已经下满了,以此判断继续还是平局{for (j = 0; j < col; j++){if (Board[i][j] == ' ')//如果有空格,说明没下满,游戏继续。return 'C';//continue}}return 'Q';//五种情况都未发生,平局。
}

(19)人机对战中玩家赢或电脑赢或平局的提示框函数

 无论是哪一方赢,总得有个结果,所以这里我们做一个提示框函数,作为赢方的结束动画展示。

//打印人机对战中玩家赢或电脑赢或平局的提示框(函数内部不带颜色)
void display_win_or_lost(char x)  //参数传w代表win,打印ch1[],参数传l代表lost,打印ch2[],参数传p代表平局,打印ch3[]
{char ch1[] ="__   __                          _\n""\\ \\ / /                         (_)\n"" \\ V /   ___   _   _  __      __ _  _ __\n""  \\ /  /  _ \\ | | | | \\ \\/ \\ / /| || '_ \\ \n""  | |  | (_) || |_| |  \\ V  V / | || | | |\n""  \\_/   \\___/  \\__,_|   \\_/\\_/  |_||_| |_|\n";char ch2[] ="__   __                   _               _\n""\\ \\ / /                  | |            | |  \n"" \\ V /   ___   _   _     | |  ___   ___ | |_\n""  \\ /  /  _ \\ | | | |    | | / _ \\ / __|| __|\n""  | |  | (_)| | |_| |    | | |(_) |\\__ \\| |_ \n""  \\_/   \\___/  \\__,_|    |_| \\___/ |___/ \\__|\n";char ch3[] ="                                                                                     \n""   #################                             \n""           #                 ###############     \n""    #      #      #         ###           ##     \n""    ##     #     ##         ##            ##     \n""    ###    #     ##         ################     \n""     ##    #    ##          ##                   \n""     ###   #   ###          ##                   \n""      ##   #  ###           #################    \n""       ##  # ###            ##             ##    \n""       ##  # ##             ##  #########  ##    \n""        #  #                ##  ##     ##  ##    \n""  ###################       ##  #       #  ##    \n""           #                ##  #       #  ##    \n""           #                ##  #       #  ##    \n""           #                ##  ##     ##  ##    \n""           #                ##  #########  ##    \n""           #               ##            ##      \n""           #               ##       ########     \n""           #             ##       #######        \n";if (x == 'w')//参数传w代表win,打印ch1[]{for (int i = 0; i < strlen(ch1) - 1; i++){printf("%c", ch1[i]);if (ch1[i] == '\n'){Sleep(50);}}}else if (x == 'l')//参数传w代表lost,打印ch2[]{for (int i = 0; i < strlen(ch2) - 1; i++){printf("%c", ch2[i]);if (ch1[i] == '\n'){Sleep(50);}}}else if (x == 'p')//参数传p代表平局,打印ch3[]{for (int i = 0; i < strlen(ch3) - 1; i++){printf("%c", ch3[i]);if (ch3[i] == '\n'){Sleep(10);}}}
}

效果图展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(20)游戏主体函数

这块是游戏的重点部分,上面写的所有函数都是为了这里做准备工作,上面所有的逻辑写好以后,我们就可以编写游戏主体函数了,按照一定次序排列,实现整个下棋逻辑。通过不同的参数判断玩家选择是人机对战或人人对战,玩家电脑依次下棋,直到有一方胜出为止,或玩家一,玩家二依次下棋,直到有一方胜出为止。

void Game(int n)//游戏主体函数
{char ret = 0;                    //用于接收判断输赢函数的返回值,返回的四种字符对应棋局的四种情况,定义在头文件中char Board[ROW][COL] = { 0 };    //定义二维数组InitBoard(Board, ROW, COL);      //初始化二维数组DisplayBoard(Board, ROW, COL);   //打印棋盘while (1)//循环内部为人机双方下棋过程或人人对战过程{if (n == 1)//人机对战玩家先手PlayerMove(Board, ROW, COL);if (n == 2)//人机对战电脑先手ComputerMove(Board, ROW, COL);if (n == 3) //人人对战玩家一Player1_VS_Player2(Board, ROW, COL, 1);ret = Judgment(Board, ROW, COL); //判断输赢if (ret != 'C')                  //等于C棋局继续,不等于C跳出循环,根据ret的值判断输赢{break;}system("cls");                   //确认落子后清屏,再打印棋盘DisplayBoard(Board, ROW, COL);   //打印棋盘if (n == 1)//人机对战电脑后手ComputerMove(Board, ROW, COL);if (n == 2)   //人机对战玩家后手PlayerMove(Board, ROW, COL);if (n == 3)  //人人对战玩家二Player1_VS_Player2(Board, ROW, COL, 2);ret = Judgment(Board, ROW, COL);if (ret != 'C'){break;}system("cls");DisplayBoard(Board, ROW, COL);//打印棋盘}if (n == 1 || n == 2)//人机对战的情况下{if (ret == 'X'){display_win_or_lost('w');}else if (ret == '#'){display_win_or_lost('l');}else{color(0XA);display_win_or_lost('p');}}else    //人人对战的情况下{if (ret == 'X'){printf("\n  玩家一胜利\n");}else if (ret == '#'){printf("\n  玩家二胜利\n");}else{color(0XA);display_win_or_lost('p');}}DisplayBoard(Board, ROW, COL);//打印棋盘
}

(21)游戏说明函数

  菜单函数编写好并且游戏主体函数也编写好后,基本的功能框架算是基本完成了了,接下来我们要做的就是实现菜单函数里所希望实现的其他功能,
先实现菜单函数里的游戏说明功能。

void Introduction()
{printf("\n1.游戏分为人机对战和人人对战,双方依次在9宫格棋盘上摆放棋子。\n");printf("\n2.双方轮流落子,每次只能在一个格子上放置一个棋子。\n");printf("\n3.如果将自己的3个棋子连成一条线,则宣告胜利。连线的方向可以是水平、垂直或对角线。\n");printf("\n4.如果在棋盘上形成平局,即双方都没有机会将自己的3个棋子连成一条线,那么游戏结束。\n\n");
}

(22)开发人员菜单函数

  此函数对应主菜单界面的第4个选项开发人员,介绍了版本号,作者信息(我本人),和制作时间,背景音乐等信息,该函数可以控制程序内的音乐打开或关闭。

//开发人员菜单函数(开发人员信息和背景音乐控制)
void kaifa()
{int control = 0;do{system("color B0");printf(" ________________________________________________________\n"); Sleep(30);printf("| 智能三子棋版本v1.1       作者   --->原始豌豆           |\n");  Sleep(30);printf("|                                                        |\n");  Sleep(30);printf("| 制作时间 2024.2.11        音乐   --->纯音乐            |\n");  Sleep(30);printf("|________________________________________________________|\n"); Sleep(30);printf("**************1.开启音乐    0.关闭音乐********************\n");printf("**************      2.回到主界面     *********************\n");printf("请选择>:");scanf("%d", &control);while (getchar() != '\n');switch (control){case 1:BackgroundMusic('s');break;case 0:BackgroundMusic('e');break;case 2:system("cls");//回到主界面前先清屏break;default:printf("\n          选择错误,请重新输入!");system("pause");//界面美化---->控制台停留,实现按任意键继续的效果system("cls"); //界面美化---->清屏,防止玩家多次输入错误造成屏幕占满		break;}} while (control != 2);
}

(23)退出游戏函数

  此函数对应主菜单界面的第5个选项退出游戏,在玩家退出游戏时打印由符号组成的退出两个汉字。

//打印退出游戏字幕
void GameOver()
{char str[] = "                                                                                           \n""    #      ############                    ##                  \n""   ###    ##############                   ##                  \n""   ###    ###        ###         ##        ##        ##\n""    ###   ##          ##         ##        ##        ##\n""     ##     ##            ##          ##        ##         ##\n""     ###  ##############         ##        ##        ##\n""      ##  ##############         ##        ##        ##\n""          ##          ##         ##        ##        ##\n""          ##          ##         ##        ##        ##\n"" ######   ##############         ##        ##        ##\n""  ######  ##############         ###       ##        ##\n""      ##  ######                  #####################\n""      ##  ## ####      ##         #################### \n""      ##  ##  ####    ###        #         ##         #\n""      ##  ##   ####  ####       ###        ##         ##\n""      ##  ##    #######         ###        ##         ##\n""      ##  ##      ####          ###        ##         ##\n""      ##  ##       ###          ###        ##         ##\n""      ##  ##  ###   ###         ###        ##         ##\n""      ##  ## ####    ####       ###        ##         ##\n""     ###  ######      ###       ###        ##         ##\n""    ##### ####         ###      ###        ##         ##\n""   #########                    ###        ##         ##\n"" ##### ###################       #######################\n"" ####    ##################       #####################\n\n";for (int i = 0; i < strlen(str) - 1; i++){printf("%c", str[i]);if (str[i] == '\n'){Sleep(10);}}
}

(24)主函数

上述函数都写好后,在主函数中调用各个函数,即可完成智能三子棋游戏游戏的制作

int main()
{int n = 0;ConsoleSet();//控制台设置BackgroundMusic('s');srand((unsigned int)time(NULL));//生成随机数生成起点//time函数参数是指针,返回值是time_t类型,而srand的参数是无符号整型,所以强制类型转换一下。system("color 02");sanziqi('p');Sleep(2000);sanziqi('c');int input = 0;do {player_vs_computer();color(14);//将菜单界面设置为亮黄色。Menu();//打印菜单scanf("%d", &input);while (getchar() != '\n');system("cls");switch (input){case 1:n = Xianshou_Houshou();Game(n);   //gamegoto_xy(50, 12);system("pause");Sleep(1000);system("cls");break;case 2:Game(3);goto_xy(50, 12);system("pause");Sleep(1000);system("cls");break;case 3:system("color 70");Introduction();system("pause");system("cls");system("color 0E");break;case 4:kaifa();system("cls");system("color 0E");break;case 5:color(8);GameOver();break;default:goto_xy(64, 16);//将输入处的光标定位在括号里面printf("选择错误,重新选择");Sleep(800);system("cls");}} while (input != 5);//当input==5时退出游戏return 0;
}

六、全部源码

后续会发出来,敬请期待

七、项目总结

  经过多次测试和调整,我们的AI在三子棋游戏中展现出了较高的智能水平。它能够在电脑先手的大多数情况下战胜人类玩家,并且具有一定的战术意识和预判能力。虽然我们的AI在三子棋游戏中已经取得了一定的成绩,但仍有很大的提升空间。未来,我们可以进一步优化评估函数和搜索算法,提高AI的智能水平和对抗性。同时,也可以尝试将本项目应用到其他类似的棋类游戏中,以检验算法的通用性和可扩展性。

全文完,感谢观看!

相关文章:

人工智能三子棋-人机对弈-人人对弈,谁会是最终赢家?

✅作者简介&#xff1a;大家好我是原始豌豆&#xff0c;感谢支持。 &#x1f194;本文由 原始豌豆 原创 CSDN首发&#x1f412; 如需转载还请通知⚠ &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​ &#x1f4e3;系列专栏&#xff1a;C语言项目实践…...

【leetcode热题100】反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;[1,4,3,2…...

谷歌 DeepMind 联合斯坦福推出了主从式遥操作双臂机器人系统增强版ALOHA 2

谷歌 DeepMind 联合斯坦福推出了 ALOHA 的增强版本 ——ALOHA 2。与一代相比&#xff0c;ALOHA 2 具有更强的性能、人体工程学设计和稳健性&#xff0c;且成本还不到 20 万元人民币。并且&#xff0c;为了加速大规模双手操作的研究&#xff0c;ALOHA 2 相关的所有硬件设计全部开…...

金融行业专题|证券超融合架构转型与场景探索合集(2023版)

更新内容 更新 SmartX 超融合在证券行业的覆盖范围、部署规模与应用场景。新增操作系统信创转型、Nutanix 国产化替代、网络与安全等场景实践。更多超融合金融核心生产业务场景实践&#xff0c;欢迎阅读文末电子书。 在金融行业如火如荼的数字化转型大潮中&#xff0c;传统架…...

【C语言】C的整理记录

前言 该笔记是建立在已经系统学习过C语言的基础上&#xff0c;笔者对C语言的知识和注意事项进行整理记录&#xff0c;便于后期查阅&#xff0c;反复琢磨。C语言是一种面向过程的编程语言。 原想在此阐述一下C语言的作用&#xff0c;然而发觉这些是编程语言所共通的作用&#…...

使用STM32Cubemx创建一个工程并且给出每一步的含义

...

C/C++模板初阶

目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.1 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int&…...

linux系统下vscode portable版本的c++/Cmake环境搭建001

linux系统下vscode portable版本的Cmake环境搭建 vscode portable 安装安装基本工具安装 build-essential安装 CMake final script code安装插件CMake Tools & cmakeC/C Extension Pack Testsettings,jsonCMakeLists.txt调试和运行工具 CG 目的&#xff1a;希望在获得一个新…...

【QT+QGIS跨平台编译】之三十一:【FreeXL+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、FreeXL介绍二、文件下载三、文件分析四、pro文件五、编译实践一、FreeXL介绍 【FreeXL跨平台编译】:Windows环境下编译成果(支撑QGIS跨平台编译,以及二次研发) 【FreeXL跨平台编译】:Linux环境下编译成果(支撑QGIS跨平台编译,以及二次研发) 【FreeXL跨平台…...

2024年 前端JavaScript入门到精通 第一天

主要讲解JavaScript核心知识&#xff0c;包含最新ES6语法&#xff0c;从基础到API再到高级。让你一边学习一边练习&#xff0c;重点知识及时实践&#xff0c;同时每天安排大量作业&#xff0c;加深记忆&#xff0c;巩固学习成果。 1.1 基本软件与准备工作 1.2 JavaScript 案例 …...

155基于matlab 的形态学权重自适应图像去噪

基于matlab 的形态学权重自适应图像去噪&#xff1b;通过串并联的滤波降噪对比图&#xff0c;说明并联降噪的优越性。输出降噪前后图像和不同方法的降噪情况的信噪比。程序已调通&#xff0c;可直接运行。 155matlab 自适应图像降噪 串并联降噪 (xiaohongshu.com)...

操作系统——内存管理(附带Leetcode算法题LRU)

目录 1.内存管理主要用来干什么&#xff1f; 2.什么是内存碎片&#xff1f; 3.虚拟内存 3.1传统存储管理方式的缺点&#xff1f; 3.2局部性原理 3.3什么是虚拟内存&#xff1f;有什么用&#xff1f; 3.3.1段式分配 3.3.2页式分配 3.3.2.1换页机制 3.3.2.2页面置换算法…...

I/O多路复用简记

IO多路复用&#xff08;服务器如何处理多个socket的同时数据传输&#xff09;&#xff1a;1、select。2、poll。3、epoll。 select使用bitmap存socket文件描述符&#xff0c;由bitmap槽位的每一位为0或1决定对应序的socket连接是否有数据到来。由单线程&#xff08;多线程处理每…...

SPECCPU2017操作说明

1、依赖包下载 yum install gcc* gfortran* 2、将软件包放至被测机器 3、增加权限 chmod X install.sh 4、运行安装 ./install.sh 5、运行 引入编译时所需的环境变量和相关库文件 source shrc 进入/spec2017&#xff0c;执行 ./runcpu -c ../config/Example-gcc-linux-ar…...

openresty (nginx)快速开始

文章目录 一、什么是openresty&#xff1f;二、openresty编译安装1. 编译安装命令1.1 编译完成后路径1.2 常用编译选项解释 2. nginx配置文件配置2.1 nginx.conf模板 3. nginx常见配置一个站点配置多个域名nginx配置中location匹配规则 三、OpenResty工作原理OpenResty工作原理…...

相机图像质量研究(11)常见问题总结:光学结构对成像的影响--像差

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…...

【深度学习】基于多层感知机的手写数字识别

案例2&#xff1a;构建自己的多层感知机: MNIST手写数字识别 相关知识点: numpy科学计算包&#xff0c;如向量化操作&#xff0c;广播机制等 1 任务目标 1.1 数据集简介 ​ MNIST手写数字识别数据集是图像分类领域最常用的数据集之一&#xff0c;它包含60,000张训练图片&am…...

给定n,m(200),构造一个n*m的矩阵a,使得每个4*4的子矩阵,左上角2*2的子矩阵的异或和等于右下角的,左下角的异或和等于右上角的

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18 5, maxm 4e4 5, mod 998244353…...

【开源】基于JAVA+Vue+SpringBoot的假日旅社管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统介绍2.2 QA 问答 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿评论4.3 查询民宿新闻4.4 新建民宿预订单4.5 查询我的民宿预订单 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的假日旅社…...

kafka 文件存储机制

文章目录 1. 思考四个问题&#xff1a;1.1 topic中partition存储分布&#xff1a;1.2 partiton中文件存储方式&#xff1a;1.3 partiton中segment文件存储结构&#xff1a;1.4 在partition中如何通过offset查找message: 2. kafka日志存储参数配置 Topic是逻辑上的概念&#xff…...

引入BertTokenizer出现OSError: Can‘t load tokenizer for ‘bert-base-uncased‘.

今天在跑一个模型的时候出现该报错&#xff0c;完整报错为&#xff1a; OSError: Cant load tokenizer for bert-base-uncased. If you were trying to load it from https://huggingface.co/models, make sure you dont have a local directory with the same name. Otherwis…...

陶陶摘苹果C++

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; int main(){//一、分析问题//已知&#xff1a;10 个苹果到地面的高度a[10],陶陶把手伸直的时候能够达到的最大高度height//未知&#xff1a;陶陶能够摘到的苹果的数目sum。//关系&#xff…...

STM32F1 引脚重映射功能

STM32 端口引脚重映射 文章目录 STM32 端口引脚重映射前言1、查阅芯片数据手册1.1 串口引脚重映射描述 2、代码部分2.1 核心代码部分 3、实验现象4、总结 前言 在写程序时遇到想要的端口功能&#xff0c;而这个引脚又被其它的功能占用了无法删除掉或直接使用&#xff0c;这种情…...

c语言的各类输出函数(带完善更新)

printf double x; x 218.82631; printf("%-6.2e\n", x);printf(“%-6.2e\n”, x);使用printf函数以指定的格式输出x的值。"%-6.2e"是格式化字符串&#xff0c;其中&#xff1a; %e表示以科学计数法的形式输出浮点数。 6表示输出的总宽度为6个字符&#…...

【linux温故】CFS调度

写在前面 网上关于CFS 调度器的文章多如牛毛&#xff0c;没必要自己写。很多文章写的都非常好。 很多文章里&#xff0c;关键的技术点&#xff0c;都是一样的&#xff0c;只是各个文章说法不一样。 掌握了核心的&#xff0c;关键的&#xff0c;其他的&#xff0c;如果工作中…...

计算机网络之一

目录 1.因特网概述 1.1网络、互连网&#xff08;互联网&#xff09;和因特网 1.2.因特网发展的三个阶段 1.3基于ISP的三层架构的因特网 1.4.因特网的组成 2.三种交换方式 2.1电路交换 2.2分组交换 1.因特网概述 1.1网络、互连网&#xff08;互联网&#xff09;和因特网…...

从一到无穷大 #23 《流计算系统图解》书评

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言内容总结 引言 春节假期回到家里断然是不会有看纸质书的时间的。造化弄人&#…...

华为问界M9:领跑未来智能交通的自动驾驶黑科技

华为问界M9是一款高端电动汽车&#xff0c;其自动驾驶技术是该车型的重要卖点之一。华为在问界M9上采用了多种传感器和高级算法&#xff0c;实现了在不同场景下的自动驾驶功能&#xff0c;包括自动泊车、自适应巡航、车道保持、自动变道等。 华为问界M9的自动驾驶技术惊艳之处…...

Java图形化界面编程——弹球游戏 笔记

Java也可用于开发一些动画。所谓动画&#xff0c;就是间隔一定的时间(通常小于0 . 1秒 )重新绘制新的图像&#xff0c;两次绘制的图像之间差异较小&#xff0c;肉眼看起来就成了所谓的动画 。 ​ 为了实现间隔一定的时间就重新调用组件的 repaint()方法&#xff0c;可以借助于…...

浅谈人工智能之深度学习~

目录 前言&#xff1a;深度学习的进展 一&#xff1a;深度学习的基本原理和算法 二&#xff1a;深度学习的应用实例 三&#xff1a;深度学习的挑战和未来发展方向 四&#xff1a;深度学习与机器学习的关系 五&#xff1a;深度学习与人类的智能交互 悟已往之不谏&#xff0…...

【复现】大华 DSS SQL 注入漏洞_46

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 大华DSS是大华的大型监控管理应用平台&#xff0c;支持几乎所有涉及监控等方面的操作&#xff0c;支持多级跨平台联网等操作。 可…...

Python 中的断点类型详解

前言 在 Python 中&#xff0c;断点是一种在代码中设置的标记&#xff0c;用于在程序执行过程中停止或中断程序的执行&#xff0c;以便调试和查看程序的内部状态。断点是调试工具的关键组成部分&#xff0c;能够帮助开发者定位和解决代码中的错误。本文将详细介绍 Python 中的…...

一步步建立一个C#项目(连续读取S7-1200PLC数据)

这篇博客作为C#的基础系列,和大家分享如何一步步建立一个C#项目完成对S7-1200PLC数据的连续读取。首先创建一个窗体应用。 1、窗体应用 2、配置存储位置 3、选择框架 拖拽一个Button,可以选择视图菜单---工具箱 4、工具箱 拖拽Lable控件和TextBook控件 5、拖拽控件 接下来…...

Hive窗口函数详解

一、 窗口函数知识点 1.1 窗户函数的定义 窗口函数可以拆分为【窗口函数】。窗口函数官网指路&#xff1a; LanguageManual WindowingAndAnalytics - Apache Hive - Apache Software Foundationhttps://cwiki.apache.org/confluence/display/Hive/LanguageManual%20Windowing…...

车载电子电器架构 —— 电子电气系统功能开发

车载电子电器架构 —— 电子电气系统功能开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再挣扎,出门靠自己,四海皆…...

LeetCode--代码详解 7.整数反转

7.整数反转 题目 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 …...

《统计学简易速速上手小册》第6章:多变量数据分析(2024 最新版)

文章目录 6.1 主成分分析&#xff08;PCA&#xff09;6.1.1 基础知识6.1.2 主要案例&#xff1a;客户细分6.1.3 拓展案例 1&#xff1a;面部识别6.1.4 拓展案例 2&#xff1a;基因数据分析 6.2 聚类分析6.2.1 基础知识6.2.2 主要案例&#xff1a;市场细分6.2.3 拓展案例 1&…...

创新S3存储桶检索:Langchain社区S3加载器搭载OpenAI API

在瞬息万变的数据存储和处理领域&#xff0c;将高效的云存储解决方案与先进的 AI 功能相结合&#xff0c;为处理大量数据提供了一种变革性的方法。本文演示了使用 MinIO、Langchain 和 OpenAI 的 GPT-3.5 模型的实际实现&#xff0c;重点总结了存储在 MinIO 存储桶中的文档。 …...

【Linux技术宝典】Linux入门:揭开Linux的神秘面纱

文章目录 官网Linux 环境的搭建方式一、什么是Linux&#xff1f;二、Linux的起源与发展三、Linux的核心组件四、Linux企业应用现状五、Linux的发行版本六、为什么选择Linux&#xff1f;七、总结 Linux&#xff0c;一个在全球范围内广泛应用的开源操作系统&#xff0c;近年来越来…...

C语言---------对操作符的进一步认识

操作符中有⼀些操作符和⼆进制有关系&#xff0c;我们先学习了⼀下⼆进制的和进制转换的知识。 1.原码、反码和补码。 有符号整数的三种表⽰⽅法均有符号位和数值位两部分&#xff0c; 2进制序列中&#xff0c;最⾼位的1位是被当做符号位&#xff0c;剩余的都是数值位。 符号…...

HarmonyOS 鸿蒙 ArkTS ArkUI 页面之间切换转换动画设置

第一步&#xff1a;导入 import promptAction from ohos.promptAction 第二步&#xff1a;在build下方写入 pageTransition(){PageTransitionEnter({ duration: 1200 }).slide(SlideEffect.Right)PageTransitionExit({ delay: 100 }).translate({ x: 100.0, y: 100.0 }).opac…...

《CSS 简易速速上手小册》第8章:CSS 性能优化和可访问性(2024 最新版)

文章目录 8.1 CSS 文件的组织和管理8.1.1 基础知识8.1.2 重点案例&#xff1a;项目样式表结构8.1.3 拓展案例 1&#xff1a;使用BEM命名规范8.1.4 拓展案例 2&#xff1a;利用 Sass 混入创建响应式工具类 8.2 提高网页加载速度的技巧8.2.1 基础知识8.2.2 重点案例&#xff1a;图…...

Peter算法小课堂—背包问题

我们已经学过好久好久的动态规划了&#xff0c;动态规划_Peter Pan was right的博客-CSDN博客 那么&#xff0c;我用一张图片来概括一下背包问题。 大家有可能比较疑惑&#xff0c;优化决策怎么优化呢&#xff1f;答案是&#xff0c;滚动数组&#xff0c;一个神秘而简单的东西…...

网易腾讯面试题精选----50 个 Git 面试问题

介绍 Git 是 DevOps 之旅的起点。所以,我只是概述了 50 个快速问题以及 Git 的答案。这些问题非常快,你可以在 DevOps 面试中问。它适合初学者到中级水平。 面试问答 1.问:什么是Git? 答:Git 是一个分布式版本控制系统,允许多个开发人员在一个项目上进行协作并跟踪源代…...

Android CMakeLists.txt语法详解

一.CMake简介 你或许听过好几种 Make 工具&#xff0c;例如 GNU Make &#xff0c;QT 的 qmake &#xff0c;微软的 MSnmake&#xff0c;BSD Make&#xff08;pmake&#xff09;&#xff0c;Makepp&#xff0c;等等。这些 Make 工具遵循着不同的规范和标准&#xff0c;所执行的…...

Vue3快速上手(二)VSCode官方推荐插件安装及配置

一、VSCode官方插件安装&#xff0c;如下图2款插件 在用vite创建的程序里&#xff0c;提示提安装推荐的插件了&#xff0c;如下图&#xff1a; 二、配置 在设置-扩展里找到Volar插件&#xff0c;将Dot Value勾选上。这样在ref()修改变量时&#xff0c;会自动填充.value,无需…...

等保2、3级所需设备

三级等保要求及所需设备 《等级保护基本要求》所需设备 结构安全&#xff08;G3&#xff09; b)应保证网络各个部分的宽带满足业务高峰期需要&#xff1b; g)应按照对业务服务的需要次序来指定宽带分配优先级别&#xff0c;保证在网络发生拥堵的时候优先保护重要主机 负载均衡…...

6 scala-面向对象编程基础

Scala 跟 Java 一样&#xff0c;是一门面向对象编程的语言&#xff0c;有类和对象的概念。 1 类与对象 与 Java 一样&#xff0c;Scala 也是通过关键字 class 来定义类&#xff0c;使用关键字 new 创建对象。 要运行我们编写的代码&#xff0c;同样像 Java 一样&#xff0c;…...

【linux温故】linux调度机制

假如你是设计者&#xff0c;你会设计怎样的调度机制呢&#xff1f; 时间片 最简单的&#xff0c;小学生都能想出来的一种&#xff0c;每个 ready task&#xff0c;按照一个固定的时间片轮流执行。 大家不要抢&#xff0c;挨个儿排队执行。执行完时间片&#xff0c;就排在后面…...

django中如何使用mysql连接池

一&#xff1a;介绍 在Django中使用MySQL时&#xff0c;通常情况下&#xff0c;Django的数据库层会为你管理数据库连接。Django的数据库接口是线程安全的&#xff0c;这意味着它会自动为每个线程创建和管理数据库连接。在大多数情况下&#xff0c;你不需要手动创建线程池来管理…...