This commit is contained in:
daleclack 2024-02-26 22:16:09 +08:00
parent 39dc77884e
commit d7aea43567
7 changed files with 50 additions and 384 deletions

View File

@ -26,7 +26,7 @@ link_directories (${GTK4_LIBRARY_DIRS})
# set(PO_DIR ${CMAKE_BINARY_DIR}/po/zh_CN/LC_MESSAGES)
#Source files
set(SOURCE_FILE src/main.cpp src/MineSweeper.cpp src/MineCell.cpp)
set(SOURCE_FILE src/main.cpp src/MineSweeper.cpp src/MineCell.cpp src/InputBox.cpp)
#Compile Resource

View File

View File

@ -1,302 +0,0 @@
#include "MineSweeper.hh"
#include <string>
#include <iostream>
MineSweeper::MineSweeper()
: main_box(Gtk::Orientation::VERTICAL, 5),
btn_box(Gtk::Orientation::HORIZONTAL, 5),
cell(nullptr)
{
// Initalize Window
set_title("MineSweeper");
set_titlebar(header);
header.set_show_title_buttons();
header.set_decoration_layout("close,minimize,maximize:menu");
header.pack_end(menu_btn);
set_icon_name("org.gtk.daleclack");
// Initalize Menu
menu_builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/mine_menu.xml");
auto object = menu_builder->get_object<Gio::MenuModel>("mine_menu");
popover1.set_menu_model(object);
// popover1.show();
menu_btn.set_popover(popover1);
// Add Actions
add_action("new_game", sigc::mem_fun(*this, &MineSweeper::new_game));
add_action("scores", sigc::mem_fun(*this, &MineSweeper::show_scores));
add_action("show_mines", sigc::mem_fun(*this, &MineSweeper::show_mines));
add_action("quit", sigc::mem_fun(*this, &MineSweeper::hide));
// Default setting
reset_game();
// Buttons
btnstart.set_label("Start/Reset");
btnpause.set_label("Pause");
btnshow.set_label("Show All");
btnexit.set_label("Exit");
btn_box.append(btnstart);
btn_box.append(btnpause);
btn_box.append(btnshow);
btn_box.append(btnexit);
btnstart.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::new_game));
btnshow.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::show_mines));
btnexit.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::hide));
// Pack widgets
status_label.set_halign(Gtk::Align::CENTER);
btn_box.set_halign(Gtk::Align::CENTER);
mine_grid.set_halign(Gtk::Align::CENTER);
main_box.append(status_label);
main_box.append(mine_grid);
main_box.append(btn_box);
// Create a dialog
input_dialog = InputBox::create();
// Create Scores Window
scores_win = ScoresWin::create();
// Bind windows
input_dialog->set_transient_for(*this);
scores_win->set_transient_for(*this);
input_dialog->set_scores_window(scores_win);
// Show everything
set_child(main_box);
//show_all_children();
}
MineSweeper::~MineSweeper(){
// Delete all resources
delete input_dialog;
delete scores_win;
if(cell != nullptr){
delete[] cell;
}
}
void MineSweeper::new_game(){
// New game = reset game
reset_game();
}
void MineSweeper::reset_game(int width, int height, int mines)
{
// Clear the cells
if(cell != nullptr){
delete[] cell;
}
cell = new MineCell[width * height];
// Reset timer
mytimer.disconnect();
timer_count = 0;
mytimer = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MineSweeper::timer_func), 1000);
mine_count = 0;
mines_clear = 0;
mine_grid.set_sensitive();
// Reset all data
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
// Initalize cell
cell[i * 7 + j].set_has_frame();
cell[i * 7 + j].set_image_from_icon_name("", Gtk::IconSize::LARGE);
// cell[i * 7 + j].set_always_show_image();
cell[i * 7 + j].set_size_request(40, 40);
cell[i * 7 + j].mines_around = 0;
cell[i * 7 + j].has_mine = false;
cell[i * 7 + j].cleared = false;
}
}
// Reset mines
while (mine_count < mines)
{
int index = g_random_int_range(0, width * height);
if (!(cell[index].has_mine))
{
cell[index].has_mine = true;
// cell[index].set_label("x");
mine_count++;
}
}
// std::cout << mine_count << std::endl;
// game_ended = false;
// winned = true;
game_status = GameStatus::Running;
status_label.set_label(" ");
calc_mines();
// Append buttons to grid
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// cell[i * 7 + j].set_label("?");
cell[i * width + j].signal_clicked().connect(sigc::bind(
sigc::mem_fun(*this, &MineSweeper::cell_clicked), &cell[i * width + j]));
mine_grid.attach(cell[i * width + j], j, i);
cell[i * width + j].set_has_frame();
cell[i * width + j].x = j;
cell[i * width + j].y = i;
cell[i * width + j].cleared = false;
}
}
mine_grid.show();
}
void MineSweeper::pause_game(){
}
void MineSweeper::calc_mines()
{
// Calculate the mines around a cell
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 7; j++)
{
int index1, index2;
// The Search cell should not over the grids
for (index1 = MAX(0, i - 1); index1 < MIN(i + 1, 6) + 1; index1++)
{
for (index2 = MAX(0, j - 1); index2 < MIN(j + 1, 6) + 1; index2++)
{
if ((cell[index1 * 7 + index2].has_mine))
{
cell[i * 7 + j].mines_around++;
}
}
}
}
}
}
void MineSweeper::show_mines()
{
// Show all cell with a mine
for (int i = 0; i < 49; i++)
{
if (cell[i].has_mine)
{
cell[i].set_image_from_icon_name("mine", Gtk::IconSize::LARGE);
}
}
}
void MineSweeper::show_scores(){
// Show Scores Window
input_dialog->read_scores();
}
void MineSweeper::game_lost(int explode_index){
// When a cell with mine is clicked, show other mines
for (int i = 0; i < 49; i++)
{
if (cell[i].has_mine && i != explode_index)
{
cell[i].set_image_from_icon_name("mine", Gtk::IconSize::LARGE);
}
}
}
bool MineSweeper::timer_func()
{
// Set timer
char tmp[50];
timer_count++;
sprintf(tmp, "Time:%d", timer_count);
status_label.set_label(tmp);
return true;
}
void MineSweeper::cell_clicked(MineCell *cell1)
{
cell1->set_has_frame(false);
if (game_status == GameStatus::Running && !cell1->cleared)
{
//
// If get mine, the game will end now
if (cell1->has_mine)
{
// Set game to stop
// winned = false;
game_status = GameStatus::Ended;
cell1->cleared = true;
cell1->set_image_from_icon_name("exploded", Gtk::IconSize::LARGE);
// End the game
game_lost(cell1->y * 7 + cell1->x);
status_label.set_label("You lost!");
// game_ended = true;
mytimer.disconnect();
mine_grid.set_sensitive(false);
}
else
{
// If no mines, check the cell around
check_mines(cell1->x, cell1->y);
}
}
}
void MineSweeper::check_mines(int pos_x, int pos_y)
{
if (pos_x >= 0 && pos_x <= 6 &&
pos_y >= 0 && pos_y <= 6)
{
if (!cell[pos_y * 7 + pos_x].has_mine &&
!cell[pos_y * 7 + pos_x].cleared)
{
mines_clear++;
// Show the cell has no mines around
if (cell[pos_y * 7 + pos_x].mines_around == 0)
{
cell[pos_y * 7 + pos_x].set_image_from_icon_name("", Gtk::IconSize::LARGE);
}
else
{
// Show the numbers of mines around a cell
char *label = g_strdup_printf("%dmines", cell[pos_y * 7 + pos_x].mines_around);
cell[pos_y * 7 + pos_x].set_image_from_icon_name(label, Gtk::IconSize::LARGE);
g_free(label);
}
// make the cell without mines cleared
cell[pos_y * 7 + pos_x].set_has_frame(false);
cell[pos_y * 7 + pos_x].cleared = true;
// Check the cells around a cell that has no mines
if (cell[pos_y * 7 + pos_x].mines_around == 0)
{
check_mines((pos_x - 1), (pos_y - 1));
check_mines((pos_x + 1), (pos_y + 1));
check_mines((pos_x - 1), (pos_y + 1));
check_mines((pos_x + 1), (pos_y - 1));
check_mines(pos_x, (pos_y - 1));
check_mines(pos_x, (pos_y + 1));
check_mines((pos_x + 1), pos_y);
check_mines((pos_x - 1), pos_y);
}
}
}
// If all the mines has cleared, you has winned
if (mines_clear == 40)
{
// Stop the game
status_label.set_label("You winned!");
game_status = GameStatus::Winned;
mytimer.disconnect();
// Save the time of game
input_dialog->set_game_time(timer_count);
input_dialog->show();
}
}

View File

@ -16,6 +16,10 @@ struct _MineSweeper
{
GtkApplicationWindow parent_instance;
// Header widgets
GtkWidget *header, *menu_btn;
GtkBuilder *menu_builder;
// Child widgets
GtkWidget *main_box, *btn_box;
GtkWidget *mine_grid;
@ -284,10 +288,55 @@ static void btnshow_clicked(GtkButton *btn, MineSweeper *self)
}
}
// Signal Handler for menus
static void newgame_activated(GSimpleAction *action, GVariant *parmeter, gpointer data)
{
btnstart_clicked(NULL, MINE_SWEEPER(data));
}
static void scores_activated(GSimpleAction *action, GVariant *parmeter, gpointer data)
{
}
static void showmines_activated(GSimpleAction *action, GVariant *parmeter, gpointer data)
{
btnshow_clicked(NULL, MINE_SWEEPER(data));
}
static void quit_activated(GSimpleAction *action, GVariant *parmeter, gpointer data)
{
gtk_window_destroy(GTK_WINDOW(data));
}
static void mine_sweeper_init(MineSweeper *self)
{
// Initalize window
gtk_window_set_title(GTK_WINDOW(self), "MineSweeper");
self->header = gtk_header_bar_new();
gtk_window_set_titlebar(GTK_WINDOW(self), self->header);
// Add action for menu
GActionEntry entries[] =
{
{"new_game", newgame_activated, NULL, NULL, NULL},
{"scores", scores_activated, NULL, NULL, NULL},
{"show_mines", showmines_activated, NULL, NULL, NULL},
{"quit", quit_activated, NULL, NULL, NULL}};
g_action_map_add_action_entries(G_ACTION_MAP(self), entries,
G_N_ELEMENTS(entries), self);
// Create Menu and button
self->menu_btn = gtk_menu_button_new();
gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(self->menu_btn), "open-menu");
gtk_header_bar_pack_end(GTK_HEADER_BAR(self->header), self->menu_btn);
// Create Menu
self->menu_builder = gtk_builder_new_from_resource("/org/gtk/daleclack/mine_menu.xml");
GMenuModel *model = G_MENU_MODEL(gtk_builder_get_object(self->menu_builder, "mine_menu"));
gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(self->menu_btn), model);
GtkPopover *popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(self->menu_btn));
gtk_popover_set_has_arrow(popover, FALSE);
gtk_widget_set_halign(GTK_WIDGET(popover), GTK_ALIGN_END);
// Create widgets
self->main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

View File

@ -1,81 +0,0 @@
#pragma once
#include <gtkmm.h>
#include "InputBox.hh"
#include "ScoresWin.hh"
// The status of the minesweeper game
enum class GameStatus
{
Running,
Winned,
Ended,
Paused
};
// The cell of the minesweeper game,
// and the mines is under the cell
class MineCell : public Gtk::Button
{
public:
bool has_mine = false; // Whether the grid has mine
bool cleared = false; // Whether the mine is cleared
int mines_around; // The number near the grid
int x, y; // The Position of the grid
MineCell()
{
// Set button style
mines_around = 0;
}
};
// Main class
class MineSweeper : public Gtk::ApplicationWindow
{
public:
MineSweeper();
~MineSweeper();
private:
// HeaderBar
Gtk::HeaderBar header;
Gtk::MenuButton menu_btn;
Gtk::PopoverMenu popover1;
// Child widgets
Gtk::Grid mine_grid;
Gtk::Label status_label;
Gtk::Box main_box, btn_box;
Gtk::Button btnstart, btnpause, btnshow, btnexit;
// The cell to place mines
MineCell *cell;
GameStatus game_status; // Use enum class for the status of game
// bool winned, game_ended; // The status of game(win/end)
int mines_clear, mine_count; // Whether the mine is cleared
// Menu
Glib::RefPtr<Gtk::Builder> menu_builder;
// Timer
int timer_count;
sigc::connection mytimer;
// Input dialog
InputBox *input_dialog;
// Scores Window
ScoresWin *scores_win;
// Signal Handlers
void new_game(); // "New Game" handler
void reset_game(int width = 7, int height = 7, int mines = 9); // Reset all mines
void pause_game(); // Pause or continue the game
void calc_mines(); // Get the mines around
void show_mines(); // Show all mines
void show_scores(); // Show all scores
void game_lost(int explode_index); // You lost the game
void cell_clicked(MineCell *cell1); // Open a cell
bool timer_func(); // Timer
void check_mines(int pos_x, int pos_y); // Check if there is a mine
};