diff --git a/INSTALL b/INSTALL index 8c768c3..9ca73e7 100644 --- a/INSTALL +++ b/INSTALL @@ -1,83 +1,86 @@ +Installation on Linux/Unix +========================== -Installation sous Linux (Un*x) (bien/facile) ------------------------------- -* Pour installer à partir de l'archive CVS : +In the following, do not forget that the ./configure command can take options. +Run ./configure --help to have the list of available options. - ./bootstrap +* If you build from a CVS snapshot, run the following commands: + ./bootstrap + ./configure + make - ./configure - make - make install +Then, as root: + make install -* Pour installer à partir de l'archive tar.gz - - ./configure - make - make install +No graphical interface is built by default, see below for more details. -Il existe en fait 3 versions d'eliot, une en mode texte, une avec une -interface curses et une avec wxwidgets. Les modes peuvent être -sélectionnés à l'aide de la commande configure lors de la compilation -du programme. +* If you build from a release tarball, run the following commands: + ./configure + make -./configure --enable-text --enable-ncurses --enable-wxwidgets +Then, as root: + make install + +No graphical interface is built by default, see below for more details. + + +There are in fact several interfaces to Eliot: + - one in text mode: mostly useful to debug Eliot + - one using the ncursesw library: nice but not really graphical + - a wxWidgets interface: complete, but it does not allow multiplayer + modes, only training mode + - a Qt interface: currently under construction, it will replace the + wxWidgets interface when finished. It will support all Eliot features. + +These interfaces can be enabled or disabled at configuration time. Example: + ./configure --disable-text --enable-ncurses --disable-wxwidgets --enable-qt -Installation pour Windows (moins bien/facile) -------------------------- +Windows build +============= -Il y a 2 principales façons de procéder : -* directement depuis Windows, en utilisant Cygwin (http://www.cygwin.com/). -* depuis GNU/Linux, en utilisant le cross-compilateur Mingw32. +There are 2 ways to proceed: +* cross-compilation from a Linux host, using the mingw32 cross-compiler +* directly on Windows, using Cygwin (http://www.cygwin.com/) +Only the cross-compilation is officially supported (but adapting these +instructions for Cygwin shouldn't be too hard; patches welcome!). +Here are the steps for the cross-compilation: -Dans les 2 cas, les étapes sont les mêmes : -* installation de l'environnement de compilation (cette étape n'est pas - décrite ici, car elle ne rentre pas dans le cadre de ce document) +* install the build environment (this step is not documented here, + as it is out of the scope of this document) -* compilation et installation des dépendances (même remarque): +* build and install dependencies: + The Makefile in the 'extras/contrib' directory should be able to do it for you: - - wxWidgets (http://www.wxwidgets.org/), version 2.4.2 ou ultérieure, avec - support de l'unicode + cd extras/contrib && make all - - libiconv (http://www.gnu.org/software/libiconv/), de préférence compilée - en mode statique (--disable-shared --enable-static) + Eliot dependencies (libiconv, boost, wxWidgets and Qt) will be downloaded + and cross-compiled, except Qt, which is only downloaded. Install it with + Wine, ignoring the warning that mingw is not found. + The dependencies are installed in 'extras/contrib/inst' - - boost (http://www.boost.org/). Eliot n'utilise pas de librairie de Boost - (uniquement des headers), donc il n'y a pas vraiment besoin de compiler +* build Eliot: -* compilation d'Eliot : - - - si vous utilisez l'archive CVS, il faut générer le script 'configure' - (aussi bien sous Cygwin que sous GNU/Linux) : + - if you don't have the 'configure' script, generate it: ./bootstrap - - à cause d'un bug de gettext, il faut appliquer un patch aux fichiers installés - dans intl/ : - - télécharger le patch ici (lien en haut à gauche) : + - because of a bug in gettext, you need to apply a little patch to the files + installed in the 'intl' directory: + - download the patch here (link in the top-left-hand corner) http://www.koders.com/noncode/fid46DF595700FEB564B6EF45BFF55067F95DCF0420.aspx - - exécuter la commande suivante : + - apply the patch: patch -p2 < gettext-win32.patch - - avec Cygwin, configurer avec la ligne de commande suivante : - CPPFLAGS=-I/path/to/installs/include LDFLAGS=-L/path/to/installs/lib \ - CC="gcc -mno-cygwin" CXX="g++ -mno-cygwin" \ - ./configure --enable-wxwidgets --with-wx-config=/path/to/wx-config \ - --with-boost=/path/to/boost/installs - en prenant soin de remplacer les différents chemins par les bonnes valeurs. - Ensuite, un simple 'make' suffit pour terminer la compilation, - éventuellement suivi de 'make install'. - - - pour la cross-compilation depuis GNU/Linux, configurer avec la ligne - de commande suivante : - CPPFLAGS=-I/path/to/installs/include LDFLAGS=-L/path/to/installs/lib \ + - configure with the following command: + export INST=`pwd`/extras/contrib/inst && \ + CPPFLAGS=-I${INST}/include LDFLAGS=-L${INST}/lib \ CC=i586-mingw32msvc-gcc CXX=i586-mingw32msvc-g++ \ ./configure --host=i586-mingw32msvc --build=i386-linux \ - --enable-wxwidgets --with-wx-config=/path/to/wx-config \ - --with-boost=/path/to/installs - en prenant soin de remplacer les différents chemins par les bonnes valeurs. - Ensuite, un simple 'make' suffit pour terminer la compilation, - éventuellement suivi de 'make install'. + --enable-wxwidgets --with-wx-config=${INST}/bin/wx-config \ + --with-boost=${INST} + + - to compile, run 'make', possibly followed with 'make install' diff --git a/extras/contrib/Makefile b/extras/contrib/Makefile new file mode 100644 index 0000000..27be5a7 --- /dev/null +++ b/extras/contrib/Makefile @@ -0,0 +1,85 @@ +ICONV_VERSION = 1.12 +WX_VERSION = 2.6.4 +BOOST_VERSION = 1_34_1 +QT_VERSION = 4.3.3 + +PREFIX = $(shell pwd)/inst +WGET = wget -c + +# XXX: Hardcoded for mingw on linux, at the moment +CC = i586-mingw32msvc-gcc +CXX = i586-mingw32msvc-g++ +CPPFLAGS += -I$(PREFIX)/include +CONFIGURE = CC=$(CC) CXX=$(CXX) CPPFLAGS=$(CPPFLAGS) ./configure --host=i586-mingw32msvc --build=i386-linux --prefix=$(PREFIX) + +.PHONY: help all + +help: + echo "Usage: make all" + +all: .iconv .wxWidgets .boost .qt + + +### iconv ### + +ICONV_DIR = libiconv-$(ICONV_VERSION) +ICONV_ARCHIVE = libiconv-$(ICONV_VERSION).tar.gz + +$(ICONV_DIR): + $(WGET) http://ftp.gnu.org/pub/gnu/libiconv/$(ICONV_ARCHIVE) + tar xzf $(ICONV_ARCHIVE) + +.iconv: $(ICONV_DIR) + (cd $< && $(CONFIGURE) --enable-static --disable-shared && make && make install) + touch $@ + + +### wxWidgets ### + +WX_ARCHIVE = wxMSW-$(WX_VERSION).zip +WX_DIR = wxWidgets-$(WX_VERSION) +WX_DOS_FILES = config* *.sh install-sh mkinstalldirs + +$(WX_DIR): + $(WGET) http://heanet.dl.sourceforge.net/sourceforge/wxwindows/$(WX_ARCHIVE) + unzip $(WX_ARCHIVE) + +.wxWidgets: $(WX_DIR) + (cd $< && dos2unix $(WX_DOS_FILES) && chmod +x $(WX_DOS_FILES)) + (cd $< && $(CONFIGURE) --disable-shared --enable-optimise --disable-debug --enable-unicode --without-libtiff --without-expat --without-zlib --without-libpng --without-libjpeg --without-regex --disable-mediactrl && make && make install) + dos2unix $(PREFIX)/bin/wx-config + touch $@ + + +### Boost ### + +BOOST_DIR = boost_$(BOOST_VERSION) +BOOST_ARCHIVE = boost_$(BOOST_VERSION).tar.bz2 + +$(BOOST_DIR): + $(WGET) http://garr.dl.sourceforge.net/sourceforge/boost/$(BOOST_ARCHIVE) + tar xjf $(BOOST_ARCHIVE) + +# We don't build any library, because we don't need them (and it is really +# hard to cross-compile with their crappy build system) +.boost: $(BOOST_DIR) + #(cd $< && ./configure --prefix=$(PREFIX) && ./tools/jam/src/bin.linuxx86/bjam --toolset=gcc --prefix=$(PREFIX) --without-date_time --without-filesystem --without-graph --without-iostreams --without-program_options --without-python --without-regex --without-serialization --without-signals --without-test --without-thread --without-wave install) + cp -r $ #include +#if ENABLE_NLS +# include +# define _(String) gettext(String) +#else +# define _(String) String +#endif + #include "game_factory.h" #include "game.h" #include "training.h" #include "freegame.h" #include "duplicate.h" +#include "player.h" #include "dic.h" +#include "encoding.h" GameFactory *GameFactory::m_factory = NULL; -GameFactory::GameFactory(): m_dic(NULL), m_human(0), m_ai(0), m_joker(false) +GameFactory::GameFactory(): m_dic(NULL), m_joker(false) { } @@ -94,12 +103,12 @@ Game *GameFactory::createFromCmdLine(int argc, char **argv) {"dictionary", required_argument, NULL, 'd'}, {"dict", required_argument, NULL, 'd'}, {"mode", required_argument, NULL, 'm'}, - {"human", no_argument, NULL, 300}, - {"ai", no_argument, NULL, 400}, + {"human", required_argument, NULL, 'u'}, + {"ai", required_argument, NULL, 'a'}, {"joker", no_argument, NULL, 500}, {0, 0, 0, 0} }; - static char short_options[] = "hvd:m:"; + static char short_options[] = "hvd:m:u:a:"; int option_index = 1; int res; @@ -126,11 +135,22 @@ Game *GameFactory::createFromCmdLine(int argc, char **argv) m_modeStr = optarg; found_m = true; break; - case 300: - m_human++; - break; - case 400: - m_ai++; + case 'u': + case 'a': + // Handle both types of players together + { + wstring name; + if (optarg == NULL) + { + // TODO: use Boost.Format + char s[200]; + snprintf(s, 200, _("Player %u"), m_players.size() + 1); + name = convertToWc(s); + } + else + name = convertToWc(optarg); + m_players.push_back(make_pair(res == 'u', name)); + } break; case 500: m_joker = true; @@ -184,10 +204,15 @@ Game *GameFactory::createFromCmdLine(int argc, char **argv) } // 5) Add the players - for (int i = 0; i < m_human; i++) - game->addHumanPlayer(); - for (int i = 0; i < m_ai; i++) - game->addAIPlayer(); + for (unsigned int i = 0; i < m_players.size(); ++i) + { + // Human? + if (m_players[i].first) + game->addHumanPlayer(); + else + game->addAIPlayer(); + const_cast(&game->getPlayer(i))->setName(m_players[i].second); + } // 6) Set the variant if (m_joker) @@ -225,9 +250,9 @@ void GameFactory::printUsage(const string &iBinaryName) const << " -v, --version Print version information and exit" << endl << " -m, --mode {duplicate,d,freegame,f,training,t}" << endl << " Choose game mode (mandatory)" << endl - << " -d, --dict Choose a dictionary (mandatory)" << endl - << " --human Add a human player" << endl - << " --ai Add a AI (Artificial Intelligence) player" << endl + << " -d, --dict Choose a dictionary (mandatory)" << endl + << " -u --human Add a human player" << endl + << " -a --ai Add a AI (Artificial Intelligence) player" << endl << " --joker Play with the \"Joker game\" variant" << endl; } diff --git a/game/game_factory.h b/game/game_factory.h index 07faf92..dbddc7a 100644 --- a/game/game_factory.h +++ b/game/game_factory.h @@ -22,8 +22,12 @@ #define _GAME_FACTORY_H_ #include +#include using std::string; +using std::wstring; +using std::vector; +using std::pair; class Dictionary; class Game; @@ -85,15 +89,12 @@ private: /// Game mode string m_modeStr; - /// Number of human players - int m_human; - - /// Number of AI players - int m_ai; - /// Variant of the game bool m_joker; + typedef pair PlayerDesc; + vector m_players; + //@} /// Print command-line usage diff --git a/game/player.h b/game/player.h index f1fe6c4..ba20100 100644 --- a/game/player.h +++ b/game/player.h @@ -27,6 +27,8 @@ #include "pldrack.h" #include "history.h" +using std::wstring; + class Turn; @@ -43,12 +45,17 @@ public: // Pseudo RTTI virtual bool isHuman() const = 0; + /// Get the name of the player + const wstring & getName() const { return m_name; } + /// Set the name of the player + void setName(const wstring &iName) { m_name = iName; } + /************************** * General getters **************************/ - // Get the (possibly incomplete) rack of the player + /// Get the (possibly incomplete) rack of the player const PlayedRack & getCurrentRack() const; - // Get the previous rack + /// Get the previous rack const PlayedRack & getLastRack() const; /// Get the previous move (corresponding to the previous rack...) const Move & getLastMove() const; @@ -56,6 +63,7 @@ public: void setCurrentRack(const PlayedRack &iPld); const History& getHistory() const { return m_history; } + /// Remove last turn void removeLastTurn(); @@ -84,6 +92,9 @@ private: /// Score of the player int m_score; + /// Name of the player + wstring m_name; + /// History of the racks and rounds for the player History m_history; }; diff --git a/game/training.cpp b/game/training.cpp index 52bc0c4..d9e0b44 100644 --- a/game/training.cpp +++ b/game/training.cpp @@ -21,6 +21,13 @@ #include +#if ENABLE_NLS +# include +# define _(String) gettext(String) +#else +# define _(String) String +#endif + #include "dic.h" #include "tile.h" #include "rack.h" @@ -37,6 +44,9 @@ Training::Training(const Dictionary &iDic) : Game(iDic) { + // Training mode implicitly uses 1 human player + Game::addHumanPlayer(); + m_players[0]->setName(convertToWc(_("Training"))); } @@ -124,8 +134,6 @@ int Training::start() if (getNPlayers() != 0) return 1; - // Training mode implicitly uses 1 human player - Game::addHumanPlayer(); m_currPlayer = 0; return 0; } diff --git a/utils/ncurses.cpp b/utils/ncurses.cpp index 7d5fb75..7ad0069 100644 --- a/utils/ncurses.cpp +++ b/utils/ncurses.cpp @@ -27,6 +27,7 @@ #include #include +#include #include "ncurses.h" #include "dic.h" @@ -266,14 +267,27 @@ void CursesIntf::drawBoard(WINDOW *win, int y, int x) const void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const { + // Compute the longest player name + unsigned int longest = 0; + for (unsigned int i = 0; i < m_game->getNPlayers(); i++) + { + longest = std::max(longest, m_game->getPlayer(i).getName().size()); + } + Box box(win, y, x, m_game->getNPlayers() + 2, 25); box.draw(_("Scores")); + // Magic formula to truncate too long names + unsigned int maxForScores = + std::min(longest, + box.getWidth() - strlen(_("%s: %d")) - 1); for (unsigned int i = 0; i < m_game->getNPlayers(); i++) { if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attron(A_BOLD); - mvwprintw(win, y + i + 1, x + 2, - _("Player %d: %d"), i, m_game->getPlayer(i).getPoints()); + mvwprintw(win, y + i + 1, x + 2, _("%s: %d"), + truncOrPad(convertToMb(m_game->getPlayer(i).getName()), + maxForScores).c_str(), + m_game->getPlayer(i).getPoints()); if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attroff(A_BOLD); } @@ -283,13 +297,19 @@ void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const Box box2(win, y + yOff, x, m_game->getNPlayers() + 2, 25); box2.draw(_("Racks")); + // Magic formula to truncate too long names + unsigned int maxForRacks = + std::min(longest, + box.getWidth() - strlen(_("%s: %ls")) - 4); for (unsigned int i = 0; i < m_game->getNPlayers(); i++) { if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attron(A_BOLD); wstring rack = m_game->getPlayer(i).getCurrentRack().toString(PlayedRack::RACK_SIMPLE); - mvwprintw(win, y + yOff + i + 1, x + 2, - _("Player %d: %ls"), i, rack.c_str()); + mvwprintw(win, y + yOff + i + 1, x + 2, _("%s: %ls"), + truncOrPad(convertToMb(m_game->getPlayer(i).getName()), + maxForRacks).c_str(), + rack.c_str()); if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attroff(A_BOLD); // Force to refresh the whole rack