diff --git a/forms/interface.ui b/forms/interface.ui index a7367fcd035c29818be0818e7646acee4059c8d0..c059c4401f2bf0385f71421fb491fefbdf46b8ba 100644 --- a/forms/interface.ui +++ b/forms/interface.ui @@ -629,6 +629,7 @@ pattern recorded :</string> <string>Extra</string> </property> <addaction name="action_bad_apple"/> + <addaction name="action_snake"/> </widget> <addaction name="menuFichier"/> <addaction name="menuEditer"/> @@ -669,6 +670,11 @@ pattern recorded :</string> <string>Open configuration</string> </property> </action> + <action name="action_snake"> + <property name="text"> + <string>Play Snake</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/include/interface.hpp b/include/interface.hpp index ae51e1f6aaf357af8979dafcd4fb67078f695887..5a44b3190b1c08b3169e90c8bf1279c54bf899ba 100644 --- a/include/interface.hpp +++ b/include/interface.hpp @@ -147,8 +147,13 @@ private: //! \brief Fonction secrète ! void play_bad_apple(); + //! \brief Surprise aussi... + void play_snake(); + protected: void closeEvent(QCloseEvent* e) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; private: Ui::MainWindow *ui; @@ -157,6 +162,7 @@ private: NeighborhoodRule* m_neighborhood_rule; QJsonObject m_loaded_model; QTimer* timer; + bool m_arrow_key_state[4]; bool m_customizing; }; #endif // MAINWINDOW_HPP diff --git a/src/interface.cpp b/src/interface.cpp index 468b7540a4fbce6a78f0d21b262eb44413d30a04..0ebac53dd69d108952eb6042abfe59ed29921692 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -20,6 +20,7 @@ #include <QInputDialog> #include <QTimer> #include <QElapsedTimer> +#include <QKeyEvent> #if QT_VERSION_MAJOR == 5 #define BAD_APPLE_AUDIO @@ -32,12 +33,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) - , ui(new Ui::MainWindow) - , simulation() - , timer(new QTimer(this)) - , m_customizing(false) + , ui(new Ui::MainWindow) + , simulation() + , timer(new QTimer(this)) + , m_customizing(false) { ui->setupUi(this); + std::fill(std::begin(m_arrow_key_state), std::end(m_arrow_key_state), false); m_transition_rule = nullptr; m_neighborhood_rule = nullptr; @@ -50,31 +52,32 @@ MainWindow::MainWindow(QWidget *parent) connect(ui->struct_library, &StructureLibraryView::structure_copied, this, &MainWindow::copy_structure_clicked); connect(ui->grid_view, &GridView::zoom_changed, ui->struct_library, &StructureLibraryView::update_cell_pixel_size); connect(ui->statesSettingsButton, &QPushButton::clicked, this, [this] - { - ColorLabel* dialog = new ColorLabel(simulation.getAlphabet(), this); - if (dialog->exec()) - { - ui_update_alphabet(dialog->getAlphabet()); - } - }); + { + ColorLabel* dialog = new ColorLabel(simulation.getAlphabet(), this); + if (dialog->exec()) + { + ui_update_alphabet(dialog->getAlphabet()); + } + }); connect(ui->transition_list, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) - { update_transition_settings(); }); + { update_transition_settings(); }); connect(ui->action_open_configuration, &QAction::triggered, this, [this] { on_openPatternButton_clicked(); }); connect(ui->action_bad_apple, &QAction::triggered, this, &MainWindow::play_bad_apple); + connect(ui->action_snake, &QAction::triggered, this, &MainWindow::play_snake); connect(ui->action_propos_de_Cellulut, &QAction::triggered, this, [this](bool) - { - QMessageBox::about(this, "À propos de Cellulut", - "Projet de C++ réalisé dans le cadre de l'UV LO21 de l'UTC.\n\n" + { + QMessageBox::about(this, "À propos de Cellulut", + "Projet de C++ réalisé dans le cadre de l'UV LO21 de l'UTC.\n\n" "Contributeurs : Anthony Noir, Eugène Pin, Merwane Bouri, Arthur Detree, Yann Boucher"); - }); + }); connect(ui->action_propos_de_Qt, &QAction::triggered, this, [this](bool) - { - QMessageBox::aboutQt(this, "À propos de Qt"); - }); + { + QMessageBox::aboutQt(this, "À propos de Qt"); + }); connect(ui->actionCharger_depuis_une_image, &QAction::triggered, this, [this](bool) - { - load_from_image(); - }); + { + load_from_image(); + }); connect(ui->border_combo, QOverload<int>::of(&QComboBox::activated), this, &MainWindow::load_boundary_policy); ui->struct_library->update_cell_pixel_size(ui->grid_view->cell_screen_size()); @@ -647,13 +650,6 @@ QJsonObject MainWindow::default_configuration() const return doc.object(); } -// Décommenter pour activer le son sur win -//#define BAD_APPLE_WIN - -#if defined(BAD_APPLE_WIN) -#include <windows.h> -#endif - void MainWindow::play_bad_apple() { static QJsonArray bad_apple_frame_list; @@ -683,37 +679,37 @@ void MainWindow::play_bad_apple() if (!bad_apple_connected) { bad_apple_timer.callOnTimeout([this] - { + { #ifdef BAD_APPLE_AUDIO - unsigned frame_idx = bad_apple_player.position()/(1000.f/30); + unsigned frame_idx = bad_apple_player.position()/(1000.f/30); #else - unsigned frame_idx = bad_apple_elapsed.elapsed()/(1000.f/30); + unsigned frame_idx = bad_apple_elapsed.elapsed()/(1000.f/30); #endif - if ((int)frame_idx >= bad_apple_frame_list.size()) - { - bad_apple_timer.stop(); - return; - } + if ((int)frame_idx >= bad_apple_frame_list.size()) + { + bad_apple_timer.stop(); + return; + } - Grid grid(120, 160); + Grid grid(120, 160); - QJsonObject frame = bad_apple_frame_list[frame_idx].toObject(); + QJsonObject frame = bad_apple_frame_list[frame_idx].toObject(); - Coord origin; - origin.x = frame.value("left").toInt(); - origin.y = frame.value("top").toInt(); + Coord origin; + origin.x = frame.value("left").toInt(); + origin.y = frame.value("top").toInt(); - RLEStructureReader reader(frame.value("data").toString().toStdString()); - Structure s = reader.read_structure(); - s.top_left = origin; + RLEStructureReader reader(frame.value("data").toString().toStdString()); + Structure s = reader.read_structure(); + s.top_left = origin; - for (auto cell : s) - { - grid.set_cell(cell.first + s.top_left, cell.second); - } + for (auto cell : s) + { + grid.set_cell(cell.first + s.top_left, cell.second); + } - ui->grid_view->copy_grid(grid); - }); + ui->grid_view->copy_grid(grid); + }); }; // On coupe la simulation si elle est en train de s'exécuter @@ -734,6 +730,124 @@ void MainWindow::play_bad_apple() statusBar()->showMessage("Original : \"Bad Apple!!\" feat. Nomico by Alstroemeria Records", 60000); } +void MainWindow::play_snake() +{ + static QTimer snake_timer; + static Coord snake_dir; + static std::list<Coord> snake_body; + static bool snake_connected = false; + + statusBar()->showMessage("Controls : ZQSD", 60000); + + Alphabet alph; + alph.newEtat(state{stateColor{0, 0, 0}, "Snake"}); + alph.newEtat(state{stateColor{255, 0, 0}, "Apple"}); + + ui->grid_view->set_alphabet(alph); + + snake_body.clear(); + snake_dir = {+1, 0}; + + on_pushButton_clicked(); // reset la grille + + static auto restore = [&] + { + ui_update_alphabet(simulation.getAlphabet()); + snake_timer.stop(); + on_pushButton_clicked(); // reset la grille + }; + + static auto spawn_apple = [&] + { + unsigned attempt_limit = 10000; + while (attempt_limit>0) + { + --attempt_limit; + int rand_x = rand() % ui->grid_view->get_grid().get_col(); + int rand_y = rand() % ui->grid_view->get_grid().get_rows(); + Coord c = {rand_x, rand_y}; + Grid g = ui->grid_view->get_grid(); + if (g.get_state(c) != 0) + continue; + + g.set_cell(c, 2); + ui->grid_view->copy_grid(g); + break; + } + if (attempt_limit <= 0) // plus possible de placer de pomme + { + restore(); + return; + } + }; + + static auto advance_snake = [&](bool expand) + { + Coord next = snake_body.back() + snake_dir; + snake_body.push_back(next); + Grid g = ui->grid_view->get_grid(); + if (g.get_state(snake_body.back()) == 1 || + next.x < 0 || next.y < 0 || + next.x >= (int)g.get_col() || next.y >= (int)g.get_rows()) + { + restore(); + return; + } + // manger la pomme + else if (g.get_state(snake_body.back()) == 2) + { + expand = true; + spawn_apple(); + } + g = ui->grid_view->get_grid(); + g.set_cell(snake_body.back(), 1); + ui->grid_view->copy_grid(g); + + auto tail = snake_body.front(); + if (!expand) + { + snake_body.pop_front(); + Grid g = ui->grid_view->get_grid(); + g.set_cell(tail, 0); + ui->grid_view->copy_grid(g); + } + }; + + Coord start = {3, 1}; + snake_body.push_back(start); + Grid g = ui->grid_view->get_grid(); + g.set_cell(start, 1); + ui->grid_view->copy_grid(g); + + advance_snake(true); + advance_snake(true); + spawn_apple(); + + // On coupe la simulation si elle est en train de s'exécuter + if (timer->isActive()) + { + on_playPauseButton_clicked(); + } + + if (!snake_connected) + { + snake_timer.callOnTimeout([this] + { + if (m_arrow_key_state[0] && snake_dir != Coord{+1, 0}) + snake_dir = {-1, 0}; + else if (m_arrow_key_state[1] && snake_dir != Coord{0, -1}) + snake_dir = {0, +1}; + else if (m_arrow_key_state[2] && snake_dir != Coord{-1, 0}) + snake_dir = {+1, 0}; + else if (m_arrow_key_state[3] && snake_dir != Coord{0, +1}) + snake_dir = {0, -1}; + advance_snake(false); + }); + snake_connected = true; + } + snake_timer.start(100); +} + void MainWindow::closeEvent(QCloseEvent *e) { // Sauvegarder l'état actuel de l'application @@ -773,6 +887,32 @@ void MainWindow::closeEvent(QCloseEvent *e) QMainWindow::closeEvent(e); } +void MainWindow::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Q) + m_arrow_key_state[0] = true; + if (event->key() == Qt::Key_S) + m_arrow_key_state[1] = true; + if (event->key() == Qt::Key_D) + m_arrow_key_state[2] = true; + if (event->key() == Qt::Key_Z) + m_arrow_key_state[3] = true; + QMainWindow::keyPressEvent(event); +} + +void MainWindow::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Q) + m_arrow_key_state[0] = false; + if (event->key() == Qt::Key_S) + m_arrow_key_state[1] = false; + if (event->key() == Qt::Key_D) + m_arrow_key_state[2] = false; + if (event->key() == Qt::Key_Z) + m_arrow_key_state[3] = false; + QMainWindow::keyReleaseEvent(event); +} + void MainWindow::on_saveRuleButton_clicked() { save_model();