mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-05-10 02:19:46 +08:00
334 lines
10 KiB
C
334 lines
10 KiB
C
/**
|
|
* @file gui.c
|
|
* @brief 图形化用户界面实现文件
|
|
* @note 使用SDL3库实现五子棋的图形化界面
|
|
* @author 刘航宇
|
|
* @date 2025-01-15
|
|
*/
|
|
|
|
#include "gui.h"
|
|
#include "ui.h"
|
|
#include "globals.h"
|
|
#include "game_mode.h"
|
|
#include "init_board.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
// 全局变量
|
|
SDL_Window* window = NULL;
|
|
SDL_Renderer* renderer = NULL;
|
|
int gui_running = 1;
|
|
int current_player_gui = PLAYER;
|
|
int game_over = 0;
|
|
char status_message[256] = "五子棋游戏 - 黑子先行";
|
|
|
|
/**
|
|
* @brief 初始化GUI
|
|
* @return 成功返回0,失败返回-1
|
|
*/
|
|
int init_gui() {
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
printf("SDL初始化失败: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
window = SDL_CreateWindow(
|
|
"五子棋游戏 - SDL3版本",
|
|
WINDOW_WIDTH, WINDOW_HEIGHT,
|
|
SDL_WINDOW_RESIZABLE
|
|
);
|
|
|
|
// 设置窗口位置到屏幕中央
|
|
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
|
|
|
if (!window) {
|
|
printf("窗口创建失败: %s\n", SDL_GetError());
|
|
SDL_Quit();
|
|
return -1;
|
|
}
|
|
|
|
// 显示窗口
|
|
SDL_ShowWindow(window);
|
|
|
|
renderer = SDL_CreateRenderer(window, NULL);
|
|
if (!renderer) {
|
|
printf("渲染器创建失败: %s\n", SDL_GetError());
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
return -1;
|
|
}
|
|
|
|
// 初始化游戏状态
|
|
// 初始化棋盘
|
|
for (int i = 0; i < BOARD_SIZE; i++) {
|
|
for (int j = 0; j < BOARD_SIZE; j++) {
|
|
board[i][j] = EMPTY;
|
|
}
|
|
}
|
|
current_player_gui = PLAYER;
|
|
game_over = 0;
|
|
|
|
printf("图形化界面初始化成功!\n");
|
|
printf("使用鼠标点击棋盘进行落子\n");
|
|
printf("按ESC键退出游戏\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 清理GUI资源
|
|
*/
|
|
void cleanup_gui() {
|
|
if (renderer) {
|
|
SDL_DestroyRenderer(renderer);
|
|
renderer = NULL;
|
|
}
|
|
if (window) {
|
|
SDL_DestroyWindow(window);
|
|
window = NULL;
|
|
}
|
|
SDL_Quit();
|
|
printf("图形化界面已关闭\n");
|
|
}
|
|
|
|
/**
|
|
* @brief 渲染游戏画面
|
|
*/
|
|
void render_game() {
|
|
// 清空屏幕 - 设置背景色
|
|
SDL_Color bg_color = GUI_COLOR_BACKGROUND;
|
|
SDL_SetRenderDrawColor(renderer, bg_color.r, bg_color.g, bg_color.b, bg_color.a);
|
|
SDL_RenderClear(renderer);
|
|
|
|
// 绘制棋盘
|
|
draw_board();
|
|
|
|
// 绘制棋子
|
|
draw_stones();
|
|
|
|
// 绘制UI元素
|
|
draw_ui_elements();
|
|
|
|
// 显示渲染结果
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
|
|
/**
|
|
* @brief 处理事件
|
|
* @return 继续运行返回1,退出返回0
|
|
*/
|
|
int handle_events() {
|
|
SDL_Event event;
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
switch (event.type) {
|
|
case SDL_EVENT_QUIT:
|
|
gui_running = 0;
|
|
return 0;
|
|
|
|
case SDL_EVENT_KEY_DOWN:
|
|
if (event.key.key == SDLK_ESCAPE) {
|
|
gui_running = 0;
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
|
if (event.button.button == SDL_BUTTON_LEFT && !game_over) {
|
|
int board_x, board_y;
|
|
if (screen_to_board(event.button.x, event.button.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 (current_player_gui == PLAYER) {
|
|
sprintf(status_message, "游戏结束 - 黑子获胜!");
|
|
} else {
|
|
sprintf(status_message, "游戏结束 - 白子获胜!");
|
|
}
|
|
} else {
|
|
// 切换玩家
|
|
current_player_gui = (current_player_gui == PLAYER) ? AI : PLAYER;
|
|
if (current_player_gui == PLAYER) {
|
|
sprintf(status_message, "轮到黑子下棋");
|
|
} else {
|
|
sprintf(status_message, "轮到白子下棋");
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
sprintf(status_message, "该位置已有棋子,请选择其他位置");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief 绘制棋盘
|
|
*/
|
|
void draw_board() {
|
|
SDL_Color line_color = GUI_COLOR_BOARD_LINE;
|
|
SDL_SetRenderDrawColor(renderer, line_color.r, line_color.g, line_color.b, line_color.a);
|
|
|
|
// 绘制横线
|
|
for (int i = 0; i < BOARD_SIZE; i++) {
|
|
int y = BOARD_OFFSET_Y + i * CELL_SIZE;
|
|
SDL_RenderLine(renderer,
|
|
BOARD_OFFSET_X, y,
|
|
BOARD_OFFSET_X + (BOARD_SIZE - 1) * CELL_SIZE, y);
|
|
}
|
|
|
|
// 绘制竖线
|
|
for (int j = 0; j < BOARD_SIZE; j++) {
|
|
int x = BOARD_OFFSET_X + j * CELL_SIZE;
|
|
SDL_RenderLine(renderer,
|
|
x, BOARD_OFFSET_Y,
|
|
x, BOARD_OFFSET_Y + (BOARD_SIZE - 1) * CELL_SIZE);
|
|
}
|
|
|
|
// 绘制天元点和星位
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
int center = BOARD_SIZE / 2;
|
|
|
|
// 天元点
|
|
int center_x = BOARD_OFFSET_X + center * CELL_SIZE;
|
|
int center_y = BOARD_OFFSET_Y + center * CELL_SIZE;
|
|
SDL_FRect center_rect = {center_x - 2, center_y - 2, 4, 4};
|
|
SDL_RenderFillRect(renderer, ¢er_rect);
|
|
|
|
// 四个星位
|
|
int star_offset = 3;
|
|
int positions[][2] = {
|
|
{center - star_offset, center - star_offset},
|
|
{center + star_offset, center - star_offset},
|
|
{center - star_offset, center + star_offset},
|
|
{center + star_offset, center + star_offset}
|
|
};
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
int x = BOARD_OFFSET_X + positions[i][1] * CELL_SIZE;
|
|
int y = BOARD_OFFSET_Y + positions[i][0] * CELL_SIZE;
|
|
SDL_FRect star_rect = {x - 1, y - 1, 2, 2};
|
|
SDL_RenderFillRect(renderer, &star_rect);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 绘制棋子
|
|
*/
|
|
void draw_stones() {
|
|
for (int i = 0; i < BOARD_SIZE; i++) {
|
|
for (int j = 0; j < BOARD_SIZE; j++) {
|
|
if (board[i][j] != EMPTY) {
|
|
int x = BOARD_OFFSET_X + j * CELL_SIZE;
|
|
int y = BOARD_OFFSET_Y + i * CELL_SIZE;
|
|
|
|
// 设置棋子颜色
|
|
SDL_Color stone_color, border_color;
|
|
if (board[i][j] == PLAYER || board[i][j] == PLAYER1) {
|
|
stone_color = (SDL_Color)GUI_COLOR_BLACK_STONE;
|
|
} else {
|
|
stone_color = (SDL_Color)GUI_COLOR_WHITE_STONE;
|
|
}
|
|
border_color = (SDL_Color)GUI_COLOR_STONE_BORDER;
|
|
|
|
// 绘制圆形棋子
|
|
draw_circle(x, y, STONE_RADIUS, stone_color);
|
|
draw_circle(x, y, STONE_RADIUS, border_color);
|
|
|
|
// 重新绘制内部
|
|
draw_circle(x, y, STONE_RADIUS - 1, stone_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 绘制圆形
|
|
* @param center_x 圆心X坐标
|
|
* @param center_y 圆心Y坐标
|
|
* @param radius 半径
|
|
* @param color 颜色
|
|
*/
|
|
void draw_circle(int center_x, int center_y, int radius, SDL_Color color) {
|
|
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
|
|
|
for (int w = 0; w < radius * 2; w++) {
|
|
for (int h = 0; h < radius * 2; h++) {
|
|
int dx = radius - w;
|
|
int dy = radius - h;
|
|
if ((dx*dx + dy*dy) <= (radius * radius)) {
|
|
SDL_RenderPoint(renderer, center_x + dx, center_y + dy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 绘制UI元素
|
|
*/
|
|
void draw_ui_elements() {
|
|
// 绘制状态信息区域背景
|
|
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
|
|
SDL_FRect info_rect = {BOARD_OFFSET_X + BOARD_SIZE * CELL_SIZE + 20, BOARD_OFFSET_Y, 200, 100};
|
|
SDL_RenderFillRect(renderer, &info_rect);
|
|
|
|
// 绘制边框
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
SDL_RenderRect(renderer, &info_rect);
|
|
|
|
// 这里可以添加文字渲染,但SDL3需要额外的字体库
|
|
// 暂时用简单的图形表示当前玩家
|
|
int indicator_x = info_rect.x + 20;
|
|
int indicator_y = info_rect.y + 20;
|
|
|
|
if (!game_over) {
|
|
if (current_player_gui == PLAYER) {
|
|
// 黑子回合
|
|
draw_circle(indicator_x, indicator_y, 10, (SDL_Color){0, 0, 0, 255});
|
|
} else {
|
|
// 白子回合
|
|
draw_circle(indicator_x, indicator_y, 10, (SDL_Color){255, 255, 255, 255});
|
|
// 绘制当前玩家指示器(简单的矩形代替圆形)
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
SDL_FRect indicator_rect = {indicator_x - 10, indicator_y - 10, 20, 20};
|
|
SDL_RenderFillRect(renderer, &indicator_rect);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 屏幕坐标转棋盘坐标
|
|
* @param screen_x 屏幕X坐标
|
|
* @param screen_y 屏幕Y坐标
|
|
* @param board_x 输出棋盘X坐标
|
|
* @param board_y 输出棋盘Y坐标
|
|
* @return 转换成功返回1,失败返回0
|
|
*/
|
|
int screen_to_board(int screen_x, int screen_y, int* board_x, int* board_y) {
|
|
int rel_x = screen_x - BOARD_OFFSET_X;
|
|
int rel_y = screen_y - BOARD_OFFSET_Y;
|
|
|
|
*board_x = (rel_x + CELL_SIZE/2) / CELL_SIZE;
|
|
*board_y = (rel_y + CELL_SIZE/2) / CELL_SIZE;
|
|
|
|
return (*board_x >= 0 && *board_x < BOARD_SIZE &&
|
|
*board_y >= 0 && *board_y < BOARD_SIZE);
|
|
}
|
|
|
|
/**
|
|
* @brief 显示消息
|
|
* @param message 要显示的消息
|
|
*/
|
|
void show_message(const char* message) {
|
|
strncpy(status_message, message, sizeof(status_message) - 1);
|
|
status_message[sizeof(status_message) - 1] = '\0';
|
|
printf("%s\n", message);
|
|
} |