掃雷
- 前言
- 準備作業
- EasyX的下載
- 一些準備知識
- 頭檔案的參考
- 圖形化界面的創建
- 圖形化界面簡介
- 圖片加載與放置圖片
- 滑鼠操作
- 提示框
- 其它的準備知識
- 思路分析
- 代碼實作
- 準備作業
- 初始化游戲的函式
- 在界面上放置圖片的函式
- 執行滑鼠操作的函式
- 打開關閉格子的函式
- 判斷游戲是否已經結束的函式
- 執行游戲的函式
- 主函式
- 完整代碼
- 程式的缺陷
- 發給同學玩(裝逼)時的缺陷
- 游戲結束時的缺陷
- 游戲進行中的缺陷
- 寫在最后
前言
學了那么長時間的C語言,我們所有的一切似乎都被禁錮在黑框框(控制臺)里,讓人覺得很無趣,學習unity那樣的平臺又太過困難,那么有沒有什么是適合我們這些新手使用的?答案是:有!
EasyX 是針對 C/C++ 的圖形庫,可以幫助使用C/C++語言的程式員快速上手圖形和游戲編程,
這是百度百科上的解釋,目前就我的理解,下載并安裝Easyx就相當于為我們的VS(或者是別的)增加了一些庫函式,以便我們實作圖形化界面,那這里我會帶大家實作圖形化界面的掃雷,實作后的效果大概是這樣的:


當然,本人水平有限,最后實作的功能也較少,還有些地方有小的問題不會解決,希望大佬們有想法的話多多和我交流,有問題也希望大佬指正!此程式的問題和不足我會放到文章最后!
準備作業
EasyX的下載
EasyX官網
下載是非常簡單的,因為它只是相當于為我們的VS增加了一些庫函式
- 找到EasyX官網,點擊下載EasyX

- 下載完成后,找到下載的位置,點擊打開

- 點擊下一步

- 它會自動檢測我們使用的開發平臺,我們安裝對應的庫就可以了,以后不想用了就直接點擊卸載就可以了,另外,幫助檔案也推薦大家安裝一下吧,一般遇到什么問題都可以在那查,

- 這樣我們EasyX的安裝就完成了,是不是非常簡單呢!
一些準備知識
注意:這里不要問原理,只需知道作用就好了,不然會學不下去的!我們只需要知道怎么用!
頭檔案的參考
EasyX的安裝為我們提供了一些庫函式,我們只需要參考一個頭檔案就可以使用那些函式了,
#include<graphics.h>
圖形化界面的創建
我們要實作圖形化界面,這要使用一個庫函式
initgraph(400,400,0);
/*
這個函式有三個引數
第一個引數是界面水平方向的大小
第二個引數是界面豎直方向的大小
第三個引數我們可以有幾種選擇:
1:設為0
2:設為SHOWCONSOLE
作者只了解這兩種,可能還有別的,不過做掃雷夠了
*/
下面我們來測驗一下:

我的天!怎么一下子這么多錯誤,別著急,我們發現:

原來EasyX只能給C++檔案用!
那我們就把檔案后綴改為cpp再測驗

運行成功了,可是界面呢?其實,仔細觀察,程式運行后有個界面一閃而過了,那個就是我們的圖形化界面,我們怎么讓他停下來呢?
其實,界面一閃而過是因為整個程式結束后,我們的界面也就結束了
那我們就加個死回圈,讓程式無法結束,就能看到界面了,

圖中的closegraph()函式是用來關閉界面的,(以后這個圖形化界面我們就叫做界面),由于程式無法執行到那一步,所以界面未關閉,
仔細管察,我們會發現,控制臺不見了!
其實這是initgraph()函式的功能,第三個引數設為0或NULL時,我們的控制臺會被關閉(實際上程式運行后控制臺仍會存在一段時間,因為函式的執行需要一定時間)
那我們能不能選擇保留界面呢,答案是可以,我們把initgraph()函式的第三個引數修改為SHOWCONSOLE(全部大寫)就可以了:

注意:關掉控制臺或者界面都會關掉整個程式,即如果關掉控制臺,界面也會被關閉,如果關掉界面,控制臺也會被關閉
圖形化界面簡介
有同學會問了,這不是圖形化界面嗎,怎么tm全黑啊,你什么也不干,它怎么知道你要什么!在后面我們可以在這個界面中插入圖片,那插入的位置怎么確定呢?通過坐標!

該界面對應的坐標軸是這樣的,而我們又知道界面的大小(寬度,高度),我們就可以在指定的位置插入圖片了,這里我們先不講解,在掃雷中用到的時候我再告訴大家具體怎么插入圖片,
圖片加載與放置圖片
參考<graphics.h>頭檔案后,我們就可以使用圖片的陣列或變數來存放圖片(還是那就話,知道怎樣用就好,不用知道原理!),下面我們就來測驗一下:
圖片素材及其位置如下:

因為后面我們在加載圖片時需要知道圖片的儲存位置,我們最好把圖片放在我們的CPP檔案的同一路徑下,實作效果如下:
你失敗了?別急,往下看

我們首先創建了一個圖片陣列,如果只有一張圖片的話,我們可以直接用
IMAGE img;
來創建一個圖片變數,img為變數名,是可以自由起名字的(不要與關鍵字重名)
如果圖片較多,我們可以用圖中方式創建圖片陣列,[ ]中的數字為陣列大小,使用時下標從0開始,
loadimage()函式的功能是加載圖片,也就是“告訴”程式我們有這個圖片,我們可以理解為“此函式將圖片的位置存入了變數中”,第一個引數為變數的地址,第二個變數是圖片的地址,其中“./”表示與我們當前編輯的檔案(cpp檔案)同一目錄下的檔案路徑(也可以省略,因為在同一路徑下,可以直接寫為"1.png"),如果這些基礎知識還不知道的話推薦一篇博客:路徑的表示與含義
putimage()函式的功能即為放置圖片,第一個引數是圖片開始位置的X坐標,第二個引數是Y坐標,第三個引數是變數的地址
有的同學會發現自己的VS在loadimage()函式那報錯了,你可以理解為該函式需要的字符集與VS默認的不同,不懂也沒關系,直接修改VS的一個設定就好了:


提示:這里只是修改了這個專案的字符集,后面創建的字符集還會是默認的“Unicode”,不會影響我們以后創建的專案的使用,
但是我們圖片太大了,界面太小了,加載不完,這不太好看,其實我們可以loadimage()函式后面再加兩個引數,修改載入圖片的大小:

你可能會問:函式的引數個數不是只能是確定的嗎,怎么你既可以用兩個引數,又可以用四個引數,其實這涉及到函式的多載,就是說庫中會有多個名為loadimage()的函式,這些函式的引數型別、個數不太相同,程式會根據我們使用的引數幫我們呼叫對應的函式,
滑鼠操作
- MouseHit()函式,無引數,回傳值為bool型,判斷有無滑鼠資訊,如果有則回傳true,沒有則回傳false,可以理解為有則回傳1,否則回傳0,滑鼠資訊包括的內容很多:

- MOUSEMSG結構體,以下是頭檔案中它的定義,是不是看的很懵,等會下面會有解釋

- GetMouseMsg()函式,無引數,回傳型別為結構體型別,很懵?先繼續
我們寫一個這樣的代碼片段:
while(1)
/*
為防止程式執行過快以致我們還沒來得及操控滑鼠
它就判斷我們沒有滑鼠資訊,定義一個死回圈
*/
{
if (MouseHit())//判斷有沒有滑鼠訊息,有的話進入回圈
{
/*
我們定義一個結構體變數來接收此函式的回傳值
也就是說msg會接收所有上圖中的資訊,如x,y,wheel等
*/
MOUSEMSG msg = GetMouseMsg();
int r = msg.y / size;
/*
通過msg.y我們就可以使用前面得到的滑鼠資訊中滑鼠所在位置的Y坐標
msg.x同理
*/
int c = msg.x / size;
switch (msg.uMsg)
/*
msg.uMSG代表我們獲取的滑鼠資訊,上面有對滑鼠資訊的介紹
各滑鼠資訊對應的值會在下面介紹
*/
{
//看懂這里不懂了?往下看
case WM_LBUTTONDOWN:
break;
case WM_RBUTTONDOWN:
break;
}
break;//跳出回圈
}
}
這是頭檔案中的說明:
看到這,我們明白了,它可能是這樣實作的:
這些滑鼠資訊對應的都有整數值,msg.uMsg得到的也是這些資訊對應的值,因此我們可以用switch陳述句對情況分類,
作個假設:
WM_LBUTTONDOWN代表左擊,他的值假如為1,當我們左擊時,msg.uMsg就是1
到這里,我們就明白該怎樣使用這些東西了,暫時不要深究原理
提示框
這個看得我更蒙蔽,直接看下怎么使用就好了
int main()
{
initgraph(400,400,SHOWCONSOLE);
loadimage(&img, "1.png",100,100);
putimage(100, 100,&img);
MessageBox(GetHWnd(), "You win! Do you want to play again?", "notice", MB_OKCANCEL);
while (1)
{
;
}
closegraph();
return 0;
}
效果:

這里我們點確定或取消都沒有任何效果,那他有什么用呢,其實它有一個int型回傳值,當我們點確定時它會回傳1,否則回傳其它值,這樣我們用一個變數接受回傳值,就能通過if或switch陳述句執行不同的選擇了
這個函式是不是看得你很懵逼?我也是,我們只要知道這個函式的第二個引數是提示內容,第三個引數是提示標題就可以了,其它內容只要照著格式寫就可以了,
另外,判斷時我們一般這樣用:
int isok = MessageBox(GetHWnd(), "You win! Do you want to play again?", "notice", MB_OKCANCEL);
if (isok == IDOK)
{
game();
}
else
{
exit(666);
}
其中IDOK就是1,exit()函式就是退出程式,666就是退出的代碼,這個函式一般用于判斷程式在哪里退出
這是頭檔案里的定義:

這是exit()函式執行的效果,這個程式執行完exit(666);后就退出了

其它的準備知識
這里需要的是一些其它知識,如果這些內容不會的話我會推薦相應的博客
- srand()函式和rand()函式的使用:
srand()函式和rand()函式的使用 - 黑框框版掃雷能看懂
掃雷
思路分析
- 和黑框框里的掃雷一樣,我們首先創建陣串列示整個棋盤(這么說好像有點不對?),并隨機放置一定個數的雷,為了方便后期修改我們用宏來定義行、列、雷數
- 我們想實作圖形化界面,那我們就需要相應的掃雷中的圖片,這里我分享一下:
鏈接:https://pan.baidu.com/s/1JBz0Pq0R5zvuS7l92qJi5Q
提取碼:nh5f
檔案夾里是這樣的:

我們用函式實作圖片的放置,陣列的每個要使用的元素放置一張圖片
因為我們創建的陣列會有額外的兩行兩列來防止越界,那些元素我們不使用, - 可是這都是靜態圖片啊,我們怎么能實作滑鼠點擊后的轉化呢,我們知道,在黑框框里我們用-1表示雷,數字表示該非雷位置九宮格內雷的個數,我們這里還在陣列中存數字,根據數字的不同我們放置不同的圖片,再利用滑鼠操作得到滑鼠操作的位置,轉化為陣列的下標,修改對應元素的值,就可以改變該位置的圖片了,因此我們需要經常呼叫放置圖片的函式以完成修改,你已經看懵了?沒問題,下面使用的時候更容易懂
- 我們用gamedraw()函式繪制棋盤,當陣列元素為-1時,我們在這里放置雷的圖片(9.jpg),為0時我們放置空的那張(0.jpg,我們稱為空格),為1~8時我們放置相應的圖片(1-8.jpg),但是我們該開始展示的應該全部是10.jpg(我們稱為關閉的格子)那張圖片,我們給所有元素加上20,那么這些元素的值都在19-28之間,當元素的值在此范圍內時,我們放置10.jpg,當我們滑鼠左鍵點擊相應位置時,我們讓此位置對應的陣列元素的值減去20,這樣它的值就在-1到8之間了,它就會顯示打開后的圖片,標記也是同理,當我們右鍵點擊的位置是關閉的格子時,我們把它對應的元素值再加上20,這樣我們把大于30的元素換為11.jpg,即標記的格子,同時,如果我們右鍵點擊位置元素的值大于30,我們讓它減少20,這樣它又變回關閉的格子了
小總結:

這樣我們就可以實作圖片的轉換了
代碼實作
在這次演示中,我把所有的東西都放在一個.cpp檔案中了,還是推薦大家分三個檔案去放置,不過那樣的話函式需要傳參,感覺挺麻煩的,在一個檔案中我們定義幾個全域變數,就不用老是傳參了,也可以分多個檔案并在一個檔案中定義全域變數,在別的檔案中宣告這些全域變數
準備作業
#define _CRT_SECURE_NO_WARNINGS 1
#include<graphics.h>
//產生亂數需要用的頭檔案:
#include<Windows.h>
#include<time.h>
#include<stdio.h>
#pragma warning(disable : 4996)
/*
因為我們使用的部分函式是已經被棄用的
使用上面這句命令后,即使被棄用的函式我們也能使用
其實這些被棄用的函式已經有新函式替代了
我為什么不用?我不會!
*/
#define ROW 10//行數
#define COL 10//列數
#define MINE 10//雷數
#define PIC_SIZE 40
//圖片大小,程式中我們把圖片加載為正方形,寬、高相同,因此只需一個大小
int map[ROW + 2][COL + 2];//陣列,存放資料
int i, j, flag;//flag代表我們已經打開的格子數,后面用來判斷輸贏
//因為我們會用到多次遍歷陣列,我直接創建兩個全域變數i,j
//后面在每個函式中使用時就不用再創建了
IMAGE img[12];//存放我們需要的12個圖片
/*
下面兩個函式因為我定義的比較晚而使用的比較早,就在前面宣告一下,
防止報錯
*/
void open(int r, int c);
void game();
提示:
我有一次用宏定義圖片大小的名字為大寫的SIZE,結果一堆錯誤,后來發現只是重名了,用個別的名字就可以了

初始化游戲的函式
void gameinit()
{
flag = 0;//打開的格子數置為0
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
map[i][j] = 0;
}
}
for (i = 0; i < MINE; )
//隨機放10個雷并確保沒有多個雷放在一個重復的位置
{
int r = rand() % ROW+1;
int c = rand() % COL+1;
if (map[r][c] == 0)
{
map[r][c] = -1;
i++;
}
}
//初始化陣列的值,雷九宮格內的非雷元素加一
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
if (map[i][j] == -1)
{
for (int m = i - 1; m <= i + 1; m++)
{
for (int n = j - 1; n <= j + 1; n++)
{
if (map[m][n] != -1)
{
map[m][n]++;
}
}
}
}
}
}
//把陣列的值都加上20,放圖片時就會是關閉的格子了
for (int i = 1; i <= ROW; i++)
{
for (int j = 1; j <= COL; j++)
{
map[i][j] += 20;
}
}
char tmp[20] = { 0 };
//加載圖片
for (i = 0; i < 12;i++)
{
sprintf(tmp, "./image/%d.jpg",i);
loadimage(&img[i], tmp, PIC_SIZE, PIC_SIZE);
}
}
推薦一篇介紹sprintf函式的博客:sprintf()函式的用法
在界面上放置圖片的函式
void gamedraw()
{
for ( i = 1; i <= ROW; i++)
{
for ( j = 1; j <= COL; j++)
{
//下面放置圖片的坐標為什么是這樣呢?結合坐標軸就能明白了
if (map[i][j] == -1)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[9]);//雷
}
else if (map[i][j] >= 0 && map[i][j] <= 8)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[map[i][j]]);//0-8的數字
}
else if (map[i][j] >= 19 && map[i][j] <= 28)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[10]);//空白
}
else if (map[i][j] > 30)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[11]);//標記圖片
}
}
}
}
舉個例子吧,假如map[4] [5]位置值為28,那我們應該放置關閉的格子:

執行滑鼠操作的函式
int mouse()
{
if (MouseHit())//判斷有沒有滑鼠訊息
{
MOUSEMSG msg = GetMouseMsg();//獲取當前滑鼠資訊
//找到對應陣列的下標
int r = msg.y / PIC_SIZE + 1;
int c = msg.x / PIC_SIZE + 1;
switch (msg.uMsg)//根據滑鼠訊息執行不同操作
{
case WM_LBUTTONDOWN://左擊
if (map[r][c] > 8&&map[r][c]<29)//打開關閉的格子
{
map[r][c] -= 20;
flag++;//打開格子數加一
open(r, c);
}
break;
case WM_RBUTTONDOWN://右擊
if (map[r][c] > 8 && map[r][c] < 29)//標記關閉的格子
{
map[r][c] += 20;
}
else if(map[r][c] > 30)//取消標記
{
map[r][c] -= 20;
}
break;
}
return map[r][c];//回傳打開位置后該位置的值
//如果為-1則游戲結束
}
}
打開關閉格子的函式
void open(int r,int c)//打開空格周圍未被標記且不是雷的格子
{
if (map[r][c] == 0)
{
for (int m = r - 1; m <= r + 1; m++)
{
for (int n = c - 1; n <= c + 1; n++)
{
if (map[m][n]!=19&&map[m][n]>8)
{
map[m][n]-=20;
flag++;//打開周圍的格子,格子數也要加一
open(m, n);
//遞回打開(空格周圍的空格)周圍的格子
//括號內為主語
}
}
}
}
}
判斷游戲是否已經結束的函式
void judge()
{
gamedraw();//放置圖片
if (mouse() == -1)//執行mouse()函式,回傳-1則游戲結束,彈出提示框
{
int isok = MessageBox(GetHWnd(), "You lose! Do you want to play again?", "notice", MB_OKCANCEL);
if (isok == IDOK)
{
game();//再次進入游戲
}
else
{
exit(666);//退出程式
}
}
if (flag == ROW * COL - MINE)
//判斷游戲是否結束,即格子全部打開
//只需判斷總格子數-雷數==當前打開格子數即可
{
int isok = MessageBox(GetHWnd(), "You win! Do you want to play again?", "notice", MB_OKCANCEL);
if (isok == IDOK)
{
game();//再次進入游戲
}
else
{
exit(666);//退出程式
}
}
}
執行游戲的函式
void game()
{
gameinit();//初始化游戲
while (1)
{
judge();//進入判斷函式
}
}
主函式
int main()
{
srand((unsigned)time(NULL));//設定亂數種子
initgraph(ROW* PIC_SIZE,COL* PIC_SIZE,NULL);
//初始化界面,寬度為列數乘以圖片大小,高度為行數乘以圖片大小
//關閉控制臺
/*
剛開始寫時我們可以保留控制臺,并寫一個列印陣列的函式來判斷我們程式的正確性
*/
game();//進入game()函式
closegraph();//關掉界面
return 0;
}
完整代碼
#define _CRT_SECURE_NO_WARNINGS 1
#include<graphics.h>
#include<Windows.h>
#include<stdio.h>
#include<time.h>
#pragma warning(disable : 4996)
#define ROW 10
#define COL 10
#define MINE 10
#define PIC_SIZE 40
int map[ROW + 2][COL + 2];
int i, j, flag;
IMAGE img[12];
void open(int r, int c);
void game();
void gameinit()
{
flag = 0;
srand((unsigned)time(NULL));
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
map[i][j] = 0;
}
}
for (i = 0; i < MINE; )
{
int r = rand() % ROW+1;
int c = rand() % COL+1;
if (map[r][c] == 0)
{
map[r][c] = -1;
i++;
}
}
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
if (map[i][j] == -1)
{
for (int m = i - 1; m <= i + 1; m++)
{
for (int n = j - 1; n <= j + 1; n++)
{
if (map[m][n] != -1)
{
map[m][n]++;
}
}
}
}
}
}
for (int i = 1; i <= ROW; i++)
{
for (int j = 1; j <= COL; j++)
{
map[i][j] += 20;
}
}
char tmp[20] = { 0 };
for (i = 0; i < 12;i++)
{
sprintf(tmp, "./image/%d.jpg",i);
loadimage(&img[i], tmp, PIC_SIZE, PIC_SIZE);
}
}
void gamedraw()
{
for ( i = 1; i <= ROW; i++)
{
for ( j = 1; j <= COL; j++)
{
if (map[i][j] == -1)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[9]);
}
else if (map[i][j] >= 0 && map[i][j] <= 8)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[map[i][j]]);
}
else if (map[i][j] >= 19 && map[i][j] <= 28)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[10]);
}
else if (map[i][j] > 30)
{
putimage((j - 1) * PIC_SIZE, (i - 1) * PIC_SIZE, &img[11]);
}
}
}
}
int mouse()
{
if (MouseHit())
{
MOUSEMSG msg = GetMouseMsg();
int r = msg.y / PIC_SIZE + 1;
int c = msg.x / PIC_SIZE + 1;
switch (msg.uMsg)
{
case WM_LBUTTONDOWN:
if (map[r][c] > 8&&map[r][c]<29)
{
map[r][c] -= 20;
flag++;
open(r, c);
}
break;
case WM_RBUTTONDOWN:
if (map[r][c] > 8 && map[r][c] < 29)
{
map[r][c] += 20;
}
else if(map[r][c] > 30)
{
map[r][c] -= 20;
}
break;
}
return map[r][c];
}
}
void open(int r,int c)
{
if (map[r][c] == 0)
{
for (int m = r - 1; m <= r + 1; m++)
{
for (int n = c - 1; n <= c + 1; n++)
{
if (map[m][n]!=19&&map[m][n]>8)
{
map[m][n]-=20;
flag++;
open(m, n);
}
}
}
}
}
void judge()
{
gamedraw();
if (mouse() == -1)
{
int isok = MessageBox(GetHWnd(), "You lose! Do you want to play again?", "notice", MB_OKCANCEL);
if (isok == IDOK)
{
game();
}
else
{
exit(666);
}
}
if (flag == ROW * COL - MINE)
{
int isok = MessageBox(GetHWnd(), "You win! Do you want to play again?", "notice", MB_OKCANCEL);
if (isok == IDOK)
{
game();
}
else
{
exit(666);
}
}
}
void game()
{
gameinit();
while (1)
{
judge();
}
}
int main()
{
initgraph(ROW* PIC_SIZE,COL* PIC_SIZE,NULL);
game();
closegraph();
return 0;
}
程式的缺陷
發給同學玩(裝逼)時的缺陷
一般發給別人的程式是release版,注意,切換到release后需要再修改一次字符集

切換到該版本后再運行一次程式,然后去找生成的.exe檔案
找到解決方案"掃雷"并右擊,選擇在檔案資源管理器中打開

進入release檔案夾:

打開.exe檔案

奇怪,怎么什么都沒有?因為我們沒有在這個位置放置圖片,把圖片檔案夾和.exe檔案放在同一個位置,就能正確運行了
我們要把這兩個檔案放在一個檔案夾中發給朋友,但朋友不知道怎么玩,我們寫個幫助檔案readme.txt:

把這三個檔案放在一個檔案夾中,發給朋友,朋友就也能玩了!(裝逼成功)
游戲結束時的缺陷
在游戲結束(不管是輸還是贏)時,最后一個格子無法打卡,我試了好多種方法,還是沒有解決,
如圖,我點到了雷,游戲結束,雷卻未顯示

游戲進行中的缺陷
- 上圖中暴露了另一個缺陷,沒有規避第一次就點到雷的情況
- 沒有難度的選擇、計時、計分功能
- 我學習了添加音樂的方法,但沒找到相關音樂,于是就未添加
- 開始游戲和結束游戲時控制臺都會一閃而過,無法讓控制臺不出現
寫在最后
本文章是在我觀看了B站視頻后總結改進的,歡迎大家去B站看原視頻
EasyX的使用
圖形化界面掃雷
本人能力有限,實作的此程式也不太行,如有錯誤望指正,如果有大佬能解決我的一些問題,希望能和您交流,有問題有歡迎和我交流,這篇文章我也寫了好久,就先到這里吧,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/353539.html
標籤:其他
上一篇:C語言初期學習遇到的特殊點 【三子棋詳解】【初學者福音,詳細總結,復習能手】
下一篇:井字棋版本1.0(對抗人工智障)
