基于STM32的五子棋游戲
基于STM32的五子棋小游戲
前言
項目要求
項目總體設(shè)計
軟件實現(xiàn)
重要繪制圖形函數(shù)介紹
繪制按鈕函數(shù)
繪制棋子函數(shù)
繪制棋盤界面函數(shù)
重要界面函數(shù)介紹
模式選擇界面
對戰(zhàn)界面
設(shè)置界面
關(guān)于界面
重要屏幕測試相關(guān)函數(shù)
單人對戰(zhàn)下棋函數(shù)
有一方勝利后調(diào)用函數(shù)
總結(jié)
實際效果視頻鏈接
前言
之前寫過一篇文章是關(guān)于C語言實現(xiàn)五子棋,知道了實現(xiàn)五子棋的基本原理。本來打算很快的把五子棋移植到STM32F103精英版上,但是前幾周實驗室納新、期中考試、開放實驗各種事情導(dǎo)致一直沒有重大進展。在忙完以后終于在這個周完成了五子棋的全部功能。在調(diào)試的過程中總是會出現(xiàn)各種問題,然后去尋找問題、解決問題。這也是第一次一個人獨自做一個比較完備的具體項目,通過項目學(xué)到了很多知識以及知道如何具有一個系統(tǒng)的思維來完成這樣一個項目。
項目要求
基礎(chǔ)要求:實現(xiàn)下棋并判斷輸贏和基本的游戲介紹
高級要求:界面美觀、可以選擇人機模式、雙人對戰(zhàn)模式、智能提示落子點、悔棋以及一些簡單設(shè)置。
項目總體設(shè)計
通過定義一個16*16的數(shù)組g_Chess[16][16]和一個記錄棋子數(shù)量的變量g_ChessNum。初始化棋子總數(shù)為0且每點都沒有棋子,則初始化數(shù)組全部為0。每當(dāng)檢測到有棋子落下時棋子總數(shù)加一,而該點的坐標(biāo)所對應(yīng)的數(shù)組的值為下該棋子時棋子的總數(shù)。
模式選擇:定義一個變量g_Mode,初始化g_Mode=0。如果
g_Mode=1則是人機模式
,如果
g_Mode=2則是雙人對戰(zhàn)模式
。
下棋:判斷該點坐標(biāo)對應(yīng)的數(shù)組的值是否為0,如果是0則可以下棋,如果不是0則返回重新判斷。如果是人機模式,則在觸摸屏沒有按下時進行計算相關(guān)棋子坐標(biāo)。每下一顆棋子后判斷是否連成5顆棋子。如果連接成5顆棋子
判斷白棋或者黑棋:通過
棋子總數(shù)對2求余
來判斷該點是黑棋還是白棋。
悔棋:讓該點坐標(biāo)對應(yīng)的
數(shù)組值等于0棋子總數(shù)減1
,清除該棋子。如果是人機對戰(zhàn),則清理最后下的兩顆棋子,棋子總數(shù)減二。
重新開始:讓
數(shù)組的值都為0
以及
棋子總數(shù)等于0
,重新畫界面
返回:返回主界面選擇模式。
軟件實現(xiàn)
重要繪制圖形函數(shù)介紹
繪制按鈕函數(shù)
void DrawButton(int x, int y, int width, int height, u8* Text, char sta) { int iLen = strlen((char*)Text); if (x > 239 || x < 0 || y < 0 || y > 319) return; POINT_COLOR = WHITE; LCD_DrawRectangle(x, y, x+width, y+height); if (sta==1) { LCD_Fill(x+1, y+1, x+width-1, y+height-1, BLUE); BACK_COLOR = BLUE; Show_Str(x+((width-iLen*8)/2), y+((height-16)/2), 200, 16, Text, 16, 0); } else if(sta==0) { LCD_Fill(x+1, y+1, x+width-1, y+height-1, GRAY); BACK_COLOR = GRAY; Show_Str(x+((width-iLen*8)/2)+2, y+((height-16)/2)+2, 200, 16, Text, 16, 0); } else if(sta==2) { LCD_Fill(x+1, y+1, x+width-1, y+height-1, GREEN ); BACK_COLOR = GREEN ; Show_Str(x+((width-iLen*8)/2)+2, y+((height-16)/2)+2, 200, 16, Text, 16, 0); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
可以通過調(diào)用該函數(shù)來實現(xiàn)畫按鈕,通過改變參數(shù)sta的值來改變按鈕的背景顏色
繪制棋子函數(shù)
void DrawChess(int x, int y, int num, char sta) { uint16_t ChessColor; uint16_t xChess; uint16_t yChess; if (x>15 || x<0 || y>15 || y<0)return; //超范圍了 //判斷是下棋還是復(fù)盤 if (sta) { if (g_Chess[x][y]!=0)return; //已有棋子 } //判斷棋子顏色 (num%2==0) ? (ChessColor=BLACK) : (ChessColor=WHITE); //計算棋子坐標(biāo) xChess = CHESSBOARD_START_X + (x*CHESSGRID_SIZE); yChess = CHESSBOARD_START_Y + (y*CHESSGRID_SIZE); LcdDrawCircleA(xChess, yChess, CHESSGRID_SIZE/2-1, ChessColor, 1); if(voice==1) //判斷燈光是否打開 若打開 每次下子蜂鳴器響100ms { LED0=0; delay_ms(100); LED0=1; } delay_ms(10); if (sta == 0) return; g_ChessNum++; g_Chess[x][y] = g_ChessNum; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
每畫一個棋子棋子數(shù)量加一,在判斷畫棋子前判斷是否可以下棋,同時判斷燈光是否打開。如果可以下棋且燈光打開每下一顆棋LE0閃爍100ms。
繪制棋盤界面函數(shù)
void DrawChessBoard(void) { int i; //填充棋盤背景色 LCD_Fill(CHESSBOARD_START_X-5, CHESSBOARD_START_Y-5, CHESSBOARD_END_X+5, CHESSBOARD_END_Y+5, CHESSBOARD_COLOR); POINT_COLOR = BLACK; //繪制邊框線 LCD_DrawRectangle(CHESSBOARD_START_X-3, CHESSBOARD_START_Y-3, CHESSBOARD_END_X+3, CHESSBOARD_END_Y+3); LCD_DrawRectangle(CHESSBOARD_START_X-4, CHESSBOARD_START_Y-4, CHESSBOARD_END_X+4, CHESSBOARD_END_Y+4); LCD_DrawRectangle(CHESSBOARD_START_X, CHESSBOARD_START_Y, CHESSBOARD_END_X, CHESSBOARD_END_Y); POINT_COLOR = BLACK; //繪制垂直線 for (i = 1; i < 15; i++) { LCD_DrawLine(CHESSBOARD_START_X, CHESSBOARD_START_Y + (CHESSGRID_SIZE*i), CHESSBOARD_END_X, CHESSBOARD_START_Y + (CHESSGRID_SIZE*i)); } //繪制水平線 for (i = 1; i < 15; i++) { LCD_DrawLine(CHESSBOARD_START_X + (CHESSGRID_SIZE*i), CHESSBOARD_START_Y, CHESSBOARD_START_X + (CHESSGRID_SIZE*i), CHESSBOARD_END_Y); } DrawButton(5, 285, 80, 25, (u8*)"重新開始", 1); DrawButton(87, 285, 48, 25, (u8*)"悔棋", 1); DrawButton(137, 285, 48, 25, (u8*)"幫助", 1); DrawButton(187, 285, 49, 25, (u8*)"返回", 1); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
重要界面函數(shù)介紹
模式選擇界面
void Main(void)//主界面函數(shù) { LCD_Clear(GBLUE); BACK_COLOR = GBLUE; POINT_COLOR = RED; Show_Str((240 - 24*3)/2, 30, 200, 24, (u8*)"五子棋", 24, 0); //創(chuàng)建按鈕 DrawButton(40, 100, 160, 40, (u8*)"人機對戰(zhàn)", 1); DrawButton(40, 150, 160, 40, (u8*)"雙人對戰(zhàn)", 1); DrawButton(40, 200, 160, 40, (u8*)"游戲介紹", 1); DrawButton(40, 250, 160, 40, (u8*)"設(shè)置", 1); }
1
2
3
4
5
6
7
8
9
10
11
12
對戰(zhàn)界面
void One(void) { LCD_Clear(BRRED); POINT_COLOR = WHITE; DrawChessBoard(); BACK_COLOR = BRRED; POINT_COLOR=BLACK; Show_Str(34, 10, 240, 34, (u8*)"五子棋人機對戰(zhàn)", 24, 0); One_test(); }
1
2
3
4
5
6
7
8
9
10
設(shè)置界面
void Setting(void) { POINT_COLOR = WHITE; //繪制關(guān)于窗口 LCD_DrawRectangle(20, 110, 220, 220); LCD_DrawRectangle(21, 111, 219, 219); LCD_Fill(22, 112, 218, 218, BLACK); BACK_COLOR = BLACK; Show_Str(104, 115, 32, 16, (u8*)"設(shè)置", 16, 0); Show_Str(40, 140, 136, 16, (u8*)"燈光", 16, 0); Show_Str(40, 165, 136, 16, (u8*)"關(guān)于", 16, 0); //繪制按鈕 DrawButton(150, 140, 25, 20, (u8*)"開", 2); DrawButton(175, 140, 25, 20, (u8*)"關(guān)", 0); DrawButton(150, 165, 50, 20, (u8*)"打開", 2); DrawButton(90, 195, 60, 20, (u8*)"返回", 1); Set_test(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
關(guān)于界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
重要屏幕測試相關(guān)函數(shù)
屏幕測試模塊原理基本差別不大,主要是位置的差別。博主在這里主要介紹單人對戰(zhàn)時的屏幕測試函數(shù)
單人對戰(zhàn)下棋函數(shù)
void One_test(void)//雙人對戰(zhàn)屏幕掃描 { u16 x, y; static int xChess = 0; static int yChess = 0; // static int xPre = 0; // static int yPre = 0; static uint8_t State = 0; //判斷觸摸屏是否被按下 if (tp_dev.sta&TP_PRES_DOWN) { while(1) { tp_dev.scan(0); x = tp_dev.x[0]; y = tp_dev.y[0]; if(tp_dev.sta&TP_PRES_DOWN) //觸摸屏被按下 { if(tp_dev.x[0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
這個函數(shù)大致分為兩部分,第一部分在棋盤范圍內(nèi),主要負(fù)責(zé)下棋還有判斷是否連接成5顆棋子。第二部分主要是在下方按鈕范圍內(nèi)檢測并調(diào)用相關(guān)函數(shù)。同時在屏幕沒有按下時來改變t的值來產(chǎn)生偽隨機數(shù)。從而為AI下棋提供偽隨機數(shù)。同時在判斷如果有一方贏了的情況下提示勝利一秒,然后回到勝利后的最終界面,可以選擇悔棋重新思考下棋位置,也可以選擇重新開始返回主界面等。在有5顆棋子連接成的情況下點擊棋盤區(qū)域不能有反應(yīng)。
有一方勝利后調(diào)用函數(shù)
恢復(fù)至最終勝利界面的函數(shù)
void Recover(void) { u8 m,n; DrawChessBoard();//恢復(fù)棋盤結(jié)束前的模樣 for (m= 0; m < 15; m++) { for (n = 0; n < 15; n++) { if (g_Chess[m][n] > 0) { int num = g_Chess[m][n]; DrawChess(m, n, num-1, 0); } } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在判斷有一方勝利以后調(diào)用該函數(shù),可以恢復(fù)至最終勝利界面后的函數(shù)。
void Over_test(void) { int a,b; while(1) { tp_dev.scan(0); a = tp_dev.x[0]; b = tp_dev.y[0]; if(tp_dev.sta&TP_PRES_DOWN) //觸摸屏被按下 { if (a > 5 && a < 85 && b > 285 && b < 310)//重新開始 { ResStart(); } if (a > 87 && a < 135 && b > 285 && b < 310)//悔棋 { RetractChess(); Recover(); Two_test(); } if (a > 187 && a < 236 && b > 285 && b < 310)//返回 { ResStart(); GameInit(); } } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
在恢復(fù)至最終勝利界面后調(diào)用該函數(shù),主要實現(xiàn)的功能是按棋盤范圍內(nèi)沒有反應(yīng),按下方按鈕才有反應(yīng)。主要避免在測試界面判斷勝利以后恢復(fù)至最終勝利界面依舊可以下棋。
總結(jié)
通過用STM32F103實現(xiàn)五子棋讓我學(xué)到了很多知識,對如何完成一個工程有了更好的認(rèn)識和理解,最終實現(xiàn)了預(yù)期的所有功能讓自己很有成就感。但是這個項目也有不足之處,不能選擇誰先走誰后走,后期可以再完善一下。
實際效果視頻鏈接
基于STM32的五子棋小游戲
同時紀(jì)念一下第一次手寫代碼,加油!!!沖鴨!
5G游戲 AI
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。