鴻蒙基于WiFi IoT套件開發的猜數字小游戲代碼分享
猜數字是一個很經典的小游戲,也是編程開發入門的典型,以下為基于WiFi IoT套件開發的猜數字小游戲的具體開發過程和效果。
基本規則:
由甲方(玩家)默想一個1-99(包含)內的任意數字(整數),然后由乙方進行猜測,并詢問甲方猜測的數字是大了還是小了,甲方根據實際情況進行回復,則乙方最多問6個問題,就一定能夠猜中甲方默想的數字。
基本原理:
乙方問最多6次,包括最后一次說出猜中的數字,實際上乙方最多有7次猜測的機會。
而使用二分進行查找,2^7=128,則99以內的數字,完全可以覆蓋,因此乙方絕對可以猜中。
實現概述:
以上的基本規則和基本原理明確了,我們要在WiFi IoT套件上實現,并且甲方需要參與,需要處理以下三個部分:
- 猜數字的主邏輯
- 使用OLED屏幕顯示提示信息,讓玩家進行互動操作:我們需要在屏幕上顯示漢字,進行玩家當前猜測的數字,以及玩家按鍵后告知玩家結果
- 使用按鍵接收玩家操作(大了或者小了等):在這個實例中,我們使用了ADC方式來讀取按鍵信息,從而獲得玩家具體操作。所使用的按鍵為核心板上的USR按鍵,和OLED板上的S1,S2按鍵。使用ADC方式讀取的時候,他們所使用的輸入端口為GPIO5/ADC2,具體的按鍵作用如下:
- USR:開始游戲,或者確認
- S1:如果猜小了,則玩家按S1告知
- S2:如果猜大了,則玩家按S2告知
原始代碼修改處理:【代碼基礎為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復用端口設置:vendor/hisi/hi3861/hi3861/app/wifiiot_app/init/app_io_init.c
- #ifdef CONFIG_I2C_SUPPORT
- /* I2C IO復用也可以選擇3/4; 9/10,根據產品設計選擇 */
- // 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
- 請在心中默想一個1~99的整數,我能在6個問題之內猜出這個數
- 想好了就按【USER】開始游戲吧,【RST】重啟
- 小了按【S1】,大了按【S2】,正確按【USER】
- 第?個問題,是這個數嗎:??
- 大了啊!那我再猜小一點
- 小了啊!那我再猜大一點
- 哈哈,我猜到了吧!
- 按【USER】再玩一次(請先默想一個1~99的整數)
- 你默想的數一定是??
- // 開始:0,長度10
- // 開始:10,長度30
- // 開始:40,長度24
- // 開始:64,長度25
- // 開始:89,長度14
- // 開始:103,長度11
- // 開始:114,長度11
- // 開始:125,長度9
- // 開始:134,長度26
- // 開始:160,長度10
- */
- char *str[] = {
- "0123456789",
- "請在心中默想一個1~99的整數,我能在6個問題之內猜出這個數",
- "想好了就按【USER】開始游戲吧,【RST】重啟",
- "小了按【S1】,大了按【S2】,正確按【USER】",
- "第?個問題,是這個數嗎:??",
- "大了啊!那我再猜小一點",
- "小了啊!那我再猜大一點",
- "哈哈,我猜到了吧!",
- "按【USER】再玩一次(請先默想一個1~99的整數)",
- "你默想的數一定是??"
- };
- 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 "第?個問題,是這個數嗎:??",
- 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);
- }
- // 主任務
- static void *GuessTask(const char *arg){
- (void)arg;
- gpio_button_init();
- oled_display_init();
- OLED_Clear();
- printf("請在心中默想一個1~100的整數,我能在6個問題之內猜出這個數是什么:\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; //電腦猜測的數字
- while ((zf = gpio_button_get())!=KEY_EVENT_NONE)
- {
- // getchar();//忽略回車
- char min_shu = 1; // 1是初始最小數。
- char max_shu = 100; // 100是初始最大數。
- if (zf == KEY_EVENT_USER)
- {
- int jishu = 1; // 計數用的,6個問題以內嘛。
- while (1) // 條件一直為真,死循環,能用break跳出循環,或用return跳出整個函數。
- {
- number = (min_shu + max_shu) / 2; // 最小數和最大數的和除2 ,意思就是取它們的中間值。
- printf("\n第%d個問題,是這個數嗎:%d", jishu, number);
- display_string(4,0,jishu, number);
- zf = gpio_button_get();
- // getchar();//忽略回車
- if (zf == KEY_EVENT_S2)
- {
- printf("\n大了啊!那我再猜小一點\n");
- display_string(5,2,0,0);
- max_shu = number - 1; //如果是大了,那最大值至少比目前的數小1。
- jishu++; //回答次數加1 ,如果你回答了電腦6次問題,電腦還沒有猜對,那電腦就輸了。
- }
- if (zf == KEY_EVENT_S1)
- {
- printf("\n小了啊!那我再猜大一點\n");
- display_string(6,2,0,0);
- min_shu = number + 1; //如果是小了,那最小值至少比目前的數大1。
- jishu++; //同上面,計數加1
- }
- if (zf == KEY_EVENT_USER)
- {
- // printf("y\n");
- printf("\n哈哈,我猜到了吧!\n");
- display_string(7,2,0,0);
- printf("按【USER】再玩一次(請在心中先默想一個1~100的整數),【RST】重啟\n");
- display_string(8,0,0,0);
- break;
- }
- if (jishu == 7)
- {
- printf("\n你默想的數一定是%d",(min_shu + max_shu) / 2);
- display_string(9,2,0,0);
- printf("\n按【USER】再玩一次(請在心中先默想一個1~100的整數),【RST】重啟\n");
- display_string(8,0,0,0);
- break;
- }
- }
- }
- else {
- printf("\n按鍵無效,請重新選擇(按【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);
主邏輯代碼說明:
因為在OLED上面顯示字符(包括漢字),需要預先取得漢字的字模點陣數據;在這個實例中,會有不同的提示語出現,且未中文,為了方便處理,我將每句話的字模點陣數據單獨取出,所以定義了str[],pos[][2],以及display_string(),用于顯示對應的語句。其最終調用oled/oled.c中的OLED_ShowChineseString()來將漢字輸出到OLED屏幕;特別的,語句4“第?個問題,是這個數嗎:??”需要處理具體數字,所以進行了特殊的處理。
獲取按鍵的部分,在button/button.c中的gpio_button_get(),代碼隨后展示,用于獲取按鍵的狀態
OLED部分代碼:【以下為oled/oled.h,oled/oled.c和字模數據oled/oledfont.h請查看附件】
- #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 //寫數據
- #define u8 unsigned char
- #define u16 unsigned short
- #define u32 unsigned int
- //OLED控制用函數
- 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漢字字模數據獲取方式:
在OLED上面顯示字符(包括漢字),本質上是描點,所以獲取對應字符的點陣數據即可。
生成字模數據的工具為PCToLCD,設置為字符模式和C51格式;這個工具還可以用于取圖片的點陣數據。
具體獲取方式如下:
按鍵部分代碼:【以下為button/button.h,button/button.c請查看附件】
- #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
- // 按鍵狀態定義
- typedef enum
- {
- KEY_EVENT_NONE = 0,
- KEY_EVENT_S1,
- KEY_EVENT_S2,
- KEY_EVENT_USER
- } key_event_t;
- //獲取當前按鍵
- key_event_t get_key_event(void);
- // ADC轉換
- hi_void convert_to_voltage(hi_u32 data_len);
- // ADC獲取
- void button_adc_test(void);
- // 設置 按鍵中斷響應
- void gpio_button_init(void);
- // 獲取需要的按鍵狀態
- key_event_t gpio_button_get(void);
- #endif
按鍵部分代碼說明:
當使用ADC方式來讀取按鍵狀態的時候,本質上,是讀取了ADC輸入端口的數據,這個數據進過一定的轉換,能夠化為對應的電壓數據。而不同的按鍵按下后,ADC端口讀取的電壓是不同的,并且是在一定范圍內波動的,對應按鍵的電壓范圍在上述vlt_val_scopes中進行了定義。我們獲取到了對應的電壓數據,然后與vlt_val_scopes每個范圍數據進行對比,從而據此得到對應的按鍵信息。
實際結果演示:
視頻地址: 鏈接: https://pan.baidu.com/s/1RtT8Wh3ZPbasJ-dK7x1QRg 提取碼: vkyh
完整代碼:
下載地址: https://pan.baidu.com/s/1RtT8Wh3ZPbasJ-dK7x1QRg 提取碼: vkyh