/// @brief what can I say? This is the main function! int main(int argc, char **argv) { // fancy error messages creation with snprintf char errorStringBuffer[ERROR_STRING_BUFFER_SIZE]; // Init type system as soon as possible g_type_init (); // SINGLETON creation is not thread safe. Ensure they are instantiated // before the app creates extra threads // BlockemConfig singleton contains general purpose configuration data that // will be loaded from the config xml file // It MUST be instantiated before i18n in windows because it loads the // preferred language BlockemConfig::Instance(); // enable internationalisation support. It MUST be called before // ProcessCommandLine I18nInit(); // parse command line options. options will be saved in a bunch of static // global variables (scope to this file only) defined at the beginning of // this file (they all start with 'g_') ProcessCommandLine(argc, argv); if (g_version) { // "version" option takes priority. If it set nothing else will be done std::cout << "Blockem, version " << VERSION << " (git commit " << GIT_COMMIT << ")" << std::endl; std::cout << " Compiled " << COMPILETIME << std::endl << std::endl; std::cout << "Copyright (C) 2009-2013 Faustino Frechilla" << std::endl; std::cout << "Blockem is open source software, see http://blockem.sourceforge.net/" << std::endl << std::endl; } else if (g_mode == 0) { ////////////////// // GUI MODE // gtkmm can do strange stuff if its internals are not initialised early // enough Gtk::Main::init_gtkmm_internals(); // initialise singletons before extra threads are created. // Singleton creation is not thread safe // Game1v1Config data. Used to store GUI related configuration for 1vs1 games Game1v1Config::Instance(); Game4PlayersConfig::Instance(); GameTotalAllocationConfig::Instance(); GameChallengeConfig::Instance(); // g_thread_supported returns TRUE if the thread system is initialised, // and FALSE if it is not. Initiliase gthreads only if they haven't been // initialised already. Since glib 2.24.0 (from its changelog): // "the requirements for g_thread_init() have been relaxed slightly, it // can be called multiple times, and does not have to be the first call. // GObject now links to GThread and threads are enabled automatically // when g_type_init() is called" // for backwards compatibility call to g_thread_init only if it hasn't // been called already if(!g_thread_supported()) { // Initialise gthreads even before gtk_init g_thread_init(NULL); } // This call is needed only if extra threads (not the main thread) // updates directly the GUI widgets using gdk_threads_enter // and gdk_threads_leave // It's not really needed since there's only 1 extra thread // (apart from the main one) and it doesn't update the GUI // straight away. It uses signals for that matter (see comment // in MainWindow::WorkerThread_computingFinished) //gdk_threads_init(); // this should be called before starting manipulating GUI stuff Gtk::Main kit(argc, argv); // create a new gtk builder. add_from_string will load the definitions Glib::RefPtr<Gtk::Builder> gtkBuilder = Gtk::Builder::create(); gtkBuilder->set_translation_domain(GETTEXT_PACKAGE); try { // Load the GUI from the xml code embedded in the executable file if (!gtkBuilder->add_from_string( // see src/gui_glade.h for more information reinterpret_cast<const char *>(__BIN_GUI_GLADE_START__), reinterpret_cast<gsize>(__BIN_GUI_GLADE_SIZE__))) { FatalError( argv[0], // i18n TRANSLATORS: Please leave the word Gtkbuilder as it is since it's the name // i18n of the component that failed // i18n Thank you for contributing to this project _("Could not load Gtkbuilder definitions"), GUI_EXCEPTION_ERR); } } #ifdef GLIBMM_EXCEPTIONS_ENABLED catch(const Glib::MarkupError& ex) { FatalError(argv[0], ex.what().c_str(), GUI_EXCEPTION_ERR); } catch(const Gtk::BuilderError& ex) { FatalError(argv[0], ex.what().c_str(), GUI_EXCEPTION_ERR); } #endif // GLIBMM_EXCEPTIONS_ENABLED catch (const GUIException& ex) { FatalError(argv[0], ex.what(), GUI_EXCEPTION_ERR); } MainWindow *pMainWindow = NULL; try { // first of all retrieve the Gtk::window object gtkBuilder->get_widget_derived(GUI_MAIN_WINDOW_NAME, pMainWindow); if (pMainWindow == NULL) { throw new GUIException( e_GUIException_GTKBuilderErr, __FILE__, __LINE__); } // if gdk_threads_enter and gdk_threads_leave were to be used // the Gtk::Main::run loop should be surrounded by // gdk_threads_enter and gdk_threads_leave // http://tadeboro.blogspot.com/2009/06/multi-threaded-gtk-applications.html kit.run(*pMainWindow); } catch (const GUIException& ex) { FatalError(argv[0], ex.what(), GUI_EXCEPTION_ERR); } catch (const std::exception& ex) { FatalError(argv[0], ex.what(), GUI_EXCEPTION_ERR); } if (pMainWindow) { // valgrind won't be happy if all this memory is not freed delete (pMainWindow); } // GUI MODE ////////////////// } else // (g_mode != 0) { ////////////////// // CONSOLE mode // do whatever we are requested to do per file passed as parameter to the // application. App will finish without running any GUI code if (g_mode == 1) { // 1 player total allocation (--mode=1) if ( (g_rows == GOPTION_INT_NOT_SET) || (g_columns == GOPTION_INT_NOT_SET) ) { FatalError( argv[0], _("Number of rows and number of columns must be specified in mode '1'"), TOTAL_ALLOC_BAD_OPTIONS_ERR); } if ( (g_rows <= 0) || (g_columns <= 0) ) { FatalError( argv[0], _("Number of rows and number of columns must be a greater than 1"), TOTAL_ALLOC_BAD_OPTIONS_ERR); } if ( (g_startingRow != g_startingColumn) && ( (g_startingRow == GOPTION_INT_NOT_SET) || (g_startingColumn == GOPTION_INT_NOT_SET) ) ) { // only one of the 2 options starting row and starting columns was set FatalError( argv[0], _("Both starting row and column must be set to configure a starting coordinate"), TOTAL_ALLOC_BAD_OPTIONS_ERR); } Coordinate startingCoord; if ( (g_startingRow != GOPTION_INT_NOT_SET) && (g_startingColumn != GOPTION_INT_NOT_SET) ) { if ( (g_startingRow >= g_rows) || (g_startingRow < 0) || (g_startingColumn >= g_columns) || (g_startingColumn < 0) ) { FatalError( argv[0], _("Application cannot start outside the board boundaries"), TOTAL_ALLOC_BAD_OPTIONS_ERR); } // overwrite uninitialised starting coordinate with the // user-selected one startingCoord.m_row = g_startingRow; startingCoord.m_col = g_startingColumn; } GameTotalAllocation theGame(g_rows, g_columns, startingCoord); if (theGame.Solve()) { std::cout << std::endl; // i18n TRANSLATORS: This string is to be printed when the applications finds // i18n a solution to a one-player total-allocation game // i18n Thank you for contributing to this project std::cout << _("SOLVED!") << std::endl; // print solved board on the screen theGame.GetBoard().PrintBoard(std::cout); } else { std::cout << std::endl; if (startingCoord.Initialised()) { // i18n TRANSLATORS: This string is to be printed when the applications cannot find // i18n a solution to a one-player total-allocation game. The integers printed on the screen // i18n are (respectively) number of rows, number of columns, starting row and starting column // i18n A typical string to be printed: // i18n "Could not allocate all the pieces in this 8x8 board, starting from row 0, column 2" // i18n Thank you for contributing to this project printf(_("Could not allocate all the pieces in this %dx%d board, starting from row %d, columnd %d\n"), g_rows, g_columns, g_startingRow, g_startingColumn); } else { // i18n TRANSLATORS: This string is to be printed when the applications cannot find // i18n a solution to a one-player total-allocation game starting from anywhere in the board // i18n Thank you for contributing to this project printf(_("Could not allocate all the pieces in this %dx%d board starting from anywhere in the board\n"), g_rows, g_columns); } } } else if (g_mode == 2) { // 1vs1 Game (--mode=2) if ( (g_blockemfilePath == NULL) || (g_blockemfilePath[0] == NULL) ) { FatalError( argv[0], _("At least one file with a 1vs1Game saved must be specified"), GAME1V1_BAD_OPTIONS_ERR); } if ( (g_heuristic < Heuristic::e_heuristicStartCount) || (g_heuristic >= Heuristic::e_heuristicCount) ) { snprintf(errorStringBuffer, ERROR_STRING_BUFFER_SIZE, // i18n TRANSLATORS: Please, leave that %d as it is. It will be replaced // i18n by the type of heuristic used by the user // i18n Thank you for contributing to this project _("Invalid heuristic type (%d)"), g_heuristic); FatalError( argv[0], errorStringBuffer, GAME1V1_BAD_OPTIONS_ERR); } // this little piece of magic uses the g_heuristic index to pick the expected evaluation // function. Have a look at heuristic.h Heuristic::EvalFunction_t heuristic = Heuristic::m_heuristicData[g_heuristic].m_evalFunction; // i18n TRANSLATORS: This string shows the chosen heuristic's name to the user // i18n the 1st %s will be replaced by the heuristic's name, and %d by its index // i18n Thank you for contributing to this project printf(_("Evaluation function set to %s (%d)\n"), _(Heuristic::m_heuristicData[g_heuristic].m_name), g_heuristic); if (g_depth <= 0) { FatalError( argv[0], _("Depth in 1vs1Game mode must be set to a positive value"), GAME1V1_BAD_OPTIONS_ERR); } else if ( (g_depth & 0x01) == 0) { std::cerr << argv[0] << ": " << _("Warning: For better results you might want to set the depth to an odd number") << std::endl; } // go through all the filenames array. Each file will be loaded into a gam1v1 and the next // move will be calculated and printed in the screen Game1v1 theGame; for (int32_t fileIndex = 0; g_blockemfilePath[fileIndex] != NULL; fileIndex++) { if (!g_file_test(g_blockemfilePath[fileIndex], G_FILE_TEST_IS_REGULAR)) { fprintf(stderr, // i18n TRANSLATORS: This string is shown when a file specified by the user // i18n cannot be loaded into the application. The 1st %s will be replaced // i18n by the name of the binary (normally "./blockem"), the 2nd one by // i18n the file that couldn't be accessed. Bear in mind the '\n' character should be there // i18n in the translated version of the string too // i18n Thank you for contributing to this project _("%s: Error: '%s' doesn't exist. Trying next file...\n"), argv[0], g_blockemfilePath[fileIndex]); continue; } std::ifstream cin; cin.open(g_blockemfilePath[fileIndex]); if(!cin) { fprintf(stderr, // i18n TRANSLATORS: This string is shown when a file specified by the user // i18n could not be opened to be read. The 1st %s will be replaced // i18n by the name of the binary (normally "./blockem"), the 2nd one by // i18n the file that couldn't be opened. Bear in mind the '\n' character should be there // i18n in the translated version of the string too // i18n Thank you for contributing to this project _("%s: Error: '%s' could not be opened. Trying next file...\n"), argv[0], g_blockemfilePath[fileIndex]); continue; } theGame.Reset(); if (theGame.LoadGame(cin) == false) { fprintf(stderr, // i18n TRANSLATORS: This string is shown when a file specified by the user // i18n does not contain a valid 1vs1 game. The 1st %s will be replaced // i18n by the name of the binary (normally "./blockem"), the 2nd one by // i18n the file that couldn't be opened. Bear in mind the '\n' character should be there // i18n in the translated version of the string too // i18n Thank you for contributing to this project _("%s: Error: '%s' does not contain a valid 1vs1Game. Trying next file...\n"), argv[0], g_blockemfilePath[fileIndex]); cin.close(); continue; } cin.close(); // i18n TRANSLATORS: '%s' will be replaced here by the path to the file // i18n successfully loaded. Bear in mind the '\n' character should be there // i18n in the translated version of the string too // i18n Thank you for contributing to this project printf (_("Game succesfully loaded from '%s'\n"), g_blockemfilePath[fileIndex]); // print current game on the screen theGame.SaveGame(std::cout); //std::cout << std::endl << std::endl; //theGame.GetMe().PrintNucleationPoints(std::cout); //std::cout << std::endl << std::endl; //theGame.GetOpponent().PrintNucleationPoints(std::cout); // these 2 variables will save the result calculated by minimax Piece resultPiece(e_noPiece); Coordinate resultCoord; // dummy volatile because no one will change it volatile sig_atomic_t dummyAtomic = 0; int32_t minimaxWinner = theGame.MinMax( heuristic, g_depth, Game1v1::e_Game1v1Player1, resultPiece, resultCoord, dummyAtomic); // i18n TRANSLATORS: '%d' will be replaced here by the value of the winning // i18n move calculated by the minimax engine. Bear in mind the '\n' character // i18n should be there in the translated version of the string too // i18n Thank you for contributing to this project printf(_("Winning evaluation function value: %d\n"), minimaxWinner); if (resultPiece.GetType() == e_noPiece) { std::cout << std::endl; // i18n TRANSLATORS: This string will be printed on the screen when the minimax // i18n engine cannot put down a piece. It is left up to the translator to decide // i18n to decide the formatting of the message, though this english version // i18n could be used as the template // i18n Thank you for contributing to this project std::cout << _(" ===================================================\n"\ " === END OF THE GAME. NO PIECE COULD BE PUT DOWN ===\n"\ " ===================================================\n"); } else { // out down the result piece on the board theGame.PutDownPiece( resultPiece, resultCoord, Game1v1::e_Game1v1Player1); // print the final game on the screen theGame.SaveGame(std::cout); } //theGame.GetMe().PrintNucleationPoints(std::cout); //theGame.GetOpponent().PrintNucleationPoints(std::cout); } // for (int32_t fileIndex = 0; g_blockemfilePath[fileIndex] != NULL; fileIndex++) } else // (g_mode != 1 && g_mode != 2) { snprintf(errorStringBuffer, ERROR_STRING_BUFFER_SIZE, // i18n TRANSLATORS: Please, leave that %d as it is. It will be replaced // i18n by the mode picked by the user // i18n Thank you for contributing to this project _("Invalid --mode option (%d)"), g_mode); FatalError( argv[0], errorStringBuffer, BAD_MODE_OPTION_ERR); } // CONSOLE mode ////////////////// } return 0; }