前面几章,代码经过重构,可能有点乱。这里给个最终版本,以供参考。
1、应用基类:
IScean.h
enum SceanResult{
SceanResult_EXIT = 1,
SceanResult_Done = 2
};
class IScean {
public:
IScean();
virtual ~IScean();
// 纯虚函数
virtual SceanResult tick(u32 ticks) = 0;
virtual int scean_init(cJSON* param) {return 0;};
virtual int scean_resume(void){return 0;};
virtual int scean_pause(void){return 0;};
protected:
map_t iconMap = NULL;
unsigned char* getImageResource(char* fn);
};
IScean.cpp
int iterate(any_t item, any_t data){
psram_heap_free((unsigned char *)data);
return MAP_OK;
}
IScean::IScean(){
iconMap = hashmap_new();
}
IScean::~IScean(){
any_t val;
hashmap_iterate(iconMap, iterate, val);
hashmap_free(iconMap);
}
unsigned char* IScean::getImageResource(char* fn){
int error;
unsigned int w,h;
unsigned char *val;
error= hashmap_get(iconMap, fn, (void**)(&val));
if(error == MAP_MISSING){
lodepng_decode32_file(&val, &w, &h, fn);
hashmap_put(iconMap, fn, val);
}
return val;
}
2、学习基类,继承应用基类:
StudyBase.h
class StudyBase : public IScean
{
public:
StudyBase(){};
virtual ~StudyBase(){};
protected:
u8 gameMode=0;
u8 winMode=0;
u8 isFinished=0;
u16 correntCount =0;
u16 wrongCount = 0;
u32 totalTime=0;
u32 lastTotalTime=0;
//×○△□
const char *controlInfo="EXIT: 返回 SELECT: 重新开始";
const char *answerInfo="× 选择 ○ 选择 □ 选择 △ 选择";
u16 answerBGColor[4] = {DBLUE, DRED, DGREEN, BROWN };
void startPrepare();
void errorDelay(u8 t);
u8 checkFinish();
void showScore();
void showTime();
u8 answerTitle[4][8] = {
{0x20,0xA1,0xC1,0x20,0x25,0x73,0x20, 0x0}, // × %s ,
{0x20,0xA1,0xF0,0x20,0x25,0x73,0x20, 0x0},
{0x20,0xA1,0xF5,0x20,0x25,0x73,0x20, 0x0},
{0x20,0xA1,0xF7,0x20,0x25,0x73,0x20, 0x0}};
private:
DisplayOption optionScore = {FONT_SIZE_1516, YELLOW, BLACK, 0, 0};
DisplayOption optionCorrentCount = {FONT_SIZE_1516, GREEN, BLACK, 0, 0};
DisplayOption optionWrongCount = {FONT_SIZE_1516, RED, BLACK, 0, 0};
DisplayOption optionTime = {FONT_SIZE_1516, YELLOW, BLACK, 0, 0};
DisplayOption optionDeCount = {FONT_SIZE_3232, WHITE, DRED, 1, 0};
DisplayOption optionDelay = {FONT_SIZE_2424, WHITE, DRED, 0, 0};
void finish();
};
StudyBase.cpp
#define Prepare_LOC 240, 100
void StudyBase::startPrepare(){
isFinished =0;
correntCount =0;
wrongCount = 0;
totalTime=0;
lastTotalTime=0;
clear_screen();
optionDeCount.backColor = DRED;
Display_String(Prepare_LOC, &optionDeCount, " 3 ");
tls_os_time_delay(1000);
optionDeCount.backColor = BROWN;
Display_String(Prepare_LOC, &optionDeCount, " 2 ");
tls_os_time_delay(1000);
optionDeCount.backColor = DGREEN;
Display_String(Prepare_LOC, &optionDeCount, " 1 ");
tls_os_time_delay(1000);
Display_Fill_Rectangle(0, 100, 480, 170 ,BLACK);
}
void StudyBase::errorDelay(u8 t){
for(;t>0;t--){
Display_String2(400, 50, &optionDelay," %d ",t);
tls_os_time_delay(1000);
}
Display_Fill_Rectangle2(400, 50, 80, 80, BLACK);
}
u8 StudyBase::checkFinish(){
switch (winMode) {
case 1:
if(totalTime > 180000) {
finish();
return 1;
}
break;
case 2:
if(correntCount+ wrongCount == 50) {
finish();
return 1;
}
break;
case 3:
if(wrongCount>0) {
finish();
return 1;
}
break;
}
return 0;
}
void StudyBase::finish(){
isFinished = 1;
show_status_info(controlInfo);
optionDeCount.backColor = DBLUE;
Display_String(Prepare_LOC, &optionDeCount, " 挑 战 结 束 ");
}
void StudyBase::showScore()
{
int score = correntCount* 60 - wrongCount * 20;
if(score<0 ) score =0;
Display_String2(10, 5, &optionScore, "得分: %06d ", score);
Display_String2(150, 5, &optionCorrentCount, "正确: %04d ", correntCount);
Display_String2(270, 5, &optionWrongCount, "错误: %04d ", wrongCount);
}
void StudyBase::showTime()
{
if(lastTotalTime == totalTime/1000){
return;
}
lastTotalTime = totalTime/1000;
if(winMode == 2)
Display_String2(400, 5, &optionTime, "%02d:%02d", ((180000- totalTime) / 1000) / 60, ((180000- totalTime) / 1000) % 60);
else
Display_String2(400, 5, &optionTime, "%02d:%02d", (totalTime / 1000) / 60, (totalTime / 1000) % 60);
}
3、语文学习类,继承学习基类:
YuWenTS.h
typedef struct {
uint16_t question; //问题诗索引
uint8_t line; //问题句子所在的行
u16 answer [4]; // 4个选项对应的行
uint8_t ans; //正确选项
} TangShiQuestion;
#define TangshiBoxStartX 10
#define TangshiBoxStartY 80
#define TangshiBoxWidth 280
#define TangshiBoxHeight 200
#define TangshiBoxLineHeight 20
class YuWenTS : public StudyBase
{
public:
YuWenTS();
~YuWenTS();
SceanResult tick(u32 ticks);
int scean_init(cJSON* param);
private:
void start();
void createTSQuestion();
void showTSQuetion();
void showTSAnswer();
void createSCQuestion();
void showSCQuetion();
void showSCAnswer();
void showTangshi();
void showSongCi();
void correct();
void wrong();
TangShiQuestion *currentQuestion;
int offsetY=0;
u16 boxHeight =0;
u16 answerX;
u16 answerY;
u16 answerIdx;
DisplayOption optionQuetion = {FONT_SIZE_2424, LIGHTBLUE, BLACK, 0, 1};
DisplayOption optionZY = {FONT_SIZE_1516, GRAYBLUE, BLACK, 0, 1};
DisplayOption optionLines = {FONT_SIZE_1516, WHITE, BLACK, 0, 1};
DisplayOption optionMiss = {FONT_SIZE_1516, RED, DGRAY, 0, 1};
DisplayOption optionAnswer[4] = {
{FONT_SIZE_1516, WHITE, answerBGColor[0], 0, 1},
{FONT_SIZE_1516, WHITE, answerBGColor[1], 0, 1},
{FONT_SIZE_1516, WHITE, answerBGColor[2], 0, 1},
{FONT_SIZE_1516, WHITE, answerBGColor[3], 0, 1},
};
u16 answerLocX = 300;
u16 answerLocY[4] = {165, 200, 235, 270};
unsigned char *DataBuff;
unsigned char *DataBuffIndex;
u16 YuWenCount;
u16 YuWenItemCount;
};
YuWenTS.cpp
#define YUW_Quetion_LOC_A 20, 25
#define YUW_Quetion_LOC_B 170, 55
//读取数据指定行
#define dataLine(idx) ((const char*)DataBuff+((idx) * 64))
//获取索引指定诗所在数据行数
#define dataLineIdx(idx) (((u16*)DataBuffIndex)[(idx)*2+2])
//获取索引指定诗句子总数
#define dataLineCount(idx) (((u16*)DataBuffIndex)[(idx)*2+3])
//获取指定诗指定句子所在行数
#define TangshiLineIdx(x,y) (dataLineIdx(x)+ y + 2 )
//获取指定诗指定句子所在数据位置
#define TangshiLine(x,y) (dataLine(dataLineIdx(x)+ y + 2 ))
//获取指定诗标题所在数据位置
#define TangshiLineTitle(x) (dataLine(dataLineIdx(x)))
//获取指定诗作者所在数据位置
#define TangshiLineAuthor(x) (dataLine(dataLineIdx(x)+ 1))
YuWenTS::YuWenTS(){
currentQuestion = new TangShiQuestion();
}
YuWenTS::~YuWenTS(){
delete currentQuestion;
psram_heap_free((void*)DataBuff);
}
int YuWenTS::scean_init(cJSON* param){
setKeyAdepterIntervalAll(200);
setKeyAdepterInterval(KEY_GPIO_A, 65535);
setKeyAdepterInterval(KEY_GPIO_B, 65535);
setKeyAdepterInterval(KEY_GPIO_C, 65535);
setKeyAdepterInterval(KEY_GPIO_D, 65535);
winMode= cJSON_GetObjectItem(param,"w")->valueint;
gameMode = cJSON_GetObjectItem(param,"m")->valueint;
printf("start chinese. winMode=%d, gameMode=%d.\n", winMode, gameMode);
switch (gameMode) {
case 1: // 唐诗300
fatfs_readFile("project/tangshi300gb.txt", &DataBuff);
fatfs_readFile("project/tangshi300index.txt", &DataBuffIndex);
YuWenCount = dataLineIdx(-1);
YuWenItemCount= dataLineCount(-1);
break;
case 2: // 宋词300
fatfs_readFile("project/songci300gb.txt", &DataBuff);
fatfs_readFile("project/songci300index.txt", &DataBuffIndex);
YuWenCount = dataLineIdx(-1);
YuWenItemCount= dataLineCount(-1);
break;
}
start();
return 0;
}
SceanResult YuWenTS::tick(u32 ticks){
if(KEY_EXIT) {
printf("goto top menu from About.\n");
return SceanResult_EXIT;
}
if(isFinished){
if(KEY_SEL) {
start();
return SceanResult_Done;
}
}else{
totalTime+=ticks;
showTime();
if(checkFinish()){
return SceanResult_Done;
}
if(KEY_A | KEY_B | KEY_C | KEY_D){
if(KEY_A && currentQuestion->ans == 0){
correct();
return SceanResult_Done;
}
if(KEY_B && currentQuestion->ans == 1){
correct();
return SceanResult_Done;
}
if(KEY_C && currentQuestion->ans == 2){
correct();
return SceanResult_Done;
}
if(KEY_D && currentQuestion->ans == 3){
correct();
return SceanResult_Done;
}
wrong();
}
if(KEY_UP && boxHeight> TangshiBoxHeight){
int oy = offsetY;
if(offsetY<0 ){
offsetY +=TangshiBoxLineHeight;
}else{
offsetY =0;
}
if(oy !=offsetY )
showTangshi();
}
if(KEY_DOWN && boxHeight> TangshiBoxHeight){
int oy = offsetY;
if(offsetY > TangshiBoxHeight - boxHeight) {
offsetY -=TangshiBoxLineHeight;
}else{
offsetY = TangshiBoxHeight - boxHeight;
}
if(oy !=offsetY )
showTangshi();
}
ran_max(10);
}
return SceanResult_Done;
}
void YuWenTS::showTSAnswer(){
Display_String(answerX, answerY, &optionMiss, dataLine(answerIdx));
for(int i=0;i<4;i++)
if(currentQuestion->ans !=i)
Display_Fill_Rectangle2(answerLocX, answerLocY[i]-2, SCREEN_WIDTH - answerLocX, 21 ,BLACK);
}
void YuWenTS::correct(){
correntCount++;
showScore();
if(checkFinish()==0){
createTSQuestion();
showTSQuetion();
showTangshi();
}
}
void YuWenTS::wrong(){
wrongCount++;
showScore();
showTSAnswer();
errorDelay(3);
if(checkFinish()==0){
createTSQuestion();
showTSQuetion();
showTangshi();
}
}
void YuWenTS::start(){
startPrepare();
show_status_info(answerInfo);
showScore();
createTSQuestion();
showTSQuetion();
showTangshi();
}
void YuWenTS::createTSQuestion()
{
u8 len=0;
offsetY=0;
do{
currentQuestion->question = ran_max(YuWenCount);
currentQuestion->line = ran_max(dataLineCount(currentQuestion->question));
len = strlen(TangshiLine(currentQuestion->question, currentQuestion->line));
}while(len < 15 || len > 25);
currentQuestion->ans = ran_max(4);
for(u8 i=0;i<4;i++){
if(i == currentQuestion->ans){
currentQuestion->answer[i] = TangshiLineIdx(currentQuestion->question,currentQuestion->line);
}else{
do{
currentQuestion->answer[i] = ran_max(YuWenItemCount);
}while(len != strlen(dataLine(currentQuestion->answer[i])));
}
}
}
void YuWenTS::showTSQuetion(){
Display_Fill_Rectangle(0,25, 480, 75, BLACK);
Display_String(YUW_Quetion_LOC_A, &optionQuetion, TangshiLineTitle(currentQuestion->question));
Display_String(YUW_Quetion_LOC_B, &optionZY, TangshiLineAuthor(currentQuestion->question));
for(u8 i=0;i<4;i++){
Display_Fill_Rectangle2(answerLocX, answerLocY[i]-2, SCREEN_WIDTH - answerLocX, 21 ,answerBGColor[i]);
Display_String2(answerLocX, answerLocY[i], &optionAnswer[i], (const char *)answerTitle[i], dataLine(currentQuestion->answer[i]));
}
}
void YuWenTS::showTangshi(){
Display_Fill_Rectangle2(TangshiBoxStartX,TangshiBoxStartY, TangshiBoxWidth, TangshiBoxHeight, BLACK);
u16 offx = 0;
int offy = offsetY;
boxHeight=1;
const char *sl;
for(u8 i=0;i< dataLineCount(currentQuestion->question) ;i++){
sl= TangshiLine(currentQuestion->question, i);
u16 len = strlen(sl);
if(sl[0] == 'g' ){
offx = 0;
offy += 20;
boxHeight++;
continue;
}
if(offx + len* 8 > TangshiBoxWidth){
offx = 0;
offy += 20;
boxHeight++;
}
if(offy <0 || offy>TangshiBoxHeight-TangshiBoxLineHeight ){
offx += len*8;
continue;
}
if (i== currentQuestion->line){
answerX=offx + TangshiBoxStartX;
answerY=offy + TangshiBoxStartY;
answerIdx = TangshiLineIdx(currentQuestion->question, i);
for(u8 j=0;j< len; j++){
Display_String(answerX+ j*8, answerY, &optionMiss, "_") ;
}
}else{
Display_String(offx + TangshiBoxStartX, offy + TangshiBoxStartY, &optionLines,
TangshiLine(currentQuestion->question, i));
}
offx += len*8;
}
boxHeight = boxHeight*TangshiBoxLineHeight;
if(boxHeight > TangshiBoxHeight){
Display_Fill_Rectangle(TangshiBoxStartX + TangshiBoxWidth - 10, TangshiBoxStartY,
TangshiBoxStartX + TangshiBoxWidth -1, TangshiBoxStartY + TangshiBoxHeight, GRAY);
double a = (double) (-offsetY) / boxHeight * TangshiBoxHeight;
double b = (double) (-offsetY + TangshiBoxHeight) / boxHeight * TangshiBoxHeight;
Display_Fill_Rectangle(TangshiBoxStartX + TangshiBoxWidth - 9,
TangshiBoxStartY + a,
TangshiBoxStartX + TangshiBoxWidth -2,
TangshiBoxStartY + b,
GREEN);
}
}
4、英语学习类,继承学习基类:
YingYu.h
typedef struct {
uint16_t question;
uint16_t answer[4];
uint8_t ans;
} EngQuestion;
class YingYu : public StudyBase
{
public:
YingYu();
~YingYu();
SceanResult tick(u32 ticks);
int scean_init(cJSON* param);
private:
EngQuestion *currentQuestion;
void start();
void createQuestion();
void createQuestionMode3();
void showQuetion();
void showAnswer();
void correct();
void wrong();
DisplayOption optionQuetion = {FONT_SIZE_2424, YELLOW, BLACK, 1, 1};
DisplayOption optionZY = {FONT_SIZE_2424, WHITE, BLACK, 1, 1};
DisplayOption optionAnswer[4] = {
{FONT_SIZE_1516, WHITE, answerBGColor[0], 0, 1},
{FONT_SIZE_1516, WHITE, answerBGColor[1], 0, 1},
{FONT_SIZE_1516, WHITE, answerBGColor[2], 0, 1},
{FONT_SIZE_1516, WHITE, answerBGColor[3], 0, 1}};
u16 Eng_Answer_LOC_X=20;
u16 Eng_Answer_LOC_Y[4] = {195,220,245,270};
char errEngWord[4][40];
unsigned char *DataBuff;
int YingYuCount;
};
YingYu.cpp
#define Eng_Quetion_LOC_A 240, 100
#define Eng_Quetion_LOC_B 240, 140
typedef struct YingYuWord
{
const char *en; //32
const char *sd; //32
const char *cn; //64
}YingYuWord;
typedef struct YingYuDuanYu
{
const char *en; //64
const char *cn; //64
}YingYuDuanYu;
YingYu::YingYu(){
currentQuestion = new EngQuestion();
}
YingYu::~YingYu(){
delete currentQuestion;
psram_heap_free((void*)DataBuff);
}
int YingYu::scean_init(cJSON* param){
setKeyAdepterIntervalAll(200);
setKeyAdepterInterval(KEY_GPIO_A, 65535);
setKeyAdepterInterval(KEY_GPIO_B, 65535);
setKeyAdepterInterval(KEY_GPIO_C, 65535);
setKeyAdepterInterval(KEY_GPIO_D, 65535);
winMode= cJSON_GetObjectItem(param,"w")->valueint;
gameMode = cJSON_GetObjectItem(param,"m")->valueint;
printf("start chinese. winMode=%d, gameMode=%d.\n", winMode, gameMode);
fatfs_readFile(cJSON_GetObjectItem(param,"f")->valuestring, &DataBuff);
YingYuCount = DataBuff[12] | (DataBuff[13]<<8) | (DataBuff[14]<<16)| (DataBuff[15]<<24);
start();
return 0;
}
void YingYu::start(){
startPrepare();
show_status_info(answerInfo);
showScore();
createQuestion();
showQuetion();
}
#define dataDC(idx) ((const char*)DataBuff+((idx) * 128) + 64)
#define dataZY(idx) ((const char*)DataBuff+((idx) * 128) + 96)
#define dataCN(idx) ((const char*)DataBuff+((idx) * 128) + 128)
void YingYu::showQuetion()
{
u8 i;
for(i=0;i<4;i++)
Display_Fill_Rectangle2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i]-2, 440, 21 ,answerBGColor[i]);
Display_Fill_Rectangle2(0,100, 480, 80, BLACK);
printf("q=%d, a=%d, a1=%d, a2=%d, a3=%d, a4=%d\n",currentQuestion->question,currentQuestion->ans,currentQuestion->answer[0]
,currentQuestion->answer[1]
,currentQuestion->answer[2]
,currentQuestion->answer[3] );
switch (gameMode) {
case 1:
Display_String(Eng_Quetion_LOC_A, &optionQuetion, dataDC(currentQuestion->question));
Display_String2(Eng_Quetion_LOC_B, &optionZY, "[%s]", dataZY(currentQuestion->question));
for(i=0;i<4;i++)
Display_String2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i], &optionAnswer[i], (const char *)answerTitle[i], dataCN(currentQuestion->answer[i]));
break;
case 2:
Display_String(Eng_Quetion_LOC_A, &optionQuetion, dataCN(currentQuestion->question));
Display_String2(Eng_Quetion_LOC_B, &optionZY, "[%s]", dataZY(currentQuestion->question));
for(i=0;i<4;i++)
Display_String2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i], &optionAnswer[i], (const char *)answerTitle[i], dataDC(currentQuestion->answer[i]));
break;
case 3:
Display_String(Eng_Quetion_LOC_A, &optionQuetion, dataCN(currentQuestion->question));
Display_String2(Eng_Quetion_LOC_B, &optionZY, "[%s]", dataZY(currentQuestion->question));
for(i=0;i<4;i++)
Display_String2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i], &optionAnswer[i], (const char *)answerTitle[i], errEngWord[i]);
break;
case 4:
Display_String(Eng_Quetion_LOC_A, &optionQuetion, dataDC(currentQuestion->question));
for(i=0;i<4;i++)
Display_String2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i], &optionAnswer[i], (const char *)answerTitle[i], dataCN(currentQuestion->answer[i]));
break;
case 5:
Display_String(Eng_Quetion_LOC_A, &optionQuetion, dataCN(currentQuestion->question));
for(i=0;i<4;i++)
Display_String2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i], &optionAnswer[i], (const char *)answerTitle[i], dataDC(currentQuestion->answer[i]));
break;
}
}
void YingYu::showAnswer(){
for(u8 i=0;i<4;i++)
if(currentQuestion->ans !=i) Display_Fill_Rectangle2(Eng_Answer_LOC_X, Eng_Answer_LOC_Y[i]-2, 440, 21 ,BLACK);
}
SceanResult YingYu::tick(u32 ticks){
if(KEY_EXIT) {
printf("goto top menu from About.\n");
return SceanResult_EXIT;
}
if(isFinished){
if(KEY_SEL) {
start();
return SceanResult_Done;
}
}else{
totalTime+=ticks;
showTime();
if(checkFinish()){
return SceanResult_Done;
}
if(KEY_A | KEY_B | KEY_C | KEY_D){
if(KEY_A && currentQuestion->ans == 0){
correct();
return SceanResult_Done;
}
if(KEY_B && currentQuestion->ans == 1){
correct();
return SceanResult_Done;
}
if(KEY_C && currentQuestion->ans == 2){
correct();
return SceanResult_Done;
}
if(KEY_D && currentQuestion->ans == 3){
correct();
return SceanResult_Done;
}
wrong();
}
ran_max(10);
}
return SceanResult_Done;
}
void YingYu::correct(){
correntCount++;
showScore();
if(checkFinish()==0){
createQuestion();
showQuetion();
}
}
void YingYu::wrong(){
wrongCount++;
showScore();
showAnswer();
errorDelay(3);
if(checkFinish()==0){
createQuestion();
showQuetion();
}
}
void YingYu::createQuestion()
{
currentQuestion->question = ran_max(YingYuCount);
currentQuestion->ans = ran_max(4);
for(u8 i=0;i<4;i++){
if(i == currentQuestion->ans){
currentQuestion->answer[i] = currentQuestion->question;
}else{
currentQuestion->answer[i] = ran_max(YingYuCount);
}
}
if(gameMode == 3){
createQuestionMode3();
}
}
void YingYu::createQuestionMode3()
{
uint8_t i,e1,e2;
char ne1,ne2;
u8 len = strlen(dataDC(currentQuestion->question));
for(i=0; i<4; i++) {
memset(errEngWord[i], 0, 40);
memcpy(errEngWord[i], dataDC(currentQuestion->question), len);
}
e1 = ran_max(len);
while(errEngWord[0][e1] < 97 || errEngWord[0][e1] > 122) {
e1 = ran_max(len);
}
e2 = e1;
while(e2 == e1 || errEngWord[0][e2] < 97 || errEngWord[0][e2] > 122) {
e2 = ran_max(len);
}
ne1 = errEngWord[0][e1] - ran_max(25) -1;
if(ne1 < 97) {
ne1 =ne1 + 26;
}
ne2 = errEngWord[0][e2] - ran_max(25) -1;
if(ne2 < 97) {
ne2 =ne2 + 26;
}
errEngWord[(currentQuestion->ans + 1) % 4][e1] = ne1;
errEngWord[(currentQuestion->ans + 2) % 4][e1] = ne1;
errEngWord[(currentQuestion->ans + 2) % 4][e2] = ne2;
errEngWord[(currentQuestion->ans + 3) % 4][e2] = ne2;
}