鴻蒙基于WiFi IoT套件開發(fā)的猜數(shù)字小游戲代碼分享
猜數(shù)字是一個(gè)很經(jīng)典的小游戲,也是編程開發(fā)入門的典型,以下為基于WiFi IoT套件開發(fā)的猜數(shù)字小游戲的具體開發(fā)過程和效果。
基本規(guī)則:
由甲方(玩家)默想一個(gè)1-99(包含)內(nèi)的任意數(shù)字(整數(shù)),然后由乙方進(jìn)行猜測,并詢問甲方猜測的數(shù)字是大了還是小了,甲方根據(jù)實(shí)際情況進(jìn)行回復(fù),則乙方最多問6個(gè)問題,就一定能夠猜中甲方默想的數(shù)字。
基本原理:
乙方問最多6次,包括最后一次說出猜中的數(shù)字,實(shí)際上乙方最多有7次猜測的機(jī)會(huì)。
而使用二分進(jìn)行查找,2^7=128,則99以內(nèi)的數(shù)字,完全可以覆蓋,因此乙方絕對(duì)可以猜中。
實(shí)現(xiàn)概述:
以上的基本規(guī)則和基本原理明確了,我們要在WiFi IoT套件上實(shí)現(xiàn),并且甲方需要參與,需要處理以下三個(gè)部分:
- 猜數(shù)字的主邏輯
- 使用OLED屏幕顯示提示信息,讓玩家進(jìn)行互動(dòng)操作:我們需要在屏幕上顯示漢字,進(jìn)行玩家當(dāng)前猜測的數(shù)字,以及玩家按鍵后告知玩家結(jié)果
- 使用按鍵接收玩家操作(大了或者小了等):在這個(gè)實(shí)例中,我們使用了ADC方式來讀取按鍵信息,從而獲得玩家具體操作。所使用的按鍵為核心板上的USR按鍵,和OLED板上的S1,S2按鍵。使用ADC方式讀取的時(shí)候,他們所使用的輸入端口為GPIO5/ADC2,具體的按鍵作用如下:
- USR:開始游戲,或者確認(rèn)
- S1:如果猜小了,則玩家按S1告知
- S2:如果猜大了,則玩家按S2告知
原始代碼修改處理:【代碼基礎(chǔ)為code-1.0.tar.gz】
- 開啟I2C:vendor/hisi/hi3861/hi3861/build/config/usr_config.mk
- ## BSP Settings
- #
- # CONFIG_I2C_SUPPORT is not set
- CONFIG_I2C_SUPPORT=y
- # CONFIG_I2S_SUPPORT is not set
- I2C復(fù)用端口設(shè)置:vendor/hisi/hi3861/hi3861/app/wifiiot_app/init/app_io_init.c
- #ifdef CONFIG_I2C_SUPPORT
- /* I2C IO復(fù)用也可以選擇3/4; 9/10,根據(jù)產(chǎn)品設(shè)計(jì)選擇 */
- // hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_I2C1_SDA);
- // hi_io_set_func(HI_IO_NAME_GPIO_1, HI_IO_FUNC_GPIO_1_I2C1_SCL);
- hi_io_set_func(HI_IO_NAME_GPIO_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
- hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);
- #endif
主邏輯代碼:guess.c
- #include <stdio.h>
- #include <unistd.h>
- #include <ohos_init.h>
- #include <cmsis_os2.h>
- #include <hiview_config.h>
- #include <hiview_log.h>
- #include <wifiiot_watchdog.h>
- #include <hi_task.h>
- #include "button/button.h"
- #include "oled/oled.h"
- /*
- 0123456789
- 請(qǐng)?jiān)谛闹心胍粋€(gè)1~99的整數(shù),我能在6個(gè)問題之內(nèi)猜出這個(gè)數(shù)
- 想好了就按【USER】開始游戲吧,【RST】重啟
- 小了按【S1】,大了按【S2】,正確按【USER】
- 第?個(gè)問題,是這個(gè)數(shù)嗎:??
- 大了??!那我再猜小一點(diǎn)
- 小了??!那我再猜大一點(diǎn)
- 哈哈,我猜到了吧!
- 按【USER】再玩一次(請(qǐng)先默想一個(gè)1~99的整數(shù))
- 你默想的數(shù)一定是??
- // 開始:0,長度10
- // 開始:10,長度30
- // 開始:40,長度24
- // 開始:64,長度25
- // 開始:89,長度14
- // 開始:103,長度11
- // 開始:114,長度11
- // 開始:125,長度9
- // 開始:134,長度26
- // 開始:160,長度10
- */
- char *str[] = {
- "0123456789",
- "請(qǐng)?jiān)谛闹心胍粋€(gè)1~99的整數(shù),我能在6個(gè)問題之內(nèi)猜出這個(gè)數(shù)",
- "想好了就按【USER】開始游戲吧,【RST】重啟",
- "小了按【S1】,大了按【S2】,正確按【USER】",
- "第?個(gè)問題,是這個(gè)數(shù)嗎:??",
- "大了?。∧俏以俨滦∫稽c(diǎn)",
- "小了?。∧俏以俨麓笠稽c(diǎn)",
- "哈哈,我猜到了吧!",
- "按【USER】再玩一次(請(qǐng)先默想一個(gè)1~99的整數(shù))",
- "你默想的數(shù)一定是??"
- };
- int pos[][2] = {
- {0, 10},
- {10, 30},
- {40, 24},
- {64, 25},
- {89, 14},
- {103, 11},
- {114, 11},
- {125, 9},
- {134, 26},
- {160, 10}
- };
- void display_string(int idx,int delay,int num1, int num2){
- int start=0;
- int len=0;
- start = pos[idx][0];
- len = pos[idx][1];
- if(idx==4 && num2==100) {
- len = len +1;
- }
- u8 no[len];
- for(int i=0;i<len;i++){
- no[i] = start+i;
- }
- // 4 "第?個(gè)問題,是這個(gè)數(shù)嗎:??",
- if(idx==4) {
- no[1] = num1;
- if(num2==100) {
- no[len-3] = 1;
- no[len-2] = 0;
- no[len-2] = 0;
- } else {
- no[len-2] = num2/10;
- no[len-1] = num2%10;
- }
- }
- OLED_Clear();
- OLED_ShowChineseString(0,0,no,len,16);
- usleep(delay*1000*1000);
- }
- // 主任務(wù)
- static void *GuessTask(const char *arg){
- (void)arg;
- gpio_button_init();
- oled_display_init();
- OLED_Clear();
- printf("請(qǐng)?jiān)谛闹心胍粋€(gè)1~100的整數(shù),我能在6個(gè)問題之內(nèi)猜出這個(gè)數(shù)是什么:\n");
- display_string(1,2,0,0);
- printf("想好了就按【USER】開始游戲吧,【RST】重啟\n");
- display_string(2,2,0,0);
- printf("小了按【S1】,大了按【S2】,正確按【USER】\n");
- display_string(3,0,0,0);
- key_event_t zf; //聲明char類型來存放輸入的字符
- char number; //電腦猜測的數(shù)字
- while ((zf = gpio_button_get())!=KEY_EVENT_NONE)
- {
- // getchar();//忽略回車
- char min_shu = 1; // 1是初始最小數(shù)。
- char max_shu = 100; // 100是初始最大數(shù)。
- if (zf == KEY_EVENT_USER)
- {
- int jishu = 1; // 計(jì)數(shù)用的,6個(gè)問題以內(nèi)嘛。
- while (1) // 條件一直為真,死循環(huán),能用break跳出循環(huán),或用return跳出整個(gè)函數(shù)。
- {
- number = (min_shu + max_shu) / 2; // 最小數(shù)和最大數(shù)的和除2 ,意思就是取它們的中間值。
- printf("\n第%d個(gè)問題,是這個(gè)數(shù)嗎:%d", jishu, number);
- display_string(4,0,jishu, number);
- zf = gpio_button_get();
- // getchar();//忽略回車
- if (zf == KEY_EVENT_S2)
- {
- printf("\n大了??!那我再猜小一點(diǎn)\n");
- display_string(5,2,0,0);
- max_shu = number - 1; //如果是大了,那最大值至少比目前的數(shù)小1。
- jishu++; //回答次數(shù)加1 ,如果你回答了電腦6次問題,電腦還沒有猜對(duì),那電腦就輸了。
- }
- if (zf == KEY_EVENT_S1)
- {
- printf("\n小了??!那我再猜大一點(diǎn)\n");
- display_string(6,2,0,0);
- min_shu = number + 1; //如果是小了,那最小值至少比目前的數(shù)大1。
- jishu++; //同上面,計(jì)數(shù)加1
- }
- if (zf == KEY_EVENT_USER)
- {
- // printf("y\n");
- printf("\n哈哈,我猜到了吧!\n");
- display_string(7,2,0,0);
- printf("按【USER】再玩一次(請(qǐng)?jiān)谛闹邢饶胍粋€(gè)1~100的整數(shù)),【RST】重啟\n");
- display_string(8,0,0,0);
- break;
- }
- if (jishu == 7)
- {
- printf("\n你默想的數(shù)一定是%d",(min_shu + max_shu) / 2);
- display_string(9,2,0,0);
- printf("\n按【USER】再玩一次(請(qǐng)?jiān)谛闹邢饶胍粋€(gè)1~100的整數(shù)),【RST】重啟\n");
- display_string(8,0,0,0);
- break;
- }
- }
- }
- else {
- printf("\n按鍵無效,請(qǐng)重新選擇(按【USER】開始,【RST】重啟):");
- }
- }
- return NULL;
- }
- // 程序入口
- static void GuessEntry(void)
- {
- osThreadAttr_t attr;
- WatchDogDisable();
- SetLogLevel(HILOG_LV_ERROR);
- attr.name = "GuessTask";
- attr.attr_bits = 0U;
- attr.cb_mem = NULL;
- attr.cb_size = 0U;
- attr.stack_mem = NULL;
- attr.stack_size = 1024;
- attr.priority = osPriorityNormal;
- if (osThreadNew((osThreadFunc_t)GuessTask, NULL, &attr) == NULL) {
- printf("[GuessNum] Falied to create GuessTask!\n");
- }
- }
- SYS_RUN(GuessEntry);
主邏輯代碼說明:
因?yàn)樵贠LED上面顯示字符(包括漢字),需要預(yù)先取得漢字的字模點(diǎn)陣數(shù)據(jù);在這個(gè)實(shí)例中,會(huì)有不同的提示語出現(xiàn),且未中文,為了方便處理,我將每句話的字模點(diǎn)陣數(shù)據(jù)單獨(dú)取出,所以定義了str[],pos[][2],以及display_string(),用于顯示對(duì)應(yīng)的語句。其最終調(diào)用oled/oled.c中的OLED_ShowChineseString()來將漢字輸出到OLED屏幕;特別的,語句4“第?個(gè)問題,是這個(gè)數(shù)嗎:??”需要處理具體數(shù)字,所以進(jìn)行了特殊的處理。
獲取按鍵的部分,在button/button.c中的gpio_button_get(),代碼隨后展示,用于獲取按鍵的狀態(tài)
OLED部分代碼:【以下為oled/oled.h,oled/oled.c和字模數(shù)據(jù)oled/oledfont.h請(qǐng)查看附件】
- #ifndef __OLED_H
- #define __OLED_H
- #define OLED_MODE 0
- #define SIZE 8
- #define XLevelL 0x00
- #define XLevelH 0x10
- #define Max_Column 128
- #define Max_Row 64
- #define Brightness 0xFF
- #define X_WIDTH 128
- #define Y_WIDTH 64
- #define OLED_CMD 0 //寫命令
- #define OLED_DATA 1 //寫數(shù)據(jù)
- #define u8 unsigned char
- #define u16 unsigned short
- #define u32 unsigned int
- //OLED控制用函數(shù)
- void delay_ms(unsigned int ms);
- void OLED_ColorTurn(u8 i);
- void OLED_DisplayTurn(u8 i);
- void OLED_WR_Byte(u8 dat,u8 cmd);
- void OLED_Set_Pos(u8 x, u8 y);
- void OLED_Display_On(void);
- void OLED_Display_Off(void);
- void OLED_Clear(void);
- void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 sizey);
- u32 oled_pow(u8 m,u8 n);
- void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 sizey);
- void OLED_ShowString(u8 x,u8 y,char *chr,u8 sizey);
- void OLED_ShowChinese(u8 x,u8 y,u8 no,u8 sizey);
- void OLED_ShowChineseString(u8 x,u8 y,u8 no[],u8 length,u8 sizey);
- void OLED_Direct_ShowString(u8 x,u8 y,char *chr,u8 sizey);
- void OLED_DrawBMP(u8 x,u8 y,u8 sizex, u8 sizey,u8 BMP[]);
- void OLED_Init(void);
- void oled_display_init(void);
- #endif
OLED漢字字模數(shù)據(jù)獲取方式:
在OLED上面顯示字符(包括漢字),本質(zhì)上是描點(diǎn),所以獲取對(duì)應(yīng)字符的點(diǎn)陣數(shù)據(jù)即可。
生成字模數(shù)據(jù)的工具為PCToLCD,設(shè)置為字符模式和C51格式;這個(gè)工具還可以用于取圖片的點(diǎn)陣數(shù)據(jù)。
具體獲取方式如下:
按鍵部分代碼:【以下為button/button.h,button/button.c請(qǐng)查看附件】
- #ifndef __BUTTON_H
- #define __BUTTON_H
- #include <hi_types_base.h>
- #define APP_DEMO_ADC
- #define ADC_TEST_LENGTH 64
- #define VLT_MIN 100
- #define STATUS_LEN 4
- // 按鍵狀態(tài)定義
- typedef enum
- {
- KEY_EVENT_NONE = 0,
- KEY_EVENT_S1,
- KEY_EVENT_S2,
- KEY_EVENT_USER
- } key_event_t;
- //獲取當(dāng)前按鍵
- key_event_t get_key_event(void);
- // ADC轉(zhuǎn)換
- hi_void convert_to_voltage(hi_u32 data_len);
- // ADC獲取
- void button_adc_test(void);
- // 設(shè)置 按鍵中斷響應(yīng)
- void gpio_button_init(void);
- // 獲取需要的按鍵狀態(tài)
- key_event_t gpio_button_get(void);
- #endif
按鍵部分代碼說明:
當(dāng)使用ADC方式來讀取按鍵狀態(tài)的時(shí)候,本質(zhì)上,是讀取了ADC輸入端口的數(shù)據(jù),這個(gè)數(shù)據(jù)進(jìn)過一定的轉(zhuǎn)換,能夠化為對(duì)應(yīng)的電壓數(shù)據(jù)。而不同的按鍵按下后,ADC端口讀取的電壓是不同的,并且是在一定范圍內(nèi)波動(dòng)的,對(duì)應(yīng)按鍵的電壓范圍在上述vlt_val_scopes中進(jìn)行了定義。我們獲取到了對(duì)應(yīng)的電壓數(shù)據(jù),然后與vlt_val_scopes每個(gè)范圍數(shù)據(jù)進(jìn)行對(duì)比,從而據(jù)此得到對(duì)應(yīng)的按鍵信息。
實(shí)際結(jié)果演示:
視頻地址: 鏈接: https://pan.baidu.com/s/1RtT8Wh3ZPbasJ-dK7x1QRg 提取碼: vkyh
完整代碼:
下載地址: https://pan.baidu.com/s/1RtT8Wh3ZPbasJ-dK7x1QRg 提取碼: vkyh