Files
Gobang-Game/src/gui/gui_game.c
T
Serendipity e41e856fe0 feat(gui): 经典木纹风格 UI 视觉美化
- 配色方案:暖木色棋盘、米白窗口背景、深棕按钮,统一视觉语言
- 主菜单:IupFrame 分组(选择模式/功能),主按钮深棕底白字
- 棋盘渲染:渐变棋子(3层同心圆模拟立体感)、坐标标注(A-O/1-15)、
  蓝色圆环最后落子标记、深色棋盘边框、加大星位天元
- 侧边面板:IupFrame 包裹对局信息,按钮统一样式
- 设置页面:IupFrame 分组(基本/AI/大模型),按钮样式统一
- 网络/复盘对话框:IupFrame 分组,配色和按钮样式统一
- 全局字体:SimHei 11 作为默认字体

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 15:57:06 +08:00

784 lines
24 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "gui.h"
#include "gui_internal.h"
#include "gui_menu.h"
#include "globals.h"
#include "gobang.h"
#include "ai.h"
#include "record.h"
#include "network.h"
#include "llm_ai.h"
#ifdef _WIN32
#include <windows.h>
#endif
#include <iup.h>
#include <iupdraw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static Ihandle *timer = NULL; // 网络轮询定时器
static Ihandle *llm_timer = NULL; // LLM异步轮询定时器
/**
* @brief 网络事件轮询回调
*/
static int timer_cb(Ihandle *ih)
{
(void)ih;
if (gui_game_mode != 3 || game_over)
return IUP_DEFAULT;
NetworkMessage msg;
// 非阻塞接收消息
if (receive_network_message(&msg, 0))
{
if (msg.type == MSG_MOVE)
{
int bx = msg.x;
int by = msg.y;
int pid = msg.player_id;
if (have_space(bx, by))
{
player_move(bx, by, pid);
if (check_win(bx, by, pid))
{
game_over = 1;
sprintf(status_message, "对手获胜!");
IupMessage("游戏结束", "对手获胜!");
}
else
{
current_player_gui = network_state.local_player_id;
sprintf(status_message, "轮到你落子");
}
update_ui_labels();
if (board_canvas)
IupUpdate(board_canvas);
}
}
else if (msg.type == MSG_DISCONNECT || msg.type == MSG_SURRENDER)
{
game_over = 1;
sprintf(status_message, "对手已断开连接/认输");
IupMessage("游戏结束", "对手已退出游戏,你赢了!");
update_ui_labels();
}
}
if (!is_network_connected() && !game_over)
{
game_over = 1;
sprintf(status_message, "与服务器断开连接");
IupMessage("错误", "网络连接已断开");
update_ui_labels();
}
return IUP_DEFAULT;
}
/**
* @brief 处理AI落子结果(LLM或算法)
*/
static void process_ai_move_result(void)
{
Step last_step = steps[step_count - 1];
if (check_win(last_step.x, last_step.y, AI))
{
game_over = 1;
sprintf(status_message, "AI获胜!");
IupMessage("游戏结束", "AI获胜!");
}
else
{
current_player_gui = PLAYER;
sprintf(status_message, "轮到玩家");
}
update_ui_labels();
if (board_canvas)
IupUpdate(board_canvas);
}
/**
* @brief LLM异步轮询定时器回调
*/
static int llm_timer_cb(Ihandle *ih)
{
(void)ih;
int x, y;
int result = llm_ai_poll_result(&x, &y);
if (result == 0)
return IUP_DEFAULT; // 仍在思考
// 停止轮询定时器
if (llm_timer)
{
IupSetAttribute(llm_timer, "RUN", "NO");
}
if (result == 1 && x >= 0 && y >= 0 && player_move(x, y, AI))
{
// LLM成功且落子合法
}
else
{
// LLM失败或坐标非法,回退到算法AI
if (result == 1)
snprintf(status_message, sizeof(status_message), "大模型返回非法位置,使用算法AI");
else
snprintf(status_message, sizeof(status_message), "大模型响应失败,使用算法AI");
update_ui_labels();
ai_move(ai_difficulty);
}
process_ai_move_result();
return IUP_DEFAULT;
}
/**
* @brief MAP_CB 回调:Canvas映射后强制重绘
*/
static int map_cb(Ihandle *ih)
{
(void)ih;
IupUpdate(board_canvas);
return IUP_DEFAULT;
}
/**
* @brief ACTION 回调:负责重绘(经典木纹风格)
*/
int action_cb(Ihandle *ih)
{
HWND hwnd = (HWND)IupGetAttribute(ih, "WID");
if (!hwnd)
return IUP_DEFAULT;
HDC hdc = GetDC(hwnd);
if (!hdc)
return IUP_DEFAULT;
RECT rc;
GetClientRect(hwnd, &rc);
// === 预创建所有 GDI 对象 ===
HBRUSH bg_brush = CreateSolidBrush(RGB(CLR_BOARD_BG_R, CLR_BOARD_BG_G, CLR_BOARD_BG_B));
HBRUSH black_outer = CreateSolidBrush(RGB(CLR_BLACK_STONE_R, CLR_BLACK_STONE_G, CLR_BLACK_STONE_B));
HBRUSH black_core = CreateSolidBrush(RGB(0, 0, 0));
HBRUSH black_hl = CreateSolidBrush(RGB(CLR_BLACK_HIGHLIGHT_R, CLR_BLACK_HIGHLIGHT_G, CLR_BLACK_HIGHLIGHT_B));
HBRUSH white_outer = CreateSolidBrush(RGB(CLR_WHITE_BORDER_R, CLR_WHITE_BORDER_G, CLR_WHITE_BORDER_B));
HBRUSH white_core = CreateSolidBrush(RGB(CLR_WHITE_STONE_R, CLR_WHITE_STONE_G, CLR_WHITE_STONE_B));
HBRUSH white_hl = CreateSolidBrush(RGB(CLR_WHITE_HIGHLIGHT_R, CLR_WHITE_HIGHLIGHT_G, CLR_WHITE_HIGHLIGHT_B));
HBRUSH star_brush = CreateSolidBrush(RGB(CLR_STAR_POINT_R, CLR_STAR_POINT_G, CLR_STAR_POINT_B));
HPEN grid_pen = CreatePen(PS_SOLID, 1, RGB(CLR_GRID_LINE_R, CLR_GRID_LINE_G, CLR_GRID_LINE_B));
HPEN border_pen = CreatePen(PS_SOLID, 2, RGB(CLR_BOARD_BORDER_R, CLR_BOARD_BORDER_G, CLR_BOARD_BORDER_B));
HPEN last_move_pen = CreatePen(PS_SOLID, 2, RGB(CLR_LAST_MOVE_R, CLR_LAST_MOVE_G, CLR_LAST_MOVE_B));
HPEN null_pen = CreatePen(PS_NULL, 0, RGB(0, 0, 0));
HFONT hfont_coord = CreateFont(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, 0, 0, 0, 0, "SimHei");
// 1. 填充背景
FillRect(hdc, &rc, bg_brush);
// 2. 绘制棋盘边框(深色外框)
HPEN prev_pen = (HPEN)SelectObject(hdc, border_pen);
HBRUSH prev_brush = (HBRUSH)SelectObject(hdc, (HBRUSH)GetStockObject(NULL_BRUSH));
int grid_left = BOARD_OFFSET_X;
int grid_top = BOARD_OFFSET_Y;
int grid_right = BOARD_OFFSET_X + (BOARD_SIZE - 1) * CELL_SIZE;
int grid_bottom = BOARD_OFFSET_Y + (BOARD_SIZE - 1) * CELL_SIZE;
Rectangle(hdc, grid_left - 2, grid_top - 2, grid_right + 3, grid_bottom + 3);
// 3. 绘制棋盘网格
SelectObject(hdc, grid_pen);
SelectObject(hdc, (HBRUSH)GetStockObject(NULL_BRUSH));
for (int i = 0; i < BOARD_SIZE; i++)
{
MoveToEx(hdc, grid_left, BOARD_OFFSET_Y + i * CELL_SIZE, NULL);
LineTo(hdc, grid_right, BOARD_OFFSET_Y + i * CELL_SIZE);
MoveToEx(hdc, BOARD_OFFSET_X + i * CELL_SIZE, grid_top, NULL);
LineTo(hdc, BOARD_OFFSET_X + i * CELL_SIZE, grid_bottom);
}
// 4. 星位/天元(实心圆)
SelectObject(hdc, null_pen);
SelectObject(hdc, star_brush);
if (BOARD_SIZE == 15)
{
int stars[] = {3, 7, 11};
for (int si = 0; si < 3; si++)
for (int sj = 0; sj < 3; sj++)
{
int cx = BOARD_OFFSET_X + stars[si] * CELL_SIZE;
int cy = BOARD_OFFSET_Y + stars[sj] * CELL_SIZE;
Ellipse(hdc, cx - 4, cy - 4, cx + 5, cy + 5);
}
}
else if (BOARD_SIZE >= 9)
{
int cx = BOARD_OFFSET_X + (BOARD_SIZE / 2) * CELL_SIZE;
int cy = BOARD_OFFSET_Y + (BOARD_SIZE / 2) * CELL_SIZE;
Ellipse(hdc, cx - 4, cy - 4, cx + 5, cy + 5);
}
// 5. 绘制坐标标注
{
HFONT prev_font = (HFONT)SelectObject(hdc, hfont_coord);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(CLR_BOARD_BORDER_R, CLR_BOARD_BORDER_G, CLR_BOARD_BORDER_B));
// 列坐标 (A, B, C, ...)
for (int j = 0; j < BOARD_SIZE; j++)
{
char label[2] = {'A' + j, '\0'};
int tx = BOARD_OFFSET_X + j * CELL_SIZE - 4;
TextOut(hdc, tx, grid_top - 18, label, 1);
TextOut(hdc, tx, grid_bottom + 5, label, 1);
}
// 行坐标 (1, 2, 3, ...)
for (int i = 0; i < BOARD_SIZE; i++)
{
char label[4];
int len = snprintf(label, sizeof(label), "%d", i + 1);
int ty = BOARD_OFFSET_Y + i * CELL_SIZE - 7;
TextOut(hdc, grid_left - 18 - (len > 1 ? 4 : 0), ty, label, len);
TextOut(hdc, grid_right + 6, ty, label, len);
}
SelectObject(hdc, prev_font);
}
// 6. 绘制棋子(渐变效果:3层同心圆)
SelectObject(hdc, null_pen);
for (int i = 0; i < BOARD_SIZE; i++)
for (int j = 0; j < BOARD_SIZE; j++)
{
if (board[i][j] == EMPTY)
continue;
int cx = BOARD_OFFSET_X + j * CELL_SIZE;
int cy = BOARD_OFFSET_Y + i * CELL_SIZE;
if (board[i][j] == PLAYER)
{
// 黑子:外圈深灰 → 中圈黑 → 中心高光
SelectObject(hdc, black_outer);
Ellipse(hdc, cx - STONE_RADIUS, cy - STONE_RADIUS,
cx + STONE_RADIUS + 1, cy + STONE_RADIUS + 1);
SelectObject(hdc, black_core);
Ellipse(hdc, cx - STONE_RADIUS + 2, cy - STONE_RADIUS + 2,
cx + STONE_RADIUS - 1, cy + STONE_RADIUS - 1);
SelectObject(hdc, black_hl);
Ellipse(hdc, cx - 4, cy - 5, cx - 1, cy - 2);
}
else
{
// 白子:外圈灰边 → 中圈白 → 中心高光
SelectObject(hdc, white_outer);
Ellipse(hdc, cx - STONE_RADIUS, cy - STONE_RADIUS,
cx + STONE_RADIUS + 1, cy + STONE_RADIUS + 1);
SelectObject(hdc, white_core);
Ellipse(hdc, cx - STONE_RADIUS + 2, cy - STONE_RADIUS + 2,
cx + STONE_RADIUS - 1, cy + STONE_RADIUS - 1);
SelectObject(hdc, white_hl);
Ellipse(hdc, cx - 4, cy - 5, cx - 1, cy - 2);
}
}
// 7. 标记最后落子位置(蓝色圆环)
if (step_count > 0 && step_count <= MAX_STEPS)
{
Step last = steps[step_count - 1];
int cx = BOARD_OFFSET_X + last.y * CELL_SIZE;
int cy = BOARD_OFFSET_Y + last.x * CELL_SIZE;
SelectObject(hdc, last_move_pen);
SelectObject(hdc, (HBRUSH)GetStockObject(NULL_BRUSH));
Ellipse(hdc, cx - 5, cy - 5, cx + 6, cy + 6);
}
// 恢复原始 GDI 对象,然后清理
SelectObject(hdc, prev_pen);
SelectObject(hdc, prev_brush);
ReleaseDC(hwnd, hdc);
DeleteObject(bg_brush);
DeleteObject(black_outer);
DeleteObject(black_core);
DeleteObject(black_hl);
DeleteObject(white_outer);
DeleteObject(white_core);
DeleteObject(white_hl);
DeleteObject(star_brush);
DeleteObject(grid_pen);
DeleteObject(border_pen);
DeleteObject(last_move_pen);
DeleteObject(null_pen);
DeleteObject(hfont_coord);
return IUP_DEFAULT;
}
/**
* @brief 悔棋按钮回调
*/
int btn_undo_cb(Ihandle *ih)
{
(void)ih;
if (game_over)
return IUP_DEFAULT;
int steps_to_undo = 1;
if (gui_game_mode == 1) // PvE
{
steps_to_undo = 2; // 悔棋两步(玩家+AI
}
else if (gui_game_mode == 3) // Network
{
IupMessage("提示", "网络模式暂不支持悔棋");
return IUP_DEFAULT;
}
if (step_count >= steps_to_undo)
{
return_move(steps_to_undo);
// 更新当前玩家
if (step_count % 2 == 0)
current_player_gui = PLAYER;
else
current_player_gui = AI; // or PLAYER2
sprintf(status_message, "已悔棋");
update_ui_labels();
IupUpdate(board_canvas);
}
else
{
sprintf(status_message, "无法悔棋");
update_ui_labels();
}
return IUP_DEFAULT;
}
/**
* @brief 保存按钮回调
*/
int btn_save_cb(Ihandle *ih)
{
(void)ih;
Ihandle *file_dlg = IupFileDlg();
IupSetAttribute(file_dlg, "DIALOGTYPE", "SAVE");
IupSetAttribute(file_dlg, "TITLE", "保存游戏记录");
IupSetAttribute(file_dlg, "FILTER", "*.csv");
IupSetAttribute(file_dlg, "FILTERINFO", "CSV Files");
IupPopup(file_dlg, IUP_CENTER, IUP_CENTER);
if (IupGetInt(file_dlg, "STATUS") != -1)
{
char *filename = IupGetAttribute(file_dlg, "VALUE");
char *base_name = strrchr(filename, '\\');
if (!base_name)
base_name = strrchr(filename, '/');
if (base_name)
base_name++;
else
base_name = filename;
int mode;
if (gui_game_mode == 0)
mode = GAME_MODE_PVP;
else if (gui_game_mode == 3)
mode = GAME_MODE_NETWORK;
else
mode = GAME_MODE_AI;
if (save_game_to_file(base_name, mode) == 0)
{
snprintf(status_message, sizeof(status_message), "保存成功: %s", base_name);
}
else
{
snprintf(status_message, sizeof(status_message), "保存失败");
}
update_ui_labels();
}
IupDestroy(file_dlg);
return IUP_DEFAULT;
}
/**
* @brief 返回菜单回调
*/
int btn_back_cb(Ihandle *ih)
{
(void)ih;
// 停止所有定时器
if (timer)
{
IupSetAttribute(timer, "RUN", "NO");
IupDestroy(timer);
timer = NULL;
}
if (llm_timer)
{
IupSetAttribute(llm_timer, "RUN", "NO");
IupDestroy(llm_timer);
llm_timer = NULL;
}
// 如果是网络模式,彻底清理网络资源
if (gui_game_mode == 3)
{
cleanup_network();
}
// 1. 先显示主菜单
show_main_menu();
// 2. 销毁游戏窗口
if (dlg)
{
Ihandle *old_dlg = dlg;
dlg = NULL; // 先清除全局指针
IupDestroy(old_dlg);
}
return IUP_IGNORE; // 返回 IUP_IGNORE 以阻止默认处理
}
/**
* @brief 鼠标点击回调
*/
int button_cb(Ihandle *ih, int button, int pressed, int x, int y, char *status)
{
(void)status; // 未使用
if (gui_game_mode == 2)
return IUP_DEFAULT; // 复盘模式禁用点击
if (button == IUP_BUTTON1 && pressed)
{ // 左键按下
if (game_over)
return IUP_DEFAULT;
if (gui_game_mode == 3 && current_player_gui != network_state.local_player_id)
{
return IUP_DEFAULT; // 网络模式下,非自己回合不可落子
}
int board_x, board_y;
if (screen_to_board(x, y, &board_x, &board_y))
{
if (have_space(board_x, board_y))
{
// 执行落子操作
if (player_move(board_x, board_y, current_player_gui))
{
// 检查是否获胜
if (check_win(board_x, board_y, current_player_gui))
{
game_over = 1;
if (gui_game_mode == 3)
send_move(board_x, board_y, current_player_gui); // 发送最后一步
if (current_player_gui == PLAYER)
{
sprintf(status_message, "黑子获胜!");
IupMessage("游戏结束", "黑子获胜!");
}
else
{
sprintf(status_message, "白子获胜!");
IupMessage("游戏结束", "白子获胜!");
}
}
else
{
if (gui_game_mode == 0) // PvP
{
current_player_gui = (current_player_gui == PLAYER) ? AI : PLAYER; // AI 在这里表示玩家2
if (current_player_gui == PLAYER)
sprintf(status_message, "轮到黑子");
else
sprintf(status_message, "轮到白子");
}
else if (gui_game_mode == 3) // Network
{
send_move(board_x, board_y, current_player_gui);
current_player_gui = network_state.remote_player_id;
sprintf(status_message, "等待对手落子...");
}
else // PvE
{
current_player_gui = AI;
update_ui_labels();
IupUpdate(ih); // 立即更新显示
IupFlush(); // 强制刷新事件队列
if (llm_use)
{
// 大模型AI - 异步调用,不阻塞UI
sprintf(status_message, "AI思考中(大模型)...");
update_ui_labels();
// 创建或复用轮询定时器
if (!llm_timer)
{
llm_timer = IupTimer();
IupSetCallback(llm_timer, "ACTION_CB", (Icallback)llm_timer_cb);
IupSetAttribute(llm_timer, "TIME", "100"); // 100ms轮询
}
llm_ai_start_move();
IupSetAttribute(llm_timer, "RUN", "YES");
}
else
{
// 算法AI - 同步调用
sprintf(status_message, "AI思考中...");
update_ui_labels();
ai_move(ai_difficulty);
process_ai_move_result();
}
}
}
update_ui_labels();
IupUpdate(ih); // 请求重绘
}
}
else
{
sprintf(status_message, "无效位置!");
update_ui_labels();
}
}
}
return IUP_DEFAULT;
}
/**
* @brief 键盘回调
*/
int k_any_cb(Ihandle *ih, int c)
{
(void)ih;
if (c == K_ESC)
{
if (dlg && IupGetInt(dlg, "VISIBLE"))
{
btn_back_cb(ih); // 调用返回菜单逻辑
return IUP_DEFAULT;
}
}
return IUP_DEFAULT;
}
/**
* @brief 创建游戏窗口
*/
void create_game_window()
{
if (dlg)
{
IupDestroy(dlg);
dlg = NULL;
}
// 创建Canvas (棋盘)
board_canvas = IupCanvas(NULL);
if (!board_canvas)
printf("ERROR: Failed to create board_canvas\n");
IupSetCallback(board_canvas, "ACTION", (Icallback)action_cb);
IupSetCallback(board_canvas, "BUTTON_CB", (Icallback)button_cb);
IupSetCallback(board_canvas, "K_ANY", (Icallback)k_any_cb);
IupSetCallback(board_canvas, "MAP_CB", (Icallback)map_cb);
// 计算棋盘像素大小(含坐标标注区域)
int board_pixel_size = (BOARD_SIZE - 1) * CELL_SIZE + BOARD_OFFSET_X * 2 + 20;
char size[32];
sprintf(size, "%dx%d", board_pixel_size, board_pixel_size);
IupSetAttribute(board_canvas, "RASTERSIZE", size);
IupSetAttribute(board_canvas, "EXPAND", "NO");
IupSetAttribute(board_canvas, "BORDER", "NO");
IupSetAttribute(board_canvas, "BGCOLOR", "212 165 116");
// === 创建标签 ===
lbl_player = IupLabel("当前玩家: 黑子");
IupSetAttribute(lbl_player, "FONT", "SimHei, 13");
IupSetAttribute(lbl_player, "FGCOLOR", CLR_TEXT_TITLE);
lbl_status = IupLabel("准备开始");
IupSetAttribute(lbl_status, "FONT", "SimHei, 11");
IupSetAttribute(lbl_status, "FGCOLOR", CLR_TEXT_NORMAL);
// === 对局信息面板 ===
Ihandle *info_vbox = IupVbox(lbl_player, lbl_status, NULL);
IupSetAttribute(info_vbox, "GAP", "4");
IupSetAttribute(info_vbox, "MARGIN", "10x8");
Ihandle *frm_info = IupFrame(info_vbox);
IupSetAttribute(frm_info, "TITLE", "对局信息");
IupSetAttribute(frm_info, "FONT", "SimHei, 11");
IupSetAttribute(frm_info, "FGCOLOR", CLR_TEXT_NORMAL);
// === 按钮样式宏 ===
#define SET_BTN_STYLE(btn, w, h, font) \
IupSetAttribute(btn, "SIZE", #w "x" #h); \
IupSetAttribute(btn, "FONT", font); \
IupSetAttribute(btn, "BGCOLOR", CLR_BTN_NORMAL_BG); \
IupSetAttribute(btn, "FGCOLOR", CLR_BTN_NORMAL_FG); \
IupSetAttribute(btn, "FLAT", "YES")
Ihandle *vbox_controls;
if (gui_game_mode == 2) // 复盘模式
{
Ihandle *btn_prev = IupButton("上一步", NULL);
IupSetCallback(btn_prev, "ACTION", (Icallback)btn_replay_prev_cb);
SET_BTN_STYLE(btn_prev, 120, 35, "SimHei, 11");
Ihandle *btn_next = IupButton("下一步", NULL);
IupSetCallback(btn_next, "ACTION", (Icallback)btn_replay_next_cb);
SET_BTN_STYLE(btn_next, 120, 35, "SimHei, 11");
Ihandle *hbox_nav = IupHbox(btn_prev, btn_next, NULL);
IupSetAttribute(hbox_nav, "GAP", "8");
IupSetAttribute(hbox_nav, "ALIGNMENT", "ACENTER");
Ihandle *btn_back = IupButton("返回菜单", NULL);
IupSetCallback(btn_back, "ACTION", (Icallback)btn_back_cb);
SET_BTN_STYLE(btn_back, 120, 35, "SimHei, 11");
vbox_controls = IupVbox(
frm_info,
hbox_nav,
btn_back,
NULL);
}
else // 游戏模式 (PvP / PvE / Network)
{
Ihandle *btn_undo = IupButton("悔棋", NULL);
IupSetCallback(btn_undo, "ACTION", (Icallback)btn_undo_cb);
SET_BTN_STYLE(btn_undo, 120, 35, "SimHei, 11");
Ihandle *btn_save = IupButton("保存棋谱", NULL);
IupSetCallback(btn_save, "ACTION", (Icallback)btn_save_cb);
SET_BTN_STYLE(btn_save, 120, 35, "SimHei, 11");
Ihandle *btn_back = IupButton("返回菜单", NULL);
IupSetCallback(btn_back, "ACTION", (Icallback)btn_back_cb);
SET_BTN_STYLE(btn_back, 120, 35, "SimHei, 11");
vbox_controls = IupVbox(
frm_info,
btn_undo,
btn_save,
btn_back,
NULL);
}
#undef SET_BTN_STYLE
IupSetAttribute(vbox_controls, "GAP", "10");
IupSetAttribute(vbox_controls, "MARGIN", "10x10");
IupSetAttribute(vbox_controls, "ALIGNMENT", "ACENTER");
Ihandle *hbox_main = IupHbox(board_canvas, vbox_controls, NULL);
IupSetAttribute(hbox_main, "MARGIN", "10x10");
IupSetAttribute(hbox_main, "GAP", "8");
// 创建Dialog
dlg = IupDialog(hbox_main);
if (!dlg)
printf("ERROR: Failed to create dialog\n");
IupSetAttribute(dlg, "TITLE", "五子棋");
IupSetAttribute(dlg, "RESIZE", "NO");
IupSetAttribute(dlg, "BGCOLOR", CLR_WINDOW_BG);
// 设置 CLOSE_CB 回调,确保点击X也能正确返回菜单
IupSetCallback(dlg, "CLOSE_CB", (Icallback)btn_back_cb);
}
void start_pvp_game_gui()
{
gui_game_mode = 0;
empty_board();
current_player_gui = PLAYER;
game_over = 0;
create_game_window();
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
IupFlush(); // 确保窗口完全映射
sprintf(status_message, "玩家对战模式 - 黑方先行");
update_ui_labels();
if (board_canvas)
IupUpdate(board_canvas);
}
void start_pve_game_gui()
{
gui_game_mode = 1;
empty_board();
current_player_gui = PLAYER;
game_over = 0;
create_game_window();
if (dlg)
{
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
IupFlush();
}
else
{
return;
}
sprintf(status_message, "人机对战模式 - 玩家执黑先行");
update_ui_labels();
if (board_canvas)
IupUpdate(board_canvas);
}
void start_network_game_gui()
{
gui_game_mode = 3;
empty_board();
current_player_gui = PLAYER1;
game_over = 0;
create_game_window();
if (dlg)
{
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
IupFlush();
}
if (network_state.is_server)
sprintf(status_message, "局域网联机 - 你是主机(黑子),轮到你落子");
else
sprintf(status_message, "局域网联机 - 你是客机(白子),等待对手落子...");
update_ui_labels();
// 强制初始重绘
if (board_canvas)
{
IupUpdate(board_canvas);
}
// 启动网络轮询定时器
timer = IupTimer();
IupSetCallback(timer, "ACTION_CB", (Icallback)timer_cb);
IupSetAttribute(timer, "TIME", "50"); // 50ms 轮询一次
IupSetAttribute(timer, "RUN", "YES");
}