//五子棋应用 #include "stdlib.h" #include "sys.h" #include "systick.h" #include "key.h" #include "lcd.h" #include "hc12.h" #include "GameEngine.h" #include "APP_Gobang.h" /**************************************** 私有定义 ****************************************/ #define MAP_COLOR 0xdc84 #define BROADWIDTH 15 #define WIDTH (BROADWIDTH + 1) #define BLACK_TURN 0 #define WHITE_TURN 1 #define NO_CHESS 0 #define BLACK_CHESS 1 #define WHITE_CHESS 2 #define CHECK_X 0 #define CHECK_Y 1 #define CHECK_DIAG_LEFT 2 #define CHECK_DIAG_RIGHT 3 #define CHESS_RADIUS 5 #define CONNETING_STATUS 0 #define BATTLING_STATUS 1 #define NONE_MSG 0 #define REQUEST_MSG 'R' #define AGREE_MSG 'A' #define REFUSE_MSG 'N' #define CONFIRM_MSG 'X' #define START_BYTE '#' #define END_BYTE '&' #define Random() rand() /*****************************************************************************************/ /**************************************** 全局变量 ****************************************/ //棋盘交点阵 int map[WIDTH][WIDTH] = {{0, 0}}; //光标位置 int cursor_x = 7; int cursor_y = 7; //记录执子方 int turn = BLACK_TURN; //棋子类型 int chess_kind = BLACK_CHESS; //我方执子类型 int my_turn = BLACK_TURN; //当前所处状态 int status = CONNETING_STATUS; //联机消息 uint8_t connetion_msg = NONE_MSG; uint8_t interval = 240 / WIDTH; uint16_t x_start, x_end, y_start, y_end; /*****************************************************************************************/ /**************************************** 私有函数 ****************************************/ void APP_Gobang_Init(void); void APP_Gobang_DispGobang(void); void APP_Gobang_DispText(void); void APP_Gobang_DispMap(void); void APP_Gobang_DispChess(void); void APP_Gobang_MoveChess(void); void APP_Gobang_DispCursor(void); int APP_Gobang_CheckNum(int check_type); void APP_Gobang_Msg(uint8_t *head, uint8_t *content); void APP_Gobang_SendPosition(void); void APP_Gobang_RevPosition(void); uint8_t APP_Gobang_Connect(void); uint8_t APP_Gobang_InitiateConnet(void); uint8_t APP_Gobang_ReplyConnect(void); void APP_Gobang_DecideMyTurn(void); void APP_Gobang_RevMyTurn(void); void APP_Gobang_ConfirmBeforeStart(void); void APP_Gobang_ConnectRevHandler(uint8_t byte); /*****************************************************************************************/ /** * @brief 启动五子棋 */ void APP_Gobang_Launcher(void) { srand(SysTick->VAL); APP_Gobang_Init(); APP_Gobang_DispGobang(); if (APP_Gobang_Connect() == 0) return; status = BATTLING_STATUS; HC12_BindReceiveHandle(NULL); HC12_ClearReceive; KEY_ClearKey(); while (1) { APP_Gobang_DispGobang(); if (turn == my_turn) APP_Gobang_MoveChess(); else APP_Gobang_RevPosition(); turn = !turn; //更换执子方 //判断胜负 if ( APP_Gobang_CheckNum(CHECK_X) >= 5 || APP_Gobang_CheckNum(CHECK_Y) >= 5 || APP_Gobang_CheckNum(CHECK_DIAG_LEFT) >= 5 || APP_Gobang_CheckNum(CHECK_DIAG_RIGHT) >= 5) { if (chess_kind == BLACK_CHESS ? 1 : 0) APP_Gobang_Msg("游戏结束", "黑棋获得胜利!"); else APP_Gobang_Msg("游戏结束", "白棋获得胜利!"); return; } } } /** * @brief 初始化变量 */ void APP_Gobang_Init(void) { ge_font_print_set.font_size = FONT_16; for (int i = 0; i < WIDTH; i++) for (int j = 0; j < WIDTH; j++) map[i][j] = NO_CHESS; cursor_x = cursor_y = 7; turn = BLACK_TURN; chess_kind = BLACK_CHESS; status = CONNETING_STATUS; connetion_msg = NONE_MSG; x_start = (LCD_WIDTH - LCD_HEIGHT) / 2 + interval / 2; x_end = x_start + (WIDTH - 1) * interval; y_start = interval / 2; y_end = y_start + (WIDTH - 1) * interval; } /** * @brief 绘制游戏 */ void APP_Gobang_DispGobang(void) { GE_Draw_ClrAll(MAP_COLOR); APP_Gobang_DispMap(); APP_Gobang_DispChess(); APP_Gobang_DispCursor(); if (status == BATTLING_STATUS) APP_Gobang_DispText(); GE_Draw_Disp(); } /** * @brief 绘制文字 * @param head: 标题 * @param content: 内容 */ void APP_Gobang_DispText(void) { if (turn == my_turn) GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, interval / 2, BORDER_MAX, BORDER_MAX, "我方"); else GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, interval / 2, BORDER_MAX, BORDER_MAX, "对方"); GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, interval / 2 + FONT_16, BORDER_MAX, BORDER_MAX, "回合"); GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, LCD_HEIGHT - interval / 2 - 2 * FONT_16, BORDER_MAX, BORDER_MAX, "我方"); if (my_turn == BLACK_TURN) GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, LCD_HEIGHT - interval / 2 - FONT_16, BORDER_MAX, BORDER_MAX, "执黑"); else GE_Font_Print_WithSet(LCD_WIDTH - FONT_16 * 2, LCD_HEIGHT - interval / 2 - FONT_16, BORDER_MAX, BORDER_MAX, "执白"); } /** * @brief 绘制棋盘 */ void APP_Gobang_DispMap(void) { uint8_t i = 0; for (i = 0; i < WIDTH; i++) { GE_Draw_Line(x_start, y_start + i * interval, x_end, y_start + i * interval, BLACK); GE_Draw_Line(x_start + i * interval, y_start, x_start + i * interval, y_end, BLACK); } } /** * @brief 绘制棋子 */ void APP_Gobang_DispChess(void) { uint8_t i, j; for (i = 0; i < WIDTH; i++) { for (j = 0; j < WIDTH; j++) { if (map[i][j] == BLACK_CHESS) { GE_Draw_FillCircle(x_start + j * interval, y_start + i * interval, CHESS_RADIUS, BLACK); } else if (map[i][j] == WHITE_CHESS) { GE_Draw_FillCircle(x_start + j * interval, y_start + i * interval, CHESS_RADIUS, WHITE); } } } } /** * @brief 移动棋子 */ void APP_Gobang_MoveChess(void) { while (1) { switch (KEY_GetKeyWait()) { case JOY_U_DOWN: { cursor_x--; if (cursor_x < 0) cursor_x = WIDTH - 1; APP_Gobang_DispGobang(); } break; case JOY_D_DOWN: { cursor_x++; if (cursor_x > WIDTH - 1) cursor_x = 0; APP_Gobang_DispGobang(); } break; case JOY_L_DOWN: { cursor_y--; if (cursor_y < 0) cursor_y = WIDTH - 1; APP_Gobang_DispGobang(); } break; case JOY_R_DOWN: { cursor_y++; if (cursor_y > WIDTH - 1) cursor_y = 0; APP_Gobang_DispGobang(); } break; case JOY_OK_DOWN: { if (map[cursor_x][cursor_y] != NO_CHESS) break; chess_kind = map[cursor_x][cursor_y] = turn == BLACK_TURN ? BLACK_CHESS : WHITE_CHESS; APP_Gobang_SendPosition(); APP_Gobang_DispGobang(); return; } } } } /** * @brief 显示光标 */ void APP_Gobang_DispCursor(void) { GE_Draw_Circle(x_start + cursor_y * interval, y_start + cursor_x * interval, CHESS_RADIUS, RED); } /** * @brief 检查某个方向上的连子数 * @param check_type: 检查方向 * @retval 连子数 */ int APP_Gobang_CheckNum(int check_type) { int temp_x = cursor_x, temp_y = cursor_y; int num = 0; while (temp_x >= 0 && temp_x < WIDTH && temp_y >= 0 && temp_y < WIDTH && map[temp_x][temp_y] == chess_kind) { num++; switch (check_type) { case CHECK_X: temp_x--; break; case CHECK_Y: temp_y--; break; case CHECK_DIAG_LEFT: temp_x--; temp_y--; break; case CHECK_DIAG_RIGHT: temp_x--; temp_y++; } } switch (check_type) { case CHECK_X: temp_x = cursor_x + 1; break; case CHECK_Y: temp_y = cursor_y + 1; ; break; case CHECK_DIAG_LEFT: temp_x = cursor_x + 1; temp_y = cursor_y + 1; break; case CHECK_DIAG_RIGHT: temp_x = cursor_x + 1; temp_y = cursor_y - 1; } while (temp_x >= 0 && temp_x < WIDTH && temp_y >= 0 && temp_y < WIDTH && map[temp_x][temp_y] == chess_kind) { num++; switch (check_type) { case CHECK_X: temp_x++; break; case CHECK_Y: temp_y++; break; case CHECK_DIAG_LEFT: temp_x++; temp_y++; break; case CHECK_DIAG_RIGHT: temp_x++; temp_y--; } } return num; } /** * @brief 消息框,任意键按下后退出 * @param head: 标题 * @param content: 内容 */ void APP_Gobang_Msg(uint8_t *head, uint8_t *content) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, head, content, NULL); KEY_WaitKey(JOY_L); } void APP_Gobang_SendPosition(void) { uint8_t position[6]; position[0] = START_BYTE; position[1] = '0' + cursor_x / 10; position[2] = '0' + cursor_x % 10; position[3] = '0' + cursor_y / 10; position[4] = '0' + cursor_y % 10; position[5] = END_BYTE; HC12_SendBuff(position, 6); } void APP_Gobang_RevPosition(void) { uint8_t byte; uint8_t position[5]; while (1) { if (HC12_Receive(&byte) == 1) { if (byte == START_BYTE) { HC12_ReceiveBuffUntil(position, END_BYTE, 500); cursor_x = (position[0] - '0') * 10 + position[1] - '0'; cursor_y = (position[2] - '0') * 10 + position[3] - '0'; if (map[cursor_x][cursor_y] == NO_CHESS) { chess_kind = map[cursor_x][cursor_y] = turn == BLACK_TURN ? BLACK_CHESS : WHITE_CHESS; APP_Gobang_DispGobang(); break; } } } } } uint8_t APP_Gobang_Connect(void) { HC12_BindReceiveHandle(APP_Gobang_ConnectRevHandler); GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "按“OK”以发起联机", NULL); while (1) { switch (KEY_GetKey()) { case JOY_OK_DOWN: { if (APP_Gobang_InitiateConnet()) { uint8_t *head = (my_turn == BLACK_TURN ? "本局我方执黑" : "本局我方执白"); GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, head, "按“OK”开始游戏", NULL); Delay_ms(500); KEY_WaitKey(JOY_OK); APP_Gobang_ConfirmBeforeStart(); return 1; } else { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "按“OK”以发起联机", NULL); } } break; case JOY_L_DOWN: { return 0; } break; } if (connetion_msg == REQUEST_MSG) { if (APP_Gobang_ReplyConnect()) { uint8_t *head = (my_turn == BLACK_TURN ? "本局我方执黑" : "本局我方执白"); GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, head, "按“OK”开始游戏", NULL); Delay_ms(500); KEY_WaitKey(JOY_OK); APP_Gobang_ConfirmBeforeStart(); return 1; } else { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "按“OK”以发起联机", NULL); } } } } uint8_t APP_Gobang_InitiateConnet(void) { int32_t start_time = SysTick_GetRunTime(); uint8_t msg[] = {START_BYTE, REQUEST_MSG}; HC12_SendBuff(msg, 2); HC12_ClearReceive; GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "当前未联机", "请求已发送,等待对方响应...", NULL); while (1) { if (SysTick_CheckRunTime(start_time) > 10000) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "联机失败", "对方无响应,按“OK”退出", NULL); KEY_WaitKey(JOY_OK); connetion_msg = NONE_MSG; return 0; } if (connetion_msg == AGREE_MSG) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "联机成功", "按“OK”进入游戏", NULL); //connetion_msg = NONE_MSG; KEY_WaitKey(JOY_OK); return 1; } else if (connetion_msg == REFUSE_MSG) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "联机失败", "对方拒绝联机,按“OK”退出", NULL); KEY_WaitKey(JOY_OK); connetion_msg = NONE_MSG; return 0; } } } uint8_t APP_Gobang_ReplyConnect(void) { uint8_t content[2][GE_GUI_MENUBOX_CONTENT_LEN] = {"是", "否"}; GE_Draw_Fill(60, 75, 200, 90, WHITE); switch (GE_GUI_MenuBox(60, 75, 200, 90, "对方请求联机,是否接受?", 2, content, NULL)) { case 0: return 0; break; case 1: { uint8_t msg[] = {START_BYTE, AGREE_MSG}; HC12_SendBuff(msg, 2); APP_Gobang_DecideMyTurn(); GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "联机成功", "按“OK”进入游戏", NULL); KEY_WaitKey(JOY_OK); return 1; } break; case 2: { uint8_t msg[] = {START_BYTE, REFUSE_MSG}; HC12_SendBuff(msg, 2); GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "联机失败", "已拒绝联机,按“OK”退出", NULL); KEY_WaitKey(JOY_OK); return 0; } break; } } void APP_Gobang_DecideMyTurn(void) { my_turn = Random() % 2; uint8_t msg[] = {START_BYTE, !my_turn}; HC12_SendBuff(msg, 2); } void APP_Gobang_ConfirmBeforeStart(void) { if (my_turn == BLACK_TURN) { GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "游戏即将开始", "正在确认白色方设备状态...", NULL); while (connetion_msg != CONFIRM_MSG) { Delay_ms(100); } GE_Draw_Fill(60, 75, 200, 90, WHITE); GE_GUI_MsgBox(60, 75, 200, 90, "准备就绪", "按“OK”开始游戏", NULL); Delay_ms(500); KEY_WaitKey(JOY_OK); } else { uint8_t msg[] = {START_BYTE, CONFIRM_MSG}; HC12_SendBuff(msg, 2); } } void APP_Gobang_ConnectRevHandler(uint8_t byte) { static uint8_t is_receiving = FALSE; if (is_receiving == FALSE && byte == START_BYTE) { is_receiving = TRUE; return; } if (is_receiving == TRUE) { if (byte == REQUEST_MSG || byte == AGREE_MSG || byte == REFUSE_MSG || byte == CONFIRM_MSG) { connetion_msg = byte; is_receiving = FALSE; } else if (byte == BLACK_TURN || byte == WHITE_TURN) { my_turn = byte; is_receiving = FALSE; } } }