文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

效果最接近《羊了个羊》(卡牌堆叠游戏)的开源代码

2023-09-06 11:32

关注

⭐零、教程概述

效果最接近《羊了个羊》(卡牌堆叠游戏)的开源代码,有数据库和关卡。
我写的程序是指 卡牌堆叠游戏 ,效果与羊了个羊一致。本教程有两个版本
PHP 使用 PHP + H5 + CSS + JS + MySql 实现。
H5 使用 H5 + CSS + JS 实现 。

⭐零·壹、代码获取

MieGame_C_Core 代码链接:
Github:https://github.com/MR-XieXuan/MieGame_C_Core
CandyMieGame_PHP(注意获取V0.0.0版本的代码):
 Github: https://github.com/MR-XieXuan/CandyMieGame_PHP
CandyMieGame_H5(注意获取V0.0.0版本的代码):
 Github: https://github.com/MR-XieXuan/CandyMieGame_H5
微信小程序版本 :
https://blog.csdn.net/apple_53792700/article/details/128041151

目录

一 、⭐逻辑的实现

♾️1.1 C语言的内核实现

逻辑实现我最先是使用 C++ 进行的,后面迁移到了 JS 。C++ 实现的牌堆代码已开源,并且注释写的也很明确,有问题可以在网站底部联系我,我很乐于回答你们的问题。
好的,回到问题。我的牌堆实现思路如下:

  1. n 种牌
  2. z 张相同的牌可以被抵消
  3. 共有 N = kz (k属于正整数) 张牌
  4. 选中格有 F 格,满了即被消除
  5. 牌被分为 亮牌(可选) 暗牌(不可选)
  6. 每张牌都有唯一编号 1 - N
  7. 从1号牌开始放牌,大号周围 x 距离内,小号牌为暗牌 牌的大小为 2x
  8. 所有牌都被消除即为游戏胜利
  9. 牌的存储形式为 ( x,y,t,a ) [位置与类型与亮暗]
  10. 没有被消除的牌被标记为1,被消除的牌被标记为0,以bitmap的形式存储

有些规则可能后面我会有所改动,但是我都会一一讲解。实在有疑问也可以联系我。
MieGame_C_Core 代码链接: https://github.com/MR-XieXuan/MieGame_C_Core
不要忘记了 StarFork 哦,还有关注和爱心。

MieGame 方法定义 :

class MieGame {    public :        // 放置一张牌        uint8_t place_a_poker(int x, int y, uint8_t type);        // 拿走一张牌        uint8_t takeaway_a_poker(int number);   // 通过编号拿走        uint8_t takeaway_a_poker(int x, int y); // 通过位置拿走        // 更新亮暗状态         void refresh_poker_a();        // 获取亮暗状态         uint8_t get_poker_a(int n);        uint8_t get_poker_x(int n);        uint8_t get_poker_y(int n);        uint8_t get_poker_t(int n);    private:        int amount = 0; // 牌的总数        uint8_t pokerBitmap[63] = {0}; // 最多容纳 504 张牌        int pokerPosition[504][4] = {0}; // [牌号][位置x,位置y,类型t,亮暗a]        char Get_bit( uint8_t * st , char num ){            return ( st[(int)(num/8)] & (0x01 << (num % 8) ) ) > 0 ? 1 : 0 ;        }        void Set_bit( uint8_t * st , char num ){            st[(int)(num/8)] |= (0x01 << (num % 8) ) ;        }        void Res_bit( uint8_t * st , char num ){            st[(int)(num/8)] &= ~(0x01 << (num % 8) ) ;        }};

♾️1.2变量以及方法介绍

♾️变量

int amount

牌的总数 , 每放置一张牌都会加一,但是拿走一张牌却不会减。

pokerBitmap[63]

bitmap 牌是否被拿走,是一个bitmap列表,被拿走为0,没有被拿走为1

pokerPosition[504][4]

牌的信息 [牌号][位置x位置y类型t,亮暗a]

place_a_poker 放一张牌

uint8_t place_a_poker(int x, int y, uint8_t type);
在指定位置放置一张指定类型的卡牌

♾️方法

takeaway_a_poker

uint8_t takeaway_a_poker(int number);

编号的形式拿走一张牌,返回值是 。。。好像没有写,到时候更新。

uint8_t takeaway_a_poker(int x, int y);

位置的形式拿走一张牌,比如一张牌大小为 4*4 位置在 (2,2) 则输入的是(1,1)也可以拿走这张卡牌,只要卡牌覆盖了这个位置且卡牌为亮牌。返回值为这张牌的类型,如果没有拿走牌,则返回 0 。

refresh_poker_a

void refresh_poker_a();

刷新牌堆的亮暗状态。

get_poker_x x:[a,x,y,t]

uint8_t get_poker_a(int n);
uint8_t get_poker_x(int n);
uint8_t get_poker_y(int n);
uint8_t get_poker_t(int n);

获取 编号为 n 的牌的 a(亮暗状态) x(x位置) y(y位置) t(Type类型)

Get_bit Set_bit Res_bit

位操作,通过位操作来操作与获取bitmap的内容 ,Get是获取指定位 ,Set是把指定位变为1,Res是把指定位变为0。这是一个很简单的操作,基础不好的可以多看几遍。也不是太难理解的,我这里讲解一下 Res_bit 的实现,看懂了Res_bit 其他的都很好理解。

void Res_bit( uint8_t * st , char num ){            st[(int)(num/8)] &= ~(0x01 << (num % 8) ) ;}

Res_bit 需要传入两个参数,第一个是stbitmap的首位地址,第二个num是需要操作的位置。
我们假设我们的num是12 st传入了一个bitmap,数据为 0b1011111111111111 是从低位往高位读,不要读反了(从右往左)。
从右往左分析,0x01 也就是 16进制的1,<< 左移 , (num%8) 操作的位置除以8取余数也就是 4 。 0x01<<(num%8) 也就是1<<4 也就是二进制的00010000(现在计算机最小的单位是8个字一个字节,也就是8位二进制),~ 取反 也就变成了11101111

st[(int)(num/8)] C语言强制类型转换是去掉小数部分,只保留整数部分的,也就是说(int)(12/8)就为 1 。 把 st[1] &= 0b11101111 (0b是二进制的意思,但是一般编程语言不支持直接这样子操作二进制)展开也就是 st[1] = st[1] & 0b11101111
也就是让 0b101111110b11101111 做并操作 得 st[1] = 0b10101111
最后结果就是 传入的这个bitmap从 0b1011111111111111 变成了 0b1010111111111111 第 12位变成了0。

更多关于Bitmap的可以去我的主页搜索Bitmap获取

二、⭐配置CandyMieGame并运行代码

♾️2.1.1 CandyMieGame_PHP

你需要再你的服务器中创建 Mysql 库 ,库的结构在 文件 SQL.md 中。
并且在文件index.php中填写入你访问数据库的账号与密码

$sqlname = "";$sqlpassword = "";

并且运行文件 SQL.md中的命令。

♾️2.1.2 CandyMieGame_H5

♾️Windows Chrome 谷歌浏览器

打开index.html文件后,打开开发者工具,并且切换到设备仿真模式选中iphone SE,然后刷新。
如果你的控制台输出了 :
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. at cut_img_loding
请使用 file协议 再次打开浏览器 。
使用file协议打开浏览器的方式如下:
CMD 运行

chrome.exe --allow-file-access-from-files index.html

chrome.exe 替换成自己电脑中 chrome.exe 的路径 ,index.html 替换成自己下载好后 CandyMieGame_H5 中index.html 中的路径 。

♾️2.2.添加关卡

♾️3.2.1 CandyMieGame_PHP

在写完地图(json文件)后,把文件放在map\level\文件夹下并且
需要在数据库 miegame 中表 level 中添加字段 代码如下:

INSERT INTO `level` (`id`, `address`, `level`, `trytasnum`, `tasnum`) VALUES ([第几关], [json存放路径], 4, 0, 0);

♾️3.2.2 CandyMieGame_H5

在写完地图(json文件)后,把文件放在map\level\文件夹下
命名为 【level】 + 【第几关】+ 【.json】
修改setup.js文件中window.level_amount的值为最新的关卡数。

♾️3.运行程序

直接访问程序即可,此地址:CandyMieGame: https://game.mrxie.xyz/
我服务器用的CF的CDN,所以速度较慢,且配置拉跨。首次加载时间较长。需要等待一会儿。
(PS:我经常需要加载25s左右)
电脑请切换到 设备仿真模式 iphone SE 下使用。

三、⭐JS 游戏的逻辑实现

♾️3.1MieGamePoker 对象

MieGamePoker主要控制牌堆的处理逻辑。
该对象内的成员变量有:MIEGAME_POKERAMOUNT_MAX牌堆的最大容量,POKERTYPE_AMOUNT现牌堆有牌类型的数量,POKER_REMOVE_NUM已经被取走的牌的数量,POKER_SIZE扑克牌在地图中的边长(正方形),amount现牌堆有牌类型的数量,residue除去被被拿走的,剩余牌的数量,pokerBitmap牌是否被拿走的列表(bitmap),pokerPosition牌的信息。
该对象内的方法有:
place_a_poker放置牌,takeaway_a_poker拿走牌,refresh_poker_a刷新牌堆亮暗信息,get_poker_a获取牌的亮暗信息,get_poker_x获取牌的x位置,get_poker_y获取牌的y位置,get_poker_t获取牌的类型,get_poker_have获取牌是否被拿走,Set_bitBitmap置位1,Res_bitBitmap置位0,Get_bit获取Bitmap的位。
其中外部主要用到的方法有前3个,分别为place_a_poker放置牌,takeaway_a_poker拿走牌,refresh_poker_a刷新牌堆亮暗信息。
具体的实现代码可以查看miegame.js文件。

♾️3.2MieGameCan 对象

MieGamePoker主要控制消除区的处理逻辑。
该对象内的成员变量有:
pokerMap现在消除区有的牌列表,pokerAmount现在消除区有的牌的数量。
该对象内的方法有:
place_a_poker放置一张牌到消除区,eliminate_pokers对消除区的牌进行消除,get_poker_fall获取消除区的牌是否已经堆满。
其中外部主要用到的方法有前3个,分别为place_a_poker放置牌,takeaway_a_poker拿走牌,refresh_poker_a刷新牌堆亮暗信息。
具体的实现代码可以查看miegame.js文件。

四、⭐游戏界面层设计

♾️4.1、index.html

整个游戏都在一个 convas 画布上工作,要让这个画布充满屏幕,就应该把body的边框全部设为0;

<body onload="" onclick="" onmousemove="" style="border: 0; margin: 0; touch-action: none;">    <div id="canvas_div">    div>body>

♾️miegame.index 解说

CandyMieGame
CandyMieGame 游戏界面(左图)分为 5 个区,分别为 head头 , map地图区fre释放牌区props 道具区 。 因为用户设备的屏幕大小不确定,所以我们的布局也要按照用户的设备来自适应。

if (wh >= 0.45 && wh <= 0.5){window.headerSize = [window.prW * 0.9, window.prW * 0.1];window.headerPosition = [window.prW * 0.05, window.prW * 0.05 + prH * 0.05];window.mapSize = parseInt((prW) * 0.9);window.mapPosition = [parseInt((prW - mapSize) / 2), parseInt((headerPosition[1] + headerSize[1]) + prH * 0.03)];window.frePosition = [parseInt(mapPosition[0]), parseInt((mapPosition[1] + mapSize) + prH * 0.03)];window.freSize = [parseInt(mapSize), parseInt(prH * 0.1)];window.canPosition = [parseInt(0), parseInt((frePosition[1] + freSize[1]) + prH * 0.02)];window.canSize = [parseInt(mapSize), parseInt(mapSize / 7)];window.canPosition[0] = (parseInt(prW - window.canSize[0]) / 2);window.propsPosition = [parseInt(mapPosition[0]), parseInt((canPosition[1] + canSize[1]) + prH * 0.03)];window.propsSize = [parseInt(mapSize), parseInt(prH * 0.1)];}

在程序的最开始就按照用户操作界面的chan宽比开始分配这些区应该在哪些位置,大小应该是多少。
如下图,在长宽比不同时,各个区会以不同的方式展示在用户的屏幕上,这样子不仅可以保证用户良好观感外还能改善用户的操作体验,比如细长的屏幕就要将地图放的更靠下。方形的屏幕可以将道具区放到右边,释放牌区放到左边。
不同设备上界面都不同
拥有各个区的大小以后,我们要将用户界面和游戏地图联系起来,游戏地图大小为(12*12), 而用户界面的大小却为 (420,420).所以我们需要创建一个方法来建立游戏界面与用户界面的联系

window.communicate = new CanvasCommunicatGame(window.gameJson.mapSizeW, window.gameJson.mapSizeH, mapSize, mapSize);

CanvasCommunicatGame 对象我定义在文件 miegame.js 中 。
里面共有4个方法。cw_to_gw界面x转游戏x,ch_to_gh界面y转游戏y,gw_to_cw游戏x转界面x,gh_to_ch游戏y转界面y。
有4个方法以后就可以轻易的让用户界面与游戏核心建立联系,。
在设计时,设置的用户放地图的界面为正方形的,所以地图设计也应该为正方形的,这样子打印出来以后,地图才不会变型。

miegame_put_background(); 函数会将除游戏以外的元素展现到用户的界面上。再把地图打印在应有的位置上。同时,箭头用户鼠标按下的动作,并且对应实现操作。

document.getElementById("botton_canvas").addEventListener("click", function (event) {        get_mouse_pos(document.getElementById("botton_canvas"), event);    });

这个版本的程序并没有加入过多动画,如果需要加入动画的话,就应该实时刷新画布,而不是在用户有操作后再刷新画布。

五、⭐服务层的设计

实际上我这个服务层的设计是草草了事的,很多东西都没有做。这里主要讲解一下 PHP 与 MySQL 数据库的交互。
服务器获取Post表单的数据,ask为必须要有的数据 , 他表示现在如果没有 就默认为 ask = "main" 处理。
askmain时返回地区的星星数量。为level时为关卡地图请求。为newgame时为新游戏请求。

♾️5.1 PHP 获取请求者的IP地址 并且 获取他所在的地区

♾️5.1.1获取IP地址

如果你的服务器没有经过DNS服务,那么用最简单的方法就可以了。$ip = $_SERVER['REMOTE_ADDR']但是我的服务器经过了CloudFlare简称CF的DNS服务。那么久不可以通过上面的那个代码来获取用户的IP地址了,否者获取到的IP地址就是CF的DNS服务器的地址了。因为CF的IP报文可能不一定,Key有几种可能,所以我们为了使用方便,封装成了一个函数:

function GET_IP(){    global $ip;    if ($_SERVER['HTTP_CF_CONNECTING_IP'])        $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];    else if ($_SERVER['REMOTE_ADDR'])        $ip = $_SERVER['REMOTE_ADDR'];    else if ($_SERVER['HTTP_X_FORWARDED_FOR'])        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];    else if ($_SERVER['HTTP_CLIENT_IP'])        $ip = $_SERVER['HTTP_CLIENT_IP'];    else        $ip = "Unknow";    return $ip;}

这样子调用 GET_IP 方法就可以获取到用户的IP了。

♾️5.1.2通过IP获取用户所在地区

因为 IPV4 是动态分配的,所以不可以通过IP直接来确定用户的地理位置,只能通过IP数据库来获取。但是IP库又是一直更新的,维护起来特别麻烦。所以我们采用调用百度的API来实现;

function get_city($star){    $ip = GET_IP();    //获取用户IP    if (empty($ip)) {        return  0;    }    $url = 'https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=' . $ip . '&co=&resource_id=6006&t=&ie=utf8&oe=gbk&cb=op_aladdin_callback&format=json&tn=baidu&cb=&_=';    //调用了百度接口    $str = file_get_contents($url);    $encode = mb_detect_encoding($str, array("ASCII", 'UTF-8', "GB2312", "GBK", 'BIG5'));    $str = mb_convert_encoding($str, 'UTF-8', $encode);    //转化编码    $str = json_decode($str);    //转换为json类型    $str = $str->data[0]->location;        return 0;}

我们通过 baidu 的 API 获取IP的地理位置,并且与我们的数据库的市名称逐条比对,如果获取到的IP地理位置里存在我们数据库的市名称的话,那么他所在的市就是那一条。

♾️5.2 PHP 通过 Mysql 数据库获取信息

♾️5.2.1 PHP 连接数据库

我们要从MySQL数据库获取信息,第一步肯定是连接数据库了。

//从函数外引入数据库账号密码等global $servername, $sqlname, $sqlpassword, $sqldatabase;// 连接数据库$mysql = new mysqli($servername, $sqlname, $sqlpassword);// 转换字符集$mysql->set_charset('utf8');// 打开数据库if (!SQL\usebase($mysql, $sqldatabase) == true) {// 如果打开失败则程序直接死亡$mysql->close();    die();}

因为我连接数据库是写在方程里的,而数据库账号密码等均在函数外,所以第一步是从函数外引入数据库账号密码等。global 【需要引入的变量】;。引入后调用 mysqli 对象mysqli(【服务器】, 【账号】, 【密码】)连接。如果是本地服务器那么服务器就是localhost。把字符集设置为UTF-8,如果你是别的就设置成别的。最后一步是打开数据库,这里我使用的是我自己写的MYSQL库,过一段时间我完善后就会公开完整代码。本项目使用到的在项目里面都导入了。

♾️5.2.2 PHP获取数据库的表中的信息

下面的代码演示了 查询 level 表中的 address 条件是 id 要等于 要查的id

// 查询 level 表 中的 address 条件是 id 要等于 要查的id$yon = SQL\selectsql($mysql, "`level`", "address", "id", $id);if (!$yon == true) {    die();    $mysql->close();}$map["address"] = mysqli_fetch_array($yon)[0];

当然我们也可以全部都查询比如在获取所有地区的星星数量的时候我们就使用了下面的代码。
查询 表 star*所有 信息 没有条件 所以star的后面要加上 -- 。 不要漏了任何一个空格。这个写法很丑,实际上是因为我库没有完善。当然你叶可以直接通过 mysqli 的内置方法来查询。

// 查询 表 star 的 所有信息 没有条件$yon = SQL\selectsql($mysql, "`star` -- ", "*", "", "");// 星星列表$star = [];// 把查询到的的信息全部都添加到 star 列表里面 while ($row = $yon->fetch_assoc()) //这里不能直接使用$row{    array_push($star, $row);}

♾️5.2.3 PHP读取出地图文件并打乱牌

通过数据库获取到关卡文件存放的路径以后,就要读取出来并且打乱卡牌了。

// 通过路径读取出文件,并且将文件转换成字符串$mapJson = file_get_contents($map["address"]);// 调用打乱方法$mapJson = replay_map($mapJson);// 打乱方法的实现function replay_map($json, $random = true){// decode 解码 JSON    $json = json_decode($json, true);    // 扑克牌类型列表    $pokerType = [];    // 别管     if ($random) {    // 给 pokerType 列表随机添加牌    // 如果 i 小于 扑克总数则继续添加        for ($i = 0; $i < $json["pokerAmount"];) {        // 生成随机的一种牌的类型,类型要在 扑克类型总数以内            $type = mt_rand(1, $json['pokerTypeNum']);            // 在pokerType列表后面添加3个这个牌的类型            array_push($pokerType, $type, $type, $type);            // i = i + 3            $i += 3;        }        // 打乱 pokerType 列表        shuffle($pokerType);        // 给 地图 的扑克按照pokerType列表的顺序修改扑克类型        for ($i = 0; $i < $json["pokerAmount"]; $i++) {            $json["pokerList"][$i][2] = $pokerType[$i];        }            } else {        $type = [];        for ($i = 0; $i < $json['pokerTypeNum']; $i++) {            $type[$i] = $i + 1;        }        shuffle($type);        for ($i = 0; $i < $json["pokerAmount"]; $i++) {            $json["pokerList"][$i][2] = $type[$json["pokerList"][$i][2] - 1];        }    }        // 返回 编码后的 JSON 地图    return json_encode($json);}

六、⭐JS 如何发送 HTTP 请求

♾️6.1 发送的方式

如果你要发送HTTP请求的话,你需要创建一个 XMLHttpRequest (Chrome为内核的浏览器)对象
然后通过调用 open 方法来设置请求方法与请求列表。
可以使用 setRequestHeader来设置请求头 , 例如 把 content-type 设置成 urlencoded

// 创建 XML HTTP 对象var xhr = new XMLHttpRequest;// 请求为 POST 请求 ,请求地址为 '/' xhr.open("POST",'/',true);// 设置 Content-Type 为 application/x-www-form-urlencodedxhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');// 设置回调函数 发送后的信息都由这个回调函数获得xhr.onreadystatechange = function(){ console.log(xhr.readyState); };// 发送请求 ask = 1 , num = 2xhr.send("ask=1&num=2");

这样子就可以发送 HTTP 请求了。
如果细心的人可以发现 控制台会输出4次数字。这代表 xhr.onreadystatechange 被调用了4次。
这 4 次 分别是

  1. 正在加载
  2. 请求已发送
  3. 交互中
  4. 加载完毕
    所以一般我们会在 xhr.onreadystatechange 函数里面添加判断该语句 判断 当前的状态。
switch( xhr.readyState ) {case 0 :console.log("现在还没有被初始化");break;case 1 :console.log("正在加载");break;case 2 :console.log("请求已发送");break;case 3 :console.log("交互中");break; case 4 :console.log("加载完毕");break; }

♾️6.2 封装成函数

为了方便发送表单,我们把这个过程封装成函数
并且为了兼容其他浏览器,我们也要使用判断方法。

function SERVER(url,postList) {    // POST    var xhr;    if (window.XMLHttpRequest) { // Mozilla, Safari...        xhr = new XMLHttpRequest();    } else if (window.ActiveXObject) { // IE        try {            xhr = new ActiveXObject('Msxml2.XMLHTTP');        } catch (e) {            try {                xhr = new ActiveXObject('Microsoft.XMLHTTP');            } catch (e) { }        }    }    if (xhr) {        xhr.onreadystatechange = onReadyStateChange;        xhr.open('POST', url , true);        // 设置 Content-Type 为 application/x-www-form-urlencoded        // 以表单的形式传递数据        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');        let msg = "";        for (let i in postList) {            msg = msg + i + "=" + postList[i] + "&";        }        xhr.send(msg);    }    // onreadystatechange 方法    function onReadyStateChange() {        // 该函数会被调用四次        console.log(xhr.readyState);        if (xhr.readyState === 4) {            // everything is good, the response is received            if (xhr.status === 200) {            // 当请求的返回值是 200 成功 的话就打印回复的报文                console.log(xhr.responseText);            } else {                console.log('There was a problem with the request.');            }        } else {            // still not ready            console.log('still not ready...');        }    }}

利用这个方法我们就可以用简单的两句来实现 发送API 请求啦:

var url = "https://mrxie.xyz/"var form = {ask: "h"};var server = SERVER(url,form);

七、⭐地图文件格式解析

地图的文件格式是 Json ,里面一定得包括以下参数:

现版本仅支持正方形地图,也就是说,mapSizeW 要等于 mapSizeH

{    "pokerAmount" : 18 ,    "mapSizeW" :  120,    "mapSizeH" :  120,    "pokerSize" : 10 ,    "pokerTypeNum" : 1,    "pokerList" : [        [10,10,1],        [10,11,1],        [10,12,1]    ]}

pokerList

pokerList是存放每张扑克的数据的地方,按照 大号压小号 的规则编写。比如上面的例子中,[10,12,1]就压住了[10,10,1] 。 这3个数据的意思分别是 扑克的 [x 坐标 ,y 坐标 ,t 种类] 。坐标均为 中心坐标

♾️7.1 地图文件的命名

普通的关卡地图均放在 ./map/level/文件夹下,以 level 加关卡数 加文件类型 .json 命名。例如 ./map/level/level1.json

⭐预告

如果 CandyMieGame_PHPCandyMieGame_H5 任意一项的 fork 超过 50 我就会发布 兼容 微信小程序的版本。
虽然没有达成目标 但是还是 : https://blog.csdn.net/apple_53792700/article/details/128041151

👋联系作者

✍️本文作者为 > 【谢玄.】 Mr-XieXuan < 于 2022/10/11/3:00 发布于 CSDN 。

📧E-mail: [ Mr_Xie_@outlook.com ]
⌨️GitHub: [ https://github.com/MR-XieXuan }
🔍个人私站: [ https://main.mrxie.xyz/ ]

来源地址:https://blog.csdn.net/apple_53792700/article/details/127255790

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯