#2020征文-开发板# 基于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
乔帮主周六晚上辛苦了,回答了那么多问题。和粉丝们互动到10点多。
请问乔老师能把完整代码贴在文章附件下吗,百度云实在太慢了
已上传。
征文大赛正在火热进行中,楼主这么优秀的文章真的不考虑参加吗?
例如这篇在标题开头添加“#2020征文-开发板#“,
再找到相应的专栏位置投稿,
就可以参加比赛啦!
详细步骤可以点击链接https://harmonyos.51cto.com/posts/1940进行了解
用更多的文章来赢取更多的奖励和人气吧!期待楼主后续的活跃表现。
已经设置好啦!