C语言课程设计之井字棋
井字棋,想必我們大家都玩過,那么今天就讓我們一起來看一下如何利用c語言和EasyX圖形庫實現(xiàn)井字棋的制作。
編寫前的準(zhǔn)備
- 下載好EasyX圖形庫,并安裝在VS上。
- 對井字棋規(guī)則進(jìn)行深度了解
- 對函數(shù),數(shù)組,循環(huán)知識的復(fù)習(xí)
整體思路
下棋第一步,我們需要準(zhǔn)備棋盤和棋子,我們可以利用圖片制作棋盤,同時利用圖片制作棋子,由于井字棋是3X3布局所以,這里我們采用二維數(shù)組的形式來對棋盤上每個位置進(jìn)行標(biāo)記。定義map[3] [3]并將其全部初始化為0,同時為了區(qū)分棋子,我們規(guī)定用戶棋子在數(shù)組中的值為1,AI為-1,當(dāng)棋子落入棋盤后,將對應(yīng)的數(shù)組位置填入對應(yīng)的數(shù)字。考慮到規(guī)則,一共有八種情況每一個里面有三個位置,每個位置有橫縱坐標(biāo),此時我們可以想到通過三維數(shù)組驚醒定義Key[8] [3] [2]。在這里我們使用loadimage函數(shù)實現(xiàn)地圖與棋子的加載。由于我們要多次使用棋子,我們可以將棋子定義為IMAGE類型的變量用來標(biāo)記地圖。當(dāng)棋子下在對應(yīng)方格中時,對應(yīng)的map[i] [j]應(yīng)該發(fā)生改變。當(dāng)然,前面的-1,1我們可以定義為枚舉類型來使用,這樣能夠使我們的結(jié)構(gòu)更加清晰。
void gameInit() {//創(chuàng)建游戲窗口并設(shè)計大小initgraph(654,654);//寬度和高度//構(gòu)建背景loadimage(0,"S_game4.png");//地圖loadimage(&imgMan, "Man.png");//棋子loadimage(&imgAI, "AI.png");//棋子//對地圖進(jìn)行初始化for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {map[i][j] = 0;}}srand(time(NULL)); }用戶信息的輸入
首先我們需要寫一個死循環(huán)保證游戲的運行,由于用戶是從鼠標(biāo)點擊棋盤不同位置進(jìn)行下棋,故我們通過下列代碼實現(xiàn)操作:
MOUSEMSG msg= GetMouseMsg();//定義MOUSEMSG類型的變量,獲取用戶鼠標(biāo)信息//判斷鼠標(biāo)信息類型if (msg.uMsg == WM_LBUTTONDOWN) {//WM_LBUTTONDOWN 鼠標(biāo)左鍵按下 //用戶下棋manGo(&msg);//判斷結(jié)果manGo函數(shù)接收到鼠標(biāo)信息后,進(jìn)行確定位置和輸出棋子:
void manGo(MOUSEMSG*msg) {int H = msg->y / 218; //行int L = msg->x /218; //列//判斷用戶所選位置是否有棋子if (map[H][L] == 0) {map[H][L] = Man;//將該位置標(biāo)記為用戶使用//設(shè)置坐標(biāo),將標(biāo)記呈現(xiàn)在所在行列int x = L * 218 +10;int y = H * 218 +10;//使用putimage畫圖putimage(x,y,&imgMan);} }AI信息的輸入
AI是要模擬人的思想進(jìn)行操作具體想法如下:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-bYYHOFiF-1671679880549)(C:/Users/wwwZ1/AppData/Roaming/Typora/typora-user-images/image-20221222105055552.png)]
? 我們可以定義一個find函數(shù)用于發(fā)現(xiàn)用戶與電腦所下棋子與八種情況之間的重合度。根據(jù)井字棋規(guī)則可知,中間位置為最佳位置,四個頂角位置次之,故先通過map數(shù)組對中間位置進(jìn)行判斷,如果map[1] [1] ==0,則說明用戶沒有下在中間,如果map[1] [1]==1說明用戶下在中間,自己則下在四個頂角的任意位置。當(dāng)下兩個棋子以上時,通過find函數(shù)的返回值進(jìn)行對用戶或自己是否能勝利進(jìn)行判斷,如果返回值大于0,說明在八種情況中自己符合某種情況,可以取勝,反之不能取勝。
定義的find函數(shù)的原理:例如:find(2)首先對八種情況的每一種進(jìn)行遍歷,判斷用戶所下位置是否是在一種情況中的兩個位置,如果是,返回i,說明用戶在下一次能夠取勝,反之,不能。AI情況與之類似。
int find(int value) {for (int i = 0; i < 8; i++) {//step1:對8種制勝的情況進(jìn)行遍歷 int s = 0;//用于記錄3個位置的標(biāo)記情況for (int j = 0; j < 3; j++) {//step2:對每種情況中的3個可能位置進(jìn)行遍歷int H = Key[i][j][0];//行坐標(biāo)int L = Key[i][j][1];//列坐標(biāo)s += map[H][L];}if (s == value) {return i;}} return -1; }結(jié)果的判斷
下棋的結(jié)局一共有三種情況,分別是用戶勝利,AI勝利,平局,這時我們可以用之前定義的find函數(shù)進(jìn)行判斷是否勝利。平局的情況,我們可以從棋盤是否被占滿來進(jìn)行判斷,首先定義一個count變量進(jìn)行計數(shù),當(dāng)棋盤中map[i] [j]==0,count++,最后再使用if條件語句進(jìn)行判斷棋盤是否占滿。
注意:棋盤的橫縱坐標(biāo),枚舉,函數(shù)的使用,EasyX圖形庫的應(yīng)用。
代碼實現(xiàn)
#define _CRT_SECURE_NO_WARNINGS 1 //編寫前的準(zhǔn)備 /* * 安裝Easyx圖形庫 * 了解井字棋規(guī)則 * 對數(shù)組、循環(huán)、函數(shù)知識回顧 //作用: initgraph 創(chuàng)建一個窗口 loadimage 加載圖片到指定位置 GetMouseMsg(); 獲取用戶鼠標(biāo)信息 */ #include<stdio.h> #include<graphics.h>//用于畫圖,Easyx圖形庫 #include<time.h>//用于生成隨機(jī)數(shù) //用于播放后臺音樂 #include<windows.h> #pragma comment(lib,"Winmm.lib") enum { AI = -1, Man = 1 };//枚舉------>標(biāo)記棋子 int map[3][3];//設(shè)計地圖 IMAGE imgMan;//定義一個IMAGE類型的變量用于存放用戶輸入的標(biāo)志 IMAGE imgAI;//定義一個IMAGE類型的變量用于存放電腦輸入的標(biāo)志 //棋子勝利有8種情況,3個格子,每個格子有x、y兩個坐標(biāo) /*********************************************************************************************************/ int Key[8][3][2] = {0,0, 0,1, 0,2,//第一行1,0, 1,1, 1,2,//第二行2,0, 2,1, 2,2,//第三行0,0, 1,0, 2,0,//第一列0,1, 1,1, 2,1,//第二列0,2, 1,2, 2,2,//第三列0,0, 1,1, 2,2,//正對角線0,2, 1,1, 2,0//反對角線 }; /*********************************************************************************************************/ int find(int value) {for (int i = 0; i < 8; i++) {//step1:對8種制勝的情況進(jìn)行遍歷 int s = 0;//用于記錄3個位置的標(biāo)記情況for (int j = 0; j < 3; j++) {//step2:對每種情況中的3個可能位置進(jìn)行遍歷int H = Key[i][j][0];//行坐標(biāo)int L = Key[i][j][1];//列坐標(biāo)s += map[H][L];}if (s == value) {//判斷結(jié)果return i;}} return -1; } /*********************************************************************************************************/ void gameInit() {//創(chuàng)建游戲窗口并設(shè)計大小initgraph(654,654);//寬度和高度//構(gòu)建背景loadimage(0,"S_game4.png");loadimage(&imgMan, "Man.png");loadimage(&imgAI, "AI.png");//初始化棋盤for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {map[i][j] = 0;}}srand(time(NULL)); } /*********************************************************************************************************/ //用戶下棋 void manGo(MOUSEMSG*msg) {int H = msg->y / 218; //行int L = msg->x /218; //列//判斷用戶所選位置是否有棋子if (map[H][L] == 0) {map[H][L] = Man;//將該位置標(biāo)記為用戶使用//設(shè)置坐標(biāo),將標(biāo)記呈現(xiàn)在所在行列int x = L * 218 +10;int y = H * 218 +10;//使用putimage畫圖putimage(x,y,&imgMan);} } /*********************************************************************************************************/ //AI下棋 void aiGo() {int step = 0;int H, L; /********************************************************************************************************* * 判斷哪些位置已經(jīng)被選擇,并記錄總共所走的步數(shù) * 遍歷數(shù)組map的9個位置,確定哪些位置已經(jīng)被占用 /*********************************************************************************************************/for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (map[i][j]) {step++;}}} /********************************************************************************************************* * 如果step=0,說明棋盤中沒有位置被選中,只需要等待用戶選擇即可 * 井字棋最佳位置時中心位置,其次是四個頂角位置 /*********************************************************************************************************/if (step == 1) {//判斷中間位置是否有棋子if (map[1][1] == 0) {H = 1;L = 1;}//將棋子位置放在中間else {H = 0;L = 2;}//將棋子位置放在四個頂角之一的位置} /********************************************************************************************************* * 當(dāng)step=2時,對可能出現(xiàn)的情況進(jìn)行分析 /**********************************************************************************************************/else {int i = find(-2);//當(dāng)返回值后,i<0,證明沒有找到使得AI一舉獲勝的情況if (i < 0) {i = find(2);//對用戶情況的判斷}if (i >= 0)//說明用戶有獲勝的可能 {//在第i條路徑上的空白位置落子(每條路徑有三個位置)for (int j = 0; j < 3; j++) {int H1 = Key[i][j][0];//確定獲勝路徑中的橫坐標(biāo)int L1 = Key[i][j][1];//確定獲勝路徑中的縱坐標(biāo)if (map[H1][L1] == 0) {//將棋子放在獲勝路徑中的空位置,實現(xiàn)阻止用戶獲勝H = H1;L = L1;break;//已經(jīng)找到位置,終止循環(huán)}}} /*********************************************************************************************************/else//用戶不能一舉獲勝的情況,棋子位置隨意{while (1) {H = rand() % 3;//產(chǎn)生小于3的隨機(jī)數(shù),賦給行坐標(biāo)L = rand() % 3;//產(chǎn)生小于3的隨機(jī)數(shù),賦給列坐標(biāo)if (map[H][L] == 0) {break;//棋子填入空位置,終止循環(huán)}}}}putimage(L * 218 + 15, H* 218 + 15, &imgAI);//將標(biāo)記放在AI所選位置map[H][L] = AI;//將-1給當(dāng)前坐標(biāo),表明該位置已經(jīng)被占用 } /*********************************************************************************************************/ //定義布爾型函數(shù),檢查結(jié)果 bool check() {int i = find(3);//用戶獲勝Sleep(250);//保證每次操作都有間歇期if (i >= 0){loadimage(0, "WIN2.png");system("pause");//對地圖進(jìn)行初始化,實現(xiàn)新游戲for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {map[i][j] = 0;}}loadimage(0, "S_game4.png");//地圖重加載return true;} /*********************************************************************************************************///AI獲勝i = find(-3);Sleep(250);//保證每次操作都有間歇期if(i>=0){loadimage(0, "Defeat.png");system("pause");//對地圖進(jìn)行初始化,實現(xiàn)新游戲for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {map[i][j] = 0;}}loadimage(0, "S_game4.png");//地圖重加載return true;} /*********************************************************************************************************///平局情況 地圖上面沒有空余位置int count = 0;//用于統(tǒng)計map數(shù)組中是否含有0for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (map[i][j] == 0) {count++;}}}if (count == 0)//count=0說明棋盤中沒有空余位置{loadimage(0, "Ping.png");system("pause");//對地圖進(jìn)行初始化,實現(xiàn)新游戲for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {map[i][j] = 0;}}loadimage(0, "S_game4.png");//地圖重加載return true;}return false; } /*********************************************************************************************************/ int main(void) {//初始化gameInit();//用于構(gòu)建游戲所需要的條件//游戲的主循環(huán)//設(shè)置游戲背景音樂mciSendString("open 井字游戲.mp3 alias bkmusic", NULL, 0, NULL);mciSendString("play bkmusic repeat", NULL, 0, NULL);//循環(huán)播放音樂while (1) {//開始下棋MOUSEMSG msg= GetMouseMsg();//定義MOUSEMSG類型的變量,獲取用戶鼠標(biāo)信息//判斷鼠標(biāo)信息類型if (msg.uMsg == WM_LBUTTONDOWN) {//WM_LBUTTONDOWN 鼠標(biāo)左鍵按下 //用戶下棋manGo(&msg);//判斷結(jié)果if (check()) {continue;//如果勝利,跳過循環(huán)};//電腦下棋aiGo();check();}}system("pause");//設(shè)置暫停,使窗口停留return 0; } /*********************************************************************************************************/效果展示
感興趣的小伙伴還可以為它添加多種音效,開始界面的美化,以及其他功能。井字棋的教學(xué)到這里就結(jié)束了。
重要的事情說三遍:
一定要先捋清楚思路再嘗試代碼實現(xiàn)!!!一定要先捋清楚思路再嘗試代碼實現(xiàn)!!!一定要先捋清楚思路再嘗試代碼實現(xiàn)!!!
總結(jié)
以上是生活随笔為你收集整理的C语言课程设计之井字棋的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么我的物联网创业失败?看看这五个原因
- 下一篇: mysql主要有哪三大数据类型_MySQ