diff --git a/INSTALL b/INSTALL index bd7fcb4..8c768c3 100644 --- a/INSTALL +++ b/INSTALL @@ -1,6 +1,7 @@ + Installation sous Linux (Un*x) (bien/facile) ------------------------------ -* Pour installer à partir de l'archive CVS : +* Pour installer à partir de l'archive CVS : ./bootstrap @@ -8,16 +9,16 @@ Installation sous Linux (Un*x) (bien/facile) make make install -* Pour installer à partir de l'archive tar.gz +* Pour installer à partir de l'archive tar.gz ./configure make make install -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 +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. ./configure --enable-text --enable-ncurses --enable-wxwidgets @@ -27,39 +28,56 @@ du programme. Installation pour Windows (moins bien/facile) ------------------------- -Il y a 2 principales façons de procéder : +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. -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) +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) -* compilation et installation de wxWindows (http://www.wxwidgets.org/), - version 2.4.2 ou ultérieure (même remarque) +* compilation et installation des dépendances (même remarque): + + - wxWidgets (http://www.wxwidgets.org/), version 2.4.2 ou ultérieure, avec + support de l'unicode + + - libiconv (http://www.gnu.org/software/libiconv/), de préférence compilée + en mode statique (--disable-shared --enable-static) + + - 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 * compilation d'Eliot : - - si vous utilisez l'archive CVS, il faut générer le script 'configure' + - si vous utilisez l'archive CVS, il faut générer le script 'configure' (aussi bien sous Cygwin que sous GNU/Linux) : ./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) : + http://www.koders.com/noncode/fid46DF595700FEB564B6EF45BFF55067F95DCF0420.aspx + - exécuter la commande suivante : + 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 --with-wx-config=/path/to/wx-config - en prenant soin d'indiquer le chemin correct vers le fichier - 'wx-config' de l'installation de wxWindows. + ./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'. + é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 \ CC=i586-mingw32msvc-gcc CXX=i586-mingw32msvc-g++ \ ./configure --host=i586-mingw32msvc --build=i386-linux \ - --with-wx-config=/path/to/wx-config - en prenant soin d'indiquer le chemin correct vers le fichier - 'wx-config' de l'installation de wxWindows. + --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'. + éventuellement suivi de 'make install'. diff --git a/TODO b/TODO index 79f33f3..07b5aff 100644 --- a/TODO +++ b/TODO @@ -1,33 +1,19 @@ -* ==================== -* TODO Current version -* ==================== - - - Correct game save/load functions : Advanced format - file saving for freegames and duplicate need a serious - rewrite. We need to specifie a file format that can handle - all the information contained in a multiplayer game. - - - Add "joker" type games in wxwin version of Eliot, Freegame - and Duplicate will follow - - - full French i18n interface and error messages for wxwin. - - * ================== * Next Eliot version * ================== - - new dictionnary format that includes tiles - - number - - points - - printable equivalent - - other languages support using the new dictionary - - new wxWidgets interface + - Improve error handling (use exceptions more) + - Correct game save/load functions: Advanced format + file saving for freegames and duplicate need a serious + rewrite. We need to specify a file format that can handle + all the information contained in a multiplayer game. + - rack shuffling + - new wxWidgets or QT interface - support of the different modes - ability to choose the number and type of the players - ability to display the history and score of all the players - -- partly done : history is now a separate class + -- partly done: history is now a separate class - detection of blocked positions? - getopt support for all the interfaces (only ncurses is done) diff --git a/configure.in b/configure.in index 30f9793..b10ec68 100644 --- a/configure.in +++ b/configure.in @@ -7,6 +7,8 @@ AC_INIT(eliot, 1.5) AC_CONFIG_SRCDIR(wxwin/main.cc) AM_INIT_AUTOMAKE AM_CONFIG_HEADER(config.h) +AC_CANONICAL_HOST +AC_CANONICAL_BUILD AM_OPTIONS_WXCONFIG dnl -------------------------------------------------------------- @@ -14,6 +16,8 @@ dnl Checks for programs. dnl -------------------------------------------------------------- AC_PROG_INSTALL AC_PROG_CC +dnl Needed for gettext +AC_GNU_SOURCE AC_PROG_CXX AC_PROG_MAKE_SET AC_PROG_RANLIB @@ -23,6 +27,7 @@ if test "$YACC" = yacc ; then AC_MSG_ERROR([Could not find the 'bison' program on your system]) fi +dnl Better than AC_PROG_LEX AM_PROG_LEX if test "$LEX" != "flex" ; then AC_MSG_ERROR([Could not find the 'flex' program on your system]) @@ -56,15 +61,17 @@ fi dnl Debug mode AC_ARG_ENABLE([debug],AC_HELP_STRING([--enable-debug],[debug mode (default disabled)])) if test "${enable_debug}" = "yes"; then - CFLAGS+=" -g -DDEBUG" - CXXFLAGS+=" -g -DDEBUG" + CPPFLAGS+=" -DDEBUG" + CFLAGS+=" -g" + CXXFLAGS+=" -g" fi dnl Profile mode AC_ARG_ENABLE([profile],AC_HELP_STRING([--enable-profile],[profile mode (default disabled)])) if test "${enable_profile}" = "yes"; then - CFLAGS+=" -pg -DPROFILE" - CXXFLAGS+=" -pg -DPROFILE" + CPPFLAGS+=" -DPROFILE" + CFLAGS+=" -pg" + CXXFLAGS+=" -pg" LDFLAGS+=" -pg" fi @@ -77,6 +84,9 @@ dnl Checks for header files. dnl -------------------------------------------------------------- AC_HEADER_STDC AC_CHECK_HEADERS(fcntl.h unistd.h sys/wait.h) +AC_CHECK_HEADERS(arpa/inet.h netinet/in.h) +AC_CHECK_HEADERS([readline/readline.h], [has_readline=1], [has_readline=0]) +AM_CONDITIONAL(HAS_READLINE, test "$has_readline" = "1") dnl -------------------------------------------------------------- dnl Checks for typedefs, structures, and compiler characteristics. @@ -85,22 +95,26 @@ AC_C_CONST AC_TYPE_SIZE_T AC_C_BIGENDIAN AC_C_INLINE -AC_CHECK_SIZEOF(char, 1) -AC_CHECK_SIZEOF(short, 2) -AC_CHECK_SIZEOF(int *, 4) -AC_CHECK_SIZEOF(int, 4) -AC_CHECK_SIZEOF(long, 4) -AC_CHECK_SIZEOF(long long, 0) +dnl AC_CHECK_SIZEOF(char, 1) +dnl AC_CHECK_SIZEOF(short, 2) +dnl AC_CHECK_SIZEOF(int *, 4) +dnl AC_CHECK_SIZEOF(int, 4) +dnl AC_CHECK_SIZEOF(long, 4) +dnl AC_CHECK_SIZEOF(long long, 0) dnl -------------------------------------------------------------- dnl Checks for library functions. dnl -------------------------------------------------------------- AC_FUNC_MEMCMP +AC_CHECK_FUNCS([wcwidth]) dnl -------------------------------------------------------------- dnl Checks for libraries. dnl -------------------------------------------------------------- +dnl Check for the Boost libraries (in fact we only need the headers) +AX_BOOST_BASE([1.33.1]) + dnl Check for wxWidgets AC_ARG_ENABLE([wxwidgets],AC_HELP_STRING([--enable-wxwidgets],[wxWidgets interface support (default disabled)])) if test "${enable_wxwidgets}" = "yes" @@ -125,27 +139,53 @@ fi AM_CONDITIONAL([BUILD_WXWIDGETS], [test "${wxWin}" = "1"]) dnl Check for ncurses -AC_ARG_ENABLE([ncurses],AC_HELP_STRING([--enable-ncurses],[ncurses interface support (default disabled)])) -if test "${enable_ncurses}" = "yes" -then - AC_CHECK_HEADERS(ncurses.h, want_ncurses=1, - [AC_MSG_ERROR([Could not find the ncurses library on your system])]) +dnl We enable it if asked by the user, or if ncursesw is found +AC_ARG_ENABLE([ncurses],AC_HELP_STRING([--enable-ncurses], + [ncurses interface support (default enabled if ncursesw found on your system)])) +AC_CHECK_HEADERS(ncursesw/curses.h, [has_ncursesw=1], [has_ncursesw=0]) +if test "${enable_ncurses}" != "no" -a "${has_ncursesw}" = "1"; then + want_ncurses=1 +else + want_ncurses=0 + if test "${enable_ncurses}" = "yes"; then + AC_MSG_ERROR([Could not find the ncursesw library on your system]) + fi fi AM_CONDITIONAL([BUILD_NCURSES], [test "${want_ncurses}" = "1"]) dnl Enable/disable text version AC_ARG_ENABLE([text],AC_HELP_STRING([--enable-text],[text interface support (default enabled)])) -if test "${enable_text}" != "no" -then - AC_CHECK_HEADERS(readline/readline.h, want_text=1, - [AC_MSG_ERROR([Could not find the readline library on your system])]) -fi AM_CONDITIONAL([BUILD_TEXT], [test "${enable_text}" != "no"]) dnl Internationalization macros -AM_GNU_GETTEXT_VERSION(0.11.5) +AM_GNU_GETTEXT_VERSION(0.16.1) AM_GNU_GETTEXT +dnl Iconv +dnl This test depends on AM_GNU_GETTEXT executed before +AS_IF([test "$am_cv_func_iconv" != "yes"], + [AC_MSG_ERROR([libiconv is needed for Eliot to work properly])]) + + +dnl Information about who built eliot (useful for the dictionary) +AC_DEFINE_UNQUOTED(ELIOT_COMPILE_BY, "`whoami`", [user who ran configure]) +AC_DEFINE_UNQUOTED(ELIOT_COMPILE_HOST, "`hostname`", [host which ran configure]) + +dnl Check the operating system +case "${host_os}" in + *mingw32* | *cygwin*) + SYS=mingw32 + ;; + *) + dnl nothing to do + ;; +esac + +if test "$SYS" = "mingw32"; then + # For ntohl, in particular + LIBS="${LIBS} -lws2_32" +fi + dnl -------------------------------------------------------------- dnl Output dnl -------------------------------------------------------------- diff --git a/dic/.cvsignore b/dic/.cvsignore index b4d3462..567e869 100644 --- a/dic/.cvsignore +++ b/dic/.cvsignore @@ -1,10 +1,10 @@ .deps Makefile Makefile.in -scanner.h -er.c -libdic_a-er.c -libdic_a-er.h +libdic_a-erl.cpp +libdic_a-erl.h +libdic_a-ery.cpp +libdic_a-ery.h compdic listdic regexp diff --git a/dic/Makefile.am b/dic/Makefile.am index 2567778..513bcd9 100644 --- a/dic/Makefile.am +++ b/dic/Makefile.am @@ -1,6 +1,7 @@ # Eliot -# Copyright (C) 1999 Antoine Fraboulet -# antoine.fraboulet@free.fr +# Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière +# Authors: Antoine Fraboulet +# Olivier Teulière # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,67 +19,71 @@ noinst_LIBRARIES = libdic.a -INCLUDES = -I$(top_srcdir) +localedir = $(datadir)/locale +AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" -libdic_a_CFLAGS=$(DEBUGFLAGS) -libdic_a_YFLAGS= +INCLUDES = -I$(top_srcdir) -I../intl -I$(top_srcdir)/intl $(INCICONV) + +libdic_a_CFLAGS= +libdic_a_YFLAGS=-d libdic_a_LFLAGS= -libdic_a_SOURCES= \ - ery.y \ - erl.l \ - dic_internals.h \ - dic_search.c dic_search.h \ - dic.c dic.h \ - automaton.c automaton.h \ - hashtable.h hashtable.c \ - regexp.c regexp.h \ - alist.h alist.c +libdic_a_SOURCES = \ + erl.lpp \ + ery.ypp \ + dic_exception.cpp dic_exception.h \ + header.cpp header.h \ + dic_internals.h \ + tile.cpp tile.h \ + dic.cpp dic.h \ + dic_search.cpp \ + encoding.cpp encoding.h \ + automaton.cpp automaton.h \ + regexp.cpp regexp.h -BUILT_SOURCES= \ - libdic_a-erl.c \ - libdic_a-erl.h \ - libdic_a-ery.c \ +BUILT_SOURCES= \ + libdic_a-erl.cpp \ + libdic_a-erl.h \ + libdic_a-ery.cpp \ libdic_a-ery.h -nodist_libdic_a_SOURCES= \ - libdic_a-erl.c \ - libdic_a-erl.h \ - libdic_a-ery.c \ +# This hook triggers on 'make dist' (and 'make distcheck') +# XXX: In fact, the recommended behaviour is: +# - list only libdic_a-ery.h in BUILT_SOURCES, +# - do not die with an error in configure.in if flex or bison is not found +# - do not have any dist-hook trigger +# The result is that the generated files are kept in the tarball generated with make dist, +# with still an error message for developers when the ypp or lpp file has been modified +# and bison or flex is not found. +# The problem is that, even though Automake is aware of the header generated by bison, +# it seems to have problems with the one generated by flex... +dist-hook: + -for file in $(BUILT_SOURCES) ; do rm -f $(distdir)/$$file ; done + +CLEANFILES= \ + libdic_a-erl.cpp \ + libdic_a-erl.h \ + libdic_a-ery.cpp \ libdic_a-ery.h -CLEANFILES= \ - libdic_a-erl.c \ - libdic_a-erl.h \ - libdic_a-ery.c \ - libdic_a-ery.h - - -## automake workaround to generate .h file -libdic_a-erl.h: erl.l - ${LEX} ${srcdir}/erl.l - -##################################### - ##################################### if BUILD_DICTOOLS -bin_PROGRAMS = \ - compdic \ - listdic \ +bin_PROGRAMS = \ + compdic \ + listdic \ regexp -compdic_SOURCES= \ - dic_internals.h \ - hashtable.c hashtble.h \ - compdic.c +compdic_SOURCES=compdic.cpp \ + hashtable.h hashtable.cpp hashtable.i +compdic_CPPFLAGS=$(AM_CPPFLAGS) @BOOST_CPPFLAGS@ +compdic_LDADD=libdic.a @LIBINTL@ -listdic_SOURCES= \ - dic_internals.h \ - dic.c dic.h \ - listdic.c +listdic_SOURCES=listdic.cpp +listdic_LDADD=libdic.a @LIBINTL@ #regexp_CFLAGS=-DDEBUG_RE -regexp_SOURCES=regexpmain.c -regexp_LDADD=libdic.a +regexp_SOURCES=regexpmain.cpp +regexp_LDADD=libdic.a @LIBINTL@ endif + diff --git a/dic/alist.c b/dic/alist.c deleted file mode 100644 index 88d3cef..0000000 --- a/dic/alist.c +++ /dev/null @@ -1,201 +0,0 @@ -/* Eliot */ -/* Copyright (C) 2005 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file alist.c - * \brief List type used by automaton - * \author Antoine Fraboulet - * \date 2005 - */ - -#include -#include "alist.h" - -#define __UNUSED__ __attribute__((unused)) - -struct alist_elt_t { - void* info; - alist_elt next; -}; - -struct alist_t { - int size; - void (*delete_function)(void*); - alist_elt start; -}; - - -void* -alist_elt_get_value(alist_elt e) -{ - return e->info; -} - -alist_elt -alist_elt_create(void* info) -{ - alist_elt e; - e = (alist_elt)malloc(sizeof(struct alist_elt_t)); - e->info = info; - e->next = NULL; - return e; -} - -/* ************************************************** */ -/* ************************************************** */ -/* ************************************************** */ - -alist -alist_create() -{ - alist l; - l = (alist)malloc(sizeof(struct alist_t)); - l->size = 0; - l->start = NULL; - l->delete_function = NULL; - return l; -} - -alist -alist_clone(alist l) -{ - alist t; - alist_elt ptr; - t = alist_create(); - for(ptr = alist_get_first(l); ptr ; ptr = alist_get_next(l,ptr)) - { - alist_add(t,alist_elt_get_value(ptr)); - } - return t; -} - -void -alist_set_delete (alist l, void (*f)(void*)) -{ - l->delete_function = f; -} - -static void -alist_delete_rec(alist_elt e, void (*delete_function)(void*)) -{ - if (e != NULL) - { - alist_delete_rec(e->next, delete_function); - if (delete_function) - delete_function(e->info); - e->info = NULL; - free(e); - } -} - -void -alist_delete(alist l) -{ - alist_delete_rec(l->start,l->delete_function); - free(l); -} - -void -alist_add(alist l, void* value) -{ - alist_elt e; - e = alist_elt_create(value); - e->next = l->start; - l->start = e; - l->size ++; -} - -int -alist_is_in(alist l, void* e) -{ - alist_elt ptr; - for(ptr = alist_get_first(l); ptr; ptr = alist_get_next(l,ptr)) - if (alist_elt_get_value(ptr) == e) - return 1; - return 0; -} - -int -alist_equal(alist id1, alist id2) -{ - alist_elt e1; - - if (alist_get_size(id1) != alist_get_size(id2)) - return 0; - - for(e1 = alist_get_first(id1) ; e1 ; e1 = alist_get_next(id1,e1)) - { - if (! alist_is_in(id2, alist_elt_get_value(e1))) - return 0; - } - - return 1; -} - -void -alist_insert(alist dst, alist src) -{ - alist_elt ptr; - for(ptr = alist_get_first(src); ptr ; ptr = alist_get_next(src,ptr)) - { - void *e = alist_elt_get_value(ptr); - if (! alist_is_in(dst,e)) - alist_add(dst,e); - } -} - -alist_elt -alist_get_first(alist l) -{ - return l->start; -} - -alist_elt -alist_get_next(alist __UNUSED__ l, alist_elt e) -{ - return e->next; -} - -void* -alist_pop_first_value(alist l) -{ - void* p = NULL; - alist_elt e = l->start; - if (e) - { - l->start = e->next; - e->next = NULL; - p = e->info; - l->size --; - alist_delete_rec(e,l->delete_function); - } - return p; -} - -int -alist_get_size(alist l) -{ - return l->size; -} - -int -alist_is_empty(alist l) -{ - return l->size == 0; -} diff --git a/dic/alist.h b/dic/alist.h deleted file mode 100644 index aeaf35c..0000000 --- a/dic/alist.h +++ /dev/null @@ -1,98 +0,0 @@ -/* Eliot */ -/* Copyright (C) 2005 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file alist.h - * \brief List type used by automaton - * \author Antoine Fraboulet - * \date 2005 - */ - -#ifndef _ALIST_H_ -#define _ALIST_H_ -#if defined(__cplusplus) -extern "C" - { -#endif - - /** - * untyped list type element - */ - typedef struct alist_elt_t* alist_elt; - - /** - * extract the value from an alist element - * result is untyped si the user should know - * what the value type is - */ - void* alist_elt_get_value(alist_elt); - - /** - * untyped list type - */ - typedef struct alist_t* alist; - - /** - * list creation - * @returns list - */ - alist alist_create (); - alist alist_clone (alist); - - /** - * funtion to use on data during list deletion. - */ - void alist_set_delete (alist,void (*f)(void*)); - - /** - * delete a complete list. - */ - void alist_delete (alist); - - /** - * add a element to the list - */ - void alist_add (alist, void*); - void alist_insert (alist, alist); - /** - * get first element - */ - int alist_is_in (alist l, void* e); - int alist_equal (alist , alist); - - alist_elt alist_get_first (alist); - - /** - * get next element from current - */ - alist_elt alist_get_next (alist,alist_elt); - - /** - * @returns 0 or 1 - */ - int alist_is_empty (alist); - - int alist_get_size (alist); - - void* alist_pop_first_value (alist); - -#if defined(__cplusplus) - } -#endif -#endif /* _ALIST_H_ */ diff --git a/dic/automaton.c b/dic/automaton.c deleted file mode 100644 index 4ea9a3d..0000000 --- a/dic/automaton.c +++ /dev/null @@ -1,679 +0,0 @@ -/* Eliot */ -/* Copyright (C) 2005 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file automaton.c - * \brief (Non)Deterministic Finite Automaton for Regexp - * \author Antoine Fraboulet - * \date 2005 - */ - -#include "config.h" -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_WAIT_H -# include -#endif -#include - -#include "dic.h" -#include "regexp.h" -#include "alist.h" -#include "automaton.h" - -#ifdef DEBUG_AUTOMATON -#define DMSG(a) a -#else -#define DMSG(a) -#endif - -#define MAX_TRANSITION_LETTERS 256 - -typedef struct automaton_state_t *astate; -typedef struct Automaton_t *Automaton; - -/* ************************************************** * - exported functions for static automata - * ************************************************** */ - -automaton automaton_build (int init_state, int *ptl, int *PS, struct search_RegE_list_t *list); -void automaton_delete (automaton a); -int automaton_get_nstate (automaton a); -int automaton_get_init (automaton a); -int automaton_get_accept (automaton a, int state); -int automaton_get_next_state (automaton a, int start, char l); -void automaton_dump (automaton a, char* filename); - - -/* ************************************************** * - static functions for dynamic automata - * ************************************************** */ - -static Automaton s_automaton_create (); -static void s_automaton_delete (Automaton a); - -static alist s_automaton_id_create (int id); -static astate s_automaton_state_create (alist id); - -static void s_automaton_add_state (Automaton a, astate s); -static astate s_automaton_get_state (Automaton a, alist id); - -static Automaton s_automaton_PS_to_NFA (int init_state, int *ptl, int *PS); -static Automaton s_automaton_NFA_to_DFA (Automaton a, struct search_RegE_list_t *list); -static automaton s_automaton_finalize (Automaton a); - -#ifdef DEBUG_AUTOMATON -static char* s_automaton_id_to_str (alist id); -static void s_automaton_dump (Automaton a, char* filename); -#endif - -/* ************************************************** * - data types - * ************************************************** */ - -struct automaton_state_t { - alist id; // alist of int - int accept; - int id_static; - astate next[MAX_TRANSITION_LETTERS]; -}; - -struct Automaton_t { - int nstates; - astate init_state; - alist states; // alist of alist of int -}; - -struct automaton_t { - int nstates; - int init; - int *accept; - int **trans; -}; - -/* ************************************************** * - exported functions for static automata - * ************************************************** */ - -automaton automaton_build(int init_state, int *ptl, int *PS, struct search_RegE_list_t *list) -{ - Automaton nfa,dfa; - automaton final; - - nfa = s_automaton_PS_to_NFA(init_state,ptl,PS); - DMSG(printf("\n non deterministic automaton OK \n\n")); - DMSG(s_automaton_dump(nfa,"auto_nfa")); - - dfa = s_automaton_NFA_to_DFA(nfa, list); - DMSG(printf("\n deterministic automaton OK \n\n")); - DMSG(s_automaton_dump(dfa,"auto_dfa")); - - final = s_automaton_finalize(dfa); - DMSG(printf("\n final automaton OK \n\n")); - DMSG(automaton_dump(final,"auto_fin")); - - s_automaton_delete(nfa); - s_automaton_delete(dfa); - return final; -} - -void automaton_delete(automaton a) -{ - int i; - free(a->accept); - for(i=0; i <= a->nstates; i++) - free(a->trans[i]); - free(a->trans); - free(a); -} - -inline int automaton_get_nstates(automaton a) -{ - return a->nstates; -} - -inline int automaton_get_init(automaton a) -{ - return a->init; -} - -inline int automaton_get_accept(automaton a, int state) -{ - return a->accept[state]; -} - -inline int automaton_get_next_state(automaton a, int state, char l) -{ - return a->trans[state][(int)l]; -} - -void automaton_dump(automaton a, char* filename) -{ - int i,l; - FILE* f; -#ifdef HAVE_SYS_WAIT_H - pid_t pid; -#endif - - if (a == NULL) - return ; - f=fopen(filename,"w"); - fprintf(f,"digraph automaton {\n"); - for(i=1; i<=a->nstates; i++) - { - fprintf(f,"\t%d [label = \"%d\"",i,i); - if (i == a->init) - fprintf(f,", style = filled, color=lightgrey"); - if (a->accept[i]) - fprintf(f,", shape = doublecircle"); - fprintf(f,"];\n"); - } - fprintf(f,"\n"); - for(i=1; i<=a->nstates; i++) - for(l=0; l < MAX_TRANSITION_LETTERS; l++) - if (a->trans[i][l]) - { - fprintf(f,"\t%d -> %d [label = \"",i,a->trans[i][l]); - regexp_print_letter(f,l); - fprintf(f,"\"];\n"); - } - fprintf(f,"fontsize=20;\n"); - fprintf(f,"}\n"); - fclose(f); - -#ifdef HAVE_SYS_WAIT_H - pid = fork (); - if (pid > 0) { - wait(NULL); - } else if (pid == 0) { - execlp("dotty","dotty",filename,NULL); - printf("exec dotty failed\n"); - exit(1); - } -#endif -} - -/* ************************************************** * - * ************************************************** * - * ************************************************** */ - -void state_delete_fun(void* ps) -{ - astate s = ps; - alist_delete(s->id); - free(s); -} - -static Automaton s_automaton_create() -{ - Automaton a; - a = (Automaton)malloc(sizeof(struct Automaton_t)); - a->nstates = 0; - a->init_state = NULL; - a->states = alist_create(); - alist_set_delete(a->states,state_delete_fun); - return a; -} - - -static void s_automaton_delete(Automaton a) -{ - alist_delete(a->states); - free(a); -} - -static alist s_automaton_id_create(int id) -{ - alist a = alist_create(); - alist_add(a,(void*)((unsigned long)id)); - return a; -} - -static astate s_automaton_state_create(alist id) -{ - astate s; - s = (astate)malloc(sizeof(struct automaton_state_t)); - s->id = id; - s->accept = 0; - memset(s->next,0,sizeof(astate)*MAX_TRANSITION_LETTERS); - DMSG(printf("** state %s creation\n",s_automaton_id_to_str(id))); - return s; -} - -static void s_automaton_add_state(Automaton a, astate s) -{ - a->nstates ++; - alist_add(a->states,(void*)s); - DMSG(printf("** state %s added to automaton\n",s_automaton_id_to_str(s->id))); -} - -static astate s_automaton_get_state(Automaton a, alist id) -{ - astate s; - alist_elt ptr; - for(ptr = alist_get_first(a->states) ; ptr ; ptr = alist_get_next(a->states,ptr)) - { - s = alist_elt_get_value(ptr); - if (alist_equal(s->id,id)) - { - //DMSG(printf("** get state %s ok\n",s_automaton_id_to_str(s->id))); - return s; - } - } - return NULL; -} - -/* ************************************************** * - * ************************************************** * - * ************************************************** */ - -Automaton s_automaton_PS_to_NFA(int init_state_id, int *ptl, int *PS) -{ - int p; - int maxpos = PS[0]; - Automaton nfa = NULL; - alist temp_id; - alist_elt ptr; - astate temp_state,current_state; - alist L; - char used_letter[MAX_TRANSITION_LETTERS]; - - nfa = s_automaton_create(); - L = alist_create(); - - /* 1: init_state = root->PP */ - temp_id = s_automaton_id_create(init_state_id); - temp_state = s_automaton_state_create(temp_id); - nfa->init_state = temp_state; - s_automaton_add_state(nfa,temp_state); - alist_add(L,temp_state); - /* 2: while \exist state \in state_list */ - while (! alist_is_empty(L)) - { - current_state = (astate)alist_pop_first_value(L); - DMSG(printf("** current state = %s\n",s_automaton_id_to_str(current_state->id))); - memset(used_letter,0,sizeof(used_letter)); - /* 3: \foreach l in \sigma | l \neq # */ - for(p=1; p < maxpos; p++) - { - int current_letter = ptl[p]; - if (used_letter[current_letter] == 0) - { - /* 4: int set = \cup { PS(pos) | pos \in state \wedge pos == l } */ - int pos, ens = 0; - for(pos = 1; pos <= maxpos; pos++) - { - if (ptl[pos] == current_letter && - (unsigned long)alist_elt_get_value(alist_get_first(current_state->id)) & (1 << (pos - 1))) - ens |= PS[pos]; - } - /* 5: transition from current_state to temp_state */ - if (ens) - { - temp_id = s_automaton_id_create(ens); - temp_state = s_automaton_get_state(nfa,temp_id); - if (temp_state == NULL) - { - temp_state = s_automaton_state_create(temp_id); - s_automaton_add_state (nfa,temp_state); - current_state->next[current_letter] = temp_state; - alist_add(L,temp_state); - } - else - { - alist_delete(temp_id); - current_state->next[current_letter] = temp_state; - } - } - used_letter[current_letter] = 1; - } - } - } - - alist_delete(L); - - for(ptr = alist_get_first(nfa->states); ptr ; ptr = alist_get_next(nfa->states,ptr)) - { - astate s = (astate)alist_elt_get_value(ptr); - if ((unsigned long)alist_elt_get_value(alist_get_first(s->id)) & (1 << (maxpos - 1))) - s->accept = 1; - } - - return nfa; -} - -/* ************************************************** * - * ************************************************** * - * ************************************************** */ - -static alist s_automaton_successor(alist S, int letter, Automaton nfa, struct search_RegE_list_t *list) -{ - alist R,r; - alist_elt ptr; - R = alist_create(); /* R = \empty */ - /* \forall y \in S */ - for(ptr = alist_get_first(S); ptr ; ptr = alist_get_next(S,ptr)) - { - int i; - alist t, Ry; astate y,z; - - i = (unsigned long)alist_elt_get_value(ptr); - t = s_automaton_id_create(i); - assert(y = s_automaton_get_state(nfa,t)); - alist_delete(t); - - Ry = alist_create(); /* Ry = \empty */ - - if ((z = y->next[letter]) != NULL) /* \delta (y,z) = l */ - { - r = s_automaton_successor(z->id,RE_EPSILON,nfa, list); - alist_insert(Ry,r); - alist_delete(r); - alist_insert(Ry,z->id); /* Ry = Ry \cup succ(z) */ - } - - /* \epsilon transition from start node */ - if ((z = y->next[RE_EPSILON]) != NULL) /* \delta (y,z) = \epsilon */ - { - r = s_automaton_successor(z->id,letter,nfa, list); - alist_insert(Ry,r); /* Ry = Ry \cup succ(z) */ - alist_delete(r); - } - - if (letter < RE_FINAL_TOK) - { - for(i = 0 ; i < DIC_SEARCH_REGE_LIST ; i++) - if (list->valid[i]) - { - if (list->letters[i][letter] && (z = y->next[(int)list->symbl[i]]) != NULL) - { - DMSG(printf("*** letter ")); - DMSG(regexp_print_letter(stdout,letter)); - DMSG(printf("is in ")); - DMSG(regexp_print_letter(stdout,i)); - - r = s_automaton_successor(z->id,RE_EPSILON,nfa, list); - alist_insert(Ry,r); - alist_delete(r); - alist_insert(Ry,z->id); - } - } - } - -#if 0 - if (alist_is_empty(Ry)) /* Ry = \empty */ - return Ry; -#endif - - alist_insert(R,Ry); /* R = R \cup Ry */ - alist_delete(Ry); - } - - return R; -} - -static void s_automaton_node_set_accept(astate s, Automaton nfa) -{ - void* idx; - alist_elt ptr; - - DMSG(printf("=== setting accept for node (%s) :",s_automaton_id_to_str(s->id))); - for(ptr = alist_get_first(nfa->states) ; ptr ; ptr = alist_get_next(nfa->states,ptr)) - { - astate ns = (astate)alist_elt_get_value(ptr); - idx = alist_elt_get_value(alist_get_first(ns->id)); - DMSG(printf("%s ",s_automaton_id_to_str(ns->id))); - if (ns->accept && alist_is_in(s->id,idx)) - { - DMSG(printf("(ok) ")); - s->accept = 1; - } - } - DMSG(printf("\n")); -} - -static Automaton s_automaton_NFA_to_DFA(Automaton nfa, struct search_RegE_list_t *list) -{ - Automaton dfa = NULL; - alist temp_id; - alist_elt ptr; - astate temp_state, current_state; - alist L; - int letter; - - dfa = s_automaton_create(); - L = alist_create(); - - temp_id = alist_clone(nfa->init_state->id); - temp_state = s_automaton_state_create(temp_id); - dfa->init_state = temp_state; - s_automaton_add_state(dfa,temp_state); - alist_add(L,temp_state); - while (! alist_is_empty(L)) - { - current_state = (astate)alist_pop_first_value(L); - DMSG(printf("** current state = %s\n",s_automaton_id_to_str(current_state->id))); - for(letter = 1; letter < DIC_LETTERS; letter++) - { - // DMSG(printf("*** start successor of %s\n",s_automaton_id_to_str(current_state->id))); - - temp_id = s_automaton_successor(current_state->id,letter,nfa,list); - - if (! alist_is_empty(temp_id)) - { - - DMSG(printf("*** successor of %s for ",s_automaton_id_to_str(current_state->id))); - DMSG(regexp_print_letter(stdout,letter)); - DMSG(printf(" = %s\n", s_automaton_id_to_str(temp_id))); - - temp_state = s_automaton_get_state(dfa,temp_id); - - // DMSG(printf("*** automaton get state -%s- ok\n",s_automaton_id_to_str(temp_id))); - - if (temp_state == NULL) - { - temp_state = s_automaton_state_create(temp_id); - s_automaton_add_state(dfa,temp_state); - current_state->next[letter] = temp_state; - alist_add(L,temp_state); - } - else - { - alist_delete(temp_id); - current_state->next[letter] = temp_state; - } - } - else - { - alist_delete(temp_id); - } - } - } - - for(ptr = alist_get_first(dfa->states) ; ptr ; ptr = alist_get_next(dfa->states,ptr)) - { - astate s = (astate)alist_elt_get_value(ptr); - s_automaton_node_set_accept(s,nfa); - } - - alist_delete(L); - return dfa; -} - -/* ************************************************** * - * ************************************************** * - * ************************************************** */ - -static automaton s_automaton_finalize(Automaton a) -{ - int i,l; - automaton fa = NULL; - alist_elt ptr; - astate s; - - if (a == NULL) - return NULL; - - /* creation */ - fa = (automaton)malloc(sizeof(struct automaton_t)); - fa->nstates = a->nstates; - fa->accept = (int*) malloc((fa->nstates + 1)*sizeof(int)); - memset(fa->accept,0,(fa->nstates + 1)*sizeof(int)); - fa->trans = (int**)malloc((fa->nstates + 1)*sizeof(int*)); - for(i=0; i <= fa->nstates; i++) - { - fa->trans[i] = (int*)malloc(MAX_TRANSITION_LETTERS * sizeof(int)); - memset(fa->trans[i],0,MAX_TRANSITION_LETTERS * sizeof(int)); - } - - /* create new id for states */ - for(i = 1 , ptr = alist_get_first(a->states); ptr ; ptr = alist_get_next(a->states,ptr), i++) - { - s = (astate)alist_elt_get_value(ptr); - s->id_static = i; - } - - /* build new automaton */ - for(ptr = alist_get_first(a->states); ptr ; ptr = alist_get_next(a->states,ptr)) - { - s = (astate)alist_elt_get_value(ptr); - i = s->id_static; - - if (s == a->init_state) - fa->init = i; - if (s->accept == 1) - fa->accept[i] = 1; - - for(l=0; l < MAX_TRANSITION_LETTERS; l++) - if (s->next[l]) - fa->trans[i][l] = s->next[l]->id_static; - } - - return fa; -} - - -/* ************************************************** * - * ************************************************** * - * ************************************************** */ - -#ifdef DEBUG_AUTOMATON -static char* s_automaton_id_to_str(alist id) -{ - static char s[250]; - memset(s,0,sizeof(s)); - alist_elt ptr; - for(ptr = alist_get_first(id); ptr ; ptr = alist_get_next(id,ptr)) - { - char tmp[50]; - sprintf(tmp,"%d ",(int)alist_elt_get_value(ptr)); - strcat(s,tmp); - } - return s; -} -#endif // DEBUG_AUTOMATON - -#ifdef DEBUG_AUTOMATON -static void s_automaton_print_nodes(FILE* f, Automaton a) -{ - char * sid; - astate s; - alist_elt ptr; - for(ptr = alist_get_first(a->states) ; ptr != NULL ; ptr = alist_get_next(a->states,ptr)) - { - s = alist_elt_get_value(ptr); - sid = s_automaton_id_to_str(s->id); - fprintf(f,"\t\"%s\" [label = \"%s\"",sid,sid); - if (s == a->init_state) - { - fprintf(f,", style = filled, color=lightgrey"); - } - if (s->accept) - { - fprintf(f,", shape = doublecircle"); - } - fprintf(f,"];\n"); - } - fprintf(f,"\n"); -} -#endif // DEBUG_AUTOMATON - -#ifdef DEBUG_AUTOMATON -static void s_automaton_print_edges(FILE* f, Automaton a) -{ - int letter; - char * sid; - astate s; - alist_elt ptr; - for(ptr = alist_get_first(a->states) ; ptr != NULL ; ptr = alist_get_next(a->states,ptr)) - { - s = (astate)alist_elt_get_value(ptr); - for(letter=0; letter < 255; letter++) - { - if (s->next[letter]) - { - sid = s_automaton_id_to_str(s->id); - fprintf(f,"\t\"%s\" -> ",sid); - sid = s_automaton_id_to_str(s->next[letter]->id); - fprintf(f,"\"%s\" [label = \"",sid); - regexp_print_letter(f,letter); - fprintf(f,"\"];\n"); - } - } - } -} -#endif // DEBUG_AUTOMATON - -#ifdef DEBUG_AUTOMATON -static void s_automaton_dump(Automaton a, char* filename) -{ - FILE* f; -#ifdef HAVE_SYS_WAIT_H - pid_t pid; -#endif - if (a == NULL) - return; - f=fopen(filename,"w"); - fprintf(f,"digraph automaton {\n"); - s_automaton_print_nodes(f,a); - s_automaton_print_edges(f,a); - fprintf(f,"fontsize=20;\n"); - fprintf(f,"}\n"); - fclose(f); - -#ifdef HAVE_SYS_WAIT_H - pid = fork (); - if (pid > 0) { - wait(NULL); - } else if (pid == 0) { - execlp("dotty","dotty",filename,NULL); - printf("exec dotty failed\n"); - exit(1); - } -#endif -} -#endif // DEBUG_AUTOMATON - -/* ************************************************** * - * ************************************************** * - * ************************************************** */ - diff --git a/dic/automaton.cpp b/dic/automaton.cpp new file mode 100644 index 0000000..f21ab5a --- /dev/null +++ b/dic/automaton.cpp @@ -0,0 +1,603 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file automaton.c + * \brief (Non)Deterministic Finite AutomatonHelper for Regexp + * \author Antoine Fraboulet + * \date 2005 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#include + +#include "dic.h" +#include "regexp.h" +#include "automaton.h" + +using namespace std; + +#ifdef DEBUG_AUTOMATON +# define DMSG(a) (a) +#else +# define DMSG(a) +#endif + +#define MAX_TRANSITION_LETTERS 256 + +typedef struct automaton_state_t *astate; + + +/* ************************************************** * + Helper class, allowing to build a NFA, then a DFA + * ************************************************** */ + +class AutomatonHelper +{ +public: + AutomatonHelper(astate iInitState); + ~AutomatonHelper(); + + astate getInitState() const { return m_initState; } +#ifdef DEBUG_AUTOMATON + void dump(const string &iFileName) const; +#endif + + static AutomatonHelper *ps2nfa(int iInitState, int *ptl, int *PS); + static AutomatonHelper *nfa2dfa(const AutomatonHelper &iNfa, + struct search_RegE_list_t *iList); + + /// List of states + list m_states; + +private: + /// Initial state of the automaton + astate m_initState; + + void addState(astate s); + astate getState(const set &iId) const; + void printNodes(FILE* f) const; + void printEdges(FILE* f) const; + void setAccept(astate s) const; + set getSuccessor(const set &S, int letter, struct search_RegE_list_t *iList) const; +}; + + +/* ************************************************** * + State handling + * ************************************************** */ + +static set s_state_id_create(int id); +static string s_state_id_to_str(const set &iId); +static astate s_state_create (const set &iId); + +struct automaton_state_t +{ + set id; + int accept; + int id_static; + astate next[MAX_TRANSITION_LETTERS]; +}; + + +/* ************************************************** * + Definition of the Automaton class + * ************************************************** */ + +Automaton::Automaton(int iInitState, int *ptl, int *PS, struct search_RegE_list_t *iList) +{ + AutomatonHelper *nfa = AutomatonHelper::ps2nfa(iInitState, ptl, PS); + DMSG(printf("\n non deterministic automaton OK \n\n")); + DMSG(nfa->dump("auto_nfa")); + + AutomatonHelper *dfa = AutomatonHelper::nfa2dfa(*nfa, iList); + DMSG(printf("\n deterministic automaton OK \n\n")); + DMSG(dfa->dump("auto_dfa")); + + finalize(*dfa); + DMSG(printf("\n final automaton OK \n\n")); + DMSG(automaton_dump("auto_fin")); + + delete nfa; + delete dfa; +} + + +Automaton::~Automaton() +{ + delete[] m_acceptors; + for (int i = 0; i <= m_nbStates; i++) + { + delete[] m_transitions[i]; + } + delete[] m_transitions; +} + + +void Automaton::finalize(const AutomatonHelper &iHelper) +{ + /* Creation */ + m_nbStates = iHelper.m_states.size(); + m_acceptors = new bool[m_nbStates + 1]; + memset(m_acceptors, 0, (m_nbStates + 1) * sizeof(bool)); + m_transitions = new int*[m_nbStates + 1]; + for (int i = 0; i <= m_nbStates; i++) + { + m_transitions[i] = new int[MAX_TRANSITION_LETTERS]; + memset(m_transitions[i], 0, MAX_TRANSITION_LETTERS * sizeof(int)); + } + + /* Create new id for states */ + list::const_iterator it; + int i; + for (i = 1, it = iHelper.m_states.begin(); + it != iHelper.m_states.end(); it++, i++) + { + (*it)->id_static = i; + } + + /* Build new automaton */ + for (it = iHelper.m_states.begin(); it != iHelper.m_states.end(); it++) + { + astate s = *it; + int i = s->id_static; + + if (s == iHelper.getInitState()) + m_init = i; + if (s->accept == 1) + m_acceptors[i] = true; + + for (int l = 0; l < MAX_TRANSITION_LETTERS; l++) + { + if (s->next[l]) + m_transitions[i][l] = s->next[l]->id_static; + } + } +} + + +void Automaton::dump(const string &iFileName) const +{ + FILE *f = fopen(iFileName.c_str(), "w"); + fprintf(f, "digraph automaton {\n"); + for (int i = 1; i <= m_nbStates; i++) + { + fprintf(f, "\t%d [label = \"%d\"", i, i); + if (i == m_init) + fprintf(f, ", style = filled, color=lightgrey"); + if (accept(i)) + fprintf(f, ", shape = doublecircle"); + fprintf(f, "];\n"); + } + fprintf(f, "\n"); + for (int i = 1; i <= m_nbStates; i++) + { + for (int l = 0; l < MAX_TRANSITION_LETTERS; l++) + { + if (m_transitions[i][l]) + { + fprintf(f, "\t%d -> %d [label = \"", i, m_transitions[i][l]); + regexp_print_letter(f, l); + fprintf(f, "\"];\n"); + } + } + } + fprintf(f, "fontsize=20;\n"); + fprintf(f, "}\n"); + fclose(f); + +#ifdef HAVE_SYS_WAIT_H + pid_t pid = fork (); + if (pid > 0) + { + wait(NULL); + } + else if (pid == 0) + { + execlp("dotty", "dotty", iFileName.c_str(), NULL); + printf("exec dotty failed\n"); + exit(1); + } +#endif +} + + +/* ************************************************** * + Definition of the state handling methods + * ************************************************** */ + +static set s_state_id_create(int id) +{ + set l; + l.insert(id); + return l; +} + + +static string s_state_id_to_str(const set &iId) +{ + string s; + set::const_iterator it; + for (it = iId.begin(); it != iId.end(); it++) + { + char tmp[50]; + sprintf(tmp, "%d ", *it); + s += tmp; + } + return s; +} + + +static astate s_state_create(const set &iId) +{ + astate s = new automaton_state_t(); + // TODO: use copy constructor + s->id = iId; + s->accept = 0; + memset(s->next, 0, sizeof(astate)*MAX_TRANSITION_LETTERS); + DMSG(printf("** state %s creation\n", s_state_id_to_str(iId).c_str())); + return s; +} + + +/* ************************************************** * + Definition of the AutomatonHelper class + * ************************************************** */ + +AutomatonHelper::AutomatonHelper(astate iInitState) + : m_initState(iInitState) +{ +} + + +AutomatonHelper::~AutomatonHelper() +{ + list::const_iterator it; + for (it = m_states.begin(); it != m_states.end(); it++) + { + delete *it; + } +} + + +void AutomatonHelper::addState(astate s) +{ + m_states.push_front(s); + DMSG(printf("** state %s added to automaton\n", s_state_id_to_str(s->id).c_str())); +} + + +astate AutomatonHelper::getState(const set &iId) const +{ + list::const_iterator it; + for (it = m_states.begin(); it != m_states.end(); it++) + { + astate s = *it; + if (s->id == iId) + { + //DMSG(printf("** get state %s ok\n", s_state_id_to_str(s->id).c_str())); + return s; + } + } + return NULL; +} + +/* ************************************************** * + * ************************************************** * + * ************************************************** */ + +AutomatonHelper *AutomatonHelper::ps2nfa(int init_state_id, int *ptl, int *PS) +{ + int maxpos = PS[0]; + astate current_state; + char used_letter[MAX_TRANSITION_LETTERS]; + + + /* 1: init_state = root->PP */ + set temp_id0 = s_state_id_create(init_state_id); + astate temp_state = s_state_create(temp_id0); + AutomatonHelper *nfa = new AutomatonHelper(temp_state); + nfa->addState(temp_state); + list L; + L.push_front(temp_state); + /* 2: while \exist state \in state_list */ + while (! L.empty()) + { + current_state = L.front(); + L.pop_front(); + DMSG(printf("** current state = %s\n", s_state_id_to_str(current_state->id).c_str())); + memset(used_letter, 0, sizeof(used_letter)); + /* 3: \foreach l in \sigma | l \neq # */ + for (int p = 1; p < maxpos; p++) + { + int current_letter = ptl[p]; + if (used_letter[current_letter] == 0) + { + /* 4: int set = \cup { PS(pos) | pos \in state \wedge pos == l } */ + int ens = 0; + for (int pos = 1; pos <= maxpos; pos++) + { + if (ptl[pos] == current_letter && + (unsigned int)*(current_state->id.begin()) & (1 << (pos - 1))) + ens |= PS[pos]; + } + /* 5: transition from current_state to temp_state */ + if (ens) + { + set temp_id = s_state_id_create(ens); + temp_state = nfa->getState(temp_id); + if (temp_state == NULL) + { + temp_state = s_state_create(temp_id); + nfa->addState(temp_state); + current_state->next[current_letter] = temp_state; + L.push_front(temp_state); + } + else + { + current_state->next[current_letter] = temp_state; + } + } + used_letter[current_letter] = 1; + } + } + } + + list::const_iterator it; + for (it = nfa->m_states.begin(); it != nfa->m_states.end(); it++) + { + astate s = *it; + if (*(s->id.begin()) & (1 << (maxpos - 1))) + s->accept = 1; + } + + return nfa; +} + +/* ************************************************** * + * ************************************************** * + * ************************************************** */ + +set AutomatonHelper::getSuccessor(const set &S, + int letter, + struct search_RegE_list_t *iList) const +{ + set R, r; + set::const_iterator it; + for (it = S.begin(); it != S.end(); it++) /* \forall y \in S */ + { + astate y, z; + + set t = s_state_id_create(*it); + assert(y = getState(t)); + + set Ry; /* Ry = \empty */ + + if ((z = y->next[letter]) != NULL) /* \delta (y,z) = l */ + { + r = getSuccessor(z->id, RE_EPSILON, iList); + Ry.insert(r.begin(), r.end()); + Ry.insert(z->id.begin(), z->id.end()); /* Ry = Ry \cup succ(z) */ + } + + /* \epsilon transition from start node */ + if ((z = y->next[RE_EPSILON]) != NULL) /* \delta (y,z) = \epsilon */ + { + r = getSuccessor(z->id, letter, iList); + Ry.insert(r.begin(), r.end()); /* Ry = Ry \cup succ(z) */ + } + + if (letter < RE_FINAL_TOK) + { + for (int i = 0; i < DIC_SEARCH_REGE_LIST; i++) + { + if (iList->valid[i]) + { + if (iList->letters[i][letter] && (z = y->next[(int)iList->symbl[i]]) != NULL) + { + DMSG(printf("*** letter ")); + DMSG(regexp_print_letter(stdout, letter)); + DMSG(printf("is in ")); + DMSG(regexp_print_letter(stdout, i)); + + r = getSuccessor(z->id, RE_EPSILON, iList); + Ry.insert(r.begin(), r.end()); + Ry.insert(z->id.begin(), z->id.end()); + } + } + } + } + +#if 0 + if (alist_is_empty(Ry)) /* Ry = \empty */ + return Ry; +#endif + + R.insert(Ry.begin(), Ry.end()); /* R = R \cup Ry */ + } + + return R; +} + + +void AutomatonHelper::setAccept(astate s) const +{ + DMSG(printf("=== setting accept for node (%s) :", s_state_id_to_str(s->id).c_str())); + list::const_iterator it; + for (it = m_states.begin(); it != m_states.end(); it++) + { + astate ns = *it; + int idx = *(ns->id.begin()); + DMSG(printf("%s ", s_state_id_to_str(ns->id).c_str())); + if (ns->accept && (find(s->id.begin(), s->id.end(), idx) != s->id.end())) + { + DMSG(printf("(ok) ")); + s->accept = 1; + } + } + DMSG(printf("\n")); +} + + +AutomatonHelper *AutomatonHelper::nfa2dfa(const AutomatonHelper &iNfa, + struct search_RegE_list_t *iList) +{ + astate current_state; + + list L; + + // Clone the list + set temp_id0 = iNfa.m_initState->id; + astate temp_state = s_state_create(temp_id0); + AutomatonHelper *dfa = new AutomatonHelper(temp_state); + dfa->addState(temp_state); + L.push_front(temp_state); + while (! L.empty()) + { + current_state = L.front(); + L.pop_front(); + DMSG(printf("** current state = %s\n", s_state_id_to_str(current_state->id).c_str())); + for (int letter = 1; letter < DIC_LETTERS; letter++) + { + // DMSG(printf("*** start successor of %s\n", s_state_id_to_str(current_state->id).c_str())); + + set temp_id = iNfa.getSuccessor(current_state->id, letter, iList); + + if (! temp_id.empty()) + { + + DMSG(printf("*** successor of %s for ", s_state_id_to_str(current_state->id).c_str())); + DMSG(regexp_print_letter(stdout, letter)); + DMSG(printf(" = %s\n", s_state_id_to_str(temp_id).c_str())); + + temp_state = dfa->getState(temp_id); + + // DMSG(printf("*** automaton get state -%s- ok\n", s_state_id_to_str(temp_id).c_str())); + + if (temp_state == NULL) + { + temp_state = s_state_create(temp_id); + dfa->addState(temp_state); + current_state->next[letter] = temp_state; + L.push_front(temp_state); + } + else + { + current_state->next[letter] = temp_state; + } + } + } + } + + list::const_iterator it; + for (it = dfa->m_states.begin(); it != dfa->m_states.end(); it++) + { + iNfa.setAccept(*it); + } + + return dfa; +} + +/* ************************************************** * + * ************************************************** * + * ************************************************** */ + +void AutomatonHelper::printNodes(FILE* f) const +{ + list::const_iterator it; + for (it = m_states.begin(); it != m_states.end(); it++) + { + astate s = *it; + string sid = s_state_id_to_str(s->id); + fprintf(f, "\t\"%s\" [label = \"%s\"", sid.c_str(), sid.c_str()); + if (s == m_initState) + { + fprintf(f, ", style = filled, color=lightgrey"); + } + if (s->accept) + { + fprintf(f, ", shape = doublecircle"); + } + fprintf(f, "];\n"); + } + fprintf(f, "\n"); +} + + +void AutomatonHelper::printEdges(FILE* f) const +{ + list::const_iterator it; + for (it = m_states.begin(); it != m_states.end(); it++) + { + astate s = *it; + for (int letter = 0; letter < 255; letter++) + { + if (s->next[letter]) + { + string sid = s_state_id_to_str(s->id); + fprintf(f, "\t\"%s\" -> ", sid.c_str()); + sid = s_state_id_to_str(s->next[letter]->id); + fprintf(f, "\"%s\" [label = \"", sid.c_str()); + regexp_print_letter(f, letter); + fprintf(f, "\"];\n"); + } + } + } +} + + +#ifdef DEBUG_AUTOMATON +void AutomatonHelper::dump(const string &iFileName) const +{ + FILE *f = fopen(iFileName.c_str(), "w"); + fprintf(f, "digraph automaton {\n"); + printNodes(f); + printEdges(f); + fprintf(f, "fontsize=20;\n"); + fprintf(f, "}\n"); + fclose(f); + +#ifdef HAVE_SYS_WAIT_H + pid_t pid = fork(); + if (pid > 0) + { + wait(NULL); + } + else if (pid == 0) + { + execlp("dotty", "dotty", iFileName.c_str(), NULL); + printf("exec dotty failed\n"); + exit(1); + } +#endif +} +#endif + diff --git a/dic/automaton.h b/dic/automaton.h index 1986a42..f186ec7 100644 --- a/dic/automaton.h +++ b/dic/automaton.h @@ -1,21 +1,22 @@ -/* Eliot */ -/* Copyright (C) 2005 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file automaton.h @@ -26,51 +27,68 @@ #ifndef _DIC_AUTOMATON_H_ #define _DIC_AUTOMATON_H_ -#if defined(__cplusplus) -extern "C" - { -#endif -typedef struct automaton_t *automaton; +class AutomatonHelper; +class Automaton +{ +public: + /// Constructor /** - * build a static deterministic finite automaton from + * Build a static deterministic finite automaton from * "init_state", "ptl" and "PS" given by the parser */ -automaton automaton_build(int init_state, int *ptl, int *PS, struct search_RegE_list_t *list); + Automaton(int init_state, int *ptl, int *PS, struct search_RegE_list_t *iList); + + /// Destructor + ~Automaton(); /** - * automaton delete function - */ -void automaton_delete (automaton a); - - /** - * get the number of states in the automaton + * Get the number of states in the automaton. * @returns number of states */ -int automaton_get_nstate (automaton a); + int getNbStates() const { return m_nbStates; } /** - * query the id of the init state + * Query the id of the init state. * @returns init state id */ -int automaton_get_init (automaton a); + int getInitId() const { return m_init; } /** - * ask for the acceptor flag for the state - * @returns boolean flag 0 or 1 + * Query the acceptor flag for the given state + * @return true/false */ -int automaton_get_accept (automaton a, int state); + bool accept(int state) const { return m_acceptors[state]; } /** - * returns the next state when the transition is taken + * Return the next state when the transition is taken * @returns next state id (1 <= id <= nstate, 0 = invalid id) */ -int automaton_get_next_state (automaton a, int start, char l); + int getNextState(int start, char l) const + { + return m_transitions[start][(int)l]; + } -void automaton_dump (automaton a, char* filename); + /** + * Dump the automaton into a file (for debugging purposes) + */ + void dump(const string &iFileName) const; + +private: + /// Number of states + int m_nbStates; + + /// ID of the init state + int m_init; + + /// Array of booleans, one for each state + bool *m_acceptors; + + /// Matrix of transitions + int **m_transitions; + + void finalize(const AutomatonHelper &a); +}; -#if defined(__cplusplus) - } -#endif #endif /* _DIC_AUTOMATON_H_ */ diff --git a/dic/compdic.c b/dic/compdic.c deleted file mode 100644 index fb043f7..0000000 --- a/dic/compdic.c +++ /dev/null @@ -1,350 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file compdic.c - * \brief Program used to compress a dictionary - * \author Antoine Fraboulet - * \date 1999 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hashtable.h" -#include "dic_internals.h" -#include "dic.h" - -//#define DEBUG_LIST -//#define DEBUG_OUTPUT -//#define DEBUG_OUTPUT_L2 -#define CHECK_RECURSION - -char* -load_uncompressed(const char* file_name, unsigned int *dic_size) -{ - unsigned r; - char *uncompressed; - FILE* file_desc; - - if ((file_desc = fopen (file_name, "r")) == NULL) - return NULL; - - if ((uncompressed = (char*)malloc (sizeof(char)*(*dic_size))) == NULL) - return NULL; - - r = fread (uncompressed, 1, *dic_size, file_desc); - if (r < *dic_size) - { - /* \n is 2 chars under MS OS */ - printf("\n"); - printf("** The number of bytes read is less than the size of the file **\n"); - printf("** this may be OK if you run a Microsoft OS but not on Unix **\n"); - printf("** please check the results. **\n"); - printf("\n"); - *dic_size = r; - } - - fclose(file_desc); - return uncompressed; -} - - -int -file_length(const char* file_name) -{ - struct stat stat_buf; - if (stat (file_name, &stat_buf) < 0) - return - 1; - return (int) stat_buf.st_size; -} - - -void -skip_init_header(FILE* outfile, Dict_header *header) -{ - header->unused_1 = 0; - header->unused_2 = 0; - header->root = 0; - header->nwords = 0; - header->nodesused = 1; - header->edgesused = 1; - header->nodessaved = 0; - header->edgessaved = 0; - - fwrite (header, sizeof(Dict_header), 1, outfile); -} - - -void -fix_header(FILE* outfile, Dict_header* header) -{ - strcpy(header->ident,_COMPIL_KEYWORD_); - header->root = header->edgesused; - rewind (outfile); -#if defined(WORDS_BIGENDIAN) - #warning "**********************************************" - #warning "compdic does not run yet on bigendian machines" - #warning "**********************************************" -#else - fwrite (header, sizeof(Dict_header), 1, outfile); -#endif -} - - -void -print_header_info(Dict_header *header) -{ - printf("============================\n"); - printf("keyword length %lu bytes\n", (unsigned long)strlen(_COMPIL_KEYWORD_)); - printf("keyword size %lu bytes\n", (unsigned long)sizeof(_COMPIL_KEYWORD_)); - printf("header size %lu bytes\n", (unsigned long)sizeof(Dict_header)); - printf("\n"); - printf("%d words\n",header->nwords); - printf("\n"); - printf("root : %7d (edge)\n",header->root); - printf("root : %7lu (byte)\n",(unsigned long) header->root * sizeof(Dawg_edge)); - printf("\n"); - printf("nodes : %d+%d\n",header->nodesused, header->nodessaved); - printf("edges : %d+%d\n",header->edgesused, header->edgessaved); - printf("============================\n"); -} - - -void -write_node(Dawg_edge *edges, int size, int num, FILE* outfile) -{ -#ifdef DEBUG_OUTPUT - int i; - printf("writing %d edges\n",num); - for(i=0; i max_rec) - max_rec = current_rec; -#endif - - while (prefix == global_endstring) - { - /* More edges out of node */ - edgeptr->ptr = 0; - edgeptr->term = 0; - edgeptr->last = 0; - edgeptr->fill = 0; - edgeptr->chr = 0; - - (*(edgeptr++)).chr = (*global_endstring++ = *global_input++) & DIC_CHAR_MASK; - if (*global_input == '\n') /* End of a word */ - { - global_header.nwords++; - edgeptr[-1].term = 1; /* Mark edge as word */ - *global_endstring++ = *global_input++; /* Skip \n */ - if (global_input == global_endofinput) /* At end of input? */ - break; - - global_endstring = global_stringbuf; - while(*global_endstring == *global_input) - { - global_endstring++; - global_input++; - } - } - /* make dawg pointed to by this edge */ - edgeptr[-1].ptr = makenode(prefix + 1); - } - - numedges = edgeptr - edges; - if (numedges == 0) - { -#ifdef CHECK_RECURSION - current_rec --; -#endif - return 0; /* Special node zero - no edges */ - } - - edgeptr[-1].last = 1; /* Mark the last edge */ - - saved_position = (unsigned int*) hash_find (global_hashtable, - (void*)edges, - numedges*sizeof(Dawg_edge)); - if (saved_position) - { - global_header.edgessaved += numedges; - global_header.nodessaved++; - -#ifdef CHECK_RECURSION - current_rec --; -#endif - return *saved_position; - } - else - { - unsigned int node_pos; - - node_pos = global_header.edgesused; - hash_add(global_hashtable, - (void*)edges,numedges*sizeof(Dawg_edge), - (void*)(&global_header.edgesused),sizeof(global_header.edgesused)); - global_header.edgesused += numedges; - global_header.nodesused++; - write_node (edges, sizeof(Dawg_edge), numedges, global_outfile); - -#ifdef CHECK_RECURSION - current_rec --; -#endif - return node_pos; - } -} - - - - -int -main(int argc, char* argv[]) -{ - unsigned int dicsize; - char *uncompressed; - Dawg_edge rootnode = {0,0,0,0,0}; - Dawg_edge specialnode = {0,0,0,0,0}; - - int size; - char* outfilename; - char outfilenamedefault[] = "dict.daw"; - clock_t starttime, endtime; - - if (argc < 2) - { - fprintf(stderr,"usage: %s uncompressed_dic [compressed_dic]\n",argv[0]); - exit(1); - } - - size = file_length (argv[1]); - if (size < 0) - { - fprintf(stderr,"Cannot stat uncompressed dictionary %s\n",argv[1]); - exit(1); - } - - dicsize = size; - outfilename = (argc == 3) ? argv[2] : outfilenamedefault; - - if ((global_outfile = fopen (outfilename,"wb")) == NULL) - { - fprintf(stderr,"Cannot open output file %s\n",outfilename); - exit(1); - } - - if ((uncompressed = load_uncompressed(argv[1], &dicsize)) == NULL) - { - fprintf(stderr,"Cannot load uncompressed dictionary into memory\n"); - exit(1); - } - - global_input = uncompressed; - global_endofinput = global_input + dicsize; - -#define SCALE 0.6 - global_hashtable = hash_init((unsigned int)(dicsize * SCALE)); -#undef SCALE - - skip_init_header(global_outfile,&global_header); - - specialnode.last = 1; - write_node(&specialnode,sizeof(specialnode),1,global_outfile); - /* - * Call makenode with null (relative to stringbuf) prefix; - * Initialize string to null; Put index of start node on output - */ - starttime=clock(); - rootnode.ptr = makenode(global_endstring = global_stringbuf); - endtime=clock(); - write_node(&rootnode,sizeof(rootnode),1,global_outfile); - - fix_header(global_outfile,&global_header); - - print_header_info(&global_header); - hash_destroy(global_hashtable); - free(uncompressed); - fclose(global_outfile); - - printf(" Elapsed time is : %f s\n", 1.0*(endtime-starttime) / CLOCKS_PER_SEC); -#ifdef CHECK_RECURSION - printf(" Maximum recursion level reached : %d\n",max_rec); -#endif - return 0; -} - - diff --git a/dic/compdic.cpp b/dic/compdic.cpp new file mode 100644 index 0000000..669c278 --- /dev/null +++ b/dic/compdic.cpp @@ -0,0 +1,563 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file compdic.c + * \brief Program used to compress a dictionary + * \author Antoine Fraboulet + * \date 1999 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// For ntohl & Co. +#ifdef WIN32 +# include +#else +# if HAVE_NETINET_IN_H +# include +# endif +# if HAVE_ARPA_INET_H +# include +# endif +#endif + +#if ENABLE_NLS +# include +# define _(String) gettext(String) +#else +# define _(String) String +#endif + +#include "hashtable.h" +#include "encoding.h" +#include "header.h" +#include "dic_internals.h" +#include "dic_exception.h" + +using namespace std; + +//#define DEBUG_LIST +//#define DEBUG_OUTPUT +//#define DEBUG_OUTPUT_L2 +#define CHECK_RECURSION + + +const wchar_t* load_uncompressed(const string &iFileName, unsigned int &ioDicSize) +{ + ifstream file(iFileName.c_str()); + if (!file.is_open()) + throw DicException("Could not open file " + iFileName); + + // Place the buffer in a vector to avoid worrying about memory handling + vector buffer(ioDicSize); + // Load the file data, everything in one shot + file.read(&buffer.front(), ioDicSize); + file.close(); + + // Buffer for the wide characters (it will use at most as many characters + // as the utf-8 version) + wchar_t *wideBuf = new wchar_t[ioDicSize]; + unsigned int number; + + try + { + number = readFromUTF8(wideBuf, ioDicSize, &buffer.front(), + ioDicSize, "load_uncompressed"); + ioDicSize = number; + return wideBuf; + } + catch (...) + { + // Avoid leaks, and propagate the exception + delete[] wideBuf; + throw; + } +} + + +void readLetters(const char *iFileName, DictHeaderInfo &ioHeaderInfo) +{ + ifstream in(iFileName); + if (!in.is_open()) + throw DicException("Could not open file " + string(iFileName)); + + // Use a more friendly type name + typedef boost::tokenizer > Tokenizer; + + int lineNb = 1; + string line; + while (getline(in, line)) + { + // Split the lines on space characters + vector tokens; + boost::char_separator sep(" "); + Tokenizer tok(line, sep); + Tokenizer::iterator it; + for (it = tok.begin(); it != tok.end(); ++it) + { + tokens.push_back(*it); + } + + // Ignore empty lines + if (tokens.empty()) + continue; + + // We expect 5 fields on the line, and the first one is a letter, so + // it cannot exceed 4 bytes + if (tokens.size() != 5 || tokens[0].size() > 4) + { + ostringstream ss; + ss << "readLetters: Invalid line in " << iFileName; + ss << " (line " << lineNb; + throw DicException(ss.str()); + } + +#define MAX_SIZE 4 + char buff[MAX_SIZE]; + strncpy(buff, tokens[0].c_str(), MAX_SIZE); + + wstring letter = readFromUTF8(buff, tokens[0].size(), "readLetters"); + + if (letter.size() != 1) + { + ostringstream ss; + ss << "readLetters: Invalid letter at line " << lineNb; + throw DicException(ss.str()); + } +#undef MAX_SIZE + + ioHeaderInfo.letters += towupper(letter[0]); + + ioHeaderInfo.points.push_back(atoi(tokens[1].c_str())); + ioHeaderInfo.frequency.push_back(atoi(tokens[2].c_str())); + ioHeaderInfo.vowels.push_back(atoi(tokens[3].c_str())); + ioHeaderInfo.consonants.push_back(atoi(tokens[4].c_str())); + + ++lineNb; + } +} + + +Header skip_init_header(ostream &outfile, DictHeaderInfo &ioHeaderInfo) +{ + ioHeaderInfo.root = 0; + ioHeaderInfo.nwords = 0; + ioHeaderInfo.nodesused = 1; + ioHeaderInfo.edgesused = 1; + ioHeaderInfo.nodessaved = 0; + ioHeaderInfo.edgessaved = 0; + + Header aHeader(ioHeaderInfo); + aHeader.write(outfile); + return aHeader; +} + + +void fix_header(ostream &outfile, DictHeaderInfo &ioHeaderInfo) +{ + ioHeaderInfo.root = ioHeaderInfo.edgesused; + // Go back to the beginning of the stream to overwrite the header + outfile.seekp(0, ios::beg); +#if defined(WORDS_BIGENDIAN) +#warning "**********************************************" +#warning "compdic does not run yet on bigendian machines" +#warning "**********************************************" +#else + Header aHeader(ioHeaderInfo); + aHeader.write(outfile); +#endif +} + + +// Change endianness of the pointes edges, and write them to the given ostream +void write_node(uint32_t *ioEdges, unsigned int num, ostream &outfile) +{ + // Handle endianness + for (unsigned int i = 0; i < num; ++i) + { + ioEdges[i] = htonl(ioEdges[i]); + } + +#ifdef DEBUG_OUTPUT + printf("writing %d edges\n", num); + for (int i = 0; i < num; i++) + { +#ifdef DEBUG_OUTPUT_L2 + printf("ptr=%2d t=%d l=%d chr=%2d (%c)\n", + ioEdges[i].ptr, ioEdges[i].term, ioEdges[i].last, + ioEdges[i].chr, ioEdges[i].chr -1 +'a'); +#endif + outfile.write((char*)(ioEdges + i), sizeof(DicEdge)); + } +#else + outfile.write((char*)ioEdges, num * sizeof(DicEdge)); +#endif +} + +#define MAX_STRING_LENGTH 200 + + +#define MAX_EDGES 2000 +/* ods3: ?? */ +/* ods4: 1746 */ + +// Hashing function for a vector of DicEdge, based on the hashing function +// of the HashTable +struct HashVector +{ + unsigned int operator()(const vector &iKey) const + { + if (iKey.empty()) + return 0; + return HashPtr(&iKey.front(), iKey.size() * sizeof(DicEdge)); + } +}; + +#ifdef CHECK_RECURSION +class IncDec +{ + public: + IncDec(int &ioCounter) + : m_counter(ioCounter) + { + m_counter++; + } + + ~IncDec() + { + m_counter--; + } + private: + int &m_counter; +}; + +int current_rec = 0; +int max_rec = 0; +#endif + +/* global variables */ +HashTable, unsigned int, HashVector> *global_hashtable; + +wchar_t global_stringbuf[MAX_STRING_LENGTH]; /* Space for current string */ +wchar_t* global_endstring; /* Marks END of current string */ +const wchar_t* global_input; +const wchar_t* global_endofinput; +#ifdef CHECK_RECURSION +map > global_mapfordepth; +#endif + +/** + * Makenode takes a prefix (as position relative to stringbuf) and + * returns an index of the start node of a dawg that recognizes all + * words beginning with that prefix. String is a pointer (relative + * to stringbuf) indicating how much of iPrefix is matched in the + * input. + * @param iPrefix: prefix to work on + * @param outfile: stream where to write the nodes + * @param ioHeaderInfo: information needed to build the final header, updated + * during the processing + * @param iHeader: temporary header, used only to do the conversion between + * the (wide) chars and their corresponding internal code + */ +unsigned int makenode(const wchar_t *iPrefix, ostream &outfile, + DictHeaderInfo &ioHeaderInfo, const Header &iHeader) +{ +#ifdef CHECK_RECURSION + IncDec inc(current_rec); + if (current_rec > max_rec) + max_rec = current_rec; +#endif + +#ifdef CHECK_RECURSION + // Instead of creating a vector, try to reuse an existing one + vector &edges = global_mapfordepth[current_rec]; + edges.reserve(MAX_EDGES); + edges.clear(); +#else + vector edges; + // Optimize allocation + edges.reserve(MAX_EDGES); +#endif + DicEdge newEdge; + + while (iPrefix == global_endstring) + { + // More edges out of node + newEdge.ptr = 0; + newEdge.term = 0; + newEdge.last = 0; + newEdge.chr = iHeader.getCodeFromChar(*global_endstring++ = *global_input++); + edges.push_back(newEdge); + + // End of a word? + if (*global_input == L'\n' || *global_input == L'\r') + { + ioHeaderInfo.nwords++; + *global_endstring = L'\0'; + // Mark edge as word + edges.back().term = 1; + + // Skip \r and/or \n + while (global_input != global_endofinput && + (*global_input == L'\n' || *global_input == L'\r')) + { + ++global_input; + } + // At the end of input? + if (global_input == global_endofinput) + break; + + global_endstring = global_stringbuf; + while (*global_endstring == *global_input) + { + global_endstring++; + global_input++; + } + } + // Make dawg pointed to by this edge + edges.back().ptr = + makenode(iPrefix + 1, outfile, ioHeaderInfo, iHeader); + } + + int numedges = edges.size(); + if (numedges == 0) + { + // Special node zero - no edges + return 0; + } + + // Mark the last edge + edges.back().last = 1; + + const unsigned int *saved_position = global_hashtable->find(edges); + if (saved_position) + { + ioHeaderInfo.edgessaved += numedges; + ioHeaderInfo.nodessaved++; + + return *saved_position; + } + else + { + unsigned int node_pos = ioHeaderInfo.edgesused; + global_hashtable->add(edges, ioHeaderInfo.edgesused); + ioHeaderInfo.edgesused += numedges; + ioHeaderInfo.nodesused++; + write_node(reinterpret_cast(&edges.front()), + numedges, outfile); + + return node_pos; + } +} + + +void printUsage(const string &iBinaryName) +{ + cout << "Usage: " << iBinaryName << " [options]" << endl + << _("Mandatory options:") << endl + << _(" -d, --dicname Set the dictionary name and version") << endl + << _(" -l, --letters Path to the file containing the letters (see below)") << endl + << _(" -i, --input Path to the uncompressed dictionary file (encoded in UTF-8)") << endl + << _(" -o, --output , unsigned int, HashVector>((unsigned int)(dicsize * SCALE)); +#undef SCALE + + headerInfo.dawg = true; + Header tempHeader = skip_init_header(outfile, headerInfo); + + DicEdge specialnode = {0, 0, 0, 0}; + specialnode.last = 1; + // Temporary variable to avoid a warning when compiling with -O2 + // (there is no warning with -O0... g++ bug?) + DicEdge *tmpPtr = &specialnode; + write_node(reinterpret_cast(tmpPtr), 1, outfile); + + /* + * Call makenode with null (relative to stringbuf) prefix; + * Initialize string to null; Put index of start node on output + */ + DicEdge rootnode = {0, 0, 0, 0}; + global_endstring = global_stringbuf; + clock_t startBuildTime = clock(); + rootnode.ptr = makenode(global_endstring, outfile, headerInfo, tempHeader); + clock_t endBuildTime = clock(); + // Reuse the temporary variable + tmpPtr = &rootnode; + write_node(reinterpret_cast(tmpPtr), 1, outfile); + + fix_header(outfile, headerInfo); + + Header aHeader(headerInfo); + aHeader.print(); + + delete global_hashtable; + delete[] uncompressed; + outfile.close(); + + printf(_(" Load time: %.3f s\n"), 1.0 * (endLoadTime - startLoadTime) / CLOCKS_PER_SEC); + printf(_(" Compression time: %.3f s\n"), 1.0 * (endBuildTime - startBuildTime) / CLOCKS_PER_SEC); +#ifdef CHECK_RECURSION + printf(_(" Maximum recursion level reached: %d\n"), max_rec); +#endif + return 0; + } + catch (std::exception &e) + { + cerr << e.what() << endl; + return 1; + } + +} + diff --git a/dic/dic.c b/dic/dic.c deleted file mode 100644 index b3c820e..0000000 --- a/dic/dic.c +++ /dev/null @@ -1,282 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file dic.c - * \brief Dawg dictionary - * \author Antoine Fraboulet - * \date 2002 - */ - -#include -#include -#include -#include -#include - -#include "config.h" -#include "dic_internals.h" -#include "dic.h" - -#define __UNUSED__ __attribute__((unused)) - -#if defined(WORDS_BIGENDIAN) -static uint32_t swap4(uint32_t v) -{ - uint32_t r; - uint8_t *pv,*pr; - - pv = (uint8_t*)&v; - pr = (uint8_t*)&r; - - pr[0] = pv[3]; - pr[1] = pv[2]; - pr[2] = pv[1]; - pr[3] = pv[0]; - - return r; -} -#endif - -static int -Dic_read_convert_header(Dict_header *header, FILE* file) -{ - - if (fread(header,sizeof(Dict_header),1,file) != 1) - return 1; - -#if defined(WORDS_BIGENDIAN) - header->root = swap4(header->root); - header->nwords = swap4(header->nwords); - header->nodesused = swap4(header->nodesused); - header->edgesused = swap4(header->edgesused); - header->nodessaved = swap4(header->nodessaved); - header->edgessaved = swap4(header->edgessaved); -#else - -#endif - return 0; -} - -int -Dic_check_header(Dict_header *header, const char *path) -{ - int r; - FILE* file; - if ((file = fopen(path,"rb")) == NULL) - return 1; - - r = Dic_read_convert_header(header,file); - fclose(file); - - return r || strcmp(header->ident,_COMPIL_KEYWORD_); -} - -static void -Dic_convert_data_to_arch(Dictionary __UNUSED__ dic) -{ -#if defined(WORDS_BIGENDIAN) - int i; - uint32_t* p; - p = (uint32_t*)dic->dawg; - for(i=0; i < (dic->nedges + 1); i++) - { - p[i] = swap4(p[i]); - } -#endif -} - -int -Dic_load(Dictionary *dic, const char* path) -{ - FILE* file; - Dict_header header; - - - *dic = NULL; - if ((file = fopen(path,"rb")) == NULL) - return 1; - - Dic_read_convert_header(&header,file); - - if ((*dic = (Dictionary) malloc(sizeof(struct _Dictionary))) == NULL) - return 3; - - if (((*dic)->dawg = (Dawg_edge*)malloc((header.edgesused + 1)*sizeof(Dawg_edge))) == NULL) - { - free(*dic); - *dic = NULL; - return 4; - } - - if (fread((*dic)->dawg,sizeof(Dawg_edge),header.edgesused + 1,file) != - (header.edgesused + 1)) - { - free((*dic)->dawg); - free(*dic); - *dic = NULL; - return 5; - } - - (*dic)->root = header.root; - (*dic)->nwords = header.nwords; - (*dic)->nnodes = header.nodesused; - (*dic)->nedges = header.edgesused; - - Dic_convert_data_to_arch(*dic); - - fclose(file); - return 0; -} - - -int -Dic_destroy(Dictionary dic) -{ - if (dic != NULL) - { - if (dic->dawg != NULL) - free(dic->dawg); - else - { - free(dic); - return 2; - } - free(dic); - } - else - return 1; - - return 0; -} - - -dic_elt_t -Dic_next(Dictionary d, dic_elt_t e) -{ - if (! Dic_last(d,e)) - return e+1; - return 0; -} - - -dic_elt_t -Dic_succ(Dictionary d, dic_elt_t e) -{ - return (d->dawg[e]).ptr; -} - - -dic_elt_t -Dic_root(Dictionary d) -{ - return d->root; -} - - -dic_code_t -Dic_chr(Dictionary d, dic_elt_t e) -{ - return (dic_code_t)(d->dawg[e]).chr; -} - - -int -Dic_last(Dictionary d, dic_elt_t e) -{ - return (d->dawg[e]).last; -} - - -int -Dic_word(Dictionary d, dic_elt_t e) -{ - return (d->dawg[e]).term; -} - -unsigned int -Dic_lookup(Dictionary d, dic_elt_t root, dic_code_t* s) -{ - unsigned int p; -begin: - if (! *s) - return root; - if (! Dic_succ(d, root)) - return 0; - p = Dic_succ(d, root); - do - { - if (Dic_chr(d, p) == *s) - { - root = p; - s++; - goto begin; - } - else if (Dic_last(d, p)) - { - return 0; - } - p = Dic_next(d, p); - } while (1); - - return 0; -} - -/* **************************************************************************** */ -/* **************************************************************************** */ -/* **************************************************************************** */ -/* **************************************************************************** */ - -char -Dic_char(Dictionary d, dic_elt_t e) -{ - char c = (d->dawg[e]).chr; - if (c) - return c + 'A' - 1; - else - return 0; -} - -unsigned int -Dic_char_lookup(Dictionary d, dic_elt_t root, char* s) -{ - unsigned int p; -begin: - if (! *s) - return root; - if (! Dic_succ(d, root)) - return 0; - p = Dic_succ(d, root); - do - { - if (Dic_char(d, p) == *s) - { - root = p; - s++; - goto begin; - } - else if (Dic_last(d, p)) - { - return 0; - } - p = Dic_next(d, p); - } while (1); - - return 0; -} diff --git a/dic/dic.cpp b/dic/dic.cpp new file mode 100644 index 0000000..86bcd17 --- /dev/null +++ b/dic/dic.cpp @@ -0,0 +1,276 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file dic.c + * \brief Dawg dictionary + * \author Antoine Fraboulet & Olivier Teuliere + * \date 2002 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +// For ntohl & Co. +#ifdef WIN32 +# include +#else +# if HAVE_NETINET_IN_H +# include +# endif +# if HAVE_ARPA_INET_H +# include +# endif +#endif + +#include "dic.h" +#include "header.h" +#include "dic_exception.h" +#include "dic_internals.h" +#include "tile.h" + + +const Dictionary *Dictionary::m_dic = NULL; + + +// Note: duplicated in header.cpp +#if defined(WORDS_BIGENDIAN) +static uint32_t swap4(uint32_t v) +{ + uint32_t r; + uint8_t *pv = (uint8_t*)&v; + uint8_t *pr = (uint8_t*)&r; + + pr[0] = pv[3]; + pr[1] = pv[2]; + pr[2] = pv[1]; + pr[3] = pv[0]; + + return r; +} +#endif + + +Dictionary::Dictionary(const string &iPath) + : m_dawg(NULL) +{ + ifstream file(iPath.c_str(), ios::in | ios::binary); + + if (!file.is_open()) + throw DicException("Cannot open " + iPath); + + // XXX: we should protect these allocations with auto_ptr + m_header = new Header(file); + m_dawg = new uint32_t[m_header->getNbEdgesUsed() + 1]; + + streamsize toRead = (m_header->getNbEdgesUsed() + 1) * sizeof(uint32_t); + file.read((char*)m_dawg, toRead); + if (file.gcount() != toRead) + { + delete[] m_dawg; + delete m_header; + throw DicException("Problem reading dictionary arcs"); + } + + // Handle endianness + convertDataToArch(); + + initializeTiles(); + + // Concatenate the uppercase and lowercase letters + wstring lower = m_header->getLetters(); + std::transform(lower.begin(), lower.end(), lower.begin(), towlower); + m_allLetters = m_header->getLetters() + lower; + + m_dic = this; +} + + +Dictionary::~Dictionary() +{ + delete[] m_dawg; + delete m_header; +} + + +void Dictionary::convertDataToArch() +{ + if (m_header->getVersion() == 0) + { +#if defined(WORDS_BIGENDIAN) + for (unsigned int i = 0; i < (m_header->getNbEdgesUsed() + 1); i++) + { + m_dawg[i] = swap4(m_dawg[i]); + } +#endif + } + else + { + for (unsigned int i = 0; i < (m_header->getNbEdgesUsed() + 1); i++) + { + m_dawg[i] = ntohl(m_dawg[i]); + } + } +} + + +void Dictionary::initializeTiles() +{ + // "Activate" the dictionary by giving the header to the Tile class + Tile::SetHeader(*m_header); + + // XXX: temp + Tile::m_TheJoker = Tile(Tile::kTILE_JOKER); + + m_tilesVect.reserve(m_header->getLetters().size() + 1); + // Create a tile for each letter in the dictionary header + for (unsigned int i = 0; i < m_header->getLetters().size(); ++i) + m_tilesVect.push_back(Tile(m_header->getLetters()[i])); +} + + +bool Dictionary::validateLetters(const wstring &iLetters, + const wstring &iAccepted) const +{ + return iLetters.empty() + || iLetters.find_first_not_of(m_allLetters + iAccepted) == string::npos; +} + + +const dic_elt_t Dictionary::getNext(const dic_elt_t &e) const +{ + if (!isLast(e)) + return e + 1; + return 0; +} + + +const dic_elt_t Dictionary::getSucc(const dic_elt_t &e) const +{ + if (m_header->getVersion() == 0) + return reinterpret_cast(m_dawg + e)->ptr; + else + return reinterpret_cast(m_dawg + e)->ptr; +} + + +const dic_elt_t Dictionary::getRoot() const +{ + return m_header->getRoot(); +} + + +const dic_code_t Dictionary::getCode(const dic_elt_t &e) const +{ + if (m_header->getVersion() == 0) + return reinterpret_cast(m_dawg + e)->chr; + else + return reinterpret_cast(m_dawg + e)->chr; +} + + +wchar_t Dictionary::getChar(const dic_elt_t &e) const +{ + return m_header->getCharFromCode(getCode(e)); +} + + +bool Dictionary::isLast(const dic_elt_t &e) const +{ + if (m_header->getVersion() == 0) + return reinterpret_cast(m_dawg + e)->last; + else + return reinterpret_cast(m_dawg + e)->last; +} + + +bool Dictionary::isEndOfWord(const dic_elt_t &e) const +{ + if (m_header->getVersion() == 0) + return reinterpret_cast(m_dawg + e)->term; + else + return reinterpret_cast(m_dawg + e)->term; +} + + +unsigned int Dictionary::lookup(const dic_elt_t &root, const dic_code_t *s) const +{ + unsigned int p; + dic_elt_t rootCopy = root; +begin: + if (! *s) + return rootCopy; + if (! getSucc(rootCopy)) + return 0; + p = getSucc(rootCopy); + do + { + if (getCode(p) == *s) + { + rootCopy = p; + s++; + goto begin; + } + else if (isLast( p)) + { + return 0; + } + p = getNext(p); + } while (1); + + return 0; +} + + +unsigned int Dictionary::charLookup(const dic_elt_t &iRoot, const wchar_t *s) const +{ + unsigned int p; + dic_elt_t rootCopy = iRoot; +begin: + if (! *s) + return rootCopy; + if (! getSucc(rootCopy)) + return 0; + p = getSucc(rootCopy); + do + { + if (getChar(p) == *s) + { + rootCopy = p; + s++; + goto begin; + } + else if (isLast(p)) + { + return 0; + } + p = getNext(p); + } while (1); + + return 0; +} + diff --git a/dic/dic.h b/dic/dic.h index a6dad3b..3e63fa4 100644 --- a/dic/dic.h +++ b/dic/dic.h @@ -1,119 +1,153 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file dic.h * \brief Dawg dictionary - * \author Antoine Fraboulet + * \author Antoine Fraboulet & Olivier Teuliere * \date 2002 */ #ifndef _DIC_H_ #define _DIC_H_ -#if defined(__cplusplus) -extern "C" - { -#endif -/** - * different letters in the dictionary - */ -#define DIC_LETTERS 27 +#include +#include +#include + +#include "tile.h" + +using namespace std; + /** * max length of words (including last \0) */ #define DIC_WORD_MAX 16 -typedef struct _Dict_header Dict_header; -typedef struct _Dictionary *Dictionary; +class Header; typedef unsigned int dic_elt_t; typedef unsigned char dic_code_t; +struct params_cross_t; +struct params_7plus1_t; +struct params_regexp_t; +struct search_RegE_list_t; - - /** - * Dictionary header loading from a file - * @param dic : pointer to a header - * @param path : compressed dictionary path - * @return 0 ok, otherwise error - */ -int Dic_check_header(Dict_header *header, const char* path); - +class Dictionary +{ +public: /** * Dictionary creation and loading from a file - * @param dic : pointer to a dictionary - * @param path : compressed dictionary path - * @return 0 ok, 1 error + * @param path: compressed dictionary path */ -int Dic_load (Dictionary* dic,const char* path); + Dictionary(const string &path); + + /// Destructor + ~Dictionary(); /** - * Destroy a dictionary + * Return the current instance of the dictionary object + * XXX: This is ugly, but I don't see any clean way apart from carrying + * a reference to a Dictionary object in many places... + * Other more or less ugly options: + * - Make the dictionary a singleton (2 dictionaries cannot coexist...) + * - Make many classes inherit from a common base class with a dictionary + * member (possibly bad for performances) + * A new created dictionary replaces the previous instance, even if the + * previous instance is not destroyed yet + * If no dictionary object is instanciated when this method is called, + * it will probably crash... */ -int Dic_destroy(Dictionary dic); + static const Dictionary& GetDic() { return *m_dic; } + + /** Give access to the dictionary header */ + const Header& getHeader() const { return *m_header; } /** - * Dic_chr returns the character code associated with an element, + * Check whether all the given letters are present in the dictionary, + * or are one of the other accepted letters. + * Return true if this is the case, false otherwise + */ + bool validateLetters(const wstring &iLetters, + const wstring &iAccepted = L"") const; + + /** Return a vector containing one of each possible tile */ + const vector& getAllTiles() const { return m_tilesVect; } + + /** Return the number of different tiles (including the joker) */ + unsigned int getTileNumber() const { return m_tilesVect.size(); } + + /** Return a tile from its code */ + const Tile &getTileFromCode(unsigned int iCode) const { return m_tilesVect[iCode - 1]; } + + /** + * Returns the character code associated with an element, * codes may range from 0 to 31. 0 is the null character. * @returns code for the encoded character */ -dic_code_t Dic_chr (Dictionary dic, dic_elt_t elt); + const dic_code_t getCode(const dic_elt_t &elt) const; + + /** + * Returns the wide character associated with an element. + * @returns wide character for the element + */ + wchar_t getChar(const dic_elt_t &elt) const; /** * Returns a boolean to show if there is another available * character in the current depth (a neighbor in the tree) * @returns 0 or 1 (true) */ -int Dic_last(Dictionary dic, dic_elt_t elt); + bool isLast(const dic_elt_t &elt) const; /** * Returns a boolean to show if we are at the end of a word - * (see Dic_next) + * (see getNext) * @returns 0 or 1 (true) */ -int Dic_word(Dictionary dic, dic_elt_t elt); + bool isEndOfWord(const dic_elt_t &elt) const; /** * Returns the root of the dictionary * @returns root element */ -dic_elt_t Dic_root(Dictionary dic); + const dic_elt_t getRoot() const; /** - * Returns the next available neighbor (see Dic_last) + * Returns the next available neighbor (see getLast) * @returns next dictionary element at the same depth */ -dic_elt_t Dic_next(Dictionary dic, dic_elt_t elt); + const dic_elt_t getNext(const dic_elt_t &elt) const; /** * Returns the first element available at the next depth * in the dictionary - * @params dic : dictionary * @params elt : current dictionary element * @returns next element (successor) */ -dic_elt_t Dic_succ(Dictionary dic, dic_elt_t elt); + const dic_elt_t getSucc(const dic_elt_t &elt) const; /** * Find the dictionary element matching the pattern starting * from the given root node by walking the dictionary tree - * @params dic : valid dictionary * @params root : starting dictionary node for the search * @params pattern : string encoded according to the dictionary codes, * the pattern must be null ('\0') terminated @@ -121,19 +155,11 @@ dic_elt_t Dic_succ(Dictionary dic, dic_elt_t elt); * element that results from walking the dictionary according to the * pattern */ -unsigned int Dic_lookup(Dictionary dic, dic_elt_t root, dic_code_t* pattern); - - /** - * Dic_char returns the character associated with an element - * (in the range ['A'-'Z']), or the null character ('\0'). - * @returns ASCII code for the character - */ -char Dic_char (Dictionary dic, dic_elt_t elt); + unsigned int lookup(const dic_elt_t &root, const dic_code_t *pattern) const; /** * Find the dictionary element matching the pattern starting * from the given root node by walking the dictionary tree - * @params dic : valid dictionary * @params root : starting dictionary node for the search * @params pattern : string made of uppercase characters in the range * ['A'-'Z']. The pattern must be null ('\0') terminated @@ -141,11 +167,136 @@ char Dic_char (Dictionary dic, dic_elt_t elt); * element that results from walking the dictionary according to the * pattern */ -unsigned int Dic_char_lookup(Dictionary dic, dic_elt_t root, char* pattern); + unsigned int charLookup(const dic_elt_t &iRoot, const wchar_t *iPattern) const; + + /// Getter for the edge at the given position + const uint32_t *getEdgeAt(const dic_elt_t &iElt) const { return m_dawg + iElt; } + + /** + * Search for a word in the dictionary + * @param iWord: lookup word + * @return true if the word is valid, false otherwise + */ + bool searchWord(const wstring &iWord) const; + + /** + * Search for benjamins + * @param iWord: letters + * @param oWordList: results + */ + void searchBenj(const wstring &iWord, list &oWordList) const; + + /** + * Search for all words feasible by adding a letter in front or at the end + * @param iWord: word + * @param oWordList: results + */ + void searchRacc(const wstring &iWord, list &oWordList) const; + + /** + * Search for crosswords + * @param iMask: letters + * @param oWordList: results + */ + void searchCross(const wstring &iMask, list &oWordList) const; + + /** + * Search for all feasible word with "rack" plus one letter + * @param iRack: letters + * @param oWordlist: results + * @param joker: true if the search must be performed when a joker is in the rack + */ + void search7pl1(const wstring &iRack, + map > &oWordList, + bool joker) const; + + /** + * Search for words matching a regular expression + * @param iRegexp: regular expression + * @param oWordList: results + * @param iList: parameters for the search (?) + */ + void searchRegExp(const wstring &iRegexp, + list &oWordList, + struct search_RegE_list_t *iList) const; + + + +private: + // Prevent from copying the dictionary! + Dictionary &operator=(const Dictionary&); + Dictionary(const Dictionary&); + + Header *m_header; + uint32_t *m_dawg; + + /** Letters of the dictionary, both in uppercase and lowercase */ + wstring m_allLetters; + + /// Vector of available tiles + vector m_tilesVect; + + static const Dictionary *m_dic; + + void convertDataToArch(); + void initializeTiles(); + + /// Template getter for the edge at the given position + template + const DAWG_EDGE * getEdgeAt(const dic_elt_t &iElt) const + { + return reinterpret_cast(m_dawg + iElt); + } + + /** + * Walk the dictionary until the end of the word + * @param s: current pointer to letters + * @param eptr: current edge in the dawg + */ + template + const DAWG_EDGE * seekEdgePtr(const wchar_t *s, const DAWG_EDGE *eptr) const; + + /// Helper for searchBenj() + template + void searchBenjTempl(const wstring &iWord, list &oWordList) const; + + /// Helper for searchRacc() + template + void searchRaccTempl(const wstring &iWord, list &oWordList) const; + + /// Helper for searchCross() + template + void searchCrossRecTempl(struct params_cross_t *params, + list &oWordList, + const DAWG_EDGE *edgeptr) const; + + /// Helper for search7pl1() + template + void search7pl1Templ(const wstring &iRack, + map > &oWordList, + bool joker) const; + + /// Second helper for search7pl1() + template + void searchWordByLen(struct params_7plus1_t *params, + int i, const DAWG_EDGE *edgeptr) const; + + /** + * Internal version of searchRegExp, needed until + * wide chars are supported by our regexp engine. + */ + void searchRegExpInner(const string &iRegexp, + list &oWordList, + struct search_RegE_list_t *iList) const; + + /// Helper for searchRegExp() + template + void searchRegexpRecTempl(struct params_regexp_t *params, + int state, + const DAWG_EDGE *edgeptr, + list &oWordList) const; +}; -#if defined(__cplusplus) - } -#endif #endif /* _DIC_H_ */ /// Local Variables: diff --git a/dic/dic_exception.cpp b/dic/dic_exception.cpp new file mode 100644 index 0000000..556b043 --- /dev/null +++ b/dic/dic_exception.cpp @@ -0,0 +1,36 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include "dic_exception.h" + +using namespace std; + + +DicException::DicException(const string &iMessage) + : m_message(iMessage) +{ +} + + +const char *DicException::what() const throw() +{ + return m_message.c_str(); +} + diff --git a/dic/dic_exception.h b/dic/dic_exception.h new file mode 100644 index 0000000..2ecf790 --- /dev/null +++ b/dic/dic_exception.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#ifndef _DIC_EXCEPTION_H_ +#define _DIC_EXCEPTION_H_ + +#include +#include + + +/** + * Exception class for the dictionary. + * It simply inherits from the standard exception and overrides + * its what() method. + */ +class DicException: public std::exception +{ + public: + DicException(const std::string &iMessage); + ~DicException() throw() {} + virtual const char *what() const throw(); + + private: + std::string m_message; +}; + +#endif diff --git a/dic/dic_internals.h b/dic/dic_internals.h index 0b5cd3a..09e8b4c 100644 --- a/dic/dic_internals.h +++ b/dic/dic_internals.h @@ -1,21 +1,22 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file dic_internals.h @@ -26,26 +27,10 @@ #ifndef _DIC_INTERNALS_H_ #define _DIC_INTERNALS_H_ -#if defined(__cplusplus) -extern "C" - { -#endif #include #include "config.h" -/** - * bit masking for ascii characters \n - * ('a' & CHAR) == ('A' & CHAR) == 1 - */ -#define DIC_CHAR_MASK 0x1F - -/** - * keyword included in dictionary headers - * implies little endian storage on words - */ -#define _COMPIL_KEYWORD_ "_COMPILED_DICTIONARY_" - /** * structure of a compressed dictionary \n * \n @@ -60,53 +45,36 @@ extern "C" * ---------------- */ -#if defined(WORDS_BIGENDIAN) -struct __attribute__ ((packed)) _Dawg_edge { - uint32_t - chr : 5, - fill : 1, - last : 1, - term : 1, - ptr : 24; -}; -#else -struct __attribute__ ((packed)) _Dawg_edge { - uint32_t - ptr : 24, - term : 1, - last : 1, - fill : 1, - chr : 5; -}; -#endif - -typedef struct _Dawg_edge Dawg_edge; - - -struct _Dict_header { - char ident[sizeof(_COMPIL_KEYWORD_)]; - char unused_1; - char unused_2; - int32_t root; - int32_t nwords; - uint32_t edgesused; - uint32_t nodesused; - uint32_t nodessaved; - uint32_t edgessaved; -}; - - -struct _Dictionary +struct __attribute__ ((packed)) DicEdgeOld { - Dawg_edge *dawg; - unsigned int root; - int nwords; - int nnodes; - int nedges; + public: + uint32_t + ptr : 24, + term: 1, + last: 1, + fill: 1, + chr : 5; + bool operator==(const DicEdgeOld &iOther) const + { + return memcmp(this, &iOther, sizeof(*this)) == 0; + } }; -#if defined(__cplusplus) - } -#endif + +struct __attribute__ ((packed)) DicEdge +{ + public: + uint32_t + ptr : 24, + term: 1, + last: 1, + chr : 6; + bool operator==(const DicEdge &iOther) const + { + return memcmp(this, &iOther, sizeof(*this)) == 0; + } +}; + + #endif /* _DIC_INTERNALS_H */ diff --git a/dic/dic_search.c b/dic/dic_search.c deleted file mode 100644 index 601f160..0000000 --- a/dic/dic_search.c +++ /dev/null @@ -1,690 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file dic_search.c - * \brief Dictionary lookup functions - * \author Antoine Fraboulet - * \date 2002 - */ - -#include -#include -#include -#include - -#include "dic_internals.h" -#include "dic.h" -#include "regexp.h" -#include "dic_search.h" -#include "libdic_a-ery.h" /* generated by bison */ -#include "libdic_a-erl.h" /* generated by flex */ -#include "automaton.h" - -/** - * function prototype for bison generated parser - */ -int regexpparse(yyscan_t scanner, NODE** root, - struct search_RegE_list_t *list, - struct regexp_error_report_t *err); - -/** - * Dic_seel_edgeptr - * walk the dictionary until the end of the word - * @param dic : dictionnary - * @param s : current pointer to letters - * @param eptr : current edge in the dawg - */ -static Dawg_edge* -Dic_seek_edgeptr(const Dictionary dic, const char* s, Dawg_edge *eptr) -{ - if (*s) - { - Dawg_edge *p = dic->dawg + eptr->ptr; - do { - if (p->chr == (unsigned)(*s & DIC_CHAR_MASK)) - return Dic_seek_edgeptr (dic,s + 1, p); - } while (!(*p++).last); - return dic->dawg; - } - else - return eptr; -} - - -/** - * Dic_search_word_inner : direct application of Dic_seek_edgeptr - * @param dic : dictionary - * @param word : word to lookup - * @result 0 not a valid word, 1 ok - */ -static int Dic_search_word_inner(const Dictionary dic, const char* word) -{ - Dawg_edge *e; - e = Dic_seek_edgeptr(dic, word, dic->dawg + dic->root); - return e->term; -} - - -/** - * Wrapper around Dic_search_word_inner, until we have multibyte support in - * the dictionary - */ -int Dic_search_word(const Dictionary dic, const wchar_t* word) -{ - int res; - char *tmp_word = malloc(wcslen(word) + 1); - sprintf(tmp_word, "%ls", word); - - // Do the actual work - res = Dic_search_word_inner(dic, tmp_word); - - // Release memory - free(tmp_word); - return res; -} - - -/** - * global variables for Dic_search_word_by_len : - * - * a pointer to the structure is passed as a parameter - * so that all the search_* variables appear to the functions - * as global but the code remains re-entrant. - * Should be better to change the algorithm ... - */ - -struct params_7plus1_t { - Dictionary search_dic; - int search_len; - int search_wordlistlen; - int search_wordlistlenmax; - char search_wordtst[DIC_WORD_MAX]; - char search_letters[DIC_LETTERS]; - char (*search_wordlist)[RES_7PL1_MAX][DIC_WORD_MAX]; -}; - -static void -Dic_search_word_by_len(struct params_7plus1_t *params, int i, Dawg_edge *edgeptr) -{ - /* depth first search in the dictionary */ - do { - /* we use a static array and not a real list so we have to stop if - * the array is full */ - if (params->search_wordlistlen >= params->search_wordlistlenmax) - break; - - /* the test is false only when reach the end-node */ - if (edgeptr->chr) - { - - /* is the letter available in search_letters */ - if (params->search_letters[edgeptr->chr]) - { - params->search_wordtst[i] = edgeptr->chr + 'A' - 1; - params->search_letters[edgeptr->chr] --; - if (i == params->search_len) - { - if ((edgeptr->term) - /* && (params->search_wordlistlen < params->search_wordlistlenmax) */) - strcpy((*params->search_wordlist)[params->search_wordlistlen++],params->search_wordtst); - } - else /* if (params->search_wordlistlen < params->search_wordlistlenmax) */ - { - Dic_search_word_by_len(params,i + 1, params->search_dic->dawg + edgeptr->ptr); - } - params->search_letters[edgeptr->chr] ++; - params->search_wordtst[i] = '\0'; - } - - /* the letter is of course available if we have a joker available */ - if (params->search_letters[0]) - { - params->search_wordtst[i] = edgeptr->chr + 'a' - 1; - params->search_letters[0] --; - if (i == params->search_len) - { - if ((edgeptr->term) - /* && (params->search_wordlistlen < params->search_wordlistlenmax) */) - strcpy((*(params->search_wordlist))[params->search_wordlistlen++],params->search_wordtst); - } - else /* if (params->search_wordlistlen < params->search_wordlistlenmax) */ - { - Dic_search_word_by_len(params,i + 1,params->search_dic->dawg + edgeptr->ptr); - } - params->search_letters[0] ++; - params->search_wordtst[i] = '\0'; - } - } - } while (! (*edgeptr++).last); -} - -static void -Dic_search_7pl1_inner(const Dictionary dic, const char* rack, - char buff[DIC_LETTERS][RES_7PL1_MAX][DIC_WORD_MAX], - int joker) -{ - int i,j,wordlen; - const char* r = rack; - struct params_7plus1_t params; - Dawg_edge *root_edge; - - for(i=0; i < DIC_LETTERS; i++) - for(j=0; j < RES_7PL1_MAX; j++) - buff[i][j][0] = '\0'; - - for(i=0; idawg + (dic->dawg[dic->root].ptr); - - params.search_dic = dic; - params.search_wordlistlenmax = RES_7PL1_MAX; - - /* search for all the words that can be done with the letters */ - params.search_len = wordlen - 1; - params.search_wordtst[wordlen]='\0'; - params.search_wordlist = & buff[0]; - params.search_wordlistlen = 0; - Dic_search_word_by_len(¶ms,0,root_edge); - - /* search for all the words that can be done with the letters +1 */ - params.search_len = wordlen; - params.search_wordtst[wordlen + 1]='\0'; - for(i='a'; i <= 'z'; i++) - { - params.search_letters[i & DIC_CHAR_MASK]++; - - params.search_wordlist = & buff[i & DIC_CHAR_MASK]; - params.search_wordlistlen = 0; - Dic_search_word_by_len(¶ms,0,root_edge); - - params.search_letters[i & DIC_CHAR_MASK]--; - } -} - - -/** - * Wrapper around Dic_search_7pl1_inner, until we have multibyte support in - * the dictionary - */ -void -Dic_search_7pl1(const Dictionary dic, const wchar_t* rack, - wchar_t buff[DIC_LETTERS][RES_7PL1_MAX][DIC_WORD_MAX], - int joker) -{ - int i, j, k; - char tmp_buff[DIC_LETTERS][RES_7PL1_MAX][DIC_WORD_MAX]; - char *tmp_rack = malloc(wcslen(rack) + 1); - sprintf(tmp_rack, "%ls", rack); - // Do the actual work - Dic_search_7pl1_inner(dic, tmp_rack, tmp_buff, joker); - - for (i = 0; i < DIC_LETTERS; i++) - { - for (j = 0; j < RES_7PL1_MAX; j++) - { - for (k = 0; k < DIC_WORD_MAX; k++) - { - buff[i][j][k] = tmp_buff[i][j][k]; - } - } - } - free(tmp_rack); -} - -/****************************************/ -/****************************************/ - -static void -Dic_search_Racc_inner(const Dictionary dic, const char* word, - char wordlist[RES_RACC_MAX][DIC_WORD_MAX]) -{ - /* search_racc will try to add a letter in front and at the end of a word */ - - int i,wordlistlen; - Dawg_edge *edge; - char wordtst[DIC_WORD_MAX]; - - for(i=0; i < RES_RACC_MAX; i++) - wordlist[i][0] = 0; - - if (dic == NULL || wordlist == NULL || *wordlist == '\0') - return; - - /* let's try for the front */ - wordlistlen = 0; - strcpy(wordtst+1,word); - for(i='a'; i <= 'z'; i++) - { - wordtst[0] = i; - if (Dic_search_word_inner(dic,wordtst) && wordlistlen < RES_RACC_MAX) - strcpy(wordlist[wordlistlen++],wordtst); - } - - /* add a letter at the end */ - for(i=0; word[i]; i++) - wordtst[i] = word[i]; - - wordtst[i ] = '\0'; - wordtst[i+1] = '\0'; - - edge = Dic_seek_edgeptr(dic,word,dic->dawg + dic->root); - - /* points to what the next letter can be */ - edge = dic->dawg + edge->ptr; - - if (edge != dic->dawg) - { - do { - if (edge->term && wordlistlen < RES_RACC_MAX) - { - wordtst[i] = edge->chr + 'a' - 1; - strcpy(wordlist[wordlistlen++],wordtst); - } - } while (!(*edge++).last); - } -} - -/** - * Wrapper around Dic_search_Racc_inner, until we have multibyte support in - * the dictionary - */ -void -Dic_search_Racc(const Dictionary dic, const wchar_t* word, - wchar_t wordlist[RES_RACC_MAX][DIC_WORD_MAX]) -{ - int i, j; - char tmp_buff[RES_RACC_MAX][DIC_WORD_MAX]; - char *tmp_word = malloc(wcslen(word) + 1); - sprintf(tmp_word, "%ls", word); - // Do the actual work - Dic_search_Racc_inner(dic, tmp_word, tmp_buff); - - for (i = 0; i < RES_RACC_MAX; i++) - { - for (j = 0; j < DIC_WORD_MAX; j++) - { - wordlist[i][j] = tmp_buff[i][j]; - } - } - free(tmp_word); -} - -/****************************************/ -/****************************************/ - - -static void -Dic_search_Benj_inner(const Dictionary dic, const char* word, - char wordlist[RES_BENJ_MAX][DIC_WORD_MAX]) -{ - int i,wordlistlen; - char wordtst[DIC_WORD_MAX]; - Dawg_edge *edge0,*edge1,*edge2,*edgetst; - - for(i=0; i < RES_BENJ_MAX; i++) - wordlist[i][0] = 0; - - if (dic == NULL || word == NULL || *word == '\0') - return; - - wordlistlen = 0; - - strcpy(wordtst+3,word); - edge0 = dic->dawg + (dic->dawg[dic->root].ptr); - do { - wordtst[0] = edge0->chr + 'a' - 1; - edge1 = dic->dawg + edge0->ptr; - do { - wordtst[1] = edge1->chr + 'a' - 1; - edge2 = dic->dawg + edge1->ptr; - do { - wordtst[2] = edge2->chr + 'a' - 1; - edgetst = Dic_seek_edgeptr(dic,word,edge2); - if (edgetst->term && wordlistlen < RES_BENJ_MAX) - strcpy(wordlist[wordlistlen++],wordtst); - } while (!(*edge2++).last); - } while (!(*edge1++).last); - } while (!(*edge0++).last); -} - -/** - * Wrapper around Dic_search_Benj_inner, until we have multibyte support in - * the dictionary - */ -void -Dic_search_Benj(const Dictionary dic, const wchar_t* word, - wchar_t wordlist[RES_BENJ_MAX][DIC_WORD_MAX]) -{ - int i, j; - char tmp_buff[RES_BENJ_MAX][DIC_WORD_MAX]; - char *tmp_word = malloc(wcslen(word) + 1); - sprintf(tmp_word, "%ls", word); - // Do the actual work - Dic_search_Benj_inner(dic, tmp_word, tmp_buff); - - for (i = 0; i < RES_BENJ_MAX; i++) - { - for (j = 0; j < DIC_WORD_MAX; j++) - { - wordlist[i][j] = tmp_buff[i][j]; - } - } - free(tmp_word); -} - - -/****************************************/ -/****************************************/ - -struct params_cross_t { - Dictionary dic; - int wordlen; - int wordlistlen; - int wordlistlenmax; - char mask[DIC_WORD_MAX]; -}; - - -void -Dic_search_cross_rec(struct params_cross_t *params, - char wordlist[RES_CROS_MAX][DIC_WORD_MAX], - Dawg_edge *edgeptr) -{ - Dawg_edge *current = params->dic->dawg + edgeptr->ptr; - - if (params->mask[params->wordlen] == '\0' && edgeptr->term) - { - if (params->wordlistlen < params->wordlistlenmax) - strcpy(wordlist[params->wordlistlen++],params->mask); - } - else if (params->mask[params->wordlen] == '.') - { - do - { - params->mask[params->wordlen] = current->chr + 'a' - 1; - params->wordlen ++; - Dic_search_cross_rec(params,wordlist,current); - params->wordlen --; - params->mask[params->wordlen] = '.'; - } - while (!(*current++).last); - } - else - { - do - { - if (current->chr == (unsigned int)(params->mask[params->wordlen] & DIC_CHAR_MASK)) - { - params->wordlen ++; - Dic_search_cross_rec(params,wordlist,current); - params->wordlen --; - break; - } - } - while (!(*current++).last); - } -} - - -static void -Dic_search_Cros_inner(const Dictionary dic, const char* mask, - char wordlist[RES_CROS_MAX][DIC_WORD_MAX]) -{ - int i; - struct params_cross_t params; - - for(i=0; i < RES_CROS_MAX; i++) - wordlist[i][0] = 0; - - if (dic == NULL || mask == NULL || *mask == '\0') - return; - - for(i=0; i < DIC_WORD_MAX && mask[i]; i++) - { - if (isalpha(mask[i])) - params.mask[i] = (mask[i] & DIC_CHAR_MASK) + 'A' - 1; - else - params.mask[i] = '.'; - } - params.mask[i] = '\0'; - - params.dic = dic; - params.wordlen = 0; - params.wordlistlen = 0; - params.wordlistlenmax = RES_CROS_MAX; - Dic_search_cross_rec(¶ms, wordlist, dic->dawg + dic->root); -} - - -/** - * Wrapper around Dic_search_Cros_inner, until we have multibyte support in - * the dictionary - */ -void -Dic_search_Cros(const Dictionary dic, const wchar_t* mask, - wchar_t wordlist[RES_CROS_MAX][DIC_WORD_MAX]) -{ - int i, j; - char tmp_buff[RES_CROS_MAX][DIC_WORD_MAX]; - char *tmp_mask = malloc(wcslen(mask) + 1); - sprintf(tmp_mask, "%ls", mask); - // Do the actual work - Dic_search_Cros_inner(dic, tmp_mask, tmp_buff); - - for (i = 0; i < RES_CROS_MAX; i++) - { - for (j = 0; j < DIC_WORD_MAX; j++) - { - wordlist[i][j] = tmp_buff[i][j]; - } - } - free(tmp_mask); -} - -/****************************************/ -/****************************************/ - -struct params_regexp_t { - Dictionary dic; - int minlength; - int maxlength; - automaton automaton; - struct search_RegE_list_t *charlist; - char word[DIC_WORD_MAX]; - int wordlen; - int wordlistlen; - int wordlistlenmax; -}; - -void -Dic_search_regexp_rec(struct params_regexp_t *params, - int state, - Dawg_edge *edgeptr, - char wordlist[RES_REGE_MAX][DIC_WORD_MAX]) -{ - int next_state; - Dawg_edge *current; - /* if we have a valid word we store it */ - if (automaton_get_accept(params->automaton,state) && edgeptr->term) - { - int l = strlen(params->word); - if (params->wordlistlen < params->wordlistlenmax && - params->minlength <= l && - params->maxlength >= l) - { - strcpy(wordlist[params->wordlistlen++],params->word); - } - } - /* we now drive the search by exploring the dictionary */ - current = params->dic->dawg + edgeptr->ptr; - do { - /* the current letter is current->chr */ - next_state = automaton_get_next_state(params->automaton,state,current->chr); - /* 1 : the letter appears in the automaton as is */ - if (next_state) - { - params->word[params->wordlen] = current->chr + 'a' - 1; - params->wordlen ++; - Dic_search_regexp_rec(params,next_state,current,wordlist); - params->wordlen --; - params->word[params->wordlen] = '\0'; - } - } while (!(*current++).last); -} - - -void -Dic_search_RegE_inner(const Dictionary dic, const char* re, - char wordlist[RES_REGE_MAX][DIC_WORD_MAX], - struct search_RegE_list_t *list) -{ - int i,p,n,value; - int ptl[REGEXP_MAX+1]; - int PS [REGEXP_MAX+1]; - NODE* root; - yyscan_t scanner; - YY_BUFFER_STATE buf; - automaton a; - char stringbuf[250]; - struct params_regexp_t params; - struct regexp_error_report_t report; - - /* init */ - for(i=0; i < RES_REGE_MAX; i++) - wordlist[i][0] = 0; - - if (dic == NULL || re == NULL || *re == '\0') - return; - - /* (expr)# */ - sprintf(stringbuf,"(%s)#",re); - for(i=0; i < REGEXP_MAX; i++) - { - PS[i] = 0; - ptl[i] = 0; - } - - report.pos1 = 0; - report.pos2 = 0; - report.msg[0] = '\0'; - - /* parsing */ - regexplex_init( &scanner ); - buf = regexp_scan_string( stringbuf, scanner ); - root = NULL; - value = regexpparse( scanner , &root, list, &report); - regexp_delete_buffer(buf,scanner); - regexplex_destroy( scanner ); - - if (value) - { -#ifdef DEBUG_FLEX_IS_BROKEN - fprintf(stderr,"parser error at pos %d - %d : %s\n", - report.pos1, report.pos2, report.msg); -#endif - regexp_delete_tree(root); - return ; - } - - n = 1; - p = 1; - regexp_parcours(root, &p, &n, ptl); - PS [0] = p - 1; - ptl[0] = p - 1; - - regexp_possuivante(root,PS); - - if ((a = automaton_build(root->PP,ptl,PS,list)) != NULL) - { - params.dic = dic; - params.minlength = list->minlength; - params.maxlength = list->maxlength; - params.automaton = a; - params.charlist = list; - memset(params.word,'\0',sizeof(params.word)); - params.wordlen = 0; - params.wordlistlen = 0; - params.wordlistlenmax = RES_REGE_MAX; - Dic_search_regexp_rec(¶ms, automaton_get_init(a), dic->dawg + dic->root, wordlist); - - automaton_delete(a); - } - regexp_delete_tree(root); -} - -/** - * Wrapper around Dic_search_RegE_inner, until we have multibyte support in - * the dictionary - */ -void -Dic_search_RegE(const Dictionary dic, const wchar_t* re, - wchar_t wordlist[RES_REGE_MAX][DIC_WORD_MAX], - struct search_RegE_list_t *list) -{ - int i; - char tmp_buff[RES_REGE_MAX][DIC_WORD_MAX]; - char *tmp_re = malloc(wcslen(re) + 1); - sprintf(tmp_re, "%ls", re); - // Do the actual work - Dic_search_RegE_inner(dic, tmp_re, tmp_buff, list); - - for (i = 0; i < RES_REGE_MAX; i++) - { - mbstowcs(wordlist[i], tmp_buff[i], DIC_WORD_MAX); - } - free(tmp_re); -} - -/****************************************/ -/****************************************/ - diff --git a/dic/dic_search.cpp b/dic/dic_search.cpp new file mode 100644 index 0000000..caf765f --- /dev/null +++ b/dic/dic_search.cpp @@ -0,0 +1,566 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file dic_search.c + * \brief Dictionary lookup functions + * \author Antoine Fraboulet + * \date 2002 + */ + +#include +#include +#include +#include + +#include "dic_internals.h" +#include "dic.h" +#include "header.h" +#include "encoding.h" +#include "regexp.h" +#include "libdic_a-ery.h" /* generated by bison */ +#include "libdic_a-erl.h" /* generated by flex */ +#include "automaton.h" + + +/** + * Function prototype for bison generated parser + */ +int regexpparse(yyscan_t scanner, NODE** root, + struct search_RegE_list_t *iList, + struct regexp_error_report_t *err); + + +template +const DAWG_EDGE* Dictionary::seekEdgePtr(const wchar_t* s, const DAWG_EDGE *eptr) const +{ + if (*s) + { + const DAWG_EDGE *p = getEdgeAt(eptr->ptr); + do + { + if (p->chr == getHeader().getCodeFromChar(*s)) + return seekEdgePtr(s + 1, p); + } while (!(*p++).last); + return getEdgeAt(0); + } + else + return eptr; +} + + +bool Dictionary::searchWord(const wstring &iWord) const +{ + if (!validateLetters(iWord)) + return false; + + if (getHeader().getVersion() == 0) + { + const DicEdgeOld *e = + seekEdgePtr(iWord.c_str(), getEdgeAt(getRoot())); + return e->term; + } + else + { + const DicEdge *e = + seekEdgePtr(iWord.c_str(), getEdgeAt(getRoot())); + return e->term; + } +} + + +/** + * Global variables for searchWordByLen: + * + * A pointer to the structure is passed as a parameter + * so that all the search_* variables appear to the functions + * as global but the code remains re-entrant. + * Should be better to change the algorithm ... + */ + +struct params_7plus1_t +{ + wchar_t added_char; + map > *results; + int search_len; + wchar_t search_wordtst[DIC_WORD_MAX]; + char search_letters[63]; +}; + +template +void Dictionary::searchWordByLen(struct params_7plus1_t *params, + int i, const DAWG_EDGE *edgeptr) const +{ + /* depth first search in the dictionary */ + do + { + /* the test is false only when reach the end-node */ + if (edgeptr->chr) + { + /* is the letter available in search_letters */ + if (params->search_letters[edgeptr->chr]) + { + params->search_wordtst[i] = getHeader().getCharFromCode(edgeptr->chr); + params->search_letters[edgeptr->chr] --; + if (i == params->search_len) + { + if (edgeptr->term) + { + (*params->results)[params->added_char].push_back(params->search_wordtst); + } + } + else + { + searchWordByLen(params, i + 1, getEdgeAt(edgeptr->ptr)); + } + params->search_letters[edgeptr->chr] ++; + params->search_wordtst[i] = L'\0'; + } + + /* the letter is of course available if we have a joker available */ + if (params->search_letters[0]) + { + params->search_wordtst[i] = getHeader().getCharFromCode(edgeptr->chr); + params->search_letters[0] --; + if (i == params->search_len) + { + if (edgeptr->term) + { + (*params->results)[params->added_char].push_back(params->search_wordtst); + } + } + else + { + searchWordByLen(params, i + 1, getEdgeAt(edgeptr->ptr)); + } + params->search_letters[0] ++; + params->search_wordtst[i] = L'\0'; + } + } + } while (! (*edgeptr++).last); +} + + +template +void Dictionary::search7pl1Templ(const wstring &iRack, + map > &oWordList, + bool joker) const +{ + if (iRack == L"" || iRack.size() > DIC_WORD_MAX) + return; + + struct params_7plus1_t params; + + for (unsigned int i = 0; i < sizeof(params.search_letters); i++) + params.search_letters[i] = 0; + + /* + * the letters are verified and changed to the dic internal + * representation (using getCodeFromChar(*r)) + */ + int wordlen = 0; + for (const wchar_t* r = iRack.c_str(); *r; r++) + { + if (iswalpha(*r)) + { + params.search_letters[getHeader().getCodeFromChar(*r)]++; + wordlen++; + } + else if (*r == L'?') + { + if (joker) + { + params.search_letters[0]++; + wordlen++; + } + else + { + oWordList[0].push_back(L"** joker **"); + return; + } + } + } + + if (wordlen < 1) + return; + + const DAWG_EDGE *root_edge = getEdgeAt(getRoot()); + root_edge = getEdgeAt(root_edge->ptr); + + params.results = &oWordList; + + /* search for all the words that can be done with the letters */ + params.added_char = L'\0'; + params.search_len = wordlen - 1; + params.search_wordtst[wordlen] = L'\0'; + searchWordByLen(¶ms, 0, root_edge); + + /* search for all the words that can be done with the letters +1 */ + params.search_len = wordlen; + params.search_wordtst[wordlen + 1] = L'\0'; + const wstring &letters = getHeader().getLetters(); + for (unsigned int i = 0; i < letters.size(); i++) + { + params.added_char = letters[i]; + unsigned int code = getHeader().getCodeFromChar(letters[i]); + params.search_letters[code]++; + + searchWordByLen(¶ms, 0, root_edge); + + params.search_letters[code]--; + } +} + + +void Dictionary::search7pl1(const wstring &iRack, + map > &oWordList, + bool joker) const +{ + if (getHeader().getVersion() == 0) + search7pl1Templ(iRack, oWordList, joker); + else + search7pl1Templ(iRack, oWordList, joker); +} + +/****************************************/ +/****************************************/ + +template +void Dictionary::searchRaccTempl(const wstring &iWord, list &oWordList) const +{ + if (iWord == L"") + return; + + /* search_racc will try to add a letter in front and at the end of a word */ + + /* let's try for the front */ + wchar_t wordtst[DIC_WORD_MAX]; + wcscpy(wordtst + 1, iWord.c_str()); + const wstring &letters = getHeader().getLetters(); + for (unsigned int i = 0; i <= letters.size(); i++) + { + wordtst[0] = letters[i]; + if (searchWord(wordtst)) + oWordList.push_back(wordtst); + } + + /* add a letter at the end */ + int i; + for (i = 0; iWord[i]; i++) + wordtst[i] = iWord[i]; + + wordtst[i ] = '\0'; + wordtst[i+1] = '\0'; + + const DAWG_EDGE *edge_seek = + seekEdgePtr(iWord.c_str(), getEdgeAt(getRoot())); + + /* points to what the next letter can be */ + const DAWG_EDGE *edge = getEdgeAt(edge_seek->ptr); + + if (edge != getEdgeAt(0)) + { + do + { + if (edge->term) + { + wordtst[i] = getHeader().getCharFromCode(edge->chr); + oWordList.push_back(wordtst); + } + } while (!(*edge++).last); + } +} + + +void Dictionary::searchRacc(const wstring &iWord, list &oWordList) const +{ + if (getHeader().getVersion() == 0) + searchRaccTempl(iWord, oWordList); + else + searchRaccTempl(iWord, oWordList); +} + +/****************************************/ +/****************************************/ + +template +void Dictionary::searchBenjTempl(const wstring &iWord, list &oWordList) const +{ + if (iWord == L"") + return; + + wchar_t wordtst[DIC_WORD_MAX]; + wcscpy(wordtst + 3, iWord.c_str()); + const DAWG_EDGE *edge0, *edge1, *edge2, *edgetst; + edge0 = getEdgeAt(getRoot()); + edge0 = getEdgeAt(edge0->ptr); + do + { + wordtst[0] = getHeader().getCharFromCode(edge0->chr); + edge1 = getEdgeAt(edge0->ptr); + do + { + wordtst[1] = getHeader().getCharFromCode(edge1->chr); + edge2 = getEdgeAt(edge1->ptr); + do + { + edgetst = seekEdgePtr(iWord.c_str(), edge2); + if (edgetst->term) + { + wordtst[2] = getHeader().getCharFromCode(edge2->chr); + oWordList.push_back(wordtst); + } + } while (!(*edge2++).last); + } while (!(*edge1++).last); + } while (!(*edge0++).last); +} + + +void Dictionary::searchBenj(const wstring &iWord, list &oWordList) const +{ + if (getHeader().getVersion() == 0) + searchBenjTempl(iWord, oWordList); + else + searchBenjTempl(iWord, oWordList); +} + +/****************************************/ +/****************************************/ + +struct params_cross_t +{ + int wordlen; + wchar_t mask[DIC_WORD_MAX]; +}; + + +template +void Dictionary::searchCrossRecTempl(struct params_cross_t *params, + list &oWordList, + const DAWG_EDGE *edgeptr) const +{ + const DAWG_EDGE *current = getEdgeAt(edgeptr->ptr); + + if (params->mask[params->wordlen] == '\0' && edgeptr->term) + { + oWordList.push_back(params->mask); + } + else if (params->mask[params->wordlen] == '.') + { + do + { + params->mask[params->wordlen] = getHeader().getCharFromCode(current->chr); + params->wordlen ++; + searchCrossRecTempl(params, oWordList, current); + params->wordlen --; + params->mask[params->wordlen] = '.'; + } + while (!(*current++).last); + } + else + { + do + { + if (current->chr == getHeader().getCodeFromChar(params->mask[params->wordlen])) + { + params->wordlen ++; + searchCrossRecTempl(params, oWordList, current); + params->wordlen --; + break; + } + } + while (!(*current++).last); + } +} + + +void Dictionary::searchCross(const wstring &iMask, list &oWordList) const +{ + if (iMask == L"") + return; + + struct params_cross_t params; + + int i; + for (i = 0; i < DIC_WORD_MAX && iMask[i]; i++) + { + if (iswalpha(iMask[i])) + params.mask[i] = towupper(iMask[i]); + else + params.mask[i] = '.'; + } + params.mask[i] = '\0'; + + params.wordlen = 0; + if (getHeader().getVersion() == 0) + { + searchCrossRecTempl(¶ms, oWordList, + getEdgeAt(getRoot())); + } + else + { + searchCrossRecTempl(¶ms, oWordList, + getEdgeAt(getRoot())); + } +} + +/****************************************/ +/****************************************/ + +struct params_regexp_t +{ + int minlength; + int maxlength; + Automaton *automaton_field; + struct search_RegE_list_t *charlist; + char word[DIC_WORD_MAX]; + int wordlen; +}; + + +template +void Dictionary::searchRegexpRecTempl(struct params_regexp_t *params, + int state, + const DAWG_EDGE *edgeptr, + list &oWordList) const +{ + int next_state; + /* if we have a valid word we store it */ + if (params->automaton_field->accept(state) && edgeptr->term) + { + int l = strlen(params->word); + if (params->minlength <= l && + params->maxlength >= l) + { + oWordList.push_back(params->word); + } + } + /* we now drive the search by exploring the dictionary */ + const DAWG_EDGE *current = getEdgeAt(edgeptr->ptr); + do + { + /* the current letter is current->chr */ + next_state = params->automaton_field->getNextState(state, current->chr); + /* 1: the letter appears in the automaton as is */ + if (next_state) + { + params->word[params->wordlen] = current->chr + 'a' - 1; + params->wordlen ++; + searchRegexpRecTempl(params, next_state, current, oWordList); + params->wordlen --; + params->word[params->wordlen] = '\0'; + } + } while (!(*current++).last); +} + + +void Dictionary::searchRegExpInner(const string &iRegexp, + list &oWordList, + struct search_RegE_list_t *iList) const +{ + int ptl[REGEXP_MAX+1]; + int PS [REGEXP_MAX+1]; + + /* (expr)# */ + char stringbuf[250]; + sprintf(stringbuf, "(%s)#", iRegexp.c_str()); + for (int i = 0; i < REGEXP_MAX; i++) + { + PS[i] = 0; + ptl[i] = 0; + } + + struct regexp_error_report_t report; + report.pos1 = 0; + report.pos2 = 0; + report.msg[0] = '\0'; + + /* parsing */ + yyscan_t scanner; + regexplex_init( &scanner ); + YY_BUFFER_STATE buf = regexp_scan_string(stringbuf, scanner); + NODE *root = NULL; + int value = regexpparse(scanner , &root, iList, &report); + regexp_delete_buffer(buf, scanner); + regexplex_destroy(scanner); + + if (value) + { +#ifdef DEBUG_FLEX_IS_BROKEN + fprintf(stderr, "parser error at pos %d - %d: %s\n", + report.pos1, report.pos2, report.msg); +#endif + regexp_delete_tree(root); + return ; + } + + int n = 1; + int p = 1; + regexp_parcours(root, &p, &n, ptl); + PS [0] = p - 1; + ptl[0] = p - 1; + + regexp_possuivante(root, PS); + + Automaton *a = new Automaton(root->PP, ptl, PS, iList); + if (a) + { + struct params_regexp_t params; + params.minlength = iList->minlength; + params.maxlength = iList->maxlength; + params.automaton_field = a; + params.charlist = iList; + memset(params.word, '\0', sizeof(params.word)); + params.wordlen = 0; + if (getHeader().getVersion() == 0) + { + searchRegexpRecTempl(¶ms, a->getInitId(), + getEdgeAt(getRoot()), oWordList); + } + else + { + searchRegexpRecTempl(¶ms, a->getInitId(), + getEdgeAt(getRoot()), oWordList); + } + + delete a; + } + regexp_delete_tree(root); +} + + +void Dictionary::searchRegExp(const wstring &iRegexp, + list &oWordList, + struct search_RegE_list_t *iList) const +{ + if (iRegexp == L"") + return; + + list tmpWordList; + // Do the actual work + searchRegExpInner(convertToMb(iRegexp), tmpWordList, iList); + + list::const_iterator it; + for (it = tmpWordList.begin(); it != tmpWordList.end(); it++) + { + oWordList.push_back(convertToWc(*it)); + } +} + diff --git a/dic/dic_search.h b/dic/dic_search.h deleted file mode 100644 index c9b2d52..0000000 --- a/dic/dic_search.h +++ /dev/null @@ -1,131 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file dic_search.h - * \brief Dictionary lookup functions - * \author Antoine Fraboulet - * \date 2002 - */ - -#ifndef _DIC_SEARCH_H_ -#define _DIC_SEARCH_H_ -#if defined(__cplusplus) -extern "C" - { -#endif - - /** - * number of results for Rack+1 search (Dic_search_7pl1) - */ -#define RES_7PL1_MAX 200 - - /** - * number of results for Extensions search (Dic_search_Racc) - */ -#define RES_RACC_MAX 100 - - /** - * number of results for Benjamin search (Dic_search_Benj) - */ -#define RES_BENJ_MAX 100 - - /** - * number of results for CrossWords search (Dic_search_Cros) - */ -#define RES_CROS_MAX 200 - - /** - * number of results for Regular Expression search (Dic_search_RegE) - */ -#define RES_REGE_MAX 200 - - /** - * Search for a word in the dictionnary - * @param dic : dictionary - * @param path : lookup word - * @return 1 present, 0 error - */ -int Dic_search_word(Dictionary dic, - const wchar_t* path); - - /** - * Search for all feasible word with "rack" plus one letter - * @param dic : dictionary - * @param rack : letters - * @param wordlist : results - */ -void Dic_search_7pl1(Dictionary dic, - const wchar_t* rack, - wchar_t wordlist[DIC_LETTERS][RES_7PL1_MAX][DIC_WORD_MAX], - int joker); - - /** - * Search for all feasible word adding a letter in front or at the end - * @param dic : dictionary - * @param word : word - * @param wordlist : results - */ -void Dic_search_Racc(Dictionary dic, - const wchar_t* word, - wchar_t wordlist[RES_RACC_MAX][DIC_WORD_MAX]); - - /** - * Search for benjamins - * @param dic : dictionary - * @param rack : letters - * @param wordlist : results - */ -void Dic_search_Benj(Dictionary dic, - const wchar_t* word, - wchar_t wordlist[RES_BENJ_MAX][DIC_WORD_MAX]); - - /** - * Search for crosswords - * @param dic : dictionary - * @param rack : letters - * @param wordlist : results - */ -void Dic_search_Cros(Dictionary dic, - const wchar_t* mask, - wchar_t wordlist[RES_CROS_MAX][DIC_WORD_MAX]); - - /** - * Search for words matching a regular expression - * @param dic : dictionary - * @param re : regular expression - * @param wordlist : results - */ -void Dic_search_RegE(Dictionary dic, - const wchar_t* re, - wchar_t wordlist[RES_REGE_MAX][DIC_WORD_MAX], - struct search_RegE_list_t *list); - - /** - * Internal version of Dic_search_RegE, used inside the dictionary. - * Please use Dic_search_RegE instead from outside the dic library. - */ -void Dic_search_RegE_inner(const Dictionary dic, const char* re, - char wordlist[RES_REGE_MAX][DIC_WORD_MAX], - struct search_RegE_list_t *list); - -#if defined(__cplusplus) - } -#endif -#endif /* _DIC_SEARCH_H_ */ diff --git a/dic/encoding.cpp b/dic/encoding.cpp new file mode 100644 index 0000000..207d819 --- /dev/null +++ b/dic/encoding.cpp @@ -0,0 +1,396 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file encoding.cpp + * \brief Utility functions to ease handling of wide-character strings + * \author Olivier Teuliere + * \date 2005 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#include "encoding.h" +#include "dic_exception.h" + +using namespace std; + + +#ifdef WIN32 +// Utility function to get the last system error as a string +static string GetWin32Error() +{ + char *lpMsgBuf; + DWORD dw = GetLastError(); + cerr << dw << endl; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL); + string msg = lpMsgBuf; + LocalFree(lpMsgBuf); + return msg; +} +#endif + + +#if !HAVE_WCWIDTH +// wcwidth replacement (for win32 in particular) +// Inspired from the gnulib package, without some of the refinements +static inline int wcwidth(wchar_t c) +{ + // Assume all the printable characters have width 1 + return c == 0 ? 0 : (iswprint(c) ? 1 : -1); +} +#endif + + +int _wtoi(const wchar_t *iWStr) +{ + return wcstol(iWStr, NULL, 10); +} + + +int _swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...) +{ + int res; + va_list argp; + va_start(argp, format); +#ifdef WIN32 + // Mingw32 does not take the maxlen argument + res = vswprintf(wcs, format, argp); +#else + res = vswprintf(wcs, maxlen, format, argp); +#endif + va_end(argp); + return res; +} + + +wchar_t *_wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr) +{ +#ifdef WIN32 + // Mingw32 does not take the third argument + return wcstok(wcs, delim); +#else + return wcstok(wcs, delim, ptr); +#endif +} + + +#define _MAX_SIZE_FOR_STACK_ 30 +wstring convertToWc(const string& iStr) +{ +#ifdef WIN32 + // XXX: Assume the input is in UTF-8 + return readFromUTF8(iStr.c_str(), iStr.size(), "convertToWc"); +#else + // Get the needed length (we _can't_ use string::size()) + size_t len = mbstowcs(NULL, iStr.c_str(), 0); + if (len == (size_t)-1) + return L""; + + // Change the allocation method depending on the length of the string + if (len < _MAX_SIZE_FOR_STACK_) + { + // Without multi-thread, we can use static storage + static wchar_t tmp[_MAX_SIZE_FOR_STACK_]; + len = mbstowcs(tmp, iStr.c_str(), len + 1); + return tmp; + } + else + { + wchar_t *tmp = new wchar_t[len + 1]; + len = mbstowcs(tmp, iStr.c_str(), len + 1); + wstring res = tmp; + delete[] tmp; + return res; + } +#endif +} + + +string convertToMb(const wstring& iWStr) +{ +#ifdef WIN32 + const unsigned int size = iWStr.size() * 4; + char buf[size]; + // XXX: Assume the output is in UTF-8 + int nb = writeInUTF8(iWStr, buf, size, "convertToMb"); + return string(buf, nb); +#else + // Get the needed length (we _can't_ use wstring::size()) + size_t len = wcstombs(NULL, iWStr.c_str(), 0); + if (len == (size_t)-1) + return ""; + + // Change the allocation method depending on the length of the string + if (len < _MAX_SIZE_FOR_STACK_) + { + // Without multi-thread, we can use static storage + static char tmp[_MAX_SIZE_FOR_STACK_]; + len = wcstombs(tmp, iWStr.c_str(), len + 1); + return tmp; + } + else + { + char *tmp = new char[len + 1]; + len = wcstombs(tmp, iWStr.c_str(), len + 1); + string res = tmp; + delete[] tmp; + return res; + } +#endif +} +#undef _MAX_SIZE_FOR_STACK_ + + +string convertToMb(wchar_t iWChar) +{ +#ifdef WIN32 + return convertToMb(wstring(1, iWChar)); +#else + char res[MB_CUR_MAX + 1]; + int len = wctomb(res, iWChar); + if (len == -1) + return ""; + res[len] = '\0'; + + return res; +#endif +} + + +string truncString(const string &iStr, unsigned int iMaxWidth) +{ + // Heuristic: the width of a character cannot exceed the number of + // bytes used to represent it (even in UTF-8) + if (iStr.size() <= iMaxWidth) + return iStr; + return truncAndConvert(convertToWc(iStr), iMaxWidth); +} + + +string truncAndConvert(const wstring &iWstr, unsigned int iMaxWidth) +{ + unsigned int width = 0; + unsigned int pos; + for (pos = 0; pos < iWstr.size(); ++pos) + { + int n = wcwidth(iWstr[pos]); + if (n == -1) + { + ostringstream ss; + ss << "truncAndConvert: non printable character: " << iWstr[pos]; + // XXX: Should we throw an exception instead? Just ignore the problem? + cerr << ss.str() << endl;; + //throw DicException(ss.str()); + return convertToMb(iWstr); + } + if (width + n > iMaxWidth) + break; + width += n; + } + + return convertToMb(iWstr.substr(0, pos)); +} + + +string truncOrPad(const string &iStr, unsigned int iWidth, char iChar) +{ + wstring wstr = convertToWc(iStr); + unsigned int width = 0; + unsigned int pos; + for (pos = 0; pos < wstr.size(); ++pos) + { + int n = wcwidth(wstr[pos]); + if (n == -1) + { + ostringstream ss; + ss << "truncAndConvert: non printable character: " << wstr[pos]; + // XXX: Should we throw an exception instead? Just ignore the problem? + cerr << ss.str() << endl;; + //throw DicException(ss.str()); + return convertToMb(wstr); + } + if (width + n > iWidth) + break; + width += n; + } + + if (iWidth > width) + return convertToMb(wstr.substr(0, pos)) + string(iWidth - width, iChar); + else + return convertToMb(wstr.substr(0, pos)); +} + + +string padAndConvert(const wstring &iWstr, unsigned int iLength, + bool iLeftPad, char c) +{ + int width = 0; + for (unsigned int i = 0; i < iWstr.size(); ++i) + { + int n = wcwidth(iWstr[i]); + if (n == -1) + { + ostringstream ss; + ss << "padAndConvert: non printable character: " << iWstr[i]; + // XXX: Should we throw an exception instead? Just ignore the problem? + cerr << ss.str() << endl;; + //throw DicException(ss.str()); + return convertToMb(iWstr); + } + width += n; + } + + if ((unsigned int)width >= iLength) + return convertToMb(iWstr); + else + { + // Padding is needed + string s(iLength - width, c); + if (iLeftPad) + return s + convertToMb(iWstr); + else + return convertToMb(iWstr) + s; + } +} + + +unsigned int readFromUTF8(wchar_t *oString, unsigned int iWideSize, + const char *iBuffer, unsigned int iBufSize, + const string &iContext) +{ +#ifdef WIN32 + int res = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, iBuffer, + iBufSize, oString, iWideSize); + if (res == 0) + { + // Retrieve the system error message for the last-error code + throw DicException("readFromUTF8: MultiByteToWideChar failed (" + + iContext + "): " + GetWin32Error()); + } + return res; +#else + iconv_t handle = iconv_open("WCHAR_T", "UTF-8"); + if (handle == (iconv_t)(-1)) + throw DicException("readFromUTF8: iconv_open failed"); + size_t inChars = iBufSize; + size_t outChars = iWideSize * sizeof(wchar_t); + // Use the ICONV_CONST trick because the declaration of iconv() + // differs depending on the implementations... + ICONV_CONST char *in = const_cast(iBuffer); + char *out = (char*)oString; + size_t res = iconv(handle, &in, &inChars, &out, &outChars); + iconv_close(handle); + // Problem during encoding conversion? + if (res == (size_t)(-1)) + { + throw DicException("readFromUTF8: iconv failed (" + + iContext + "): " + string(strerror(errno))); + } + return iWideSize - outChars / sizeof(wchar_t); +#endif +} + + +wstring readFromUTF8(const char *iBuffer, unsigned int iBufSize, + const string &iContext) +{ + // Temporary buffer for output + // We will have at most as many characters as in the UTF-8 string + wchar_t *wideBuf = new wchar_t[iBufSize]; + unsigned int number; + try + { + number = readFromUTF8(wideBuf, iBufSize, iBuffer, iBufSize, iContext); + } + catch (...) + { + // Make sure not to leak + delete[] wideBuf; + throw; + } + // Copy the string + wstring res(wideBuf, number); + delete[] wideBuf; + return res; +} + + +unsigned int writeInUTF8(const wstring &iWString, char *oBuffer, + unsigned int iBufSize, const string &iContext) +{ +#ifdef WIN32 + int res = WideCharToMultiByte(CP_UTF8, 0, iWString.c_str(), iWString.size(), + oBuffer, iBufSize, NULL, NULL); + if (res == 0) + { + DWORD dw = GetLastError(); + cerr << dw << endl; + // Retrieve the system error message for the last-error code + throw DicException("writeInUTF8: WideCharToMultiByte failed (" + + iContext + "): " + GetWin32Error()); + } + return res; +#else + iconv_t handle = iconv_open("UTF-8", "WCHAR_T"); + if (handle == (iconv_t)(-1)) + throw DicException("writeInUTF8: iconv_open failed"); + size_t length = iWString.size(); + size_t inChars = sizeof(wchar_t) * length; + size_t outChars = iBufSize; + // Use the ICONV_CONST trick because the declaration of iconv() + // differs depending on the implementations... + // FIXME: bonus ugliness for doing 2 casts at once, and accessing string + // internals... + ICONV_CONST char *in = (ICONV_CONST char*)(&iWString[0]); + char *out = oBuffer; + size_t res = iconv(handle, &in, &inChars, &out, &outChars); + iconv_close(handle); + // Problem during encoding conversion? + if (res == (size_t)(-1)) + { + throw DicException("writeInUTF8: iconv failed (" + + iContext + ")" + string(strerror(errno))); + } + // Return the number of written bytes + return iBufSize - outChars; +#endif +} + diff --git a/dic/encoding.h b/dic/encoding.h new file mode 100644 index 0000000..997a408 --- /dev/null +++ b/dic/encoding.h @@ -0,0 +1,122 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file encoding.h + * \brief Utility functions to ease manipulation of wide-character strings + * \author Olivier Teuliere + * \date 2005 + */ + +#ifndef _ENCODING_H_ +#define _ENCODING_H_ + +#include + +using std::string; +using std::wstring; + + +/// Equivalent of atoi for wide-caracter strings +int _wtoi(const wchar_t *iWStr); + +/// Equivalent of swprintf, but working also with mingw32 +int _swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...); + +/// Equivalent of wcstok, but working also with mingw32 +wchar_t *_wcstok(wchar_t *wcs, const wchar_t *delim, wchar_t **ptr); + +/// Convert a multi-byte string into a wide-character string +wstring convertToWc(const string &iStr); + +/// Convert a wide-character string into a multi-byte string +string convertToMb(const wstring &iWStr); + +/// Convert a wide character into a multi-byte string +string convertToMb(wchar_t iWChar); + +/** + * Truncate the given string to ensure that the number of columns needed + * to display it is at most iMaxWidth. If the string is already less wide, + * it is returned without truncation + */ +string truncString(const string &iStr, unsigned int iMaxWidth); + +/** + * Convert the given string into a multi-byte one. If the number of columns + * needed to display the resulting string is more than iMaxWidth, truncate it + * on the right before conversion + */ +string truncAndConvert(const wstring &iWStr, unsigned int iMaxWidth); + +/** + * Make sure the displayed version of iStr has a width of iWidth. + * If the string is too long, truncate it, if it is too short, pad it + * with iChar + */ +string truncOrPad(const string &iStr, unsigned int iWidth, char iChar = ' '); + +/** + * Convert the given string into a multi-byte one. If the number of columns + * needed to display the resulting string is less than iLength, pad it with + * the given character (defaulting to space) + */ +string padAndConvert(const wstring &iWstr, unsigned int iLength, + bool iLeftPad = true, char c = ' '); + +/** + * Utility function to convert a char* buffer encoded in UTF-8 into a + * wchar_t* string + * @param oString: where to write the converted string + * @param iWideSize: size available in oString (number of wchar_t) + * @param iBuffer: UTF-8 string to convert + * @param iBufSize: available size in iBuffer + * @param iContext: free text used in case of exception + * @return: number of wide chars actually written + */ +unsigned int readFromUTF8(wchar_t *oString, unsigned int iWideSize, + const char *iBuffer, unsigned int iBufSize, + const string &iContext); + +/** + * Same as the other readFromUTF8 function, dealing with a wstring + * instead of a wchar_t*. Note that it performs an additional copy + * of the output string... + * @param iBuffer: UTF-8 string to convert + * @param iBufSize: available size in iBuffer + * @param iContext: free text used in case of exception + * @return: the converted wide string + */ +wstring readFromUTF8(const char *iBuffer, unsigned int iBufSize, + const string &iContext); + +/** + * Utility function to convert a wstring into an UTF-8 char* buffer + * @param iWString: the wide string to encode + * @param oBuffer: where to write the encoded string + * @param iBufSize: available size in oBuffer + * @param iContext: free text used in case of exception + * @return: number of bytes actually written + */ +unsigned int writeInUTF8(const wstring &iWString, char *oBuffer, + unsigned int iBufSize, const string &iContext); + +#endif + diff --git a/dic/erl.l b/dic/erl.l deleted file mode 100644 index 207ce9f..0000000 --- a/dic/erl.l +++ /dev/null @@ -1,58 +0,0 @@ -%{ -/* Eliot */ -/* Copyright (C) 2005 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Elit is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "dic.h" -#include "regexp.h" -#include "libdic_a-ery.h" - -#define MASK_TO_REMOVE 0x1F - -%} -%option prefix="regexp" -%option outfile="lex.yy.c" -%option header-file="libdic_a-erl.h" -%option reentrant bison-bridge -%option bison-locations -%option noyywrap nounput - -/* TODO : remove lexer translation */ -alphabet [a-zA-Z] -%% - -{alphabet} {yylval_param->c=(yytext[0]&MASK_TO_REMOVE); return LEX_CHAR;} -"[" {return LEX_L_SQBRACKET;} -"]" {return LEX_R_SQBRACKET;} -"(" {return LEX_L_BRACKET;} -")" {return LEX_R_BRACKET;} -"^" {return LEX_HAT;} - -"." {return LEX_ALL;} -":v:" {return LEX_VOWL;} -":c:" {return LEX_CONS;} -":1:" {return LEX_USER1;} -":2:" {return LEX_USER2;} - -"?" {return LEX_QMARK;} -"+" {return LEX_PLUS;} -"*" {return LEX_STAR;} - -"#" {return LEX_SHARP;} -%% - diff --git a/dic/erl.lpp b/dic/erl.lpp new file mode 100644 index 0000000..2d1e435 --- /dev/null +++ b/dic/erl.lpp @@ -0,0 +1,59 @@ +%{ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include "dic.h" +#include "regexp.h" +#include "libdic_a-ery.h" + +#define MASK_TO_REMOVE 0x1F + +%} +%option prefix="regexp" +%option outfile="lex.yy.c" +%option header-file="libdic_a-erl.h" +%option reentrant bison-bridge +%option bison-locations +%option noyywrap nounput + +/* TODO : remove lexer translation */ +alphabet [a-zA-Z] +%% + +{alphabet} {yylval_param->c=(yytext[0]&MASK_TO_REMOVE); return LEX_CHAR;} +"[" {return LEX_L_SQBRACKET;} +"]" {return LEX_R_SQBRACKET;} +"(" {return LEX_L_BRACKET;} +")" {return LEX_R_BRACKET;} +"^" {return LEX_HAT;} + +"." {return LEX_ALL;} +":v:" {return LEX_VOWL;} +":c:" {return LEX_CONS;} +":1:" {return LEX_USER1;} +":2:" {return LEX_USER2;} + +"?" {return LEX_QMARK;} +"+" {return LEX_PLUS;} +"*" {return LEX_STAR;} + +"#" {return LEX_SHARP;} +%% + diff --git a/dic/ery.y b/dic/ery.ypp similarity index 80% rename from dic/ery.y rename to dic/ery.ypp index 6b4ce4d..2a06707 100644 --- a/dic/ery.y +++ b/dic/ery.ypp @@ -1,27 +1,28 @@ %{ -/* Eliot */ -/* Copyright (C) 2005 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Elit is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ -#include +#include +#include +#include #include -#include -#include #include "dic.h" #include "regexp.h" diff --git a/dic/hashtable.c b/dic/hashtable.c deleted file mode 100644 index ed7969d..0000000 --- a/dic/hashtable.c +++ /dev/null @@ -1,163 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file hashtable.c - * \brief Simple hashtable type - * \author Antoine Fraboulet - * \date 1999 - */ - -#include -#include -#include -#include "hashtable.h" - -typedef struct _Hash_node { - struct _Hash_node *next; - void* key; - unsigned int keysize; - void* value; - unsigned int valuesize; -} Hash_node; - -struct _Hash_table { - unsigned int size; - Hash_node** nodes; -}; - - -Hash_table -hash_init(unsigned int size) -{ - Hash_table ht; - - ht = (Hash_table) calloc(1,sizeof(struct _Hash_table)); - ht->size = size; - ht->nodes = (Hash_node **) calloc (size, sizeof (Hash_node*)); - return ht; -} - -void -hash_rec_free(Hash_node* node) -{ - if (node) - { - if (node->next) - hash_rec_free(node->next); - if (node->key) - free(node->key); - if (node->value) - free(node->value); - free(node); - } -} - -int -hash_destroy(Hash_table hashtable) -{ - unsigned int i; - if (hashtable) - { - for(i=0; isize; i++) - if (hashtable->nodes[i]) - hash_rec_free(hashtable->nodes[i]); - if (hashtable->nodes) - free(hashtable->nodes); - free(hashtable); - } - return 0; -} - - -static unsigned int -hash_key(Hash_table hashtable, void* ptr, unsigned int size) -{ - unsigned int i; - unsigned int key = 0; - - if (size % 4 == 0) - { - unsigned int *v = (unsigned int*)ptr; - for (i = 0; i < (size / 4); i++) - key ^= (key << 3) ^ (key >> 1) ^ v[i]; - } - else - { - unsigned char *v = (unsigned char*)ptr; - for (i = 0; i < size; i++) - key ^= (key << 3) ^ (key >> 1) ^ v[i]; - } - key %= hashtable->size; - return key; -} - - -void* -hash_find(Hash_table hashtable, void* key, unsigned int keysize) -{ - Hash_node *entry; - unsigned int h_key; - - h_key = hash_key(hashtable,key,keysize); - for (entry = hashtable->nodes[h_key]; entry; entry = entry -> next) - { - if ((entry -> keysize == keysize) && - (memcmp(entry->key,key,keysize) == 0)) - { - return entry->value; - } - } - return NULL; -} - - -static Hash_node* -new_entry(void* key, unsigned int keysize, void* value, unsigned int - valuesize) -{ - Hash_node *n; - n = (Hash_node*)calloc(1,sizeof(Hash_node)); - n->key = (void*)malloc(keysize); - n->value = (void*)malloc(valuesize); - n->keysize = keysize; - n->valuesize = valuesize; - memcpy(n->key,key,keysize); - memcpy(n->value,value,valuesize); - return n; -} - - -int -hash_add(Hash_table hashtable, - void* key, unsigned int keysize, - void* value, unsigned int valuesize) -{ - Hash_node *entry; - unsigned int h_key; - - h_key = hash_key(hashtable,key,keysize); - entry = new_entry(key,keysize,value,valuesize); - entry->next = hashtable->nodes[h_key]; - hashtable->nodes[h_key] = entry; - - return 0; -} - - diff --git a/dic/hashtable.cpp b/dic/hashtable.cpp new file mode 100644 index 0000000..aa72b10 --- /dev/null +++ b/dic/hashtable.cpp @@ -0,0 +1,52 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file hashtable.c + * \brief Simple hashtable type + * \author Antoine Fraboulet + * \date 1999 + */ + +#include "hashtable.h" + + +unsigned int HashPtr(const void *iPtr, unsigned int iSize) +{ + unsigned int key = 0; + + if (iSize % sizeof(unsigned int) == 0) + { + const unsigned int *ptr = + reinterpret_cast(iPtr); + for (unsigned int i = 0; i < (iSize / sizeof(unsigned int)); ++i) + key ^= (key << 3) ^ (key >> 1) ^ ptr[i]; + } + else + { + const unsigned char *ptr = + reinterpret_cast(iPtr); + for (unsigned int i = 0; i < iSize; ++i) + key ^= (key << 3) ^ (key >> 1) ^ ptr[i]; + } + return key; +} + diff --git a/dic/hashtable.h b/dic/hashtable.h index a862e20..61f2577 100644 --- a/dic/hashtable.h +++ b/dic/hashtable.h @@ -1,21 +1,23 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file hashtable.h @@ -26,21 +28,55 @@ #ifndef _HASHTABLE_H #define _HASHTABLE_H -#if defined(__cplusplus) -extern "C" - { -#endif -typedef struct _Hash_table* Hash_table; -Hash_table hash_init(unsigned int); -int hash_destroy(Hash_table); -int hash_size(Hash_table); -void* hash_find(Hash_table,void* key,unsigned keysize); -int hash_add (Hash_table,void* key,unsigned keysize, - void* value,unsigned valuesize); +/// Compute a hash for the data pointed to by iPtr +/** + * This function is useful to define the HASH_FCN template parameter + * of HashTable. + */ +unsigned int HashPtr(const void *iPtr, unsigned int iSize); + + +template +class HashTable +{ + public: + /// Constructor taking the number of records in the table + HashTable(unsigned int iSize); + + /// Destructor + ~HashTable(); + + /// Return the number of records in the table + unsigned int size() const { return m_size; } + + /// Return the value corresponding to the given key, or NULL if not found + const VALUE *find(const KEY &iKey) const; + + /// Add a new key/value pair (both the key and the value are copied) + void add(const KEY& iKey, const VALUE &iValue); + + private: + /// Maximum number of records + unsigned int m_size; + + /// Definition of a record + class Node + { + public: + Node(const KEY &iKey, const VALUE &iValue, const Node *iNext); + ~Node(); + KEY m_key; + VALUE m_value; + const Node *m_next; + }; + + /// All the nodes + const Node **m_nodes; +}; + +// Include the implementation of the template +#include "hashtable.i" -#if defined(__cplusplus) - } -#endif #endif /* _HASHTABLE_H_ */ diff --git a/dic/hashtable.i b/dic/hashtable.i new file mode 100644 index 0000000..f682d2c --- /dev/null +++ b/dic/hashtable.i @@ -0,0 +1,88 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include + +#include "hashtable.h" + + +template +HashTable::HashTable(unsigned int iSize) + : m_size(iSize) +{ + m_nodes = new const Node*[m_size]; + for (unsigned int i = 0; i < m_size; ++i) + { + m_nodes[i] = NULL; + } +} + + +template +HashTable::~HashTable() +{ + for (unsigned int i = 0; i < m_size; ++i) + delete m_nodes[i]; + delete[] m_nodes; +} + + +template +HashTable::Node::Node(const KEY &iKey, const VALUE &iValue, const Node *iNext) + : m_key(iKey), m_value(iValue), m_next(iNext) +{ +} + + +template +HashTable::Node::~Node() +{ + delete m_next; +} + + +template +const VALUE *HashTable::find(const KEY &iKey) const +{ + HASH_FCN aHashFunc; + unsigned int h_key = aHashFunc(iKey) % m_size; + for (const Node *entry = m_nodes[h_key]; entry; entry = entry->m_next) + { + // Note: we need to be able to call == on a type KEY + if (entry->m_key == iKey) + { + return &entry->m_value; + } + } + return NULL; +} + + +template +void HashTable::add(const KEY &iKey, const VALUE &iValue) +{ + HASH_FCN aHashFunc; + unsigned int h_key = aHashFunc(iKey) % m_size; + const Node *entry = new Node(iKey, iValue, m_nodes[h_key]); + m_nodes[h_key] = entry; +} + +// vim: ft=cpp diff --git a/dic/header.cpp b/dic/header.cpp new file mode 100644 index 0000000..8064e93 --- /dev/null +++ b/dic/header.cpp @@ -0,0 +1,526 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include "config.h" + +#include +#include +#include + +// For ntohl & Co. +#ifdef WIN32 +# include +#else +# if HAVE_NETINET_IN_H +# include +# endif +# if HAVE_ARPA_INET_H +# include +# endif +#endif + +#if ENABLE_NLS +# include +# define _(String) gettext(String) +#else +# define _(String) String +#endif + +#include "header.h" +#include "encoding.h" +#include "dic_exception.h" + + +// Note: swap4 is duplicated in dic.cpp +#if defined(WORDS_BIGENDIAN) +static uint32_t swap4(uint32_t v) +{ + uint32_t r; + uint8_t *pv = (uint8_t*)&v; + uint8_t *pr = (uint8_t*)&r; + + pr[0] = pv[3]; + pr[1] = pv[2]; + pr[2] = pv[1]; + pr[3] = pv[0]; + + return r; +} + +// Nothing to do on big-endian machines +# define ntohll(x) (x) +# define htonll(x) (x) +#else +static inline uint64_t htonll(uint64_t host64) +{ + return (((uint64_t)htonl((host64 << 32) >> 32)) << 32) | htonl(host64 >> 32); +} + +static inline uint64_t ntohll(uint64_t net64) +{ + return htonll(net64); +} +#endif + + +/** + * Keyword included in dictionary headers + * Implies little endian storage on words + */ +#define _COMPIL_KEYWORD_ "_COMPILED_DICTIONARY_" + +/** Old format of the header (i.e. version 0) */ +struct Dict_header_old +{ + /// Identification string + char ident[sizeof(_COMPIL_KEYWORD_)]; + /// Version of the serialization format + uint8_t version; + /// Unused at the moment, reserved for further use + char unused; + uint32_t root; + uint32_t nwords; + /// Information about the compression + //@{ + uint32_t edgesused; + uint32_t nodesused; + uint32_t nodessaved; + uint32_t edgessaved; + //@} +}; + +// Do not change these values, as they impact the size of the structure! +// Note: they are chosen carefully to avoid alignment issues +#define _MAX_USER_HOST_ 32 +#define _MAX_DIC_NAME_SIZE_ 30 +#define _MAX_LETTERS_NB_ 63 +#define _MAX_LETTERS_SIZE_ 80 + +/** Extension of the old format (used in version 1)*/ +struct Dict_header_ext +{ + // Time when the dictionary was compressed + // (number of seconds since the Epoch) + uint64_t compressDate; + // Build information + char userHost[_MAX_USER_HOST_]; + // Size taken by the build information + uint32_t userHostSize; + + // --- we have a multiple of 64 bytes here + + // Compression algorithm (1 = DAWG, 2 = GADDAG) + uint8_t algorithm; + // Variant used in the rules (XXX: currently unused) + uint8_t variant; + + // --- we have a multiple of 64 bytes here + + // Dictionary official name and version (e.g.: ODS 5.0) + char dicName[_MAX_DIC_NAME_SIZE_]; + // Size taken by the dictionary name + uint32_t dicNameSize; + + // --- we have a multiple of 64 bytes here + + // Letters used in the dictionary + // We should have: nbLetters <= lettersSize <= _MAX_LETTERS_SIZE_ + // and: nbLetters <= _MAX_LETTERS_NB_ + // The letters themselves, in UTF-8 + char letters[_MAX_LETTERS_SIZE_]; + // Size taken by the letters + uint32_t lettersSize; + // Number of letters (XXX: in theory useless, but allows a sanity check) + uint32_t nbLetters; + + // --- we have a multiple of 64 bytes here + + // Points of the letters (indexed by their code) + // The "+ 1" is there for struct alignment + uint8_t points[_MAX_LETTERS_NB_ + 1]; + // Frequency of the letters (indexed by their code) + // The "+ 1" is there for struct alignment + uint8_t frequency[_MAX_LETTERS_NB_ + 1]; + // Bitfield indicating whether letters are vowels + uint64_t vowels; + // Bitfield indicating whether letters are consonants + uint64_t consonants; + + // --- we have a multiple of 64 bytes here +}; + + +Header::Header(istream &iStream) + : m_root(0), m_nbWords(0), m_nodesUsed(0), m_edgesUsed(0), + m_nodesSaved(0), m_edgesSaved(0), m_type(kDAWG) +{ + // Simply delegate to the read() method + // The code is not moved here because I find it more natural to have a + // read() method symmetrical to the write() one + read(iStream); + buildMapCodeFromChar(); +} + + +Header::Header(const DictHeaderInfo &iInfo) +{ + // Use the latest serialization format + m_version = 1; + + // Sanity checks + if (iInfo.letters.size() > _MAX_LETTERS_NB_) + { + ostringstream ss; + ss << _MAX_LETTERS_NB_; + throw DicException("Header::Header: Too many different letters for " + "the current format; only " + ss.str() + + " are supported"); + } + if (iInfo.points.size() != iInfo.letters.size()) + { + throw DicException("Header::Header: Different sizes for " + "iInfo.points and iInfo.letters"); + } + if (iInfo.frequency.size() != iInfo.letters.size()) + { + throw DicException("Header::Header: Different sizes for " + "iInfo.frequency and iInfo.letters"); + } + if (iInfo.vowels.size() != iInfo.letters.size()) + { + throw DicException("Header::Header: Different sizes for " + "iInfo.vowels and iInfo.letters"); + } + if (iInfo.consonants.size() != iInfo.letters.size()) + { + throw DicException("Header::Header: Different sizes for " + "iInfo.consonants and iInfo.letters"); + } + + m_compressDate = time(NULL); + m_userHost = convertToWc(ELIOT_COMPILE_BY + string("@") + ELIOT_COMPILE_HOST); + m_root = iInfo.root; + m_nbWords = iInfo.nwords; + m_nodesUsed = iInfo.nodesused; + m_edgesUsed = iInfo.edgesused; + m_nodesSaved = iInfo.nodessaved; + m_edgesSaved = iInfo.edgessaved; + m_type = iInfo.dawg ? kDAWG : kGADDAG; + m_dicName = iInfo.dicName; + m_letters = iInfo.letters; + m_points = iInfo.points; + m_frequency = iInfo.frequency; + m_vowels = iInfo.vowels; + m_consonants = iInfo.consonants; + + buildMapCodeFromChar(); +} + + +void Header::buildMapCodeFromChar() +{ + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + // We don't differentiate uppercase and lowercase letters + m_mapCodeFromChar[towlower(m_letters[i])] = i + 1; + m_mapCodeFromChar[towupper(m_letters[i])] = i + 1; + } +} + + +wchar_t Header::getCharFromCode(unsigned int iCode) const +{ + // Safety check + if (iCode == 0 || iCode > m_letters.size()) + { + ostringstream ss; + ss << iCode; + throw DicException("Header::getCharFromCode: no letter for code " + ss.str()); + } + return m_letters[iCode - 1]; +} + + +unsigned int Header::getCodeFromChar(wchar_t iChar) const +{ + map::const_iterator pair = + m_mapCodeFromChar.find(iChar); + if (pair == m_mapCodeFromChar.end()) + { + throw DicException("Header::getCodeFromChar: No code for letter " + + convertToMb(iChar)); + } + return pair->second; +} + + +void Header::read(istream &iStream) +{ + Dict_header_old aHeader; + iStream.read((char*)&aHeader, sizeof(Dict_header_old)); + if (iStream.gcount() != sizeof(Dict_header_old)) + throw DicException("Header::read: expected to read more bytes"); + + // Check the identification string + if (string(aHeader.ident) != _COMPIL_KEYWORD_) + throw DicException("Header::read: incorrect header keyword; is it a dictionary file?"); + + m_version = aHeader.version; + + // Handle endianness + if (m_version == 0) + { +#if defined(WORDS_BIGENDIAN) + aHeader.root = swap4(aHeader.root); + aHeader.nwords = swap4(aHeader.nwords); + aHeader.nodesused = swap4(aHeader.nodesused); + aHeader.edgesused = swap4(aHeader.edgesused); + aHeader.nodessaved = swap4(aHeader.nodessaved); + aHeader.edgessaved = swap4(aHeader.edgessaved); +#endif + m_root = aHeader.root; + m_nbWords = aHeader.nwords; + m_nodesUsed = aHeader.nodesused; + m_edgesUsed = aHeader.edgesused; + m_nodesSaved = aHeader.nodessaved; + m_edgesSaved = aHeader.edgessaved; + } + else + { + m_root = ntohl(aHeader.root); + m_nbWords = ntohl(aHeader.nwords); + m_nodesUsed = ntohl(aHeader.nodesused); + m_edgesUsed = ntohl(aHeader.edgesused); + m_nodesSaved = ntohl(aHeader.nodessaved); + m_edgesSaved = ntohl(aHeader.edgessaved); + } + + if (m_version == 0) + { + m_compressDate = 0; + m_userHost = convertToWc(_("Unknown (old format)")); + m_dicName = convertToWc(_("Unknown (old format)")); + + // In version 0, the letters, points, frequency, + // vowels and consonants were hard-coded... + m_letters = convertToWc("ABCDEFGHIJKLMNOPQRSTUVWXYZ?"); + + static const uint8_t Frenchpoints[] = + { + // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? + 1,3,3,2, 1,4,2,4,1,8,10,1,2,1,1,3,8,1,1,1,1,4,10,10,10,10,0 + }; + + static const uint8_t FrenchFrequency[] = + { + // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? + 9,2,2,3,15,2,2,2,8,1, 1,5,3,6,6,2,1,6,6,6,6,2, 1, 1, 1, 1,2 + }; + + // The jokers and the 'Y' can be considered both as vowels or consonants + static const uint8_t FrenchVowels[] = + { + // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? + 1,0,0,0, 1,0,0,0,1,0, 0,0,0,0,1,0,0,0,0,0,1,0, 0, 0, 1, 0,1 + }; + + static const uint8_t FrenchConsonants[] = + { + // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? + 0,1,1,1, 0,1,1,1,0,1, 1,1,1,1,0,1,1,1,1,1,0,1, 1, 1, 1, 1,1 + }; + + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + m_points.push_back(Frenchpoints[i]); + m_frequency.push_back(FrenchFrequency[i]); + m_vowels.push_back(FrenchVowels[i]); + m_consonants.push_back(FrenchConsonants[i]); + } + } + else + { + // This header doesn't use the old serialization format, so read the + // extension as well + Dict_header_ext aHeaderExt; + iStream.read((char*)&aHeaderExt, sizeof(Dict_header_ext)); + if (iStream.gcount() != sizeof(Dict_header_ext)) + throw DicException("Header::read: expected to read more bytes"); + + // Handle endianness in the extension + aHeaderExt.compressDate = ntohll(aHeaderExt.compressDate); + aHeaderExt.userHostSize = ntohl(aHeaderExt.userHostSize); + aHeaderExt.dicNameSize = ntohl(aHeaderExt.dicNameSize); + aHeaderExt.lettersSize = ntohl(aHeaderExt.lettersSize); + aHeaderExt.nbLetters = ntohl(aHeaderExt.nbLetters); + aHeaderExt.vowels = ntohll(aHeaderExt.vowels); + aHeaderExt.consonants = ntohll(aHeaderExt.consonants); + + m_compressDate = aHeaderExt.compressDate; + + if (aHeaderExt.algorithm == kDAWG) + m_type = kDAWG; + else if (aHeaderExt.algorithm == kGADDAG) + m_type = kGADDAG; + else + throw DicException("Header::read: unrecognized algorithm type"); + + m_userHost = readFromUTF8(aHeaderExt.userHost, aHeaderExt.userHostSize, + "user and host information"); + + // Convert the dictionary letters from UTF-8 to wchar_t* + m_dicName = readFromUTF8(aHeaderExt.dicName, aHeaderExt.dicNameSize, + "dictionary name"); + + // Convert the dictionary letters from UTF-8 to wchar_t* + m_letters = readFromUTF8(aHeaderExt.letters, aHeaderExt.lettersSize, + "dictionary letters"); + // Safety check: correct number of letters? + if (m_letters.size() != aHeaderExt.nbLetters) + { + throw DicException("Header::read: inconsistent header"); + } + + // Letters points and frequency + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + m_points.push_back(aHeaderExt.points[i]); + m_frequency.push_back(aHeaderExt.frequency[i]); + } + + // Vowels and consonants + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + m_vowels.push_back(aHeaderExt.vowels & (1 << i)); + m_consonants.push_back(aHeaderExt.consonants & (1 << i)); + } + + } +} + + +void Header::write(ostream &oStream) const +{ + Dict_header_old aHeader; + strcpy(aHeader.ident, _COMPIL_KEYWORD_); + aHeader.version = m_version; + aHeader.unused = 0; + aHeader.root = htonl(m_root); + aHeader.nwords = htonl(m_nbWords); + aHeader.nodesused = htonl(m_nodesUsed); + aHeader.edgesused = htonl(m_edgesUsed); + aHeader.nodessaved = htonl(m_nodesSaved); + aHeader.edgessaved = htonl(m_edgesSaved); + + oStream.write((char*)&aHeader, sizeof(Dict_header_old)); + if (!oStream.good()) + throw DicException("Header::write: error when writing to file"); + + if (m_version != 0) + { + Dict_header_ext aHeaderExt; + aHeaderExt.compressDate = m_compressDate; + aHeaderExt.userHostSize = + writeInUTF8(m_userHost, aHeaderExt.userHost, + _MAX_USER_HOST_, "user and host information"); + aHeaderExt.algorithm = m_type; + + // Convert the dictionary name to UTF-8 + aHeaderExt.dicNameSize = + writeInUTF8(m_dicName, aHeaderExt.dicName, + _MAX_DIC_NAME_SIZE_, "dictionary name"); + + // Convert the dictionary letters to UTF-8 + aHeaderExt.lettersSize = + writeInUTF8(m_letters, aHeaderExt.letters, + _MAX_LETTERS_SIZE_, "dictionary letters"); + aHeaderExt.nbLetters = (uint32_t)m_letters.size(); + + // Letters points and frequency + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + aHeaderExt.points[i] = m_points[i]; + aHeaderExt.frequency[i] = m_frequency[i]; + } + + // Vowels and consonants + aHeaderExt.vowels = 0; + aHeaderExt.consonants = 0; + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + if (m_vowels[i]) + aHeaderExt.vowels |= 1 << i; + if (m_consonants[i]) + aHeaderExt.consonants |= 1 << i; + } + + // Handle endianness in the extension + aHeaderExt.userHostSize = htonl(aHeaderExt.userHostSize); + aHeaderExt.compressDate = htonll(aHeaderExt.compressDate); + aHeaderExt.dicNameSize = htonl(aHeaderExt.dicNameSize); + aHeaderExt.lettersSize = htonl(aHeaderExt.lettersSize); + aHeaderExt.nbLetters = htonl(aHeaderExt.nbLetters); + aHeaderExt.vowels = htonll(aHeaderExt.vowels); + aHeaderExt.consonants = htonll(aHeaderExt.consonants); + + // Write the extension + oStream.write((char*)&aHeaderExt, sizeof(Dict_header_ext)); + if (!oStream.good()) + throw DicException("Header::write: error when writing to file"); + } +} + + +void Header::print() const +{ + printf(_("dictionary name: %s\n"), convertToMb(m_dicName).c_str()); + if (m_version) + { + char buf[50]; + strftime(buf, sizeof(buf), "%c", gmtime(&m_compressDate)); + printf(_("compressed on: %s\n"), buf); + } + else + { + printf(_("compressed on: Unknown date (old format)\n")); + } + printf(_("compressed using a binary compiled by: %s\n"), convertToMb(m_userHost).c_str()); + printf(_("dictionary type: %s\n"), m_type == kDAWG ? "DAWG" : "GADDAG"); + printf(_("letters: %s\n"), convertToMb(m_letters).c_str()); + printf(_("number of letters: %lu\n"), (long unsigned int)m_letters.size()); + printf(_("number of words: %d\n"), m_nbWords); + long unsigned int size = + sizeof(Dict_header_old) + (m_version ? sizeof(Dict_header_ext) : 0); + printf(_("header size: %lu bytes\n"), size); + printf(_("root: %d (edge)\n"), m_root); + printf(_("nodes: %d used + %d saved\n"), m_nodesUsed, m_nodesSaved); + printf(_("edges: %d used + %d saved\n"), m_edgesUsed, m_edgesSaved); + printf("===============================================\n"); + printf(_("letter | points | frequency | vowel | consonant\n")); + printf("-------+--------+-----------+-------+----------\n"); + for (unsigned int i = 0; i < m_letters.size(); ++i) + { + printf(" %s | %2d | %2d | %d | %d\n", + padAndConvert(wstring(1, m_letters[i]), 2).c_str(), + m_points[i], m_frequency[i], m_vowels[i], m_consonants[i]); + } + printf("===============================================\n"); +} + diff --git a/dic/header.h b/dic/header.h new file mode 100644 index 0000000..b8874a7 --- /dev/null +++ b/dic/header.h @@ -0,0 +1,191 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#ifndef _HEADER_H +#define _HEADER_H + +#include +#include +#include +#include + +using namespace std; + + +/** + * Structure used to create a Header object. + * Note: this structure doesn't pretend to map the way the data is stored in a + * file. For (de)serialization, always use a Header object + */ +struct DictHeaderInfo +{ + uint32_t root; + uint32_t nwords; + uint32_t edgesused; + uint32_t nodesused; + uint32_t nodessaved; + uint32_t edgessaved; + bool dawg; + wstring dicName; + wstring letters; + vector points; + vector frequency; + vector vowels; + vector consonants; +}; + + +/** + * Dictionary header. + * There are 2 ways to create a Header object: + * - fill a DictHeaderInfo structure and give it in the constructor of the + * Header class (usually to call write() afterwards) + * - give it an input stream on a compiled dictionary + * + * The class is immutable: a Header object cannot be modified after creation. + * + * Serialization: + * Several formats of headers will be handled by this class, even though only + * the first one is handled at the moment. You can use the write() method to + * write the latest version of the header into a given file. + * When using the constructor taking an input stream, all the header versions + * are supported. + */ +class Header +{ +public: + + /// Dictionary type + enum DictType + { + kDAWG = 1, + kGADDAG = 2 + }; + + /** + * Constructor from an input stream + * @param iStream: Input stream where to read the header + */ + Header(istream &iStream); + + /** + * Constructor from a filled structure + * @param iInfo: info needed to build the header + */ + Header(const DictHeaderInfo &iInfo); + + /// Return the version of the dictionary format + uint8_t getVersion() const { return m_version; } + + /// Getters + //@{ + unsigned int getRoot() const { return m_root; } + unsigned int getNbWords() const { return m_nbWords; } + unsigned int getNbNodesUsed() const { return m_nodesUsed; } + unsigned int getNbEdgesUsed() const { return m_edgesUsed; } + unsigned int getNbNodesSaved() const { return m_nodesSaved; } + unsigned int getNbEdgesSaved() const { return m_edgesSaved; } + DictType getType() const { return m_type; } + wstring getLetters() const { return m_letters; } + uint8_t getPoints(unsigned int iCode) const { return m_points[iCode - 1]; } + uint8_t getFrequency(unsigned int iCode) const { return m_frequency[iCode - 1]; } + bool isVowel(unsigned int iCode) const { return m_vowels[iCode - 1]; } + bool isConsonant(unsigned int iCode) const { return m_consonants[iCode - 1]; } + //@} + + /** + * Return the letter corresponding to the given code + */ + wchar_t getCharFromCode(unsigned int iCode) const; + + /** + * Return the code corresponding to the given letter + */ + unsigned int getCodeFromChar(wchar_t iChar) const; + + /** + * Print a readable summary of the header on standard output + */ + void print() const; + + /** + * Write the header to a file, using the latest format + * @param oStream: Output stream where to write the header + * @exception: Throw a DicException in case of problem + */ + void write(ostream &oStream) const; + +private: + /// Version of the serialization + uint8_t m_version; + + wstring m_userHost; + time_t m_compressDate; + + uint32_t m_root; + uint32_t m_nbWords; + uint32_t m_nodesUsed; + uint32_t m_edgesUsed; + uint32_t m_nodesSaved; + uint32_t m_edgesSaved; + + /// Specify whether the dictionary is a DAWG or a GADDAG + DictType m_type; + + /// Dictionary name (e.g.: ODS 5.0) + wstring m_dicName; + + /// The letters constituting the words of the dictionary + wstring m_letters; + + /// Points of the letters + vector m_points; + + /// Frequency of the letters + vector m_frequency; + + /// Vowels + vector m_vowels; + + /// Consonants + vector m_consonants; + + map m_mapCodeFromChar; + + /** + * Load the header from a file + * @param iStream: Input stream where to read the header + * @exception: Throw a DicException in case of problem + */ + void read(istream &iStream); + + /** Build m_mapCodeFromChar */ + void buildMapCodeFromChar(); +}; + + +#endif /* _HEADER_H */ + +/// Local Variables: +/// mode: c++ +/// mode: hs-minor +/// c-basic-offset: 4 +/// indent-tabs-mode: nil +/// End: diff --git a/dic/listdic.c b/dic/listdic.c deleted file mode 100644 index e93007e..0000000 --- a/dic/listdic.c +++ /dev/null @@ -1,207 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file listdic.c - * \brief Program used to list a dictionary - * \author Antoine Fraboulet - * \date 1999 - */ - -#include -#include -#include -#include -#include "dic_internals.h" -#include "dic.h" - - -static void -print_dic_rec(FILE* out, Dictionary dic, char *buf, char* s, Dawg_edge i) -{ - if (i.term) /* edge points at a complete word */ - { - *s = '\0'; - fprintf (out,"%s\n", buf); - } - if (i.ptr) - { /* Compute index: is it non-zero ? */ - Dawg_edge *p = dic->dawg + i.ptr; - do { /* for each edge out of this node */ - *s = p->chr + 'a' - 1; - print_dic_rec (out,dic,buf,s + 1, *p); - } - while (!(*p++).last); - } -} - - -void -dic_load(Dictionary* dic, char* filename) -{ - int res; - if ((res = Dic_load(dic, filename)) != 0) - { - switch (res) { - case 1: printf("chargement: problème d'ouverture de %s\n",filename); break; - case 2: printf("chargement: mauvais en-tete de dictionnaire\n"); break; - case 3: printf("chargement: problème 3 d'allocation mémoire\n"); break; - case 4: printf("chargement: problème 4 d'alocation mémoire\n"); break; - case 5: printf("chargement: problème de lecture des arcs du dictionnaire\n"); break; - default: printf("chargement: problème non-repertorié\n"); break; - } - exit(res); - } -} - - -void -print_dic_list(char* filename, char* out) -{ - FILE* fout; - Dictionary dic; - static char buf[80]; - - dic_load(&dic,filename); - - if (strcmp(out,"stdout") == 0) - print_dic_rec(stdout,dic,buf,buf,dic->dawg[dic->root]); - else if (strcmp(out,"stderr") == 0) - print_dic_rec(stderr,dic,buf,buf,dic->dawg[dic->root]); - else - { - if ((fout = fopen(out,"w")) == NULL) - return; - print_dic_rec(fout,dic,buf,buf,dic->dawg[dic->root]); - fclose(fout); - } - Dic_destroy(dic); -} - - -void -print_header(char* filename) -{ - Dict_header header; - - Dic_check_header(&header,filename); - -#define OO(IDENT) (unsigned long)offsetof(Dict_header,IDENT) - - printf("Dictionary header information\n"); - printf("0x%02lx ident : %s\n", OO(ident) ,header.ident); - printf("0x%02lx unused 1 : %6d %06x\n",OO(unused_1) ,header.unused_1 ,header.unused_1); - printf("0x%02lx unused 2 : %6d %06x\n",OO(unused_2) ,header.unused_2 ,header.unused_2); - printf("0x%02lx root : %6d %06x\n",OO(root) ,header.root ,header.root); - printf("0x%02lx words : %6d %06x\n",OO(nwords) ,header.nwords ,header.nwords); - printf("0x%02lx edges used : %6d %06x\n",OO(edgesused) ,header.edgesused ,header.edgesused); - printf("0x%02lx nodes used : %6d %06x\n",OO(nodesused) ,header.nodesused ,header.nodesused); - printf("0x%02lx nodes saved : %6d %06x\n",OO(nodessaved),header.nodessaved,header.nodessaved); - printf("0x%02lx edges saved : %6d %06x\n",OO(edgessaved),header.edgessaved,header.edgessaved); - printf("\n"); - printf("sizeof(header) = 0x%lx (%lu)\n", (unsigned long)sizeof(header), (unsigned long)sizeof(header)); -} - - -static void -print_node_hex(Dictionary dic, int i) -{ - union edge_t { - Dawg_edge e; - uint32_t s; - } ee; - - ee.e = dic->dawg[i]; - - printf("0x%04lx %08x |%4d ptr=%8d t=%d l=%d f=%d chr=%2d (%c)\n", - (unsigned long)i*sizeof(ee), (unsigned int)(ee.s), - i, ee.e.ptr, ee.e.term, ee.e.last, ee.e.fill, ee.e.chr, ee.e.chr +'a' -1); -} - - -void -print_dic_hex(char* filename) -{ - int i; - Dictionary dic; - dic_load(&dic,filename); - - printf("offs binary structure \n"); - printf("---- -------- | ------------------\n"); - for(i=0; i < (dic->nedges + 1); i++) - print_node_hex(dic,i); - Dic_destroy(dic); -} - - -void -usage(char* name) -{ - printf("usage: %s [-a|-d|-h|-l] dictionnaire\n", name); - printf(" -a : print all\n"); - printf(" -h : print header\n"); - printf(" -d : print dic in hex\n"); - printf(" -l : print dic word list\n"); -} - - -int -main(int argc, char *argv[]) -{ - int arg_count; - int option_print_all = 0; - int option_print_header = 0; - int option_print_dic_hex = 0; - int option_print_dic_list = 0; - - if (argc < 3) - { - usage(argv[0]); - exit(1); - } - - arg_count = 1; - while(argv[arg_count][0] == '-') - { - switch (argv[arg_count][1]) - { - case 'a': option_print_all = 1; break; - case 'h': option_print_header = 1; break; - case 'd': option_print_dic_hex = 1; break; - case 'l': option_print_dic_list = 1; break; - default: usage(argv[0]); exit(2); - break; - } - arg_count++; - } - - if (option_print_header || option_print_all) - { - print_header(argv[arg_count]); - } - if (option_print_dic_hex || option_print_all) - { - print_dic_hex(argv[arg_count]); - } - if (option_print_dic_list || option_print_all) - { - print_dic_list(argv[arg_count],"stdout"); - } - return 0; -} diff --git a/dic/listdic.cpp b/dic/listdic.cpp new file mode 100644 index 0000000..826d02e --- /dev/null +++ b/dic/listdic.cpp @@ -0,0 +1,187 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file listdic.c + * \brief Program used to list a dictionary + * \author Antoine Fraboulet + * \date 1999 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#if ENABLE_NLS +# include +# define _(String) gettext(String) +#else +# define _(String) String +#endif + +#include "header.h" +#include "encoding.h" +#include "dic_internals.h" +#include "dic.h" + +using namespace std; + + +template +static void print_dic_rec(ostream &out, const Dictionary &iDic, wchar_t *buf, wchar_t *s, DAWG_EDGE i) +{ + if (i.term) /* edge points at a complete word */ + { + *s = '\0'; + out << convertToMb(buf) << endl; + } + if (i.ptr) + { /* Compute index: is it non-zero ? */ + const DAWG_EDGE *p = reinterpret_cast(iDic.getEdgeAt(i.ptr)); + do + { /* for each edge out of this node */ + *s = iDic.getHeader().getCharFromCode(p->chr); + print_dic_rec(out, iDic, buf, s + 1, *p); + } + while (!(*p++).last); + } +} + + +template +void print_dic_list(const Dictionary &iDic) +{ + static wchar_t buf[80]; + print_dic_rec(cout, iDic, buf, buf, *reinterpret_cast(iDic.getEdgeAt(iDic.getRoot()))); +} + + +template +static void print_node_hex(const Dictionary &dic, int i) +{ + union edge_t + { + DAWG_EDGE e; + uint32_t s; + } ee; + + ee.e = *reinterpret_cast(dic.getEdgeAt(i)); + + printf("0x%04lx %08x |%4d ptr=%8d t=%d l=%d chr=%2d (%c)\n", + (unsigned long)i*sizeof(ee), (unsigned int)(ee.s), + i, ee.e.ptr, ee.e.term, ee.e.last, ee.e.chr, ee.e.chr +'a' -1); +} + + +template +void print_dic_hex(const Dictionary &iDic) +{ + printf(_("offset binary | structure\n")); + printf("------ -------- | --------------------\n"); + for (unsigned int i = 0; i < (iDic.getHeader().getNbEdgesUsed() + 1); i++) + print_node_hex(iDic, i); +} + + +void usage(const string &iName) +{ + printf(_("usage: %s [-a|-h|-l|-x] dictionary\n"), iName.c_str()); + printf(_(" -a: print all\n")); + printf(_(" -h: print header\n")); + printf(_(" -l: print dictionary word list\n")); + printf(_(" -x: print dictionary in hex\n")); +} + + +int main(int argc, char *argv[]) +{ +#if HAVE_SETLOCALE + // Set locale via LC_ALL + setlocale(LC_ALL, ""); +#endif + +#if ENABLE_NLS + // Set the message domain + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + int arg_count; + int option_print_all = 0; + int option_print_header = 0; + int option_print_dic_hex = 0; + int option_print_dic_list = 0; + + if (argc < 3) + { + usage(argv[0]); + exit(1); + } + + arg_count = 1; + while (argv[arg_count][0] == '-') + { + switch (argv[arg_count][1]) + { + case 'a': option_print_all = 1; break; + case 'h': option_print_header = 1; break; + case 'l': option_print_dic_list = 1; break; + case 'x': option_print_dic_hex = 1; break; + default: usage(argv[0]); exit(2); break; + } + arg_count++; + } + + try + { + Dictionary dic(argv[arg_count]); + + if (option_print_header || option_print_all) + { + dic.getHeader().print(); + } + if (option_print_dic_hex || option_print_all) + { + if (dic.getHeader().getVersion() == 0) + print_dic_hex(dic); + else + print_dic_hex(dic); + } + if (option_print_dic_list || option_print_all) + { + if (dic.getHeader().getVersion() == 0) + print_dic_list(dic); + else + print_dic_list(dic); + } + return 0; + } + catch (std::exception &e) + { + cerr << e.what() << endl; + return 1; + } +} diff --git a/dic/regexp.c b/dic/regexp.c deleted file mode 100644 index eb6a2ef..0000000 --- a/dic/regexp.c +++ /dev/null @@ -1,382 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file regexp.c - * \brief Regular Expression functions - * \author Antoine Fraboulet - * \date 2005 - */ - -#include "config.h" -#include -#include -#include -#ifdef HAVE_SYS_WAIT_H -# include -#endif -#include - -#include "dic.h" -#include "regexp.h" -#include "automaton.h" - -#ifndef PDBG -#ifdef DEBUG_RE2 -#define PDBG(x) x -#else -#define PDBG(x) -#endif -#endif - -NODE* regexp_createNODE(int type,char v,NODE *fg,NODE *fd) -{ - NODE *x; - x=(NODE *)malloc(sizeof(NODE)); - x->type = type; - x->var = v; - x->fd = fd; - x->fg = fg; - x->numero = 0; - x->position = 0; - x->annulable = 0; - x->PP = 0; - x->DP = 0; - return x; -} - -void regexp_delete_tree(NODE *root) -{ - if (root == NULL) - return; - regexp_delete_tree(root->fg); - regexp_delete_tree(root->fd); - free(root); -} - -#ifdef DEBUG_RE -static void print_node(FILE*, NODE *n, int detail); -#endif - -/** - * computes position, annulable, PP, DP attributes - * @param r = root - * @param p = current leaf position - * @param n = current node number - * @param ptl = position to letter - */ - -void regexp_parcours(NODE* r, int *p, int *n, int ptl[]) -{ - if (r == NULL) - return; - - regexp_parcours(r->fg,p,n,ptl); - regexp_parcours(r->fd,p,n,ptl); - - switch (r->type) - { - case NODE_VAR: - r->position = *p; - ptl[*p] = r->var; - *p = *p + 1; - r->annulable = 0; - r->PP = 1 << (r->position - 1); - r->DP = 1 << (r->position - 1); - break; - case NODE_OR: - r->position = 0; - r->annulable = r->fg->annulable || r->fd->annulable; - r->PP = r->fg->PP | r->fd->PP; - r->DP = r->fg->DP | r->fd->DP; - break; - case NODE_AND: - r->position = 0; - r->annulable = r->fg->annulable && r->fd->annulable; - r->PP = (r->fg->annulable) ? (r->fg->PP | r->fd->PP) : r->fg->PP; - r->DP = (r->fd->annulable) ? (r->fg->DP | r->fd->DP) : r->fd->DP; - break; - case NODE_PLUS: - r->position = 0; - r->annulable = 0; - r->PP = r->fg->PP; - r->DP = r->fg->DP; - break; - case NODE_STAR: - r->position = 0; - r->annulable = 1; - r->PP = r->fg->PP; - r->DP = r->fg->DP; - break; - } - - r->numero = *n; - *n = *n + 1; -} - -/** - * computes possuivante - * @param r = root - * @param PS = next position - */ - -void regexp_possuivante(NODE* r, int PS[]) -{ - int pos; - if (r == NULL) - return; - - regexp_possuivante(r->fg,PS); - regexp_possuivante(r->fd,PS); - - switch (r->type) - { - case NODE_AND: - /************************************/ - /* \forall p \in DP(left) */ - /* PS[p] = PS[p] \cup PP(right) */ - /************************************/ - for(pos=1; pos <= PS[0]; pos++) - { - if (r->fg->DP & (1 << (pos-1))) - PS[pos] |= r->fd->PP; - } - break; - case NODE_PLUS: - /************************************/ - /* == same as START */ - /* \forall p \in DP(left) */ - /* PS[p] = PS[p] \cup PP(left) */ - /************************************/ - for(pos=1; pos <= PS[0]; pos++) - { - if (r->DP & (1 << (pos-1))) - PS[pos] |= r->PP; - } - break; - case NODE_STAR: - /************************************/ - /* \forall p \in DP(left) */ - /* PS[p] = PS[p] \cup PP(left) */ - /************************************/ - for(pos=1; pos <= PS[0]; pos++) - { - if (r->DP & (1 << (pos-1))) - PS[pos] |= r->PP; - } - break; - } -} - -/*//////////////////////////////////////////////// -// DEBUG only fonctions -////////////////////////////////////////////////*/ - -#ifdef DEBUG_RE -void regexp_print_PS(int PS[]) -{ - int i; - printf("** positions suivantes **\n"); - for(i=1; i <= PS[0]; i++) - { - printf("%02d: 0x%08x\n", i, PS[i]); - } -} -#endif - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -#ifdef DEBUG_RE -void regexp_print_ptl(int ptl[]) -{ - int i; - printf("** pos -> lettre: "); - for(i=1; i <= ptl[0]; i++) - { - printf("%d=%c ",i,ptl[i]); - } - printf("\n"); -} -#endif - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -void regexp_print_letter(FILE* f, char l) -{ - switch (l) - { - case RE_EPSILON: fprintf(f,"( & [%d])",l); break; - case RE_FINAL_TOK: fprintf(f,"( # [%d])",l); break; - case RE_ALL_MATCH: fprintf(f,"( . [%d])",l); break; - case RE_VOWL_MATCH: fprintf(f,"(:v: [%d])",l); break; - case RE_CONS_MATCH: fprintf(f,"(:c: [%d])",l); break; - case RE_USR1_MATCH: fprintf(f,"(:1: [%d])",l); break; - case RE_USR2_MATCH: fprintf(f,"(:2: [%d])",l); break; - default: - if (l < RE_FINAL_TOK) - fprintf(f," (%c [%d]) ",l + 'a' - 1, l); - else - fprintf(f," (liste %d)",l - RE_LIST_USER_END); - break; - } -} - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -void regexp_print_letter2(FILE* f, char l) -{ - switch (l) - { - case RE_EPSILON: fprintf(f,"&"); break; - case RE_FINAL_TOK: fprintf(f,"#"); break; - case RE_ALL_MATCH: fprintf(f,"."); break; - case RE_VOWL_MATCH: fprintf(f,":v:"); break; - case RE_CONS_MATCH: fprintf(f,":c:"); break; - case RE_USR1_MATCH: fprintf(f,":1:"); break; - case RE_USR2_MATCH: fprintf(f,":2:"); break; - default: - if (l < RE_FINAL_TOK) - fprintf(f,"%c",l + 'a' - 1); - else - fprintf(f,"l%d",l - RE_LIST_USER_END); - break; - } -} - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -#ifdef DEBUG_RE -static void print_node(FILE* f, NODE *n, int detail) -{ - if (n == NULL) - return; - - switch (n->type) - { - case NODE_VAR: - regexp_print_letter(f,n->var); - break; - case NODE_OR: - fprintf(f,"OR"); - break; - case NODE_AND: - fprintf(f,"AND"); - break; - case NODE_PLUS: - fprintf(f,"+"); - break; - case NODE_STAR: - fprintf(f,"*"); - break; - } - if (detail == 2) - { - fprintf(f,"\\n pos=%d\\n annul=%d\\n PP=0x%04x\\n DP=0x%04x", - n->position,n->annulable,n->PP,n->DP); - } -} -#endif - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -#ifdef DEBUG_RE -static void print_tree_nodes(FILE* f, NODE* n, int detail) -{ - if (n == NULL) - return; - - print_tree_nodes(f,n->fg,detail); - print_tree_nodes(f,n->fd,detail); - - fprintf(f,"%d [ label=\"",n->numero); - print_node(f,n,detail); - fprintf(f,"\"];\n"); -} -#endif - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -#ifdef DEBUG_RE -static void print_tree_edges(FILE* f, NODE* n) -{ - if (n == NULL) - return; - - print_tree_edges(f,n->fg); - print_tree_edges(f,n->fd); - - switch (n->type) - { - case NODE_OR: - fprintf(f,"%d -> %d;",n->numero,n->fg->numero); - fprintf(f,"%d -> %d;",n->numero,n->fd->numero); - break; - case NODE_AND: - fprintf(f,"%d -> %d;",n->numero,n->fg->numero); - fprintf(f,"%d -> %d;",n->numero,n->fd->numero); - break; - case NODE_PLUS: - case NODE_STAR: - fprintf(f,"%d -> %d;",n->numero,n->fg->numero); - break; - } -} -#endif - -/*//////////////////////////////////////////////// -////////////////////////////////////////////////*/ - -#ifdef DEBUG_RE -void regexp_print_tree(NODE* n, char* name, int detail) -{ - FILE* f; - pid_t pid; - - f=fopen(name,"w"); - fprintf(f,"digraph %s {\n",name); - print_tree_nodes(f,n,detail); - print_tree_edges(f,n); - fprintf(f,"fontsize=20;\n"); - fprintf(f,"}\n"); - fclose(f); - -#ifdef HAVE_SYS_WAIT_H - pid = fork (); - if (pid > 0) { - wait(NULL); - } else if (pid == 0) { - execlp("dotty","dotty",name,NULL); - printf("exec dotty failed\n"); - exit(1); - } -#endif -} -#endif - - -/// Local Variables: -/// mode: hs-minor -/// c-basic-offset: 2 -/// End: diff --git a/dic/regexp.cpp b/dic/regexp.cpp new file mode 100644 index 0000000..1c2179f --- /dev/null +++ b/dic/regexp.cpp @@ -0,0 +1,384 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2006 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file regexp.c + * \brief Regular Expression functions + * \author Antoine Fraboulet + * \date 2005 + */ + +#include "config.h" + +#include +#include +#include +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#include + +#include "dic.h" +#include "regexp.h" +#include "automaton.h" + +#ifndef PDBG +#ifdef DEBUG_RE2 +#define PDBG(x) x +#else +#define PDBG(x) +#endif +#endif + + +NODE* regexp_createNODE(int type, char v, NODE *fg, NODE *fd) +{ + NODE *x; + x=(NODE *)malloc(sizeof(NODE)); + x->type = type; + x->var = v; + x->fd = fd; + x->fg = fg; + x->number = 0; + x->position = 0; + x->annulable = 0; + x->PP = 0; + x->DP = 0; + return x; +} + +void regexp_delete_tree(NODE *root) +{ + if (root == NULL) + return; + regexp_delete_tree(root->fg); + regexp_delete_tree(root->fd); + free(root); +} + +#ifdef DEBUG_RE +static void print_node(FILE*, NODE *n, int detail); +#endif + +/** + * computes position, annulable, PP, DP attributes + * @param r = root + * @param p = current leaf position + * @param n = current node number + * @param ptl = position to letter + */ + +void regexp_parcours(NODE* r, int *p, int *n, int ptl[]) +{ + if (r == NULL) + return; + + regexp_parcours(r->fg, p, n, ptl); + regexp_parcours(r->fd, p, n, ptl); + + switch (r->type) + { + case NODE_VAR: + r->position = *p; + ptl[*p] = r->var; + *p = *p + 1; + r->annulable = 0; + r->PP = 1 << (r->position - 1); + r->DP = 1 << (r->position - 1); + break; + case NODE_OR: + r->position = 0; + r->annulable = r->fg->annulable || r->fd->annulable; + r->PP = r->fg->PP | r->fd->PP; + r->DP = r->fg->DP | r->fd->DP; + break; + case NODE_AND: + r->position = 0; + r->annulable = r->fg->annulable && r->fd->annulable; + r->PP = (r->fg->annulable) ? (r->fg->PP | r->fd->PP) : r->fg->PP; + r->DP = (r->fd->annulable) ? (r->fg->DP | r->fd->DP) : r->fd->DP; + break; + case NODE_PLUS: + r->position = 0; + r->annulable = 0; + r->PP = r->fg->PP; + r->DP = r->fg->DP; + break; + case NODE_STAR: + r->position = 0; + r->annulable = 1; + r->PP = r->fg->PP; + r->DP = r->fg->DP; + break; + } + + r->number = *n; + *n = *n + 1; +} + +/** + * computes possuivante + * @param r = root + * @param PS = next position + */ + +void regexp_possuivante(NODE* r, int PS[]) +{ + if (r == NULL) + return; + + regexp_possuivante(r->fg, PS); + regexp_possuivante(r->fd, PS); + + switch (r->type) + { + case NODE_AND: + /************************************/ + /* \forall p \in DP(left) */ + /* PS[p] = PS[p] \cup PP(right) */ + /************************************/ + for (int pos = 1; pos <= PS[0]; pos++) + { + if (r->fg->DP & (1 << (pos-1))) + PS[pos] |= r->fd->PP; + } + break; + case NODE_PLUS: + /************************************/ + /* == same as START */ + /* \forall p \in DP(left) */ + /* PS[p] = PS[p] \cup PP(left) */ + /************************************/ + for (int pos = 1; pos <= PS[0]; pos++) + { + if (r->DP & (1 << (pos-1))) + PS[pos] |= r->PP; + } + break; + case NODE_STAR: + /************************************/ + /* \forall p \in DP(left) */ + /* PS[p] = PS[p] \cup PP(left) */ + /************************************/ + for (int pos = 1; pos <= PS[0]; pos++) + { + if (r->DP & (1 << (pos-1))) + PS[pos] |= r->PP; + } + break; + } +} + +/*//////////////////////////////////////////////// +// DEBUG only fonctions +////////////////////////////////////////////////*/ + +#ifdef DEBUG_RE +void regexp_print_PS(int PS[]) +{ + printf("** positions suivantes **\n"); + for (int i = 1; i <= PS[0]; i++) + { + printf("%02d: 0x%08x\n", i, PS[i]); + } +} +#endif + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +#ifdef DEBUG_RE +void regexp_print_ptl(int ptl[]) +{ + printf("** pos -> lettre: "); + for (int i = 1; i <= ptl[0]; i++) + { + printf("%d=%c ", i, ptl[i]); + } + printf("\n"); +} +#endif + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +void regexp_print_letter(FILE* f, char l) +{ + switch (l) + { + case RE_EPSILON: fprintf(f, "( & [%d])", l); break; + case RE_FINAL_TOK: fprintf(f, "( # [%d])", l); break; + case RE_ALL_MATCH: fprintf(f, "( . [%d])", l); break; + case RE_VOWL_MATCH: fprintf(f, "(:v: [%d])", l); break; + case RE_CONS_MATCH: fprintf(f, "(:c: [%d])", l); break; + case RE_USR1_MATCH: fprintf(f, "(:1: [%d])", l); break; + case RE_USR2_MATCH: fprintf(f, "(:2: [%d])", l); break; + default: + if (l < RE_FINAL_TOK) + fprintf(f, " (%c [%d]) ", l + 'a' - 1, l); + else + fprintf(f, " (liste %d)", l - RE_LIST_USER_END); + break; + } +} + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +void regexp_print_letter2(FILE* f, char l) +{ + switch (l) + { + case RE_EPSILON: fprintf(f, "&"); break; + case RE_FINAL_TOK: fprintf(f, "#"); break; + case RE_ALL_MATCH: fprintf(f, "."); break; + case RE_VOWL_MATCH: fprintf(f, ":v:"); break; + case RE_CONS_MATCH: fprintf(f, ":c:"); break; + case RE_USR1_MATCH: fprintf(f, ":1:"); break; + case RE_USR2_MATCH: fprintf(f, ":2:"); break; + default: + if (l < RE_FINAL_TOK) + fprintf(f, "%c", l + 'a' - 1); + else + fprintf(f, "l%d", l - RE_LIST_USER_END); + break; + } +} + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +#ifdef DEBUG_RE +static void print_node(FILE* f, NODE *n, int detail) +{ + if (n == NULL) + return; + + switch (n->type) + { + case NODE_VAR: + regexp_print_letter(f, n->var); + break; + case NODE_OR: + fprintf(f, "OR"); + break; + case NODE_AND: + fprintf(f, "AND"); + break; + case NODE_PLUS: + fprintf(f, "+"); + break; + case NODE_STAR: + fprintf(f, "*"); + break; + } + if (detail == 2) + { + fprintf(f, "\\n pos=%d\\n annul=%d\\n PP=0x%04x\\n DP=0x%04x", + n->position, n->annulable, n->PP, n->DP); + } +} +#endif + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +#ifdef DEBUG_RE +static void print_tree_nodes(FILE* f, NODE* n, int detail) +{ + if (n == NULL) + return; + + print_tree_nodes(f, n->fg, detail); + print_tree_nodes(f, n->fd, detail); + + fprintf(f, "%d [ label=\"", n->number); + print_node(f, n, detail); + fprintf(f, "\"];\n"); +} +#endif + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +#ifdef DEBUG_RE +static void print_tree_edges(FILE *f, NODE *n) +{ + if (n == NULL) + return; + + print_tree_edges(f, n->fg); + print_tree_edges(f, n->fd); + + switch (n->type) + { + case NODE_OR: + fprintf(f, "%d -> %d;", n->number, n->fg->number); + fprintf(f, "%d -> %d;", n->number, n->fd->number); + break; + case NODE_AND: + fprintf(f, "%d -> %d;", n->number, n->fg->number); + fprintf(f, "%d -> %d;", n->number, n->fd->number); + break; + case NODE_PLUS: + case NODE_STAR: + fprintf(f, "%d -> %d;", n->number, n->fg->number); + break; + } +} +#endif + +/*//////////////////////////////////////////////// +////////////////////////////////////////////////*/ + +#ifdef DEBUG_RE +void regexp_print_tree(NODE* n, const string &iName, int detail) +{ + FILE *f = fopen(iName.c_str(), "w"); + if (f == NULL) + return; + fprintf(f, "digraph %s {\n", iName.c_str()); + print_tree_nodes(f, n, detail); + print_tree_edges(f, n); + fprintf(f, "fontsize=20;\n"); + fprintf(f, "}\n"); + fclose(f); + +#ifdef HAVE_SYS_WAIT_H + pid_t pid = fork(); + if (pid > 0) + { + wait(NULL); + } + else if (pid == 0) + { + execlp("dotty", "dotty", iName.c_str(), NULL); + printf("exec dotty failed\n"); + exit(1); + } +#endif +} +#endif + + +/// Local Variables: +/// mode: hs-minor +/// c-basic-offset: 2 +/// End: diff --git a/dic/regexp.h b/dic/regexp.h index 698b796..510ae65 100644 --- a/dic/regexp.h +++ b/dic/regexp.h @@ -1,21 +1,22 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 1999-2006 Antoine Fraboulet + * Authors: Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file regexp.h @@ -24,12 +25,8 @@ * \date 2005 */ -#ifndef _TREE_H_ -#define _TREE_H_ -#if defined(__cplusplus) -extern "C" - { -#endif +#ifndef _REGEXP_H_ +#define _REGEXP_H_ #define NODE_TOP 0 #define NODE_VAR 1 @@ -38,18 +35,25 @@ extern "C" #define NODE_STAR 4 #define NODE_PLUS 5 -typedef struct node { - int type; - char var; - struct node *fg; - struct node *fd; - int numero; - int position; - int annulable; - int PP; - int DP; + +typedef struct node +{ + int type; + char var; + struct node *fg; + struct node *fd; + int number; + int position; + int annulable; + int PP; + int DP; } NODE; + /** + * different letters in the dictionary + */ +#define DIC_LETTERS 27 + /** * maximum number of accepted terminals in regular expressions */ @@ -139,7 +143,7 @@ struct regexp_error_report_t { char msg[MAX_REGEXP_ERROR_LENGTH]; }; -#include +#include void regexp_print_letter(FILE* f, char l); void regexp_print_letter2(FILE* f, char l); @@ -147,10 +151,7 @@ void regexp_print_PS(int PS[]); void regexp_print_ptl(int ptl[]); void regexp_print_tree(NODE* n, char* name, int detail); -#if defined(__cplusplus) - } -#endif -#endif /* _TREE_H_ */ +#endif /* _REGEXP_H_ */ /// Local Variables: /// mode: c++ diff --git a/dic/regexpmain.c b/dic/regexpmain.c deleted file mode 100644 index faa2559..0000000 --- a/dic/regexpmain.c +++ /dev/null @@ -1,140 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file regexpmain.c - * \brief Program used to test regexp - * \author Antoine Fraboulet - * \date 2005 - */ - -#include "config.h" -#include -#include -#include - -#include "dic.h" -#include "regexp.h" -#include "dic_search.h" - -#define __UNUSED__ __attribute__((unused)) - -/********************************************************/ -/********************************************************/ -/********************************************************/ - -const unsigned int all_letter[DIC_LETTERS] = - { - /* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 */ - /* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 */ - /* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ - 0,1,1,1,1, 1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1, 1, 1, 1, 1 - }; - -const unsigned int vowels[DIC_LETTERS] = - { - /* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ - 0,1,0,0,0, 1,0,0,0,1,0, 0,0,0,0,1,0,0,0,0,0,1,0, 0, 0, 1, 0 - }; - -const unsigned int consonants[DIC_LETTERS] = - { - /* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ - 0,0,1,1,1, 0,1,1,1,0,1, 1,1,1,1,0,1,1,1,1,1,0,1, 1, 1, 1, 1 - }; - -void init_letter_lists(struct search_RegE_list_t *list) -{ - int i; - memset (list,0,sizeof(*list)); - list->minlength = 1; - list->maxlength = 15; - list->valid[0] = 1; // all letters - list->symbl[0] = RE_ALL_MATCH; - list->valid[1] = 1; // vowels - list->symbl[1] = RE_VOWL_MATCH; - list->valid[2] = 1; // consonants - list->symbl[2] = RE_CONS_MATCH; - for(i=0; i < DIC_LETTERS; i++) - { - list->letters[0][i] = all_letter[i]; - list->letters[1][i] = vowels[i]; - list->letters[2][i] = consonants[i]; - } - list->valid[3] = 0; // user defined list 1 - list->symbl[3] = RE_USR1_MATCH; - list->valid[4] = 0; // user defined list 2 - list->symbl[4] = RE_USR2_MATCH; -} - -/********************************************************/ -/********************************************************/ -/********************************************************/ -void -usage(int __UNUSED__ argc, char* argv[]) -{ - fprintf(stderr,"usage: %s dictionary\n",argv[0]); - fprintf(stderr," dictionary : path to dawg eliot dictionary\n"); -} - -int main(int argc, char* argv[]) -{ - int i; - Dictionary dic; - char wordlist[RES_REGE_MAX][DIC_WORD_MAX]; - char er[200]; - strcpy(er,"."); - struct search_RegE_list_t list; - - if (argc < 2) - { - usage(argc,argv); - } - - if (Dic_load(&dic,argv[1])) - { - fprintf(stdout,"impossible de lire le dictionnaire\n"); - return 1; - } - - while (strcmp(er,"")) - { - fprintf(stdout,"**************************************************************\n"); - fprintf(stdout,"**************************************************************\n"); - fprintf(stdout,"entrer une ER:\n"); - fgets(er,sizeof(er),stdin); - /* strip \n */ - er[strlen(er) - 1] = '\0'; - if (strcmp(er,"") == 0) - break; - - /* automaton */ - init_letter_lists(&list); - Dic_search_RegE_inner(dic,er,wordlist,&list); - - fprintf(stdout,"résultat:\n"); - for(i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +/** + * \file regexpmain.c + * \brief Program used to test regexp + * \author Antoine Fraboulet + * \date 2005 + */ + +#include "config.h" + +#include +#include +#include +#include + +#if ENABLE_NLS +# include +# define _(String) gettext(String) +#else +# define _(String) String +#endif + +#include "dic.h" +#include "regexp.h" +#include "encoding.h" + + +#define __UNUSED__ __attribute__((unused)) + +/********************************************************/ +/********************************************************/ +/********************************************************/ + +const unsigned int all_letter[DIC_LETTERS] = +{ + /* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 */ + /* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 */ + /* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ + 0,1,1,1,1, 1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1, 1, 1, 1, 1 +}; + +const unsigned int vowels[DIC_LETTERS] = +{ + /* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ + 0,1,0,0,0, 1,0,0,0,1,0, 0,0,0,0,1,0,0,0,0,0,1,0, 0, 0, 1, 0 +}; + +const unsigned int consonants[DIC_LETTERS] = +{ + /* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ + 0,0,1,1,1, 0,1,1,1,0,1, 1,1,1,1,0,1,1,1,1,1,0,1, 1, 1, 1, 1 +}; + +void init_letter_lists(struct search_RegE_list_t *iList) +{ + memset (iList, 0, sizeof(*iList)); + iList->minlength = 1; + iList->maxlength = 15; + iList->valid[0] = 1; // all letters + iList->symbl[0] = RE_ALL_MATCH; + iList->valid[1] = 1; // vowels + iList->symbl[1] = RE_VOWL_MATCH; + iList->valid[2] = 1; // consonants + iList->symbl[2] = RE_CONS_MATCH; + for (int i = 0; i < DIC_LETTERS; i++) + { + iList->letters[0][i] = all_letter[i]; + iList->letters[1][i] = vowels[i]; + iList->letters[2][i] = consonants[i]; + } + iList->valid[3] = 0; // user defined list 1 + iList->symbl[3] = RE_USR1_MATCH; + iList->valid[4] = 0; // user defined list 2 + iList->symbl[4] = RE_USR2_MATCH; +} + +/********************************************************/ +/********************************************************/ +/********************************************************/ +void usage(const char *iBinaryName) +{ + cerr << _("usage: %s dictionary") << iBinaryName << endl; + cerr << _(" dictionary: path to eliot dawg dictionary") << endl; +} + + +int main(int argc, char* argv[]) +{ +#if HAVE_SETLOCALE + // Set locale via LC_ALL + setlocale(LC_ALL, ""); +#endif + +#if ENABLE_NLS + // Set the message domain + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + if (argc != 2) + { + usage(argv[0]); + return 0; + } + + try + { + Dictionary dic(argv[1]); + + char er[200]; + strcpy(er, "."); + + struct search_RegE_list_t regList; + while (strcmp(er, "")) + { + cout << "**************************************************************" << endl; + cout << "**************************************************************" << endl; + cout << _("enter a regular expression:") << endl; + fgets(er, sizeof(er), stdin); + /* strip \n */ + er[strlen(er) - 1] = '\0'; + if (strcmp(er, "") == 0) + break; + + /* automaton */ + init_letter_lists(®List); + list wordList; + dic.searchRegExp(convertToWc(er), wordList, ®List); + + cout << _("result:") << endl; + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) + { + cerr << convertToMb(*it) << endl; + } + } + + return 0; + } + catch (std::exception &e) + { + std::cerr << e.what() << endl; + return 1; + } + catch (...) + { + std::cerr << "Unkown exception taken" << endl; + return 1; + } +} diff --git a/dic/tile.cpp b/dic/tile.cpp new file mode 100644 index 0000000..20c4469 --- /dev/null +++ b/dic/tile.cpp @@ -0,0 +1,150 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include +#include +#include +#include "tile.h" +#include "header.h" +#include "encoding.h" +#include "dic_exception.h" + + +const Header * Tile::m_header = NULL; +Tile Tile::m_TheJoker; + + +Tile::Tile(wchar_t c) +{ + if (iswalpha(c)) + { + m_joker = iswlower(c); + m_char = towupper(c); + m_code = m_header->getCodeFromChar(m_char); + } + else if (c == kTILE_JOKER) + { + m_joker = true; + m_char = kTILE_JOKER; + m_code = m_header->getCodeFromChar(m_char); + } + else if (c == kTILE_DUMMY) + { + // The char and the code are chosen to be different from any possible + // real tile + m_joker = false; + m_char = kTILE_DUMMY; + m_code = 0; + } + else + { + ostringstream ss; + ss << "Tile::Tile: Unknown character: " << convertToMb(c); + throw DicException(ss.str()); + } +} + + +bool Tile::isVowel() const +{ + if (m_code == 0) + throw DicException("Tile::isVowel: Invalid tile"); + return m_header->isVowel(m_code); +} + + +bool Tile::isConsonant() const +{ + if (m_code == 0) + throw DicException("Tile::isConsonant: Invalid tile"); + return m_header->isConsonant(m_code); +} + + +unsigned int Tile::maxNumber() const +{ + if (m_code == 0) + throw DicException("Tile::maxNumber: Invalid tile"); + return m_header->getFrequency(m_code); +} + + +unsigned int Tile::getPoints() const +{ + if (m_code == 0) + throw DicException("Tile::getPoints: Invalid tile"); + return m_header->getPoints(m_code); +} + + +wchar_t Tile::toChar() const +{ + if (m_joker) + { + if (iswalpha(m_char)) + return towlower(m_char); + else + return kTILE_JOKER; + } + return m_char; +} + + +unsigned int Tile::toCode() const +{ + return m_code; +} + + +bool Tile::operator<(const Tile &iOther) const +{ + if (m_joker) + return false; + else if (iOther.m_joker) + return true; + else + return m_char < iOther.m_char; +} + + +bool Tile::operator==(const Tile &iOther) const +{ + if (m_joker || iOther.m_joker) + { + if (m_joker != iOther.m_joker) + return false; + return m_char == iOther.m_char; + } + return m_char == iOther.m_char; +} + + +bool Tile::operator!=(const Tile &iOther) const +{ + return !(*this == iOther); +} + +/// Local Variables: +/// mode: c++ +/// mode: hs-minor +/// c-basic-offset: 4 +/// indent-tabs-mode: nil +/// End: diff --git a/game/tile.h b/dic/tile.h similarity index 69% rename from game/tile.h rename to dic/tile.h index ec677c3..2a877e1 100644 --- a/game/tile.h +++ b/dic/tile.h @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +27,8 @@ using namespace std; +class Header; + /************************* * A Tile is the internal representation @@ -34,17 +38,17 @@ using namespace std; class Tile { + friend class Dictionary; public: - // a lowercase character always a joker - // - this permits to detect joker in already played games - // - we need to pay attention when inserting character taken - // from user input + // a lowercase character is always a joker + // - this permits to detect joker in already played games + // - we need to pay attention when inserting characters taken + // from user input - Tile(wchar_t c = 0); - virtual ~Tile() {} + Tile(wchar_t c = kTILE_DUMMY); - bool isEmpty() const { return m_dummy; } + bool isEmpty() const { return m_char == kTILE_DUMMY; } bool isJoker() const { return m_joker; } bool isVowel() const; bool isConsonant() const; @@ -53,10 +57,7 @@ public: wchar_t toChar() const; unsigned int toCode() const; - static const Tile &dummy() { return m_TheDummy; } static const Tile &Joker() { return m_TheJoker; } - static const list& getAllTiles(); - static const Tile &GetTileFromCode(unsigned int iCode); bool operator <(const Tile &iOther) const; bool operator ==(const Tile &iOther) const; @@ -65,7 +66,6 @@ public: private: wchar_t m_char; bool m_joker; - bool m_dummy; /** * Internal code, used in the dictionary to represent the letter. @@ -73,16 +73,17 @@ private: */ int m_code; - // Special tiles are declared static - static const Tile m_TheJoker; - static const Tile m_TheDummy; + static const wchar_t kTILE_DUMMY = L'%'; + static const wchar_t kTILE_JOKER = L'?'; - /// List of available tiles - static list m_tilesList; - /// Vector of tiles indexed by their code, for fast look-up - static vector m_tilesVect; - /// True when m_tilesVect is correctly initialized - static bool m_vectInitialized; + // Special tiles are declared static + static Tile m_TheJoker; + + /// Dictionary header + static const Header *m_header; + + /// Update the dictionary header + static void SetHeader(const Header &iHeader) { m_header = &iHeader; } }; #endif diff --git a/doc/dic.txt b/doc/dic.txt index ac94afd..5e565cf 100644 --- a/doc/dic.txt +++ b/doc/dic.txt @@ -1,16 +1,16 @@ The dictionary is a directed tree. Compression is achieved by - sharing word prefix and suffix. Search is NOT case sensitive. - + sharing word prefix and suffix. Search is NOT case sensitive. + considering this 3 words dictionary: - + ABC ADA EDAA The tree will look like this: - root + root | A----------E! | | @@ -24,41 +24,56 @@ ------------------- 0!* (sink) The tree is saved using an array of 32 bits words. - A cell is a binary structure + A cell is a binary structure ptr : index in the array of the first child term : is it the last letter of a word (*) last : is it the last child of its local root (!) - fill : currently unused. chr : guess what ! There is no pointer from a cell to its brother, it is simply the next cell in the array (you know you are on the last brother when the flag "last" is set). - The way it is stored in a file is different thing! The tree is + The way it is stored in a file is a different thing! The tree is stored bottom-up. The sink (offset 0) is the first cell of the array. - Using compdict (which you can found in the eliot/dic directory), + Using compdic (which you can find in the eliot/dic directory), the compiled dictionary will look like this: -compdict's console output: -============================ -keyword length 21 bytes -keyword size 22 bytes -header size 48 bytes +compdic console output (cut in the middle): +=================================================================== +dictionary name: ODS 4.0 +compressed on: mer 12 déc 2007 07:29:50 GMT +compressed using a binary compiled by: ipkiss@ulukai +dictionary type: DAWG +letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ? +number of letters: 27 +number of words: 369085 +header size: 360 bytes +root: 100950 (edge) +nodes: 40377 used + 418387 saved +edges: 100950 used + 601922 saved +=============================================== +letter | points | frequency | vowel | consonant +-------+--------+-----------+-------+---------- + A | 1 | 9 | 1 | 0 + B | 3 | 2 | 0 | 1 + C | 3 | 2 | 0 | 1 + D | 2 | 3 | 0 | 1 +[... output cut here ...] + X | 10 | 1 | 0 | 1 + Y | 10 | 1 | 1 | 1 + Z | 10 | 1 | 0 | 1 + ? | 0 | 2 | 1 | 1 +=============================================== + Load time: 0,060 s + Compression time: 0,170 s + Maximum recursion level reached: 16 +=================================================================== -3 words - -root : 9 (edge) -root : 36 (byte) - -nodes : 7+1 -edges : 9+1 -============================ - -binary view of the dictionary: +binary view of the dictionary (FIXME: not up to date): =================================================================== 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f 00000000: 5f43 4f4d 5049 4c45 445f 4449 4354 494f _COMPILED_DICTIO @@ -69,27 +84,85 @@ binary view of the dictionary: 00000050: 0600 002a 0700 0000 ...*.... =================================================================== -The header structure is the following: - +The header is made of 2 structures (for backwards compatibility +with older headers) like this: +=================================================================== #define _COMPIL_KEYWORD_ "_COMPILED_DICTIONARY_" -typedef struct _Dict_header { // offset +struct Dict_header_old // offset +{ char ident[sizeof(_COMPIL_KEYWORD_)]; // 0x00 - char unused_1; // 0x16 - char unused_2; // 0x17 - int root; // 0x18 - int nwords; // 0x1c - unsigned int edgesused; // 0x20 - unsigned int nodesused; // 0x24 - unsigned int nodessaved; // 0x2c - unsigned int edgessaved; // 0x30 -} Dict_header; + uint8_t version; // 0x16 + char unused; // 0x17 + uint32_t root; // 0x18 + uint32_t nwords; // 0x1c + uint32_t edgesused; // 0x20 + uint32_t nodesused; // 0x24 + uint32_t nodessaved; // 0x28 + uint32_t edgessaved; // 0x2c +}; -binary output of the header: +#define _MAX_USER_HOST_ 32 +#define _MAX_DIC_NAME_SIZE_ 30 +#define _MAX_LETTERS_NB_ 63 +#define _MAX_LETTERS_SIZE_ 80 + +struct Dict_header +{ + uint64_t compressDate; + // Build information + char userHost[_MAX_USER_HOST_]; + // Size taken by the build information + uint32_t userHostSize; + + // Compression algorithm (1 = DAWG, 2 = GADDAG) + uint8_t algorithm; + // Variant used in the rules (XXX: currently unused) + uint8_t variant; + + // Dictionary official name and version (e.g.: ODS 5.0) + char dicName[_MAX_DIC_NAME_SIZE_]; + // Size taken by the dictionary name + uint32_t dicNameSize; + + // Letters used in the dictionary + // We should have: nbLetters <= lettersSize <= _MAX_LETTERS_SIZE_ + // and: nbLetters <= _MAX_LETTERS_NB_ + // The letters themselves, in UTF-8 + char letters[_MAX_LETTERS_SIZE_]; + // Size taken by the letters + uint32_t lettersSize; + // Number of letters (XXX: in theory useless, but allows a sanity check) + uint32_t nbLetters; + + // Points of the letters (indexed by their code) + // The "+ 1" is there for struct alignment + uint8_t points[_MAX_LETTERS_NB_ + 1]; + // Frequency of the letters (indexedy their code) + // The "+ 1" is there for struct alignment + uint8_t frequency[_MAX_LETTERS_NB_ + 1]; + // Bitfield indicating whether letters are vowels + uint64_t vowels; + // Bitfield indicating whether letters are consonants + uint64_t consonants; +} +=================================================================== + +In the old version of the dictionary, only the first structure was used +(with version = 0). The current format (version = 1) has the 2 structs +next to each other. +The dictionary name, the letters, and the user/host information are +stored in UTF-8. All the numbers are big endian (i.e. the output of +the htonl() function). +To avoid alignment issues, the extended header has been designed to +have multiples of 64 bits regularly. + + +binary output of the header (FIXME: not up to date): =================================================================== 0x00 ident : _COMPILED_DICTIONARY_ -0x16 unused 1 : 0 00000000 -0x17 unused 2 : 0 00000000 +0x16 version : 0 00000001 +0x17 unused : 0 00000000 0x18 root : 9 00000009 0x1c words : 3 00000003 0x20 edges used : 9 00000009 @@ -98,40 +171,37 @@ binary output of the header: 0x2c edges saved : 1 00000001 =================================================================== -The real array of data begins at offset 0x34. Integer are stored in a -machine dependent way. This dictionary was compiled on a i386 and is -not readable on a machine with a different endianess (unless swapping -all necessary information). The array is stored 'as is' right after -the header. Each array cell is a bit-structure on 4 bytes : +The real array of data begins at offset 0x168. The array is stored +'as is' right after the header. Each array cell is a bit-structure +on 4 bytes: -typedef struct _Dawg_edge { - unsigned int ptr : 24; - unsigned int term : 1; - unsigned int last : 1; - unsigned int fill : 1; // reserved (currently unused) - unsigned int chr : 5; -} Dawg_edge; +struct DicEdge +{ + unsigned int ptr : 24; + unsigned int term : 1; + unsigned int last : 1; + unsigned int chr : 6; +}; -Characters are not stored in ASCII. The order is preserved but -we changed the values: A=1, B=2, ... This is very easy to do -with the ASCII table as ('A' & 0x1f) == ('a' & 0x1f) == 1. -This may not work on machines that are not using ASCII. The dictionary -can thus handle up to 32 different letters but not more. +Characters are not stored in ASCII. The order of the letters given +to the compdic binary is preserved, but we changed the values: the +first letter is 1, the second one is 2, etc... +The dictionary can thus handle up to 64 different letters but not more. +The letter 0 is special (used for the sink node in particular), so +in practice there are only 63 distinct letters. -offs binary structure +offs binary structure ---- -------- | ------------------ -0x00 02000000 | 0 ptr= 0 t=0 l=1 f=0 chr=0 (`) -0x04 1b000000 | 1 ptr= 0 t=1 l=1 f=0 chr=3 (c) -0x08 0b000000 | 2 ptr= 0 t=1 l=1 f=0 chr=1 (a) -0x0c 10000001 | 3 ptr= 1 t=0 l=0 f=0 chr=2 (b) -0x10 22000002 | 4 ptr= 2 t=0 l=1 f=0 chr=4 (d) -0x14 0a000002 | 5 ptr= 2 t=0 l=1 f=0 chr=1 (a) -0x18 22000005 | 6 ptr= 5 t=0 l=1 f=0 chr=4 (d) -0x1c 08000003 | 7 ptr= 3 t=0 l=0 f=0 chr=1 (a) -0x20 2a000006 | 8 ptr= 6 t=0 l=1 f=0 chr=5 (e) -0x24 00000007 | 9 ptr= 7 t=0 l=0 f=0 chr=0 (`) - -Strictly speaking, there is no node in the graph, only labelled edges. - +0x00 02000000 | 0 ptr= 0 t=0 l=1 chr=0 (`) +0x04 1b000000 | 1 ptr= 0 t=1 l=1 chr=3 (c) +0x08 0b000000 | 2 ptr= 0 t=1 l=1 chr=1 (a) +0x0c 10000001 | 3 ptr= 1 t=0 l=0 chr=2 (b) +0x10 22000002 | 4 ptr= 2 t=0 l=1 chr=4 (d) +0x14 0a000002 | 5 ptr= 2 t=0 l=1 chr=1 (a) +0x18 22000005 | 6 ptr= 5 t=0 l=1 chr=4 (d) +0x1c 08000003 | 7 ptr= 3 t=0 l=0 chr=1 (a) +0x20 2a000006 | 8 ptr= 6 t=0 l=1 chr=5 (e) +0x24 00000007 | 9 ptr= 7 t=0 l=0 chr=0 (`) +Strictly speaking, there is no node in the graph, only labelled edges. diff --git a/game/Makefile.am b/game/Makefile.am index 5937e81..6284d99 100644 --- a/game/Makefile.am +++ b/game/Makefile.am @@ -1,6 +1,7 @@ # Eliot -# Copyright (C) 1999 Antoine Fraboulet -# antoine.fraboulet@free.fr +# Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière +# Authors: Antoine Fraboulet +# Olivier Teulière # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,7 +24,6 @@ INCLUDES = -I$(top_srcdir)/dic libgame_a_SOURCES= \ ai_percent.cpp ai_percent.h \ ai_player.h \ - tile.cpp tile.h \ bag.cpp bag.h \ coord.cpp coord.h \ cross.cpp cross.h \ @@ -31,17 +31,19 @@ libgame_a_SOURCES= \ board_cross.cpp \ board_search.cpp \ duplicate.cpp duplicate.h \ - encoding.cpp encoding.h \ freegame.cpp freegame.h \ game.cpp game.h \ game_factory.cpp game_factory.h \ game_io.cpp \ + move.cpp move.h \ player.cpp player.h \ pldrack.cpp pldrack.h \ rack.cpp rack.h \ results.cpp results.h \ round.cpp round.h \ + settings.cpp settings.h \ training.cpp training.h \ turn.cpp turn.h \ - history.cpp history.h + history.cpp history.h \ + debug.h diff --git a/game/ai_percent.cpp b/game/ai_percent.cpp index b38e25a..af3ef61 100644 --- a/game/ai_percent.cpp +++ b/game/ai_percent.cpp @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +22,7 @@ #include "rack.h" #include "pldrack.h" #include "round.h" +#include "move.h" #include "results.h" #include "board.h" #include "ai_percent.h" @@ -37,32 +39,46 @@ AIPercent::AIPercent(int iId, float iPercent) } -void AIPercent::compute(const Dictionary &iDic, Board &iBoard, int turn) +void AIPercent::compute(const Dictionary &iDic, Board &iBoard, bool iFirstWord) { m_results.clear(); Rack rack; getCurrentRack().getRack(rack); - m_results.search(iDic, iBoard, rack, turn); + m_results.search(iDic, iBoard, rack, iFirstWord); } -bool AIPercent::changesLetters() const +Move AIPercent::getMove() const { - return (m_results.size() == 0); -} - - -const Round & AIPercent::getChosenRound() const -{ - int index = (int)(m_percent * (m_results.size() - 1)); - return m_results.get(index); -} - - -vector AIPercent::getChangedLetters() const -{ - return vector(); + if (m_results.size() == 0) + { + // If there is no result, simply pass the turn + // XXX: it is forbidden in duplicate mode, but well, what else to do? + return Move(L""); + } + else + { + // If there are results, apply the algorithm + double wantedScore = m_percent * m_results.get(0).getPoints(); + // Look for the first round giving at least 'wantedScore' points + // Browse the results 10 by 10 (a dichotomy would be better, but this + // is not performance critical) + unsigned int index = 0; + while (index < m_results.size() && + m_results.get(index).getPoints() > wantedScore) + { + index += 10; + } + // Now the wanted round is in the last 10 indices + if (index >= m_results.size()) + index = m_results.size() - 1; + while (m_results.get(index).getPoints() < wantedScore) + { + --index; + } + return Move(m_results.get(index)); + } } /// Local Variables: diff --git a/game/ai_percent.h b/game/ai_percent.h index 35a10e6..1a98a24 100644 --- a/game/ai_percent.h +++ b/game/ai_percent.h @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,10 +28,10 @@ * This kind of AI is parameterized by a percentage p. * The computation consists in finding all the N possible rounds for the * current rack/board, and sorting the list. - * The chosen round is the n'th element of the sorted list, such that n/N - * is closest to the percentage p. - * A percentage of 0 should always return the best round (i.e. the one with - * the highest score), while a percentage of 1 should return the worst one. + * The chosen round is the one with the smallest score at least equal to + * p * best_score. + * A percentage of 1 should always return the best round (i.e. the one with + * the highest score), while a percentage of 0 should return the worst one. * This kind of AI will never change letters (unless it cannot play anything, * in which case it just passes without changing letters). */ @@ -45,17 +46,15 @@ public: * This method does the actual computation. It will be called before any * of the following methods, so it must prepare everything for them. */ - virtual void compute(const Dictionary &iDic, Board &iBoard, int turn); - /// Return true when the AI wants to change letters instead of playing a word - virtual bool changesLetters() const; - /// Return the round played by the AI (if changesLetters() returns false) - virtual const Round & getChosenRound() const; - /// Get the letters to change (if changesLetters() returns true) - virtual vector getChangedLetters() const; + virtual void compute(const Dictionary &iDic, Board &iBoard, bool iFirstWord); + + /// Return the move played by the AI + virtual Move getMove() const; private: /// Percentage used for this player float m_percent; + /// Container for all the found solutions Results m_results; }; diff --git a/game/ai_player.h b/game/ai_player.h index 8efa452..22dab40 100644 --- a/game/ai_player.h +++ b/game/ai_player.h @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,10 +23,10 @@ #include "player.h" +class Dictionary; class Round; class Board; class Tile; -typedef struct _Dictionary * Dictionary; /** * This class is a pure interface, that must be implemented by all the AI @@ -69,22 +70,14 @@ public: * This method does the actual computation. It will be called before any * of the following methods, so it must prepare everything for them. */ - virtual void compute(const Dictionary &iDic, Board &iBoard, int turn) = 0; - /** - * Return true when the AI wants to change letters instead of playing a - * word. - * Should return false in duplicate mode, as it is not allowed to change - * letters. - */ - virtual bool changesLetters() const = 0; - /// Return the round played by the AI (if changesLetters() returns false) - virtual const Round & getChosenRound() const = 0; - /// Get the letters to change (if changesLetters() returns true) - virtual vector getChangedLetters() const = 0; + virtual void compute(const Dictionary &iDic, Board &iBoard, bool iFirstWord) = 0; + + /// Return the move played by the AI + virtual Move getMove() const = 0; protected: /// This class is a pure interface, forbid any direct instanciation - AIPlayer(int iId): Player(iId) {} + AIPlayer(unsigned int iId): Player(iId) {} }; #endif diff --git a/game/bag.cpp b/game/bag.cpp index c35680d..78dcd9b 100644 --- a/game/bag.cpp +++ b/game/bag.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,22 +21,18 @@ #include -#include "tile.h" +#include #include "bag.h" #include "debug.h" +#include "encoding.h" -Bag::Bag() -{ - init(); -} - - -void Bag::init() +Bag::Bag(const Dictionary &iDic) + : m_dic(iDic) { m_ntiles = 0; - const list& allTiles = Tile::getAllTiles(); - list::const_iterator it; + const vector& allTiles = m_dic.getAllTiles(); + vector::const_iterator it; for (it = allTiles.begin(); it != allTiles.end(); it++) { m_tilesMap[*it] = it->maxNumber(); @@ -53,7 +50,7 @@ unsigned int Bag::in(const Tile &iTile) const } -unsigned int Bag::nVowels() const +unsigned int Bag::getNbVowels() const { map::const_iterator it; int v = 0; @@ -67,7 +64,7 @@ unsigned int Bag::nVowels() const } -unsigned int Bag::nConsonants() const +unsigned int Bag::getNbConsonants() const { map::const_iterator it; int c = 0; @@ -84,7 +81,7 @@ unsigned int Bag::nConsonants() const void Bag::takeTile(const Tile &iTile) { ASSERT(in(iTile), - (wstring(L"The bag does not contain the letter ") + iTile.toChar()).c_str()); + "The bag does not contain the letter " + convertToMb(iTile.toChar())); m_tilesMap[iTile]--; m_ntiles--; @@ -94,20 +91,21 @@ void Bag::takeTile(const Tile &iTile) void Bag::replaceTile(const Tile &iTile) { ASSERT(in(iTile) < iTile.maxNumber(), - (wstring(L"Cannot replace tile: ") + iTile.toChar()).c_str()); + "Cannot replace tile: " + convertToMb(iTile.toChar())); m_tilesMap[iTile]++; m_ntiles++; } -Tile Bag::selectRandom() +Tile Bag::selectRandom() const { - map::const_iterator it; - int n; double max = m_ntiles; + ASSERT(max > 0, "The bag is empty"); - n = (int)(max * rand() / (RAND_MAX + 1.0)); + int n = (int)(max * rand() / (RAND_MAX + 1.0)); + + map::const_iterator it; for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++) { if (n < it->second) @@ -115,7 +113,49 @@ Tile Bag::selectRandom() n -= it->second; } ASSERT(false, "We should not come here"); - return Tile::dummy(); + return Tile(); +} + + +Tile Bag::selectRandomVowel() const +{ + double max = getNbVowels(); + ASSERT(max > 0, "Not enough vowels in the bag"); + + int n = (int)(max * rand() / (RAND_MAX + 1.0)); + + map::const_iterator it; + for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++) + { + if (!it->first.isVowel()) + continue; + if (n < it->second) + return it->first; + n -= it->second; + } + ASSERT(false, "We should not come here"); + return Tile(); +} + + +Tile Bag::selectRandomConsonant() const +{ + double max = getNbConsonants(); + ASSERT(max > 0, "Not enough consonants in the bag"); + + int n = (int)(max * rand() / (RAND_MAX + 1.0)); + + map::const_iterator it; + for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++) + { + if (!it->first.isConsonant()) + continue; + if (n < it->second) + return it->first; + n -= it->second; + } + ASSERT(false, "We should not come here"); + return Tile(); } diff --git a/game/bag.h b/game/bag.h index 6f8889d..65d8e83 100644 --- a/game/bag.h +++ b/game/bag.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,11 +22,13 @@ #ifndef _BAG_H_ #define _BAG_H_ -#include "tile.h" #include +#include "tile.h" using std::map; +class Dictionary; + /** * A bag stores the set of free tiles for the game. @@ -33,9 +36,7 @@ using std::map; class Bag { public: - Bag(); - virtual ~Bag() {} - void init(); + explicit Bag(const Dictionary &iDic); /// Take a tile in the bag void takeTile(const Tile &iTile); @@ -47,18 +48,30 @@ public: /** * Return how many tiles/vowels/consonants are available - * Warning: b.nVowels() + b.nConsonants() != b.nTiles(), + * Warning: b.getNbVowels() + b.getNbConsonants() != b.getNbTiles(), * because of the jokers and the 'Y'. */ - unsigned int nTiles() const { return m_ntiles; } - unsigned int nVowels() const; - unsigned int nConsonants() const; + unsigned int getNbTiles() const { return m_ntiles; } + unsigned int getNbVowels() const; + unsigned int getNbConsonants() const; /** * Return a random available tile * The tile is not taken out of the bag. */ - Tile selectRandom(); + Tile selectRandom() const; + + /** + * Return a random available vowel. + * The tile is not taken out of the bag. + */ + Tile selectRandomVowel() const; + + /** + * Return a random available consonant. + * The tile is not taken out of the bag. + */ + Tile selectRandomConsonant() const; void operator=(const Bag &iOther); @@ -66,8 +79,12 @@ public: void dumpAll() const; private: + /// Dictionary + const Dictionary &m_dic; + /// Associate to each tile its number of occurrences in the bag map m_tilesMap; + /// Total number of tiles in the bag int m_ntiles; }; diff --git a/game/board.cpp b/game/board.cpp index 3a78080..a1504b0 100644 --- a/game/board.cpp +++ b/game/board.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,8 +83,8 @@ const int Board::m_wordMultipliers[BOARD_REALDIM][BOARD_REALDIM] = Board::Board(): - m_tilesRow(BOARD_REALDIM, Tile::dummy()), - m_tilesCol(BOARD_REALDIM, Tile::dummy()), + m_tilesRow(BOARD_REALDIM, Tile()), + m_tilesCol(BOARD_REALDIM, Tile()), m_jokerRow(BOARD_REALDIM, false), m_jokerCol(BOARD_REALDIM, false), m_crossRow(BOARD_REALDIM, Cross()), @@ -154,13 +155,12 @@ bool Board::isVacant(int iRow, int iCol) const void Board::addRound(const Dictionary &iDic, const Round &iRound) { Tile t; - int row, col; - row = iRound.getCoord().getRow(); - col = iRound.getCoord().getCol(); + int row = iRound.getCoord().getRow(); + int col = iRound.getCoord().getCol(); if (iRound.getCoord().getDir() == Coord::HORIZONTAL) { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (m_tilesRow[row][col + i].isEmpty()) { @@ -174,7 +174,7 @@ void Board::addRound(const Dictionary &iDic, const Round &iRound) } else { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (m_tilesRow[row + i][col].isEmpty()) { @@ -195,20 +195,18 @@ void Board::addRound(const Dictionary &iDic, const Round &iRound) void Board::removeRound(const Dictionary &iDic, const Round &iRound) { - int row, col; - - row = iRound.getCoord().getRow(); - col = iRound.getCoord().getCol(); + int row = iRound.getCoord().getRow(); + int col = iRound.getCoord().getCol(); if (iRound.getCoord().getDir() == Coord::HORIZONTAL) { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (iRound.isPlayedFromRack(i)) { - m_tilesRow[row][col + i] = Tile::dummy(); + m_tilesRow[row][col + i] = Tile(); m_jokerRow[row][col + i] = false; m_crossRow[row][col + i].setAny(); - m_tilesCol[col + i][row] = Tile::dummy(); + m_tilesCol[col + i][row] = Tile(); m_jokerCol[col + i][row] = false; m_crossCol[col + i][row].setAny(); } @@ -216,14 +214,14 @@ void Board::removeRound(const Dictionary &iDic, const Round &iRound) } else { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (iRound.isPlayedFromRack(i)) { - m_tilesRow[row + i][col] = Tile::dummy(); + m_tilesRow[row + i][col] = Tile(); m_jokerRow[row + i][col] = false; m_crossRow[row + i][col].setAny(); - m_tilesCol[col][row + i] = Tile::dummy(); + m_tilesCol[col][row + i] = Tile(); m_jokerCol[col][row + i] = false; m_crossCol[col][row + i].setAny(); } @@ -246,17 +244,15 @@ int Board::checkRoundAux(Matrix &iTilesMx, bool firstturn) { Tile t; - int row, col, i; - int l, p, fromrack; - int pts, ptscross, wordmul; + int l, p; bool isolated = true; - fromrack = 0; - pts = 0; - ptscross = 0; - wordmul = 1; - row = iRound.getCoord().getRow(); - col = iRound.getCoord().getCol(); + unsigned int fromrack = 0; + int pts = 0; + int ptscross = 0; + int wordmul = 1; + int row = iRound.getCoord().getRow(); + int col = iRound.getCoord().getCol(); /* Is the word an extension of another word? */ if (!iTilesMx[row][col - 1].isEmpty() || @@ -265,7 +261,7 @@ int Board::checkRoundAux(Matrix &iTilesMx, return 1; } - for (i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { t = iRound.getTile(i); if (!iTilesMx[row][col + i].isEmpty()) @@ -383,13 +379,12 @@ int Board::checkRound(Round &iRound, bool firstturn) void Board::testRound(const Round &iRound) { Tile t; - int row, col; - row = iRound.getCoord().getRow(); - col = iRound.getCoord().getCol(); + int row = iRound.getCoord().getRow(); + int col = iRound.getCoord().getCol(); if (iRound.getCoord().getDir() == Coord::HORIZONTAL) { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (m_tilesRow[row][col + i].isEmpty()) { @@ -405,7 +400,7 @@ void Board::testRound(const Round &iRound) } else { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (m_tilesRow[row + i][col].isEmpty()) { @@ -430,11 +425,11 @@ void Board::removeTestRound() { if (m_testsRow[row][col]) { - m_tilesRow[row][col] = Tile::dummy(); + m_tilesRow[row][col] = Tile(); m_testsRow[row][col] = 0; m_jokerRow[row][col] = false; - m_tilesCol[col][row] = Tile::dummy(); + m_tilesCol[col][row] = Tile(); m_jokerCol[col][row] = false; } } diff --git a/game/board.h b/game/board.h index 004b46d..d9f8b1c 100644 --- a/game/board.h +++ b/game/board.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,12 +22,13 @@ #ifndef _BOARD_H_ #define _BOARD_H_ -#include "tile.h" -#include "cross.h" #include #include -typedef struct _Dictionary*Dictionary; +#include "tile.h" +#include "cross.h" + +class Dictionary; class Rack; class Round; class Results; @@ -61,7 +63,6 @@ class Board { public: Board(); - virtual ~Board() {} /************************* * Coordinates have to be BOARD_MIN <= int <= BOARD_MAX diff --git a/game/board_cross.cpp b/game/board_cross.cpp index 2671a4f..d3819ac 100644 --- a/game/board_cross.cpp +++ b/game/board_cross.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,11 +26,14 @@ * \date 2005 */ +#include + #include #include "tile.h" #include "board.h" #include "debug.h" + static void Board_checkout_tile(const Dictionary &iDic, vector& iTiles, vector & iJoker, @@ -37,7 +41,7 @@ static void Board_checkout_tile(const Dictionary &iDic, int& oPoints, int index) { - int i,left; + int i, left; unsigned int node, succ; oPoints = 0; @@ -52,36 +56,36 @@ static void Board_checkout_tile(const Dictionary &iDic, } // FIXME: create temporary strings until the dictionary uses Tile objects - char leftTiles [BOARD_DIM + 1]; - char rightTiles[BOARD_DIM + 1]; + wchar_t leftTiles [BOARD_DIM + 1]; + wchar_t rightTiles[BOARD_DIM + 1]; for (i = left; i < index; i++) - leftTiles[i - left] = toupper(iTiles[i].toChar()); + leftTiles[i - left] = towupper(iTiles[i].toChar()); leftTiles[index - left] = 0; for (i = index + 1; !iTiles[i].isEmpty(); i++) - rightTiles[i - index - 1] = toupper(iTiles[i].toChar()); + rightTiles[i - index - 1] = towupper(iTiles[i].toChar()); rightTiles[i - index - 1] = 0; /* Tiles that can be played */ - node = Dic_char_lookup(iDic, Dic_root(iDic), leftTiles); + node = iDic.charLookup(iDic.getRoot(), leftTiles); if (node == 0) { oCross.setNone(); return; } - for (succ = Dic_succ(iDic, node); succ; succ = Dic_next(iDic, succ)) + for (succ = iDic.getSucc(node); succ; succ = iDic.getNext(succ)) { - if (Dic_word(iDic, Dic_char_lookup(iDic, succ, rightTiles))) - oCross.insert(Tile(Dic_char(iDic, succ))); - if (Dic_last(iDic, succ)) + if (iDic.isEndOfWord(iDic.charLookup(succ, rightTiles))) + oCross.insert(Tile(iDic.getChar(succ))); + if (iDic.isLast(succ)) break; } /* Points on the right part */ - /* yes, it is REALLY [index+1] */ - while (!iTiles[index+1].isEmpty()) + /* yes, it is REALLY [index + 1] */ + while (!iTiles[index + 1].isEmpty()) { index++; if (!iJoker[index]) @@ -104,7 +108,7 @@ static void Board_check(const Dictionary &iDic, if (!iTilesMx[i][j].isEmpty()) { iCrossMx[j][i].setNone(); - } + } else if (!iTilesMx[i][j - 1].isEmpty() || !iTilesMx[i][j + 1].isEmpty()) { @@ -119,7 +123,7 @@ static void Board_check(const Dictionary &iDic, else { iCrossMx[j][i].setAny(); - } + } } } } diff --git a/game/board_search.cpp b/game/board_search.cpp index 77424d6..f538476 100644 --- a/game/board_search.cpp +++ b/game/board_search.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,21 +39,17 @@ static void BoardSearchEvalMove(const Board &iBoard, Matrix &iJokerMx, Results &iResults, Round &iWord) { - int i, pts, ptscross; - int l, t, fromrack; - int len, row, col, wordmul; + unsigned int fromrack = 0; + int pts = 0; + int ptscross = 0; + int wordmul = 1; - fromrack = 0; - pts = 0; - ptscross = 0; - wordmul = 1; + unsigned int len = iWord.getWordLen(); - len = iWord.getWordLen(); + int row = iWord.getCoord().getRow(); + int col = iWord.getCoord().getCol(); - row = iWord.getCoord().getRow(); - col = iWord.getCoord().getCol(); - - for (i = 0; i < len; i++) + for (unsigned int i = 0; i < len; i++) { if (!iTilesMx[row][col+i].isEmpty()) { @@ -61,6 +58,7 @@ static void BoardSearchEvalMove(const Board &iBoard, } else { + int l; if (!iWord.isJoker(i)) l = iWord.getTile(i).getPoints() * iBoard.getLetterMultiplier(row, col + i); @@ -69,7 +67,7 @@ static void BoardSearchEvalMove(const Board &iBoard, pts += l; wordmul *= iBoard.getWordMultiplier(row, col + i); - t = iPointsMx[row][col+i]; + int t = iPointsMx[row][col+i]; if (t >= 0) ptscross += (t + l) * iBoard.getWordMultiplier(row, col + i); fromrack++; @@ -109,33 +107,40 @@ static void ExtendRight(const Board &iBoard, if (iTilesMx[iRow][iCol].isEmpty()) { - if (Dic_word(iDic, iNode) && iCol > iAnchor) + if (iDic.isEndOfWord(iNode) && iCol > iAnchor) + { BoardSearchEvalMove(iBoard, iTilesMx, iPointsMx, iJokerMx, iResults, ioPartialWord); + } - for (succ = Dic_succ(iDic, iNode); succ; succ = Dic_next(iDic, succ)) + // Optimization: avoid entering the for loop if no tile can match + if (iCrossMx[iRow][iCol].isNone()) + return; + + bool hasJokerInRack = iRack.in(Tile::Joker()); + for (succ = iDic.getSucc(iNode); succ; succ = iDic.getNext(succ)) { - l = Tile(Dic_char(iDic, succ)); + l = Tile(iDic.getChar(succ)); if (iCrossMx[iRow][iCol].check(l)) { if (iRack.in(l)) { iRack.remove(l); - ioPartialWord.addRightFromRack(l, 0); + ioPartialWord.addRightFromRack(l, false); ExtendRight(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, ioPartialWord, iResults, succ, iRow, iCol + 1, iAnchor); - ioPartialWord.removeRightToRack(l, 0); + ioPartialWord.removeRightToRack(l, false); iRack.add(l); } - if (iRack.in(Tile::Joker())) + if (hasJokerInRack) { iRack.remove(Tile::Joker()); - ioPartialWord.addRightFromRack(l, 1); + ioPartialWord.addRightFromRack(l, true); ExtendRight(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, ioPartialWord, iResults, succ, iRow, iCol + 1, iAnchor); - ioPartialWord.removeRightToRack(l, 1); + ioPartialWord.removeRightToRack(l, true); iRack.add(Tile::Joker()); } } @@ -144,15 +149,19 @@ static void ExtendRight(const Board &iBoard, else { l = iTilesMx[iRow][iCol]; - for (succ = Dic_succ(iDic, iNode); succ ; succ = Dic_next(iDic, succ)) + wint_t upperChar = towupper(l.toChar()); + for (succ = iDic.getSucc(iNode); succ ; succ = iDic.getNext(succ)) { - if (Dic_char(iDic, succ) == toupper(l.toChar())) + if ((wint_t)iDic.getChar(succ) == upperChar) { ioPartialWord.addRightFromBoard(l); ExtendRight(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, ioPartialWord, iResults, succ, iRow, iCol + 1, iAnchor); ioPartialWord.removeRightToBoard(l); + // The letter will be present only once in the dictionary, + // so we can stop looping + break; } } } @@ -177,31 +186,32 @@ static void LeftPart(const Board &iBoard, if (iLimit > 0) { - for (succ = Dic_succ(iDic, n); succ; succ = Dic_next(iDic, succ)) + bool hasJokerInRack = iRack.in(Tile::Joker()); + for (succ = iDic.getSucc(n); succ; succ = iDic.getNext(succ)) { - l = Tile(Dic_char(iDic, succ)); + l = Tile(iDic.getChar(succ)); if (iRack.in(l)) { iRack.remove(l); - ioPartialWord.addRightFromRack(l, 0); + ioPartialWord.addRightFromRack(l, false); ioPartialWord.accessCoord().setCol(ioPartialWord.getCoord().getCol() - 1); LeftPart(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, ioPartialWord, iResults, succ, iRow, iAnchor, iLimit - 1); ioPartialWord.accessCoord().setCol(ioPartialWord.getCoord().getCol() + 1); - ioPartialWord.removeRightToRack(l, 0); + ioPartialWord.removeRightToRack(l, false); iRack.add(l); } - if (iRack.in(Tile::Joker())) + if (hasJokerInRack) { iRack.remove(Tile::Joker()); - ioPartialWord.addRightFromRack(l, 1); + ioPartialWord.addRightFromRack(l, true); ioPartialWord.accessCoord().setCol(ioPartialWord.getCoord().getCol() - 1); LeftPart(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, ioPartialWord, iResults, succ, iRow, iAnchor, iLimit - 1); ioPartialWord.accessCoord().setCol(ioPartialWord.getCoord().getCol() + 1); - ioPartialWord.removeRightToRack(l, 1); + ioPartialWord.removeRightToRack(l, true); iRack.add(Tile::Joker()); } } @@ -221,10 +231,10 @@ static void BoardSearchAux(const Board &iBoard, int row, col, lastanchor; Round partialword; - list rackTiles; + vector rackTiles; iRack.getTiles(rackTiles); - list::const_iterator it; - bool match; + vector::const_iterator it; + vector::const_iterator itEnd; for (row = 1; row <= BOARD_DIM; row++) { @@ -237,7 +247,7 @@ static void BoardSearchAux(const Board &iBoard, if (iTilesMx[row][col].isEmpty() && (!iTilesMx[row][col - 1].isEmpty() || !iTilesMx[row][col + 1].isEmpty() || - !iTilesMx[row - 1][col].isEmpty() || + !iTilesMx[row - 1][col].isEmpty() || !iTilesMx[row + 1][col].isEmpty())) { #if defined(DONT_USE_SEARCH_OPTIMIZATION) @@ -261,13 +271,13 @@ static void BoardSearchAux(const Board &iBoard, // Optimization compared to the original Appel & Jacobson // algorithm: skip Leftpart if none of the tiles of the rack // matches the cross mask for the current anchor - match = false; - for (it = rackTiles.begin(); - !match && it != rackTiles.end(); it++) + bool match = false; + for (it = rackTiles.begin(); it != rackTiles.end(); it++) { if (iCrossMx[row][col].check(*it)) { match = true; + break; } } if (match) @@ -277,14 +287,14 @@ static void BoardSearchAux(const Board &iBoard, partialword.accessCoord().setCol(lastanchor + 1); ExtendRight(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, partialword, iResults, - Dic_root(iDic), row, lastanchor + 1, col); + iDic.getRoot(), row, lastanchor + 1, col); } else { partialword.accessCoord().setCol(col); LeftPart(iBoard, iDic, iTilesMx, iCrossMx, iPointsMx, iJokerMx, iRack, partialword, iResults, - Dic_root(iDic), row, col, col - + iDic.getRoot(), row, col, col - lastanchor - 1); } } @@ -328,8 +338,8 @@ void Board::searchFirst(const Dictionary &iDic, partialword.accessCoord().setDir(Coord::HORIZONTAL); LeftPart(*this, iDic, m_tilesRow, m_crossRow, m_pointRow, m_jokerRow, - copyRack, partialword, oResults, Dic_root(iDic), row, col, - copyRack.nTiles() - 1); + copyRack, partialword, oResults, iDic.getRoot(), row, col, + copyRack.getNbTiles() - 1); } /// Local Variables: diff --git a/game/coord.cpp b/game/coord.cpp index 8e2f793..900ccc0 100644 --- a/game/coord.cpp +++ b/game/coord.cpp @@ -1,21 +1,23 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file coord.cpp diff --git a/game/coord.h b/game/coord.h index 32c6fd1..7b303b6 100644 --- a/game/coord.h +++ b/game/coord.h @@ -1,21 +1,23 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file coord.h @@ -27,19 +29,24 @@ #ifndef _COORD_H #define _COORD_H -using std::string; +#include + using std::wstring; + +/** + * This class handles coordinates of a square on the board. + * The row and column start at 1. + */ class Coord { public: enum Direction {VERTICAL, HORIZONTAL}; - // Construction, destruction + // Construction Coord(int iRow = -1, int iCol = -1, Direction iDir = HORIZONTAL); Coord(const wstring &iStr); - virtual ~Coord() {} // Accessors void setRow(int iRow) { m_row = iRow; } diff --git a/game/cross.cpp b/game/cross.cpp index 32cb938..b9bf130 100644 --- a/game/cross.cpp +++ b/game/cross.cpp @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,26 +24,25 @@ #define CROSS_MASK 0xFFFFFFFF + Cross::Cross() { // The default behaviour is to match everything setAny(); } -void Cross::setAny() -{ + +void Cross::setAny() +{ m_mask = CROSS_MASK; } + bool Cross::isAny() const -{ - return m_mask == CROSS_MASK; +{ + return m_mask == CROSS_MASK; } -void Cross::setNone() -{ - m_mask = 0; -} string Cross::getHexContent() const { @@ -51,22 +52,21 @@ string Cross::getHexContent() const return s; } + bool Cross::check(const Tile& iTile) const { return (iTile.isJoker() && m_mask != 0) || (m_mask & (1 << iTile.toCode())); } + void Cross::insert(const Tile& iTile) -{ +{ m_mask |= (1 << iTile.toCode()); } + bool Cross::operator==(const Cross &iOther) const { - /* - * if (isAny() || iOther.isAny()) - * return isAny() && iOther.isAny(); - */ return m_mask == iOther.m_mask; } diff --git a/game/cross.h b/game/cross.h index 9a0beb2..e30a132 100644 --- a/game/cross.h +++ b/game/cross.h @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,13 +36,12 @@ class Cross { public: Cross(); - virtual ~Cross() {} - void setAny(); - void setNone(); + void setAny(); + void setNone() { m_mask = 0; } bool isAny() const; - bool isNone() const; + bool isNone() const { return m_mask == 0; } bool check(const Tile& iTile) const; diff --git a/game/debug.h b/game/debug.h index 244d31e..bcbd98b 100644 --- a/game/debug.h +++ b/game/debug.h @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet + * Eliot + * Copyright (C) 2004-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,8 +19,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ -#ifndef _CONST_H_ -#define _CONST_H_ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ /********** * General diff --git a/game/duplicate.cpp b/game/duplicate.cpp index ac07137..fe25eec 100644 --- a/game/duplicate.cpp +++ b/game/duplicate.cpp @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,78 +18,74 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +#include "duplicate.h" #include "dic.h" #include "tile.h" #include "rack.h" #include "round.h" +#include "move.h" #include "pldrack.h" #include "results.h" #include "player.h" #include "ai_player.h" -#include "duplicate.h" +#include "settings.h" #include "debug.h" -Duplicate::Duplicate(const Dictionary &iDic): Game(iDic) +Duplicate::Duplicate(const Dictionary &iDic) + : Game(iDic) { } -Duplicate::~Duplicate() -{ -} - - -int Duplicate::setRackRandom(int p, bool iCheck, set_rack_mode mode) -{ - int res; - do - { - res = helperSetRackRandom(p, iCheck, mode); - } while (res == 2); - return res; -} - - int Duplicate::play(const wstring &iCoord, const wstring &iWord) { - /* Perform all the validity checks, and fill a round */ + // Perform all the validity checks, and try to fill a round Round round; int res = checkPlayedWord(iCoord, iWord, round); - if (res != 0) + if (res != 0 && Settings::Instance().getBool("duplicate-reject-invalid")) { return res; } - /* Everything is OK, we can play the word */ - playRound(round, m_currPlayer); + // If we reach this point, either the move is valid and we can use the + // "round" variable, or it is invalid but played nevertheless + if (res == 0) + { + // Everything is OK, we can play the word + playMove(Move(round), m_currPlayer); + } + else + { + // Record the invalid move of the player + playMove(Move(iWord, iCoord), m_currPlayer); + } - /* Next turn */ - // XXX: Should it be done by the interface instead? - endTurn(); + // Little hack to handle duplicate games with only AI players. + // This will have no effect when there is at least one human player + tryEndTurn(); return 0; } -void Duplicate::duplicateAI(int n) +void Duplicate::playAI(unsigned int p) { - ASSERT(0 <= n && n < getNPlayers(), "Wrong player number"); - ASSERT(!m_players[n]->isHuman(), "AI requested for a human player"); + ASSERT(p < getNPlayers(), "Wrong player number"); - AIPlayer *player = static_cast(m_players[n]); - player->compute(*m_dic, m_board, m_history.getSize()); + AIPlayer *player = dynamic_cast(m_players[p]); + ASSERT(player != NULL, "AI requested for a human player"); - if (player->changesLetters()) + player->compute(m_dic, m_board, m_history.beforeFirstRound()); + const Move move = player->getMove(); + if (move.getType() == Move::CHANGE_LETTERS || + move.getType() == Move::PASS) { - // The AI player has nothing to play. This should not happen in - // duplicate mode, otherwise the implementation of the AI is buggy... - ASSERT(false, "AI player has nothing to play!"); - } - else - { - playRound(player->getChosenRound(), n); + // The AI player must be buggy... + ASSERT(false, "AI tried to cheat!"); } + + playMove(move, p); } @@ -96,165 +93,155 @@ int Duplicate::start() { ASSERT(getNPlayers(), "Cannot start a game without any player"); + // Arbitrary player, since they should all have the same rack m_currPlayer = 0; - /* XXX: code similar with endTurnForReal() */ - /* Complete the rack for the player that just played */ - int res = setRackRandom(m_currPlayer, true, RACK_NEW); - /* End of the game? */ + // Complete the rack for the player that just played + int res = helperSetRackRandom(m_currPlayer, true, RACK_NEW); + // End of the game? if (res == 1) { - end(); + endGame(); return 1; } const PlayedRack& pld = m_players[m_currPlayer]->getCurrentRack(); - /* All the players have the same rack */ - for (int i = 0; i < getNPlayers(); i++) + // All the players have the same rack + for (unsigned int i = 0; i < getNPlayers(); i++) { if (i != m_currPlayer) { m_players[i]->setCurrentRack(pld); } - /* Nobody has played yet in this round */ + // Nobody has played yet in this round m_hasPlayed[i] = false; } - /* Next turn */ - // XXX: Should it be done by the interface instead? - endTurn(); + // Little hack to handle duplicate games with only AI players. + // This will have no effect when there is at least one human player + tryEndTurn(); return 0; } -/* - * This function does not terminate the turn itself, but performs some - * checks to know whether or not it should be terminated (with a call to - * endTurnForReal()). - * - * For the turn to be terminated, all the players must have played. - * Since the AI players play after the human players, we check whether - * one of the human players has not played yet: - * - if so, we have nothing to do (we are waiting for him) - * - if not (all human players have played), the AI players can play, - * and we finish the turn. - */ -int Duplicate::endTurn() +void Duplicate::tryEndTurn() { - int i; - for (i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { if (m_players[i]->isHuman() && !m_hasPlayed[i]) { - /* A human player has not played... */ + // A human player has not played... m_currPlayer = i; - // XXX: check return code meaning - return 1; + // So we don't finish the turn + return; } } - /* If all the human players have played */ - if (i == getNPlayers()) + // Now that all the human players have played, + // make AI players play their turn + for (unsigned int i = 0; i < getNPlayers(); i++) { - /* Make AI players play their turn */ - for (i = 0; i < getNPlayers(); i++) + if (!m_players[i]->isHuman()) { - if (!m_players[i]->isHuman()) - { - duplicateAI(i); - } + playAI(i); } - - /* Next turn */ - endTurnForReal(); } - // XXX: check return code meaning - return 0; + // Next turn + endTurn(); } -void Duplicate::playRound(const Round &iRound, int n) +void Duplicate::playMove(const Move &iMove, unsigned int p) { - ASSERT(0 <= n && n < getNPlayers(), "Wrong player number"); - Player *player = m_players[n]; + ASSERT(p < getNPlayers(), "Wrong player number"); - /* Update the rack and the score of the current player */ - player->addPoints(iRound.getPoints()); - player->endTurn(iRound, m_history.getSize()); + // Update the rack and the score of the playing player + m_players[p]->endTurn(iMove, m_history.getSize()); - m_hasPlayed[n] = true; + m_hasPlayed[p] = true; } -/* - * This function really changes the turn, i.e. the best word is played and - * a new rack is given to the players. - * We suppose that all the players have finished to play for this turn (this - * should have been checked by endturn()) - */ -int Duplicate::endTurnForReal() +void Duplicate::endTurn() { - int res, i, imax; - - /* Play the best word on the board */ - imax = 0; - for (i = 1; i < getNPlayers(); i++) + // Find the player with the best score + unsigned int imax = 0; + for (unsigned int i = 1; i < getNPlayers(); i++) { - if (m_players[i]->getLastRound().getPoints() > - m_players[imax]->getLastRound().getPoints()) + if (m_players[i]->getLastMove().getScore() > + m_players[imax]->getLastMove().getScore()) { imax = i; } } - m_currPlayer = imax; - helperPlayRound(m_players[imax]->getLastRound()); - /* Complete the rack for the player that just played */ - res = setRackRandom(imax, true, RACK_NEW); - /* End of the game? */ - if (res == 1) + // TODO: do something if nobody played a valid round! + + // Handle solo bonus + // First check whether there are enough players in the game for the + // bonus to apply + int minNbPlayers = Settings::Instance().getInt("duplicate-solo-players"); + if (getNPlayers() >= (unsigned int)minNbPlayers && + m_players[imax]->getLastMove().getType() == Move::VALID_ROUND) { - end(); - return 1; + int maxScore = m_players[imax]->getLastMove().getScore(); + // Find whether other players than imax have the same score + bool otherWithSameScore = false; + for (unsigned int i = imax + 1; i < getNPlayers(); i++) + { + if (m_players[i]->getLastMove().getScore() >= maxScore) + { + otherWithSameScore = true; + break; + } + } + if (!otherWithSameScore) + { + // Give the bonus to player imax + int bonus = Settings::Instance().getInt("duplicate-solo-value"); + m_players[imax]->addPoints(bonus); + // TODO: keep a trace of the solo, so the interface + // can be aware of it... + } } + // Play the best word on the board + helperPlayMove(imax, m_players[imax]->getLastMove()); + + // Leave the same reliquate to all players + // This is required by the start() method which will be called to + // start the next turn const PlayedRack& pld = m_players[imax]->getCurrentRack(); - /* All the players have the same rack */ - for (i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { if (i != imax) { m_players[i]->setCurrentRack(pld); } - /* Nobody has played yet in this round */ - m_hasPlayed[i] = false; } - /* XXX: Little hack to handle the games with only AI players. - * This will have no effect when there is at least one human player */ - endTurn(); - - return 0; + // Start next turn... + start(); } -void Duplicate::end() +void Duplicate::endGame() { m_finished = true; } -int Duplicate::setPlayer(int n) +int Duplicate::setPlayer(unsigned int p) { - ASSERT(0 <= n && n < getNPlayers(), "Wrong player number"); + ASSERT(p < getNPlayers(), "Wrong player number"); - /* Forbid switching to an AI player */ - if (!m_players[n]->isHuman()) + // Forbid switching to an AI player + if (!m_players[p]->isHuman()) return 1; - m_currPlayer = n; + m_currPlayer = p; return 0; } diff --git a/game/duplicate.h b/game/duplicate.h index 19fa8b1..f969f6f 100644 --- a/game/duplicate.h +++ b/game/duplicate.h @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +35,7 @@ using std::wstring; * and rack are updated. He cannot change his word afterwards. * - if there is still a human player who has not played for the current * turn, we wait for him - * - if all the human players have played, it's the turn to the AI players + * - if all the human players have played, it's the turn of the AI players * (currently handled in a loop, but we could imagine that they are running * in their own thread). * - once all the players have played, we can really end the turn: @@ -57,26 +58,73 @@ public: /************************* * Game handling *************************/ + /** + * In Duplicate mode, the start() method starts a new turn, and is + * automatically called when the previous turn is finished. + * + * Pre-requisite: all the players must have the same rack when this + * method is called + */ virtual int start(); - virtual int setRackRandom(int, bool, set_rack_mode); - virtual int play(const wstring &iCoord, const wstring &iWord); - virtual int endTurn(); - int setPlayer(int); - // Switch to the previous human player who has not played yet + /** + * See description of Game::play() for the possible return values. + * Note that if the "duplicate-reject-invalid" setting is set to false + * the method always returns 0 (the player will have 0 for this turn) + */ + virtual int play(const wstring &iCoord, const wstring &iWord); + + /** + * Set the current player, given its ID. + * The given player ID must correspond to a human player, which did not + * play yet for this turn. + * Possible return values: + * 0: everything went fine + * 1: the player is not human + */ + int setPlayer(unsigned int p); + + /// Switch to the previous human player who has not played yet void prevHumanPlayer(); - // Switch to the next human player who has not played yet + + /// Switch to the next human player who has not played yet void nextHumanPlayer(); private: - // Private constructor and destructor to force using the GameFactory class + // Private constructor to force using the GameFactory class Duplicate(const Dictionary &iDic); - virtual ~Duplicate(); - void playRound(const Round &iRound, int n); - int endTurnForReal(); - void end(); - void duplicateAI(int n); + void playMove(const Move &iMove, unsigned int p); + + /// Make the AI player whose ID is p play its turn + void playAI(unsigned int p); + + /** + * This function does not terminate the turn itself, but performs some + * checks to know whether or not it should be terminated (with a call to + * endTurn()). + * + * For the turn to be terminated, all the players must have played. + * Since the AI players play after the human players, we check whether + * one of the human players has not played yet: + * - if so, we have nothing to do (we are waiting for him/her) + * - if not (all human players have played), the AI players can play, + * and we finish the turn. + */ + void tryEndTurn(); + + /** + * This function really changes the turn, i.e. the best word is played, + * the game history is updated, a "solo" bonus is given if needed, and + * all racks are made equal to the one of the player who played the + * best move. + * We suppose here that all the players have finished to play for this + * turn (this should have been checked by tryEndturn()) + */ + void endTurn(); + + /// Finish the game + void endGame(); // m_hasPlayed[p] is true iff player p has played for this turn map m_hasPlayed; diff --git a/game/encoding.cpp b/game/encoding.cpp deleted file mode 100644 index 274da98..0000000 --- a/game/encoding.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file encoding.cpp - * \brief Utility functions to ease manipulation of wide-character strings - * \author Olivier Teuliere - * \date 2005 - */ - -#include -#include -#include -#include -#include "encoding.h" - - -int _wtoi(const wchar_t *iWStr) -{ - return wcstol(iWStr,NULL,10); -} - - -int _swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...) -{ - int res; - va_list argp; - va_start(argp, format); -#ifdef WIN32 - // Mingw32 does not take the maxlen argument - res = vswprintf(wcs, format, argp); -#else - res = vswprintf(wcs, maxlen, format, argp); -#endif - va_end(argp); - return res; -} - - -wstring convertToWc(const string& iStr) -{ - // Get the needed length (we _can't_ use string::size()) - size_t len = mbstowcs(NULL, iStr.c_str(), 0); - if (len == (size_t)-1) - return L""; - - wchar_t *tmp = new wchar_t[len + 1]; - len = mbstowcs(tmp, iStr.c_str(), len + 1); - wstring res = tmp; - delete[] tmp; - - return res; -} - - -string convertToMb(const wstring& iWStr) -{ - // Get the needed length (we _can't_ use wstring::size()) - size_t len = wcstombs(NULL, iWStr.c_str(), 0); - if (len == (size_t)-1) - return ""; - - char *tmp = new char[len + 1]; - len = wcstombs(tmp, iWStr.c_str(), len + 1); - string res = tmp; - delete[] tmp; - - return res; -} - - -string convertToMb(wchar_t iWChar) -{ - char res[MB_CUR_MAX + 1]; - int len = wctomb(res, iWChar); - if (len == -1) - return ""; - res[len] = '\0'; - - return res; -} - diff --git a/game/encoding.h b/game/encoding.h deleted file mode 100644 index 3fc0c92..0000000 --- a/game/encoding.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -/** - * \file encoding.h - * \brief Utility functions to ease manipulation of wide-character strings - * \author Olivier Teuliere - * \date 2005 - */ - -#ifndef _ENCODING_H_ -#define _ENCODING_H_ - -#include - -using std::string; -using std::wstring; - - -/// Equivalent of atoi for wide-caracter strings -int _wtoi(const wchar_t *iWStr); - -/// Equivalent of swprintf, but working also with mingw32 -int _swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...); - -/// Convert a multi-byte string into a wide-character string -wstring convertToWc(const string& iStr); - -/// Convert a wide-character string into a multi-byte string -string convertToMb(const wstring& iWStr); - -/// Convert a wide character into a multi-byte string -string convertToMb(wchar_t iWChar); - -#endif - diff --git a/game/freegame.cpp b/game/freegame.cpp index 03ee459..0514b14 100644 --- a/game/freegame.cpp +++ b/game/freegame.cpp @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,89 +20,88 @@ #include #include + +#include "freegame.h" #include "dic.h" #include "tile.h" #include "rack.h" #include "round.h" +#include "move.h" #include "pldrack.h" #include "results.h" #include "player.h" #include "ai_player.h" -#include "freegame.h" +#include "settings.h" #include "debug.h" -FreeGame::FreeGame(const Dictionary &iDic): Game(iDic) +FreeGame::FreeGame(const Dictionary &iDic) + : Game(iDic) { } -FreeGame::~FreeGame() -{ -} - - -int FreeGame::setRackRandom(int p, bool iCheck, set_rack_mode mode) -{ - int res; - do - { - res = helperSetRackRandom(p, iCheck, mode); - } while (res == 2); - return res; -} - - int FreeGame::play(const wstring &iCoord, const wstring &iWord) { - /* Perform all the validity checks, and fill a round */ + // Perform all the validity checks, and try to fill a round Round round; - int res = checkPlayedWord(iCoord, iWord, round); - if (res != 0) + if (res != 0 && Settings::Instance().getBool("freegame-reject-invalid")) { return res; } - /* Update the rack and the score of the current player */ - m_players[m_currPlayer]->addPoints(round.getPoints()); - m_players[m_currPlayer]->endTurn(round, m_history.getSize()); + // If we reach this point, either the move is valid and we can use the + // "round" variable, or it is invalid but played nevertheless + if (res == 0) + { + Move move(round); - /* Everything is OK, we can play the word */ - helperPlayRound(round); + // Update the rack and the score of the current player + m_players[m_currPlayer]->endTurn(move, m_history.getSize()); - /* Next turn */ - // XXX: Should it be done by the interface instead? + // Everything is OK, we can play the word + helperPlayMove(m_currPlayer, move); + } + else + { + Move move(iWord, iCoord); + + // Record the invalid move of the player + m_players[m_currPlayer]->endTurn(move, m_history.getSize()); + + // Update the game + helperPlayMove(m_currPlayer, move); + } + + // Next turn endTurn(); return 0; } -void FreeGame::freegameAI(int n) +void FreeGame::playAI(unsigned int p) { - ASSERT(0 <= n && n < getNPlayers(), "Wrong player number"); - ASSERT(!m_players[n]->isHuman(), "AI requested for a human player"); + ASSERT(p < getNPlayers(), "Wrong player number"); + ASSERT(!m_players[p]->isHuman(), "AI requested for a human player"); - AIPlayer *player = static_cast(m_players[n]); + AIPlayer *player = static_cast(m_players[p]); - player->compute(*m_dic, m_board, m_history.getSize()); - if (player->changesLetters()) + player->compute(m_dic, m_board, m_history.beforeFirstRound()); + const Move move = player->getMove(); + if (move.getType() == Move::CHANGE_LETTERS || + move.getType() == Move::PASS) { - helperPass(player->getChangedLetters(), n); - endTurn(); + ASSERT(checkPass(move.getChangedLetters(), p) == 0, "AI tried to cheat!"); } - else - { - const Round &round = player->getChosenRound(); - /* Update the rack and the score of the current player */ - player->addPoints(round.getPoints()); - player->endTurn(round, m_history.getSize()); - helperPlayRound(round); - endTurn(); - } + // Update the rack and the score of the current player + player->endTurn(move, m_history.getSize()); + + helperPlayMove(p, move); + endTurn(); } @@ -109,19 +109,18 @@ int FreeGame::start() { ASSERT(getNPlayers(), "Cannot start a game without any player"); - /* Set the initial racks of the players */ - for (int i = 0; i < getNPlayers(); i++) + // Set the initial racks of the players + for (unsigned int i = 0; i < getNPlayers(); i++) { - setRackRandom(i, false, RACK_NEW); + helperSetRackRandom(i, false, RACK_NEW); } - // XXX m_currPlayer = 0; - /* If the first player is an AI, make it play now */ - if (!m_players[0]->isHuman()) + // If the first player is an AI, make it play now + if (!m_players[m_currPlayer]->isHuman()) { - freegameAI(0); + playAI(m_currPlayer); } return 0; @@ -130,21 +129,21 @@ int FreeGame::start() int FreeGame::endTurn() { - /* Complete the rack for the player that just played */ - if (setRackRandom(m_currPlayer, false, RACK_NEW) == 1) + // Complete the rack for the player that just played + if (helperSetRackRandom(m_currPlayer, false, RACK_NEW) == 1) { - /* End of the game */ - end(); + // End of the game + endGame(); return 1; } - /* Next player */ + // Next player nextPlayer(); - /* If this player is an AI, make it play now */ + // If this player is an AI, make it play now if (!m_players[m_currPlayer]->isHuman()) { - freegameAI(m_currPlayer); + playAI(m_currPlayer); } return 0; @@ -152,7 +151,7 @@ int FreeGame::endTurn() // Adjust the scores of the players with the points of the remaining tiles -void FreeGame::end() +void FreeGame::endGame() { vector tiles; @@ -165,12 +164,12 @@ void FreeGame::end() // We currently handle case 1, and cannot handle case 3 until timers are // implemented. // For case 2, we need both to detect a blocked situation (not easy...) and - // to handle it in the end() method (very easy). + // to handle it in the endGame() method (very easy). /* Add the points of the remaining tiles to the score of the current * player (i.e. the first player with an empty rack), and remove them * from the score of the players who still have tiles */ - for (int i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { if (i != m_currPlayer) { @@ -184,73 +183,73 @@ void FreeGame::end() } } - /* Lock game */ + // Lock game m_finished = true; } -int FreeGame::pass(const wstring &iToChange, int n) +int FreeGame::checkPass(const wstring &iToChange, unsigned int p) const { + ASSERT(p < getNPlayers(), "Wrong player number"); + + // Check that the game is not finished if (m_finished) return 3; + // Check that the letters are valid for the current dictionary + if (!m_dic.validateLetters(iToChange)) + return 4; + + // It is forbidden to change letters when the bag does not contain at + // least 7 letters (this is explicitly stated in the ODS). But it is + // still allowed to pass + Bag bag(m_dic); + realBag(bag); + if (bag.getNbTiles() < 7 && !iToChange.empty()) + { + return 1; + } + + // Check that the letters are all present in the player's rack + Player *player = m_players[p]; + PlayedRack pld = player->getCurrentRack(); + Rack rack; + pld.getRack(rack); + for (unsigned int i = 0; i < iToChange.size(); i++) + { + // Remove the letter from the rack + if (!rack.in(Tile(iToChange[i]))) + { + return 2; + } + rack.remove(Tile(iToChange[i])); + } + // According to the rules in the ODS, it is allowed to pass its turn (no // need to change letters for that). // TODO: However, if all the players pass their turn, the first one has to // play, or change at least one letter. To implement this behaviour, we // must also take care of blocked positions, where no one _can_ play (see - // also comment in the end() method). + // also comment in the endGame() method). - // Convert the string into tiles - vector tilesVect; - for (unsigned int i = 0; i < iToChange.size(); i++) - { - Tile tile(towupper(iToChange[i])); - tilesVect.push_back(tile); - } - - int res = helperPass(tilesVect, n); - if (res == 0) - endTurn(); - return res; + return 0; } -int FreeGame::helperPass(const vector &iToChange, int n) +int FreeGame::pass(const wstring &iToChange) { - ASSERT(0 <= n && n < getNPlayers(), "Wrong player number"); + int res = checkPass(iToChange, m_currPlayer); + if (res != 0) + return res; - // It is forbidden to change letters when the bag does not contain at - // least 7 letters (this is explicitely stated in the ODS). - Bag bag; - realBag(bag); - if (bag.nTiles() < 7) - { - return 1; - } + Move move(iToChange); + // End the player's turn + m_players[m_currPlayer]->endTurn(move, m_history.getSize()); + // Update the game + helperPlayMove(m_currPlayer, move); - Player *player = m_players[n]; - PlayedRack pld = player->getCurrentRack(); - Rack rack; - pld.getRack(rack); - - for (unsigned int i = 0; i < iToChange.size(); i++) - { - /* Remove the letter from the rack */ - if (!rack.in(iToChange[i])) - { - return 2; - } - rack.remove(iToChange[i]); - } - - pld.reset(); - pld.setOld(rack); - - player->setCurrentRack(pld); - - // FIXME: the letters to change should not be in the bag while generating - // the new rack! + // Next game turn + endTurn(); return 0; } diff --git a/game/freegame.h b/game/freegame.h index 8be7581..9bc0be2 100644 --- a/game/freegame.h +++ b/game/freegame.h @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,20 +49,54 @@ public: /************************* * Game handling *************************/ + /** + * Start the game. + * Possible return values: + * 0: everything went fine + */ virtual int start(); - virtual int setRackRandom(int, bool, set_rack_mode); + + /** + * See description of Game::play() for the possible return values. + * Note that if the "freegame-reject-invalid" setting is set to false + * the method always returns 0 (the player will have 0 for this turn) + */ virtual int play(const wstring &iCoord, const wstring &iWord); - virtual int endTurn(); - int pass(const wstring &iToChange, int n); + + /** + * Pass the turn, changing the letters listed in iToChange. + * If you simply want to pass the turn without changing any letter, + * provide an empty string. + * + * Possible return values: + * 0: everything went fine + * 1: changing letters is not allowed if there are less than 7 tiles + * left in the bag + * 2: the rack of the current player does not contain all the + * listed letters + * 3: the game is already finished + * 4: some letters are invalid for the current dictionary + */ + int pass(const wstring &iToChange); private: - // Private constructor and destructor to force using the GameFactory class + // Private constructor to force using the GameFactory class FreeGame(const Dictionary &iDic); - virtual ~FreeGame(); - void freegameAI(int n); - void end(); - int helperPass(const vector &iToChange, int n); + /// Make the AI player whose ID is p play its turn + void playAI(unsigned int p); + + /// Finish the current turn + int endTurn(); + + /// Finish the game + void endGame(); + + /** + * Check whether it is legal to change the letters of iToChange. + * The return codes are the same as the ones on the pass() method + */ + int checkPass(const wstring &iToChange, unsigned int p) const; }; #endif /* _FREEGAME_H_ */ diff --git a/game/game.cpp b/game/game.cpp index 0659238..3990d3d 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +20,6 @@ *****************************************************************************/ #include "dic.h" -#include "dic_search.h" #include "tile.h" #include "rack.h" #include "round.h" @@ -35,65 +35,70 @@ #include "debug.h" -const int Game::RACK_SIZE = 7; +const unsigned int Game::RACK_SIZE = 7; const int Game::BONUS_POINTS = 50; Game::Game(const Dictionary &iDic): - m_dic(&iDic) + m_dic(iDic), m_bag(iDic) { m_variant = kNONE; m_points = 0; - m_currPlayer = -1; + m_currPlayer = 0; m_finished = false; } Game::~Game() { - for (int i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { delete m_players[i]; } } -const Player& Game::getPlayer(int iNum) const +const Player& Game::getPlayer(unsigned int iNum) const { - ASSERT(0 <= iNum && iNum < (int)m_players.size(), "Wrong player number"); + ASSERT(iNum < m_players.size(), "Wrong player number"); return *(m_players[iNum]); } -/* This function plays a round on the board */ -int Game::helperPlayRound(const Round &iRound) +void Game::helperPlayMove(unsigned int iPlayerId, const Move &iMove) { - /* - * We remove tiles from the bag only when they are played - * on the board. When going back in the game, we must only - * replace played tiles. - * We test a rack when it is set but tiles are left in the bag. - */ - // History of the game - m_history.setCurrentRack(getCurrentPlayer().getLastRack()); - m_history.playRound(m_currPlayer, m_history.getSize(), iRound); + m_history.setCurrentRack(getPlayer(iPlayerId).getLastRack()); + m_history.playMove(iPlayerId, m_history.getSize(), iMove); - debug(" helper: %d points\n",iRound.getPoints()); - m_points += iRound.getPoints(); + // Points + debug(" helper: %d points\n", iMove.getScore()); + m_points += iMove.getScore(); + // For moves corresponding to a valid round, we have much more + // work to do... + if (iMove.getType() == Move::VALID_ROUND) + { + helperPlayRound(iPlayerId, iMove.getRound()); + } +} + + + +void Game::helperPlayRound(unsigned int iPlayerId, const Round &iRound) +{ // Before updating the bag and the board, if we are playing a "joker game", // we replace in the round the joker by the letter it represents // This is currently done by a succession of ugly hacks :-/ if (m_variant == kJOKER) { - for (int i = 0; i < iRound.getWordLen(); i++) + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (iRound.isPlayedFromRack(i) && iRound.isJoker(i)) { // Is the represented letter still available in the bag? - // FIXME: this way to get the represented letter sucks... + // XXX: this way to get the represented letter sucks... Tile t(towupper(iRound.getTile(i).toChar())); - Bag bag; + Bag bag(m_dic); realBag(bag); // FIXME: realBag() does not give us a real bag in this // particular case! This is because Player::endTurn() is called @@ -108,12 +113,12 @@ int Game::helperPlayRound(const Round &iRound) // There is a big design problem here, but i am unsure what is // the best way to fix it. vector tiles; - getPlayer(m_currPlayer).getCurrentRack().getAllTiles(tiles); + getPlayer(iPlayerId).getCurrentRack().getAllTiles(tiles); for (unsigned int j = 0; j < tiles.size(); j++) { bag.replaceTile(tiles[j]); } - getPlayer(m_currPlayer).getLastRack().getAllTiles(tiles); + getPlayer(iPlayerId).getLastRack().getAllTiles(tiles); for (unsigned int j = 0; j < tiles.size(); j++) { bag.takeTile(tiles[j]); @@ -128,12 +133,19 @@ int Game::helperPlayRound(const Round &iRound) // rounds const_cast(iRound).setJoker(i, false); } + + // In a joker game we should have only 1 joker in the rack + break; } } } - // Update the bag and the board - for (int i = 0; i < iRound.getWordLen(); i++) + // Update the bag + // We remove tiles from the bag only when they are played + // on the board. When going back in the game, we must only + // replace played tiles. + // We test a rack when it is set but tiles are left in the bag. + for (unsigned int i = 0; i < iRound.getWordLen(); i++) { if (iRound.isPlayedFromRack(i)) { @@ -147,65 +159,53 @@ int Game::helperPlayRound(const Round &iRound) } } } - m_board.addRound(*m_dic, iRound); - return 0; + // Update the board + m_board.addRound(m_dic, iRound); } -int Game::back(int n) +int Game::back(unsigned int n) { - int i, j; - Player *player; - - if (n < 0) - { - debug("Game::back negative argument\n"); - n = -n; - } debug("Game::back %d\n",n); - for (i = 0; i < n; i++) + // TODO: throw an exception + if (m_history.getSize() < n) + return 1; + + for (unsigned int i = 0; i < n; i++) { - if (m_history.getSize() > 0) + prevPlayer(); + const Move &lastMove = m_history.getPreviousTurn().getMove(); + // Nothing to cancel if the move was not a valid round + if (lastMove.getType() != Move::VALID_ROUND) + continue; + + const Round &lastround = lastMove.getRound(); + debug("Game::back last round %s\n", + convertToMb(lastround.toString()).c_str()); + /* Remove the word from the board, and put its letters back + * into the bag */ + m_board.removeRound(m_dic, lastround); + for (unsigned int j = 0; j < lastround.getWordLen(); j++) { - prevPlayer(); - player = m_players[m_currPlayer]; - const Round &lastround = m_history.getPreviousTurn().getRound(); - debug("Game::back last round %s\n", - convertToMb(lastround.toString()).c_str()); - /* Remove the word from the board, and put its letters back - * into the bag */ - m_board.removeRound(*m_dic, lastround); - for (j = 0; j < lastround.getWordLen(); j++) + if (lastround.isPlayedFromRack(j)) { - if (lastround.isPlayedFromRack(j)) - { - if (lastround.isJoker(j)) - m_bag.replaceTile(Tile::Joker()); - else - m_bag.replaceTile(lastround.getTile(j)); - } + if (lastround.isJoker(j)) + m_bag.replaceTile(Tile::Joker()); + else + m_bag.replaceTile(lastround.getTile(j)); } - /* Remove the points of this round */ - player->addPoints(- lastround.getPoints()); - m_points -= lastround.getPoints(); - /* Remove the turns */ - player->removeLastTurn(); - m_history.removeLastTurn(); - } - else - { - return 1; } + /* Remove the points of this round */ + m_points -= lastround.getPoints(); + /* Remove the turns */ + m_players[m_currPlayer]->removeLastTurn(); + m_history.removeLastTurn(); } return 0; } -/** - * The realBag is the current bag minus all the racks - * present in the game. It represents the actual - * letters that are left in the bag. - */ + void Game::realBag(Bag &ioBag) const { vector tiles; @@ -217,7 +217,7 @@ void Game::realBag(Bag &ioBag) const if (getMode() == kFREEGAME) { /* In freegame mode, take the letters from all the racks */ - for (int i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { getPlayer(i).getCurrentRack().getAllTiles(tiles); for (unsigned int j = 0; j < tiles.size(); j++) @@ -239,22 +239,36 @@ void Game::realBag(Bag &ioBag) const } -int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) +int Game::helperSetRackRandom(unsigned int p, bool iCheck, set_rack_mode mode) { - ASSERT(0 <= p && p < getNPlayers(), "Wrong player number"); + ASSERT(p < getNPlayers(), "Wrong player number"); + // FIXME: RACK_MANUAL shouldn't be in the enum + ASSERT(mode != RACK_MANUAL, "Invalid rack mode"); - int nold, min; + // When iCheck is true, we must make sure that there are at least 2 vowels + // and 2 consonants in the rack up to the 15th turn, and at least one of + // each starting from the 16th turn. + // So before trying to fill the rack, we'd better make sure there is a way + // to complete the rack with these constraints... + unsigned int min = 0; + if (iCheck) + { + // 2 vowels and 2 consonants are needed up to the 15th turn + if (m_history.getSize() < 15) + min = 2; + else + min = 1; + } // Make a copy of the current player's rack PlayedRack pld = getPlayer(p).getCurrentRack(); - nold = pld.nOld(); + int nold = pld.getNbOld(); // Create a copy of the bag in which we can do everything we want, // and take from it the tiles of the players rack so that "bag" // contains the right number of tiles. - Bag bag; + Bag bag(m_dic); realBag(bag); - if (mode == RACK_NEW && nold != 0) { // We may have removed too many letters from the bag (i.e. the 'new' @@ -286,8 +300,212 @@ int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) debug("Game::helperSetRackRandom not a random mode\n"); } + // Get the tiles remaining on the rack + vector tiles; + pld.getOldTiles(tiles); + ASSERT(tiles.size() < RACK_SIZE, + "Cannot complete the rack, it is already complete"); + + bool jokerAdded = false; + // Are we dealing with a normal game or a joker game? + if (m_variant == kJOKER) + { + // 1) Is there already a joker in the remaining letters of the rack? + bool jokerFound = false; + for (unsigned int i = 0; i < tiles.size(); i++) + { + if (tiles[i].isJoker()) + { + jokerFound = true; + break; + } + } + + // 2) If there was no joker, we add one if possible + if (!jokerFound && bag.in(Tile::Joker())) + { + jokerAdded = true; + pld.addNew(Tile::Joker()); + tiles.push_back(Tile::Joker()); + } + + // 3) Remove all the jokers from the bag, to avoid taking another one + for (unsigned int i = 0; i < bag.in(Tile::Joker()); ++i) + { + bag.takeTile(Tile::Joker()); + } + } + + // Nothing in the rack, nothing in the bag --> end of the (free)game + if (bag.getNbTiles() == 0 && pld.getNbTiles() == 0) + { + return 1; + } + // End of game condition + if (iCheck) + { + if (bag.getNbVowels() == 0 || bag.getNbConsonants() == 0 || + bag.getNbTiles() == 1) + { + return 1; + } + } + + // Handle reject: + // Now that the joker has been dealt with, we try to complete the rack + // with truly random tiles. If it meets the requirements (i.e. if there + // are at least "min" vowels and "min" consonants in the rack), fine. + // Otherwise, we reject the rack completely, and we try again + // to complete it, but this time we ensure by construction that the + // requirements will be met. + while (bag.getNbTiles() != 0 && pld.getNbTiles() < RACK_SIZE) + { + Tile l = bag.selectRandom(); + bag.takeTile(l); + pld.addNew(l); + } + + if (!pld.checkRack(min, min)) + { + // Bad luck... we have to reject the rack + vector rejectedTiles; + pld.getAllTiles(rejectedTiles); + for (unsigned int i = 0; i < rejectedTiles.size(); i++) + { + bag.replaceTile(rejectedTiles[i]); + } + pld.reset(); + // Do not mark the rack as rejected if it was empty + if (nold > 0) + pld.setReject(); + + // Restore the joker if we are in a joker game + if (jokerAdded) + pld.addNew(Tile::Joker()); + + // Count the needed consonants and vowels in the rack + // (i.e. minimum required, minus what we already have in the rack) + unsigned int neededVowels = min; + unsigned int neededConsonants = min; + for (unsigned int i = 0; i < tiles.size(); ++i) + { + if (neededVowels > 0 && tiles[i].isVowel()) + neededVowels--; + if (neededConsonants > 0 && tiles[i].isConsonant()) + neededConsonants--; + } + + // Check whether it is possible to complete the rack properly + if (bag.getNbVowels() < neededVowels || + bag.getNbConsonants() < neededConsonants) + { + return 1; + } + + // RACK_SIZE - tiles.size() is the number of letters to add to the rack + if (neededVowels > RACK_SIZE - tiles.size() || + neededConsonants > RACK_SIZE - tiles.size()) + { + // We cannot fill the rack with enough vowels or consonants! + // Actually this should never happen, but it doesn't hurt to check... + // FIXME: this test is not completely right, because it supposes no + // letter can be at the same time a vowel and a consonant + return 3; + } + + // Get the required vowels and consonants first + for (unsigned int i = 0; i < neededVowels; ++i) + { + Tile l = bag.selectRandomVowel(); + bag.takeTile(l); + pld.addNew(l); + // Handle the case where the vowel can also be considered + // as a consonant + if (l.isConsonant() && neededConsonants > 0) + neededConsonants--; + } + for (unsigned int i = 0; i < neededConsonants; ++i) + { + Tile l = bag.selectRandomConsonant(); + bag.takeTile(l); + pld.addNew(l); + } + + // The difficult part is done: + // - we have handled joker games + // - we have handled the checks + // Now complete the rack with truly random letters + while (bag.getNbTiles() != 0 && pld.getNbTiles() < RACK_SIZE) + { + Tile l = bag.selectRandom(); + bag.takeTile(l); + pld.addNew(l); + } + } + + // Shuffle the new tiles, to hide the order we imposed (joker first in a + // joker game, then needed vowels, then needed consonants, and rest of the + // rack) + pld.shuffleNew(); + + // Post-condition check. This should never fail, of course :) + ASSERT(pld.checkRack(min, min), "helperSetRackRandom() is buggy!") + + // Until now we didn't modify anything except local variables. + // Let's "commit" the changes + m_players[p]->setCurrentRack(pld); + + return 0; +} + + +int Game::helperSetRackRandomOld(unsigned int p, bool iCheck, set_rack_mode mode) +{ + ASSERT(p < getNPlayers(), "Wrong player number"); + + // Make a copy of the current player's rack + PlayedRack pld = getPlayer(p).getCurrentRack(); + int nold = pld.getNbOld(); + + // Create a copy of the bag in which we can do everything we want, + // and take from it the tiles of the players rack so that "bag" + // contains the right number of tiles. + Bag bag(m_dic); + realBag(bag); + + if (mode == RACK_NEW && nold != 0) + { + // We may have removed too many letters from the bag (i.e. the 'new' + // letters of the player) + vector tiles; + pld.getNewTiles(tiles); + for (unsigned int i = 0; i < tiles.size(); i++) + { + bag.replaceTile(tiles[i]); + } + pld.resetNew(); + } + else if (mode == RACK_NEW && nold == 0 || mode == RACK_ALL) + { + // Replace all the tiles in the bag before choosing random ones + vector tiles; + pld.getAllTiles(tiles); + for (unsigned int i = 0; i < tiles.size(); i++) + { + bag.replaceTile(tiles[i]); + } + // RACK_NEW with an empty rack is equivalent to RACK_ALL + pld.reset(); + // Do not forget to update nold, for the RACK_ALL case + nold = 0; + } + else + { + debug("Game::helperSetRackRandomOld not a random mode\n"); + } + // Nothing in the rack, nothing in the bag --> end of the game - if (bag.nTiles() == 0 && pld.nTiles() == 0) + if (bag.getNbTiles() == 0 && pld.getNbTiles() == 0) { return 1; } @@ -297,17 +515,17 @@ int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) // them from the 16th turn. // So before trying to fill the rack, we'd better make sure there is a way // to complete the rack with these constraints... - min = 0; + unsigned int min = 0; if (iCheck) { - int oldc, oldv; + unsigned int oldc, oldv; - if (bag.nVowels() == 0 || bag.nConsonants() == 0) + if (bag.getNbVowels() == 0 || bag.getNbConsonants() == 0) { return 1; } // 2 vowels and 2 consonants are needed up to the 15th turn - if (bag.nVowels() > 1 && bag.nConsonants() > 1 + if (bag.getNbVowels() > 1 && bag.getNbConsonants() > 1 && m_history.getSize() < 15) min = 2; else @@ -360,7 +578,9 @@ int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) // 3) Complete the rack normally... but without any joker! Tile l; - while (bag.nTiles() != 0 && pld.nTiles() != RACK_SIZE) + // FIXME: this can be an infinite loop if the only tile left in the + // bag is a joker! + while (bag.getNbTiles() != 0 && pld.getNbTiles() != RACK_SIZE) { l = bag.selectRandom(); if (!l.isJoker()) @@ -374,7 +594,7 @@ int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) { // Get new tiles from the bag Tile l; - while (bag.nTiles() != 0 && pld.nTiles() != RACK_SIZE) + while (bag.getNbTiles() != 0 && pld.getNbTiles() != RACK_SIZE) { l = bag.selectRandom(); bag.takeTile(l); @@ -382,7 +602,7 @@ int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) } } - if (iCheck && !pld.checkRack(min,min)) + if (iCheck && !pld.checkRack(min, min)) return 2; m_players[p]->setCurrentRack(pld); @@ -391,20 +611,10 @@ int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode) } -/** - * Check if the players rack can be obtained from the bag. - * Since letters are removed from the bag only when the - * round is played we need to check that ALL the racks - * are in the bag simultaneously. - * - * FIXME: since we do not check for all racks it works - * for training and duplicate but it won't work for - * freegames. - */ bool Game::rackInBag(const Rack &iRack, const Bag &iBag) const { - const list& allTiles = Tile::getAllTiles(); - list::const_iterator it; + const vector& allTiles = m_dic.getAllTiles(); + vector::const_iterator it; for (it = allTiles.begin(); it != allTiles.end(); it++) { if (iRack.in(*it) > iBag.in(*it)) @@ -413,20 +623,16 @@ bool Game::rackInBag(const Rack &iRack, const Bag &iBag) const return true; } -/** - * Set the rack of the player p manually. - */ -int Game::helperSetRackManual(int p, bool iCheck, const wstring &iLetters) + +int Game::helperSetRackManual(unsigned int p, bool iCheck, const wstring &iLetters) { - int min, ret; + ASSERT(p < getNPlayers(), "Wrong player number"); - PlayedRack pld = getPlayer(p).getCurrentRack(); - pld.reset(); + if (!m_dic.validateLetters(iLetters, L"+")) + return 3; - if ((ret = pld.setManual(iLetters)) > 0) - { - return 1; /* add new tests */ - } + PlayedRack pld; + pld.setManual(iLetters); Rack rack; pld.getRack(rack); @@ -438,12 +644,13 @@ int Game::helperSetRackManual(int p, bool iCheck, const wstring &iLetters) if (iCheck) { - if (m_bag.nVowels() > 1 && m_bag.nConsonants() > 1 + int min; + if (m_bag.getNbVowels() > 1 && m_bag.getNbConsonants() > 1 && m_history.getSize() < 15) min = 2; else min = 1; - if (!pld.checkRack(min,min)) + if (!pld.checkRack(min, min)) return 2; } @@ -456,10 +663,10 @@ int Game::helperSetRackManual(int p, bool iCheck, const wstring &iLetters) *********************************************************/ -int Game::getNHumanPlayers() const +unsigned int Game::getNHumanPlayers() const { - int count = 0; - for (int i = 0; i < getNPlayers(); i++) + unsigned int count = 0; + for (unsigned int i = 0; i < getNPlayers(); i++) count += (getPlayer(i).isHuman() ? 1 : 0); return count; } @@ -474,7 +681,8 @@ void Game::addHumanPlayer() void Game::addAIPlayer() { - m_players.push_back(new AIPercent(getNPlayers(), 0)); + // TODO: allow other percentages, and even other types of AI + m_players.push_back(new AIPercent(getNPlayers(), 1)); } @@ -500,34 +708,15 @@ void Game::nextPlayer() } -/* - * This function checks whether it is legal to play the given word at the - * given coordinates. If so, the function fills a Round object, also given as - * a parameter. - * Possible return values: - * 0: correct word, the Round can be used by the caller - * 1: no dictionary set - * 2: invalid coordinates (unreadable or out of the board) - * 3: word not present in the dictionary - * 4: not enough letters in the rack to play the word - * 5: word is part of a longer one - * 6: word overwriting an existing letter - * 7: invalid crosscheck, or word going out of the board - * 8: word already present on the board (no new letter from the rack) - * 9: isolated word (not connected to the rest) - * 10: first word not horizontal - * 11: first word not covering the H8 square - */ int Game::checkPlayedWord(const wstring &iCoord, const wstring &iWord, Round &oRound) { ASSERT(getNPlayers() != 0, "Expected at least one player"); - int res; - vector tiles; - Tile t; + if (!m_dic.validateLetters(iWord)) + return 1; - /* Init the round with the given coordinates */ + // Init the round with the given coordinates oRound.init(); oRound.accessCoord().setFromString(iCoord); if (!oRound.getCoord().isValid()) @@ -535,17 +724,18 @@ int Game::checkPlayedWord(const wstring &iCoord, debug("game: incorrect coordinates\n"); return 2; } - - /* Check the existence of the word */ - if (Dic_search_word(*m_dic, iWord.c_str()) == 0) + + // Check the existence of the word + if (!m_dic.searchWord(iWord)) { return 3; } - /* Set the word */ + // Set the word // TODO: make this a Round_ function (Round_setwordfromchar for example) // or a Tiles_ function (to transform a char* into a vector) // Adding a getter on the word could help too... + vector tiles; for (unsigned int i = 0; i < iWord.size(); i++) { tiles.push_back(Tile(iWord[i])); @@ -557,20 +747,21 @@ int Game::checkPlayedWord(const wstring &iCoord, oRound.setJoker(i); } - /* Check the word position, compute its points, - * and specify the origin of each letter (board or rack) */ - res = m_board.checkRound(oRound, m_history.getSize() == 0); + // Check the word position, compute its points, + // and specify the origin of each letter (board or rack) + int res = m_board.checkRound(oRound, m_history.getSize() == 0); if (res != 0) return res + 4; - /* Check that the word can be formed with the tiles in the rack: - * we first create a copy of the rack, then we remove the tiles - * one by one */ + // Check that the word can be formed with the tiles in the rack: + // we first create a copy of the rack, then we remove the tiles + // one by one Rack rack; Player *player = m_players[m_currPlayer]; player->getCurrentRack().getRack(rack); - for (int i = 0; i < oRound.getWordLen(); i++) + Tile t; + for (unsigned int i = 0; i < oRound.getWordLen(); i++) { if (oRound.isPlayedFromRack(i)) { diff --git a/game/game.h b/game/game.h index 5b01963..8273bcb 100644 --- a/game/game.h +++ b/game/game.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,12 +29,12 @@ #include "board.h" #include "history.h" +class Dictionary; class Player; class PlayedRack; class Round; class Rack; class Turn; -typedef struct _Dictionary * Dictionary; using namespace std; @@ -47,13 +48,17 @@ class Game { public: /// Game specs. - static const int RACK_SIZE; + static const unsigned int RACK_SIZE; static const int BONUS_POINTS; Game(const Dictionary &iDic); virtual ~Game(); + /*************** + * Game type + ***************/ + /// Game mode: each one of these modes is implemented in an inherited class enum GameMode { @@ -79,100 +84,118 @@ public: void setVariant(GameVariant iVariant) { m_variant = iVariant; } GameVariant getVariant() const { return m_variant; } - /** - * Dictionary associated with the game. - * The dictionary can be changed during a game without problem - */ - const Dictionary & getDic() const { return *m_dic; } - void setDic(const Dictionary &iDic) { m_dic = &iDic; } + /*************** + * Various getters + ***************/ - const Board& getBoard() const { return m_board; } - const Bag& getBag() const { return m_bag; } - const Player& getPlayer(int iNum) const; - const Turn& getTurn(int iNum) const; + /** + * Get the dictionary associated with the game. + * You should never create a new dictionary object while a Game + * object still exists + */ + const Dictionary & getDic() const { return m_dic; } + + /// Get the board + const Board& getBoard() const { return m_board; } + /// Get the bag + const Bag& getBag() const { return m_bag; } + /// Get the history of the game */ + const History& getHistory() const { return m_history; } + + /*************** + * Methods to access players. + ***************/ + + const Player& getPlayer(unsigned int iNum) const; const Player& getCurrentPlayer() const { return getPlayer(currPlayer()); }; + unsigned int getNPlayers() const { return m_players.size(); } + unsigned int getNHumanPlayers() const; + virtual void addHumanPlayer(); + // TODO: Ability to specify which kind of AI player is wanted + virtual void addAIPlayer(); + unsigned int currPlayer() const { return m_currPlayer; } + + /*************** + * Game handling + ***************/ /** - * Eliot file formats + * Start the game. + * AI players are handled automatically, so if the game only has AI + * players, it will play until the end. */ - typedef enum { - FILE_FORMAT_STANDARD, - FILE_FORMAT_ADVANCED - } game_file_format; + virtual int start() = 0; + + /** + * Method used by human players to play the word iWord at coordinates + * iCoord, and end the turn (if possible) + * Possible return values: + * 0: correct word, the Round can be used by the caller + * 1: one letter of the word is invalid in the current dictionary + * 2: invalid coordinates (unreadable or out of the board) + * 3: word not present in the dictionary + * 4: not enough letters in the rack to play the word + * 5: word is part of a longer one + * 6: word overwriting an existing letter + * 7: invalid crosscheck, or word going out of the board + * 8: word already present on the board (no new letter from the rack) + * 9: isolated word (not connected to the rest) + * 10: first word not horizontal + * 11: first word not covering the H8 square + */ + virtual int play(const wstring &iCoord, const wstring &iWord) = 0; + + /** + * Go back to turn iTurn. + * We must have: iTurn < getHistory().getSize() + * Possible return values: + * 0: everything went fine + * 1: iTurn is invalid + */ + int back(unsigned int iTurn); + + /*************** + * Saved games handling + ***************/ + + /** + * Possible formats for the saved games + */ + enum game_file_format + { + FILE_FORMAT_STANDARD, + FILE_FORMAT_ADVANCED + }; /** - * Saved games handling. - * * load() returns the loaded game, or NULL if there was a problem - * load() might need some more work to be robust enough to + * load() does need some more work to be robust enough to * handle "hand written" files */ static Game * load(FILE *fin, const Dictionary &iDic); /** - * Save a game to a File + * Save a game to a file * Standard format is used for training games so that it is compatible * with previous versions of Eliot. * - * Saving can be forced to advanced format for training games by + * Saving can be forced to advanced format for training games by * setting the last parameter to FILE_FORMAT_ADVANCED */ - void save(ostream &out, game_file_format format=FILE_FORMAT_STANDARD) const; + void save(ostream &out, game_file_format format = FILE_FORMAT_STANDARD) const; - /************************* - * Playing the game - * the int parameter should be 0 <= int < getNTurns - *************************/ - int back(int); + /*************** + * Setting the rack + ***************/ - /************************* - * Set the rack for searching - * - * The int parameter is a boolean, if this parameter - * set the rack will check that there are at least - * 2 vowels and 2 consonants before the round 15. - * - * The setrackmanual parameter string has to contain - * 'a' <= char <= 'z' or 'A' <= char <= 'Z' or '?' - * - * return value - * 0 : the rack has been set - * 1 : the bag does not contain enough tiles - * 2 : the rack check was set on and failed - * 3 : the rack cannot be completed (Game_*_setrackrandom only) - *************************/ enum set_rack_mode {RACK_ALL, RACK_NEW, RACK_MANUAL}; - int setRack(int player, set_rack_mode mode, bool check, const wstring& str); - /** - * Methods to access already played words. - * The int parameter should be 0 <= int < getNTurns() - */ - const History& getHistory() const { return m_history; } - - /** - * Methods to access players. - * The int parameter should be 0 <= int < getNPlayers() - */ - int getNPlayers() const { return m_players.size(); } - int getNHumanPlayers() const; - virtual void addHumanPlayer(); - // TODO: Ability to specify which kind of AI player is wanted - virtual void addAIPlayer(); - int currPlayer() const { return m_currPlayer; } - - /** - * Game handling - */ - virtual int start() = 0; - virtual int play(const wstring &iCoord, const wstring &iWord) = 0; - virtual int endTurn() = 0; protected: /// All the players, indexed by their ID vector m_players; /// ID of the "current" player - int m_currPlayer; + unsigned int m_currPlayer; // TODO: check what should be private and what should be protected // private: @@ -181,7 +204,7 @@ protected: GameVariant m_variant; /// Dictionary currently associated to the game - const Dictionary * m_dic; + const Dictionary & m_dic; /// Bag Bag m_bag; @@ -203,14 +226,91 @@ protected: * Helper functions *********************************************************/ - int helperPlayRound(const Round &iRound); - int helperSetRackRandom(int p, bool iCheck, set_rack_mode mode); - int helperSetRackManual(int p, bool iCheck, const wstring &iLetters); + /** Play a Move for the given player, updating game history */ + void helperPlayMove(unsigned int iPlayerId, const Move &iMove); + + /** + * Set the rack randomly for the player p + * Possible return values: + * 0: everything went fine + * 1: the game is over + * 3: there is no chance to set the rack with the vowels/consonants + * constraints + * + * Completing a rack randomly is more complex than it seems, because we + * must take into account several constraints: + * - if iCheck is true, we must ensure that the rack contains a minimum + * number of vowels and consonants (2 of each in the 15 first moves of + * the game, 1 of each after) + * - the game is over if the (real) bag contains only vowels or only + * consonants, and in particular if it contains only one letter + * - some letters (in particular the joker) can count both as a vowel and + * as a consonant (but not at the same time) + * - in a joker game, the joker must be present in the rack unless there + * is no joker left in the bag. In addition, we must prevent that both + * jokers are present in the rack at the same time + * - if completing a rack doesn't meet the requirements on the vowels and + * consonants, we must reject the rack completely (but only once, + * otherwise we have no guarantee that the rejects will stop eventually). + * This also means we have to check whether completing the rack with the + * requirements is possible... + */ + int helperSetRackRandom(unsigned int p, bool iCheck, set_rack_mode mode); + + /** + * Set the rack randomly for the player p + * Possible return values: + * 0: everything went fine + * 1: the game is over + * 2: the rack was checked and was not correct (try calling the + * function again) + * 3: there is no chance to set the rack with the vowels/consonants + * constraints + * + * @deprecated: use helperSetRackRandom instead + */ + int helperSetRackRandomOld(unsigned int p, bool iCheck, set_rack_mode mode); + + /** + * Set the rack for the player p with the given letters + * Possible return values: + * 0: everything went fine + * 1: the bag doesn't have the wanted letters + * 2: the rack was checked for vowels/consonants and was not correct + */ + int helperSetRackManual(unsigned int p, bool iCheck, const wstring &iLetters); void prevPlayer(); void nextPlayer(); + + /** + * Check if the players rack can be obtained from the bag. + * Since letters are removed from the bag only when the + * round is played we need to check that ALL the racks + * are in the bag simultaneously. + * + * FIXME: since we do not check for all racks it works + * for training and duplicate but it won't work for + * freegames. + */ bool rackInBag(const Rack &iRack, const Bag &iBag) const; + + /** + * The realBag is the current bag minus all the racks + * present in the game. It represents the actual + * letters that are left in the bag. + * FIXME: in Duplicate mode, this method uses m_currPlayer to find the + * rack of the player. Since not all the players played the same word, + * it is important to set m_currPlayer accurately before! + */ void realBag(Bag &iBag) const; + + /** + * This function checks whether it is legal to play the given word at the + * given coordinates. If so, the function fills a Round object, also given + * as a parameter. + * Possible return values: same as the play() method + */ int checkPlayedWord(const wstring &iCoord, const wstring &iWord, Round &oRound); @@ -236,6 +336,14 @@ protected: */ void gameSaveFormat_15(ostream &out) const; +private: + + /** + * Play a round on the board. + * This should only be called by helperPlayMove(). + */ + void helperPlayRound(unsigned int iPlayerId, const Round &iRound); + }; #endif /* _GAME_H_ */ diff --git a/game/game_factory.cpp b/game/game_factory.cpp index 7a490a0..c2395e7 100644 --- a/game/game_factory.cpp +++ b/game/game_factory.cpp @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +19,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +#include "config.h" + #include #include +#include +#include -#include "config.h" -#include "dic.h" #include "game_factory.h" +#include "game.h" +#include "training.h" +#include "freegame.h" +#include "duplicate.h" +#include "dic.h" GameFactory *GameFactory::m_factory = NULL; @@ -35,8 +44,7 @@ GameFactory::GameFactory(): m_dic(NULL), m_human(0), m_ai(0), m_joker(false) GameFactory::~GameFactory() { - if (m_dic) - Dic_destroy(m_dic); + delete m_dic; } @@ -50,8 +58,7 @@ GameFactory *GameFactory::Instance() void GameFactory::Destroy() { - if (m_factory) - delete m_factory; + delete m_factory; m_factory = NULL; } @@ -139,16 +146,20 @@ Game *GameFactory::createFromCmdLine(int argc, char **argv) cerr << "dict"; else if (!found_m) cerr << "mode"; - cerr << "\n"; + cerr << endl; printUsage(argv[0]); return NULL; } // 3) Try to load the dictionary - if (Dic_load(&m_dic, m_dicStr.c_str())) + try { - cerr << "Could not load dictionary '" << m_dicStr << "'\n"; + m_dic = new Dictionary(m_dicStr); + } + catch (std::exception &e) + { + cerr << e.what() << endl; return NULL; } @@ -156,19 +167,19 @@ Game *GameFactory::createFromCmdLine(int argc, char **argv) Game *game = NULL; if (m_modeStr == "training" || m_modeStr == "t") { - game = createTraining(m_dic); + game = createTraining(*m_dic); } else if (m_modeStr == "freegame" || m_modeStr == "f") { - game = createFreeGame(m_dic); + game = createFreeGame(*m_dic); } else if (m_modeStr == "duplicate" || m_modeStr == "d") { - game = createDuplicate(m_dic); + game = createDuplicate(*m_dic); } else { - cerr << "Invalid game mode '" << m_modeStr << "'\n"; + cerr << "Invalid game mode '" << m_modeStr << "'" << endl; return NULL; } @@ -185,21 +196,21 @@ Game *GameFactory::createFromCmdLine(int argc, char **argv) return game; } -Game* GameFactory::load(string filename, const Dictionary &iDic) + +Game* GameFactory::load(const string &iFileName, const Dictionary &iDic) { - Game* game; - FILE* fin; - if ((fin = fopen(filename.c_str(), "r")) == NULL) - { - printf("impossible d'ouvrir %s\n", - filename.c_str()); - return NULL; - } - game = Game::load(fin,iDic); + FILE* fin = fopen(iFileName.c_str(), "r"); + if (fin == NULL) + { + printf("Cannot open %s\n", iFileName.c_str()); + return NULL; + } + Game *game = Game::load(fin, iDic); fclose(fin); return game; } + void GameFactory::releaseGame(Game &iGame) { delete &iGame; @@ -208,25 +219,26 @@ void GameFactory::releaseGame(Game &iGame) void GameFactory::printUsage(const string &iBinaryName) const { - cout << "Usage: " << iBinaryName << " [options]\n" - << "Options:\n" - << " -h, --help Print this help and exit\n" - << " -v, --version Print version information and exit\n" - << " -m, --mode {duplicate,d,freegame,f,training,t}\n" - << " Choose game mode (mandatory)\n" - << " -d, --dict Choose a dictionary (mandatory)\n" - << " --human Add a human player\n" - << " --ai Add a AI (Artificial Intelligence) player\n" - << " --joker Play with the \"Joker game\" variant\n"; + cout << "Usage: " << iBinaryName << " [options]" << endl + << "Options:" << endl + << " -h, --help Print this help and exit" << endl + << " -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 + << " --joker Play with the \"Joker game\" variant" << endl; } void GameFactory::printVersion() const { - cout << PACKAGE_STRING << "\n" + cout << PACKAGE_STRING << endl << "This program comes with NO WARRANTY, to the extent permitted by " - << "law.\nYou may redistribute it under the terms of the GNU General " - << "Public License;\nsee the file named COPYING for details.\n"; + << "law." << endl << "You may redistribute it under the terms of the " + << "GNU General Public License;" << endl + << "see the file named COPYING for details." << endl; } diff --git a/game/game_factory.h b/game/game_factory.h index 72d38ab..07faf92 100644 --- a/game/game_factory.h +++ b/game/game_factory.h @@ -1,6 +1,7 @@ /***************************************************************************** - * Copyright (C) 2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Olivier Teulière + * Authors: Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,10 +21,15 @@ #ifndef _GAME_FACTORY_H_ #define _GAME_FACTORY_H_ -#include "game.h" -#include "training.h" -#include "freegame.h" -#include "duplicate.h" +#include + +using std::string; + +class Dictionary; +class Game; +class Training; +class FreeGame; +class Duplicate; /** @@ -52,7 +58,7 @@ public: * load() might need some more work to be robust enough to * handle "hand written" files */ - Game *load(string filename, const Dictionary &iDic); + Game *load(const string &iFileName, const Dictionary &iDic); Game *createFromCmdLine(int argc, char **argv); @@ -62,13 +68,13 @@ public: private: GameFactory(); - virtual ~GameFactory(); + ~GameFactory(); /// The unique instance of the class static GameFactory *m_factory; /// Initial dictionary (it could be changed later) - Dictionary m_dic; + Dictionary *m_dic; /** Parameters specified on the command-line */ //@{ diff --git a/game/game_io.cpp b/game/game_io.cpp index deb696f..de233c9 100644 --- a/game/game_io.cpp +++ b/game/game_io.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,12 +26,16 @@ * \date 2002 - 2005 */ +#include "dic.h" #include "pldrack.h" #include "round.h" #include "turn.h" #include "player.h" #include "game.h" #include "game_factory.h" +#include "training.h" +#include "freegame.h" +#include "duplicate.h" #include "encoding.h" #include "debug.h" @@ -107,14 +112,14 @@ Game* Game::gameLoadFormat_14(FILE *fin, const Dictionary& iDic) char delim[]=" \t\n\012\015"; char *token; Game *pGame = NULL; - + debug("Game::gameLoadFormat_14\n"); pGame = GameFactory::Instance()->createTraining(iDic); pGame->start(); - + /* rack word ?bonus pts coord */ /* EUOFMIE FUMEE * 26 H 4 */ - + /* read all turns until total */ while (fgets(buff, sizeof(buff), fin)) { @@ -192,14 +197,7 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) Game *pGame = NULL; char buff[4096]; - int num; - char rack[20]; - char word[20]; - char ref[4]; - int pts; - int player; char *pos; - Tile tile; /*************/ /* Game type */ @@ -322,9 +320,15 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) } + int num; + char rack[20]; + char tmpWord[20]; + char ref[4]; + int pts; + unsigned int player; char bonus = 0; int res = sscanf(buff, " %2d | %8s | %s | %3s | %3d | %1d | %c", - &num, rack, word, ref, &pts, &player, &bonus); + &num, rack, tmpWord, ref, &pts, &player, &bonus); debug(" -- line %s",buff); @@ -335,7 +339,7 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) } debug(" %2d | %8s | %s | %3s | %3d | %1d | %c \n", - num, rack, word, ref, pts, player, bonus); + num, rack, tmpWord, ref, pts, player, bonus); // Integrity checks // TODO: add more checks @@ -344,7 +348,7 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) debug(" Game::load15 line -%s- points < 0 ?\n",buff); continue; } - if (player < 0 || player > pGame->getNPlayers()) + if (player > pGame->getNPlayers()) { debug(" Game::load15 line -%s- too much player (%d>%d)",buff,player,pGame->getNPlayers()); continue; @@ -357,10 +361,11 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) // Build a rack for the correct player PlayedRack pldrack; - if ((res = pldrack.setManual(convertToWc(rack))) > 0) + if (!iDic.validateLetters(convertToWc(rack))) { - debug(" Game::load15 set rack manual returned with error %d\n",res); + debug(" Game::load15 rack invalid for the current dictionary\n"); } + pldrack.setManual(convertToWc(rack)); debug(" history rack %s\n", convertToMb(pldrack.toString()).c_str()); // Build a round @@ -369,6 +374,8 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) if (bonus == '*') round.setBonus(1); + wstring word = convertToWc(tmpWord); + Tile tile; if (isalpha(ref[0])) { // Horizontal word @@ -376,7 +383,7 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) round.accessCoord().setRow(ref[0] - 'A' + 1); round.accessCoord().setCol(atoi(ref + 1)); - for (unsigned int i = 0; i < strlen(word); i++) + for (unsigned int i = 0; i < word.size(); i++) { tile = Tile(word[i]); @@ -386,8 +393,8 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) } else { - round.addRightFromRack(tile, islower(word[i])); - pGame->m_bag.takeTile((islower(word[i])) ? Tile::Joker() : tile); + round.addRightFromRack(tile, iswlower(word[i])); + pGame->m_bag.takeTile((iswlower(word[i])) ? Tile::Joker() : tile); } } } @@ -398,7 +405,7 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) round.accessCoord().setRow(ref[strlen(ref) - 1] - 'A' + 1); round.accessCoord().setCol(atoi(ref)); - for (unsigned int i = 0; i < strlen(word); i++) + for (unsigned int i = 0; i < word.size(); i++) { tile = Tile(word[i]); @@ -408,20 +415,20 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) } else { - round.addRightFromRack(tile, islower(word[i])); - pGame->m_bag.takeTile((islower(word[i])) ? Tile::Joker() : tile); + round.addRightFromRack(tile, iswlower(word[i])); + pGame->m_bag.takeTile((iswlower(word[i])) ? Tile::Joker() : tile); } } } - // pGame->m_currPlayer = player; - // // Update the rack for the player - // pGame->m_players[player]->setCurrentRack(pldrack); - // // End the turn for the current player (this creates a new rack) - // pGame->m_players[player]->endTurn(round,num - 1); +// pGame->m_currPlayer = player; +// // Update the rack for the player +// pGame->m_players[player]->setCurrentRack(pldrack); +// // End the turn for the current player (this creates a new rack) +// pGame->m_players[player]->endTurn(round,num - 1); // Play the round - pGame->helperPlayRound(round); + pGame->helperPlayRound(pGame->m_currPlayer, round); } /**************************************/ @@ -452,7 +459,7 @@ Game* Game::gameLoadFormat_15(FILE *fin, const Dictionary& iDic) { // We don't really know whose turn it is, but at least we know that // the game was saved while a human was to play. - for (int i = 0; i < pGame->getNPlayers(); i++) + for (unsigned int i = 0; i < pGame->getNPlayers(); i++) { if (pGame->m_players[i]->isHuman()) { @@ -490,21 +497,21 @@ void Game::gameSaveFormat_14(ostream &out) const const string decal = " "; out << IDENT_STRING << endl << endl; - for (int i = 0; i < m_history.getSize(); i++) + for (unsigned int i = 0; i < m_history.getSize(); i++) { const Turn& turn = m_history.getTurn(i); - string rack = convertToMb(turn.getPlayedRack().toString(PlayedRack::RACK_EXTRA)); - string word = convertToMb(turn.getRound().getWord()); - string coord = convertToMb(turn.getRound().getCoord().toString(Coord::COORD_MODE_LONG)); + wstring rack = turn.getPlayedRack().toString(PlayedRack::RACK_EXTRA); + // FIXME: this will not work if the move does not correspond to a played round! + const Round &round = turn.getMove().getRound(); + wstring word = round.getWord(); + string coord = convertToMb(round.getCoord().toString(Coord::COORD_MODE_LONG)); // rack [space] word [space] bonus points coord - sprintf(line,"%s%s%s%s%c%4d %s", - rack.c_str(), - string(12 - rack.size(), ' ').c_str(), - word.c_str(), - string(16 - word.size(), ' ').c_str(), - turn.getRound().getBonus() ? '*' : ' ', - turn.getRound().getPoints(), + sprintf(line,"%s%s%c%4d %s", + padAndConvert(rack, 12, false).c_str(), + padAndConvert(word, 16, false).c_str(), + round.getBonus() ? '*' : ' ', + round.getPoints(), coord.c_str() ); @@ -513,7 +520,7 @@ void Game::gameSaveFormat_14(ostream &out) const out << endl; out << decal << "total" << string(24,' '); - sprintf(line,"%4d", getCurrentPlayer().getPoints()); + sprintf(line, "%4d", getCurrentPlayer().getPoints()); out << line << endl; } @@ -526,7 +533,7 @@ void Game::gameSaveFormat_15(ostream &out) const // Game type out << "Game type: " << getModeAsString() << endl; // Player list - for (int i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { out << "Player " << i << ": "; if (m_players[i]->isHuman()) @@ -542,21 +549,69 @@ void Game::gameSaveFormat_15(ostream &out) const out << decal << "===|==========|=================|=====|=====|===|======" << endl; // Print the game itself - for (int i = 0; i < m_history.getSize(); i++) + for (unsigned int i = 0; i < m_history.getSize(); i++) { const Turn& turn = m_history.getTurn(i); - string rack = convertToMb(turn.getPlayedRack().toString(PlayedRack::RACK_EXTRA)); - string word = convertToMb(turn.getRound().getWord()); - string coord = convertToMb(turn.getRound().getCoord().toString()); - sprintf(line, "%2d | %8s | %s%s | %3s | %3d | %1d | %c", - i + 1, - rack.c_str(), /* pldrack */ - word.c_str(), /* word */ - string(15 - word.size(), ' ').c_str(), /* fill spaces */ - coord.c_str(), /* coord */ - turn.getRound().getPoints(), - turn.getPlayer(), - turn.getRound().getBonus() ? '*' : ' '); + wstring rack = turn.getPlayedRack().toString(PlayedRack::RACK_EXTRA); + const Move &move = turn.getMove(); + switch (move.getType()) + { + case Move::VALID_ROUND: + { + const Round &round = move.getRound(); + wstring word = round.getWord(); + string coord = convertToMb(round.getCoord().toString()); + sprintf(line, "%2d | %s | %s | %3s | %3d | %1d | %c", + i + 1, + padAndConvert(rack, 8).c_str(), /* pldrack */ + padAndConvert(word, 15, false).c_str(), /* word */ + coord.c_str(), /* coord */ + move.getScore(), + turn.getPlayer(), + round.getBonus() ? '*' : ' '); + break; + } + case Move::INVALID_WORD: + { + wstring word = move.getBadWord(); + string coord = convertToMb(move.getBadCoord()); + sprintf(line, "%2d | %s | %s | %3s | %3d | %1d |", + i + 1, + padAndConvert(rack, 8).c_str(), /* pldrack */ + padAndConvert(word, 15, false).c_str(), /* word */ + coord.c_str(), /* coord */ + move.getScore(), + turn.getPlayer()); + break; + } + case Move::PASS: + { + string action = "(PASS)"; + string coord = " - "; + sprintf(line, "%2d | %s | %s | %3s | %3d | %1d |", + i + 1, + padAndConvert(rack, 8).c_str(), /* pldrack */ + truncOrPad(action, 15, ' ').c_str(), /* word */ + coord.c_str(), /* coord */ + move.getScore(), + turn.getPlayer()); + break; + } + case Move::CHANGE_LETTERS: + { + wstring action = L"(-" + move.getChangedLetters() + L")"; + string coord = " - "; + sprintf(line, "%2d | %s | %s | %3s | %3d | %1d |", + i + 1, + padAndConvert(rack, 8).c_str(), /* pldrack */ + padAndConvert(action, 15, false).c_str(), /* word */ + coord.c_str(), /* coord */ + move.getScore(), + turn.getPlayer()); + break; + } + + } out << decal << line << endl; } @@ -577,7 +632,7 @@ void Game::gameSaveFormat_15(ostream &out) const // Print current rack for all the players out << endl; - for (int i = 0; i < getNPlayers(); i++) + for (unsigned int i = 0; i < getNPlayers(); i++) { wstring rack = m_players[i]->getCurrentRack().toString(); out << "Rack " << i << ": " << convertToMb(rack) << endl; diff --git a/game/history.cpp b/game/history.cpp index 91e3b9f..96f0bba 100644 --- a/game/history.cpp +++ b/game/history.cpp @@ -1,21 +1,23 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file history.cpp @@ -27,7 +29,7 @@ #include #include "rack.h" #include "pldrack.h" -#include "round.h" +#include "move.h" #include "turn.h" #include "history.h" #include "encoding.h" @@ -41,7 +43,7 @@ History::History() { - Turn* t = new Turn (); + Turn* t = new Turn(); m_history.clear(); m_history.push_back(t); } @@ -51,17 +53,14 @@ History::~History() { for (unsigned int i = 0; i < m_history.size(); i++) { - if (m_history[i] != NULL) - { - delete m_history[i]; - m_history[i] = NULL; - } + delete m_history[i]; } } -int History::getSize() const +unsigned int History::getSize() const { + ASSERT(!m_history.empty(), "Invalid history size"); return m_history.size() - 1; } @@ -88,44 +87,61 @@ const Turn& History::getPreviousTurn() const const Turn& History::getTurn(unsigned int n) const { - // ASSERT(0 <= n && n < m_history.size(), "Wrong turn number"); ASSERT(n < m_history.size(), "Wrong turn number"); return *(m_history[n]); } -/* - * This function increments the number of racks, and fills the new rack - * with the unplayed tiles from the previous one. - * 03 sept 2000 : We have to sort the tiles according to the new rules - */ -void History::playRound(int player, int turn, const Round& round) + +bool History::beforeFirstRound() const { + for (unsigned int i = 0; i < m_history.size() - 1; i++) + { + if (m_history[i]->getMove().getType() == Move::VALID_ROUND) + return false; + } + return true; +} + + +void History::playMove(unsigned int iPlayer, unsigned int iTurn, const Move &iMove) +{ + Turn * current_turn = m_history.back(); + + // Set the number and the round + current_turn->setNum(iTurn); + current_turn->setPlayer(iPlayer); + current_turn->setMove(iMove); + + // Get what was the rack for the current turn Rack rack; - Turn * current_turn; - - current_turn = m_history.back(); - - /* set the number and the round */ - current_turn->setNum(turn); - current_turn->setPlayer(player); - current_turn->setRound(round); - - /* get what was the rack for the current turn */ current_turn->getPlayedRack().getRack(rack); - /* remove the played tiles from the rack */ - for (int i = 0; i < round.getWordLen(); i++) + if (iMove.getType() == Move::VALID_ROUND) { - if (round.isPlayedFromRack(i)) + // Remove the played tiles from the rack + const Round &round = iMove.getRound(); + for (unsigned int i = 0; i < round.getWordLen(); i++) { - if (round.isJoker(i)) - rack.remove(Tile::Joker()); - else - rack.remove(round.getTile(i)); + if (round.isPlayedFromRack(i)) + { + if (round.isJoker(i)) + rack.remove(Tile::Joker()); + else + rack.remove(round.getTile(i)); + } + } + } + else if (iMove.getType() == Move::CHANGE_LETTERS) + { + // Remove the changed tiles from the rack + const wstring & changed = iMove.getChangedLetters(); + for (unsigned int i = 0; i < changed.size(); ++i) + { + rack.remove(Tile(changed[i])); } } - /* create a new turn */ + // Create a new turn Turn * next_turn = new Turn(); PlayedRack pldrack; pldrack.setOld(rack); @@ -146,11 +162,11 @@ void History::removeLastTurn() delete t; } - // now we have the previous played round in back() - Turn* t = m_history.back(); + // Now we have the previous played round in back() + Turn *t = m_history.back(); t->setNum(0); t->setPlayer(0); - t->setRound(Round()); + //t->setRound(Round()); #ifdef BACK_REMOVE_RACK_NEW_PART t->getPlayedRound().setNew(Rack()); #endif diff --git a/game/history.h b/game/history.h index 9ccc70b..0b2920b 100644 --- a/game/history.h +++ b/game/history.h @@ -1,21 +1,23 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file history.h @@ -33,7 +35,7 @@ using std::wstring; using std::vector; -class Round; +class Move; class Turn; class PlayedRack; @@ -41,15 +43,12 @@ class PlayedRack; * History stores all the turns that have been played * This class is used many times in the game * - one for the complete game - * - one for each of the players + * - one for each player * - * A History is never void (getSize() can be used as the is the current turn - * number for the complete game history). - * - * History starts at zero. - * - * The top of the history is an empty - * Turn until it has been filled and game is up to a new round. + * The top of the history is an empty Turn until it has been filled + * and the game is up to a new turn. So a History object is never empty. + * However, the getSize() method only returns the number of complete + * turns, and can therefore return 0. * * getCurrentRack() can/should be used to store the current played rack. * setCurrentRack must be called whenever the current played rack is @@ -58,31 +57,41 @@ class PlayedRack; * History owns the turns that it stores. Do not delete a turn referenced * by History */ - class History { public: History(); - virtual ~History(); + ~History(); - /// get the size of the history - int getSize() const; + /// Get the size of the history (without the current incomplete turn) + unsigned int getSize() const; /// Get the (possibly incomplete) rack const PlayedRack& getCurrentRack() const; /// Set the current rack - void setCurrentRack(const PlayedRack &iPld); + void setCurrentRack(const PlayedRack &iPld); - /// Get the previous turn - const Turn& getPreviousTurn() const; + /// Get the previous (complete) turn + const Turn& getPreviousTurn() const; - /// Get turn 'n' - const Turn& getTurn(unsigned int) const; + /// Get turn 'n' (starting at 0) + const Turn& getTurn(unsigned int) const; - /// Update the "history" with the given round and complete the turn. - /// A new turn is created with the remaining letters in the rack - void playRound(int player, int turn, const Round& round); + /** + * Return true if the history doesn't contain at least one move + * corresponding to a valid round, false otherwise. + * Said differently, this method checks whether a word was already played + * on the board. + */ + bool beforeFirstRound() const; + + /** + * Update the history with the given move and complete the turn. + * A new turn is created with the unplayed letters in the rack + * 03 sept 2000: We have to sort the tiles according to the new rules + */ + void playMove(unsigned int player, unsigned int turn, const Move &iMove); /// Remove last turn void removeLastTurn(); diff --git a/game/move.cpp b/game/move.cpp new file mode 100644 index 0000000..8c99a8b --- /dev/null +++ b/game/move.cpp @@ -0,0 +1,107 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include +#include + +#include "move.h" + + +Move::Move(const Round &iRound) + : m_score(0), m_round(iRound) +{ + m_type = VALID_ROUND; + m_score = m_round.getPoints(); +} + + +Move::Move(const wstring &iWord, const wstring &iCoord) + : m_word(iWord), m_coord(iCoord) +{ + m_type = INVALID_WORD; + m_score = 0; +} + + +Move::Move(const wstring &iLetters) + : m_score(0), m_letters(iLetters) +{ + // Make the letters uppercase + std::transform(m_letters.begin(), m_letters.end(), + m_letters.begin(), towupper); + + if (m_letters.empty()) + m_type = PASS; + else + m_type = CHANGE_LETTERS; +} + + +const Round & Move::getRound() const +{ + if (m_type != VALID_ROUND) + { + // FIXME: throw an exception object instead + throw 1; + } + return m_round; +} + + +const wstring & Move::getBadWord() const +{ + if (m_type != INVALID_WORD) + { + // FIXME: throw an exception object instead + throw 1; + } + return m_word; +} + + +const wstring & Move::getBadCoord() const +{ + if (m_type != INVALID_WORD) + { + // FIXME: throw an exception object instead + throw 1; + } + return m_coord; +} + + +const wstring & Move::getChangedLetters() const +{ + if (m_type != CHANGE_LETTERS && + m_type != PASS) + { + // FIXME: throw an exception object instead + throw 1; + } + return m_letters; +} + + +wstring Move::toString() const +{ + // TODO + return L""; +} + diff --git a/game/move.h b/game/move.h new file mode 100644 index 0000000..ed511e4 --- /dev/null +++ b/game/move.h @@ -0,0 +1,134 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#ifndef _MOVE_H +#define _MOVE_H + +#include + +#include "round.h" + +using std::wstring; + + +/** + * A Move is what a player can do during the game: + * - play a valid word + * - play an invalid or misplaced word + * - pass the turn (freegame only) + * - change letters (freegame only) + * - play nothing (timeout) (not supported yet) + * + * Moves are useful to record what happened, even if the board doesn't keep + * a trace of the move (e.g.: an invalid move which was rejected will still + * be remembered in the player history). + * + * Currently, moves are not used by the interfaces (they are only used + * internally), but this could change in the future. + */ +class Move +{ + public: + /** + * Constructor taking a (valid) round + */ + explicit Move(const Round &iRound); + + /** + * Constructor taking a word and its coordinates, corresponding + * to an invalid move by the player (invalid word, invalid coordinates, + * letters not corresponding to the rack, ...) + */ + explicit Move(const wstring &iWord, const wstring &iCoord); + + /** + * Constructor taking letters to change. + * An empty string means that the player simply passes without + * changing any letter. + * The given letters must have been already validated for correctness. + */ + explicit Move(const wstring &iLetters); + + enum Type + { + VALID_ROUND, + INVALID_WORD, + PASS, + CHANGE_LETTERS + }; + + /// Return the type of move + Type getType() const { return m_type; } + + /// Get the score of this move (0 unless the round is valid) + int getScore() const { return m_score; }; + + /** + * Return the round associated with the move, or throw an exception + * if this move was not constructed from a valid round + */ + const Round & getRound() const; + + /** + * Return the word played at this move associated with the move, or + * throw an exception if this move was not constructed from an invalid + * pair (word, coord) + */ + const wstring & getBadWord() const; + + /** + * Return the coordinates of the (incorrect) word played at this move, + * or throw an exception if this move was not constructed from an + * invalid pair (cord, coord) + */ + const wstring & getBadCoord() const; + + /** + * Return the changed letters (possibly an empty string if the player + * simply wanted to pass the turn), or throw an exception if this move + * does not correspond to a passed turn + */ + const wstring & getChangedLetters() const; + + /// To help debugging + wstring toString() const; + + private: + /// Type of move + Type m_type; + + /// Score associated with this move + int m_score; + + /// Round played at this turn + Round m_round; + + /// Word played (incorrectly) + wstring m_word; + + /// Coordinates of the word played (incorrectly) + wstring m_coord; + + /// Changed letters (or empty string for passed turn) + wstring m_letters; +}; + +#endif + diff --git a/game/player.cpp b/game/player.cpp index 436b369..78380e2 100644 --- a/game/player.cpp +++ b/game/player.cpp @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 2004-2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2004-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,14 +33,8 @@ #include "debug.h" -Player::Player(int iId) -{ - m_id = iId; - m_score = 0; -} - - -Player::~Player() +Player::Player(unsigned int iId) + : m_id(iId), m_score(0) { } @@ -61,19 +57,22 @@ const PlayedRack & Player::getLastRack() const } -const Round & Player::getLastRound() const +const Move & Player::getLastMove() const { - return m_history.getPreviousTurn().getRound(); + return m_history.getPreviousTurn().getMove(); } -void Player::endTurn(const Round &iRound, int iTurn) +void Player::endTurn(const Move &iMove, unsigned int iTurn) { - m_history.playRound(m_id,iTurn,iRound); + addPoints(iMove.getScore()); + m_history.playMove(m_id, iTurn, iMove); } void Player::removeLastTurn() { + // Remove points of the last turn + addPoints(- m_history.getPreviousTurn().getMove().getScore()); m_history.removeLastTurn(); } diff --git a/game/player.h b/game/player.h index 5579c9b..f1fe6c4 100644 --- a/game/player.h +++ b/game/player.h @@ -1,6 +1,8 @@ /***************************************************************************** - * Copyright (C) 2004-2005 Eliot - * Authors: Olivier Teuliere + * Eliot + * Copyright (C) 2004-2007 Olivier Teulière & Antoine Fraboulet + * Authors: Olivier Teulière + * Antoine Fraboulet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,8 +37,8 @@ class Turn; class Player { public: - Player(int iId); - virtual ~Player(); + explicit Player(unsigned int iId); + virtual ~Player() {} // Pseudo RTTI virtual bool isHuman() const = 0; @@ -48,8 +50,8 @@ public: const PlayedRack & getCurrentRack() const; // Get the previous rack const PlayedRack & getLastRack() const; - // Get the previous round (corresponding to the previous rack...) - const Round & getLastRound() const; + /// Get the previous move (corresponding to the previous rack...) + const Move & getLastMove() const; void setCurrentRack(const PlayedRack &iPld); @@ -65,15 +67,19 @@ public: void addPoints(int iPoints) { m_score += iPoints; } int getPoints() const { return m_score; } - // Update the player "history", with the given round. - // A new rack is created with the remaining letters - void endTurn(const Round &iRound, int iTurn); + /** + * Update the player "history", with the given move. + * A new rack is created with the remaining letters. + * The score of the player is updated with the one of the move, if it is + * meaningful. + */ + void endTurn(const Move &iMove, unsigned int iTurn); wstring toString() const; private: /// ID of the player - int m_id; + unsigned int m_id; /// Score of the player int m_score; @@ -89,8 +95,6 @@ private: class HumanPlayer: public Player { public: - string name; - HumanPlayer(int iId): Player(iId) {} virtual ~HumanPlayer() {} diff --git a/game/pldrack.cpp b/game/pldrack.cpp index 391482c..b480fd0 100644 --- a/game/pldrack.cpp +++ b/game/pldrack.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,17 +26,17 @@ * \date 2002 - 2005 */ -#include "rack.h" +#include #include "pldrack.h" - -#include "debug.h" +#include "rack.h" PlayedRack::PlayedRack() + : m_reject(false) { - reject = false; } + void PlayedRack::addOld(const Tile &t) { m_oldTiles.push_back(t); @@ -51,26 +52,22 @@ void PlayedRack::addNew(const Tile &t) void PlayedRack::getOldTiles(vector &oTiles) const { oTiles.clear(); - for (int i = 0; i < nOld(); i++) - oTiles.push_back(m_oldTiles[i]); + oTiles = m_oldTiles; } void PlayedRack::getNewTiles(vector &oTiles) const { oTiles.clear(); - for (int i = 0; i < nNew(); i++) - oTiles.push_back(m_newTiles[i]); + oTiles = m_newTiles; } void PlayedRack::getAllTiles(vector &oTiles) const { oTiles.clear(); - for (int i = 0; i < nOld(); i++) - oTiles.push_back(m_oldTiles[i]); - for (int j = 0; j < nNew(); j++) - oTiles.push_back(m_newTiles[j]); + oTiles = m_oldTiles; + oTiles.insert(oTiles.end(), m_newTiles.begin(), m_newTiles.end()); } @@ -78,6 +75,7 @@ void PlayedRack::reset() { m_oldTiles.clear(); m_newTiles.clear(); + m_reject = false; } @@ -122,72 +120,47 @@ void PlayedRack::getRack(Rack &oRack) const void PlayedRack::setOld(const Rack &iRack) { - list l; - iRack.getTiles(l); - m_oldTiles.clear(); - list::const_iterator it; - for (it = l.begin(); it != l.end(); it++) - { - addOld(*it); - } + iRack.getTiles(m_oldTiles); } void PlayedRack::setNew(const Rack &iRack) { - list l; - iRack.getTiles(l); - m_newTiles.clear(); - list::const_iterator it; - for (it = l.begin(); it != l.end(); it++) - { - addNew(*it); - } + iRack.getTiles(m_newTiles); } -int PlayedRack::setManual(const wstring& iLetters) + +void PlayedRack::setManual(const wstring& iLetters) { - unsigned int i; reset(); - if (iLetters.size() == 0) - { - return 0; /* empty is ok */ - } + // An empty rack is OK + if (iLetters.empty()) + return; + unsigned int i; for (i = 0; i < iLetters.size() && iLetters[i] != L'+'; i++) { - Tile tile(iLetters[i]); - if (tile.isEmpty()) - { - return 1; /* */ - } - addOld(tile); + addOld(Tile(iLetters[i])); } if (i < iLetters.size() && iLetters[i] == L'+') { for (i++; i < iLetters.size(); i++) { - Tile tile(iLetters[i]); - if (tile.isEmpty()) - { - return 1; /* */ - } - addNew(tile); + addNew(Tile(iLetters[i])); } } - - return 0; } -bool PlayedRack::checkRack(int cMin, int vMin) const + +bool PlayedRack::checkRack(unsigned int cMin, unsigned int vMin) const { vector::const_iterator it; - int v = 0; - int c = 0; + unsigned int v = 0; + unsigned int c = 0; for (it = m_oldTiles.begin(); it != m_oldTiles.end(); it++) { @@ -203,10 +176,9 @@ bool PlayedRack::checkRack(int cMin, int vMin) const } -void PlayedRack::operator=(const PlayedRack &iOther) +void PlayedRack::shuffleNew() { - m_oldTiles = iOther.m_oldTiles; - m_newTiles = iOther.m_newTiles; + std::random_shuffle(m_newTiles.begin(), m_newTiles.end()); } @@ -215,25 +187,23 @@ wstring PlayedRack::toString(display_mode mode) const wstring s; vector::const_iterator it; - if (nOld() > 0) + if (mode >= RACK_EXTRA && m_reject) + { + s += L"-"; + } + + if (getNbOld() > 0) { for (it = m_oldTiles.begin(); it != m_oldTiles.end(); it++) s += it->toChar(); } - if (mode > RACK_SIMPLE && nOld() > 0 && nNew() > 0) + if (mode > RACK_SIMPLE && getNbOld() > 0 && getNbNew() > 0) { s += L"+"; } - if (mode > RACK_EXTRA && reject) - { - s += L"-"; - // new rack: reject - // not after a scrabble - } - - if (nNew() > 0) + if (getNbNew() > 0) { for (it = m_newTiles.begin(); it != m_newTiles.end(); it++) s += it->toChar(); diff --git a/game/pldrack.h b/game/pldrack.h index 26e81ae..a6822a1 100644 --- a/game/pldrack.h +++ b/game/pldrack.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,7 +48,6 @@ class PlayedRack { public: PlayedRack(); - virtual ~PlayedRack() {} void reset(); void resetNew(); @@ -58,11 +58,12 @@ public: void setOld(const Rack &iRack); void setNew(const Rack &iRack); - int setManual(const wstring& iLetters); + void setManual(const wstring& iLetters); + void setReject(bool iReject = true) { m_reject = iReject; } - int nTiles() const { return nNew() + nOld(); } - int nNew() const { return m_newTiles.size(); } - int nOld() const { return m_oldTiles.size(); } + unsigned int getNbTiles() const { return getNbNew() + getNbOld(); } + unsigned int getNbNew() const { return m_newTiles.size(); } + unsigned int getNbOld() const { return m_oldTiles.size(); } void addNew(const Tile &t); void addOld(const Tile &t); @@ -70,9 +71,10 @@ public: void getOldTiles(vector &oTiles) const; void getAllTiles(vector &oTiles) const; - bool checkRack(int cMin, int vMin) const; + bool checkRack(unsigned int cMin, unsigned int vMin) const; - void operator=(const PlayedRack &iOther); + /// Randomly change the order of the "new" tiles + void shuffleNew(); enum display_mode { @@ -83,7 +85,7 @@ public: wstring toString(display_mode iShowExtraSigns = RACK_EXTRA) const; private: - bool reject; + bool m_reject; vector m_oldTiles; vector m_newTiles; }; diff --git a/game/rack.cpp b/game/rack.cpp index 5a88e3f..cb4c3cb 100644 --- a/game/rack.cpp +++ b/game/rack.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,23 +23,21 @@ * \file rack.cpp * \brief Rack class : multiset of tiles * \author Antoine Fraboulet & Olivier Teuliere - * \date 2002 - 2005 + * \date 2002 - 2007 */ #include "rack.h" +#include "dic.h" #include "encoding.h" #include "debug.h" -// FIXME: should not be here (duplicated from tile.cpp) -#define TILES_NUMBER 28 -#define MIN_CODE 1 - Rack::Rack() - : m_tiles(TILES_NUMBER, 0), m_ntiles(0) + : m_tiles(Dictionary::GetDic().getTileNumber() + 1, 0), m_ntiles(0) { } + void Rack::remove(const Tile &t) { ASSERT(in(t), @@ -58,14 +57,13 @@ void Rack::clear() } -void Rack::getTiles(list &oTiles) const +void Rack::getTiles(vector &oTiles) const { - for (unsigned int i = MIN_CODE; i < m_tiles.size(); i++) + oTiles.reserve(m_ntiles); + for (unsigned int i = 1; i < m_tiles.size(); i++) { - for (unsigned int j = 0; j < m_tiles[i]; j++) - { - oTiles.push_back(Tile::GetTileFromCode(i)); - } + // Add m_tiles[i] copies of the tile at the end of the vector + oTiles.insert(oTiles.end(), m_tiles[i], Dictionary::GetDic().getTileFromCode(i)); } } @@ -73,12 +71,10 @@ void Rack::getTiles(list &oTiles) const wstring Rack::toString() { wstring rs; - for (unsigned int i = MIN_CODE; i < m_tiles.size(); i++) + for (unsigned int i = 1; i < m_tiles.size(); i++) { - for (unsigned int j = 0; j < m_tiles[i]; j++) - { - rs += Tile::GetTileFromCode(i).toChar(); - } + // Append m_tiles[i] copies of the char + rs.append(m_tiles[i], Dictionary::GetDic().getTileFromCode(i).toChar()); } return rs; } diff --git a/game/rack.h b/game/rack.h index 7df01d9..7caac53 100644 --- a/game/rack.h +++ b/game/rack.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,17 +23,17 @@ * \file rack.h * \brief Rack class : multiset of tiles * \author Antoine Fraboulet & Olivier Teuliere - * \date 2002 - 2005 + * \date 2002 - 2007 */ #ifndef _RACK_H_ #define _RACK_H_ -#include "tile.h" -#include -#include +#include #include +#include "tile.h" + using namespace std; @@ -44,23 +45,22 @@ class Rack { public: Rack(); - virtual ~Rack() {} - int nTiles() const { return m_ntiles; } - bool isEmpty() const { return nTiles() == 0; } + unsigned int getNbTiles() const { return m_ntiles; } + bool isEmpty() const { return getNbTiles() == 0; } unsigned int in(const Tile &t) const { return m_tiles[t.toCode()]; } void add(const Tile &t) { m_tiles[t.toCode()]++; m_ntiles++; } void remove(const Tile &t); void clear(); - void getTiles(list &oTiles) const; + void getTiles(vector &oTiles) const; wstring toString(); private: /// Vector indexed by tile codes, containing the number of tiles vector m_tiles; - int m_ntiles; + unsigned int m_ntiles; }; #endif diff --git a/game/results.cpp b/game/results.cpp index 89a4b3e..f4994a3 100644 --- a/game/results.cpp +++ b/game/results.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,19 +47,19 @@ struct less_points : public binary_function - * Olivier Teuliere + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,9 +34,9 @@ using namespace std; +class Dictionary; class Board; class Rack; -typedef struct _Dictionary * Dictionary; /** @@ -47,24 +48,22 @@ typedef struct _Dictionary * Dictionary; class Results { public: - Results() {} - virtual ~Results() {} - - int size() const { return m_rounds.size(); } + unsigned int size() const { return m_rounds.size(); } void clear() { m_rounds.clear(); } - const Round & get(int) const; + const Round & get(unsigned int) const; - // Perform a search on the board + /// Perform a search on the board void search(const Dictionary &iDic, Board &iBoard, - const Rack &iRack, int iTurn); + const Rack &iRack, bool iFirstWord); - // FIXME: These methods are used to fill the container with the rounds, - // but they should not be part of the public interface + // FIXME: This method is used to fill the container with the rounds, + // but it should not be part of the public interface void add(const Round &iRound) { m_rounds.push_back(iRound); } - void sort_by_points(); private: vector m_rounds; + + void sortByPoints(); }; #endif diff --git a/game/round.cpp b/game/round.cpp index 504498d..a4af4d7 100644 --- a/game/round.cpp +++ b/game/round.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,33 +53,27 @@ void Round::init() void Round::setWord(const vector &iTiles) { - m_word.clear(); - - vector::const_iterator it; - for (it = iTiles.begin(); it != iTiles.end(); it++) - { - m_word.push_back(*it); - // XXX: always from rack? - m_tileOrigin.push_back(FROMRACK); - } + m_word = iTiles; + // XXX: always from rack? + m_tileOrigin = vector(iTiles.size(), FROMRACK); } -void Round::setFromRack(int iIndex) +void Round::setFromRack(unsigned int iIndex) { m_tileOrigin[iIndex] &= ~FROMBOARD; m_tileOrigin[iIndex] |= FROMRACK; } -void Round::setFromBoard(int iIndex) +void Round::setFromBoard(unsigned int iIndex) { m_tileOrigin[iIndex] &= ~FROMRACK; m_tileOrigin[iIndex] |= FROMBOARD; } -void Round::setJoker(int iIndex, bool value) +void Round::setJoker(unsigned int iIndex, bool value) { if (value) m_tileOrigin[iIndex] |= JOKER; @@ -87,25 +82,19 @@ void Round::setJoker(int iIndex, bool value) } -bool Round::isJoker(int iIndex) const +bool Round::isJoker(unsigned int iIndex) const { return m_tileOrigin[iIndex] & JOKER; } -const Tile& Round::getTile(int iIndex) const +const Tile& Round::getTile(unsigned int iIndex) const { return m_word[iIndex]; } -int Round::getWordLen() const -{ - return m_word.size(); -} - - -bool Round::isPlayedFromRack(int iIndex) const +bool Round::isPlayedFromRack(unsigned int iIndex) const { return m_tileOrigin[iIndex] & FROMRACK; } @@ -150,7 +139,7 @@ wstring Round::getWord() const wchar_t c; wstring s; - for (int i = 0; i < getWordLen(); i++) + for (unsigned int i = 0; i < getWordLen(); i++) { c = getTile(i).toChar(); if (isJoker(i)) diff --git a/game/round.h b/game/round.h index 3136268..3e39919 100644 --- a/game/round.h +++ b/game/round.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,7 +43,6 @@ public: * *************************/ Round(); - virtual ~Round() {} void init(); /************************* @@ -58,23 +58,23 @@ public: *************************/ void setPoints(int iPoints) { m_points = iPoints; } void setBonus(bool iBonus) { m_bonus = iBonus; } - void setTile(int iIndex, const Tile &iTile) { m_word[iIndex] = iTile; } + void setTile(unsigned int iIndex, const Tile &iTile) { m_word[iIndex] = iTile; } void setWord(const vector &iTiles); - void setFromRack(int iIndex); - void setFromBoard(int iIndex); - void setJoker(int iIndex, bool value = true); + void setFromRack(unsigned int iIndex); + void setFromBoard(unsigned int iIndex); + void setJoker(unsigned int iIndex, bool value = true); /************************* * General getters *************************/ - bool isJoker (int iIndex) const; - bool isPlayedFromRack(int iIndex) const; - const Tile& getTile (int iIndex) const; + bool isJoker (unsigned int iIndex) const; + bool isPlayedFromRack(unsigned int iIndex) const; + const Tile& getTile (unsigned int iIndex) const; wstring getWord() const; - int getWordLen() const; - int getPoints() const { return m_points; } - int getBonus() const { return m_bonus; } + unsigned int getWordLen() const { return m_word.size(); } + int getPoints() const { return m_points; } + int getBonus() const { return m_bonus; } /************************* * Coordinates diff --git a/game/settings.cpp b/game/settings.cpp new file mode 100644 index 0000000..d06bfce --- /dev/null +++ b/game/settings.cpp @@ -0,0 +1,134 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#include + +#include "settings.h" + + +Settings *Settings::m_instance = NULL; + + +Settings & Settings::Instance() +{ + if (m_instance == NULL) + { + m_instance = new Settings; + } + return *m_instance; +} + + +void Settings::Destroy() +{ + delete m_instance; + m_instance = NULL; +} + + +Settings::Settings() +{ + // ============== General options ============== + + + // ============== Training mode options ============== + + + // ============== Duplicate mode options ============== + + // Minimum number of players in a duplicate game needed to apply a "solo" bonus + // (16 is the ODS value) + m_intHandler.addOption("duplicate-solo-players", 16); + // Number of points granted for a solo (10 is the ODS value) + m_intHandler.addOption("duplicate-solo-value", 10); + + // If true, Eliot complains when the player does something illegal + // If false, the word is accepted (with a score of 0) and the player does + // not get a second chance + m_boolHandler.addOption("duplicate-reject-invalid", false); + + + // ============== Freegame mode options ============== + + // If true, Eliot complains when the player does something illegal + // If false, the word is accepted (with a score of 0) and the player does + // not get a second chance. + // Trying to change letters or to pass the turn in an incorrect way will + // be rejected in any case. + m_boolHandler.addOption("freegame-reject-invalid", false); +} + + +void Settings::setBool(const string &iName, bool iValue) +{ + m_boolHandler.setOption(iName, iValue); +} + + +bool Settings::getBool(const string &iName) const +{ + return m_boolHandler.getOption(iName); +} + + +void Settings::setInt(const string &iName, int iValue) +{ + m_intHandler.setOption(iName, iValue); +} + + +int Settings::getInt(const string &iName) const +{ + return m_intHandler.getOption(iName); +} + + +template +void Settings::OptionsHandler::addOption(const string &iName, const T &iValue) +{ + m_options[iName] = iValue; +} + + +template +void Settings::OptionsHandler::setOption(const string &iName, const T &iValue) +{ + typename map::iterator it = m_options.find(iName); + if (it == m_options.end()) + { + // FIXME: throw an exception object instead + throw 1; + } + it->second = iValue; +} + + +template +const T& Settings::OptionsHandler::getOption(const string &iName) const +{ + typename map::const_iterator it = m_options.find(iName); + if (it == m_options.end()) + { + // FIXME: throw an exception object instead + throw 1; + } + return it->second; +} + diff --git a/game/settings.h b/game/settings.h new file mode 100644 index 0000000..1032317 --- /dev/null +++ b/game/settings.h @@ -0,0 +1,109 @@ +/***************************************************************************** + * Eliot + * Copyright (C) 2007 Olivier Teulière + * Authors: Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ + +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include +#include + +using std::string; +using std::map; + + +/** + * This class centralizes the various configuration options of Eliot. + * It implements the Singleton pattern. + * + * Currently, there are few settings, and their initial value is hard-coded. + * In a later phase, this class will be able to export/import settings + * to/from a configuration file, and it should be possible to override + * configuration settings with settings given on the command-line (TODO). + * The boost::program_options library could be useful for this. + * + * This class will also be helpful for the "Settings" dialog box of the GUI. + */ +class Settings +{ +public: + /// Access to the singleton + static Settings& Instance(); + /// Destroy the singleton cleanly + static void Destroy(); + + void setBool(const string &iName, bool iValue); + bool getBool(const string &iName) const; + + void setInt(const string &iName, int iValue); + int getInt(const string &iName) const; + +private: + + /** + * This nested class is simply there to handle storage and retrieval + * for options of a particular type (and factorize code) + */ + template + class OptionsHandler + { + public: + /// Set the value of an option + /** + * If the option already exists, its value is replaced, + * otherwise the option is created + */ + void addOption(const string &iName, const T &iValue); + + /** + * Change the value of an existing option. + * An exception is thrown if the option doesn't exist yet + */ + void setOption(const string &iName, const T &iValue); + + /** + * Query the value of an option. + * An exception is thrown if the option doesn't exist + */ + const T& getOption(const string &iName) const; + + private: + map m_options; + }; + + + /// Singleton instance + static Settings *m_instance; + Settings(); + + /// The settings can be of various types + OptionsHandler m_boolHandler; + OptionsHandler m_intHandler; + // Add types as needed... + +}; + +#endif + +/// Local Variables: +/// mode: c++ +/// mode: hs-minor +/// c-basic-offset: 4 +/// indent-tabs-mode: nil +/// End: diff --git a/game/tile.cpp b/game/tile.cpp deleted file mode 100644 index 7080321..0000000 --- a/game/tile.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Olivier Teuliere - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - *****************************************************************************/ - -#include "tile.h" -#include - - -/************************* - * French tiles - * Zero + 26 letters + joker - * tiles ares supposed to be contiguous and joker is separated - *************************/ - -#define TILE_START 'A' -#define TILE_END 'Z' -#define TILE_JOKER '?' -#define TILE_DUMMY '%' - -#define TILE_IDX_DUMMY 0 -#define TILE_IDX_START 1 -#define TILE_IDX_END 26 -#define TILE_IDX_JOKER 27 - -#define TILES_NUMBER 28 - -/* The jokers and the 'Y' can be considered both as vowels or consonants */ -const unsigned int Tiles_vowels[TILES_NUMBER] = -{ -/* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? */ - 0,1,0,0,0, 1,0,0,0,1,0, 0,0,0,0,1,0,0,0,0,0,1,0, 0, 0, 1, 0,1 -}; - -const unsigned int Tiles_consonants[TILES_NUMBER] = -{ -/* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? */ - 0,0,1,1,1, 0,1,1,1,0,1, 1,1,1,1,0,1,1,1,1,1,0,1, 1, 1, 1, 1,1 -}; - -const unsigned int Tiles_numbers[TILES_NUMBER] = -{ -/* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? */ - 0,9,2,2,3,15,2,2,2,8,1, 1,5,3,6,6,2,1,6,6,6,6,2, 1, 1, 1, 1,2 -}; - -const unsigned int Tiles_points[TILES_NUMBER] = -{ -/* x A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? */ - 0,1,3,3,2, 1,4,2,4,1,8,10,1,2,1,1,3,8,1,1,1,1,4,10,10,10,10,0 -}; - -/*************************** - ***************************/ - -const Tile Tile::m_TheJoker(TILE_JOKER); -const Tile Tile::m_TheDummy(0); -list Tile::m_tilesList; -vector Tile::m_tilesVect(TILES_NUMBER, Tile::dummy()); -bool Tile::m_vectInitialized(false); - - -Tile::Tile(wchar_t c) -{ - if (c == TILE_JOKER) - { - m_joker = true; - m_dummy = false; - m_char = TILE_JOKER; - m_code = 27; - } - else if (isalpha(c)) - { - m_joker = islower(c); - m_dummy = false; - m_char = towupper(c); - m_code = m_char - 'A' + 1; - } - else - { - m_joker = false; - m_dummy = true; - m_char = 0; - m_code = 0; - } -} - - -bool Tile::isVowel() const -{ - if (m_dummy) - return false; - if (m_joker) - return Tiles_vowels[TILE_IDX_JOKER]; - return Tiles_vowels[TILE_IDX_START + m_char - TILE_START]; -} - - -bool Tile::isConsonant() const -{ - if (m_dummy) - return false; - if (m_joker) - return Tiles_consonants[TILE_IDX_JOKER]; - return Tiles_consonants[TILE_IDX_START + m_char - TILE_START]; -} - - -unsigned int Tile::maxNumber() const -{ - if (m_dummy) - return false; - if (m_joker) - return Tiles_numbers[TILE_IDX_JOKER]; - return Tiles_numbers[TILE_IDX_START + m_char - TILE_START]; -} - - -unsigned int Tile::getPoints() const -{ - if (m_dummy) - return false; - if (m_joker) - return Tiles_points[TILE_IDX_JOKER]; - return Tiles_points[TILE_IDX_START + m_char - TILE_START]; -} - - -const list& Tile::getAllTiles() -{ - if (Tile::m_tilesList.size() == 0) - { - // XXX: this should be filled from a "language file" instead - for (char i = TILE_START; i <= TILE_END; i++) - Tile::m_tilesList.push_back(Tile(i)); - m_tilesList.push_back(Tile(TILE_JOKER)); - } - return Tile::m_tilesList; -} - - -const Tile& Tile::GetTileFromCode(unsigned int iCode) -{ - if (!m_vectInitialized) - { - // XXX: this should be filled from a "language file" instead - for (char i = TILE_IDX_START; i <= TILE_IDX_END; i++) - Tile::m_tilesVect[i] = Tile(i + 'A' - TILE_IDX_START); - m_tilesVect[TILE_IDX_JOKER] = Tile::Joker(); - m_vectInitialized = true; - } - return Tile::m_tilesVect[iCode]; -} - - -wchar_t Tile::toChar() const -{ - if (m_dummy) - return TILE_DUMMY; - if (m_joker) - { - if (iswalpha(m_char)) - return towlower(m_char); - else - return TILE_JOKER; - } - return m_char; -} - -unsigned int Tile::toCode() const -{ - return m_code; -} - -bool Tile::operator <(const Tile &iOther) const -{ - if (iOther.m_dummy) - return false; - else if (m_dummy) - return true; - else if (m_joker) - return false; - else if (iOther.m_joker) - return true; - else - return m_char < iOther.m_char; -} - - -bool Tile::operator ==(const Tile &iOther) const -{ - if (m_dummy || iOther.m_dummy) - return m_dummy == iOther.m_dummy; - if (m_joker || iOther.m_joker) - { - if (m_joker != iOther.m_joker) - return false; - return m_char == iOther.m_char; - } - return m_char == iOther.m_char; -// return (m_joker && iOther.m_joker && m_char == iOther.m_char) || -// (m_dummy && iOther.m_dummy) || -// (!m_dummy && !iOther.m_dummy -// && !m_joker && !iOther.m_joker -// && m_char == iOther.m_char); -} - - -bool Tile::operator !=(const Tile &iOther) const -{ - return !(*this == iOther); -} - -/// Local Variables: -/// mode: c++ -/// mode: hs-minor -/// c-basic-offset: 4 -/// indent-tabs-mode: nil -/// End: diff --git a/game/training.cpp b/game/training.cpp index f68a1d5..5a0f2ce 100644 --- a/game/training.cpp +++ b/game/training.cpp @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +19,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +#include + #include "dic.h" #include "tile.h" #include "rack.h" #include "round.h" +#include "move.h" #include "pldrack.h" #include "player.h" #include "training.h" @@ -30,12 +34,8 @@ #include "debug.h" -Training::Training(const Dictionary &iDic): Game(iDic) -{ -} - - -Training::~Training() +Training::Training(const Dictionary &iDic) + : Game(iDic) { } @@ -46,11 +46,10 @@ int Training::setRackRandom(bool iCheck, set_rack_mode mode) int res; int try_number = 0; - int p = m_currPlayer; m_results.clear(); do { - res = helperSetRackRandom(p, iCheck, mode); + res = helperSetRackRandomOld(m_currPlayer, iCheck, mode); try_number ++; } while (res == 2 && try_number < MAX_RANDOM_TRY); // 0 : ok @@ -59,28 +58,27 @@ int Training::setRackRandom(bool iCheck, set_rack_mode mode) return res; } + int Training::setRackManual(bool iCheck, const wstring &iLetters) { - int res; - int p = m_currPlayer; - wstring::iterator it; - wstring uLetters; // uppercase letters - // letters can be lowercase or uppercase as they are + // Letters can be lowercase or uppercase as they are // coming from user input. We do not consider a lowercase // letter to be a joker which has been assigned to a letter. - m_results.clear(); - uLetters = iLetters; - for (it = uLetters.begin(); it != uLetters.end(); it ++) - { - *it = towupper(*it); - } - res = helperSetRackManual(p, iCheck, uLetters); - // 0 : ok - // 1 : not enough tiles - // 2 : check failed (number of voyels before round 15) + // As a result, we simply make all the letters uppercase + wstring upperLetters = iLetters; + std::transform(upperLetters.begin(), upperLetters.end(), + upperLetters.begin(), towupper); + int res = helperSetRackManual(m_currPlayer, iCheck, upperLetters); + // 0: ok + // 1: not enough tiles + // 2: check failed (number of vowels before round 15) + // 3: letters not in the dictionary + if (res == 0) + m_results.clear(); return res; } + int Training::setRack(set_rack_mode iMode, bool iCheck, const wstring &iLetters) { int res = 0; @@ -99,9 +97,10 @@ int Training::setRack(set_rack_mode iMode, bool iCheck, const wstring &iLetters) return res; } + int Training::play(const wstring &iCoord, const wstring &iWord) { - /* Perform all the validity checks, and fill a round */ + // Perform all the validity checks, and fill a round Round round; int res = checkPlayedWord(iCoord, iWord, round); @@ -111,23 +110,21 @@ int Training::play(const wstring &iCoord, const wstring &iWord) return res; } - /* Update the rack and the score of the current player */ debug("play: %s %s %d\n", convertToMb(round.getWord()).c_str(), convertToMb(round.getCoord().toString()).c_str(), round.getPoints()); - m_players[m_currPlayer]->addPoints(round.getPoints()); - // see game.cpp::helperPlayRound():99 comment - m_players[m_currPlayer]->endTurn(round, m_history.getSize()); + Move move(round); + // Update the rack and the score of the current player + // Player::endTurn() must be called before Game::helperPlayMove(). + // See the big comment in game.cpp, line 96 + m_players[m_currPlayer]->endTurn(move, m_history.getSize()); - /* Everything is OK, we can play the word */ - if (helperPlayRound(round)) - { - debug("play: error during play\n"); - } + // Everything is OK, we can play the word + helperPlayMove(m_currPlayer, move); - /* Next turn */ + // Next turn endTurn(); return 0; @@ -146,10 +143,9 @@ int Training::start() } -int Training::endTurn() +void Training::endTurn() { - // Nothing to do? - return 0; + // Nothing to do, but this method is kept for consistency with other modes } @@ -159,31 +155,27 @@ void Training::search() Rack r; m_players[m_currPlayer]->getCurrentRack().getRack(r); debug("Training::search for %s\n", convertToMb(r.toString()).c_str()); - m_results.search(*m_dic, m_board, r, m_history.getSize()); + m_results.search(m_dic, m_board, r, m_history.beforeFirstRound()); } -int Training::playResult(int n) +int Training::playResult(unsigned int n) { - Player *player = m_players[m_currPlayer]; if (n >= m_results.size()) return 2; - const Round &round = m_results.get(n); - /* Update the rack and the score of the current player */ - player->addPoints(round.getPoints()); - player->endTurn(round, m_history.getSize()); + Move move(m_results.get(n)); + // Update the rack and the score of the current player + m_players[m_currPlayer]->endTurn(move, m_history.getSize()); - int res = helperPlayRound(round); + // Update the game + helperPlayMove(m_currPlayer, move); + m_results.clear(); - if (res == 0) - m_results.clear(); - - /* Next turn */ - // XXX: Should it be done by the interface instead? + // Next turn endTurn(); - return res; + return 0; } @@ -201,9 +193,9 @@ void Training::addAIPlayer() } -void Training::testPlay(int num) +void Training::testPlay(unsigned int num) { - ASSERT(0 <= num && num < m_results.size(), "Wrong result number"); + ASSERT(num < m_results.size(), "Wrong result number"); m_testRound = m_results.get(num); m_board.testRound(m_results.get(num)); } @@ -215,6 +207,7 @@ void Training::removeTestPlay() m_testRound = Round(); } + wstring Training::getTestPlayWord() const { return m_testRound.getWord(); diff --git a/game/training.h b/game/training.h index ba477bf..e40bb85 100644 --- a/game/training.h +++ b/game/training.h @@ -1,7 +1,8 @@ /***************************************************************************** - * Copyright (C) 1999-2005 Eliot - * Authors: Antoine Fraboulet - * Olivier Teuliere + * Eliot + * Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +25,7 @@ #include #include "game.h" +#include "round.h" #include "results.h" using std::string; @@ -34,6 +36,7 @@ using std::wstring; * This class handles the logic specific to a training game. * As its name indicates, it is not a game in the literal meaning of the word, * in particular because the rack can be set at will. + * * Note: No player should be added to this game, a human player is added * automatically (in the start() method) */ @@ -48,10 +51,13 @@ public: * Game handling *************************/ virtual int start(); + + /// See description of Game::play() virtual int play(const wstring &iCoord, const wstring &iWord); - virtual int endTurn(); + void search(); - int playResult(int); + const Results& getResults() const { return m_results; }; + int playResult(unsigned int); int setRackRandom(bool, set_rack_mode); int setRackManual(bool iCheck, const wstring &iLetters); @@ -59,7 +65,7 @@ public: /************************* * Override the default behaviour of these methods, because in training - * we only want a human player + * mode we only want a human player *************************/ virtual void addHumanPlayer(); virtual void addAIPlayer(); @@ -68,23 +74,25 @@ public: * Functions to access the current search results * The int parameter should be 0 <= int < getNResults *************************/ - const Results& getResults() const { return m_results; }; - /// Place a temporary word on the board for preview purpose - void testPlay(int); - /// Remove the temporary word(s) + /// Place a temporary word on the board for preview purposes + void testPlay(unsigned int); + /// Remove the temporary word void removeTestPlay(); /// Get the temporary word wstring getTestPlayWord() const; private: - // Private constructor and destructor to force using the GameFactory class + /// Private constructor and destructor to force using the GameFactory class Training(const Dictionary &iDic); - virtual ~Training(); - // Search results, with all the possible rounds - Round m_testRound; + void endTurn(); + + /// Search results, with all the possible rounds Results m_results; + + /// Round corresponding to the last test play (if any) + Round m_testRound; }; #endif /* _TRAINING_H_ */ diff --git a/game/turn.cpp b/game/turn.cpp index c4ba38e..471f5d8 100644 --- a/game/turn.cpp +++ b/game/turn.cpp @@ -1,67 +1,57 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file turn.cpp - * \brief Game turn (= id + pldrack + round) + * \brief Game turn (= id + pldrack + move) * \author Antoine Fraboulet * \date 2005 */ -#include -#include "pldrack.h" -#include "round.h" #include "turn.h" +// FIXME: move set to an invalid value. It would be better to get rid of this +// constructor completely Turn::Turn() -{ - m_num = 0; - m_playerId = 0; - m_pldrack = PlayedRack(); - m_round = Round(); -} - -Turn::Turn(int iNum, int iPlayerId, - const PlayedRack& iPldRack, const Round& iRound) - : m_num(iNum), m_playerId(iPlayerId), m_pldrack(iPldRack), m_round(iRound) + : m_num(0), m_playerId(0), m_move(L"", L"") { } -#if 0 -void Turn::operator=(const Turn &iOther) + +Turn::Turn(unsigned int iNum, unsigned int iPlayerId, + const PlayedRack& iPldRack, const Move& iMove) + : m_num(iNum), m_playerId(iPlayerId), m_pldrack(iPldRack), m_move(iMove) { - m_num = iOther.m_num; - m_playerId = iOther.m_playerId; - m_pldrack = iOther.m_pldrack; - m_round = iOther.m_round; } -#endif + wstring Turn::toString(bool iShowExtraSigns) const { - wstring rs = L""; + wstring rs; if (iShowExtraSigns) { // TODO } - rs = rs + m_pldrack.toString() + L" " + m_round.toString(); + rs = rs + m_pldrack.toString() + L" " + m_move.toString(); return rs; } diff --git a/game/turn.h b/game/turn.h index cb9ed6b..dfc6a14 100644 --- a/game/turn.h +++ b/game/turn.h @@ -1,25 +1,27 @@ -/* Eliot */ -/* Copyright (C) 1999 Antoine Fraboulet */ -/* */ -/* This file is part of Eliot. */ -/* */ -/* Eliot is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* Eliot is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/***************************************************************************** + * Eliot + * Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière + * Authors: Antoine Fraboulet + * Olivier Teulière + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + *****************************************************************************/ /** * \file turn.h - * \brief Game turn (= id + pldrack + round) + * \brief Game turn (= id + pldrack + move) * \author Antoine Fraboulet * \date 2005 */ @@ -27,35 +29,45 @@ #ifndef _TURN_H #define _TURN_H +#include +#include "pldrack.h" +#include "move.h" + +using std::wstring; + + +/** + * A Turn is the information about one 'move' done by a player. + * It consists of the player who played, the rack, and the actual move. + * A turn also has an id (XXX: currently never read) + * + * This class has no logic, it is merely there to aggregate corresponding + * data. + */ class Turn { public: Turn(); - Turn(int iNum, int iPlayerId, - const PlayedRack& iPldRack, const Round& iRound); - virtual ~Turn() {}; + Turn(unsigned int iNum, unsigned int iPlayerId, + const PlayedRack& iPldRack, const Move& iMove); - void setNum(int iNum) { m_num = iNum; } - void setPlayer(int iPlayerId) { m_playerId = iPlayerId; } + void setNum(unsigned int iNum) { m_num = iNum; } + void setPlayer(unsigned int iPlayerId) { m_playerId = iPlayerId; } void setPlayedRack(const PlayedRack& iPldRack) { m_pldrack = iPldRack; } - void setRound(const Round& iRound) { m_round = iRound; } + void setMove(const Move& iMove) { m_move = iMove; } - int getNum() const { return m_num; } - int getPlayer() const { return m_playerId; } + unsigned int getNum() const { return m_num; } + unsigned int getPlayer() const { return m_playerId; } const PlayedRack& getPlayedRack() const { return m_pldrack; } - const Round& getRound() const { return m_round; } + const Move& getMove() const { return m_move; } -#if 0 - void operator=(const Turn &iOther); -#endif wstring toString(bool iShowExtraSigns = false) const; private: - int m_num; - int m_playerId; - PlayedRack m_pldrack; - Round m_round; - + unsigned int m_num; + unsigned int m_playerId; + PlayedRack m_pldrack; + Move m_move; }; #endif diff --git a/m4/.cvsignore b/m4/.cvsignore index dae6bed..2030157 100644 --- a/m4/.cvsignore +++ b/m4/.cvsignore @@ -1,18 +1,30 @@ codeset.m4 gettext.m4 +glibc2.m4 glibc21.m4 iconv.m4 intdiv0.m4 +intl.m4 +intldir.m4 +intmax.m4 inttypes-pri.m4 -inttypes.m4 inttypes_h.m4 -isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 +lock.m4 +longdouble.m4 +longlong.m4 +nls.m4 +po.m4 +printf-posix.m4 progtest.m4 +size_max.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4 - +visibility.m4 +wchar_t.m4 +wint_t.m4 +xsize.m4 diff --git a/m4/ax_boost_base.m4 b/m4/ax_boost_base.m4 new file mode 100644 index 0000000..1d2537e --- /dev/null +++ b/m4/ax_boost_base.m4 @@ -0,0 +1,198 @@ +##### http://autoconf-archive.cryp.to/ax_boost_base.html +# +# SYNOPSIS +# +# AX_BOOST_BASE([MINIMUM-VERSION]) +# +# DESCRIPTION +# +# Test for the Boost C++ libraries of a particular version (or newer) +# +# If no path to the installed boost library is given the macro +# searchs under /usr, /usr/local, and /opt, and evaluates the +# $BOOST_ROOT environment variable. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) +# +# And sets: +# +# HAVE_BOOST +# +# LAST MODIFICATION +# +# 2007-03-15 +# +# COPYLEFT +# +# Copyright (c) 2007 Thomas Porschberg +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty provided +# the copyright notice and this notice are preserved. + +AC_DEFUN([AX_BOOST_BASE], +[ +AC_ARG_WITH([boost], + AS_HELP_STRING([--with-boost@<:@=DIR@:>@], [use boost (default is yes) - it is possible to specify the root directory for boost (optional)]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ac_boost_path="" + else + want_boost="yes" + ac_boost_path="$withval" + fi + ], + [want_boost="yes"]) + +if test "x$want_boost" = "xyes"; then + boost_lib_version_req=ifelse([$1], ,1.20.0,$1) + boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` + boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` + boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` + boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` + if test "x$boost_lib_version_req_sub_minor" = "x" ; then + boost_lib_version_req_sub_minor="0" + fi + WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` + AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) + succeeded=no + + dnl first we check the system location for boost libraries + dnl this location ist chosen if boost libraries are installed with the --layout=system option + dnl or if you install boost with RPM + if test "$ac_boost_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_path/lib" + BOOST_CPPFLAGS="-I$ac_boost_path/include" + else + for ac_boost_path_tmp in /usr /usr/local /opt ; do + if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then + BOOST_LDFLAGS="-L$ac_boost_path_tmp/lib" + BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" + break; + fi + done + fi + + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + + + + dnl if we found no boost with system layout we search for boost libraries + dnl built and installed without the --layout=system option or for a staged(not installed) version + if test "x$succeeded" != "xyes"; then + _version=0 + if test "$ac_boost_path" != ""; then + BOOST_LDFLAGS="-L$ac_boost_path/lib" + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + fi + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" + done + fi + else + for ac_boost_path in /usr /usr/local /opt ; do + if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then + for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do + _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` + V_CHECK=`expr $_version_tmp \> $_version` + if test "$V_CHECK" = "1" ; then + _version=$_version_tmp + best_path=$ac_boost_path + fi + done + fi + done + + VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` + BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" + BOOST_LDFLAGS="-L$best_path/lib" + + if test "x$BOOST_ROOT" != "x"; then + if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/lib" && test -r "$BOOST_ROOT/stage/lib"; then + version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` + stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` + stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` + V_CHECK=`expr $stage_version_shorten \>\= $_version` + if test "$V_CHECK" = "1" ; then + AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) + BOOST_CPPFLAGS="-I$BOOST_ROOT" + BOOST_LDFLAGS="-L$BOOST_ROOT/stage/lib" + fi + fi + fi + fi + + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + #if BOOST_VERSION >= $WANT_BOOST_VERSION + // Everything is okay + #else + # error Boost version is too old + #endif + ]])],[ + AC_MSG_RESULT(yes) + succeeded=yes + found_system=yes + ],[ + ]) + AC_LANG_POP([C++]) + fi + + if test "$succeeded" != "yes" ; then + if test "$_version" = "0" ; then + AC_MSG_ERROR([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) + else + AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) + fi + else + AC_SUBST(BOOST_CPPFLAGS) + AC_SUBST(BOOST_LDFLAGS) + AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" +fi + +]) diff --git a/po/.cvsignore b/po/.cvsignore index b7bd589..372c261 100644 --- a/po/.cvsignore +++ b/po/.cvsignore @@ -1,6 +1,7 @@ Makefile Makefile.in Makefile.in.in +Makevars.template POTFILES *.mo *.gmo @@ -8,3 +9,9 @@ POTFILES *.sin *.header Rules-quot +stamp-po +en@boldquot.insert-header +en@boldquot.po +en@quot.insert-header +en@quot.po + diff --git a/po/LINGUAS b/po/LINGUAS index 527e861..195fa84 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -fr +fr en@quot en@boldquot diff --git a/po/POTFILES.in b/po/POTFILES.in index 135d23d..9c6c2fc 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,16 +1,20 @@ -./dic/automaton.c +./dic/automaton.cpp ./dic/automaton.h -./dic/compdic.c -./dic/dic.c +./dic/header.cpp +./dic/header.h +./dic/dic.cpp ./dic/dic.h ./dic/dic_internals.h -./dic/dic_search.c -./dic/dic_search.h -./dic/hashtable.c +./dic/dic_search.cpp +./dic/hashtable.cpp ./dic/hashtable.h -./dic/listdic.c -./dic/regexp.c +./dic/regexp.cpp ./dic/regexp.h +./dic/tile.cpp +./dic/tile.h +./dic/compdic.cpp +./dic/listdic.cpp +./dic/regexpmain.cpp ./game/bag.cpp ./game/bag.h ./game/board.cpp @@ -39,11 +43,9 @@ ./game/results.h ./game/round.cpp ./game/round.h -./game/tile.cpp -./game/tile.h ./game/training.cpp ./game/training.h -./utils/eliottxt.cpp +#./utils/eliottxt.cpp ./utils/game_io.h ./utils/game_io.cpp ./utils/ncurses.cpp @@ -68,4 +70,3 @@ ./wxwin/printout.cc ./wxwin/searchpanel.h ./wxwin/searchpanel.cc -./config.h diff --git a/po/eliot.pot b/po/eliot.pot index ebfcdce..a533479 100644 --- a/po/eliot.pot +++ b/po/eliot.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2005-04-03 00:04+0200\n" +"POT-Creation-Date: 2007-12-23 23:08+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,247 +16,1163 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: utils/ncurses.cpp:184 -msgid " Scores " +#: dic/header.cpp:317 dic/header.cpp:318 +msgid "Unknown (old format)" msgstr "" -#: utils/ncurses.cpp:190 +#: dic/header.cpp:492 +#, c-format +msgid "dictionary name: %s\n" +msgstr "" + +#: dic/header.cpp:497 +#, c-format +msgid "compressed on: %s\n" +msgstr "" + +#: dic/header.cpp:501 +#, c-format +msgid "compressed on: Unknown date (old format)\n" +msgstr "" + +#: dic/header.cpp:503 +#, c-format +msgid "compressed using a binary compiled by: %s\n" +msgstr "" + +#: dic/header.cpp:504 +#, c-format +msgid "dictionary type: %s\n" +msgstr "" + +#: dic/header.cpp:505 +#, c-format +msgid "letters: %s\n" +msgstr "" + +#: dic/header.cpp:506 +#, c-format +msgid "number of letters: %d\n" +msgstr "" + +#: dic/header.cpp:507 +#, c-format +msgid "number of words: %d\n" +msgstr "" + +#: dic/header.cpp:508 +#, c-format +msgid "header size: %u bytes\n" +msgstr "" + +#: dic/header.cpp:510 +#, c-format +msgid "root: %d (edge)\n" +msgstr "" + +#: dic/header.cpp:511 +#, c-format +msgid "nodes: %d used + %d saved\n" +msgstr "" + +#: dic/header.cpp:512 +#, c-format +msgid "edges: %d used + %d saved\n" +msgstr "" + +#: dic/header.cpp:514 +#, c-format +msgid "letter | points | frequency | vowel | consonant\n" +msgstr "" + +#: dic/compdic.cpp:392 +msgid "Mandatory options:" +msgstr "" + +#: dic/compdic.cpp:393 +msgid " -d, --dicname Set the dictionary name and version" +msgstr "" + +#: dic/compdic.cpp:394 +msgid "" +" -l, --letters Path to the file containing the letters (see below)" +msgstr "" + +#: dic/compdic.cpp:395 +msgid "" +" -i, --input Path to the uncompressed dictionary file (encoded " +"in UTF-8)" +msgstr "" + +#: dic/compdic.cpp:396 +msgid "" +" -o, --output , Navigate in a box line by line" msgstr "" -#: utils/ncurses.cpp:330 +#: utils/ncurses.cpp:450 msgid " , Navigate in a box page by page" msgstr "" -#: utils/ncurses.cpp:331 +#: utils/ncurses.cpp:451 msgid " Ctrl-l Refresh the screen" msgstr "" -#: utils/ncurses.cpp:339 -msgid " Play a word " +#: utils/ncurses.cpp:462 wxwin/auxframes.cc:148 +msgid "Bag" +msgstr "" + +#: utils/ncurses.cpp:469 +msgid " LETTER | POINTS | FREQUENCY | REMAINING" +msgstr "" + +#: utils/ncurses.cpp:522 +msgid "Play a word" msgstr "" #. TRANSLATORS: Align the : when translating "Played word:" and #. "Coordinates:". For example: #. Pl. word : #. Coordinates: -#: utils/ncurses.cpp:340 utils/ncurses.cpp:348 +#: utils/ncurses.cpp:523 utils/ncurses.cpp:531 msgid "Played word:" msgstr "" -#: utils/ncurses.cpp:341 utils/ncurses.cpp:349 +#: utils/ncurses.cpp:524 utils/ncurses.cpp:532 msgid "Coordinates:" msgstr "" -#: utils/ncurses.cpp:363 +#: utils/ncurses.cpp:546 msgid "Incorrect or misplaced word" msgstr "" -#: utils/ncurses.cpp:373 -msgid " Dictionary " +#: utils/ncurses.cpp:556 +msgid "Dictionary" msgstr "" -#: utils/ncurses.cpp:374 +#: utils/ncurses.cpp:557 msgid "Enter the word to check:" msgstr "" -#: utils/ncurses.cpp:383 +#: utils/ncurses.cpp:566 #, c-format -msgid "The word '%s' exists" +msgid "The word '%ls' exists" msgstr "" -#: utils/ncurses.cpp:385 +#: utils/ncurses.cpp:568 #, c-format -msgid "The word '%s' does not exist" +msgid "The word '%ls' does not exist" msgstr "" -#: utils/ncurses.cpp:395 -msgid " Save the game " +#: utils/ncurses.cpp:578 wxwin/mainframe.cc:460 wxwin/mainframe.cc:468 +msgid "Save the game" msgstr "" -#: utils/ncurses.cpp:396 utils/ncurses.cpp:425 +#: utils/ncurses.cpp:579 utils/ncurses.cpp:609 msgid "Enter the file name:" msgstr "" -#: utils/ncurses.cpp:406 +#: utils/ncurses.cpp:589 #, c-format -msgid "Cannot open file %s for writing" +msgid "Cannot open file %ls for writing" msgstr "" -#: utils/ncurses.cpp:413 +#: utils/ncurses.cpp:597 #, c-format -msgid "Game saved in %s" +msgid "Game saved in '%ls'" msgstr "" -#: utils/ncurses.cpp:424 -msgid " Load a game " +#: utils/ncurses.cpp:608 wxwin/mainframe.cc:269 wxwin/mainframe.cc:390 +#: wxwin/mainframe.cc:414 +msgid "Load a game" msgstr "" -#: utils/ncurses.cpp:435 +#: utils/ncurses.cpp:619 #, c-format -msgid "Cannot open file %s for reading" +msgid "Cannot open file '%ls' for reading" msgstr "" -#: utils/ncurses.cpp:443 +#: utils/ncurses.cpp:627 #, c-format msgid "Invalid saved game" msgstr "" -#: utils/ncurses.cpp:447 +#: utils/ncurses.cpp:632 #, c-format msgid "Game loaded" msgstr "" -#: utils/ncurses.cpp:462 -msgid " Pass your turn " +#: utils/ncurses.cpp:647 +msgid "Pass your turn" msgstr "" -#: utils/ncurses.cpp:463 +#: utils/ncurses.cpp:648 msgid "Enter the letters to change:" msgstr "" -#: utils/ncurses.cpp:472 +#: utils/ncurses.cpp:657 msgid "Cannot pass the turn" msgstr "" -#: utils/ncurses.cpp:482 -msgid " Set rack " +#: utils/ncurses.cpp:667 +msgid "Set rack" msgstr "" -#: utils/ncurses.cpp:483 +#: utils/ncurses.cpp:668 msgid "Enter the new letters:" msgstr "" -#: utils/ncurses.cpp:798 +#: utils/ncurses.cpp:677 +msgid "Cannot take these letters from the bag" +msgstr "" + +#: utils/ncurses.cpp:1071 msgid "Training mode" msgstr "" -#: utils/ncurses.cpp:800 +#: utils/ncurses.cpp:1073 msgid "Free game mode" msgstr "" -#: utils/ncurses.cpp:802 +#: utils/ncurses.cpp:1075 msgid "Duplicate mode" msgstr "" -#: utils/ncurses.cpp:805 +#: utils/ncurses.cpp:1078 msgid "Joker game" msgstr "" -#: utils/ncurses.cpp:806 +#: utils/ncurses.cpp:1079 msgid "[h for help]" msgstr "" + +#: wxwin/auxframes.cc:123 +msgid "Grid" +msgstr "" + +#: wxwin/auxframes.cc:203 +msgid "Search" +msgstr "" + +#: wxwin/auxframes.cc:234 +msgid "Check" +msgstr "" + +#: wxwin/auxframes.cc:239 +msgid "Word to check" +msgstr "" + +#: wxwin/auxframes.cc:257 wxwin/configdb.cc:203 wxwin/searchpanel.cc:107 +msgid "No dictionary" +msgstr "" + +#: wxwin/auxframes.cc:261 +msgid "exists" +msgstr "" + +#: wxwin/auxframes.cc:263 +msgid "doesn't exist" +msgstr "" + +#: wxwin/auxframes.cc:304 +msgid "Copy" +msgstr "" + +#: wxwin/auxframes.cc:348 wxwin/mainframe.cc:487 wxwin/mainframe.cc:507 +#: wxwin/mainframe.cc:538 +msgid "No on going game" +msgstr "" + +#: wxwin/auxframes.cc:363 wxwin/searchpanel.cc:119 +msgid "No result" +msgstr "" + +#: wxwin/auxframes.cc:409 +msgid "Rack: " +msgstr "" + +#: wxwin/auxframes.cc:525 wxwin/mainframe.cc:319 +msgid "Game history" +msgstr "" + +#: wxwin/auxframes.cc:557 wxwin/mainframe.cc:321 +msgid "Results" +msgstr "" + +#: wxwin/confdimdlg.cc:62 +msgid "left" +msgstr "" + +#: wxwin/confdimdlg.cc:63 +msgid "centered" +msgstr "" + +#: wxwin/confdimdlg.cc:64 +msgid "right" +msgstr "" + +#: wxwin/confdimdlg.cc:118 +msgid "Alignment" +msgstr "" + +#: wxwin/confdimdlg.cc:120 +msgid "Spacing" +msgstr "" + +#: wxwin/confdimdlg.cc:124 wxwin/confdimdlg.cc:135 +msgid "Font..." +msgstr "" + +#: wxwin/confdimdlg.cc:129 +msgid "Title column " +msgstr "" + +#: wxwin/confdimdlg.cc:140 +msgid "Text column " +msgstr "" + +#: wxwin/confdimdlg.cc:150 +msgid "Column heading" +msgstr "" + +#: wxwin/confdimdlg.cc:154 +msgid "Alignment of the column heading" +msgstr "" + +#: wxwin/confdimdlg.cc:158 +msgid "Spacing of the heading characters" +msgstr "" + +#: wxwin/confdimdlg.cc:167 +msgid "Inner dimension of the column (in mm)" +msgstr "" + +#: wxwin/confdimdlg.cc:176 +msgid "Alignment of the column text" +msgstr "" + +#: wxwin/confdimdlg.cc:180 +msgid "Characters spacing" +msgstr "" + +#: wxwin/confdimdlg.cc:231 +msgid "Title spc. top" +msgstr "" + +#: wxwin/confdimdlg.cc:233 +msgid "Text spc. top" +msgstr "" + +#: wxwin/confdimdlg.cc:235 +msgid "Text spc. left" +msgstr "" + +#: wxwin/confdimdlg.cc:252 +msgid "Title spc. bot." +msgstr "" + +#: wxwin/confdimdlg.cc:254 +msgid "Text spc. bot." +msgstr "" + +#: wxwin/confdimdlg.cc:256 +msgid "Text spc. right" +msgstr "" + +#: wxwin/confdimdlg.cc:286 +msgid "Printer" +msgstr "" + +#: wxwin/confdimdlg.cc:288 +msgid "Configure the printer" +msgstr "" + +#: wxwin/confdimdlg.cc:290 +msgid "Page" +msgstr "" + +#: wxwin/confdimdlg.cc:292 +msgid "Configure the dimensions of the page" +msgstr "" + +#: wxwin/confdimdlg.cc:298 +msgid "Cancel the last changes" +msgstr "" + +#: wxwin/confdimdlg.cc:304 wxwin/confsearch.cc:42 +msgid "Save the changes" +msgstr "" + +#: wxwin/configdb.cc:322 +msgid "Nb" +msgstr "" + +#: wxwin/configdb.cc:323 wxwin/mainframe.cc:174 +msgid "Rack" +msgstr "" + +#: wxwin/configdb.cc:324 +msgid "Solution" +msgstr "" + +#: wxwin/configdb.cc:325 wxwin/gfxresult.cc:69 +msgid "Pos" +msgstr "" + +#: wxwin/configdb.cc:326 wxwin/gfxresult.cc:70 +msgid "Pts" +msgstr "" + +#: wxwin/confsearch.cc:36 +msgid "Search on joker in 7+1 panel" +msgstr "" + +#: wxwin/confsearch.cc:37 +msgid "Check rack validity" +msgstr "" + +#: wxwin/confsearch.cc:40 +msgid "Cancel last changes" +msgstr "" + +#: wxwin/gfxresult.cc:67 +msgid "Word" +msgstr "" + +#: wxwin/gfxresult.cc:71 +msgid "Results of the search" +msgstr "" + +#: wxwin/mainframe.cc:205 +msgid " Rack " +msgstr "" + +#: wxwin/mainframe.cc:206 +msgid " Complement " +msgstr "" + +#: wxwin/mainframe.cc:207 +msgid " Search " +msgstr "" + +#: wxwin/mainframe.cc:208 +msgid " Back " +msgstr "" + +#: wxwin/mainframe.cc:209 +msgid " Play " +msgstr "" + +#: wxwin/mainframe.cc:211 +msgid "Random rack" +msgstr "" + +#: wxwin/mainframe.cc:212 +msgid "Random complement of the rack" +msgstr "" + +#: wxwin/mainframe.cc:213 +msgid "Search with the current rack" +msgstr "" + +#: wxwin/mainframe.cc:214 +msgid "Go back one turn" +msgstr "" + +#: wxwin/mainframe.cc:215 +msgid "Play the selected word" +msgstr "" + +#: wxwin/mainframe.cc:266 +msgid "&New game\tctrl+n" +msgstr "" + +#: wxwin/mainframe.cc:266 +msgid "Start a new game" +msgstr "" + +#: wxwin/mainframe.cc:267 +msgid "New &joker game\tctrl+j" +msgstr "" + +#: wxwin/mainframe.cc:267 +msgid "Start a new joker game" +msgstr "" + +#: wxwin/mainframe.cc:269 +msgid "&Load...\tctrl+l" +msgstr "" + +#: wxwin/mainframe.cc:270 +msgid "&Save as...\tctrl+s" +msgstr "" + +#: wxwin/mainframe.cc:270 +msgid "Save the current game" +msgstr "" + +#: wxwin/mainframe.cc:272 +msgid "&Print...\tctrl+p" +msgstr "" + +#: wxwin/mainframe.cc:272 +msgid "Print this game" +msgstr "" + +#: wxwin/mainframe.cc:273 +msgid "Print pre&view..." +msgstr "" + +#: wxwin/mainframe.cc:273 +msgid "Print preview of the game" +msgstr "" + +#: wxwin/mainframe.cc:275 +msgid "Print in PostS&cript..." +msgstr "" + +#: wxwin/mainframe.cc:275 +msgid "Print in a PostScript file" +msgstr "" + +#: wxwin/mainframe.cc:278 +msgid "&Quit" +msgstr "" + +#: wxwin/mainframe.cc:278 +msgid "Quit Eliot" +msgstr "" + +#: wxwin/mainframe.cc:281 +msgid "&Dictionary..." +msgstr "" + +#: wxwin/mainframe.cc:281 wxwin/mainframe.cc:589 +msgid "Choose a dictionary" +msgstr "" + +#: wxwin/mainframe.cc:282 +msgid "&Search..." +msgstr "" + +#: wxwin/mainframe.cc:282 +msgid "Search options" +msgstr "" + +#: wxwin/mainframe.cc:285 +msgid "&Background..." +msgstr "" + +#: wxwin/mainframe.cc:285 +msgid "Background color" +msgstr "" + +#: wxwin/mainframe.cc:286 +msgid "L&ines..." +msgstr "" + +#: wxwin/mainframe.cc:286 +msgid "Color of the lines" +msgstr "" + +#: wxwin/mainframe.cc:288 +msgid "&Played letters..." +msgstr "" + +#: wxwin/mainframe.cc:288 +msgid "Color of the letters played on the board" +msgstr "" + +#: wxwin/mainframe.cc:289 +msgid "&Temporary letters..." +msgstr "" + +#: wxwin/mainframe.cc:289 +msgid "Color of the letters of the temporary word" +msgstr "" + +#: wxwin/mainframe.cc:290 +msgid "B&ackground of played letters..." +msgstr "" + +#: wxwin/mainframe.cc:290 +msgid "Background color of the letters played on the board" +msgstr "" + +#: wxwin/mainframe.cc:291 +msgid "Ba&ckground of temporary letters..." +msgstr "" + +#: wxwin/mainframe.cc:291 +msgid "Background color of the temporary letters on the board" +msgstr "" + +#: wxwin/mainframe.cc:293 +msgid "Double &letter..." +msgstr "" + +#: wxwin/mainframe.cc:293 +msgid "Color of the \"double letter\" squares" +msgstr "" + +#: wxwin/mainframe.cc:294 +msgid "Triple l&etter..." +msgstr "" + +#: wxwin/mainframe.cc:294 +msgid "Color of the \"triple letter\" squares" +msgstr "" + +#: wxwin/mainframe.cc:295 +msgid "Double &word..." +msgstr "" + +#: wxwin/mainframe.cc:295 +msgid "Color of the \"double word\" squares" +msgstr "" + +#: wxwin/mainframe.cc:296 +msgid "Triple w&ord..." +msgstr "" + +#: wxwin/mainframe.cc:296 +msgid "Color of the \"triple word\" squares" +msgstr "" + +#: wxwin/mainframe.cc:298 +msgid "&Default colors" +msgstr "" + +#: wxwin/mainframe.cc:298 +msgid "Restore the default colors" +msgstr "" + +#: wxwin/mainframe.cc:301 +msgid "&Search letters..." +msgstr "" + +#: wxwin/mainframe.cc:301 +msgid "Font for the search" +msgstr "" + +#: wxwin/mainframe.cc:304 wxwin/mainframe.cc:328 +msgid "&Game" +msgstr "" + +#: wxwin/mainframe.cc:304 +msgid "Configuration of the game" +msgstr "" + +#: wxwin/mainframe.cc:305 +msgid "&Fonts" +msgstr "" + +#: wxwin/mainframe.cc:305 +msgid "Configuration of the fonts" +msgstr "" + +#: wxwin/mainframe.cc:306 +msgid "&Colors" +msgstr "" + +#: wxwin/mainframe.cc:306 +msgid "Configuration of the colors" +msgstr "" + +#: wxwin/mainframe.cc:307 +msgid "&Printing..." +msgstr "" + +#: wxwin/mainframe.cc:307 +msgid "Configuration of the printing parameters" +msgstr "" + +#: wxwin/mainframe.cc:310 +msgid "&Board" +msgstr "" + +#: wxwin/mainframe.cc:310 +msgid "Game board" +msgstr "" + +#: wxwin/mainframe.cc:311 +msgid "Ba&g" +msgstr "" + +#: wxwin/mainframe.cc:311 +msgid "Remaining letters in the bag" +msgstr "" + +#: wxwin/mainframe.cc:312 +msgid "&Check" +msgstr "" + +#: wxwin/mainframe.cc:312 +msgid "Check a word in the dictionary" +msgstr "" + +#: wxwin/mainframe.cc:313 +msgid "&Search" +msgstr "" + +#: wxwin/mainframe.cc:313 +msgid "Search in the dictionary" +msgstr "" + +#: wxwin/mainframe.cc:315 +msgid "&Rack + 1" +msgstr "" + +#: wxwin/mainframe.cc:315 +msgid "Letters of the rack plus one" +msgstr "" + +#: wxwin/mainframe.cc:316 +msgid "R&accords" +msgstr "" + +#: wxwin/mainframe.cc:316 +msgid "Raccords on a word of the search" +msgstr "" + +#: wxwin/mainframe.cc:317 +msgid "&Benjamins" +msgstr "" + +#: wxwin/mainframe.cc:317 +msgid "Benjamins on a word of the search" +msgstr "" + +#: wxwin/mainframe.cc:319 +msgid "Game &history" +msgstr "" + +#: wxwin/mainframe.cc:321 +msgid "R&esults" +msgstr "" + +#: wxwin/mainframe.cc:325 +msgid "&About..." +msgstr "" + +#: wxwin/mainframe.cc:325 wxwin/mainframe.cc:718 +msgid "About Eliot" +msgstr "" + +#: wxwin/mainframe.cc:329 +msgid "&Settings" +msgstr "" + +#: wxwin/mainframe.cc:330 +msgid "&Windows" +msgstr "" + +#: wxwin/mainframe.cc:331 +msgid "&Help" +msgstr "" + +#: wxwin/mainframe.cc:355 wxwin/mainframe.cc:393 +msgid "No dictionary selected" +msgstr "" + +#: wxwin/mainframe.cc:355 wxwin/mainframe.cc:393 wxwin/mainframe.cc:487 +#: wxwin/mainframe.cc:507 wxwin/mainframe.cc:538 +msgid "Eliot: error" +msgstr "" + +#: wxwin/mainframe.cc:413 +msgid "Cannot open " +msgstr "" + +#: wxwin/mainframe.cc:425 wxwin/mainframe.cc:434 +msgid "Error while loading the game" +msgstr "" + +#: wxwin/mainframe.cc:426 +msgid "Invalid game" +msgstr "" + +#: wxwin/mainframe.cc:435 +msgid "The game is empty" +msgstr "" + +#: wxwin/mainframe.cc:467 +msgid "Cannot create " +msgstr "" + +#: wxwin/mainframe.cc:496 wxwin/mainframe.cc:560 +msgid "Printing not done" +msgstr "" + +#: wxwin/mainframe.cc:496 wxwin/mainframe.cc:524 +msgid "Printing" +msgstr "" + +#: wxwin/mainframe.cc:519 +msgid "Print preview problem.\n" +msgstr "" + +#: wxwin/mainframe.cc:520 +msgid "The printer may not be correctly initialized" +msgstr "" + +#: wxwin/mainframe.cc:521 +msgid "Print preview" +msgstr "" + +#: wxwin/mainframe.cc:542 +msgid "Print to a PostScript file" +msgstr "" + +#: wxwin/mainframe.cc:561 wxwin/mainframe.cc:567 +msgid "PostScript printing" +msgstr "" + +#: wxwin/mainframe.cc:566 +msgid "Cannot initialize PostScript printer" +msgstr "" + +#: wxwin/mainframe.cc:714 +msgid "" +"This program is free software; you can redistribute it and/or modify it " +"under the terms of the GNU General Public License as published by the Free " +"Software Foundation; either version 2 of the License, or (at your option) " +"any later version." +msgstr "" + +#: wxwin/mainframe.cc:912 +msgid "turn:" +msgstr "" + +#: wxwin/mainframe.cc:913 +msgid "points:" +msgstr "" + +#: wxwin/mainframe.cc:948 +msgid "" +"The bag doesn't contain enough letters\n" +"for a new rack." +msgstr "" + +#: wxwin/mainframe.cc:949 wxwin/mainframe.cc:953 wxwin/mainframe.cc:957 +msgid "Rack validation" +msgstr "" + +#: wxwin/mainframe.cc:952 +msgid "The rack must contain at least 2 consonants and 2 vowels." +msgstr "" + +#: wxwin/mainframe.cc:956 +msgid "The rack contains invalid letters for the current dictionary" +msgstr "" + +#: wxwin/mainframe.cc:960 +msgid "The rack has been modified manually" +msgstr "" + +#: wxwin/searchpanel.cc:146 wxwin/searchpanel.cc:190 +#, c-format +msgid "The search is limited to %d letters" +msgstr "" + +#: wxwin/searchpanel.cc:301 +msgid "Minimum length" +msgstr "" + +#: wxwin/searchpanel.cc:303 +msgid "Maximum length" +msgstr "" + +#: wxwin/searchpanel.cc:366 +msgid "Cross words" +msgstr "" + +#: wxwin/searchpanel.cc:367 +msgid "Plus 1" +msgstr "" + +#: wxwin/searchpanel.cc:368 +msgid "Regular expressions" +msgstr "" diff --git a/po/fr.po b/po/fr.po index 1f67c4e..e125551 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,271 +8,1265 @@ msgid "" msgstr "" "Project-Id-Version: eliot 1.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2005-04-03 00:04+0200\n" +"POT-Creation-Date: 2007-12-23 23:08+0100\n" "PO-Revision-Date: 2005-02-06 20:03+0100\n" "Last-Translator: Olivier Teuliere \n" "Language-Team: French \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: utils/ncurses.cpp:184 -msgid " Scores " -msgstr " Scores " +#: dic/header.cpp:317 dic/header.cpp:318 +msgid "Unknown (old format)" +msgstr "Inconnu (vieux format)" -#: utils/ncurses.cpp:190 +#: dic/header.cpp:492 +#, c-format +msgid "dictionary name: %s\n" +msgstr "Nom du dictionnaire : %s\n" + +#: dic/header.cpp:497 +#, c-format +msgid "compressed on: %s\n" +msgstr "Compressé le : %s\n" + +#: dic/header.cpp:501 +#, c-format +msgid "compressed on: Unknown date (old format)\n" +msgstr "Compressé le : Date inconnue (vieux format)\n" + +#: dic/header.cpp:503 +#, c-format +msgid "compressed using a binary compiled by: %s\n" +msgstr "Compressé avec un binaire compilé par : %s\n" + +#: dic/header.cpp:504 +#, c-format +msgid "dictionary type: %s\n" +msgstr "Type de dictionnaire : %s\n" + +#: dic/header.cpp:505 +#, c-format +msgid "letters: %s\n" +msgstr "Lettres : %s\n" + +#: dic/header.cpp:506 +#, c-format +msgid "number of letters: %d\n" +msgstr "Nombre de lettres : %d\n" + +#: dic/header.cpp:507 +#, c-format +msgid "number of words: %d\n" +msgstr "Nombre de mots : %d\n" + +#: dic/header.cpp:508 +#, c-format +msgid "header size: %u bytes\n" +msgstr "Taille du header : %u octets\n" + +#: dic/header.cpp:510 +#, c-format +msgid "root: %d (edge)\n" +msgstr "Racine : %d (arcs)\n" + +#: dic/header.cpp:511 +#, c-format +msgid "nodes: %d used + %d saved\n" +msgstr "Noeuds : %d utilisés + %d évités\n" + +#: dic/header.cpp:512 +#, c-format +msgid "edges: %d used + %d saved\n" +msgstr "Arcs : %d utilisés + %d évités\n" + +#: dic/header.cpp:514 +#, c-format +msgid "letter | points | frequency | vowel | consonant\n" +msgstr "lettre | points | frequence | voye. | consonne\n" + +#: dic/compdic.cpp:392 +msgid "Mandatory options:" +msgstr "Options obligatoires :" + +#: dic/compdic.cpp:393 +msgid " -d, --dicname Set the dictionary name and version" +msgstr " -d, --dicname Choisir le nom et la version du dictionnaire" + +#: dic/compdic.cpp:394 +msgid "" +" -l, --letters Path to the file containing the letters (see below)" +msgstr "" +" -l, --letters Chemin vers un fichier contenant les lettres (voir " +"ci-dessous)" + +#: dic/compdic.cpp:395 +msgid "" +" -i, --input Path to the uncompressed dictionary file (encoded " +"in UTF-8)" +msgstr "" +" -i, --input Chemin vers le fichier de dictionnaire non " +"compressé (encodé en UTF-8)" + +#: dic/compdic.cpp:396 +msgid "" +" -o, --output , Navigate in a box line by line" -msgstr " , Naviguer dans une boîte ligne par ligne" +msgstr " , Naviguer dans une boîte ligne par ligne" -#: utils/ncurses.cpp:330 +#: utils/ncurses.cpp:450 msgid " , Navigate in a box page by page" -msgstr " , Naviguer dans une boîte page par page" +msgstr " , Naviguer dans une boîte page par page" -#: utils/ncurses.cpp:331 +#: utils/ncurses.cpp:451 msgid " Ctrl-l Refresh the screen" -msgstr " Ctrl-l Rafraîchir l'écran" +msgstr " Ctrl-l Rafraîchir l'écran" -#: utils/ncurses.cpp:339 -msgid " Play a word " -msgstr " Jouer un mot " +#: utils/ncurses.cpp:462 wxwin/auxframes.cc:148 +msgid "Bag" +msgstr "Sac" + +#: utils/ncurses.cpp:469 +msgid " LETTER | POINTS | FREQUENCY | REMAINING" +msgstr " LETTRE | POINTS | FREQUENCE | RESTANT" + +#: utils/ncurses.cpp:522 +msgid "Play a word" +msgstr "Jouer un mot" #. TRANSLATORS: Align the : when translating "Played word:" and #. "Coordinates:". For example: #. Pl. word : #. Coordinates: -#: utils/ncurses.cpp:340 utils/ncurses.cpp:348 +#: utils/ncurses.cpp:523 utils/ncurses.cpp:531 msgid "Played word:" -msgstr "Mot joué :" +msgstr "Mot joué :" -#: utils/ncurses.cpp:341 utils/ncurses.cpp:349 +#: utils/ncurses.cpp:524 utils/ncurses.cpp:532 msgid "Coordinates:" -msgstr "Coordonnées :" +msgstr "Coordonnées :" -#: utils/ncurses.cpp:363 +#: utils/ncurses.cpp:546 msgid "Incorrect or misplaced word" -msgstr "Mot incorrect ou mal placé" +msgstr "Mot incorrect ou mal placé" -#: utils/ncurses.cpp:373 -msgid " Dictionary " -msgstr " Dictionnaire " +#: utils/ncurses.cpp:556 +msgid "Dictionary" +msgstr "Dictionnaire" -#: utils/ncurses.cpp:374 +#: utils/ncurses.cpp:557 msgid "Enter the word to check:" -msgstr "Entrer le mot à vérifier:" +msgstr "Entrer le mot à vérifier:" -#: utils/ncurses.cpp:383 +#: utils/ncurses.cpp:566 #, c-format -msgid "The word '%s' exists" -msgstr "Le mot '%s' existe" +msgid "The word '%ls' exists" +msgstr "Le mot '%ls' existe" -#: utils/ncurses.cpp:385 +#: utils/ncurses.cpp:568 #, c-format -msgid "The word '%s' does not exist" -msgstr "Le mot '%s' n'existe pas" +msgid "The word '%ls' does not exist" +msgstr "Le mot '%ls' n'existe pas" -#: utils/ncurses.cpp:395 -msgid " Save the game " -msgstr " Sauver la partie " +#: utils/ncurses.cpp:578 wxwin/mainframe.cc:460 wxwin/mainframe.cc:468 +msgid "Save the game" +msgstr "Sauvegarder la partie" -#: utils/ncurses.cpp:396 utils/ncurses.cpp:425 +#: utils/ncurses.cpp:579 utils/ncurses.cpp:609 msgid "Enter the file name:" msgstr "Entrer le nom du fichier :" -#: utils/ncurses.cpp:406 +#: utils/ncurses.cpp:589 #, c-format -msgid "Cannot open file %s for writing" -msgstr "Impossible d'ouvrir le fichier %s en écriture" +msgid "Cannot open file %ls for writing" +msgstr "Impossible d'ouvrir le fichier %ls en écriture" -#: utils/ncurses.cpp:413 +#: utils/ncurses.cpp:597 #, c-format -msgid "Game saved in %s" -msgstr "Partie sauvée dans %s" +msgid "Game saved in '%ls'" +msgstr "Partie sauvée dans '%ls'" -#: utils/ncurses.cpp:424 -msgid " Load a game " -msgstr " Charger une partie " +#: utils/ncurses.cpp:608 wxwin/mainframe.cc:269 wxwin/mainframe.cc:390 +#: wxwin/mainframe.cc:414 +msgid "Load a game" +msgstr "Charger une partie" -#: utils/ncurses.cpp:435 +#: utils/ncurses.cpp:619 #, c-format -msgid "Cannot open file %s for reading" -msgstr "Impossible d'ouvrir le fichier %s en lecture" +msgid "Cannot open file '%ls' for reading" +msgstr "Impossible d'ouvrir le fichier '%ls' en lecture" -#: utils/ncurses.cpp:443 +#: utils/ncurses.cpp:627 #, c-format msgid "Invalid saved game" -msgstr "Partie sauvée invalide" +msgstr "Partie sauvée invalide" -#: utils/ncurses.cpp:447 +#: utils/ncurses.cpp:632 #, c-format msgid "Game loaded" -msgstr "Partie chargée" +msgstr "Partie chargée" -#: utils/ncurses.cpp:462 -msgid " Pass your turn " -msgstr " Passer son tour " +#: utils/ncurses.cpp:647 +msgid "Pass your turn" +msgstr "Passer son tour" -#: utils/ncurses.cpp:463 +#: utils/ncurses.cpp:648 msgid "Enter the letters to change:" -msgstr "Entrer les lettres à changer:" +msgstr "Entrer les lettres à changer:" -#: utils/ncurses.cpp:472 +#: utils/ncurses.cpp:657 msgid "Cannot pass the turn" msgstr "Impossible de passer le tour" -#: utils/ncurses.cpp:482 -msgid " Set rack " -msgstr " Choix du tirage " +#: utils/ncurses.cpp:667 +msgid "Set rack" +msgstr "Choix du tirage" -#: utils/ncurses.cpp:483 +#: utils/ncurses.cpp:668 msgid "Enter the new letters:" msgstr "Entrer les nouvelles lettres:" -#: utils/ncurses.cpp:798 -msgid "Training mode" -msgstr "Mode entraînement" +#: utils/ncurses.cpp:677 +msgid "Cannot take these letters from the bag" +msgstr "Impossible de retirer ces lettres du sac" -#: utils/ncurses.cpp:800 +#: utils/ncurses.cpp:1071 +msgid "Training mode" +msgstr "Mode entraînement" + +#: utils/ncurses.cpp:1073 msgid "Free game mode" msgstr "Mode partie libre" -#: utils/ncurses.cpp:802 +#: utils/ncurses.cpp:1075 msgid "Duplicate mode" msgstr "Mode duplicate" -#: utils/ncurses.cpp:805 +#: utils/ncurses.cpp:1078 msgid "Joker game" msgstr "Partie joker" -#: utils/ncurses.cpp:806 +#: utils/ncurses.cpp:1079 msgid "[h for help]" msgstr "[h pour l'aide]" +#: wxwin/auxframes.cc:123 +msgid "Grid" +msgstr "Grille" + +#: wxwin/auxframes.cc:203 +msgid "Search" +msgstr "Recherche" + +#: wxwin/auxframes.cc:234 +msgid "Check" +msgstr "Vérification" + +#: wxwin/auxframes.cc:239 +msgid "Word to check" +msgstr "Mot à vérifier" + +#: wxwin/auxframes.cc:257 wxwin/configdb.cc:203 wxwin/searchpanel.cc:107 +msgid "No dictionary" +msgstr "Pas de dictionnaire" + +#: wxwin/auxframes.cc:261 +msgid "exists" +msgstr "existe" + +#: wxwin/auxframes.cc:263 +msgid "doesn't exist" +msgstr "n'existe pas" + +#: wxwin/auxframes.cc:304 +msgid "Copy" +msgstr "Copier" + +#: wxwin/auxframes.cc:348 wxwin/mainframe.cc:487 wxwin/mainframe.cc:507 +#: wxwin/mainframe.cc:538 +msgid "No on going game" +msgstr "Pas de partie en cours" + +#: wxwin/auxframes.cc:363 wxwin/searchpanel.cc:119 +msgid "No result" +msgstr "Pas de résultat" + +#: wxwin/auxframes.cc:409 +msgid "Rack: " +msgstr "Tirage :" + +#: wxwin/auxframes.cc:525 wxwin/mainframe.cc:319 +msgid "Game history" +msgstr "Historique de la partie" + +#: wxwin/auxframes.cc:557 wxwin/mainframe.cc:321 +msgid "Results" +msgstr "Résultats" + +#: wxwin/confdimdlg.cc:62 +msgid "left" +msgstr "gauche" + +#: wxwin/confdimdlg.cc:63 +msgid "centered" +msgstr "centré" + +#: wxwin/confdimdlg.cc:64 +msgid "right" +msgstr "droite" + +#: wxwin/confdimdlg.cc:118 +msgid "Alignment" +msgstr "Justification" + +#: wxwin/confdimdlg.cc:120 +msgid "Spacing" +msgstr "Espacement" + +#: wxwin/confdimdlg.cc:124 wxwin/confdimdlg.cc:135 +msgid "Font..." +msgstr "Police..." + +#: wxwin/confdimdlg.cc:129 +msgid "Title column " +msgstr "titre colomne " + +#: wxwin/confdimdlg.cc:140 +msgid "Text column " +msgstr "Texte colomne " + +#: wxwin/confdimdlg.cc:150 +msgid "Column heading" +msgstr "Titre de la colomne" + +#: wxwin/confdimdlg.cc:154 +msgid "Alignment of the column heading" +msgstr "Justification du titre de la colomne" + +#: wxwin/confdimdlg.cc:158 +msgid "Spacing of the heading characters" +msgstr "Espacement des caractères du titre" + +#: wxwin/confdimdlg.cc:167 +msgid "Inner dimension of the column (in mm)" +msgstr "Dimension interne de la colomne (en mm)" + +#: wxwin/confdimdlg.cc:176 +msgid "Alignment of the column text" +msgstr "Justification du texte de la colomne" + +#: wxwin/confdimdlg.cc:180 +msgid "Characters spacing" +msgstr "Espacement des caractères" + +#: wxwin/confdimdlg.cc:231 +msgid "Title spc. top" +msgstr "Titre esp. haut" + +#: wxwin/confdimdlg.cc:233 +msgid "Text spc. top" +msgstr "Texte esp. haut" + +#: wxwin/confdimdlg.cc:235 +msgid "Text spc. left" +msgstr "Texte esp. gauche" + +#: wxwin/confdimdlg.cc:252 +msgid "Title spc. bot." +msgstr "Titre esp. bas" + +#: wxwin/confdimdlg.cc:254 +msgid "Text spc. bot." +msgstr "Texte esp. bas" + +#: wxwin/confdimdlg.cc:256 +msgid "Text spc. right" +msgstr "Text esp. droite" + +#: wxwin/confdimdlg.cc:286 +msgid "Printer" +msgstr "Imprimante" + +#: wxwin/confdimdlg.cc:288 +msgid "Configure the printer" +msgstr "Configuration de l'imprimante" + +#: wxwin/confdimdlg.cc:290 +msgid "Page" +msgstr "Page" + +#: wxwin/confdimdlg.cc:292 +msgid "Configure the dimensions of the page" +msgstr "Configuration des dimensions de la page" + +#: wxwin/confdimdlg.cc:298 +msgid "Cancel the last changes" +msgstr "Annuler les derniers changements" + +#: wxwin/confdimdlg.cc:304 wxwin/confsearch.cc:42 +msgid "Save the changes" +msgstr "Sauvegarder les changements" + +#: wxwin/configdb.cc:322 +msgid "Nb" +msgstr "Num" + +#: wxwin/configdb.cc:323 wxwin/mainframe.cc:174 +msgid "Rack" +msgstr "Tirage" + +#: wxwin/configdb.cc:324 +msgid "Solution" +msgstr "Solution" + +#: wxwin/configdb.cc:325 wxwin/gfxresult.cc:69 +msgid "Pos" +msgstr "Pos" + +#: wxwin/configdb.cc:326 wxwin/gfxresult.cc:70 +msgid "Pts" +msgstr "Pts" + +#: wxwin/confsearch.cc:36 +msgid "Search on joker in 7+1 panel" +msgstr "Rechercher sur joker dans la fenêtre 7+1" + +#: wxwin/confsearch.cc:37 +msgid "Check rack validity" +msgstr "Vérifier la validité du tirage" + +#: wxwin/confsearch.cc:40 +msgid "Cancel last changes" +msgstr "Annuler les derniers changements" + +#: wxwin/gfxresult.cc:67 +msgid "Word" +msgstr "Mot" + +#: wxwin/gfxresult.cc:71 +msgid "Results of the search" +msgstr "Résultats de la recherche" + +#: wxwin/mainframe.cc:205 +msgid " Rack " +msgstr " Tirage " + +#: wxwin/mainframe.cc:206 +msgid " Complement " +msgstr " Complément " + +#: wxwin/mainframe.cc:207 +msgid " Search " +msgstr " Rechercher " + +#: wxwin/mainframe.cc:208 +msgid " Back " +msgstr " Arrière " + +#: wxwin/mainframe.cc:209 +msgid " Play " +msgstr " Jouer " + +#: wxwin/mainframe.cc:211 +msgid "Random rack" +msgstr "Tirage aléatoire" + +#: wxwin/mainframe.cc:212 +msgid "Random complement of the rack" +msgstr "Complément aléatoire du tirage" + +#: wxwin/mainframe.cc:213 +msgid "Search with the current rack" +msgstr "Recherche sur le tirage courant" + +#: wxwin/mainframe.cc:214 +msgid "Go back one turn" +msgstr "Revenir un coup en arrière" + +#: wxwin/mainframe.cc:215 +msgid "Play the selected word" +msgstr "Jouer le mot selectionné" + +#: wxwin/mainframe.cc:266 +msgid "&New game\tctrl+n" +msgstr "&Nouvelle partie\tctrl+n" + +#: wxwin/mainframe.cc:266 +msgid "Start a new game" +msgstr "Démarrer une nouvelle partie" + +#: wxwin/mainframe.cc:267 +msgid "New &joker game\tctrl+j" +msgstr "Nouvelle partie &joker\tctrl+j" + +#: wxwin/mainframe.cc:267 +msgid "Start a new joker game" +msgstr "Démarrer une nouvelle partie joker" + +#: wxwin/mainframe.cc:269 +msgid "&Load...\tctrl+l" +msgstr "&Charger...\tctrl+l" + +#: wxwin/mainframe.cc:270 +msgid "&Save as...\tctrl+s" +msgstr "&Enregistrer sous...\tctrl+s" + +#: wxwin/mainframe.cc:270 +msgid "Save the current game" +msgstr "Sauvegarder la partie en cours" + +#: wxwin/mainframe.cc:272 +msgid "&Print...\tctrl+p" +msgstr "&Imprimer...\tctrl+p" + +#: wxwin/mainframe.cc:272 +msgid "Print this game" +msgstr "Imprimer la partie en cours" + +#: wxwin/mainframe.cc:273 +msgid "Print pre&view..." +msgstr "&Aperçu avant impression..." + +#: wxwin/mainframe.cc:273 +msgid "Print preview of the game" +msgstr "Aperçu avant impression de la partie" + +#: wxwin/mainframe.cc:275 +msgid "Print in PostS&cript..." +msgstr "Imprimer au format &PostScript..." + +#: wxwin/mainframe.cc:275 +msgid "Print in a PostScript file" +msgstr "Imprimer dans un fichier PostScript" + +#: wxwin/mainframe.cc:278 +msgid "&Quit" +msgstr "&Quitter" + +#: wxwin/mainframe.cc:278 +msgid "Quit Eliot" +msgstr "Quitter Eliot" + +#: wxwin/mainframe.cc:281 +msgid "&Dictionary..." +msgstr "&Dictionnaire..." + +#: wxwin/mainframe.cc:281 wxwin/mainframe.cc:589 +msgid "Choose a dictionary" +msgstr "Choisir un dictionnaire" + +#: wxwin/mainframe.cc:282 +msgid "&Search..." +msgstr "&Recherche..." + +#: wxwin/mainframe.cc:282 +msgid "Search options" +msgstr "Options de recherche" + +#: wxwin/mainframe.cc:285 +msgid "&Background..." +msgstr "&Fond..." + +#: wxwin/mainframe.cc:285 +msgid "Background color" +msgstr "Couleur de fond" + +#: wxwin/mainframe.cc:286 +msgid "L&ines..." +msgstr "L&ignes..." + +#: wxwin/mainframe.cc:286 +msgid "Color of the lines" +msgstr "Couleur des lignes" + +#: wxwin/mainframe.cc:288 +msgid "&Played letters..." +msgstr "Lettres &jouées..." + +#: wxwin/mainframe.cc:288 +msgid "Color of the letters played on the board" +msgstr "Couleur des lettres jouées sur la grille" + +#: wxwin/mainframe.cc:289 +msgid "&Temporary letters..." +msgstr "Lettres &temporaires..." + +#: wxwin/mainframe.cc:289 +msgid "Color of the letters of the temporary word" +msgstr "Couleur des lettres du mot temporaire" + +#: wxwin/mainframe.cc:290 +msgid "B&ackground of played letters..." +msgstr "Fo&nd des lettres jouées..." + +#: wxwin/mainframe.cc:290 +msgid "Background color of the letters played on the board" +msgstr "Couleur de fond des lettres jouées sur la grille" + +#: wxwin/mainframe.cc:291 +msgid "Ba&ckground of temporary letters..." +msgstr "Fon&d des lettres temporaires..." + +#: wxwin/mainframe.cc:291 +msgid "Background color of the temporary letters on the board" +msgstr "Couleur de fond des lettres temporaires sur la grille" + +#: wxwin/mainframe.cc:293 +msgid "Double &letter..." +msgstr "&Lettre compte double..." + +#: wxwin/mainframe.cc:293 +msgid "Color of the \"double letter\" squares" +msgstr "Couleur des cases \"mot compte double\"" + +#: wxwin/mainframe.cc:294 +msgid "Triple l&etter..." +msgstr "L&ettre compte triple..." + +#: wxwin/mainframe.cc:294 +msgid "Color of the \"triple letter\" squares" +msgstr "Couleur des cases \"mot compte triple\"" + +#: wxwin/mainframe.cc:295 +msgid "Double &word..." +msgstr "&Mot compte double..." + +#: wxwin/mainframe.cc:295 +msgid "Color of the \"double word\" squares" +msgstr "Couleur des cases \"mot compte double\"" + +#: wxwin/mainframe.cc:296 +msgid "Triple w&ord..." +msgstr "M&ot compte triple..." + +#: wxwin/mainframe.cc:296 +msgid "Color of the \"triple word\" squares" +msgstr "Couleur des cases \"mot compte triple\"" + +#: wxwin/mainframe.cc:298 +msgid "&Default colors" +msgstr "&Couleurs d'origine" + +#: wxwin/mainframe.cc:298 +msgid "Restore the default colors" +msgstr "Restaurer les couleurs d'origine" + +#: wxwin/mainframe.cc:301 +msgid "&Search letters..." +msgstr "&Lettres de recherche..." + +#: wxwin/mainframe.cc:301 +msgid "Font for the search" +msgstr "Police de caractères pour la recherche" + +#: wxwin/mainframe.cc:304 wxwin/mainframe.cc:328 +msgid "&Game" +msgstr "&Partie" + +#: wxwin/mainframe.cc:304 +msgid "Configuration of the game" +msgstr "Configuration de la partie" + +#: wxwin/mainframe.cc:305 +msgid "&Fonts" +msgstr "P&olices de caractères" + +#: wxwin/mainframe.cc:305 +msgid "Configuration of the fonts" +msgstr "Configuration des polices de caractères" + +#: wxwin/mainframe.cc:306 +msgid "&Colors" +msgstr "&Couleurs" + +#: wxwin/mainframe.cc:306 +msgid "Configuration of the colors" +msgstr "Configuration des couleurs" + +#: wxwin/mainframe.cc:307 +msgid "&Printing..." +msgstr "&Impression..." + +#: wxwin/mainframe.cc:307 +msgid "Configuration of the printing parameters" +msgstr "Configuration des paramètres d'impression" + +#: wxwin/mainframe.cc:310 +msgid "&Board" +msgstr "&Grille" + +#: wxwin/mainframe.cc:310 +msgid "Game board" +msgstr "Plateau de jeu" + +#: wxwin/mainframe.cc:311 +msgid "Ba&g" +msgstr "&Sac" + +#: wxwin/mainframe.cc:311 +msgid "Remaining letters in the bag" +msgstr "Lettres restantes dans le sac" + +#: wxwin/mainframe.cc:312 +msgid "&Check" +msgstr "&Vérification" + +#: wxwin/mainframe.cc:312 +msgid "Check a word in the dictionary" +msgstr "Vérifier l'existence d'un mot dans le dictionnaire" + +#: wxwin/mainframe.cc:313 +msgid "&Search" +msgstr "&Recherche" + +#: wxwin/mainframe.cc:313 +msgid "Search in the dictionary" +msgstr "Recherche dans le dictionnaire" + +#: wxwin/mainframe.cc:315 +msgid "&Rack + 1" +msgstr "&Tirage + 1" + +#: wxwin/mainframe.cc:315 +msgid "Letters of the rack plus one" +msgstr "Lettres du tirage plus une" + +#: wxwin/mainframe.cc:316 +msgid "R&accords" +msgstr "R&accords" + +#: wxwin/mainframe.cc:316 +msgid "Raccords on a word of the search" +msgstr "Raccords sur un mot de la recherche" + +#: wxwin/mainframe.cc:317 +msgid "&Benjamins" +msgstr "&Benjamins" + +#: wxwin/mainframe.cc:317 +msgid "Benjamins on a word of the search" +msgstr "Benjamins sur un mot de la recherche" + +#: wxwin/mainframe.cc:319 +msgid "Game &history" +msgstr "&Historique de la partie" + +#: wxwin/mainframe.cc:321 +msgid "R&esults" +msgstr "Ré&sultats" + +#: wxwin/mainframe.cc:325 +msgid "&About..." +msgstr "À &propos..." + +#: wxwin/mainframe.cc:325 wxwin/mainframe.cc:718 +msgid "About Eliot" +msgstr "À propos d'Eliot" + +#: wxwin/mainframe.cc:329 +msgid "&Settings" +msgstr "&Paramètres" + +#: wxwin/mainframe.cc:330 +msgid "&Windows" +msgstr "&Fenêtres" + +#: wxwin/mainframe.cc:331 +msgid "&Help" +msgstr "&Aide" + +#: wxwin/mainframe.cc:355 wxwin/mainframe.cc:393 +msgid "No dictionary selected" +msgstr "Pas de dictionnaire sélectionné" + +#: wxwin/mainframe.cc:355 wxwin/mainframe.cc:393 wxwin/mainframe.cc:487 +#: wxwin/mainframe.cc:507 wxwin/mainframe.cc:538 +msgid "Eliot: error" +msgstr "Eliot : erreur" + +#: wxwin/mainframe.cc:413 +msgid "Cannot open " +msgstr "Impossible d'ouvrir " + +#: wxwin/mainframe.cc:425 wxwin/mainframe.cc:434 +msgid "Error while loading the game" +msgstr "Erreur pendant le chargement de la partie" + +#: wxwin/mainframe.cc:426 +msgid "Invalid game" +msgstr "Partie invalide" + +#: wxwin/mainframe.cc:435 +msgid "The game is empty" +msgstr "La partie est vide" + +#: wxwin/mainframe.cc:467 +msgid "Cannot create " +msgstr "Impossible de créer " + +#: wxwin/mainframe.cc:496 wxwin/mainframe.cc:560 +msgid "Printing not done" +msgstr "Impression non effectuée" + +#: wxwin/mainframe.cc:496 wxwin/mainframe.cc:524 +msgid "Printing" +msgstr "Impression" + +#: wxwin/mainframe.cc:519 +msgid "Print preview problem.\n" +msgstr "Problème avec l'aperçu avant impression.\n" + +#: wxwin/mainframe.cc:520 +msgid "The printer may not be correctly initialized" +msgstr "Il se peut que l'imprimante soit mal initialisée" + +#: wxwin/mainframe.cc:521 +msgid "Print preview" +msgstr "Aperçu avant impression" + +#: wxwin/mainframe.cc:542 +msgid "Print to a PostScript file" +msgstr "Imprimer dans un fichier PostScript" + +#: wxwin/mainframe.cc:561 wxwin/mainframe.cc:567 +msgid "PostScript printing" +msgstr "Impression PostScript" + +#: wxwin/mainframe.cc:566 +msgid "Cannot initialize PostScript printer" +msgstr "Impossible d'initialiser l'impression PostScript" + +#: wxwin/mainframe.cc:714 +msgid "" +"This program is free software; you can redistribute it and/or modify it " +"under the terms of the GNU General Public License as published by the Free " +"Software Foundation; either version 2 of the License, or (at your option) " +"any later version." +msgstr "" +"Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le " +"modifier selon les termes de la licence \"GNU General Public License\" " +"publiée par la Free Software Fundation ; soit la version 2 de la Licence, " +"soit (comme vous le préférez), n'importe quelle version ultérieure." + +#: wxwin/mainframe.cc:912 +msgid "turn:" +msgstr "coup :" + +#: wxwin/mainframe.cc:913 +msgid "points:" +msgstr "points :" + +#: wxwin/mainframe.cc:948 +msgid "" +"The bag doesn't contain enough letters\n" +"for a new rack." +msgstr "" +"Le sac ne contient pas assez de lettres\n" +"pour un nouveau tirage." + +#: wxwin/mainframe.cc:949 wxwin/mainframe.cc:953 wxwin/mainframe.cc:957 +msgid "Rack validation" +msgstr "Validation du tirage" + +#: wxwin/mainframe.cc:952 +msgid "The rack must contain at least 2 consonants and 2 vowels." +msgstr "Le tirage doit contenir au moins 2 consonnes et 2 voyelles." + +#: wxwin/mainframe.cc:956 +msgid "The rack contains invalid letters for the current dictionary" +msgstr "" +"Le tirage contient des lettres incorrectes pour le dictionnaire courant" + +#: wxwin/mainframe.cc:960 +msgid "The rack has been modified manually" +msgstr "Le tirage a été modifié manuellement" + +#: wxwin/searchpanel.cc:146 wxwin/searchpanel.cc:190 +#, c-format +msgid "The search is limited to %d letters" +msgstr "La recherche est limitée à %d lettres" + +#: wxwin/searchpanel.cc:301 +msgid "Minimum length" +msgstr "Longueur minimum" + +#: wxwin/searchpanel.cc:303 +msgid "Maximum length" +msgstr "Longueur maximum" + +#: wxwin/searchpanel.cc:366 +msgid "Cross words" +msgstr "Mots croisés" + +#: wxwin/searchpanel.cc:367 +msgid "Plus 1" +msgstr "Plus 1" + +#: wxwin/searchpanel.cc:368 +msgid "Regular expressions" +msgstr "Expressions régulières" + +#~ msgid " Racks " +#~ msgstr " Tirages " + +#~ msgid " Help " +#~ msgstr " Aide " + +#~ msgid " Save the game " +#~ msgstr " Sauvegarder la partie " + +#~ msgid " Load a game " +#~ msgstr " Charger une partie " + +#~ msgid "The rack must contain at least 2 consonants and 2 vowels\n" +#~ msgstr "Le tirage doit contenir au moins 2 consonnes et 2 voyelles\n" + +#~ msgid "but the bag doesn't contain enough letters.\n" +#~ msgstr "mais le sac ne contient plus assez de lettres.\n" + +#~ msgid "&Configuration" +#~ msgstr "&Configuration" + +#~ msgid "Game" +#~ msgstr "Partie" + +#~ msgid "Word counts double..." +#~ msgstr "Mot compte double..." + +#~ msgid "Word counts triple..." +#~ msgstr "Mot compte triple..." + +#~ msgid "Letter counts double..." +#~ msgstr "Lettre compte double..." + +#~ msgid "Color of the \"letter counts double\" squares" +#~ msgstr "Couleur des cases \"lettre compte double\"" + +#~ msgid "Letter counts triple..." +#~ msgstr "Lettre compte triple..." + +#~ msgid "Color of the \"letter counts triple\" squares" +#~ msgstr "Couleur des cases \"lettre compte triple\"" + +#, fuzzy +#~ msgid "Fonte des lettres" +#~ msgstr "Entrer les nouvelles lettres:" + +#~ msgid "Game..." +#~ msgstr "Partie..." + +#, fuzzy +#~ msgid "Results..." +#~ msgstr "Résultat" + +#, fuzzy +#~ msgid "Dictionnaire" +#~ msgstr " Dictionnaire " + #~ msgid "Usage: eliotcurses /path/to/ods4.dawg\n" #~ msgstr "Usage: eliotcurses /chemin vers/ods4.dawg\n" #~ msgid "Played word: " -#~ msgstr "Mot joué :" - -#~ msgid "Training" -#~ msgstr "Entraînement" +#~ msgstr "Mot joué :" #~ msgid "Duplicate" #~ msgstr "Duplicate" diff --git a/test/.cvsignore b/test/.cvsignore index 9857323..48b8b1b 100644 --- a/test/.cvsignore +++ b/test/.cvsignore @@ -1 +1,2 @@ *.run +load_saved_game.elt diff --git a/test/driver b/test/driver index 864b862..f94281b 100644 --- a/test/driver +++ b/test/driver @@ -20,19 +20,25 @@ training_dict 0 # randseed unused training_bag 0 # randseed unused # Enter a rack, then display all the possibilities training_search 0 # randseed unused +# Display the benjamins for several words +training_benj 0 # randseed unused +# Display the "raccords" for several words +training_racc 0 # randseed unused +# Test the 7 + 1 feature +training_7pl1 0 # randseed unused # Several ways of getting a rack and playing a word training_play 4 -# Training rack+search+play+back +# Training rack+search+play+back training_back 5 # Joker problem on game search training_rosace 0 -# Board cross off by one score +# Board cross off by one score training_cross 0 -# Board cross backward setAny +# Board cross backward setAny training_cross2 0 -# Board cross backward joker -training_cross3 0 +# Board cross backward joker +training_cross3 0 # Joker problem on game load training_joker 0 @@ -47,6 +53,8 @@ training_joker2 0 # 2 AI players duplicate_2_ai 5 +# 2 human players, one IA, with manual change of human player +duplicate_humans_ai 6 ################# # Free game mode @@ -60,7 +68,7 @@ freegame_change 3 freegame_3_ai 2 ############## -# Load / Save +# Load / Save ############## # load a standard training game (fumee) diff --git a/test/duplicate_2_ai.input b/test/duplicate_2_ai.input index 4fd8b30..459e0ab 100644 --- a/test/duplicate_2_ai.input +++ b/test/duplicate_2_ai.input @@ -3,6 +3,7 @@ a S a T a l a p +a g q q diff --git a/test/duplicate_2_ai.ref b/test/duplicate_2_ai.ref index e5b6f18..260f324 100644 --- a/test/duplicate_2_ai.ref +++ b/test/duplicate_2_ai.ref @@ -3,14 +3,14 @@ commande> d 0 2 mode duplicate [?] pour l'aide commande> a S -Joueur 0: 866 -Joueur 1: 866 +Joueur 0: 918 +Joueur 1: 918 commande> a T -Joueur 0: RTTUW -Joueur 1: RTTUW +Joueur 0: DQRU +Joueur 1: DQRU commande> a l A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 2 1 0 1 0 0 0 0 + 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 commande> a p Eliot 1.5 @@ -20,30 +20,50 @@ Player 1: Computer N | RACK | SOLUTION | REF | PTS | P | BONUS ===|==========|=================|=====|=====|===|====== - 1 | EA?AEBF | FABAcEE | H4 | 80 | 0 | * - 2 | LMUAEYE | YEBLE | 6F | 38 | 0 | - 3 | AMU+JEIG | MEJUGEAI | 9G | 78 | 0 | * - 4 | LEHNMGA | HALE | 8L | 46 | 0 | - 5 | GMN+NSEO | MENSONGE | O1 | 83 | 0 | * - 6 | ARAURIU | RAIRA | 5B | 21 | 0 | - 7 | UU+TSDEA | RUSTAUDE | B5 | 63 | 0 | * - 8 | ONIAVPD | VIDA | A12 | 45 | 0 | - 9 | NOP+SONE | PENONS | K1 | 33 | 0 | - 10 | O+OXTOLN | LUX | J8 | 32 | 0 | - 11 | NOOOT+SZ | ZOOS | 11E | 38 | 0 | - 12 | NOT+MIAI | DOMINAIT | 14A | 74 | 0 | * - 13 | CERPFEO | FEROCE | 15G | 47 | 0 | - 14 | P+BSVQIU | PIQUAS | C1 | 32 | 0 | - 15 | BV+ETLIE | LEVITE | A1 | 39 | 0 | - 16 | B+RLD?UC | PUBLiC | 1C | 36 | 0 | - 17 | DR+NTERR | DENREE | 2J | 22 | 0 | - 18 | RRT+TUKE | TREK | 13F | 36 | 0 | - 19 | RTU+THWI | HAI | 7G | 23 | 0 | + 1 | ?EBAAEF | FABAcEE | H4 | 80 | 0 | * + 2 | KEELIFE | KIEF | 10F | 36 | 0 | + 3 | EEEL+IJX | FIXEE | 4H | 34 | 0 | + 4 | EJL+RANS | JEANS | 11D | 49 | 0 | + 5 | LR+OAHPU | LOPE | 9E | 29 | 0 | + 6 | AHRU+OAE | HOUE | 12A | 40 | 0 | + 7 | AAR+AVOC | HAVA | A12 | 30 | 0 | + 8 | ACOR+WDN | WAX | J2 | 41 | 0 | + 9 | -VNIALZO | OZONAI | B10 | 48 | 0 | + 10 | LV+NDULU | VIN | I3 | 21 | 0 | + 11 | DLLUU+NO | DUNE | K1 | 22 | 0 | + 12 | LLOU+DAM | DOUMA | 1K | 24 | 0 | + 13 | DLL+TOBI | MOLLIT | N1 | 18 | 0 | + 14 | -ELTCUBE | CUBEBE | 6F | 31 | 0 | + 15 | -PE?STIE | SEPTImE | L6 | 85 | 0 | * + 16 | LNRSISE | SERINS | J10 | 29 | 0 | + 17 | L+RGYAET | STYLER | 15J | 75 | 0 | + 18 | AG+CURGI | CARGUE | N10 | 30 | 0 | + 19 | GI+EDRSS | DEGRISAS | 7B | 79 | 0 | * + 20 | TOEHLRT | PHOT | 8L | 27 | 0 | + 21 | ELRT+NME | REMELENT | C2 | 70 | 0 | * + 22 | QDRMUTI | MITAN | 5E | 20 | 0 | - Total: 866 + Total: 918 -Rack 0: RTTUW -Rack 1: RTTUW +Rack 0: DQRU +Rack 1: DQRU +commande> a g + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + A - - - - - - - - - - - H A V A + B - - - - - - D - - O Z O N A I + C - R E M E L E N T - - U - - - + D - - - - - - G - - - J E - - - + E - - - - M - R - L - E - - - - + F - - - - I C I - O K A - - - - + G - - - - T U S - P I N - - - - + H - - - F A B A c E E S - - - - + I - - V I N E S - - F - - - - - + J - W A X - B - - - S E R I N S + K D U N E - E - - - - - - - - T + L O - - E - S E P T I m E - - Y + M U - - - - - - H - - - - - - L + N M O L L I T - O - C A R G U E + O A - - - - - - T - - - - - - R commande> q fin du mode duplicate commande> q diff --git a/test/duplicate_humans_ai.input b/test/duplicate_humans_ai.input new file mode 100644 index 0000000..85ccbfb --- /dev/null +++ b/test/duplicate_humans_ai.input @@ -0,0 +1,20 @@ +d 2 1 +a T +j DOSAIT H4 +j DOSAIT h5 +a S +a T +n 1 +j DISAIENT 11A +a S +j DESTINAI 11E +a S +a t +j FRiPE 10K +j sERF M11 +a S +a t +a g +a p +q +q diff --git a/test/duplicate_humans_ai.ref b/test/duplicate_humans_ai.ref new file mode 100644 index 0000000..02a910c --- /dev/null +++ b/test/duplicate_humans_ai.ref @@ -0,0 +1,78 @@ +[?] pour l'aide +commande> d 2 1 +mode duplicate +[?] pour l'aide +commande> a T +Joueur 0: ATOYDSI +Joueur 1: ATOYDSI +Joueur 2: ATOYDSI +commande> j DOSAIT H4 +commande> j DOSAIT h5 +commande> a S +Joueur 0: 18 +Joueur 1: 14 +Joueur 2: 30 +commande> a T +Joueur 0: DIAEINS +Joueur 1: DIAEINS +Joueur 2: DIAEINS +commande> n 1 +commande> j DISAIENT 11A +commande> a S +Joueur 0: 18 +Joueur 1: 82 +Joueur 2: 30 +commande> j DESTINAI 11E +commande> a S +Joueur 0: 104 +Joueur 1: 82 +Joueur 2: 116 +commande> a t +P?RBFEG +commande> j FRiPE 10K +commande> j sERF M11 +commande> a S +Joueur 0: 126 +Joueur 1: 103 +Joueur 2: 145 +commande> a t +BGEEMTB +commande> a g + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + A - - - - - - - - - - - F - - - + B - - - - - - - - - - - R - - - + C - - - - - - - - - - - i - - - + D - - - - - - - - - - - P - - - + E - - - - - - - - - - D E - - - + F - - - - - - - - - - E - - - - + G - - - - - - - - - - S - - - - + H - - - - - - - O Y A T S - - - + I - - - - - - - - - - I - - - - + J - - - - - - - - - - N - - - - + K - - - - - - - - - - A - - - - + L - - - - - - - - - - I - - - - + M - - - - - - - - - - - - - - - + N - - - - - - - - - - - - - - - + O - - - - - - - - - - - - - - - +commande> a p +Eliot 1.5 + +Game type: Duplicate +Player 0: Human +Player 1: Human +Player 2: Computer + + N | RACK | SOLUTION | REF | PTS | P | BONUS + ===|==========|=================|=====|=====|===|====== + 1 | ATOYDSI | OYATS | H8 | 30 | 2 | + 2 | DI+AEINS | DESTINAI | 11E | 86 | 0 | * + 3 | P?RBFEG | FRiPE | 12A | 29 | 2 | + + Total: 145 + +Rack 0: BG+EEMTB +Rack 1: BG+EEMTB +Rack 2: BG+EEMTB +commande> q +fin du mode duplicate +commande> q diff --git a/test/freegame_3_ai.input b/test/freegame_3_ai.input index d47867b..37dd03c 100644 --- a/test/freegame_3_ai.input +++ b/test/freegame_3_ai.input @@ -2,6 +2,7 @@ l 0 3 a T a S a p +a g q q diff --git a/test/freegame_3_ai.ref b/test/freegame_3_ai.ref index 2d112e7..5d86eee 100644 --- a/test/freegame_3_ai.ref +++ b/test/freegame_3_ai.ref @@ -4,12 +4,12 @@ mode partie libre [?] pour l'aide commande> a T Joueur 0: -Joueur 1: DEPKAS -Joueur 2: DILMQ +Joueur 1: ADLT +Joueur 2: AIU commande> a S -Joueur 0: 398 -Joueur 1: 266 -Joueur 2: 205 +Joueur 0: 319 +Joueur 1: 286 +Joueur 2: 303 commande> a p Eliot 1.5 @@ -20,31 +20,51 @@ Player 2: Computer N | RACK | SOLUTION | REF | PTS | P | BONUS ===|==========|=================|=====|=====|===|====== - 1 | RTADHIR | HARDI | H4 | 26 | 0 | - 2 | ANO?EEA | ANEE | I4 | 18 | 1 | - 3 | WIG?TEN | IWaN | J1 | 38 | 2 | - 4 | RT+NTEIU | INTUITER | 1F | 77 | 0 | * - 5 | AO?+TFBA | BAFOuAT | J7 | 69 | 1 | * - 6 | EGT+OSTR | GROTTES | 9B | 70 | 2 | * - 7 | UEEOERE | GOUREE | B9 | 18 | 0 | - 8 | EAMIZUO | INTUITEREZ | 1F | 57 | 1 | - 9 | XRUMGRQ | GRAUX | 12H | 34 | 2 | - 10 | EE+ONLAS | ENOLATES | E4 | 82 | 0 | * - 11 | AIMOU+NN | NOMINAUX | L5 | 69 | 1 | * - 12 | MQR+LIHJ | HI | F6 | 28 | 2 | - 13 | PLCYEFE | CAPEYE | 3I | 52 | 0 | - 14 | EVLETDE | VOLETE | D8 | 39 | 1 | - 15 | JLMQR+OB | ROB | O3 | 25 | 2 | - 16 | FL+UUSML | FULL | D1 | 26 | 0 | - 17 | DE+ICPEV | VICE | C2 | 32 | 1 | - 18 | JLMQ+DSI | JANS | 5G | 24 | 2 | - 19 | MSU+SAAI | ASSUMAI | 15A | 85 | 0 | * + 1 | RAHITRD | HARDI | H4 | 26 | 0 | + 2 | ?TIEXFA | FAXAIEnT | 5E | 126 | 1 | * + 3 | ATOSBUF | BOUTEFAS | J1 | 73 | 2 | * + 4 | RT+AREEN | RENTERAI | 8A | 77 | 0 | * + 5 | RZUGNSS | AZURS | F5 | 34 | 1 | + 6 | MIMLLOP | PLOMB | 1F | 30 | 2 | + 7 | EEMNETV | VETEMENT | L3 | 76 | 0 | * + 8 | GNRS+PEN | REPS | 11I | 25 | 1 | + 9 | ILM+HSVI | VIZIR | 6D | 33 | 2 | + 10 | AWELOUB | WEB | J10 | 34 | 0 | + 11 | GNN+EELE | EGEENNE | B8 | 20 | 1 | + 12 | HLMS+OJC | LOCHS | 13F | 30 | 2 | + 13 | AELOU+TA | TAULE | A11 | 27 | 0 | + 14 | L+ADSALY | LYS | M1 | 34 | 1 | + 15 | JM+U?EKO | TOKaJ | D8 | 40 | 2 | + 16 | AO+QRSED | COQ | H13 | 36 | 0 | + 17 | AADL+INN | YIN | 2M | 24 | 1 | + 18 | EMU+IGRO | GOUMIER | 2B | 82 | 2 | * + 19 | ADERS+IC | ENCRAIS | O1 | 30 | 0 | + 20 | AADLN+TE | ANE | E10 | 28 | 1 | + 21 | EUIAU | TUEE | 4J | 18 | 2 | + 22 | D | DU | D1 | 5 | 0 | - Total: 869 + Total: 908 Rack 0: -Rack 1: DEP+KAS -Rack 2: DILMQ +Rack 1: ADLT +Rack 2: AIU +commande> a g + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + A - - - - - - - R - - T A U L E + B - G - - - - - E G E E N N E - + C - O - - - - - N - - - - - - - + D D U - - - V - T O K a J - - - + E - M - - F I - E - A N E - - - + F P I - - A Z U R S - - - L - - + G L E - - X I - A - - - - O - - + H O R - H A R D I - - - - C O Q + I M - - - I - - - - - R - H - - + J B O U T E F A S - W E B S - - + K - - - U n - - - - - P - - - - + L - - V E T E M E N T S - - - - + M L Y S E - - - - - - - - - - - + N - I - - - - - - - - - - - - - + O E N C R A I S - - - - - - - - commande> q fin du mode partie libre commande> q diff --git a/test/freegame_change.input b/test/freegame_change.input index 9e1df38..2a719ca 100644 --- a/test/freegame_change.input +++ b/test/freegame_change.input @@ -2,14 +2,15 @@ l 2 0 a T p JID a T -p auouel +p aowalt a T -p WX +p MA a T -p WA +p MN a T -p KEEEAAI +p SGEEEAI a T +a p q q diff --git a/test/freegame_change.ref b/test/freegame_change.ref index 377917d..403dc2b 100644 --- a/test/freegame_change.ref +++ b/test/freegame_change.ref @@ -3,28 +3,46 @@ commande> l 2 0 mode partie libre [?] pour l'aide commande> a T -Joueur 0: NEIJEDN -Joueur 1: UUEALEO +Joueur 0: JNNEDEI +Joueur 1: TLAEOWA commande> p JID commande> a T -Joueur 0: EENNWTA -Joueur 1: UUEALEO -commande> p auouel +Joueur 0: EENNMEN +Joueur 1: TLAEOWA +commande> p aowalt commande> a T -Joueur 0: EENNWTA -Joueur 1: EEAKEIA -commande> p WX +Joueur 0: EENNMEN +Joueur 1: EEEGAIS +commande> p MA commande> a T -Joueur 0: EENNWTA -Joueur 1: EEAKEIA -commande> p WA +Joueur 0: EENNMEN +Joueur 1: EEEGAIS +commande> p MN commande> a T -Joueur 0: EENNTOD -Joueur 1: EEAKEIA -commande> p KEEEAAI +Joueur 0: EEENNER +Joueur 1: EEEGAIS +commande> p SGEEEAI commande> a T -Joueur 0: EENNTOD -Joueur 1: UNLEUSS +Joueur 0: EEENNER +Joueur 1: HAAUAUO +commande> a p +Eliot 1.5 + +Game type: Free game +Player 0: Human +Player 1: Human + + N | RACK | SOLUTION | REF | PTS | P | BONUS + ===|==========|=================|=====|=====|===|====== + 1 | JNNEDEI | (-JID) | - | 0 | 0 | + 2 | TLAEOWA | (-AOWALT) | - | 0 | 1 | + 3 | EENN+MEN | (-MN) | - | 0 | 0 | + 4 | E+EEGAIS | (-SGEEEAI) | - | 0 | 1 | + + Total: 0 + +Rack 0: EEENN+ER +Rack 1: HAAUAUO commande> q fin du mode partie libre commande> q diff --git a/test/freegame_passing.input b/test/freegame_passing.input index 5f9567d..d0ee3c2 100644 --- a/test/freegame_passing.input +++ b/test/freegame_passing.input @@ -2,6 +2,9 @@ l 1 1 a S p p +a t +p UIET +a t p p p @@ -22,6 +25,9 @@ p p p p +p +a S +a T a p q q diff --git a/test/freegame_passing.ref b/test/freegame_passing.ref index 0a48ea7..ec5b3db 100644 --- a/test/freegame_passing.ref +++ b/test/freegame_passing.ref @@ -7,6 +7,11 @@ Joueur 0: 0 Joueur 1: 0 commande> p commande> p +commande> a t +EGISSTU +commande> p UIET +commande> a t +GSSTAUV commande> p commande> p commande> p @@ -27,6 +32,13 @@ commande> p commande> p commande> p commande> p +commande> p +commande> a S +Joueur 0: -11 +Joueur 1: 777 +commande> a T +Joueur 0: AGSSTUV +Joueur 1: commande> a p Eliot 1.5 @@ -36,28 +48,59 @@ Player 1: Computer N | RACK | SOLUTION | REF | PTS | P | BONUS ===|==========|=================|=====|=====|===|====== - 1 | RENLOHL | HERON | H4 | 24 | 1 | - 2 | LL+XUORC | OCREUX | 5E | 34 | 1 | - 3 | LL+NAECT | CALLENT | I7 | 73 | 1 | * - 4 | DIBB?EM | BIBENDuM | 12E | 78 | 1 | * - 5 | TOEOMLZ | TOMIEZ | F9 | 38 | 1 | - 6 | LO+ESLSI | SOLEILS | M6 | 73 | 1 | * - 7 | UEGTVAW | VAGUEZ | 14A | 38 | 1 | - 8 | TW+LAEPU | PAVE | A12 | 36 | 1 | - 9 | LTUW+FAA | FATWA | N2 | 42 | 1 | - 10 | LU+JAEYU | LAYE | O1 | 61 | 1 | - 11 | JUU+SENI | JEUNE | H11 | 47 | 1 | - 12 | ISU+RMPN | SPRAY | 3K | 32 | 1 | - 13 | IMNU+AIV | VLAN | 8L | 33 | 1 | - 14 | IIMU+UQE | QUI | B10 | 30 | 1 | - 15 | EIMU+RNF | UNIFORME | E1 | 62 | 1 | * - 16 | ODITREE | ETOURDIE | 1B | 80 | 1 | * - 17 | TEN?IKR | jERKE | 8A | 69 | 1 | + 1 | SEUTISG | (PASS) | - | 0 | 0 | + 2 | LXCORNU | ROUX | H8 | 26 | 1 | + 3 | EGISSTU | (PASS) | - | 0 | 0 | + 4 | CLN+?BEC | CiNECLUB | 10B | 75 | 1 | * + 5 | EGISSTU | (-UIET) | - | 0 | 0 | + 6 | LLESYEO | YOLES | J6 | 52 | 1 | + 7 | GSS+TAUV | (PASS) | - | 0 | 0 | + 8 | EL+AAOGT | GALETA | 11A | 29 | 1 | + 9 | AGSSTUV | (PASS) | - | 0 | 0 | + 10 | O+RTEUMI | MORUTIER | 8A | 80 | 1 | * + 11 | AGSSTUV | (PASS) | - | 0 | 0 | + 12 | EUNPFUP | PFUT | E5 | 18 | 1 | + 13 | AGSSTUV | (PASS) | - | 0 | 0 | + 14 | ENPU+FTE | FETE | 12B | 33 | 1 | + 15 | AGSSTUV | (PASS) | - | 0 | 0 | + 16 | NPU+NSIM | LUPINS | 8J | 33 | 1 | + 17 | AGSSTUV | (PASS) | - | 0 | 0 | + 18 | MN+UEWOI | WON | N6 | 32 | 1 | + 19 | AGSSTUV | (PASS) | - | 0 | 0 | + 20 | EIMNU+MI | IMMUNS | O3 | 23 | 1 | + 21 | AGSSTUV | (PASS) | - | 0 | 0 | + 22 | EI+IDRED | BIRDIE | I10 | 22 | 1 | + 23 | AGSSTUV | (PASS) | - | 0 | 0 | + 24 | DE+RJAAT | DEJETA | 15H | 45 | 1 | + 25 | AGSSTUV | (PASS) | - | 0 | 0 | + 26 | AR+ENQEE | CAFRE | B10 | 20 | 1 | + 27 | AGSSTUV | (PASS) | - | 0 | 0 | + 28 | AEENQ+BR | BADERNE | 13G | 26 | 1 | + 29 | AGSSTUV | (PASS) | - | 0 | 0 | + 30 | Q+EAZAEL | ZEN | L11 | 24 | 1 | + 31 | AGSSTUV | (PASS) | - | 0 | 0 | + 32 | AAELQ+RA | ALEA | I3 | 16 | 1 | + 33 | AGSSTUV | (PASS) | - | 0 | 0 | + 34 | AQR+LDEH | ADHERAI | 3I | 22 | 1 | + 35 | AGSSTUV | (PASS) | - | 0 | 0 | + 36 | LQ+SI?AI | QuASI | N10 | 65 | 1 | + 37 | AGSSTUV | (PASS) | - | 0 | 0 | + 38 | IL+TVHEO | OHE | 4K | 29 | 1 | + 39 | AGSSTUV | (PASS) | - | 0 | 0 | + 40 | ILTV+ONK | KOHOL | K1 | 34 | 1 | + 41 | AGSSTUV | (PASS) | - | 0 | 0 | + 42 | INTV+SIN | NAYS | 6H | 31 | 1 | + 43 | AGSSTUV | (PASS) | - | 0 | 0 | + 44 | IINTV | TIKI | 1I | 14 | 1 | + 45 | AGSSTUV | (PASS) | - | 0 | 0 | + 46 | NV | VU | D7 | 10 | 1 | + 47 | AGSSTUV | (PASS) | - | 0 | 0 | + 48 | N | NI | M7 | 7 | 1 | - Total: 850 + Total: 766 -Rack 0: EGISSTU -Rack 1: INT+ESDO +Rack 0: AGSSTUV +Rack 1: commande> q fin du mode partie libre commande> q diff --git a/test/load_saved_game.ref b/test/load_saved_game.ref index 14181fe..767b2c3 100644 --- a/test/load_saved_game.ref +++ b/test/load_saved_game.ref @@ -12,16 +12,16 @@ commande> a t LMUAEYE commande> r commande> a r - 1: YEBLE 38 6F - 2: BAYLE 36 6H - 3: AMYLE 35 10D - 4: ELYME 35 10D - 5: ELYME 35 10H + 1: AY 46 I6 + 2: YEBLE 38 6F + 3: MAYE 36 I3 + 4: BAYLE 36 6H + 5: AMYLE 35 10D 6: BAYE 35 6H - 7: BEY 34 6H - 8: LAYEE 34 10D - 9: YUE 32 10F - 10: AMYLE 30 5H + 7: ELYME 35 10D + 8: ELYME 35 10H + 9: LAYE 34 I3 + 10: LAYEE 34 10D commande> n 2 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -30,13 +30,13 @@ commande> a g C - - - - - - - - - - - - - - - D - - - - - - - - - - - - - - - E - - - - - - - - - - - - - - - - F - - - - - - - - - - - - - - - - G - - - - - - - - - - - - - - - + F - - - - - Y - - - - - - - - - + G - - - - - E - - - - - - - - - H - - - F A B A c E E - - - - - - I - - - - - A - - - - - - - - - - J - - - - - Y - - - - - - - - - - K - - - - - L - - - - - - - - - - L - - - - - E - - - - - - - - - + I - - - - - L - - - - - - - - - + J - - - - - E - - - - - - - - - + K - - - - - - - - - - - - - - - + L - - - - - - - - - - - - - - - M - - - - - - - - - - - - - - - N - - - - - - - - - - - - - - - O - - - - - - - - - - - - - - - @@ -48,19 +48,19 @@ commande> n 1 commande> commande> + commande> a t -AMUJEIG +EELMUJE commande> r commande> a r - 1: MEJUGEAI * 78 9G - 2: MEJUGEAI * 72 9C - 3: MEJUGEAI * 71 10G - 4: MEJUGEAI * 71 10C - 5: MEJUGEAI * 69 J5 - 6: MEJUGEAI * 69 J1 - 7: JUGEA 37 5B - 8: JUGEAI 37 5J - 9: JAUGE 35 5J - 10: JUGEA 35 5J + 1: JUMEL 38 J2 + 2: JUMELEE 33 10B + 3: JUMELA 28 5C + 4: JUMELEE 25 9C + 5: JUMELE 24 G10 + 6: JUMEL 23 G10 + 7: JUMELE 23 9C + 8: JEU 19 9G + 9: JEU 19 G10 + 10: JUMELEE 19 10E commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -69,50 +69,50 @@ commande> a g C - - - - - - - - - - - - - - - D - - - - - - - - - - - - - - - E - - - - - - - - - - - - - - - - F - - - - - Y - - - - - - - - - - G - - - - - E - - M - - - - - - + F - - - - - - - - - - - - - - - + G - - - - - - - - - - - - - - - H - - - F A B A c E E - - - - - - I - - - - - L - - J - - - - - - - J - - - - - E - - U - - - - - - - K - - - - - - - - G - - - - - - - L - - - - - - - - E - - - - - - - M - - - - - - - - A - - - - - - - N - - - - - - - - I - - - - - - + I - - - - - A Y - - - - - - - - + J - J U M E L - - - - - - - - - + K - - - - - - - - - - - - - - - + L - - - - - - - - - - - - - - - + M - - - - - - - - - - - - - - - + N - - - - - - - - - - - - - - - O - - - - - - - - - - - - - - - commande> commande> + commande> a t -LEHNMGA +EEIGLEH commande> r commande> a r - 1: HALE 46 8L - 2: MALE 30 8L - 3: ENGAMA 30 5C - 4: HALA 27 5E - 5: LAME 26 8L - 6: HAN 25 8M - 7: HELA 25 5C - 8: GLANA 25 5D - 9: EMANA 25 5D - 10: EGALA 25 5D + 1: HELIEE 34 K5 + 2: HELEE 32 K5 + 3: EGAYEE 32 7F + 4: EGAYE 31 7F + 5: HELE 30 K5 + 6: LAYEE 29 7G + 7: LAYE 28 7G + 8: HUILEE 26 3I + 9: HE 26 K5 + 10: HUILE 24 3I commande> n 3 commande> commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 A - - - - - - - - - - - - - - - B - - - - - - - - - - - - - - - - C - - - - E - - - - - - - - - - - D - - - - N - - - - - - - - - - - E - - - - G - - - - - - - - - - - F - - - - A Y - - - - - - - - - - G - - - - M E - - M - - - - - - + C - - - - - - - - - - - - - - - + D - - - - - - - - - - - - - - - + E - - - - - - - - - - - - - - - + F - - - - - - E - - - - - - - - + G - - - - - - G - - - - - - - - H - - - F A B A c E E - - - - - - I - - - - - L - - J - - - - - - - J - - - - - E - - U - - - - - - - K - - - - - - - - G - - - - - - - L - - - - - - - - E - - - - - - - M - - - - - - - - A - - - - - - - N - - - - - - - - I - - - - - - + I - - - - - A Y - - - - - - - - + J - J U M E L E - - - - - - - - + K - - - - - - E - - - - - - - - + L - - - - - - - - - - - - - - - + M - - - - - - - - - - - - - - - + N - - - - - - - - - - - - - - - O - - - - - - - - - - - - - - - commande> a p Eliot 1.5 @@ -123,16 +123,16 @@ Player 0: Human N | RACK | SOLUTION | REF | PTS | P | BONUS ===|==========|=================|=====|=====|===|====== 1 | EA?AEBF | FABAcEE | H4 | 80 | 0 | * - 2 | LMUAEYE | YEBLE | 6F | 38 | 0 | - 3 | AMU+JEIG | MEJUGEAI | 9G | 78 | 0 | * - 4 | LEHNMGA | ENGAMA | 5C | 30 | 0 | + 2 | LMUAEYE | AY | I6 | 46 | 0 | + 3 | EELMU+JE | JUMEL | J2 | 38 | 0 | + 4 | EE+IGLEH | EGAYEE | 7F | 32 | 0 | - Total: 226 + Total: 196 -Rack 0: HL +Rack 0: HIL commande> a l A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? - 5 1 2 3 9 1 0 2 7 0 1 4 1 5 6 2 1 6 6 6 5 2 1 1 0 1 1 + 6 1 2 3 9 1 1 2 8 0 1 4 2 6 6 2 1 6 6 6 5 2 1 1 0 1 1 commande> commande> s load_saved_game.elt commande> q @@ -145,18 +145,18 @@ commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 A - - - - - - - - - - - - - - - B - - - - - - - - - - - - - - - - C - - - - E - - - - - - - - - - - D - - - - N - - - - - - - - - - - E - - - - G - - - - - - - - - - - F - - - - A Y - - - - - - - - - - G - - - - M E - - M - - - - - - + C - - - - - - - - - - - - - - - + D - - - - - - - - - - - - - - - + E - - - - - - - - - - - - - - - + F - - - - - - E - - - - - - - - + G - - - - - - G - - - - - - - - H - - - F A B A c E E - - - - - - I - - - - - L - - J - - - - - - - J - - - - - E - - U - - - - - - - K - - - - - - - - G - - - - - - - L - - - - - - - - E - - - - - - - M - - - - - - - - A - - - - - - - N - - - - - - - - I - - - - - - + I - - - - - A Y - - - - - - - - + J - J U M E L E - - - - - - - - + K - - - - - - E - - - - - - - - + L - - - - - - - - - - - - - - - + M - - - - - - - - - - - - - - - + N - - - - - - - - - - - - - - - O - - - - - - - - - - - - - - - commande> a p Eliot 1.5 @@ -167,16 +167,16 @@ Player 0: Human N | RACK | SOLUTION | REF | PTS | P | BONUS ===|==========|=================|=====|=====|===|====== 1 | EA?AEBF | FABAcEE | H4 | 80 | 0 | * - 2 | LMUAEYE | YEBLE | 6F | 38 | 0 | - 3 | AMU+JEIG | MEJUGEAI | 9G | 78 | 0 | * - 4 | LEHNMGA | ENGAMA | 5C | 30 | 0 | + 2 | LMUAEYE | AY | I6 | 46 | 0 | + 3 | EELMU+JE | JUMEL | J2 | 38 | 0 | + 4 | EE+IGLEH | EGAYEE | 7F | 32 | 0 | - Total: 226 + Total: 196 -Rack 0: HL +Rack 0: HIL commande> a l A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? - 5 1 2 3 9 1 0 2 7 0 1 4 1 5 6 2 1 6 6 6 5 2 1 1 0 1 1 + 6 1 2 3 9 1 1 2 8 0 1 4 2 6 6 2 1 6 6 6 5 2 1 1 0 1 1 commande> commande> q fin du mode entraînement diff --git a/test/regexp.ref b/test/regexp.ref index c8745ac..401dea0 100644 --- a/test/regexp.ref +++ b/test/regexp.ref @@ -2,6 +2,10 @@ commande> x a.* 50 search for a.* (50,1,15) aa +aalenien +aalenienne +aaleniennes +aaleniens aas abaca abacas @@ -47,14 +51,14 @@ abaisserez abaisseriez abaisserions abaisserons -abaisseront -abaisses -abaisseur -abaisseurs 50 printed results commande> x a.* 200 search for a.* (200,1,15) aa +aalenien +aalenienne +aaleniennes +aaleniens aas abaca abacas @@ -250,10 +254,6 @@ abattait abattant abattants abatte -abattee -abattees -abattement -abattements 200 printed results commande> x .*a.*e.*i.*o.*u.* 50 10 search for .*a.*e.*i.*o.*u.* (50,10,15) @@ -329,13 +329,13 @@ anthophage anthophages bishop bishops +bronchopathie +bronchopathies cenesthopathie cenesthopathies chop chopa chopai -chopaient -chopais 50 printed results commande> x a.*b 50 search for a.*b (50,1,15) @@ -352,13 +352,14 @@ baobab bob bulb cab +cheb cineclub club cob coulomb crib cuproplomb -13 printed results +14 printed results commande> x [abc]*b search for [abc]*b (50,1,15) cab @@ -378,6 +379,7 @@ baobab bob bulb cab +cheb cineclub club cob @@ -386,14 +388,17 @@ crib cuproplomb dab deb +dub fob guib hidjab +hijab hub jab job kebab kob +lamb lob mahaleb mihrab @@ -417,16 +422,14 @@ stilb surplomb toubab toubib -tub -videoclub -web -winstub 50 printed results commande> x .*(cba)+b search for .*(cba)+b (50,1,15) 0 printed results commande> x .*(nn)+.* search for .*(nn)+.* (50,1,15) +aalenienne +aaleniennes abandonna abandonnai abandonnaient @@ -475,11 +478,10 @@ abandonnique abandonniques abandonnons abbevillienne -abbevilliennes -abelienne 50 printed results commande> x .*(nn)+.*x 200 search for .*(nn)+.*x (200,1,15) +actionnariaux annaux anneaux baronniaux @@ -515,6 +517,7 @@ moutonneux panneaux paonneaux pigeonneaux +pitonneux poissonneux precautionneux processionnaux @@ -535,5 +538,5 @@ tyranneaux vallonneux vanneaux vicennaux -55 printed results +57 printed results commande> q diff --git a/test/regression.pl b/test/regression.pl index 87eadf4..769b144 100755 --- a/test/regression.pl +++ b/test/regression.pl @@ -3,9 +3,19 @@ use strict; use warnings; +use Cwd 'abs_path'; + +my $root_path = $0; +$root_path =~ s/regression.pl/../; +$root_path = abs_path($root_path); + +# Change to the test/ directory, because some scenarii expect +# to find saved games in there +chdir("$root_path/test"); + my $driver_file = "driver"; -# Look for ~/ods4.dawg -my $ods = "$ENV{HOME}/ods4.dawg"; +# Look for ~/ods5.dawg +my $ods = "$ENV{HOME}/ods5.dawg"; # File extensions my $input_ext = ".input"; @@ -16,18 +26,18 @@ my $run_ext = ".run"; # Find the dictionary if (not -f $ods) { - die "Cannot find dictionary: $!"; + die "Cannot find dictionary $ods: $!"; } # Find the text interface my $eliottxt; -if (-x "../utils/eliottxt") +if (-x "$root_path/utils/eliottxt") { - $eliottxt = "../utils/eliottxt"; + $eliottxt = "$root_path/utils/eliottxt"; } -elsif (-x "../utils/eliottxt.exe") +elsif (-x "$root_path/utils/eliottxt.exe") { - $eliottxt = "../utils/eliottxt.exe"; + $eliottxt = "$root_path/utils/eliottxt.exe"; } else { diff --git a/test/training_7pl1.input b/test/training_7pl1.input new file mode 100644 index 0000000..e94a9a0 --- /dev/null +++ b/test/training_7pl1.input @@ -0,0 +1,6 @@ +e +b p maison +b p noword +b p zeuer +q +q diff --git a/test/training_7pl1.ref b/test/training_7pl1.ref new file mode 100644 index 0000000..a2660d0 --- /dev/null +++ b/test/training_7pl1.ref @@ -0,0 +1,84 @@ +[?] pour l'aide +commande> e +mode entraînement +[?] pour l'aide +commande> b p maison + AIMONS + AMNIOS + MAISON ++A + OMANAIS ++B + ABIMONS ++C + CAMIONS + MANIOCS ++D + AMIDONS + DAMIONS + DOMINAS + MADISON + MONDAIS ++E + ANOMIES + ANOSMIE ++G + GOMINAS ++I + AIMIONS ++L + LAMIONS + OSMANLI ++M + NOMMAIS ++N + ANIMONS + MANIONS + MANNOIS + MANSION + NOMINAS ++P + PAMIONS ++R + ARMIONS + MANOIRS + MARIONS + MINORAS + NORMAIS + RAMIONS + ROMAINS + ROMANIS ++S + MAISONS ++T + MATIONS + MOISANT + MONTAIS ++Z + MAZIONS +commande> b p noword +commande> b p zeuer ++A + AZUREE ++C + ECUREZ ++H + HUEREZ ++M + MUEREZ + REMUEZ ++N + NUEREZ ++P + EPUREZ + PUEREZ ++R + RUEREZ ++S + SUEREZ + USEREZ ++T + TUEREZ +commande> q +fin du mode entraînement +commande> q diff --git a/test/training_back.input b/test/training_back.input index e446154..0e157c7 100644 --- a/test/training_back.input +++ b/test/training_back.input @@ -9,17 +9,22 @@ r a r n 2 a g +a s n -1 +a s a g a p n -1 +a s a p a g a l n -1 +a s * r n 1 +a s a l a p q diff --git a/test/training_back.ref b/test/training_back.ref index 966c640..9fd83bf 100644 --- a/test/training_back.ref +++ b/test/training_back.ref @@ -12,16 +12,16 @@ commande> a t LMUAEYE commande> r commande> a r - 1: YEBLE 38 6F - 2: BAYLE 36 6H - 3: AMYLE 35 10D - 4: ELYME 35 10D - 5: ELYME 35 10H + 1: AY 46 I6 + 2: YEBLE 38 6F + 3: MAYE 36 I3 + 4: BAYLE 36 6H + 5: AMYLE 35 10D 6: BAYE 35 6H - 7: BEY 34 6H - 8: LAYEE 34 10D - 9: YUE 32 10F - 10: AMYLE 30 5H + 7: ELYME 35 10D + 8: ELYME 35 10H + 9: LAYE 34 I3 + 10: LAYEE 34 10D commande> n 2 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -30,17 +30,21 @@ commande> a g C - - - - - - - - - - - - - - - D - - - - - - - - - - - - - - - E - - - - - - - - - - - - - - - - F - - - - - - - - - - - - - - - - G - - - - - - - - - - - - - - - + F - - - - - Y - - - - - - - - - + G - - - - - E - - - - - - - - - H - - - F A B A c E E - - - - - - I - - - - - A - - - - - - - - - - J - - - - - Y - - - - - - - - - - K - - - - - L - - - - - - - - - - L - - - - - E - - - - - - - - - + I - - - - - L - - - - - - - - - + J - - - - - E - - - - - - - - - + K - - - - - - - - - - - - - - - + L - - - - - - - - - - - - - - - M - - - - - - - - - - - - - - - N - - - - - - - - - - - - - - - O - - - - - - - - - - - - - - - +commande> a s +118 commande> n -1 +commande> a s +80 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 A - - - - - - - - - - - - - - - @@ -72,6 +76,8 @@ Player 0: Human Rack 0: LMUAEYE commande> n -1 +commande> a s +0 commande> a p Eliot 1.5 @@ -105,9 +111,13 @@ commande> a l A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? 9 2 2 3 15 2 2 2 8 1 1 5 3 6 6 2 1 6 6 6 6 2 1 1 1 1 2 commande> n -1 +commande> a s +0 commande> * commande> r commande> n 1 +commande> a s +14 commande> a l A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ? 9 2 2 3 13 1 2 2 7 1 1 5 3 6 6 2 1 6 6 6 6 2 1 1 1 1 2 diff --git a/test/training_benj.input b/test/training_benj.input new file mode 100644 index 0000000..4d6e800 --- /dev/null +++ b/test/training_benj.input @@ -0,0 +1,6 @@ +e +b b maison +b b animal +b b jures +q +q diff --git a/test/training_benj.ref b/test/training_benj.ref new file mode 100644 index 0000000..4833e3f --- /dev/null +++ b/test/training_benj.ref @@ -0,0 +1,14 @@ +[?] pour l'aide +commande> e +mode entraînement +[?] pour l'aide +commande> b b maison +PLUmaison +commande> b b animal +commande> b b jures +CONjures +GOUjures +PARjures +commande> q +fin du mode entraînement +commande> q diff --git a/test/training_cross.ref b/test/training_cross.ref index e23dfd9..9ab32eb 100644 --- a/test/training_cross.ref +++ b/test/training_cross.ref @@ -46,15 +46,15 @@ commande> t IO+EOKAN commande> r commande> a r 1: KIMONO 38 6F - 2: AMOK 34 6G - 3: MOKA 34 6H - 4: MOKO 34 6H - 5: MAKI 34 6H - 6: KAMI 34 6F - 7: KINA 33 I3 + 2: KEMIA 37 6F + 3: MOKO 34 6H + 4: AMOK 34 6G + 5: MOKA 34 6H + 6: MAKI 34 6H + 7: KAMI 34 6F 8: KINE 33 I3 - 9: KAN 29 I3 - 10: OKA 27 I2 + 9: KINA 33 I3 + 10: KAN 29 I3 commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -88,9 +88,9 @@ commande> a r 4: SWEAT 32 L6 5: ETAIES 31 L1 6: AISEE 29 L4 - 7: ETAIS 27 L2 + 7: TAIES 27 L2 8: TAISE 27 L3 - 9: TAIES 27 L2 + 9: ETAIS 27 L2 10: WESTIE 27 9F commande> n 1 commande> a g @@ -124,8 +124,8 @@ commande> a r 3: LAVASSE * 78 10G 4: VASSALE * 75 K9 5: LAVASSE * 73 9C - 6: LAVASSE * 72 9D - 7: LAVASSE * 72 K9 + 6: LAVASSE * 72 K9 + 7: LAVASSE * 72 9D 8: VASSALE * 67 G8 9: LAVASSE * 64 G8 10: EVASAS 35 10I @@ -160,12 +160,12 @@ commande> a r 2: BIQUE 42 8K 3: OTIQUE 39 8J 4: TIQUE 36 8K - 5: BEQUET 30 N9 - 6: MAQUE 29 K9 - 7: EMBUT 29 M1 - 8: MOQUE 28 11D - 9: QUETE 28 N6 - 10: TAQUE 26 K9 + 5: BAQUET 34 K9 + 6: BAQUE 32 K9 + 7: BEQUET 30 N9 + 8: EMBUT 29 M1 + 9: MAQUE 29 K9 + 10: MOQUE 28 11D commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -196,13 +196,13 @@ commande> a r 1: OMNIBUS * 94 O4 2: EMBUIONS * 84 14H 3: BITUMONS * 62 15F - 4: SIMOUN 38 O10 - 5: BOUSIN 38 O7 - 6: NIMBUS 38 O5 - 7: BISON 35 O8 - 8: BISOU 35 O8 - 9: BOUMS 35 O6 - 10: IMBUS 35 O6 + 4: NIMBUS 38 O5 + 5: SIMOUN 38 O10 + 6: BOUSIN 38 O7 + 7: IMBUS 35 O6 + 8: BISON 35 O8 + 9: BISOU 35 O8 + 10: BOUMS 35 O6 commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -236,9 +236,9 @@ commande> a r 4: AXEZ 44 K10 5: IXEE 33 14E 6: EXPIEZ 32 14D - 7: ZEE 32 14F - 8: ZOE 32 14F - 9: EXPIEZ 32 14H + 7: GEX 32 5K + 8: EXPIEZ 32 14H + 9: EPIEZ 32 N10 10: EXPIE 32 N10 commande> n 1 commande> a g @@ -306,14 +306,14 @@ commande> r commande> a r 1: VAINCU 28 13C 2: VAINC 25 K9 - 3: CONVOI 24 11D - 4: CAVAI 24 K9 - 5: CONVIA 23 7A - 6: VAGIN 23 K9 - 7: CONVIA 22 13J + 3: VACOA 25 K9 + 4: CONVOI 24 11D + 5: CAVAI 24 K9 + 6: CONVIA 23 7A + 7: VAGIN 23 K9 8: VINA 22 7C 9: CAVA 22 K9 - 10: CANE 21 7E + 10: CONVIA 22 13J commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -349,8 +349,8 @@ commande> a r 6: AGRESSA 20 D7 7: OSES 19 11H 8: OSAS 19 11H - 9: ESSORA 18 7A - 10: GERA 18 7C + 9: AGES 18 5J + 10: GOSSERA 18 D7 commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -386,8 +386,8 @@ commande> a r 6: JEUDIS 30 C3 7: JUDD 29 N1 8: JAR 29 K9 - 9: JUPE 28 12L - 10: JARD 28 D12 + 9: JARD 28 D12 + 10: JUPE 28 12L commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -422,9 +422,9 @@ commande> a r 5: DYADES 36 C3 6: DYADE 34 D11 7: LAYES 34 B4 - 8: DELAYA 34 D10 - 9: YAWL 34 4J - 10: DRAYE 30 E7 + 8: YAWL 34 4J + 9: DELAYA 34 D10 + 10: ALYA 33 F8 commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -453,14 +453,14 @@ commande> t D+IHUEEB commande> r commande> a r 1: HEU 32 10B - 2: BEDE 31 10B - 3: HE 31 10B + 2: HE 31 10B + 3: BEDE 31 10B 4: BEE 29 10B - 5: DEB 28 10B - 6: IDEE 27 10A - 7: HIEE 27 10A - 8: HUEE 27 10A - 9: BUEE 26 10A + 5: BE 28 10B + 6: DEB 28 10B + 7: IDEE 27 10A + 8: HIEE 27 10A + 9: HUEE 27 10A 10: HIE 26 10A commande> n 1 commande> a g @@ -491,13 +491,13 @@ commande> r commande> a r 1: CIEL 25 D1 2: CLE 22 12D - 3: BLINDE 19 F10 - 4: ALBEDO 18 K10 - 5: DEY 18 7A - 6: DELAVASSES 18 10F - 7: LAIC 18 D12 - 8: BLIND 18 F10 - 9: CAID 18 D12 + 3: BLE 22 12D + 4: BEY 20 7A + 5: BLINDE 19 F10 + 6: BIPE 18 12L + 7: CAID 18 D12 + 8: LAIC 18 D12 + 9: BLIND 18 F10 10: CIBLE 18 4B commande> n 1 commande> a g @@ -529,13 +529,13 @@ commande> a r 1: BRADeRIE * 86 A1 2: BRIARDEs * 61 A2 3: BAuDRIER * 60 A2 - 4: BRoCARD 33 1A - 5: pARFUMEE 33 H1 - 6: CRoBARD 33 1D - 7: CRABIeR 30 1D - 8: BICARRe 30 1B + 4: pARFUMEE 33 H1 + 5: CRoBARD 33 1D + 6: BRoCARD 33 1A + 7: BICARRe 30 1B + 8: CRABIeR 30 1D 9: CIBARe 27 1D - 10: DuRCIRA 27 1A + 10: CRoBAR 27 1D commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -568,10 +568,10 @@ commande> a r 3: CAUDEE 27 1D 4: DAW 25 4J 5: CETEAU 24 1D - 6: CADET 24 1D - 7: CAGEE 24 1D - 8: CEDAT 24 1D - 9: CAUDE 24 1D + 6: CEDAT 24 1D + 7: QUETE 24 12H + 8: CAUDE 24 1D + 9: CAGUE 24 1D 10: CAGET 24 1D commande> n 1 commande> a g @@ -601,14 +601,14 @@ commande> t AEEG+LR? commande> r commande> a r 1: CERcLAGE * 80 1D - 2: RELEGuAT * 77 15A - 3: GELERAiT * 77 15A - 4: GALEREnT * 77 15A - 5: dEREGLAT * 77 15A - 6: REGALEnT * 77 15A - 7: AiGRELET * 77 15A - 8: REGELAiT * 77 15A - 9: REGELAnT * 77 15A + 2: REGELAnT * 77 15A + 3: REGELAiT * 77 15A + 4: REGALEnT * 77 15A + 5: RELEGuAT * 77 15A + 6: GELERAiT * 77 15A + 7: dEREGLAT * 77 15A + 8: AiGRELET * 77 15A + 9: GALEREnT * 77 15A 10: GREnELAT * 74 15A commande> n 1 commande> a g @@ -645,8 +645,8 @@ commande> a r 6: FLAN 21 14A 7: FAN 19 14B 8: FANON 18 11E - 9: FLA 17 14A - 10: NAKFA 17 F4 + 9: NAKFA 17 F4 + 10: FLA 17 14A commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @@ -714,12 +714,12 @@ commande> a r 1: FANON 23 I3 2: KRAFT 19 F6 3: FRET 19 14A - 4: REAI 18 E10 + 4: REFAIT 18 13J 5: FANON 18 11E - 6: FREON 18 11E - 7: REFAIT 18 13J - 8: FERAIT 18 13J - 9: FRETIN 18 13J + 6: FRETIN 18 13J + 7: FERAIT 18 13J + 8: FREON 18 11E + 9: REAI 18 E10 10: EN 18 I13 commande> n 1 commande> a g @@ -750,14 +750,14 @@ commande> r commande> a r 1: ET 18 I13 2: RELAVASSES 15 10F - 3: VAINCUE 13 13C + 3: SE 13 M10 4: EX 13 11M - 5: SE 13 M10 + 5: VAINCUE 13 13C 6: TREK 13 F3 - 7: TEK 12 F4 - 8: TES 11 M8 - 9: RE 11 E10 - 10: TET 11 14B + 7: KEA 12 F6 + 8: TEK 12 F4 + 9: TET 11 14B + 10: RE 11 E10 commande> n 1 commande> a g 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 diff --git a/test/training_cross2.ref b/test/training_cross2.ref index caf8f2f..f40fe31 100644 --- a/test/training_cross2.ref +++ b/test/training_cross2.ref @@ -79,7 +79,7 @@ commande> a gd Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c7518: 1][005c7518: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c751c: 1][005c751c: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000202: 4][00184000: 1][00200222: 2][01384100: 1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] diff --git a/test/training_cross3.ref b/test/training_cross3.ref index 00b7092..d0faf23 100644 --- a/test/training_cross3.ref +++ b/test/training_cross3.ref @@ -45,7 +45,7 @@ commande> a gd # c2 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c7518: 1][005c7518: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c751c: 1][005c751c: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000202: 4][00184000: 1][00200222: 2][01384100: 1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] @@ -114,7 +114,7 @@ commande> a gd # c4 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c7518: 1][005c7518: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c751c: 1][005c751c: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000202: 4][00184000: 1][00200222: 2][01384100: 1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] @@ -150,7 +150,7 @@ commande> a gd # c5 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:13][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c7518: 1][005c7518: 1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c751c: 1][005c751c: 1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000202: 4][00184000: 1][00200222: 2][01384100: 1][01384100: 1][00000000:13][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] @@ -165,7 +165,7 @@ commande> a gd # c5 Cc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ec [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00208222: 1][00000000:-1][00208222: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c7518: 1][00000000:-1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c751c: 1][00000000:-1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Gc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:10][00000000:-1][00200000:10][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hc [ffffffff:-1][ffffffff:-1][00000000:10][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:10][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ic [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] @@ -184,7 +184,7 @@ commande> a gd # c6 Br [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Cr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:13][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 4][010d714e: 1][00000000: 2][00000000:-1][00008000: 1][005c795a: 0][010d714e: 1][00200020: 1][ffffffff:-1][ffffffff:-1] + Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 4][010d714e: 1][00000000: 2][00000000:-1][00008000: 1][005c795e: 0][010d714e: 1][00200020: 1][ffffffff:-1][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1] Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 6][01451010: 2][00200000: 3][00000000:-1][00200222: 1][02284302: 0][00005040: 1][00200022: 1][ffffffff:-1][ffffffff:-1] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] @@ -220,7 +220,7 @@ commande> a gd # c7 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:13][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c7518: 1][005c7518: 1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000200: 4][00ddf036: 1][00000000: 2][005c751c: 1][005c751c: 1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][00000202: 4][00184000: 1][00200222: 2][01384100: 1][01384100: 1][00000000:13][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] @@ -235,7 +235,7 @@ commande> a gd # c7 Cc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ec [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00208222: 1][00000000:-1][00208222: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c7518: 1][00000000:-1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c751c: 1][00000000:-1][01384100: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Gc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:10][00000000:-1][00200000:10][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Hc [ffffffff:-1][ffffffff:-1][00000000:10][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:10][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ic [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] diff --git a/test/training_joker2.ref b/test/training_joker2.ref index 38268eb..75d6ee3 100644 --- a/test/training_joker2.ref +++ b/test/training_joker2.ref @@ -46,20 +46,20 @@ commande> a gd # j1 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000008: 6][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:16][00000000:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795a: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795e: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00200222: 1][02284302: 1][00000222: 3][00000000:-1][040c0020: 6][00000000:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00080200:12][00002020: 5][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][ffffffff:-1][00000000:-1] Kr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Lr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Mr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00080000: 8] - Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c7518: 1] + Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c751c: 1] Or [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] - Ac [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Bc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Cc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 4][00000000:-1][00280022: 4][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c7518: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c751c: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ec [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008000: 1][00000000:-1][00200222: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00200022: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008022: 4][00000000:-1][00008222: 4] Gc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][00000000:11][00000000:-1][00000000:-1][00080000:11] @@ -67,20 +67,20 @@ commande> a gd # j1 Ic [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000004: 5][00000000:-1][00000000:-1][00000000: 5][ffffffff:-1][00608022: 1][00000000:-1][00210800: 2][00000000:-1] Jc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00008000: 3][00000000:-1] Kc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 9][00000000:-1][00000000:-1][00000000:12][00000000:-1] - Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] + Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00002048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] Mc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000144: 2][00000000:-1][00000000:-1][00080220: 2][ffffffff:-1] Nc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1] Oc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:15][00000000:-1][00000000:-1][00000000:-1][00000000:-1] commande> r commande> a r 1: SaUGRENU * 82 E4 - 2: eNjUGUES * 66 D8 - 3: NUaGEUSe * 66 D1 + 2: NUaGEUSe * 66 D1 + 3: eNjUGUES * 66 D8 4: GUEUSANt * 66 11C 5: GUeUSENt * 64 D6 - 6: NUaGeUSE * 64 D4 + 6: ENjUGUeS * 64 D2 7: GUEUSeNt * 64 D3 - 8: ENjUGUeS * 64 D2 + 8: NUaGeUSE * 64 D4 9: GUeUSENT * 62 F1 10: GUEUSeNT * 60 F1 commande> q diff --git a/test/training_racc.input b/test/training_racc.input new file mode 100644 index 0000000..df8bc9a --- /dev/null +++ b/test/training_racc.input @@ -0,0 +1,6 @@ +e +b r ave +b r yeux +b r ka +q +q diff --git a/test/training_racc.ref b/test/training_racc.ref new file mode 100644 index 0000000..e231418 --- /dev/null +++ b/test/training_racc.ref @@ -0,0 +1,26 @@ +[?] pour l'aide +commande> e +mode entraînement +[?] pour l'aide +commande> b r ave +Bave +Cave +Gave +Have +Lave +Nave +Pave +Rave +aveC +aveN +aveU +aveZ +commande> b r yeux +commande> b r ka +Oka +Ska +kaN +kaS +commande> q +fin du mode entraînement +commande> q diff --git a/test/training_rosace.ref b/test/training_rosace.ref index b8ff222..76f6c5a 100644 --- a/test/training_rosace.ref +++ b/test/training_rosace.ref @@ -64,20 +64,20 @@ commande> a gd # r1 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000008: 6][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:16][00000000:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795a: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795e: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00200222: 1][02284302: 1][00000222: 3][00000000:-1][040c0020: 6][00000000:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00080200:12][00002020: 5][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][ffffffff:-1][00000000:-1] Kr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Lr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Mr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00080000: 8] - Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c7518: 1] + Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c751c: 1] Or [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] - Ac [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Bc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Cc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 4][00000000:-1][00280022: 4][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c7518: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c751c: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ec [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008000: 1][00000000:-1][00200222: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00200022: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008022: 4][00000000:-1][00008222: 4] Gc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][00000000:11][00000000:-1][00000000:-1][00080000:11] @@ -85,7 +85,7 @@ commande> a gd # r1 Ic [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000004: 5][00000000:-1][00000000:-1][00000000: 5][ffffffff:-1][00608022: 1][00000000:-1][00210800: 2][00000000:-1] Jc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00008000: 3][00000000:-1] Kc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 9][00000000:-1][00000000:-1][00000000:12][00000000:-1] - Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] + Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00002048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] Mc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000144: 2][00000000:-1][00000000:-1][00080220: 2][ffffffff:-1] Nc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1] Oc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:15][00000000:-1][00000000:-1][00000000:-1][00000000:-1] @@ -139,20 +139,20 @@ commande> a gd # r2 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000008: 6][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:16][00000000:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795a: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795e: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00200222: 1][02284302: 1][00000222: 3][00000000:-1][040c0020: 6][00000000:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00080200:12][00002020: 5][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][ffffffff:-1][00000000:-1] Kr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Lr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Mr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00080000: 8] - Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c7518: 1] + Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c751c: 1] Or [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] - Ac [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Bc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Cc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 4][00000000:-1][00280022: 4][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c7518: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c751c: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ec [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008000: 1][00000000:-1][00200222: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00200022: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008022: 4][00000000:-1][00008222: 4] Gc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][00000000:11][00000000:-1][00000000:-1][00080000:11] @@ -160,7 +160,7 @@ commande> a gd # r2 Ic [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000004: 5][00000000:-1][00000000:-1][00000000: 5][ffffffff:-1][00608022: 1][00000000:-1][00210800: 2][00000000:-1] Jc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00008000: 3][00000000:-1] Kc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 9][00000000:-1][00000000:-1][00000000:12][00000000:-1] - Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] + Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00002048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] Mc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000144: 2][00000000:-1][00000000:-1][00080220: 2][ffffffff:-1] Nc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1] Oc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:15][00000000:-1][00000000:-1][00000000:-1][00000000:-1] @@ -169,13 +169,13 @@ commande> r commande> commande> a r 1: SaUGRENU * 82 E4 - 2: ENjUGUES * 66 D8 - 3: NUaGEUSE * 66 D1 + 2: NUaGEUSE * 66 D1 + 3: ENjUGUES * 66 D8 4: GUEUSANt * 66 11C 5: GUEUSENt * 64 D6 - 6: NUaGEUSE * 64 D4 + 6: ENjUGUES * 64 D2 7: GUEUSENt * 64 D3 - 8: ENjUGUES * 64 D2 + 8: NUaGEUSE * 64 D4 9: GUeUSENT * 62 F1 10: GUEUSeNT * 60 F1 commande> @@ -186,20 +186,20 @@ commande> a gd # r3 Dr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Er [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000008: 6][ffffffff:-1] Fr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:16][00000000:-1][ffffffff:-1] - Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795a: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] + Gr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000: 5][00608022: 1][005c795e: 1][00008000: 3][00000000:-1][00000000:-1][00000000: 8] Hr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Ir [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00200222: 1][02284302: 1][00000222: 3][00000000:-1][040c0020: 6][00000000:-1] Jr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00080200:12][00002020: 5][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][ffffffff:-1][00000000:-1] Kr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Lr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00000000:-1] Mr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][ffffffff:-1][00080000: 8] - Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c7518: 1] + Nr [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:26][00000000: 3][005c751c: 1] Or [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] - Ac [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:12][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1][00000000:-1] Bc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Cc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 4][00000000:-1][00280022: 4][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] - Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c7518: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] + Dc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][005c751c: 0][00000000:-1][01384100: 0][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Ec [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008000: 1][00000000:-1][00200222: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1] Fc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00200022: 1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00008022: 4][00000000:-1][00008222: 4] Gc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1][00000000:11][00000000:-1][00000000:-1][00080000:11] @@ -207,7 +207,7 @@ commande> a gd # r3 Ic [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000004: 5][00000000:-1][00000000:-1][00000000: 5][ffffffff:-1][00608022: 1][00000000:-1][00210800: 2][00000000:-1] Jc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00200020: 1][00000000:-1][00008000: 3][00000000:-1] Kc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000: 9][00000000:-1][00000000:-1][00000000:12][00000000:-1] - Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] + Lc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00002048: 2][00000000:-1][00000000:-1][00004000: 3][00000000:-1] Mc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000144: 2][00000000:-1][00000000:-1][00080220: 2][ffffffff:-1] Nc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][010d714e: 1][00000000:-1][00005040: 1][ffffffff:-1][ffffffff:-1] Oc [ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][ffffffff:-1][00000000:15][00000000:-1][00000000:-1][00000000:-1][00000000:-1] diff --git a/test/training_search.ref b/test/training_search.ref index 0141d49..3a9983a 100644 --- a/test/training_search.ref +++ b/test/training_search.ref @@ -8,847 +8,892 @@ QPINZ?S commande> r commande> a r 1000 1: SPItZ 50 H8 - 2: ZINcS 46 H4 + 2: ZaNIS 46 H4 3: ZaINS 46 H4 - 4: ZaNIS 46 H4 - 5: QuIZ 38 H8 - 6: QuIZ 38 H5 - 7: QuIZ 38 H6 - 8: QuIZ 38 H7 - 9: SPItZ 32 H4 - 10: ZIPS 30 H5 - 11: SPItZ 30 H5 + 4: ZINcS 46 H4 + 5: QuIZ 38 H7 + 6: QuIZ 38 H8 + 7: QuIZ 38 H5 + 8: QuIZ 38 H6 + 9: QINgS 38 H4 + 10: SPItZ 32 H4 + 11: SPItZ 30 H6 12: ZIPS 30 H6 - 13: SPItZ 30 H6 - 14: ZIPS 30 H7 - 15: ZIPS 30 H8 - 16: SPItZ 30 H7 - 17: ZIPs 28 H8 - 18: ZaNIS 28 H8 - 19: ZIP 28 H6 - 20: ZIPs 28 H6 - 21: ZIP 28 H8 - 22: ZaINS 28 H8 - 23: NaZIS 28 H8 - 24: ZuPS 28 H7 - 25: ZiPS 28 H8 - 26: ZINcS 28 H8 - 27: ZuPS 28 H5 - 28: ZIPs 28 H5 - 29: ZiPS 28 H7 - 30: ZIP 28 H7 - 31: ZuPS 28 H8 - 32: ZuPS 28 H6 - 33: NaZIS 28 H4 - 34: ZiPS 28 H6 - 35: ZiPS 28 H5 - 36: ZIPs 28 H7 - 37: ZaINS 26 H7 - 38: ZINcS 26 H7 - 39: ZaNIS 26 H7 - 40: ZaNIS 26 H5 - 41: ZiP 26 H7 - 42: ZaINS 26 H5 - 43: NaZIS 26 H5 - 44: ZaNIS 26 H6 - 45: ZaINS 26 H6 - 46: NaZIS 26 H6 - 47: ZuP 26 H7 - 48: ZINcS 26 H5 - 49: ZuP 26 H8 - 50: ZiP 26 H8 - 51: ZiP 26 H6 - 52: ZuP 26 H6 - 53: NaZIS 26 H7 - 54: ZINcS 26 H6 - 55: bINZ 24 H5 - 56: ZaNI 24 H7 - 57: NaZI 24 H5 - 58: NIeZ 24 H8 - 59: ZIpS 24 H5 - 60: NIeZ 24 H7 - 61: ZaIN 24 H7 - 62: ZISt 24 H5 - 63: bINZ 24 H6 - 64: ZISt 24 H7 - 65: ZIpS 24 H7 - 66: NIeZ 24 H5 - 67: ZIgS 24 H7 - 68: NIeZ 24 H6 - 69: NaZI 24 H7 - 70: bINZ 24 H8 - 71: ZINc 24 H7 - 72: bINZ 24 H7 - 73: ZINc 24 H8 - 74: ZIgS 24 H8 - 75: ZeNS 24 H5 - 76: ZeNS 24 H6 - 77: ZeNS 24 H8 - 78: ZIgS 24 H6 - 79: ZINc 24 H6 + 13: ZIPS 30 H8 + 14: SPItZ 30 H5 + 15: ZIPS 30 H5 + 16: ZIPS 30 H7 + 17: SPItZ 30 H7 + 18: ZIPs 28 H7 + 19: ZiPS 28 H7 + 20: ZuPS 28 H7 + 21: ZaINS 28 H8 + 22: ZIP 28 H7 + 23: ZiPS 28 H8 + 24: NaZIS 28 H8 + 25: ZaNIS 28 H8 + 26: ZIPs 28 H8 + 27: ZuPS 28 H8 + 28: ZiPS 28 H6 + 29: ZiPS 28 H5 + 30: ZIPs 28 H5 + 31: ZuPS 28 H6 + 32: NaZIS 28 H4 + 33: ZIP 28 H8 + 34: ZuPS 28 H5 + 35: ZIPs 28 H6 + 36: ZINcS 28 H8 + 37: ZIP 28 H6 + 38: ZiP 26 H6 + 39: ZiP 26 H8 + 40: ZaINS 26 H7 + 41: ZINcS 26 H7 + 42: ZaNIS 26 H7 + 43: NaZIS 26 H6 + 44: ZuP 26 H6 + 45: NaZIS 26 H7 + 46: ZINcS 26 H5 + 47: ZiP 26 H7 + 48: ZINcS 26 H6 + 49: ZuP 26 H7 + 50: ZuP 26 H8 + 51: ZaINS 26 H6 + 52: ZaNIS 26 H6 + 53: ZaNIS 26 H5 + 54: ZaINS 26 H5 + 55: NaZIS 26 H5 + 56: bINZ 24 H7 + 57: NIeZ 24 H8 + 58: ZaIN 24 H8 + 59: NaZI 24 H7 + 60: NIeZ 24 H5 + 61: ZISt 24 H8 + 62: ZINc 24 H8 + 63: NIeZ 24 H6 + 64: ZIpS 24 H8 + 65: ZIgS 24 H8 + 66: ZeNS 24 H8 + 67: ZaNI 24 H8 + 68: NIeZ 24 H7 + 69: NaZI 24 H6 + 70: NaZI 24 H5 + 71: ZIgS 24 H5 + 72: ZISt 24 H6 + 73: ZIpS 24 H6 + 74: QINgS 24 H8 + 75: ZINc 24 H6 + 76: ZIgS 24 H6 + 77: ZINc 24 H5 + 78: ZeNS 24 H5 + 79: ZeNS 24 H6 80: ZaNI 24 H5 - 81: ZeNS 24 H7 - 82: ZaNI 24 H8 - 83: ZIpS 24 H8 - 84: ZIpS 24 H6 - 85: ZISt 24 H8 - 86: NaZI 24 H8 - 87: ZISt 24 H6 - 88: ZaIN 24 H5 - 89: ZaIN 24 H8 - 90: ZaNI 24 H6 - 91: ZIgS 24 H5 - 92: NaZI 24 H6 - 93: ZaIN 24 H6 - 94: ZINc 24 H5 - 95: ZIg 22 H7 - 96: ZeN 22 H7 - 97: rIZ 22 H8 - 98: NeZ 22 H6 - 99: ZeN 22 H8 -100: ZIg 22 H8 -101: ZIp 22 H8 -102: NeZ 22 H7 -103: rIZ 22 H6 + 81: ZaIN 24 H5 + 82: ZaNI 24 H6 + 83: ZaIN 24 H6 + 84: ZISt 24 H7 + 85: ZIpS 24 H7 + 86: bINZ 24 H8 + 87: ZINc 24 H7 + 88: ZIgS 24 H7 + 89: ZeNS 24 H7 + 90: ZaNI 24 H7 + 91: ZaIN 24 H7 + 92: NaZI 24 H8 + 93: ZIpS 24 H5 + 94: ZISt 24 H5 + 95: bINZ 24 H5 + 96: bINZ 24 H6 + 97: QINS 22 H8 + 98: ZIg 22 H8 + 99: QINgS 22 H6 +100: ZeN 22 H8 +101: QINS 22 H6 +102: QINgS 22 H5 +103: QINS 22 H5 104: rIZ 22 H7 -105: ZIp 22 H7 -106: NeZ 22 H8 -107: ZIg 22 H6 -108: ZeN 22 H6 -109: ZIp 22 H6 -110: cINQ 20 H8 -111: cINQ 20 H7 -112: cINQ 20 H6 -113: cINQ 20 H5 -114: PISaN 18 H4 -115: PIoNS 18 H4 -116: PuNIS 18 H4 -117: QuI 18 H7 -118: QuI 18 H6 -119: PINeS 18 H4 -120: PoINS 18 H4 -121: QuI 18 H8 -122: PaINS 18 H4 -123: PeINS 18 H4 -124: PeNIS 18 H4 -125: PIaNS 18 H4 -126: PIaNS 14 H8 -127: SPINs 14 H4 -128: SaPIN 14 H4 -129: sPINS 14 H8 -130: SuPIN 14 H8 -131: PaINS 14 H8 -132: PINeS 14 H8 -133: SaPIN 14 H8 -134: PeINS 14 H8 -135: PeNIS 14 H8 -136: PuNIS 14 H8 -137: PIoNS 14 H8 -138: SuPIN 14 H4 -139: PoINS 14 H8 -140: PISaN 14 H8 -141: PIoNS 12 H7 -142: PINS 12 H7 -143: PINeS 12 H7 -144: sPINS 12 H5 -145: SPIN 12 H8 -146: SPINs 12 H8 -147: PIaNS 12 H7 -148: PeNIS 12 H7 -149: PeINS 12 H7 -150: PaINS 12 H7 -151: sPINS 12 H4 -152: SPINs 12 H5 -153: PIaNS 12 H6 -154: SPIN 12 H7 -155: SPINs 12 H7 -156: PISaN 12 H6 -157: PuNIS 12 H5 -158: PIoNS 12 H5 -159: SuPIN 12 H7 -160: PISaN 12 H5 -161: PIaNS 12 H5 -162: PuNIS 12 H6 -163: SaPIN 12 H6 -164: SaPIN 12 H5 -165: SaPIN 12 H7 -166: PoINS 12 H6 -167: PoINS 12 H5 -168: SuPIN 12 H5 -169: PINS 12 H8 -170: PuNIS 12 H7 -171: PeNIS 12 H5 -172: PaINS 12 H6 -173: PIoNS 12 H6 -174: SPIN 12 H5 -175: PINS 12 H5 -176: SuPIN 12 H6 -177: PeINS 12 H5 -178: PINeS 12 H5 -179: SPIN 12 H6 -180: SPINs 12 H6 -181: PeNIS 12 H6 -182: PaINS 12 H5 -183: PeINS 12 H6 -184: PINS 12 H6 -185: sPINS 12 H7 -186: PISaN 12 H7 -187: sPINS 12 H6 -188: PoINS 12 H7 -189: PINeS 12 H6 -190: SPIs 10 H5 -191: PuIS 10 H5 -192: kIPS 10 H5 -193: IPeS 10 H7 -194: PuNI 10 H6 -195: SPIc 10 H5 -196: lISP 10 H6 -197: SPIs 10 H6 -198: SkIP 10 H5 -199: PuIS 10 H6 -200: lISP 10 H7 -201: PSI 10 H6 -202: SlIP 10 H5 -203: SlIP 10 H6 -204: PrIS 10 H5 -205: SIPo 10 H7 -206: IPeS 10 H5 -207: mIPS 10 H5 -208: kIPS 10 H7 -209: mIPS 10 H6 -210: SkIP 10 H7 -211: SlIP 10 H7 -212: SPiN 10 H6 -213: IPeS 10 H6 -214: kIPS 10 H6 -215: SIPo 10 H6 -216: SPI 10 H6 -217: SPIc 10 H6 -218: SIPo 10 H5 -219: SPIn 10 H6 -220: mIPS 10 H7 -221: SkIP 10 H6 -222: SPIn 10 H5 -223: PuNI 10 H5 -224: lISP 10 H5 -225: PoIS 10 H7 -226: PrIS 10 H7 -227: PSI 10 H7 -228: PuIS 10 H7 -229: PuNI 10 H7 -230: PaIN 10 H6 -231: PaIS 10 H6 -232: PInS 10 H5 -233: PINs 10 H5 -234: PaNS 10 H6 -235: PINe 10 H5 -236: PIlS 10 H5 -237: PaIN 10 H5 -238: PaIS 10 H5 -239: PaNS 10 H5 -240: PIaN 10 H6 -241: PIcS 10 H6 -242: PIeS 10 H6 -243: PIfS 10 H6 -244: PIlS 10 H6 -245: PIN 10 H6 -246: PINe 10 H6 -247: PINs 10 H6 -248: PInS 10 H6 -249: PIoN 10 H6 -250: PIS 10 H6 -251: PISe 10 H6 -252: PIaN 10 H5 -253: PIcS 10 H5 -254: PIeS 10 H5 -255: PIfS 10 H5 -256: PrIS 10 H6 -257: PoIS 10 H5 -258: PoIS 10 H6 -259: PlIS 10 H5 -260: PlIS 10 H6 -261: PiNS 10 H5 -262: PiNS 10 H6 -263: PISe 10 H5 -264: SPiN 10 H7 -265: PIoN 10 H5 -266: SPIs 10 H7 -267: SPIn 10 H7 -268: SPIc 10 H7 -269: SPI 10 H7 -270: PaIN 10 H7 -271: PaIS 10 H7 -272: PaNS 10 H7 -273: PIaN 10 H7 -274: PIcS 10 H7 -275: PIeS 10 H7 -276: PIfS 10 H7 -277: PIlS 10 H7 -278: PIN 10 H7 -279: PINe 10 H7 -280: PINs 10 H7 -281: PInS 10 H7 -282: PIoN 10 H7 -283: PIS 10 H7 -284: PISe 10 H7 -285: PiNS 10 H7 -286: PlIS 10 H7 -287: PlIS 10 H8 -288: PoIS 10 H8 -289: PrIS 10 H8 -290: PSI 10 H8 -291: PuIS 10 H8 -292: PuNI 10 H8 -293: SIPo 10 H8 -294: SkIP 10 H8 -295: SlIP 10 H8 -296: SPI 10 H8 -297: SPIc 10 H8 -298: SPIn 10 H8 -299: SPIs 10 H8 -300: SPiN 10 H8 -301: sPIN 10 H8 -302: sPIS 10 H8 -303: tIPS 10 H8 -304: tIPS 10 H5 -305: tIPS 10 H6 -306: tIPS 10 H7 -307: zIPS 10 H8 -308: sPIS 10 H5 -309: aPIS 10 H7 -310: aSPI 10 H7 -311: sPIN 10 H5 -312: sPIS 10 H6 -313: aPIS 10 H6 -314: zIPS 10 H5 -315: zIPS 10 H6 -316: zIPS 10 H7 -317: aPIS 10 H8 -318: aSPI 10 H8 -319: bIPS 10 H8 -320: ePIS 10 H8 -321: IPeS 10 H8 -322: kIPS 10 H8 -323: lISP 10 H8 -324: mIPS 10 H8 -325: PaIN 10 H8 -326: PaIS 10 H8 -327: PaNS 10 H8 -328: PIaN 10 H8 -329: PIcS 10 H8 -330: PIeS 10 H8 -331: PIfS 10 H8 -332: PIlS 10 H8 -333: PIN 10 H8 -334: PINe 10 H8 -335: PINs 10 H8 -336: PInS 10 H8 -337: PIoN 10 H8 -338: PIS 10 H8 -339: PISe 10 H8 -340: PiNS 10 H8 -341: sPIN 10 H6 -342: bIPS 10 H5 -343: aSPI 10 H5 -344: ePIS 10 H5 -345: bIPS 10 H7 -346: ePIS 10 H7 -347: aSPI 10 H6 -348: ePIS 10 H6 -349: aPIS 10 H5 -350: bIPS 10 H6 -351: SPiN 10 H5 -352: sPIS 10 H7 -353: sPIN 10 H7 -354: PaS 8 H7 -355: SPa 8 H8 -356: PhI 8 H7 -357: PI 8 H7 -358: SPi 8 H8 -359: bIP 8 H6 -360: PIc 8 H7 -361: PIf 8 H7 -362: IPe 8 H6 -363: PIe 8 H7 -364: PIn 8 H8 -365: sPI 8 H7 -366: PaN 8 H7 -367: SPi 8 H6 -368: PIs 8 H6 -369: PIu 8 H6 -370: ePI 8 H7 -371: PIf 8 H8 -372: sPI 8 H8 -373: PIe 8 H8 -374: PIc 8 H8 -375: ePI 8 H6 -376: PI 8 H8 -377: PhI 8 H8 -378: tIP 8 H8 -379: PaS 8 H8 -380: PuS 8 H8 -381: PaS 8 H6 -382: SPa 8 H6 -383: PaN 8 H6 -384: PsI 8 H8 -385: PuS 8 H7 -386: PSy 8 H8 -387: PSt 8 H8 -388: SeP 8 H8 -389: PSi 8 H8 -390: PsI 8 H7 -391: PSy 8 H7 -392: PSt 8 H7 -393: PSi 8 H7 -394: sPI 8 H6 -395: PhI 8 H6 -396: PlI 8 H7 -397: PiS 8 H7 -398: PlI 8 H8 -399: PiN 8 H7 -400: PIc 8 H6 -401: PIu 8 H7 -402: PIs 8 H7 -403: PiS 8 H8 -404: PIe 8 H6 -405: PIf 8 H6 -406: PiN 8 H8 -407: PIu 8 H8 -408: PIn 8 H7 -409: PIs 8 H8 -410: IPe 8 H7 -411: PIn 8 H6 -412: kIP 8 H6 -413: kIP 8 H8 -414: IPe 8 H8 -415: tIP 8 H7 -416: PSi 8 H6 -417: zIP 8 H8 -418: PSt 8 H6 -419: PSy 8 H6 -420: PsI 8 H6 -421: SeP 8 H6 -422: bIP 8 H7 -423: hIP 8 H8 -424: PuS 8 H6 -425: ePI 8 H8 -426: SPa 8 H7 -427: aPI 8 H7 -428: bIP 8 H8 -429: zIP 8 H7 -430: aPI 8 H8 -431: zIP 8 H6 -432: SeP 8 H7 -433: aPI 8 H6 -434: SPi 8 H7 -435: PiN 8 H6 -436: hIP 8 H7 -437: PiS 8 H6 -438: PlI 8 H6 -439: PaN 8 H8 -440: tIP 8 H6 -441: hIP 8 H6 -442: kIP 8 H7 -443: SNIf 6 H7 -444: pINS 6 H6 -445: gINS 6 H7 -446: pINS 6 H5 -447: IbNS 6 H6 -448: IbNS 6 H5 -449: IbNS 6 H7 -450: NuIS 6 H8 -451: fINS 6 H7 -452: oINS 6 H8 -453: SIeN 6 H7 -454: fINS 6 H5 -455: SeIN 6 H7 -456: SkIN 6 H7 -457: aNIS 6 H8 -458: fINS 6 H6 -459: SaIN 6 H7 -460: Pu 6 H8 -461: gINS 6 H6 -462: lINS 6 H8 -463: mINS 6 H8 -464: SpIN 6 H5 -465: INSu 6 H7 -466: IoNS 6 H8 -467: gINS 6 H5 -468: NaIS 6 H8 -469: NIaS 6 H8 -470: IoNS 6 H7 -471: INSu 6 H8 -472: NIdS 6 H8 -473: Pi 6 H8 -474: IbNS 6 H8 -475: NIeS 6 H8 -476: NIfS 6 H8 -477: gINS 6 H8 -478: fINS 6 H8 -479: SpIN 6 H6 -480: NItS 6 H8 -481: pINS 6 H7 -482: SoIN 6 H7 +105: rIZ 22 H6 +106: rIZ 22 H8 +107: NeZ 22 H8 +108: NeZ 22 H6 +109: ZeN 22 H6 +110: QINS 22 H7 +111: QINgS 22 H7 +112: ZIp 22 H8 +113: ZIp 22 H7 +114: NeZ 22 H7 +115: ZIp 22 H6 +116: ZIg 22 H7 +117: ZIg 22 H6 +118: ZeN 22 H7 +119: QIN 20 H8 +120: QIN 20 H6 +121: QINs 20 H6 +122: QiNS 20 H5 +123: QiNS 20 H6 +124: QInS 20 H6 +125: QInS 20 H5 +126: QINs 20 H5 +127: QINg 20 H6 +128: QINg 20 H5 +129: cINQ 20 H5 +130: QINs 20 H7 +131: QInS 20 H7 +132: QINg 20 H7 +133: QIN 20 H7 +134: QiNS 20 H8 +135: QiNS 20 H7 +136: QInS 20 H8 +137: QINs 20 H8 +138: cINQ 20 H7 +139: cINQ 20 H8 +140: cINQ 20 H6 +141: QINg 20 H8 +142: QIn 18 H6 +143: QiN 18 H8 +144: QIn 18 H8 +145: QuI 18 H8 +146: QiN 18 H6 +147: QuI 18 H6 +148: PeINS 18 H4 +149: PISaN 18 H4 +150: PeNIS 18 H4 +151: PIaNS 18 H4 +152: PaINS 18 H4 +153: PINeS 18 H4 +154: PuNIS 18 H4 +155: QuI 18 H7 +156: PoINS 18 H4 +157: QiN 18 H7 +158: PIoNS 18 H4 +159: QIn 18 H7 +160: SaPIN 14 H8 +161: sPINS 14 H8 +162: SuPIN 14 H4 +163: SPINs 14 H4 +164: PaINS 14 H8 +165: PeINS 14 H8 +166: PeNIS 14 H8 +167: PIaNS 14 H8 +168: SaPIN 14 H4 +169: PINeS 14 H8 +170: PIoNS 14 H8 +171: PISaN 14 H8 +172: SuPIN 14 H8 +173: PoINS 14 H8 +174: PuNIS 14 H8 +175: PIoNS 12 H5 +176: PoINS 12 H7 +177: PISaN 12 H5 +178: SPINs 12 H8 +179: PINS 12 H5 +180: PINeS 12 H5 +181: PIaNS 12 H5 +182: PISaN 12 H6 +183: PuNIS 12 H7 +184: PIoNS 12 H6 +185: PaINS 12 H6 +186: PINS 12 H6 +187: PINeS 12 H6 +188: PaINS 12 H5 +189: PIaNS 12 H6 +190: PeNIS 12 H5 +191: PeINS 12 H5 +192: PeNIS 12 H6 +193: PeINS 12 H6 +194: PIaNS 12 H7 +195: SPIN 12 H7 +196: SPINs 12 H7 +197: PeNIS 12 H7 +198: PeINS 12 H7 +199: PINS 12 H8 +200: SuPIN 12 H7 +201: SaPIN 12 H6 +202: SaPIN 12 H5 +203: PaINS 12 H7 +204: SPIN 12 H6 +205: SPINs 12 H6 +206: SPIN 12 H5 +207: SPINs 12 H5 +208: SuPIN 12 H6 +209: SuPIN 12 H5 +210: sPINS 12 H7 +211: sPINS 12 H6 +212: sPINS 12 H5 +213: sPINS 12 H4 +214: PoINS 12 H6 +215: PoINS 12 H5 +216: PIoNS 12 H7 +217: PISaN 12 H7 +218: SPIN 12 H8 +219: SaPIN 12 H7 +220: PuNIS 12 H5 +221: PINS 12 H7 +222: PINeS 12 H7 +223: PuNIS 12 H6 +224: PiNS 10 H7 +225: mIPS 10 H5 +226: mIPS 10 H6 +227: mIPS 10 H7 +228: lISP 10 H5 +229: PaIS 10 H6 +230: PaNS 10 H6 +231: PaIN 10 H5 +232: PISe 10 H7 +233: PaIS 10 H5 +234: lISP 10 H6 +235: PaNS 10 H5 +236: lISP 10 H7 +237: kIPS 10 H5 +238: kIPS 10 H6 +239: kIPS 10 H7 +240: IPeS 10 H5 +241: IPeS 10 H6 +242: PrIS 10 H7 +243: PSI 10 H7 +244: PuIS 10 H7 +245: PIlS 10 H7 +246: PIfS 10 H7 +247: PIeS 10 H7 +248: PuNI 10 H7 +249: PIcS 10 H7 +250: PoIS 10 H7 +251: PaIN 10 H6 +252: PIaN 10 H7 +253: PIN 10 H7 +254: PINe 10 H7 +255: PlIS 10 H7 +256: PaNS 10 H7 +257: PaIS 10 H7 +258: PINs 10 H7 +259: PaIN 10 H7 +260: PInS 10 H7 +261: PIoN 10 H7 +262: PIS 10 H7 +263: SPIn 10 H6 +264: SPIc 10 H6 +265: SPI 10 H6 +266: SlIP 10 H5 +267: SlIP 10 H6 +268: SkIP 10 H5 +269: SkIP 10 H6 +270: SIPo 10 H5 +271: SIPo 10 H6 +272: SPiN 10 H7 +273: SPIs 10 H7 +274: SPIn 10 H7 +275: SPIc 10 H7 +276: SPI 10 H7 +277: SlIP 10 H7 +278: SkIP 10 H7 +279: SIPo 10 H7 +280: zIPS 10 H5 +281: zIPS 10 H6 +282: zIPS 10 H7 +283: tIPS 10 H5 +284: tIPS 10 H6 +285: tIPS 10 H7 +286: sPIS 10 H5 +287: sPIN 10 H5 +288: sPIS 10 H6 +289: sPIN 10 H6 +290: sPIS 10 H7 +291: sPIN 10 H7 +292: SPiN 10 H5 +293: SPIs 10 H5 +294: SPIn 10 H5 +295: SPIc 10 H5 +296: SPiN 10 H6 +297: SPIs 10 H6 +298: PIlS 10 H5 +299: PIfS 10 H5 +300: PIeS 10 H5 +301: PIcS 10 H5 +302: PIaN 10 H5 +303: PISe 10 H6 +304: PIS 10 H6 +305: PIoN 10 H6 +306: PInS 10 H6 +307: PINs 10 H6 +308: PINe 10 H6 +309: PIN 10 H6 +310: PIlS 10 H6 +311: PIfS 10 H6 +312: PIeS 10 H6 +313: PIcS 10 H6 +314: PIaN 10 H6 +315: PuNI 10 H5 +316: PuIS 10 H5 +317: PuNI 10 H6 +318: PuIS 10 H6 +319: PSI 10 H6 +320: PrIS 10 H5 +321: PrIS 10 H6 +322: PoIS 10 H5 +323: PoIS 10 H6 +324: PlIS 10 H5 +325: PlIS 10 H6 +326: PiNS 10 H5 +327: PiNS 10 H6 +328: PISe 10 H5 +329: PIoN 10 H5 +330: PInS 10 H5 +331: PINs 10 H5 +332: PINe 10 H5 +333: SPiN 10 H8 +334: SPIs 10 H8 +335: lISP 10 H8 +336: SPIn 10 H8 +337: SPIc 10 H8 +338: SPI 10 H8 +339: kIPS 10 H8 +340: PaIN 10 H8 +341: PaIS 10 H8 +342: SlIP 10 H8 +343: IPeS 10 H8 +344: SkIP 10 H8 +345: PaNS 10 H8 +346: SIPo 10 H8 +347: PIaN 10 H8 +348: PIcS 10 H8 +349: PIeS 10 H8 +350: ePIS 10 H5 +351: ePIS 10 H6 +352: ePIS 10 H7 +353: bIPS 10 H5 +354: bIPS 10 H6 +355: bIPS 10 H7 +356: aSPI 10 H5 +357: aSPI 10 H6 +358: aPIS 10 H5 +359: aPIS 10 H6 +360: aSPI 10 H7 +361: aPIS 10 H7 +362: zIPS 10 H8 +363: tIPS 10 H8 +364: sPIS 10 H8 +365: mIPS 10 H8 +366: sPIN 10 H8 +367: PSI 10 H8 +368: PrIS 10 H8 +369: PoIS 10 H8 +370: PlIS 10 H8 +371: PIfS 10 H8 +372: bIPS 10 H8 +373: aSPI 10 H8 +374: IPeS 10 H7 +375: PIlS 10 H8 +376: aPIS 10 H8 +377: PIN 10 H8 +378: PiNS 10 H8 +379: PINe 10 H8 +380: PINs 10 H8 +381: PInS 10 H8 +382: PISe 10 H8 +383: PIS 10 H8 +384: PIoN 10 H8 +385: PuNI 10 H8 +386: PuIS 10 H8 +387: ePIS 10 H8 +388: bIP 8 H6 +389: SPa 8 H7 +390: PuS 8 H7 +391: bIP 8 H7 +392: PaN 8 H6 +393: PaS 8 H6 +394: PIn 8 H8 +395: PiN 8 H8 +396: SPi 8 H7 +397: aPI 8 H6 +398: PIe 8 H8 +399: PhI 8 H6 +400: SeP 8 H8 +401: PIc 8 H6 +402: aPI 8 H7 +403: PuS 8 H8 +404: ePI 8 H6 +405: PIf 8 H8 +406: PIn 8 H7 +407: ePI 8 H7 +408: tIP 8 H6 +409: tIP 8 H7 +410: PIs 8 H7 +411: PIu 8 H7 +412: PiN 8 H7 +413: PiS 8 H7 +414: PlI 8 H7 +415: PIs 8 H8 +416: PIu 8 H8 +417: PSi 8 H7 +418: PSt 8 H7 +419: PSy 8 H7 +420: PsI 8 H7 +421: PI 8 H8 +422: PiS 8 H8 +423: PiS 8 H6 +424: PhI 8 H8 +425: PlI 8 H6 +426: PuS 8 H6 +427: PlI 8 H8 +428: PaS 8 H8 +429: SPa 8 H8 +430: SeP 8 H6 +431: PSt 8 H8 +432: PaN 8 H8 +433: PsI 8 H6 +434: PSi 8 H8 +435: PSi 8 H6 +436: PSt 8 H6 +437: PSy 8 H6 +438: PIe 8 H6 +439: sPI 8 H6 +440: PIf 8 H6 +441: PsI 8 H8 +442: zIP 8 H8 +443: PIn 8 H6 +444: sPI 8 H7 +445: PIs 8 H6 +446: PIu 8 H6 +447: PIc 8 H8 +448: tIP 8 H8 +449: SeP 8 H7 +450: SPa 8 H6 +451: sPI 8 H8 +452: SPi 8 H6 +453: PSy 8 H8 +454: SPi 8 H8 +455: PiN 8 H6 +456: kIP 8 H6 +457: aPI 8 H8 +458: hIP 8 H7 +459: hIP 8 H6 +460: bIP 8 H8 +461: kIP 8 H7 +462: kIP 8 H8 +463: IPe 8 H8 +464: IPe 8 H7 +465: hIP 8 H8 +466: IPe 8 H6 +467: PaN 8 H7 +468: PaS 8 H7 +469: zIP 8 H7 +470: PIc 8 H7 +471: zIP 8 H6 +472: ePI 8 H8 +473: PIe 8 H7 +474: PIf 8 H7 +475: PhI 8 H7 +476: PI 8 H7 +477: SkIN 6 H8 +478: NIeS 6 H7 +479: SNIf 6 H8 +480: NIfS 6 H7 +481: SoIN 6 H8 +482: mINS 6 H6 483: NItS 6 H7 484: SeIN 6 H6 -485: yINS 6 H8 -486: SaIN 6 H5 -487: NuIS 6 H7 -488: SaIN 6 H6 +485: lINS 6 H7 +486: lINS 6 H8 +487: SaIN 6 H7 +488: NuIS 6 H7 489: NaIS 6 H6 -490: NaIS 6 H5 -491: tINS 6 H6 -492: vINS 6 H8 -493: tINS 6 H5 -494: NIaS 6 H6 -495: SpIN 6 H7 -496: NIdS 6 H6 -497: uNIS 6 H8 -498: NIeS 6 H6 -499: NIfS 6 H6 -500: NItS 6 H6 -501: NIaS 6 H5 -502: NIdS 6 H5 -503: NIeS 6 H5 -504: NIfS 6 H5 -505: lINS 6 H6 -506: SkIN 6 H5 -507: aNIS 6 H5 -508: lINS 6 H5 -509: lINS 6 H7 -510: aNIS 6 H6 -511: SkIN 6 H6 -512: mINS 6 H7 -513: SNIf 6 H6 -514: mINS 6 H6 -515: SNIf 6 H5 -516: SIeN 6 H5 -517: mINS 6 H5 -518: NaIS 6 H7 -519: aNIS 6 H7 -520: tINS 6 H7 -521: NIaS 6 H7 -522: SIeN 6 H6 -523: NIdS 6 H7 -524: SeIN 6 H5 -525: NIeS 6 H7 -526: NIfS 6 H7 -527: IoNS 6 H5 -528: IoNS 6 H6 -529: uNIS 6 H5 -530: SoIN 6 H8 -531: SNIf 6 H8 -532: SoIN 6 H5 -533: INSu 6 H5 -534: SkIN 6 H8 -535: vINS 6 H7 -536: vINS 6 H6 -537: Pi 6 H7 -538: INSu 6 H6 -539: vINS 6 H5 -540: SIeN 6 H8 -541: yINS 6 H7 -542: Pu 6 H7 -543: SeIN 6 H8 -544: yINS 6 H6 -545: SaIN 6 H8 -546: yINS 6 H5 -547: pINS 6 H8 -548: NItS 6 H5 -549: tINS 6 H8 -550: SpIN 6 H8 -551: NuIS 6 H6 -552: uNIS 6 H6 -553: uNIS 6 H7 -554: NuIS 6 H5 -555: oINS 6 H5 -556: oINS 6 H6 -557: oINS 6 H7 -558: SoIN 6 H6 -559: vIS 4 H6 -560: SoN 4 H6 -561: yIN 4 H7 -562: SpI 4 H6 -563: SkI 4 H6 -564: SkI 4 H7 -565: SIx 4 H7 -566: uNI 4 H7 -567: SIs 4 H7 -568: SIr 4 H7 -569: yIN 4 H6 -570: SIl 4 H7 -571: uNS 4 H6 -572: tIN 4 H6 -573: sIS 4 H7 -574: vIN 4 H7 -575: SeN 4 H6 -576: SaI 4 H6 -577: SoI 4 H6 -578: uNI 4 H6 -579: uNS 4 H7 -580: SIc 4 H6 -581: vIS 4 H7 -582: sIS 4 H6 -583: SIl 4 H6 -584: vIN 4 H6 -585: SoN 4 H7 -586: tIN 4 H7 -587: SIr 4 H6 -588: SoI 4 H7 -589: SIs 4 H6 -590: SpI 4 H7 -591: SIx 4 H6 -592: aIS 4 H7 -593: yIN 4 H8 -594: vIS 4 H8 -595: vIN 4 H8 -596: uNS 4 H8 -597: uNI 4 H8 -598: tIN 4 H8 -599: sIS 4 H8 -600: SpI 4 H8 -601: SoN 4 H8 -602: SoI 4 H8 -603: SkI 4 H8 -604: SIx 4 H8 -605: SIs 4 H8 -606: SIr 4 H8 -607: SIl 4 H8 -608: SIc 4 H8 -609: SI 4 H8 -610: gIS 4 H6 -611: gIN 4 H6 -612: gIS 4 H7 -613: gIN 4 H7 -614: fIS 4 H6 -615: fIN 4 H6 -616: fIS 4 H7 -617: fIN 4 H7 -618: dIS 4 H6 -619: dIN 4 H6 -620: dIS 4 H7 -621: dIN 4 H7 -622: cIS 4 H6 -623: cIS 4 H7 -624: bIS 4 H6 -625: bIS 4 H7 -626: aNS 4 H6 -627: aIS 4 H6 -628: aNS 4 H7 -629: lIS 4 H8 -630: lIN 4 H8 -631: kSI 4 H8 -632: ISo 4 H8 -633: IoN 4 H8 -634: IN 4 H8 -635: IlS 4 H8 -636: IfS 4 H8 -637: IbN 4 H8 -638: gIS 4 H8 -639: gIN 4 H8 -640: fIS 4 H8 -641: fIN 4 H8 -642: dIS 4 H8 -643: dIN 4 H8 -644: cIS 4 H8 -645: bIS 4 H8 -646: aNS 4 H8 -647: aIS 4 H8 -648: SeN 4 H8 -649: SaI 4 H8 -650: rIS 4 H8 -651: pSI 4 H8 -652: pIS 4 H8 -653: pIN 4 H8 -654: NuS 4 H8 -655: NuI 4 H8 -656: NoS 4 H8 -657: NIt 4 H8 -658: NIf 4 H8 -659: NIe 4 H8 -660: NId 4 H8 -661: NIb 4 H8 -662: NIa 4 H8 -663: NI 4 H8 -664: NeS 4 H8 -665: mIS 4 H8 -666: mIN 4 H8 -667: lIS 4 H6 -668: mIN 4 H7 -669: pSI 4 H6 -670: mIS 4 H7 -671: mIN 4 H6 -672: pIS 4 H6 -673: pIN 4 H6 -674: mIS 4 H6 -675: pSI 4 H7 -676: NeS 4 H7 -677: pIS 4 H7 -678: pIN 4 H7 -679: NI 4 H7 -680: NIa 4 H7 -681: NIb 4 H7 -682: NId 4 H7 -683: NIe 4 H7 -684: NIf 4 H7 -685: NIt 4 H7 -686: NoS 4 H7 -687: NuI 4 H7 -688: NuS 4 H6 -689: NuI 4 H6 -690: NuS 4 H7 -691: NeS 4 H6 -692: NoS 4 H6 -693: NIa 4 H6 -694: NIt 4 H6 -695: NIf 4 H6 -696: NIe 4 H6 -697: NIb 4 H6 -698: NId 4 H6 -699: IbN 4 H7 -700: SIc 4 H7 -701: IfS 4 H7 -702: IlS 4 H7 -703: IN 4 H7 -704: SI 4 H7 -705: SeN 4 H7 -706: IoN 4 H7 -707: ISo 4 H7 -708: IbN 4 H6 -709: IfS 4 H6 -710: IlS 4 H6 -711: IoN 4 H6 -712: ISo 4 H6 -713: SaI 4 H7 -714: rIS 4 H6 -715: lIN 4 H7 -716: lIS 4 H7 -717: rIS 4 H7 -718: kSI 4 H6 -719: lIN 4 H6 -720: kSI 4 H7 -721: rI 2 H7 -722: aN 2 H8 -723: hI 2 H8 -724: Si 2 H7 -725: aI 2 H8 -726: aS 2 H8 -727: bI 2 H8 -728: Ne 2 H8 -729: Na 2 H8 -730: If 2 H8 -731: nI 2 H7 -732: cI 2 H8 -733: mI 2 H8 -734: oN 2 H7 -735: oS 2 H7 -736: lI 2 H8 -737: pI 2 H7 -738: fI 2 H8 -739: Il 2 H8 -740: iN 2 H8 -741: eS 2 H8 -742: eN 2 H8 -743: Se 2 H7 -744: In 2 H8 -745: Sa 2 H7 -746: uN 2 H8 -747: uN 2 H7 -748: mI 2 H7 -749: uS 2 H8 -750: vS 2 H8 -751: xI 2 H8 -752: aI 2 H7 -753: aN 2 H7 -754: lI 2 H7 -755: aS 2 H7 -756: bI 2 H7 -757: iN 2 H7 -758: cI 2 H7 -759: sI 2 H7 -760: eN 2 H7 -761: eS 2 H7 -762: fI 2 H7 -763: In 2 H7 -764: Il 2 H7 -765: If 2 H7 -766: hI 2 H7 -767: Su 2 H7 -768: Ni 2 H8 -769: No 2 H8 -770: Nu 2 H8 -771: nI 2 H8 -772: oN 2 H8 -773: oS 2 H8 -774: pI 2 H8 -775: Nu 2 H7 -776: rI 2 H8 -777: No 2 H7 -778: Sa 2 H8 -779: Ni 2 H7 -780: Se 2 H8 -781: xI 2 H7 -782: vS 2 H7 -783: Si 2 H8 -784: Ne 2 H7 -785: Na 2 H7 -786: Su 2 H8 -787: sI 2 H8 -788: uS 2 H7 +490: Pu 6 H8 +491: IbNS 6 H7 +492: mINS 6 H7 +493: NaIS 6 H5 +494: fINS 6 H8 +495: SpIN 6 H8 +496: oINS 6 H8 +497: aSIN 6 H8 +498: NuIS 6 H8 +499: NIaS 6 H6 +500: mINS 6 H8 +501: gINS 6 H5 +502: SeIN 6 H7 +503: tINS 6 H8 +504: SaIN 6 H5 +505: SIeN 6 H5 +506: INSu 6 H7 +507: lINS 6 H6 +508: qINS 6 H8 +509: SkIN 6 H6 +510: SaIN 6 H8 +511: SIeN 6 H6 +512: IoNS 6 H7 +513: SeIN 6 H8 +514: SkIN 6 H5 +515: lINS 6 H5 +516: mINS 6 H5 +517: gINS 6 H8 +518: qINS 6 H7 +519: SIeN 6 H8 +520: NaIS 6 H7 +521: pINS 6 H5 +522: SNIf 6 H6 +523: pINS 6 H6 +524: SNIf 6 H5 +525: SoIN 6 H6 +526: pINS 6 H7 +527: qINS 6 H6 +528: SoIN 6 H5 +529: pINS 6 H8 +530: qINS 6 H5 +531: IbNS 6 H8 +532: SeIN 6 H5 +533: INSu 6 H8 +534: NIaS 6 H7 +535: IoNS 6 H8 +536: NIdS 6 H7 +537: oINS 6 H5 +538: oINS 6 H6 +539: Pu 6 H7 +540: SNIf 6 H7 +541: NIaS 6 H8 +542: vINS 6 H7 +543: oINS 6 H7 +544: Pi 6 H8 +545: IbNS 6 H5 +546: NuIS 6 H5 +547: SkIN 6 H7 +548: aSIN 6 H5 +549: NuIS 6 H6 +550: vINS 6 H6 +551: aSIN 6 H6 +552: IbNS 6 H6 +553: NItS 6 H5 +554: NaIS 6 H8 +555: fINS 6 H7 +556: tINS 6 H5 +557: uNIS 6 H7 +558: fINS 6 H6 +559: tINS 6 H6 +560: INSu 6 H6 +561: INSu 6 H5 +562: Pi 6 H7 +563: fINS 6 H5 +564: tINS 6 H7 +565: uNIS 6 H6 +566: IoNS 6 H6 +567: IoNS 6 H5 +568: SoIN 6 H7 +569: gINS 6 H7 +570: uNIS 6 H5 +571: vINS 6 H8 +572: NItS 6 H8 +573: SaIN 6 H6 +574: SIeN 6 H7 +575: NIdS 6 H6 +576: NIfS 6 H8 +577: yINS 6 H6 +578: yINS 6 H8 +579: NIeS 6 H6 +580: yINS 6 H7 +581: NIeS 6 H8 +582: yINS 6 H5 +583: aNIS 6 H7 +584: SpIN 6 H7 +585: NIfS 6 H6 +586: vINS 6 H5 +587: NItS 6 H6 +588: NIaS 6 H5 +589: NIdS 6 H5 +590: SpIN 6 H5 +591: NIeS 6 H5 +592: SpIN 6 H6 +593: aSIN 6 H7 +594: gINS 6 H6 +595: NIdS 6 H8 +596: aNIS 6 H6 +597: aNIS 6 H8 +598: uNIS 6 H8 +599: aNIS 6 H5 +600: NIfS 6 H5 +601: dIS 4 H8 +602: aIS 4 H8 +603: dIN 4 H8 +604: SaI 4 H6 +605: aNS 4 H8 +606: SpI 4 H7 +607: SIc 4 H6 +608: bIS 4 H8 +609: SeN 4 H6 +610: cIS 4 H8 +611: NoS 4 H8 +612: SpI 4 H6 +613: NIt 4 H8 +614: sIS 4 H7 +615: yIN 4 H6 +616: NIf 4 H8 +617: sIS 4 H6 +618: yIN 4 H7 +619: NIe 4 H8 +620: NId 4 H8 +621: vIS 4 H6 +622: vIN 4 H6 +623: NIb 4 H8 +624: vIS 4 H7 +625: vIN 4 H7 +626: uNS 4 H6 +627: NIa 4 H8 +628: NI 4 H8 +629: tIN 4 H7 +630: uNI 4 H6 +631: NeS 4 H8 +632: tIN 4 H6 +633: uNS 4 H7 +634: uNI 4 H7 +635: fIN 4 H8 +636: fIS 4 H8 +637: SIl 4 H6 +638: SIr 4 H6 +639: SIs 4 H6 +640: SIx 4 H6 +641: SkI 4 H6 +642: gIN 4 H8 +643: gIS 4 H8 +644: SoI 4 H6 +645: SoN 4 H6 +646: IbN 4 H8 +647: IfS 4 H8 +648: IlS 4 H8 +649: IN 4 H8 +650: IoN 4 H8 +651: ISo 4 H8 +652: kSI 4 H8 +653: lIN 4 H8 +654: lIS 4 H8 +655: mIN 4 H8 +656: NuS 4 H8 +657: NuI 4 H8 +658: mIS 4 H8 +659: fIS 4 H7 +660: fIN 4 H7 +661: IlS 4 H7 +662: IN 4 H7 +663: dIS 4 H6 +664: dIN 4 H6 +665: dIS 4 H7 +666: dIN 4 H7 +667: cIS 4 H6 +668: cIS 4 H7 +669: mIS 4 H6 +670: bIS 4 H6 +671: bIS 4 H7 +672: mIN 4 H6 +673: aNS 4 H6 +674: aIS 4 H6 +675: mIS 4 H7 +676: aNS 4 H7 +677: aIS 4 H7 +678: yIN 4 H8 +679: mIN 4 H7 +680: vIS 4 H8 +681: vIN 4 H8 +682: IoN 4 H7 +683: uNS 4 H8 +684: uNI 4 H8 +685: lIS 4 H6 +686: lIN 4 H6 +687: NIt 4 H7 +688: NIf 4 H7 +689: NIe 4 H7 +690: NoS 4 H7 +691: NId 4 H7 +692: NuI 4 H7 +693: NuS 4 H7 +694: NIb 4 H7 +695: IbN 4 H7 +696: NeS 4 H6 +697: NIa 4 H6 +698: NIa 4 H7 +699: NIb 4 H6 +700: NId 4 H6 +701: NIe 4 H6 +702: gIS 4 H6 +703: NIf 4 H6 +704: NI 4 H7 +705: NIt 4 H6 +706: NoS 4 H6 +707: NuI 4 H6 +708: NuS 4 H6 +709: IfS 4 H7 +710: NeS 4 H7 +711: gIN 4 H6 +712: gIS 4 H7 +713: gIN 4 H7 +714: fIS 4 H6 +715: fIN 4 H6 +716: SI 4 H8 +717: SeN 4 H8 +718: kSI 4 H7 +719: SaI 4 H8 +720: IbN 4 H6 +721: rIS 4 H8 +722: qIN 4 H8 +723: pSI 4 H8 +724: pIS 4 H8 +725: pIN 4 H8 +726: ISo 4 H6 +727: qIN 4 H7 +728: qIN 4 H6 +729: IoN 4 H6 +730: rIS 4 H7 +731: rIS 4 H6 +732: SaI 4 H7 +733: IlS 4 H6 +734: SeN 4 H7 +735: SI 4 H7 +736: SIc 4 H7 +737: SIl 4 H7 +738: SIr 4 H7 +739: IfS 4 H6 +740: SIs 4 H7 +741: SIx 4 H7 +742: SkI 4 H7 +743: SoI 4 H7 +744: SoN 4 H7 +745: tIN 4 H8 +746: sIS 4 H8 +747: lIS 4 H7 +748: lIN 4 H7 +749: kSI 4 H6 +750: SpI 4 H8 +751: SoN 4 H8 +752: SoI 4 H8 +753: SkI 4 H8 +754: ISo 4 H7 +755: SIx 4 H8 +756: SIs 4 H8 +757: SIr 4 H8 +758: SIl 4 H8 +759: SIc 4 H8 +760: pIN 4 H7 +761: pIS 4 H7 +762: pSI 4 H7 +763: pIN 4 H6 +764: pSI 4 H6 +765: pIS 4 H6 +766: cI 2 H8 +767: If 2 H7 +768: Na 2 H7 +769: aI 2 H8 +770: hI 2 H8 +771: eN 2 H8 +772: lI 2 H7 +773: bI 2 H8 +774: In 2 H8 +775: mI 2 H7 +776: Il 2 H7 +777: eS 2 H8 +778: fI 2 H8 +779: aN 2 H8 +780: In 2 H7 +781: Ne 2 H7 +782: iN 2 H7 +783: aS 2 H8 +784: Il 2 H8 +785: If 2 H8 +786: xI 2 H8 +787: vS 2 H8 +788: sI 2 H7 +789: Ni 2 H8 +790: uS 2 H8 +791: No 2 H8 +792: uN 2 H8 +793: Nu 2 H8 +794: sI 2 H8 +795: nI 2 H8 +796: Su 2 H8 +797: oN 2 H8 +798: oS 2 H8 +799: Si 2 H8 +800: pI 2 H7 +801: Se 2 H8 +802: Sa 2 H8 +803: rI 2 H8 +804: pI 2 H8 +805: rI 2 H7 +806: Sa 2 H7 +807: Se 2 H7 +808: Su 2 H7 +809: Si 2 H7 +810: iN 2 H8 +811: Ni 2 H7 +812: No 2 H7 +813: lI 2 H8 +814: Nu 2 H7 +815: mI 2 H8 +816: hI 2 H7 +817: Na 2 H8 +818: xI 2 H7 +819: vS 2 H7 +820: nI 2 H7 +821: oN 2 H7 +822: oS 2 H7 +823: uS 2 H7 +824: uN 2 H7 +825: fI 2 H7 +826: eS 2 H7 +827: Ne 2 H8 +828: eN 2 H7 +829: cI 2 H7 +830: bI 2 H7 +831: aS 2 H7 +832: aN 2 H7 +833: aI 2 H7 commande> j QuIZ H6 commande> t E? commande> r commande> a r 50 1: ZEn 12 9H - 2: ZEf 12 9H - 3: ZEe 12 9H - 4: ZEc 12 9H - 5: rEZ 12 9F - 6: nEZ 12 9F - 7: lEZ 12 9F - 8: fEZ 12 9F - 9: ZoE 11 9H - 10: ZeE 11 9H - 11: QuE 11 6H - 12: Eh 5 G7 - 13: En 5 G7 - 14: Es 5 G7 - 15: Ex 5 G7 - 16: pIE 2 8G - 17: oIE 2 8G - 18: nIE 2 8G - 19: mIE 2 8G - 20: lIE 2 8G - 21: hIE 2 8G - 22: fIE 2 8G - 23: aIE 2 8G - 24: pEu 2 7F - 25: lEu 2 7F - 26: jEu 2 7F - 27: hEu 2 7F - 28: fEu 2 7F - 29: rIE 2 8G - 30: vIE 2 8G - 31: EpI 2 8F - 32: lEI 2 8F - 33: IdE 2 8H - 34: IlE 2 8H - 35: IpE 2 8H - 36: IrE 2 8H - 37: IvE 2 8H - 38: IxE 2 8H - 39: Eu 2 7G - 40: duE 2 7G - 41: buE 2 7G - 42: Eue 2 7G - 43: Euh 2 7G - 44: Eus 2 7G - 45: Eut 2 7G - 46: Eux 2 7G - 47: euE 2 7G - 48: guE 2 7G - 49: huE 2 7G - 50: luE 2 7G + 2: ZEk 12 9H + 3: ZEf 12 9H + 4: ZEe 12 9H + 5: ZEc 12 9H + 6: rEZ 12 9F + 7: nEZ 12 9F + 8: lEZ 12 9F + 9: fEZ 12 9F + 10: ZoE 11 9H + 11: ZeE 11 9H + 12: QuE 11 6H + 13: Eh 5 G7 + 14: En 5 G7 + 15: Es 5 G7 + 16: Ex 5 G7 + 17: pIE 2 8G + 18: oIE 2 8G + 19: nIE 2 8G + 20: mIE 2 8G + 21: lIE 2 8G + 22: hIE 2 8G + 23: fIE 2 8G + 24: aIE 2 8G + 25: pEu 2 7F + 26: lEu 2 7F + 27: jEu 2 7F + 28: hEu 2 7F + 29: fEu 2 7F + 30: rIE 2 8G + 31: vIE 2 8G + 32: EpI 2 8F + 33: lEI 2 8F + 34: IdE 2 8H + 35: IlE 2 8H + 36: IpE 2 8H + 37: IrE 2 8H + 38: IvE 2 8H + 39: IxE 2 8H + 40: Eue 2 7G + 41: Eu 2 7G + 42: duE 2 7G + 43: buE 2 7G + 44: suE 2 7G + 45: ruE 2 7G + 46: tuE 2 7G + 47: quE 2 7G + 48: puE 2 7G + 49: vuE 2 7G + 50: nuE 2 7G commande> q fin du mode entraînement commande> q diff --git a/utils/Makefile.am b/utils/Makefile.am index cf44c0f..03104f5 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -16,24 +16,26 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -top_srcdir = @top_srcdir@ +localedir = $(datadir)/locale +AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" + +INCLUDES = -I$(top_srcdir)/dic -I$(top_srcdir)/game -I../intl -I$(top_srcdir)/intl -INCLUDES = -I$(top_srcdir)/dic -I$(top_srcdir)/game -I$(top_srcdir)/intl noinst_PROGRAMS = +bin_PROGRAMS = if BUILD_TEXT noinst_PROGRAMS += eliottxt eliottxt_SOURCES = game_io.h game_io.cpp eliottxt.cpp -eliottxt_LDADD = ../game/libgame.a ../dic/libdic.a -lreadline +eliottxt_LDADD = $(top_builddir)/game/libgame.a $(top_builddir)/dic/libdic.a @LIBINTL@ +if HAS_READLINE +eliottxt_LDADD += -lreadline +endif endif if BUILD_NCURSES -noinst_PROGRAMS += eliotcurses -eliotcurses_SOURCES = ncurses.cpp -eliotcurses_LDADD = ../game/libgame.a ../dic/libdic.a -lncurses @LIBINTL@ +bin_PROGRAMS += eliotcurses +eliotcurses_SOURCES = ncurses.cpp ncurses.h +eliotcurses_LDADD = ../game/libgame.a ../dic/libdic.a -lncursesw @LIBINTL@ endif -datadir = @datadir@ -localedir = $(datadir)/locale -DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ - diff --git a/utils/eliottxt.cpp b/utils/eliottxt.cpp index 1c0c54a..a4f8b80 100644 --- a/utils/eliottxt.cpp +++ b/utils/eliottxt.cpp @@ -18,19 +18,23 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +#include "config.h" + +#include +#include +#include #include -#include #include #include #include #include -#include -#include -#include -#include +#if HAVE_READLINE_READLINE_H +# include +# include +# include +#endif #include "dic.h" -#include "dic_search.h" #include "regexp.h" #include "game_io.h" #include "game_factory.h" @@ -41,8 +45,6 @@ /* A static variable for holding the line. */ -static char *line_read = NULL; -/* Wide version of the line */ static wchar_t *wline_read = NULL; /** @@ -51,20 +53,16 @@ static wchar_t *wline_read = NULL; */ wchar_t *rl_gets() { +#if HAVE_READLINE_READLINE_H // If the buffer has already been allocated, return the memory to the free // pool - if (line_read) - { - free(line_read); - line_read = NULL; - } if (wline_read) { delete[] wline_read; - wline_read = NULL; } // Get a line from the user + static char *line_read; line_read = readline("commande> "); // If the line has any text in it, save it on the history @@ -78,7 +76,28 @@ wchar_t *rl_gets() return NULL; wline_read = new wchar_t[len + 1]; - len = mbstowcs(wline_read, line_read, len + 1); + mbstowcs(wline_read, line_read, len + 1); + + if (line_read) + { + free(line_read); + } +#else + if (!cin.good()) + return NULL; + + cout << "commande> "; + string line; + std::getline(cin, line); + + // Get the needed length (we _can't_ use string::size()) + size_t len = mbstowcs(NULL, line.c_str(), 0); + if (len == (size_t)-1) + return NULL; + + wline_read = new wchar_t[len + 1]; + mbstowcs(wline_read, line.c_str(), len + 1); +#endif return wline_read; } @@ -86,7 +105,7 @@ wchar_t *rl_gets() wchar_t * next_token_alpha(wchar_t *cmd, const wchar_t *delim, wchar_t **state) { - wchar_t *token = wcstok(cmd, delim, state); + wchar_t *token = _wcstok(cmd, delim, state); if (token == NULL) return NULL; int i; @@ -99,7 +118,7 @@ wchar_t * next_token_alpha(wchar_t *cmd, const wchar_t *delim, wchar_t **state) wchar_t * next_token_alphanum(wchar_t *cmd, const wchar_t *delim, wchar_t **state) { - wchar_t *token = wcstok(cmd, delim, state); + wchar_t *token = _wcstok(cmd, delim, state); if (token == NULL) return NULL; int i; @@ -112,7 +131,7 @@ wchar_t * next_token_alphanum(wchar_t *cmd, const wchar_t *delim, wchar_t **stat wchar_t * next_token_alphaplusjoker(wchar_t *cmd, const wchar_t *delim, wchar_t **state) { - wchar_t *token = wcstok(cmd, delim, state); + wchar_t *token = _wcstok(cmd, delim, state); if (token == NULL) return NULL; int i; @@ -128,7 +147,7 @@ wchar_t * next_token_alphaplusjoker(wchar_t *cmd, const wchar_t *delim, wchar_t wchar_t * next_token_digit(wchar_t *cmd, const wchar_t *delim, wchar_t **state) { - wchar_t *token = wcstok(cmd, delim, state); + wchar_t *token = _wcstok(cmd, delim, state); if (token == NULL) return NULL; int i; @@ -141,7 +160,7 @@ wchar_t * next_token_digit(wchar_t *cmd, const wchar_t *delim, wchar_t **state) wchar_t * next_token_cross(wchar_t *cmd, const wchar_t *delim, wchar_t **state) { - wchar_t *token = wcstok(cmd, delim, state); + wchar_t *token = _wcstok(cmd, delim, state); if (token == NULL) return NULL; int i; @@ -156,7 +175,7 @@ wchar_t * next_token_cross(wchar_t *cmd, const wchar_t *delim, wchar_t **state) wchar_t * next_token_filename(wchar_t *cmd, const wchar_t *delim, wchar_t **state) { - wchar_t *token = wcstok(cmd, delim, state); + wchar_t *token = _wcstok(cmd, delim, state); if (token == NULL) return NULL; int i; @@ -169,13 +188,15 @@ wchar_t * next_token_filename(wchar_t *cmd, const wchar_t *delim, wchar_t **stat } -void eliottxt_get_cross(const Dictionary &iDic, wchar_t *cros) +void eliottxt_get_cross(const Dictionary &iDic, const wstring &iCros) { - wchar_t wordlist[RES_CROS_MAX][DIC_WORD_MAX]; - Dic_search_Cros(iDic, cros, wordlist); - for (int i = 0; i < RES_CROS_MAX && wordlist[i][0]; i++) + list wordList; + iDic.searchCross(iCros, wordList); + + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) { - printf(" %s\n", convertToMb(wordlist[i]).c_str()); + printf(" %s\n", convertToMb(*it).c_str()); } } @@ -198,6 +219,10 @@ void help_training() printf(" S -- score de tous les joueurs\n"); printf(" t -- tirage\n"); printf(" d [] : vérifier le mot []\n"); + printf(" b [b|p|r] [] : effectuer une recherche speciale à partir de []\n"); + printf(" b -- benjamins\n"); + printf(" p -- 7 + 1\n"); + printf(" r -- raccords\n"); printf(" * : tirage aléatoire\n"); printf(" + : tirage aléatoire ajouts\n"); printf(" t [] : changer le tirage\n"); @@ -278,7 +303,7 @@ void help() void display_data(const Game &iGame, const wchar_t *delim, wchar_t **state) { - wchar_t *token; + const wchar_t *token; token = next_token_alpha(NULL, delim, state); if (token == NULL) @@ -365,7 +390,7 @@ void display_data(const Game &iGame, const wchar_t *delim, wchar_t **state) void loop_training(Training &iGame) { - wchar_t *token; + const wchar_t *token; wchar_t *state; wchar_t *commande = NULL; wchar_t delim[] = L" \t"; @@ -376,7 +401,7 @@ void loop_training(Training &iGame) while (quit == 0) { commande = rl_gets(); - token = wcstok(commande, delim, &state); + token = _wcstok(commande, delim, &state); if (token) { switch (token[0]) @@ -387,13 +412,65 @@ void loop_training(Training &iGame) case L'a': display_data(iGame, delim, &state); break; + case L'b': + token = next_token_alpha(NULL, delim, &state); + if (token == NULL) + help_training(); + else + { + const wchar_t *word = next_token_alpha(NULL, delim, &state); + if (word == NULL) + help_training(); + else + { + switch (token[0]) + { + case L'b': + { + list wordList; + iGame.getDic().searchBenj(word, wordList); + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); ++it) + cout << convertToMb(*it) << endl; + break; + } + case L'p': + { + map > wordMap; + iGame.getDic().search7pl1(word, wordMap, false); + map >::const_iterator it; + for (it = wordMap.begin(); it != wordMap.end(); ++it) + { + if (it->first) + cout << "+" << convertToMb(it->first) << endl; + list::const_iterator itWord;; + for (itWord = it->second.begin(); itWord != it->second.end(); itWord++) + { + cout << " " << convertToMb(*itWord) << endl; + } + } + break; + } + case L'r': + { + list wordList; + iGame.getDic().searchRacc(word, wordList); + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); ++it) + cout << convertToMb(*it) << endl; + break; + } + } + } + } + break; case L'd': token = next_token_alpha(NULL, delim, &state); if (token == NULL) help_training(); else { - if (Dic_search_word(iGame.getDic(), token)) + if (iGame.getDic().searchWord(token)) { printf("le mot -%s- existe\n", convertToMb(token).c_str()); @@ -405,7 +482,7 @@ void loop_training(Training &iGame) } } break; - case L'j': + case L'j': token = next_token_alpha(NULL, delim, &state); if (token == NULL) help_training(); @@ -499,7 +576,7 @@ void loop_training(Training &iGame) void loop_freegame(FreeGame &iGame) { - wchar_t *token; + const wchar_t *token; wchar_t *state; wchar_t *commande = NULL; wchar_t delim[] = L" \t"; @@ -510,7 +587,7 @@ void loop_freegame(FreeGame &iGame) while (quit == 0) { commande = rl_gets(); - token = wcstok(commande, delim, &state); + token = _wcstok(commande, delim, &state); if (token) { switch (token[0]) @@ -527,7 +604,7 @@ void loop_freegame(FreeGame &iGame) help_freegame(); else { - if (Dic_search_word(iGame.getDic(), token)) + if (iGame.getDic().searchWord(token)) { printf("le mot -%s- existe\n", convertToMb(token).c_str()); @@ -539,7 +616,7 @@ void loop_freegame(FreeGame &iGame) } } break; - case L'j': + case L'j': token = next_token_alpha(NULL, delim, &state); if (token == NULL) help_freegame(); @@ -566,7 +643,7 @@ void loop_freegame(FreeGame &iGame) if (token == NULL) token = L""; - if (iGame.pass(token, iGame.currPlayer()) != 0) + if (iGame.pass(token) != 0) break; break; case L's': @@ -600,7 +677,7 @@ void loop_freegame(FreeGame &iGame) void loop_duplicate(Duplicate &iGame) { - wchar_t *token; + const wchar_t *token; wchar_t *state; wchar_t *commande = NULL; wchar_t delim[] = L" \t"; @@ -611,7 +688,7 @@ void loop_duplicate(Duplicate &iGame) while (quit == 0) { commande = rl_gets(); - token = wcstok(commande, delim, &state); + token = _wcstok(commande, delim, &state); if (token) { switch (token[0]) @@ -628,7 +705,7 @@ void loop_duplicate(Duplicate &iGame) help_duplicate(); else { - if (Dic_search_word(iGame.getDic(), token)) + if (iGame.getDic().searchWord(token)) { printf("le mot -%s- existe\n", convertToMb(token).c_str()); @@ -667,10 +744,14 @@ void loop_duplicate(Duplicate &iGame) help_duplicate(); else { + int n = _wtoi(token); + if (n < 0 || n >= (int)iGame.getNPlayers()) + { + fprintf(stderr, "Numéro de joueur invalide\n"); + break; + } int res = iGame.setPlayer(_wtoi(token)); if (res == 1) - fprintf(stderr, "Numéro de joueur invalide\n"); - else if (res == 2) fprintf(stderr, "Impossible de choisir un joueur non humain\n"); } break; @@ -703,7 +784,8 @@ void loop_duplicate(Duplicate &iGame) } -void eliot_regexp_build_default_llist(struct search_RegE_list_t &llist) +void eliot_regexp_build_default_llist(const Dictionary &iDic, + struct search_RegE_list_t &llist) { memset(&llist, 0, sizeof(llist)); @@ -727,8 +809,8 @@ void eliot_regexp_build_default_llist(struct search_RegE_list_t &llist) memset(llist.letters[i], 0, sizeof(llist.letters[i])); } - const list& allTiles = Tile::getAllTiles(); - list::const_iterator it; + const vector& allTiles = iDic.getAllTiles(); + vector::const_iterator it; for (it = allTiles.begin(); it != allTiles.end(); it++) { if (! it->isJoker() && ! it->isEmpty()) @@ -763,16 +845,14 @@ void eliot_regexp(const Dictionary& iDic, wchar_t __attribute__((unused)) *cmd, #define DIC_RE_MAX (3*DIC_WORD_MAX) // yes, it's 3 struct search_RegE_list_t llist; - eliot_regexp_build_default_llist(llist); + eliot_regexp_build_default_llist(iDic, llist); - wchar_t *exp, *cnres, *clmin, *clmax; + wchar_t *regexp = _wcstok(NULL, delim, state); + wchar_t *cnres = _wcstok(NULL, delim, state); + wchar_t *clmin = _wcstok(NULL, delim, state); + wchar_t *clmax = _wcstok(NULL, delim, state); - exp = wcstok(NULL, delim, state); - cnres = wcstok(NULL, delim, state); - clmin = wcstok(NULL, delim, state); - clmax = wcstok(NULL, delim, state); - - if (exp == NULL) + if (regexp == NULL) { return; } @@ -791,18 +871,17 @@ void eliot_regexp(const Dictionary& iDic, wchar_t __attribute__((unused)) *cmd, return; } - wchar_t re[DIC_RE_MAX]; - wcsncpy(re, exp, DIC_RE_MAX); - wchar_t buff[RES_REGE_MAX][DIC_WORD_MAX]; - - printf("search for %s (%d,%d,%d)\n", convertToMb(exp).c_str(), + printf("search for %s (%d,%d,%d)\n", convertToMb(regexp).c_str(), nres, lmin, lmax); - Dic_search_RegE(iDic, re, buff, &llist); + + list wordList; + iDic.searchRegExp(regexp, wordList, &llist); int nresult = 0; - for (int i = 0; i < RES_REGE_MAX && i < nres && buff[i][0]; i++) + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end() && nresult < nres; it++) { - printf("%s\n", convertToMb(buff[i]).c_str()); + printf("%s\n", convertToMb(*it).c_str()); nresult++; } printf("%d printed results\n", nresult); @@ -811,7 +890,7 @@ void eliot_regexp(const Dictionary& iDic, wchar_t __attribute__((unused)) *cmd, void main_loop(const Dictionary &iDic) { - wchar_t *token; + const wchar_t *token; wchar_t *state; wchar_t *commande = NULL; wchar_t delim[] = L" \t"; @@ -821,7 +900,7 @@ void main_loop(const Dictionary &iDic) while (quit == 0) { commande = rl_gets(); - token = wcstok(commande, delim, &state); + token = _wcstok(commande, delim, &state); if (token) { switch (token[0]) @@ -960,13 +1039,11 @@ void main_loop(const Dictionary &iDic) int main(int argc, char *argv[]) { - char dic_path[100]; + string dicPath; // Let the user choose the locale setlocale(LC_ALL, ""); - Dictionary dic = NULL; - if (argc != 2 && argc != 3) { fprintf(stdout, "Usage: eliot /chemin/vers/ods4.dawg [random_seed]\n"); @@ -974,55 +1051,29 @@ int main(int argc, char *argv[]) } else { - strcpy(dic_path, argv[1]); + dicPath = argv[1]; } - switch (Dic_load(&dic, dic_path)) + try { - case 0: - /* Normal case */ - break; - case 1: - printf("chargement: problème d'ouverture de %s\n", argv[1]); - exit(1); - break; - case 2: - printf("chargement: mauvais en-tete de dictionnaire\n"); - exit(2); - break; - case 3: - printf("chargement: problème 3 d'allocation mémoire\n"); - exit(3); - break; - case 4: - printf("chargement: problème 4 d'alocation mémoire\n"); - exit(4); - break; - case 5: - printf("chargement: problème de lecture des arcs du dictionnaire\n"); - exit(5); - break; - default: - printf("chargement: problème non-repertorié\n"); - exit(6); - break; + Dictionary dic(dicPath); + + if (argc == 3) + srand(atoi(argv[2])); + else + srand(time(NULL)); + + main_loop(dic); + GameFactory::Destroy(); + + // Free the readline static variable + if (wline_read) + delete[] wline_read; + } + catch (std::exception &e) + { + cerr << e.what() << endl; } - - if (argc == 3) - srand(atoi(argv[2])); - else - srand(time(NULL)); - - main_loop(dic); - GameFactory::Destroy(); - - Dic_destroy(dic); - - // Free the readline static variable and its wide equivalent - if (line_read) - free(line_read); - if (wline_read) - delete[] wline_read; return 0; } diff --git a/utils/game_io.cpp b/utils/game_io.cpp index 16d4d59..62b52f4 100644 --- a/utils/game_io.cpp +++ b/utils/game_io.cpp @@ -20,8 +20,9 @@ #include #include -#include "stdlib.h" +#include +#include #include "game_io.h" #include "game.h" #include "training.h" @@ -45,8 +46,11 @@ void GameIO::printBoard(ostream &out, const Game &iGame) out << " " << (char)(row - BOARD_MIN + 'A') << " "; for (col = BOARD_MIN; col <= BOARD_MAX; col++) { - char l = iGame.getBoard().getChar(row, col); - out << setw(3) << (l ? l : '-'); + wchar_t l = iGame.getBoard().getChar(row, col); + if (l == 0) + out << " -"; + else + out << padAndConvert(wstring(1, l), 3); } out << endl; } @@ -63,7 +67,7 @@ void GameIO::printBoardDebug(ostream &out, const Game &iGame) out << " " << (char)(row - BOARD_MIN + 'A') << "r "; for (col = BOARD_MIN; col <= BOARD_MAX; col++) { - out << iGame.getBoard().getCellContent_row(row, col); + out << iGame.getBoard().getCellContent_row(row, col); } out << endl; } @@ -73,7 +77,7 @@ void GameIO::printBoardDebug(ostream &out, const Game &iGame) out << " " << (char)(row - BOARD_MIN + 'A') << "c "; for (col = BOARD_MIN; col <= BOARD_MAX; col++) { - out << iGame.getBoard().getCellContent_col(row, col); + out << iGame.getBoard().getCellContent_col(row, col); } out << endl; } @@ -93,11 +97,13 @@ void GameIO::printBoardJoker(ostream &out, const Game &iGame) out << " " << (char)(row - BOARD_MIN + 'A') << " "; for (col = BOARD_MIN; col <= BOARD_MAX; col++) { - char l = iGame.getBoard().getChar(row, col); + wchar_t l = iGame.getBoard().getChar(row, col); bool j = (iGame.getBoard().getCharAttr(row, col) & ATTR_JOKER); - out << " " << (j ? '.' : (l ? ' ' : '-')); - out << (l ? l : '-'); + if (l == 0) + out << " " << (j ? "." : "--"); + else + out << " " << (j ? "." : " ") << convertToMb(l); } out << endl; } @@ -118,9 +124,9 @@ void GameIO::printBoardMultipliers(ostream &out, const Game &iGame) out << " " << (char)(row - BOARD_MIN + 'A') << " "; for (col = BOARD_MIN; col <= BOARD_MAX; col++) { - char l = iGame.getBoard().getChar(row, col); + wchar_t l = iGame.getBoard().getChar(row, col); if (l != 0) - out << " " << l; + out << padAndConvert(wstring(1, l), 3); else { int wm = iGame.getBoard().getWordMultiplier(row, col); @@ -172,12 +178,12 @@ void GameIO::printBoardMultipliers2(ostream &out, const Game &iGame) void GameIO::printNonPlayed(ostream &out, const Game &iGame) { - const list& allTiles = Tile::getAllTiles(); - list::const_iterator it; + const vector& allTiles = iGame.getDic().getAllTiles(); + vector::const_iterator it; for (it = allTiles.begin(); it != allTiles.end(); it++) { - if (iGame.getBag().in(it->toChar()) > 9) + if (iGame.getBag().in(*it) > 9) out << " "; out << setw(2) << convertToMb(it->toChar()); } @@ -185,7 +191,7 @@ void GameIO::printNonPlayed(ostream &out, const Game &iGame) for (it = allTiles.begin(); it != allTiles.end(); it++) { - out << " " << iGame.getBag().in(it->toChar()); + out << " " << iGame.getBag().in(*it); } out << endl; } @@ -199,7 +205,7 @@ void GameIO::printPlayedRack(ostream &out, const Game &iGame, int __UNUSED__ n) void GameIO::printAllRacks(ostream &out, const Game &iGame) { - for (int j = 0; j < iGame.getNPlayers(); j++) + for (unsigned int j = 0; j < iGame.getNPlayers(); j++) { out << "Joueur " << j << ": "; out << convertToMb(iGame.getPlayer(j).getCurrentRack().toString(PlayedRack::RACK_SIMPLE)) << endl; @@ -241,7 +247,7 @@ void GameIO::printPoints(ostream &out, const Game &iGame) void GameIO::printAllPoints(ostream &out, const Game &iGame) { - for (int i = 0; i < iGame.getNPlayers(); i++) + for (unsigned int i = 0; i < iGame.getNPlayers(); i++) { out << "Joueur " << i << ": " << setw(4) << iGame.getPlayer(i).getPoints() << endl; @@ -251,24 +257,24 @@ void GameIO::printAllPoints(ostream &out, const Game &iGame) void GameIO::printGameDebug(ostream &out, const Game &iGame) { - out << "Game:: joueur en cours " << iGame.currPlayer() << " sur " << iGame.getNPlayers() << endl; - out << "Game:: mode " << iGame.getModeAsString() << endl; - out << "Game:: variante "; - switch (iGame.getVariant()) + out << "Game:: joueur en cours " << iGame.currPlayer() << " sur " << iGame.getNPlayers() << endl; + out << "Game:: mode " << iGame.getModeAsString() << endl; + out << "Game:: variante "; + switch (iGame.getVariant()) { - case Game::kNONE: - out << "aucune" << endl; - break; - case Game::kJOKER: - out << "joker" << endl; - break; - default: - out << "inconnu" << endl; - break; + case Game::kNONE: + out << "aucune" << endl; + break; + case Game::kJOKER: + out << "joker" << endl; + break; + default: + out << "inconnu" << endl; + break; } - out << "Game:: rack size " << iGame.RACK_SIZE << endl; - out << "Game:: history --" << endl; - out << convertToMb(iGame.getHistory().toString()); - out << "--" << endl; - out << "" << endl; + out << "Game:: rack size " << iGame.RACK_SIZE << endl; + out << "Game:: history --" << endl; + out << convertToMb(iGame.getHistory().toString()); + out << "--" << endl; + out << "" << endl; } diff --git a/utils/ncurses.cpp b/utils/ncurses.cpp index c22388f..ea371b8 100644 --- a/utils/ncurses.cpp +++ b/utils/ncurses.cpp @@ -30,7 +30,6 @@ #include "ncurses.h" #include "dic.h" -#include "dic_search.h" #include "game_factory.h" #include "training.h" #include "duplicate.h" @@ -43,59 +42,48 @@ using namespace std; -CursesIntf::CursesIntf(WINDOW *win, Game& iGame) - : m_win(win), m_game(&iGame), m_state(DEFAULT), m_dying(false), - m_boxStart(0), m_boxLines(0), m_boxLinesData(0), m_boxY(0), - m_showDots(false) +Box::Box(WINDOW *win, int y, int x, int h, int w, + unsigned int iHeadingLines) + : m_win(win), m_x(x), m_y(y), m_w(w), m_h(h), + m_topLine(y + 1 + iHeadingLines), + m_nbLines(h - 2 - iHeadingLines), m_dataStart(0), m_dataSize(0) { } -CursesIntf::~CursesIntf() +void Box::draw(const string& iTitle) const { - GameFactory::Instance()->releaseGame(*m_game); - GameFactory::Destroy(); -} - - -void CursesIntf::drawBox(WINDOW *win, int y, int x, int h, int w, - const string& iTitle) -{ - if (w > 3 && h > 2) + if (m_w > 3 && m_h > 2) { - int i_len = iTitle.size(); + // Add one space before and after the title for readability + string title; + if (!iTitle.empty()) + title = " " + iTitle + " "; + unsigned int l = title.size(); + // Truncate the title if needed + if ((int)l > m_w - 2) + l = m_w - 2; - if (i_len > w - 2) i_len = w - 2; + mvwaddch(m_win, m_y, m_x, ACS_ULCORNER); + mvwhline(m_win, m_y, m_x + 1, ACS_HLINE, (m_w - l - 2)/2); + mvwprintw(m_win,m_y, m_x + 1 + (m_w - l - 2)/2, "%s", title.c_str()); + mvwhline(m_win, m_y, m_x + (m_w - l)/2 + l, + ACS_HLINE, m_w - 1 - ((m_w - l)/2 + l)); + mvwaddch(m_win, m_y, m_x + m_w - 1, ACS_URCORNER); - mvwaddch(win, y, x, ACS_ULCORNER); - mvwhline(win, y, x+1, ACS_HLINE, ( w-i_len-2)/2); - mvwprintw(win,y, x+1+(w-i_len-2)/2, "%s", iTitle.c_str()); - mvwhline(win, y, x+(w-i_len)/2+i_len, - ACS_HLINE, w - 1 - ((w-i_len)/2+i_len)); - mvwaddch(win, y, x+w-1,ACS_URCORNER); + mvwvline(m_win, m_y + 1, m_x, ACS_VLINE, m_h - 2); + mvwvline(m_win, m_y + 1, m_x + m_w - 1, ACS_VLINE, m_h - 2); - mvwvline(win, y+1, x, ACS_VLINE, h-2); - mvwvline(win, y+1, x+w-1, ACS_VLINE, h-2); - - mvwaddch(win, y+h-1, x, ACS_LLCORNER); - mvwhline(win, y+h-1, x+1, ACS_HLINE, w - 2); - mvwaddch(win, y+h-1, x+w-1, ACS_LRCORNER); + mvwaddch(m_win, m_y + m_h - 1, m_x, ACS_LLCORNER); + mvwhline(m_win, m_y + m_h - 1, m_x + 1, ACS_HLINE, m_w - 2); + mvwaddch(m_win, m_y + m_h - 1, m_x + m_w - 1, ACS_LRCORNER); } } -void CursesIntf::clearRect(WINDOW *win, int y, int x, int h, int w) +void Box::printDataLine(int n, int x, const char *fmt, ...) const { - for (int i = 0; i < h; i++) - { - mvwhline(win, y + i, x, ' ', w); - } -} - - -void CursesIntf::boxPrint(WINDOW *win, int y, int x, const char *fmt, ...) -{ - if (y < m_boxStart || y - m_boxStart >= m_boxLines) + if (n < getFirstLine() || n >= getLastLine() || m_w <= x - m_x + 1) return; va_list vl_args; @@ -108,17 +96,104 @@ void CursesIntf::boxPrint(WINDOW *win, int y, int x, const char *fmt, ...) { return; } - mvwprintw(win, m_boxY + y - m_boxStart, x, "%s", buf); + + mvwprintw(m_win, m_topLine + n - m_dataStart, x, "%s", + truncString(buf, m_w - 1 - x + m_x).c_str()); + free(buf); } -void CursesIntf::drawStatus(WINDOW *win, int y, int x, - const string& iMessage, bool error) +bool Box::scrollOneLineUp() { + if (m_dataSize <= m_nbLines || m_dataStart == 0) + return false; + m_dataStart--; + return true; +} + + +bool Box::scrollOneLineDown() +{ + if (m_dataSize <= m_nbLines || m_dataStart >= m_dataSize - 1) + return false; + m_dataStart++; + return true; +} + + +bool Box::scrollOnePageUp() +{ + if (m_dataSize <= m_nbLines) + return false; + m_dataStart -= m_nbLines; + if (m_dataStart < 0) + m_dataStart = 0; + return true; +} + + +bool Box::scrollOnePageDown() +{ + if (m_dataSize <= m_nbLines) + return false; + m_dataStart += m_nbLines; + if (m_dataStart > m_dataSize - 1) + m_dataStart = m_dataSize - 1; + return true; +} + + +bool Box::scrollBeginning() +{ + if (m_dataSize <= m_nbLines || m_dataStart == 0) + return false; + m_dataStart = 0; + return true; +} + + +bool Box::scrollEnd() +{ + if (m_dataSize <= m_nbLines || m_dataStart == m_dataSize - 1) + return false; + m_dataStart = m_dataSize - 1; + return true; +} + + +void Box::clearRect(WINDOW *win, int y, int x, int h, int w) +{ + for (int i = 0; i < h; i++) + { + mvwhline(win, y + i, x, ' ', w); + } +} + + +CursesIntf::CursesIntf(WINDOW *win, Game& iGame) + : m_win(win), m_game(&iGame), m_state(DEFAULT), m_dying(false), + m_box(win, 0, 0, 0, 0), m_showDots(false) +{ +} + + +CursesIntf::~CursesIntf() +{ + GameFactory::Instance()->releaseGame(*m_game); + GameFactory::Destroy(); +} + + +void CursesIntf::drawStatus(WINDOW *win, const string& iMessage, bool error) +{ + int cols; + int lines; + getmaxyx(win, lines, cols); + int x = 0; + int y = lines - 1; if (error) wattron(win, COLOR_PAIR(COLOR_YELLOW)); - mvwprintw(win, y, x, iMessage.c_str()); - whline(win, ' ', COLS - x - 1 - iMessage.size()); + mvwprintw(win, y, x, truncOrPad(iMessage, cols).c_str()); if (error) wattron(win, COLOR_PAIR(COLOR_WHITE)); } @@ -127,7 +202,8 @@ void CursesIntf::drawStatus(WINDOW *win, int y, int x, void CursesIntf::drawBoard(WINDOW *win, int y, int x) const { // Box around the board - drawBox(win, y + 1, x + 3, 17, 47, ""); + Box box(win, y + 1, x + 3, 17, 47); + box.draw(); // Print the coordinates for (int i = 0; i < 15; i++) @@ -161,14 +237,20 @@ void CursesIntf::drawBoard(WINDOW *win, int y, int x) const mvwprintw(win, y + row + 1, x + 3 * col + 1, " "); // Now add the letter - char c = m_game->getBoard().getChar(row, col); + wchar_t c = m_game->getBoard().getChar(row, col); if (c) { - if (islower(c)) - mvwaddch(win, y + row + 1, x + 3 * col + 2, - c | A_BOLD | COLOR_PAIR(COLOR_GREEN)); + cchar_t cc; + if (iswlower(c)) + { + setcchar(&cc, &c, A_BOLD, COLOR_GREEN, NULL); + mvwadd_wch(win, y + row + 1, x + 3 * col + 2, &cc); + } else - mvwaddch(win, y + row + 1, x + 3 * col + 2, c); + { + setcchar(&cc, &c, 0, 0, NULL); + mvwadd_wch(win, y + row + 1, x + 3 * col + 2, &cc); + } } else { @@ -184,8 +266,9 @@ void CursesIntf::drawBoard(WINDOW *win, int y, int x) const void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const { - drawBox(win, y, x, m_game->getNPlayers() + 2, 25, _(" Scores ")); - for (int i = 0; i < m_game->getNPlayers(); i++) + Box box(win, y, x, m_game->getNPlayers() + 2, 25); + box.draw(_("Scores")); + for (unsigned int i = 0; i < m_game->getNPlayers(); i++) { if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attron(A_BOLD); @@ -196,16 +279,17 @@ void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const } // Distance between the 2 boxes - int yOff = m_game->getNPlayers() + 3; + unsigned int yOff = m_game->getNPlayers() + 3; - drawBox(win, y + yOff, x, m_game->getNPlayers() + 2, 25, _(" Racks ")); - for (int i = 0; i < m_game->getNPlayers(); i++) + Box box2(win, y + yOff, x, m_game->getNPlayers() + 2, 25); + box2.draw(_("Racks")); + for (unsigned int i = 0; i < m_game->getNPlayers(); i++) { if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attron(A_BOLD); - string rack = convertToMb(m_game->getPlayer(i).getCurrentRack().toString(PlayedRack::RACK_SIMPLE)); + wstring rack = m_game->getPlayer(i).getCurrentRack().toString(PlayedRack::RACK_SIMPLE); mvwprintw(win, y + yOff + i + 1, x + 2, - _("Player %d: %s"), i, rack.c_str()); + _("Player %d: %ls"), i, rack.c_str()); if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer()) attroff(A_BOLD); // Force to refresh the whole rack @@ -223,127 +307,219 @@ void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const } -void CursesIntf::drawResults(WINDOW *win, int y, int x) +void CursesIntf::drawResults(Box &ioBox) const { if (m_game->getMode() != Game::kTRAINING) return; Training *tr_game = static_cast(m_game); - int h = 17; - drawBox(win, y, x, h, 25, _(" Search results ")); - m_boxY = y + 1; - m_boxLines = h - 2; - m_boxLinesData = tr_game->getResults().size(); + ioBox.draw(_("Search results")); + ioBox.setDataSize(tr_game->getResults().size()); - int i; + unsigned int i; const Results& res = tr_game->getResults(); - for (i = m_boxStart; i < res.size() && - i < m_boxStart + m_boxLines; i++) + int x = ioBox.getLeft(); + for (i = (unsigned int)ioBox.getFirstLine(); + i < res.size() && i < (unsigned int)ioBox.getLastLine(); i++) { const Round &r = res.get(i); wstring coord = r.getCoord().toString(); - boxPrint(win, i, x + 1, "%3d %s%s %3s", - r.getPoints(), - convertToMb(r.getWord()).c_str(), - string(h - 3 - r.getWordLen(), ' ').c_str(), - convertToMb(coord).c_str()); + ioBox.printDataLine(i, x, "%3d %s %3s", + r.getPoints(), + padAndConvert(r.getWord(), ioBox.getWidth() - 9, false).c_str(), + convertToMb(coord).c_str()); } // Complete the list with empty lines, to avoid trails - for (; i < m_boxStart + m_boxLines; i++) + for (; i < (unsigned int)ioBox.getLastLine(); i++) { - boxPrint(win, i, x + 1, string(23, ' ').c_str()); + ioBox.printDataLine(i, x + 1, string(ioBox.getWidth(), ' ').c_str()); } } -void CursesIntf::drawHistory(WINDOW *win, int y, int x) +void CursesIntf::drawHistory(Box &ioBox) const { // To allow pseudo-scrolling, without leaving trails - clear(); + ioBox.clearData(); - drawBox(win, y, x, LINES - y, COLS - x, _(" History of the game ")); - m_boxY = y + 1; - m_boxLines = LINES - y - 2; - m_boxLinesData = m_game->getHistory().getSize(); + ioBox.draw(_("History of the game")); + ioBox.setDataSize((int)m_game->getHistory().getSize()); + int x = ioBox.getLeft(); + int y = ioBox.getTop(); // Heading - boxPrint(win, m_boxStart, x + 2, - _(" N | RACK | SOLUTION | REF | PTS | P | BONUS")); - mvwhline(win, y + 2, x + 2, ACS_HLINE, 55); + string heading = truncString(_(" N | RACK | SOLUTION | REF | PTS | P | BONUS"), + ioBox.getWidth() - 1); + mvwprintw(m_win, y, x + 1, "%s", heading.c_str()); + mvwhline(m_win, y + 1, x + 1, ACS_HLINE, heading.size()); int i; - for (i = m_boxStart + 0; i < m_game->getHistory().getSize() && - i < m_boxStart + m_boxLines; i++) + for (i = ioBox.getFirstLine(); + i < (int)m_game->getHistory().getSize() && i < ioBox.getLastLine(); i++) { const Turn& t = m_game->getHistory().getTurn(i); - const Round& r = t.getRound(); - string word = convertToMb(r.getWord()); - string coord = convertToMb(r.getCoord().toString()); - boxPrint(win, i + 2, x + 2, - "%2d %8s %s%s %3s %3d %1d %c", - i + 1, convertToMb(t.getPlayedRack().toString()).c_str(), - word.c_str(), string(15 - word.size(), ' ').c_str(), - coord.c_str(), r.getPoints(), - t.getPlayer(), r.getBonus() ? '*' : ' '); + const Move& m = t.getMove(); + if (m.getType() == Move::VALID_ROUND) + { + // The move corresponds to a played round: display it + const Round &r = m.getRound(); + wstring coord = r.getCoord().toString(); + ioBox.printDataLine(i, x, + " %2d %s %s %s %3d %1d %c", + i + 1, padAndConvert(t.getPlayedRack().toString(), 8).c_str(), + padAndConvert(r.getWord(), 15, false).c_str(), + padAndConvert(coord, 3).c_str(), r.getPoints(), + t.getPlayer(), r.getBonus() ? '*' : ' '); + } + else if (m.getType() == Move::INVALID_WORD) + { + // The move corresponds to an invalid word: display it + wstring invWord = L"<" + m.getBadWord() + L">"; + ioBox.printDataLine(i, x, + " %2d %s %s %s %3d %1d", + i + 1, padAndConvert(t.getPlayedRack().toString(), 8).c_str(), + padAndConvert(invWord, 15, false).c_str(), + padAndConvert(m.getBadCoord(), 3).c_str(), m.getScore(), + t.getPlayer()); + } + else + { + // The move corresponds to a passed turn or changed letters + wstring action; + if (m.getType() == Move::PASS) + action = convertToWc(_("(PASS)")); + else if (m.getType() == Move::CHANGE_LETTERS) + action = L"(-" + m.getChangedLetters() + L")"; + + ioBox.printDataLine(i, x, + " %2d %s %s %s %3d %1d", + i + 1, padAndConvert(t.getPlayedRack().toString(), 8).c_str(), + padAndConvert(action, 15, false).c_str(), + " - ", m.getScore(), t.getPlayer()); + } } - mvwvline(win, y + 1, x + 5, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines)); - mvwvline(win, y + 1, x + 16, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines)); - mvwvline(win, y + 1, x + 34, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines)); - mvwvline(win, y + 1, x + 40, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines)); - mvwvline(win, y + 1, x + 46, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines)); - mvwvline(win, y + 1, x + 50, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines)); + int nbLines = min(i + 2 - ioBox.getFirstLine(), + ioBox.getLastLine() - ioBox.getFirstLine() + 2); + mvwvline(m_win, y, x + 4, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 15, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 33, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 39, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 45, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 49, ACS_VLINE, nbLines); } -void CursesIntf::drawHelp(WINDOW *win, int y, int x) +void CursesIntf::drawHelp(Box &ioBox) const { // To allow pseudo-scrolling, without leaving trails - clear(); - - drawBox(win, y, x, LINES - y, COLS - x, _(" Help ")); - m_boxY = y + 1; - m_boxLines = LINES - y - 2; + ioBox.clearData(); + ioBox.draw(_("Help")); + int x = ioBox.getLeft() + 1; int n = 0; - boxPrint(win, n++, x + 2, _("[Global]")); - boxPrint(win, n++, x + 2, _(" h, H, ? Show/hide help box")); - boxPrint(win, n++, x + 2, _(" y, Y Show/hide history of the game")); - boxPrint(win, n++, x + 2, _(" e, E Show/hide dots on empty squares of the board")); - boxPrint(win, n++, x + 2, _(" d, D Check the existence of a word in the dictionary")); - boxPrint(win, n++, x + 2, _(" j, J Play a word")); - boxPrint(win, n++, x + 2, _(" s, S Save the game")); - boxPrint(win, n++, x + 2, _(" l, L Load a game")); - boxPrint(win, n++, x + 2, _(" q, Q Quit")); - boxPrint(win, n++, x + 2, ""); + ioBox.printDataLine(n++, x, _("[Global]")); + ioBox.printDataLine(n++, x, _(" h, H, ? Show/hide help box")); + ioBox.printDataLine(n++, x, _(" y, Y Show/hide history of the game")); + ioBox.printDataLine(n++, x, _(" b, B Show/hide contents of the bag (including letters of the racks)")); + ioBox.printDataLine(n++, x, _(" e, E Show/hide dots on empty squares of the board")); + ioBox.printDataLine(n++, x, _(" d, D Check the existence of a word in the dictionary")); + ioBox.printDataLine(n++, x, _(" j, J Play a word")); + ioBox.printDataLine(n++, x, _(" s, S Save the game")); + ioBox.printDataLine(n++, x, _(" l, L Load a game")); + ioBox.printDataLine(n++, x, _(" q, Q Quit")); + ioBox.printDataLine(n++, x, ""); - boxPrint(win, n++, x + 2, _("[Training mode]")); - boxPrint(win, n++, x + 2, _(" * Take a random rack")); - boxPrint(win, n++, x + 2, _(" + Complete the current rack randomly")); - boxPrint(win, n++, x + 2, _(" t, T Set the rack manually")); - boxPrint(win, n++, x + 2, _(" c, C Compute all the possible words")); - boxPrint(win, n++, x + 2, _(" r, R Show/hide search results")); - boxPrint(win, n++, x + 2, ""); + ioBox.printDataLine(n++, x, _("[Training mode]")); + ioBox.printDataLine(n++, x, _(" * Take a random rack")); + ioBox.printDataLine(n++, x, _(" + Complete the current rack randomly")); + ioBox.printDataLine(n++, x, _(" t, T Set the rack manually")); + ioBox.printDataLine(n++, x, _(" c, C Compute all the possible words")); + ioBox.printDataLine(n++, x, _(" r, R Show/hide search results")); + ioBox.printDataLine(n++, x, ""); - boxPrint(win, n++, x + 2, _("[Duplicate mode]")); - boxPrint(win, n++, x + 2, _(" n, N Switch to the next human player")); - boxPrint(win, n++, x + 2, ""); + ioBox.printDataLine(n++, x, _("[Duplicate mode]")); + ioBox.printDataLine(n++, x, _(" n, N Switch to the next human player")); + ioBox.printDataLine(n++, x, ""); - boxPrint(win, n++, x + 2, _("[Free game mode]")); - boxPrint(win, n++, x + 2, _(" p, P Pass your turn (with or without changing letters)")); - boxPrint(win, n++, x + 2, ""); + ioBox.printDataLine(n++, x, _("[Free game mode]")); + ioBox.printDataLine(n++, x, _(" p, P Pass your turn (with or without changing letters)")); + ioBox.printDataLine(n++, x, ""); - boxPrint(win, n++, x + 2, _("[Miscellaneous]")); - boxPrint(win, n++, x + 2, _(" , Navigate in a box line by line")); - boxPrint(win, n++, x + 2, _(" , Navigate in a box page by page")); - boxPrint(win, n++, x + 2, _(" Ctrl-l Refresh the screen")); + ioBox.printDataLine(n++, x, _("[Miscellaneous]")); + ioBox.printDataLine(n++, x, _(" , Navigate in a box line by line")); + ioBox.printDataLine(n++, x, _(" , Navigate in a box page by page")); + ioBox.printDataLine(n++, x, _(" Ctrl-l Refresh the screen")); - m_boxLinesData = n; + ioBox.setDataSize(n); +} + + +void CursesIntf::drawBag(Box &ioBox) const +{ + // To allow pseudo-scrolling, without leaving trails + ioBox.clearData(); + + ioBox.draw(_("Bag")); + vector allTiles = m_game->getDic().getAllTiles(); + ioBox.setDataSize(allTiles.size()); + int x = ioBox.getLeft(); + int y = ioBox.getTop(); + + // Heading + string heading = truncString(_(" LETTER | POINTS | FREQUENCY | REMAINING"), + ioBox.getWidth() - 1); + mvwprintw(m_win, y, x + 1, "%s", heading.c_str()); + mvwhline(m_win, y + 1, x + 1, ACS_HLINE, heading.size()); + + int i; + for (i = ioBox.getFirstLine(); i < (int)allTiles.size() && i < ioBox.getLastLine(); i++) + { + ioBox.printDataLine(i, ioBox.getLeft() + 1, + " %s %2d %2d %s", + padAndConvert(wstring(1, allTiles[i].toChar()), 2).c_str(), + allTiles[i].getPoints(), + allTiles[i].maxNumber(), + convertToMb(wstring(m_game->getBag().in(allTiles[i]), + allTiles[i].toChar())).c_str()); + } + + int nbLines = min(i + 2 - ioBox.getFirstLine(), + ioBox.getLastLine() - ioBox.getFirstLine() + 2); + mvwvline(m_win, y, x + 9, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 18, ACS_VLINE, nbLines); + mvwvline(m_win, y, x + 30, ACS_VLINE, nbLines); +} + + +void CursesIntf::setState(State iState) +{ + // Clear the previous box + m_box.clear(); + + // Get the size of the screen (better than using COLS and LINES directly, + // according to the manual) + int lines; + int cols; + getmaxyx(m_win, lines, cols); + + m_state = iState; + if (m_state == DEFAULT) + m_box = Box(m_win, 0, 0, 0, 0); + else if (m_state == RESULTS) + m_box = Box(m_win, 3, 54, 17, 25); + else if (m_state == HISTORY) + m_box = Box(m_win, 1, 0, lines - 1, cols, 2); + else if (m_state == HELP) + m_box = Box(m_win, 1, 0, lines - 1, cols); + else if (m_state == BAG) + m_box = Box(m_win, 1, 0, lines - 1, cols, 2); } void CursesIntf::playWord(WINDOW *win, int y, int x) { - drawBox(win, y, x, 4, 32, _(" Play a word ")); + Box box(win, y, x, 4, 32); + box.draw(_("Play a word")); mvwprintw(win, y + 1, x + 2, _("Played word:")); mvwprintw(win, y + 2, x + 2, _("Coordinates:")); wrefresh(win); @@ -360,86 +536,87 @@ void CursesIntf::playWord(WINDOW *win, int y, int x) else xOff = l2 + 3; - string word, coord; + wstring word, coord; if (readString(win, y + 1, x + xOff, 15, word) && readString(win, y + 2, x + xOff, 3, coord)) { - int res = m_game->play(convertToWc(coord), convertToWc(word)); + int res = m_game->play(coord, word); if (res) { - drawStatus(win, LINES - 1, 0, _("Incorrect or misplaced word")); + drawStatus(win, _("Incorrect or misplaced word")); } } - m_state = DEFAULT; - clearRect(win, y, x, 4, 32); + box.clear(); } void CursesIntf::checkWord(WINDOW *win, int y, int x) { - drawBox(win, y, x, 4, 32, _(" Dictionary ")); + Box box(win, y, x, 4, 32); + box.draw(_("Dictionary")); mvwprintw(win, y + 1, x + 2, _("Enter the word to check:")); wrefresh(win); - string word; + wstring word; if (readString(win, y + 2, x + 2, 15, word)) { - int res = Dic_search_word(m_game->getDic(), convertToWc(word).c_str()); + bool res = m_game->getDic().searchWord(word); char s[100]; if (res) - snprintf(s, 100, _("The word '%s' exists"), word.c_str()); + snprintf(s, 100, _("The word '%ls' exists"), word.c_str()); else - snprintf(s, 100, _("The word '%s' does not exist"), word.c_str()); - drawStatus(win, LINES - 1, 0, s); + snprintf(s, 100, _("The word '%ls' does not exist"), word.c_str()); + drawStatus(win, s, false); } - m_state = DEFAULT; - clearRect(win, y, x, 4, 32); + box.clear(); } void CursesIntf::saveGame(WINDOW *win, int y, int x) { - drawBox(win, y, x, 4, 32, _(" Save the game ")); + Box box(win, y, x, 4, 32); + box.draw(_("Save the game")); mvwprintw(win, y + 1, x + 2, _("Enter the file name:")); wrefresh(win); - string filename; + wstring filename; if (readString(win, y + 2, x + 2, 28, filename, kFILENAME)) { - ofstream fout(filename.c_str()); + ofstream fout(convertToMb(filename).c_str()); char s[100]; if (fout.rdstate() == ios::failbit) { - snprintf(s, 100, _("Cannot open file %s for writing"), + snprintf(s, 100, _("Cannot open file %ls for writing"), filename.c_str()); + drawStatus(win, s); } else { - m_game->save(fout); + m_game->save(fout, Game::FILE_FORMAT_ADVANCED); fout.close(); - snprintf(s, 100, _("Game saved in %s"), filename.c_str()); + snprintf(s, 100, _("Game saved in '%ls'"), filename.c_str()); + drawStatus(win, s, false); } - drawStatus(win, LINES - 1, 0, s); } - m_state = DEFAULT; - clearRect(win, y, x, 4, 32); + box.clear(); } void CursesIntf::loadGame(WINDOW *win, int y, int x) { - drawBox(win, y, x, 4, 32, _(" Load a game ")); + Box box(win, y, x, 4, 32); + box.draw(_("Load a game")); mvwprintw(win, y + 1, x + 2, _("Enter the file name:")); wrefresh(win); - string filename; + wstring filename; if (readString(win, y + 2, x + 2, 28, filename, kFILENAME)) { char s[100]; FILE *fin; - if ((fin = fopen(filename.c_str(), "r")) == NULL) + if ((fin = fopen(convertToMb(filename).c_str(), "r")) == NULL) { - snprintf(s, 100, _("Cannot open file %s for reading"), + snprintf(s, 100, _("Cannot open file '%ls' for reading"), filename.c_str()); } else @@ -448,118 +625,184 @@ void CursesIntf::loadGame(WINDOW *win, int y, int x) if (loaded == NULL) { snprintf(s, 100, _("Invalid saved game")); + drawStatus(win, s); } else { snprintf(s, 100, _("Game loaded")); GameFactory::Instance()->releaseGame(*m_game); m_game = loaded; + drawStatus(win, s, false); } fclose(fin); } - drawStatus(win, LINES - 1, 0, s); } - m_state = DEFAULT; - clearRect(win, y, x, 4, 32); + box.clear(); } void CursesIntf::passTurn(WINDOW *win, int y, int x, FreeGame &iGame) { - drawBox(win, y, x, 4, 32, _(" Pass your turn ")); + Box box(win, y, x, 4, 32); + box.draw(_("Pass your turn")); mvwprintw(win, y + 1, x + 2, _("Enter the letters to change:")); wrefresh(win); - string letters; + wstring letters; if (readString(win, y + 2, x + 2, 7, letters)) { - int res = iGame.pass(convertToWc(letters), m_game->currPlayer()); + int res = iGame.pass(letters); if (res) { - drawStatus(win, LINES - 1, 0, _("Cannot pass the turn")); + drawStatus(win, _("Cannot pass the turn")); } } - m_state = DEFAULT; - clearRect(win, y, x, 4, 32); + box.clear(); } void CursesIntf::setRack(WINDOW *win, int y, int x, Training &iGame) { - drawBox(win, y, x, 4, 32, _(" Set rack ")); + Box box(win, y, x, 4, 32); + box.draw(_("Set rack")); mvwprintw(win, y + 1, x + 2, _("Enter the new letters:")); wrefresh(win); - string letters; + wstring letters; if (readString(win, y + 2, x + 2, 7, letters, kJOKER)) { - iGame.setRackManual(false, convertToWc(letters)); + int res = iGame.setRackManual(false, letters); + if (res) + { + drawStatus(win, _("Cannot take these letters from the bag")); + } } m_state = DEFAULT; - clearRect(win, y, x, 4, 32); + box.clear(); } -bool CursesIntf::readString(WINDOW *win, int y, int x, int n, string &oString, +bool CursesIntf::readString(WINDOW *win, int y, int x, int n, wstring &oString, unsigned int flag) { - int c; + // Save the initial position + int x0 = x; + wint_t c; wmove(win, y, x); curs_set(1); - while ((c = getch()) != 0) + int res; + // Position in the string before which to insert the next character + // (the character will be added at the end if pos == oString.size()) + unsigned int pos = 0; + while ((res = get_wch(&c)) != ERR) { if (c == 0x1b ) // Esc { curs_set(0); return false; } - else if (c == KEY_ENTER || c == 0xD) + else if ((c == KEY_ENTER && res == KEY_CODE_YES) || c == 0xD) { curs_set(0); return true; } else if (c == 0x0c) // Ctrl-L { -// clear(); redraw(win); wmove(win, y, x); } - else if (c == KEY_BACKSPACE && oString.size() > 0) + else if (c == 0x0b) // Ctrl-K { - x--; - mvwprintw(win, y, x, " "); + // Remove everything after the cursor position + int len = oString.size() - pos; + oString = oString.erase(pos); + mvwprintw(win, y, x, string(len, ' ').c_str()); wmove(win, y, x); - oString.erase(oString.size() - 1); } - else if (isalnum(c) && oString.size() < (unsigned int)n) + else if (c == 0x15) // Ctrl-U + { + // Remove everything before the cursor position + oString.erase(0, pos); + int len = pos; + x = x0; + pos = 0; + mvwprintw(win, y, x0, "%s", convertToMb(oString + wstring(len, L' ')).c_str()); + wmove(win, y, x); + } + else if (res == KEY_CODE_YES) + { + if (c == KEY_BACKSPACE && pos != 0) + { + x--; + pos--; + oString.erase(pos, 1); + mvwprintw(win, y, x0, "%s", convertToMb(oString + L" ").c_str()); + wmove(win, y, x); + } + else if (c == KEY_DC) + { + oString.erase(pos, 1); + mvwprintw(win, y, x0, "%s", convertToMb(oString + L" ").c_str()); + wmove(win, y, x); + } + else if (c == KEY_LEFT && pos != 0) + { + x--; + pos--; + wmove(win, y, x); + } + else if (c == KEY_RIGHT && pos != oString.size()) + { + x++; + pos++; + wmove(win, y, x); + } + else if (c == KEY_HOME) + { + x = x0; + pos = 0; + wmove(win, y, x); + } + else if (c == KEY_END) + { + x = x0 + oString.size(); + pos = oString.size(); + wmove(win, y, x); + } + else + beep(); + } + else if (res == OK && iswalnum(c) && oString.size() < (unsigned int)n) { - mvwprintw(win, y, x, "%c", c); x++; - oString += (char)c; + oString.insert(pos++, 1, c); + mvwprintw(win, y, x0, "%s", convertToMb(oString).c_str()); + wmove(win, y, x); + } + else if (flag & kJOKER && c == L'?') + { + x++; + oString.insert(pos++, 1, c); + mvwprintw(win, y, x0, "%s", convertToMb(oString).c_str()); + wmove(win, y, x); + } + else if (flag & kFILENAME) + { + if (c == L'/' || c == L'.' || c == L'-' || c == L'_' || c == L' ') + { + x++; + oString += c; + mvwprintw(win, y, x0, "%s", convertToMb(oString).c_str()); + wmove(win, y, x); + } + else + beep(); } else - { - if (flag & kJOKER && c == '?') - { - mvwprintw(win, y, x, "%c", c); - x++; - oString += (char)c; - } - if (flag & kFILENAME) - { - if (c == '/' || c == '.' || c == '-' || c == '_' || c == ' ') - { - mvwprintw(win, y, x, "%c", c); - x++; - oString += (char)c; - } - } - } -// else -// mvwprintw(win, 0, 0, "%3d", c); + beep(); } curs_set(0); - return 0; + return false; } @@ -568,15 +811,30 @@ int CursesIntf::handleKeyForGame(int iKey, Training &iGame) switch (iKey) { case '*': + if (m_state != DEFAULT) + { + setState(DEFAULT); + redraw(m_win); + } iGame.setRackRandom(false, Game::RACK_ALL); return 1; case '+': + if (m_state != DEFAULT) + { + setState(DEFAULT); + redraw(m_win); + } iGame.setRackRandom(false, Game::RACK_NEW); return 1; case 't': case 'T': + if (m_state != DEFAULT) + { + setState(DEFAULT); + redraw(m_win); + } setRack(m_win, 22, 10, iGame); return 1; @@ -623,68 +881,48 @@ int CursesIntf::handleKeyForGame(int iKey, FreeGame &iGame) int CursesIntf::handleKey(int iKey) { - if (m_state == DEFAULT) - { - int res; - if (m_game->getMode() == Game::kTRAINING) - { - res = handleKeyForGame(iKey, (Training&)*m_game); - } - else if (m_game->getMode() == Game::kDUPLICATE) - { - res = handleKeyForGame(iKey, (Duplicate&)*m_game); - } - else - { - res = handleKeyForGame(iKey, (FreeGame&)*m_game); - } + // Remove any error message in the status line + if (m_state == DEFAULT || m_state == RESULTS) + drawStatus(m_win, "", false); - if (res != 2) - return res; + // Handle game-specific keys + int res; + if (m_game->getMode() == Game::kTRAINING) + { + res = handleKeyForGame(iKey, (Training&)*m_game); } - else // m_state is in {HELP, RESULTS, HISTORY} + else if (m_game->getMode() == Game::kDUPLICATE) + { + res = handleKeyForGame(iKey, (Duplicate&)*m_game); + } + else + { + res = handleKeyForGame(iKey, (FreeGame&)*m_game); + } + if (res != 2) + return res; + + // Handle scrolling keys + if (m_state != DEFAULT) { switch (iKey) { case KEY_HOME: - if (m_boxLinesData <= m_boxLines && m_boxStart > 0) - return 0; - m_boxStart = 0; - return 1; + return m_box.scrollBeginning() ? 1 : 0; case KEY_END: - if (m_boxLinesData <= m_boxLines && - m_boxStart < m_boxLinesData - 1) - return 0; - m_boxStart = m_boxLinesData - 1; - return 1; + return m_box.scrollEnd() ? 1 : 0; case KEY_UP: - if (m_boxLinesData <= m_boxLines || m_boxStart <= 0) - return 0; - m_boxStart--; - return 1; + return m_box.scrollOneLineUp() ? 1 : 0; case KEY_DOWN: - if (m_boxLinesData <= m_boxLines || - m_boxStart >= m_boxLinesData - 1) - return 0; - m_boxStart++; - return 1; + return m_box.scrollOneLineDown() ? 1 : 0; case KEY_PPAGE: - if (m_boxLinesData <= m_boxLines) - return 0; - m_boxStart -= m_boxLines; - if (m_boxStart < 0) - m_boxStart = 0; - return 1; + return m_box.scrollOnePageUp() ? 1 : 0; case KEY_NPAGE: - if (m_boxLinesData <= m_boxLines) - return 0; - m_boxStart += m_boxLines; - if (m_boxStart > m_boxLinesData - 1) - m_boxStart = m_boxLinesData - 1; - return 1; + return m_box.scrollOnePageDown() ? 1 : 0; } } + // Handle other global keys switch (iKey) { // Toggle help @@ -692,10 +930,9 @@ int CursesIntf::handleKey(int iKey) case 'H': case '?': if (m_state == HELP) - m_state = DEFAULT; + setState(DEFAULT); else - m_state = HELP; - m_boxStart = 0; + setState(HELP); clear(); return 1; @@ -703,10 +940,9 @@ int CursesIntf::handleKey(int iKey) case 'y': case 'Y': if (m_state == HISTORY) - m_state = DEFAULT; + setState(DEFAULT); else - m_state = HISTORY; - m_boxStart = 0; + setState(HISTORY); clear(); return 1; @@ -714,13 +950,25 @@ int CursesIntf::handleKey(int iKey) case 'r': case 'R': if (m_game->getMode() != Game::kTRAINING) + { + beep(); return 0; + } if (m_state == RESULTS) - m_state = DEFAULT; + setState(DEFAULT); else - m_state = RESULTS; - m_boxStart = 0; - clearRect(m_win, 3, 54, 30, 25); + setState(RESULTS); + Box::clearRect(m_win, 3, 54, 30, 25); + return 1; + + // Toggle bag + case 'b': + case 'B': + if (m_state == BAG) + setState(DEFAULT); + else + setState(BAG); + clear(); return 1; // Toggle dots display @@ -729,49 +977,63 @@ int CursesIntf::handleKey(int iKey) m_showDots = !m_showDots; return 1; + // Ctrl-L should clear and redraw the screen + case 0x0c: + clear(); + // Force the re-definition of the current box + setState(m_state); + return 1; + // Check a word in the dictionary case 'd': case 'D': - if (m_state != DEFAULT) - return 0; + if (m_state != DEFAULT && m_state != RESULTS) + { + setState(DEFAULT); + redraw(m_win); + } checkWord(m_win, 22, 10); - return 1; + return 0; // Play a word case 'j': case 'J': - if (m_state != DEFAULT) - return 0; + if (m_state != DEFAULT && m_state != RESULTS) + { + setState(DEFAULT); + redraw(m_win); + } playWord(m_win, 22, 10); return 1; - // Ctrl-L should clear and redraw the screen - case 0x0c: - clear(); - return 1; - case 'l': case 'L': if (m_state != DEFAULT) - return 0; + { + setState(DEFAULT); + redraw(m_win); + } loadGame(m_win, 22, 10); return 1; case 's': case 'S': if (m_state != DEFAULT) - return 0; + { + setState(DEFAULT); + redraw(m_win); + } saveGame(m_win, 22, 10); - return 1; + return 0; // Quit case 'q': case 'Q': - case 0x1b: // Esc m_dying = true; return 0; default: + beep(); return 0; } } @@ -786,16 +1048,20 @@ void CursesIntf::redraw(WINDOW *win) } else if (m_state == RESULTS) { - drawResults(win, 3, 54); + drawResults(m_box); drawBoard(win, 2, 0); } else if (m_state == HELP) { - drawHelp(win, 1, 0); + drawHelp(m_box); } else if (m_state == HISTORY) { - drawHistory(win, 1, 0); + drawHistory(m_box); + } + else if (m_state == BAG) + { + drawBag(m_box); } // Title @@ -811,8 +1077,11 @@ void CursesIntf::redraw(WINDOW *win) if (m_game->getVariant() == Game::kJOKER) variant = string(" - ") + _("Joker game"); string title = "Eliot (" + mode + variant + ") " + _("[h for help]"); - mvwprintw(win, 0, 0, title.c_str()); - whline(win, ' ', COLS - title.size()); + + int lines; + int cols; + getmaxyx(m_win, lines, cols); + mvwprintw(win, 0, 0, truncOrPad(title, cols).c_str()); attroff(A_REVERSE); wrefresh(win); @@ -821,7 +1090,7 @@ void CursesIntf::redraw(WINDOW *win) int main(int argc, char ** argv) { -#ifdef HAVE_SETLOCALE +#if HAVE_SETLOCALE // Set locale via LC_ALL setlocale(LC_ALL, ""); #endif @@ -861,7 +1130,7 @@ int main(int argc, char ** argv) init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); - init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); + init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_RED); init_pair(COLOR_BLUE, COLOR_BLACK, COLOR_BLUE); init_pair(COLOR_CYAN, COLOR_BLACK, COLOR_CYAN); @@ -872,7 +1141,7 @@ int main(int argc, char ** argv) // Do not echo noecho(); - // mainIntf will take care of destroying game for us. + // mainIntf will take care of destroying game for us CursesIntf mainIntf(wBoard, *game); mainIntf.redraw(wBoard); diff --git a/utils/ncurses.h b/utils/ncurses.h index af75e41..f9eb123 100644 --- a/utils/ncurses.h +++ b/utils/ncurses.h @@ -20,7 +20,7 @@ #ifndef _NCURSES_H_ #define _NCURSES_H_ -#include +#include #include class Game; @@ -29,6 +29,70 @@ class Duplicate; class FreeGame; using std::string; +using std::wstring; + + +class Box +{ + public: + // Create a titled box with the specified position and size, + // containing iHeadingLines non-scrolling lines. + // The number of data to display (with the printLine() method) + // can be set later using the setDataSize() method. + Box(WINDOW *win, int y, int x, int h, int w, + unsigned int iHeadingLines = 0); + + // Simply draw the box (without any content) + void draw(const string& iTitle = "") const; + + // Print data line number n (starting at 0), taking care of + // the current scrolling state + void printDataLine(int n, int x, const char *fmt, ...) const; + + // Set the number of lines of the data to display + void setDataSize(unsigned int iNbLines) { m_dataSize = iNbLines; } + + // Control scrolling + // Return true if redrawing is needed, false otherwise + bool scrollOneLineUp(); + bool scrollOneLineDown(); + bool scrollOnePageUp(); + bool scrollOnePageDown(); + bool scrollBeginning(); + bool scrollEnd(); + + // Clear the box completely + void clear() const { clearRect(m_win, m_y, m_x, m_h, m_w); } + // Clear the scrolling zone of the box + void clearData() const { clearRect(m_win, m_topLine, m_x + 1, + m_nbLines, getWidth()); } + // Clear an arbitrary rectangular zone + static void clearRect(WINDOW *win, int y, int x, int h, int w); + + // First line of data to display (included) + int getFirstLine() const { return m_dataStart; } + // Last line of data to display (excluded) + int getLastLine() const { return m_dataStart + m_nbLines; } + + // First line inside the box + int getTop() const { return m_y + 1; } + // First column available for writing + int getLeft() const { return m_x + 1; } + // Client width + int getWidth() const { return m_w - 2; } + + private: + WINDOW *m_win; + int m_x; + int m_y; + int m_w; + int m_h; + string m_title; + int m_topLine; + int m_nbLines; + int m_dataStart; + int m_dataSize; +}; /** @@ -52,28 +116,27 @@ private: DEFAULT, // Default state HELP, // Help panel is shown HISTORY, // Game history panel is shown - RESULTS // Search results panel is shown + RESULTS, // Search results panel is shown + BAG // Bag contents panel is shown }; - // Draw a titled box with the specified position and size - static void drawBox(WINDOW *win, int y, int x, int h, int w, - const string& iTitle); - // Clear a rectangular zone - static void clearRect(WINDOW *win, int y, int x, int h, int w); - // Print a line in a box, taking care of the current offset - void boxPrint(WINDOW *win, int y, int x, const char *fmt, ...); + // Write a message in the "status line" - void drawStatus(WINDOW *win, int y, int x, - const string& iMessage, bool error = true); + void drawStatus(WINDOW *win, const string& iMessage, bool error = true); // Draw the board, with the coordinates void drawBoard(WINDOW *win, int y, int x) const; // Draw the boxes for scores and racks void drawScoresRacks(WINDOW *win, int y, int x) const; // Draw the results panel - void drawResults(WINDOW *win, int y, int x); + void drawResults(Box &ioBox) const; // Draw the history panel - void drawHistory(WINDOW *win, int y, int x); + void drawHistory(Box &ioBox) const; // Draw the help panel - void drawHelp(WINDOW *win, int y, int x); + void drawHelp(Box &ioBox) const; + // Draw the bag panel + void drawBag(Box &ioBox) const; + + // Change the inner state, and initialize the corresponding box + void setState(State iState); // Draw the "Play word" box, and handle the played word void playWord(WINDOW *win, int y, int x); void checkWord(WINDOW *win, int y, int x); @@ -81,17 +144,18 @@ private: void loadGame(WINDOW *win, int y, int x); void passTurn(WINDOW *win, int y, int x, FreeGame &iGame); void setRack(WINDOW *win, int y, int x, Training &iGame); + // Get a string from the user, with a maximum length // The string is validated if the user presses Enter (return value: true) // and it is cancelled if the user presses Esc (return value: false) - bool readString(WINDOW *win, int y, int x, int n, string &oString, + bool readString(WINDOW *win, int y, int x, int n, wstring &oString, unsigned int flag = 0); // Any combination of the following constants can be used as the "flag" // parameter of the readString() method. // Indicate that the '?' character is accepted static const unsigned int kJOKER = 1 << 0; // Accept characters for a file name - static const unsigned int kFILENAME = 1 << 0; + static const unsigned int kFILENAME = 1 << 1; // Handle the key in Training mode int handleKeyForGame(int iKey, Training &iGame); @@ -109,14 +173,8 @@ private: State m_state; // True when the user requested to quit bool m_dying; - // Index of the first line of data to be displayed in the current box - int m_boxStart; - // Number of lines of the current box (border excluded) - int m_boxLines; - // Number of lines of the data to be displayed in the current box - int m_boxLinesData; - // Index of the first line of the box where to write - int m_boxY; + // Scrolling box for the current panel + Box m_box; // True if dots must be shown on empty squares bool m_showDots; }; diff --git a/wxwin/Makefile.am b/wxwin/Makefile.am index 1b77d00..52c25bd 100644 --- a/wxwin/Makefile.am +++ b/wxwin/Makefile.am @@ -16,13 +16,15 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +localedir = $(datadir)/locale + if BUILD_WXWIDGETS bin_PROGRAMS = eliot -INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/dic -I$(top_srcdir)/game +AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" @WX_CPPFLAGS@ ## -Wno-strict-aliasing -AM_CPPFLAGS = @WX_CPPFLAGS@ ## -Wno-strict-aliasing +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/dic -I$(top_srcdir)/game eliot_SOURCES = \ configdb.cc configdb.h \ @@ -36,8 +38,7 @@ eliot_SOURCES = \ mainframe.cc mainframe.h \ main.cc ewx.h -## eliot_CPPFLAGS= -eliot_LDADD = @WX_LIBS@ ../dic/libdic.a ../game/libgame.a +eliot_LDADD = @WX_LIBS@ ../game/libgame.a ../dic/libdic.a @LIBINTL@ EXTRA_DIST = \ eliot.xpm \ diff --git a/wxwin/auxframes.cc b/wxwin/auxframes.cc index f24029e..e1de9e9 100644 --- a/wxwin/auxframes.cc +++ b/wxwin/auxframes.cc @@ -26,6 +26,8 @@ #include #include +#include +#include #include "wx/sizer.h" #include "wx/button.h" @@ -37,7 +39,6 @@ #include "ewx.h" #include "dic.h" -#include "dic_search.h" #include "training.h" #include "player.h" #include "game.h" @@ -79,7 +80,7 @@ AuxFrame::SwitchDisplay() Show(TRUE); Raise(); show = 1; - Reload(); + Reload(); } else { @@ -119,7 +120,7 @@ AuxFrame::Reload() /****************************************************************/ BoardFrame::BoardFrame(wxFrame* parent, Game& iGame): - AuxFrame(parent, ID_Frame_Board, _("Grille"), FRAMEBOARD) + AuxFrame(parent, ID_Frame_Board, _("Grid"), FRAMEBOARD) { board = new GfxBoard(this, iGame); wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL ); @@ -144,7 +145,7 @@ BoardFrame::Refresh(refresh_t force) /****************************************************************/ BagFrame::BagFrame(wxFrame* parent, Game& iGame): - AuxFrame(parent, ID_Frame_Bag, _("sac"), FRAMEBAG), + AuxFrame(parent, ID_Frame_Bag, _("Bag"), FRAMEBAG), m_game(iGame) { tiles = new wxListCtrl(this, -1); @@ -180,11 +181,11 @@ BagFrame::Refresh(refresh_t __UNUSED__ force) tiles->ClearAll(); - std::list::const_iterator it; - const std::list& allTiles = Tile::getAllTiles(); + std::vector::const_iterator it; + const std::vector& allTiles = m_game.getDic().getAllTiles(); for (index = 0, it = allTiles.begin(); it != allTiles.end(); index++, it++) { - n = m_game.getBag().in(*it); + n = m_game.getBag().in(*it); #ifdef DEBUG buf.Printf(format, it->toChar(), n, n - it->maxNumber()); #else @@ -198,8 +199,8 @@ BagFrame::Refresh(refresh_t __UNUSED__ force) /* RECHERCHE */ /****************************************************************/ -SearchFrame::SearchFrame(wxFrame *parent, Dictionary _dic): - AuxFrame(parent, ID_Frame_Search, _("recherche"), FRAMESEARCH) +SearchFrame::SearchFrame(wxFrame *parent, const Dictionary &_dic): + AuxFrame(parent, ID_Frame_Search, _("Search"), FRAMESEARCH) { panel = new SearchPanel(this, _dic); wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL ); @@ -222,20 +223,20 @@ SearchFrame::Refresh(refresh_t __UNUSED__ force) enum { Word_Id, - Result_Id, + Result_Id }; BEGIN_EVENT_TABLE(VerifFrame, AuxFrame) EVT_TEXT(Word_Id, VerifFrame::OnText) END_EVENT_TABLE() -VerifFrame::VerifFrame(wxFrame* parent, Dictionary _dic): - AuxFrame(parent, ID_Frame_Verif, _("verification"), FRAMEVERIF) +VerifFrame::VerifFrame(wxFrame* parent, const Dictionary &_dic): + AuxFrame(parent, ID_Frame_Verif, _("Check"), FRAMEVERIF) { - dic = _dic; + dic = &_dic; word = new wxTextCtrl(this, Word_Id, wxT("")); word->SetFont(config.getFont(LISTFONT)); - word->SetToolTip(_("Mot a verifier")); + word->SetToolTip(_("Word to check")); result = new wxStaticText(this, Result_Id, wxT("")); result->SetFont(config.getFont(LISTFONT)); wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); @@ -253,13 +254,13 @@ VerifFrame::verif() { if (dic == NULL) { - result->SetLabel(_("pas de dictionnaire")); + result->SetLabel(_("No dictionary")); return; } - if (Dic_search_word(dic, word->GetValue().wc_str())) - result->SetLabel(_("existe")); + if (dic->searchWord(word->GetValue().wc_str())) + result->SetLabel(_("exists")); else - result->SetLabel(_("n'existe pas")); + result->SetLabel(_("doesn't exist")); } void @@ -300,7 +301,7 @@ AuxFrameList::AuxFrameList(wxFrame* parent, int _id, wxString _name, wxString _c listbox->SetToolTip(name); sizer_v->Add(listbox, 1, wxEXPAND | wxALL, 1); - button = new wxButton(this, ButtonCopyID, _("Copier"), wxPoint(0, 0), wxSize(-1, -1)); + button = new wxButton(this, ButtonCopyID, _("Copy"), wxPoint(0, 0), wxSize(-1, -1)); sizer_v->Add(button, 0, wxEXPAND | wxALL, 1); wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL ); @@ -342,32 +343,25 @@ AuxFrameList::Refresh(refresh_t __UNUSED__ force) { //debug(" %s : Refresh start\n",(const char*)name.mb_str()); if (game == NULL) - { - listbox->Clear(); - listbox->Append(_("Pas de partie en cours")); - //debug(" %s : Refresh end - no game\n",(const char*)name.mb_str()); - return; - } - if (game->getDic() == NULL) - { - listbox->Clear(); - listbox->Append(_("Pas de dictionnaire")); - //debug(" %s : Refresh end - no dictionnary\n",(const char*)name.mb_str()); - return; - } + { + listbox->Clear(); + listbox->Append(_("No on going game")); + //debug(" %s : Refresh end - no game\n",(const char*)name.mb_str()); + return; + } if (show == 0) - { - //debug(" %s : Refresh end - no window\n",(const char*)name.mb_str()); - return; - } + { + //debug(" %s : Refresh end - no window\n",(const char*)name.mb_str()); + return; + } noresult = true; refresh(); if (noresult == true) - { - //debug(" %s : noresult == true\n",(const char*)name.mb_str()); - listbox->Clear(); - listbox->Append(_("Aucun resultat")); - } + { + //debug(" %s : noresult == true\n",(const char*)name.mb_str()); + listbox->Clear(); + listbox->Append(_("No result")); + } //debug(" %s : Refresh end\n",(const char*)name.mb_str()); } @@ -390,27 +384,41 @@ Plus1Frame::refresh() } savedword = rack; - wchar_t buff[DIC_LETTERS][RES_7PL1_MAX][DIC_WORD_MAX]; - Dic_search_7pl1(game->getDic(), rack.c_str(), buff, config.getJokerPlus1()); + map > wordList; + game->getDic().search7pl1(rack, wordList, config.getJokerPlus1()); - listbox->Clear(); - wxString res[DIC_LETTERS*(RES_7PL1_MAX+1)]; - int resnum = 0; - res[resnum++] = wxString(_("Tirage: ")) + wxString(wxU(rack.c_str())); - for (int i = 0; i < DIC_LETTERS; i++) + // Count the results + int sum = 0; + map >::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) { - if (i && buff[i][0][0]) + if (it->first) + sum += 1; + sum += it->second.size(); + } + // For the line containing the rack + sum += 1; + + noresult = (sum == 0); + listbox->Clear(); + if (noresult) + return; + + wxString *res = new wxString[sum]; + int resnum = 0; + res[resnum++] = wxString(_("Rack: ")) + wxString(wxU(rack.c_str())); + for (it = wordList.begin(); it != wordList.end(); it++) + { + if (it->first) + res[resnum++] = wxString(wxT("+")) + wxU((wxString)it->first); + list::const_iterator itWord; + for (itWord = it->second.begin(); itWord != it->second.end(); itWord++) { - res[resnum++] = wxString(wxT("+")) + (wxChar)(i + 'A' - 1); - noresult = false; - } - for (int j = 0; j < RES_7PL1_MAX && buff[i][j][0]; j++) - { - res[resnum++] = wxString(wxT(" ")) + wxU(buff[i][j]); - noresult = false; + res[resnum++] = wxString(wxT(" ")) + wxU(itWord->c_str()); } } listbox->Set(resnum, res); + delete[] res; //debug(" Plus1Frame::refresh end\n"); } @@ -424,7 +432,7 @@ BenjFrame::refresh() if (game->getMode() != Game::kTRAINING) return; - std::wstring word = static_cast(game)->getTestPlayWord(); + wstring word = static_cast(game)->getTestPlayWord(); if (savedword == word) { noresult = false; // keep old results @@ -432,18 +440,20 @@ BenjFrame::refresh() } savedword = word; //debug(" BenjFrame::refresh : %s\n",word.c_str()); - wchar_t wordlist[RES_BENJ_MAX][DIC_WORD_MAX]; - Dic_search_Benj(game->getDic(), word.c_str(), wordlist); + list wordList; + game->getDic().searchBenj(word, wordList); - wxString res[RES_BENJ_MAX]; + wxString *res = new wxString[wordList.size()]; int resnum = 0; - for (int i = 0; (i < RES_BENJ_MAX) && (wordlist[i][0]); i++) + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) { - res[resnum++] = wxU(wordlist[i]); + res[resnum++] = wxU(it->c_str()); //debug(" BenjFrame : %s (%d)\n",wordlist[i],resnum); noresult = false; } listbox->Set(resnum, res); + delete[] res; } @@ -457,7 +467,7 @@ RaccFrame::refresh() if (game->getMode() != Game::kTRAINING) return; - std::wstring word = static_cast(game)->getTestPlayWord(); + wstring word = static_cast(game)->getTestPlayWord(); if (savedword == word) { noresult = false; // keep old results @@ -465,18 +475,20 @@ RaccFrame::refresh() } savedword = word; //debug(" RaccFrame::refresh : %s\n",word.c_str()); - wchar_t wordlist[RES_RACC_MAX][DIC_WORD_MAX]; - Dic_search_Racc(game->getDic(), word.c_str(), wordlist); + list wordList; + game->getDic().searchRacc(word, wordList); - wxString res[RES_RACC_MAX]; + wxString *res = new wxString[wordList.size()]; int resnum = 0; - for (int i = 0; (i < RES_RACC_MAX) && (wordlist[i][0]); i++) + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) { - res[resnum++] = wxU(wordlist[i]); + res[resnum++] = wxU(it->c_str()); //debug(" RaccFrame : %s (%d)\n",wordlist[i],resnum); noresult = false; } listbox->Set(resnum, res); + delete[] res; } /****************************************************************/ @@ -491,7 +503,7 @@ AuxFrameText::AuxFrameText(wxFrame* parent, int _id, wxString _name, wxString _c wxBoxSizer *sizer = new wxBoxSizer( wxHORIZONTAL ); wxFont font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, - wxFONTWEIGHT_NORMAL, false, wxString(wxT("Courier New")), wxFONTENCODING_SYSTEM); + wxFONTWEIGHT_NORMAL, false, wxString(wxT("Courier New")), wxFONTENCODING_SYSTEM); textbox = new wxTextCtrl(this, -1, wxT(""), wxDefaultPosition, wxDefaultSize, _style); textbox->SetFont(font); @@ -510,7 +522,7 @@ AuxFrameText::AuxFrameText(wxFrame* parent, int _id, wxString _name, wxString _c /****************************************************************/ GameFrame::GameFrame(wxFrame* parent, Game& iGame): - AuxFrameText(parent, ID_Frame_Game, _("partie"), FRAMEGAME, wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP), + AuxFrameText(parent, ID_Frame_Game, _("Game history"), FRAMEGAME, wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP), m_game(iGame) { textbox->Clear(); @@ -542,7 +554,7 @@ BEGIN_EVENT_TABLE(ResultFrame, AuxFrame) END_EVENT_TABLE() ResultFrame::ResultFrame(wxFrame* parent, Game* iGame): - AuxFrame(parent, ID_Frame_Result, _("results"), FRAMERESULT) + AuxFrame(parent, ID_Frame_Result, _("Results"), FRAMERESULT) { reslist = new GfxResult(this, (MainFrame*)parent, iGame); diff --git a/wxwin/auxframes.h b/wxwin/auxframes.h index d1ae61d..baef185 100644 --- a/wxwin/auxframes.h +++ b/wxwin/auxframes.h @@ -160,7 +160,7 @@ class SearchFrame: public AuxFrame private: SearchPanel *panel; public: - SearchFrame(wxFrame*, Dictionary); + SearchFrame(wxFrame*, const Dictionary &); void Refresh(refresh_t force = REFRESH); }; @@ -171,12 +171,12 @@ public: class VerifFrame: public AuxFrame { protected: - Dictionary dic; + const Dictionary *dic; wxTextCtrl *word; wxStaticText *result; void verif(); public: - VerifFrame(wxFrame*, Dictionary); + VerifFrame(wxFrame*, const Dictionary&); void OnText(wxCommandEvent& event); void Refresh(refresh_t force = REFRESH); DECLARE_EVENT_TABLE() diff --git a/wxwin/confdimdlg.cc b/wxwin/confdimdlg.cc index 7195a7f..3531401 100644 --- a/wxwin/confdimdlg.cc +++ b/wxwin/confdimdlg.cc @@ -28,8 +28,6 @@ #include "confdimdlg.h" enum { - Button_Ok, - Button_Cancel, Button_Printer, Button_Page, Button_Save, @@ -39,10 +37,9 @@ enum { BEGIN_EVENT_TABLE(ConfDimDlg,wxDialog) EVT_CLOSE (ConfDimDlg::OnCloseWindow) - EVT_BUTTON(Button_Ok, ConfDimDlg::OnButtonOk) + EVT_BUTTON(wxID_OK, ConfDimDlg::OnButtonOk) + EVT_BUTTON(wxID_CANCEL, ConfDimDlg::OnButtonCancel) EVT_BUTTON(Button_Save, ConfDimDlg::OnButtonSave) - EVT_BUTTON(Button_Ok, ConfDimDlg::OnButtonOk) - EVT_BUTTON(Button_Cancel, ConfDimDlg::OnButtonCancel) EVT_BUTTON(Button_Printer, ConfDimDlg::OnConfPrinter) EVT_BUTTON(Button_Page, ConfDimDlg::OnConfPage) EVT_BUTTON(Button_FontHeader, ConfDimDlg::OnConfFontHead) @@ -58,14 +55,13 @@ max(int i,int j) ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) - : wxDialog(parent, -1, wxString(wxT("Eliot : Impression"))) + : wxDialog(parent, -1, wxString(wxT("Eliot: Printing"))) { int i; wxString choices[3]; - choices[0] = wxT("gauche"); -// XXX: choices[1] = wxT("centré"); - choices[1] = wxT("centre"); - choices[2] = wxT("droite"); + choices[0] = _("left"); + choices[1] = _("centered"); + choices[2] = _("right"); wxStaticText* Hcomment[5]; wxRect Hcommentrect[5]; wxRect Htitlerect[5]; @@ -119,31 +115,29 @@ ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) #define ESPSIZE wxSize(spacerect.GetRight() - spacerect.GetLeft(),-1) - wxStaticText* justif = new wxStaticText(this,-1,wxT("Justification"),wxPoint(0,0)); + wxStaticText* justif = new wxStaticText(this,-1,_("Alignment"),wxPoint(0,0)); wxRect justifrect = justif->GetRect(); - wxStaticText* space = new wxStaticText(this,-1,wxT("Espacement"),wxPoint(0,0)); + wxStaticText* space = new wxStaticText(this,-1,_("Spacing"),wxPoint(0,0)); wxRect spacerect = space->GetRect(); // heading, first part -// XXX: bfontheader = new wxButton(this,Button_FontHeader,wxT("Caractères"),HFONT); - bfontheader = new wxButton(this,Button_FontHeader,wxT("Caracteres"),HFONT); + bfontheader = new wxButton(this,Button_FontHeader,_("Font..."),HFONT); wxRect bfontheadrect = bfontheader->GetRect(); for(i=0; i<5; i++) { wxString txt; - txt << wxT("Titre colonne ") << (i+1); + txt << _("Title column ") << (i+1); Hcomment[i] = new wxStaticText(this,-1,txt,HCOMMENT(i)); Hcommentrect[i] = Hcomment[i]->GetRect(); } // text, first part -// XXX: bfonttext = new wxButton(this,Button_FontText,wxT("Caractères"),TFONT); - bfonttext = new wxButton(this,Button_FontText,wxT("Caracteres"),TFONT); + bfonttext = new wxButton(this,Button_FontText,_("Font..."),TFONT); wxRect bfonttextrect = bfonttext->GetRect(); for(i=0; i<5; i++) { wxString txt; - txt << wxT("Texte colonne ") << (i+1); + txt << _("Text column ") << (i+1); Tcomment[i] = new wxStaticText(this,-1,txt,TCOMMENT(i),wxSize(-1,-1)); Tcommentrect[i] = Tcomment[i]->GetRect(); } @@ -153,16 +147,15 @@ ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) { Htitle[i] = new wxTextCtrl(this,-1,wxT(""),HTITLE(i),wxSize(100,-1)); Htitlerect[i] = Htitle[i]->GetRect(); - Htitle[i]->SetToolTip(wxT("Texte du titre de la colonne")); + Htitle[i]->SetToolTip(_("Column heading")); Hjust[i] = new wxChoice(this,-1,HJUST(i),wxSize(-1,-1),3,choices); Hjustrect[i] = Hjust[i]->GetRect(); - Hjust[i]->SetToolTip(wxT("Justification du titre de la colonne")); + Hjust[i]->SetToolTip(_("Alignment of the column heading")); Hspaces[i] = new wxTextCtrl(this,-1,wxT("00"),HSPACES(i),ESPSIZE); Hspacesrect[i] = Hspaces[i]->GetRect(); -// XXX: Hspaces[i]->SetToolTip(wxT("Espacement des caractères du titre")); - Hspaces[i]->SetToolTip(wxT("Espacement des caracteres du titre")); + Hspaces[i]->SetToolTip(_("Spacing of the heading characters")); } @@ -171,22 +164,20 @@ ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) { Tdim[i] = new wxTextCtrl(this,-1,wxT(""),TDIM(i),wxSize(50,-1)); Tdimrect[i] = Tdim[i]->GetRect(); -// XXX: Tdim[i]->SetToolTip(wxT("Dimension intérieure de la colonne (en mm)")); - Tdim[i]->SetToolTip(wxT("Dimension interieure de la colonne (en mm)")); + Tdim[i]->SetToolTip(_("Inner dimension of the column (in mm)")); Tunit[i] = new wxStaticText(this,-1,wxT("mm"), - wxPoint(Tdimrect[i].GetRight()+ - HSPACE,TCOMMENTY(i)+2), - wxSize(-1,-1)); + wxPoint(Tdimrect[i].GetRight()+ + HSPACE,TCOMMENTY(i)+2), + wxSize(-1,-1)); Tjust[i] = new wxChoice(this,-1,TJUST(i),wxSize(-1,-1),3,choices); Tjustrect[i] = Tjust[i]->GetRect(); - Tjust[i]->SetToolTip(wxT("Justification du texte de la colonne")); + Tjust[i]->SetToolTip(_("Alignment of the column text")); Tspaces[i] = new wxTextCtrl(this,-1,wxT(""),TSPACES(i),ESPSIZE); Tspacesrect[i] = Tspaces[i]->GetRect(); -// XXX: Tspaces[i]->SetToolTip(wxT("Espacement des caractères")); - Tspaces[i]->SetToolTip(wxT("Espacement des caracteres")); + Tspaces[i]->SetToolTip(_("Characters spacing")); } justif->Move(wxPoint(Tjustrect[0].GetLeft(),bfontheadrect.GetBottom() @@ -237,11 +228,11 @@ ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) #define DIM wxSize(30,-1) // Left part - wxStaticText* dyh1text = new wxStaticText(this,-1,wxT("Titre esp. sup."),DYH1COMMENT,wxSize(-1,-1)); + wxStaticText* dyh1text = new wxStaticText(this,-1,_("Title spc. top"),DYH1COMMENT,wxSize(-1,-1)); wxRect dyh1textrect = dyh1text->GetRect(); - wxStaticText* dyt1text = new wxStaticText(this,-1,wxT("Texte esp. sup."),DYT1COMMENT,wxSize(-1,-1)); + wxStaticText* dyt1text = new wxStaticText(this,-1,_("Text spc. top"),DYT1COMMENT,wxSize(-1,-1)); wxRect dyt1textrect = dyt1text->GetRect(); - wxStaticText* dxbegintext = new wxStaticText(this,-1,wxT("Texte esp. gauche."),DXBEGINCOMMENT,wxSize(-1,-1)); + wxStaticText* dxbegintext = new wxStaticText(this,-1,_("Text spc. left"),DXBEGINCOMMENT,wxSize(-1,-1)); wxRect dxbegintextrect = dxbegintext->GetRect(); dyh1 = new wxTextCtrl(this,-1,wxT("00"),DYH1TEXT,DIM); @@ -258,11 +249,11 @@ ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) wxRect dxbeginmmrect = dxbeginmm->GetRect(); // Right part - wxStaticText* dyh2text = new wxStaticText(this,-1,wxT("Titre esp. inf."),DYH2COMMENT,wxSize(-1,-1)); + wxStaticText* dyh2text = new wxStaticText(this,-1,_("Title spc. bot."),DYH2COMMENT,wxSize(-1,-1)); wxRect dyh2textrect = dyh2text->GetRect(); - wxStaticText* dyt2text = new wxStaticText(this,-1,wxT("Texte esp. inf."),DYT2COMMENT,wxSize(-1,-1)); + wxStaticText* dyt2text = new wxStaticText(this,-1,_("Text spc. bot."),DYT2COMMENT,wxSize(-1,-1)); wxRect dyt2textrect = dyt2text->GetRect(); - wxStaticText* dxendtext = new wxStaticText(this,-1,wxT("Texte esp. droit."),DXENDCOMMENT,wxSize(-1,-1)); + wxStaticText* dxendtext = new wxStaticText(this,-1,_("Text spc. right"),DXENDCOMMENT,wxSize(-1,-1)); wxRect dxendtextrect = dxendtext->GetRect(); dyh2 = new wxTextCtrl(this,-1,wxT("00"),DYH2TEXT,DIM); @@ -292,25 +283,25 @@ ConfDimDlg::ConfDimDlg(wxWindow* parent, wxPrintData pd, wxPageSetupData psd) #define BCANCELPOINT wxPoint(Tspacesrect[0].GetRight() - CANCELWIDTH,BPOS) //#define BCANCELPOINT wxPoint(dxendmmrect.GetRight() - CANCELWIDTH,BPOS) - bprinter = new wxButton(this,Button_Printer,wxT("Imprimante"),BPRINTERPOINT); + bprinter = new wxButton(this,Button_Printer,_("Printer"),BPRINTERPOINT); wxRect bprinterrect = bprinter->GetRect(); - bprinter->SetToolTip(wxT("Configurer l'imprimante")); + bprinter->SetToolTip(_("Configure the printer")); - bpage = new wxButton(this,Button_Page,wxT("Page"),BPAGEPOINT); + bpage = new wxButton(this,Button_Page,_("Page"),BPAGEPOINT); // wxRect bpagerect = bpage->GetRect(); - bpage->SetToolTip(wxT("Configurer la taille de page")); + bpage->SetToolTip(_("Configure the dimensions of the page")); - bcancel = new wxButton(this,Button_Cancel,wxT("Annuler")); + bcancel = new wxButton(this,wxID_CANCEL); wxRect bcancelrect = bcancel->GetRect(); bcancel->Move(BCANCELPOINT); bcancelrect = bcancel->GetRect(); - bcancel->SetToolTip(wxT("Annuler les dernier changements et quitter")); + bcancel->SetToolTip(_("Cancel the last changes")); - bok = new wxButton(this,Button_Ok,wxT("OK")); + bok = new wxButton(this,wxID_OK); wxRect bokrect = bok->GetRect(); bok->Move(BOKPOINT); bokrect = bok->GetRect(); - bok->SetToolTip(wxT("Enregistrer les changements et quitter")); + bok->SetToolTip(_("Save the changes")); #define DLGWIDTH (bcancelrect.GetRight() + HSPACE) #define DLGHEIGHT (bokrect.GetBottom() + VSPACE) diff --git a/wxwin/configdb.cc b/wxwin/configdb.cc index 9cc5800..36c65e4 100644 --- a/wxwin/configdb.cc +++ b/wxwin/configdb.cc @@ -25,6 +25,7 @@ */ #include +#include "wx/wx.h" #include "ewx.h" #include "configdb.h" #include "wx/colordlg.h" @@ -199,7 +200,7 @@ wxString ConfigDB::getDicPath() wxString ConfigDB::getDicName() { - return ReadStr(DICNAME,wxT("Aucun Dictionnaire")); + return ReadStr(DICNAME,_("No dictionary")); } wxString ConfigDB::getTilePath() @@ -318,11 +319,11 @@ wxString ConfigDB::getNameH(int i) wxString res; switch (i) { - case 0: res = ReadStr(wxString(HNAME) + wxT("1"), wxT("Num")); break; - case 1: res = ReadStr(wxString(HNAME) + wxT("2"), wxT("Tirage")); break; - case 2: res = ReadStr(wxString(HNAME) + wxT("3"), wxT("Solution")); break; - case 3: res = ReadStr(wxString(HNAME) + wxT("4"), wxT("Pos")); break; - case 4: res = ReadStr(wxString(HNAME) + wxT("5"), wxT("Pts")); break; + case 0: res = ReadStr(wxString(HNAME) + wxT("1"), _("Nb")); break; + case 1: res = ReadStr(wxString(HNAME) + wxT("2"), _("Rack")); break; + case 2: res = ReadStr(wxString(HNAME) + wxT("3"), _("Solution")); break; + case 3: res = ReadStr(wxString(HNAME) + wxT("4"), _("Pos")); break; + case 4: res = ReadStr(wxString(HNAME) + wxT("5"), _("Pts")); break; default: res = wxT(""); break; } return res; diff --git a/wxwin/confsearch.cc b/wxwin/confsearch.cc index b4b29ec..3446c18 100644 --- a/wxwin/confsearch.cc +++ b/wxwin/confsearch.cc @@ -17,91 +17,85 @@ /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ewx.h" +#include "wx/wx.h" #include "wx/sizer.h" #include "confsearch.h" -enum { - Button_Ok, - Button_Cancel, - CheckBox_Joker, - CheckBox_Rack -}; -BEGIN_EVENT_TABLE(ConfSearchDlg,wxDialog) +BEGIN_EVENT_TABLE(ConfSearchDlg, wxDialog) EVT_CLOSE (ConfSearchDlg::OnCloseWindow) - EVT_BUTTON (Button_Ok, ConfSearchDlg::OnButtonOk) - EVT_BUTTON (Button_Cancel, ConfSearchDlg::OnButtonCancel) + EVT_BUTTON (wxID_OK, ConfSearchDlg::OnButtonOk) + EVT_BUTTON (wxID_CANCEL, ConfSearchDlg::OnButtonCancel) END_EVENT_TABLE() ConfSearchDlg::ConfSearchDlg(wxWindow* parent) - : wxDialog(parent, -1, wxString(wxT("Configuration recherche"))) + : wxDialog(parent, -1, wxString(wxT("Configuration recherche"))) { - joker_searching = new wxCheckBox(this,CheckBox_Joker,wxT("Recherche sur joker dans 7+1")); -// XXX: rack_checking = new wxCheckBox(this,CheckBox_Rack,wxT("Vérification de la validité des tirages")); - rack_checking = new wxCheckBox(this,CheckBox_Rack,wxT("Verification de la validite des tirages")); + joker_searching = new wxCheckBox(this, wxID_ANY, _("Search on joker in 7+1 panel")); + rack_checking = new wxCheckBox(this, wxID_ANY, _("Check rack validity")); - bcancel = new wxButton(this,Button_Cancel,wxT("Annuler"),wxPoint(-1,-1)); - bcancel->SetToolTip(wxT("Annuler les dernier changements et quitter")); - bok = new wxButton(this,Button_Ok,wxT("OK"),wxPoint(-1,-1)); - bok->SetToolTip(wxT("Enregistrer les changements et quitter")); + bcancel = new wxButton(this, wxID_CANCEL); + bcancel->SetToolTip(_("Cancel last changes")); + bok = new wxButton(this, wxID_OK); + bok->SetToolTip(_("Save the changes")); - wxBoxSizer *bsizer = new wxBoxSizer( wxHORIZONTAL); - bsizer->Add(bok, 1, wxALL, 1); - bsizer->Add(bcancel, 1, wxALL, 1); + wxBoxSizer *bsizer = new wxBoxSizer( wxHORIZONTAL); + bsizer->Add(bok, 1, wxALL, 1); + bsizer->Add(bcancel, 1, wxALL, 1); - wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL ); - sizer->Add(joker_searching, 1, wxEXPAND | wxALL, 2); - sizer->Add(rack_checking, 1, wxEXPAND | wxALL, 2); - sizer->Add(bsizer, 0, wxEXPAND, 0); + wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL ); + sizer->Add(joker_searching, 1, wxEXPAND | wxALL, 2); + sizer->Add(rack_checking, 1, wxEXPAND | wxALL, 2); + sizer->Add(bsizer, 0, wxEXPAND, 0); - SetAutoLayout(TRUE); - SetSizer(sizer); - sizer->Fit(this); - sizer->SetSizeHints(this); - readconf(); + SetAutoLayout(TRUE); + SetSizer(sizer); + sizer->Fit(this); + sizer->SetSizeHints(this); + readconf(); } -void + void ConfSearchDlg::readconf() { - joker_searching->SetValue(config.getJokerPlus1()); - rack_checking->SetValue(config.getRackChecking()); + joker_searching->SetValue(config.getJokerPlus1()); + rack_checking->SetValue(config.getRackChecking()); } -void + void ConfSearchDlg::writeconf() { - config.setJokerPlus1(joker_searching->GetValue()); - config.setRackChecking(rack_checking->GetValue()); + config.setJokerPlus1(joker_searching->GetValue()); + config.setRackChecking(rack_checking->GetValue()); } -void + void ConfSearchDlg::OnCloseWindow(wxCloseEvent&) { - if (IsModal() == TRUE) - EndModal(1); - else - this->Destroy(); + if (IsModal() == TRUE) + EndModal(1); + else + this->Destroy(); } -void + void ConfSearchDlg::OnButtonOk(wxCommandEvent&) { - writeconf(); - if (IsModal() == TRUE) - EndModal(1); - else - this->Destroy(); + writeconf(); + if (IsModal() == TRUE) + EndModal(1); + else + this->Destroy(); } -void + void ConfSearchDlg::OnButtonCancel(wxCommandEvent&) { - if (IsModal() == TRUE) - EndModal(1); - else - this->Destroy(); + if (IsModal() == TRUE) + EndModal(1); + else + this->Destroy(); } diff --git a/wxwin/gfxresult.cc b/wxwin/gfxresult.cc index aee097c..057979e 100644 --- a/wxwin/gfxresult.cc +++ b/wxwin/gfxresult.cc @@ -64,11 +64,11 @@ GfxResult::GfxResult(wxFrame *parent, MainFrame* _mf, Game* _game) : #else results->SetSingleStyle(wxLC_REPORT | wxLC_SINGLE_SEL); #endif - results->InsertColumn(0, wxT("Sol")); + results->InsertColumn(0, _("Word")); results->InsertColumn(1, wxT("*")); - results->InsertColumn(2, wxT("Pos")); - results->InsertColumn(3, wxT("Pts")); - results->SetToolTip(wxT("Resultats de la recherche")); + results->InsertColumn(2, _("Pos")); + results->InsertColumn(3, _("Pts")); + results->SetToolTip(_("Results of the search")); wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL); @@ -115,7 +115,7 @@ GfxResult::Refresh() debug(" GfxResult::Refresh : "); std::wstring rack = game->getCurrentPlayer().getCurrentRack().toString(); - if (savedrack != rack || static_cast(game)->getResults().size() != results->GetItemCount()) + if (savedrack != rack || (int)static_cast(game)->getResults().size() != results->GetItemCount()) { debug("changed (%ls -> %ls)",savedrack.c_str(),rack.c_str()); savedrack = rack; @@ -138,16 +138,16 @@ GfxResult::Search() if (game == NULL) return; - ((Training*)game)->search(); + static_cast(game)->search(); results->DeleteAllItems(); results->SetFont(config.getFont(LISTFONT)); - const Results &res = ((Training*)game)->getResults(); + const Results &res = static_cast(game)->getResults(); debug(" GfxResult::Search size = %d\n",res.size()); - for (int i = 0; i < res.size(); i++) + for (unsigned int i = 0; i < res.size(); i++) { - Round r = res.get(i); + const Round &r = res.get(i); //debug(" adding %s\n",r.toString().c_str()); wxString pts; wxString word = wxU(r.getWord().c_str()); @@ -170,7 +170,7 @@ GfxResult::Search() if (res.size() > 0) { results->SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED | wxLIST_MASK_STATE); - ((Training*)game)->testPlay(0); + static_cast(game)->testPlay(0); } } diff --git a/wxwin/main.cc b/wxwin/main.cc index 4d064d7..c6c4610 100644 --- a/wxwin/main.cc +++ b/wxwin/main.cc @@ -67,19 +67,21 @@ EliotApp::OnInit() wxConfigBase* config = wxConfigBase::Get(); config = NULL; #ifdef ENABLE_LOCALE - locale.Init(wxLocale::GetSystemLanguage(), - wxLOCALE_LOAD_DEFAULT | wxLOCALE_CONV_ENCODING); + locale.Init(); - wxLocale::AddCatalogLookupPathPrefix(wxT(".")); - wxLocale::AddCatalogLookupPathPrefix(wxT("..")); + // No need to search in the current directory, it is already done by default + // wxLocale::AddCatalogLookupPathPrefix(wxT(".")); + // Search for translations in the installation directory + wxLocale::AddCatalogLookupPathPrefix(wxT(LOCALEDIR)); locale.AddCatalog(wxT("eliot")); #ifdef __LINUX__ { wxLogNull noLog; - locale.AddCatalog(_T("fileutils")); + locale.AddCatalog(wxT("fileutils")); } #endif #endif + ConfigDB configdb; configdb.setFirstDefault(); MainFrame *mainframe = new MainFrame(configdb.getFramePos(wxT(APPNAME)), @@ -94,7 +96,7 @@ int EliotApp::OnExit() { GameFactory::Destroy(); - delete wxConfigBase::Set((wxConfigBase *) NULL); + delete wxConfigBase::Set(NULL); return 0; } diff --git a/wxwin/mainframe.cc b/wxwin/mainframe.cc index 11a6526..09a066f 100644 --- a/wxwin/mainframe.cc +++ b/wxwin/mainframe.cc @@ -24,10 +24,13 @@ * \date 2005 */ +#include "config.h" + #include #include #include #include +#include using namespace std; @@ -42,6 +45,7 @@ using namespace std; #include "dic.h" #include "game.h" +#include "training.h" #include "game_factory.h" #include "player.h" @@ -58,6 +62,7 @@ using namespace std; enum { Menu_Game_New = 1000, + Menu_Game_NewJoker, Menu_Game_Open, Menu_Game_Save, Menu_Game_Print, @@ -100,9 +105,6 @@ enum Menu_ShowGame = (IDBASE + ID_Frame_Game), Menu_ShowResult = (IDBASE + ID_Frame_Result), - Menu_Quit_Apropos = 4000, - Menu_Quit_Confirm, - Button_SetRack = 10000, Button_SetNew, Button_SetManual, @@ -112,17 +114,19 @@ enum ListCtrl_ID = 11000, Rack_ID, - Status_ID, + Status_ID }; BEGIN_EVENT_TABLE(MainFrame, wxFrame) // EVT_MENU(Menu_Game_New, MainFrame::OnMenuGameNew) + EVT_MENU(Menu_Game_NewJoker, MainFrame::OnMenuGameNew) EVT_MENU(Menu_Game_Open, MainFrame::OnMenuGameOpen) EVT_MENU(Menu_Game_Save, MainFrame::OnMenuGameSave) EVT_MENU(Menu_Game_Print, MainFrame::OnMenuGamePrint) EVT_MENU(Menu_Game_PrintPreview, MainFrame::OnMenuGamePrintPreview) EVT_MENU(Menu_Game_PrintPS, MainFrame::OnMenuGamePrintPS) + EVT_MENU(wxID_EXIT, MainFrame::OnMenuGameQuit) // EVT_MENU(Menu_Conf_Game_Dic, MainFrame::OnMenuConfGameDic) EVT_MENU(Menu_Conf_Game_Search, MainFrame::OnMenuConfGameSearch) @@ -133,8 +137,7 @@ BEGIN_EVENT_TABLE(MainFrame, wxFrame) EVT_MENU_RANGE(Menu_Conf_Aspect_BoardColour_Lines, Menu_Conf_Aspect_BoardColour_Default, MainFrame::OnMenuConfAspectBoardColour) EVT_MENU_RANGE(Menu_ShowVerif, Menu_ShowResult, MainFrame::OnMenuShowFrame) // - EVT_MENU(Menu_Quit_Apropos, MainFrame::OnMenuQuitApropos) - EVT_MENU(Menu_Quit_Confirm, MainFrame::OnMenuQuitConfirm) + EVT_MENU(wxID_ABOUT, MainFrame::OnMenuHelpAbout) // EVT_BUTTON(Button_Play, MainFrame::OnPlay) EVT_BUTTON(Button_SetRack, MainFrame::OnSetRack) @@ -152,40 +155,47 @@ END_EVENT_TABLE() // ****************************** // // ****************************** - -MainFrame::MainFrame(wxPoint __UNUSED__ pos_, wxSize size_) : - wxFrame((wxFrame *) NULL, -1, wxT(APPNAME), wxPoint(-1, -1), - size_, wxDEFAULT_FRAME_STYLE, wxT(APPNAME)), m_dic(NULL), m_game(NULL) + +MainFrame::MainFrame(wxPoint __UNUSED__ pos_, wxSize size_) + : wxFrame((wxFrame *) NULL, -1, wxT(APPNAME), wxPoint(-1, -1), + size_, wxDEFAULT_FRAME_STYLE, wxT(APPNAME)), + m_dic(NULL), m_game(NULL) { #if defined(ENABLE_RESLIST_IN_MAIN) reslist = NULL; #endif statusbar = NULL; - for(int i=0 ; i < MAX_FRAME_ID; i++) - auxframes_ptr[i] = NULL; - - wxString dicpath = config.getDicPath(); - Dic_load(&m_dic, dicpath.mb_str()); - if (m_dic == NULL) - { - wxCommandEvent event; - OnMenuConfGameDic(event); - } - m_game = GameFactory::Instance()->createTraining(m_dic); - if (m_game) - { - m_game->start(); - } + for (int i = 0 ; i < MAX_FRAME_ID; i++) + auxframes_ptr[i] = NULL; wxBoxSizer *listsizer = new wxBoxSizer(wxVERTICAL); rack = new wxTextCtrl(this, Rack_ID, wxU(""), wxPoint(-1, -1), wxSize(-1, -1), wxTE_PROCESS_ENTER); - listsizer->Add(rack , 0 , wxEXPAND | wxALL, 1); - rack->SetToolTip(_("Tirage")); + listsizer->Add(rack, 0, wxEXPAND | wxALL, 1); + rack->SetToolTip(_("Rack")); + rack->Enable(false); #ifdef ENABLE_RESLIST_IN_MAIN reslist = new GfxResult(this,(MainFrame*)this,m_game); listsizer->Add(reslist, 1, wxEXPAND | wxLEFT | wxRIGHT, 1); #endif + wxString dicpath = config.getDicPath(); + try + { + Dictionary *dic = new Dictionary(dicpath.mb_str().data()); + m_dic = dic; + m_game = GameFactory::Instance()->createTraining(*m_dic); + if (m_game) + { + m_game->start(); + } + } + catch (std::exception &e) + { + wxCommandEvent event; + // This will also start a new training game indirectly + OnMenuConfGameDic(event); + } + InitMenu(); statusbar = CreateStatusBar(2, 0, Status_ID); @@ -193,17 +203,22 @@ MainFrame::MainFrame(wxPoint __UNUSED__ pos_, wxSize size_) : statusbar->SetStatusWidths(2, ww); UpdateStatusBar(); - b_rackrandomset = new wxButton(this, Button_SetRack, _(" Tirage ")); + b_rackrandomset = new wxButton(this, Button_SetRack, _(" Rack ")); b_rackrandomnew = new wxButton(this, Button_SetNew, _(" Complement ")); - b_search = new wxButton(this, Button_Search, _(" Rechercher ")); - b_back = new wxButton(this, Button_PlayBack, _(" Arriere ")); - b_play = new wxButton(this, Button_Play, _(" Jouer ")); + b_search = new wxButton(this, Button_Search, _(" Search ")); + b_back = new wxButton(this, Button_PlayBack, _(" Back ")); + b_play = new wxButton(this, Button_Play, _(" Play ")); - b_rackrandomset->SetToolTip(_("Tirage aleatoire")); - b_rackrandomnew->SetToolTip(_("Complement aleatoire du tirage")); - b_search->SetToolTip( _("Recherche sur le tirage courant")); - b_back->SetToolTip( _("Revenir un coup en arriere")); - b_play->SetToolTip( _("Jouer le mot selectionne")); + b_rackrandomset->SetToolTip(_("Random rack")); + b_rackrandomset->Enable(false); + b_rackrandomnew->SetToolTip(_("Random complement of the rack")); + b_rackrandomnew->Enable(false); + b_search->SetToolTip( _("Search with the current rack")); + b_search->Enable(false); + b_back->SetToolTip( _("Go back one turn")); + b_back->Enable(false); + b_play->SetToolTip( _("Play the selected word")); + b_play->Enable(false); wxBoxSizer *buttonsizer = new wxBoxSizer(wxHORIZONTAL); buttonsizer->Add(b_rackrandomset, 1, wxEXPAND | wxTOP | wxBOTTOM | wxLEFT , 1); @@ -237,15 +252,12 @@ MainFrame::~MainFrame() config.setFrameSize(wxT(APPNAME), GetClientSize()); if (m_game != NULL) - { - GameFactory::Instance()->releaseGame(*m_game); - m_game = NULL; - } + { + GameFactory::Instance()->releaseGame(*m_game); + m_game = NULL; + } - if (m_dic) - { - Dic_destroy(m_dic); - } + delete m_dic; } // ****************************** @@ -257,88 +269,78 @@ MainFrame::InitMenu() { // menus wxMenu *menu_game = new wxMenu; - menu_game->Append(Menu_Game_New, _("Nouvelle"), _("Demarrer une nouvelle partie")); - menu_game->Append(Menu_Game_Open, _("Charger..."), _("Charger une partie")); - menu_game->Append(Menu_Game_Save, _("Sauver..."), _("Sauver cette partie")); + menu_game->Append(Menu_Game_New, _("&New game\tctrl+n"), _("Start a new game")); + menu_game->Append(Menu_Game_NewJoker, _("New &joker game\tctrl+j"), _("Start a new joker game")); menu_game->AppendSeparator(); - menu_game->Append(Menu_Game_Print, _("Imprimer..."), _("Imprimer cette partie")); - menu_game->Append(Menu_Game_PrintPreview, _("Preimpression"), _("Preimpression de la partie")); + menu_game->Append(Menu_Game_Open, _("&Load...\tctrl+l"), _("Load a game")); + menu_game->Append(Menu_Game_Save, _("&Save as...\tctrl+s"), _("Save the current game")); + menu_game->AppendSeparator(); + menu_game->Append(Menu_Game_Print, _("&Print...\tctrl+p"), _("Print this game")); + menu_game->Append(Menu_Game_PrintPreview, _("Print pre&view..."), _("Print preview of the game")); #ifdef ENABLE_SAVE_POSTSCRIPT - menu_game->AppendSeparator(); - menu_game->Append(Menu_Game_PrintPS, _("Imprimer du PostScript"), _("Imprimer dans un fichier PostScript")); + menu_game->Append(Menu_Game_PrintPS, _("Print in PostS&cript..."), _("Print in a PostScript file")); #endif + menu_game->AppendSeparator(); + menu_game->Append(wxID_EXIT, _("&Quit"), _("Quit Eliot")); // wxMenu *menu_conf_game = new wxMenu; - menu_conf_game->Append(Menu_Conf_Game_Dic, _("Dictionnaire"), _("Choix du dictionnaire")); - menu_conf_game->Append(Menu_Conf_Game_Search, _("Recherche"), _("Options de recherche")); - // - wxMenu *menu_tileback = new wxMenu; - menu_tileback->Append(Menu_Conf_Aspect_BoardColour_Letters , _("Lettres jouees"), _("Lettres jouees sur la grille")); - menu_tileback->Append(Menu_Conf_Aspect_BoardColour_TestLetters , _("Lettres provisoires"), _("Lettres du mot a jouer")); - menu_tileback->AppendSeparator(); - menu_tileback->Append(Menu_Conf_Aspect_BoardColour_TileBack , _("Fonds lettres jouees"), _("Fonds des pions sur la grille")); - menu_tileback->Append(Menu_Conf_Aspect_BoardColour_TestTileBack, _("Fonds lettres provisoires"), _("Fonds des pions sur la grille")); + menu_conf_game->Append(Menu_Conf_Game_Dic, _("&Dictionary..."), _("Choose a dictionary")); + menu_conf_game->Append(Menu_Conf_Game_Search, _("&Search..."), _("Search options")); // wxMenu *menu_conf_board_colour = new wxMenu; - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Background, _("Fond"), _("Couleur du fond")); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Lines, _("Lignes"), _("Couleur des lignes")); - menu_conf_board_colour->Append(Menu_Conf_Tile, _("Pions et lettres"), menu_tileback, _("Pions et lettres")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Background, _("&Background..."), _("Background color")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Lines, _("L&ines..."), _("Color of the lines")); menu_conf_board_colour->AppendSeparator(); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Letters, _("Lettres jouees"), _("Lettres jouees sur la grille")); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_TestLetters, _("Lettres provisoires"), _("Lettres du mot a jouer")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Letters, _("&Played letters..."), _("Color of the letters played on the board")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_TestLetters, _("&Temporary letters..."), _("Color of the letters of the temporary word")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_TileBack , _("B&ackground of played letters..."), _("Background color of the letters played on the board")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_TestTileBack, _("Ba&ckground of temporary letters..."), _("Background color of the temporary letters on the board")); menu_conf_board_colour->AppendSeparator(); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Wx2, _("Mot compte double"), _("Mot compte double")); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Wx3, _("Mot compte triple"), _("Mot compte triple")); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Lx2, _("Lettre compte double"), _("Lettre compte double")); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Lx3, _("Lettre compte triple"), _("Lettre compte triple")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Wx2, _("Double &letter..."), _("Color of the \"double letter\" squares")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Wx3, _("Triple l&etter..."), _("Color of the \"triple letter\" squares")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Lx2, _("Double &word..."), _("Color of the \"double word\" squares")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Lx3, _("Triple w&ord..."), _("Color of the \"triple word\" squares")); menu_conf_board_colour->AppendSeparator(); - menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Default, _("Couleurs d'origine"), _("Retrouver les couleurs d'origine")); + menu_conf_board_colour->Append(Menu_Conf_Aspect_BoardColour_Default, _("&Default colors"), _("Restore the default colors")); // wxMenu *menu_conf_board_font = new wxMenu; -// XXX: menu_conf_board_font->Append(Menu_Conf_Aspect_Font_Search, _("Lettres de recherche"), _("Police de caractères pour les recherches")); - menu_conf_board_font->Append(Menu_Conf_Aspect_Font_Search, _("Lettres de recherche"), _("Police de caracteres pour les recherches")); -// XXX: menu_conf_board_font->Append(Menu_Conf_Aspect_Font_Board, _("Lettres de la grille"), _("Police de caractères de la grille")); - menu_conf_board_font->Append(Menu_Conf_Aspect_Font_Board, _("Lettres de la grille"), _("Police de caracteres de la grille")); + menu_conf_board_font->Append(Menu_Conf_Aspect_Font_Search, _("&Search letters..."), _("Font for the search")); // wxMenu *menu_conf = new wxMenu; - menu_conf->Append(Menu_Conf_Game, _("Jeu"), menu_conf_game, _("Configuration du jeu")); - menu_conf->Append(Menu_Conf_Aspect_Font, _("Fonte des lettres"), menu_conf_board_font, _("Modification des fontes")); - menu_conf->Append(Menu_Conf_Aspect_BoardColour, _("Couleurs de la grille"), menu_conf_board_colour, _("Modification des couleurs")); - menu_conf->Append(Menu_Conf_Print, _("Impression"), _("Dimensions de la partie")); + menu_conf->Append(Menu_Conf_Game, _("&Game"), menu_conf_game, _("Configuration of the game")); + menu_conf->Append(Menu_Conf_Aspect_Font, _("&Fonts"), menu_conf_board_font, _("Configuration of the fonts")); + menu_conf->Append(Menu_Conf_Aspect_BoardColour, _("&Colors"), menu_conf_board_colour, _("Configuration of the colors")); + menu_conf->Append(Menu_Conf_Print, _("&Printing..."), _("Configuration of the printing parameters")); // wxMenu *menu_frame = new wxMenu; - menu_frame->Append(Menu_ShowBoard, _("Grille"), _("Grille de jeu")); -// XXX: menu_frame->Append(Menu_ShowVerif, _("Vérification"), _("Vérification d'un mot dans le dictionnaire")); - menu_frame->Append(Menu_ShowVerif, _("Verification"), _("Verification d'un mot dans le dictionnaire")); - menu_frame->Append(Menu_ShowSearch, _("Recherche"), _("Recherche dans le dictionnaire")); + menu_frame->AppendCheckItem(Menu_ShowBoard, _("&Board"), _("Game board")); + menu_frame->AppendCheckItem(Menu_ShowBag, _("Ba&g"), _("Remaining letters in the bag")); + menu_frame->AppendCheckItem(Menu_ShowVerif, _("&Check"), _("Check a word in the dictionary")); + menu_frame->AppendCheckItem(Menu_ShowSearch, _("&Search"), _("Search in the dictionary")); menu_frame->AppendSeparator(); - menu_frame->Append(Menu_ShowPlus1, _("Tirage + 1"), _("Lettres du tirage plus une")); - menu_frame->Append(Menu_ShowRacc, _("Raccords"), _("Raccords sur un mot de la recherche")); - menu_frame->Append(Menu_ShowBenj, _("Benjamins"), _("Benjamins sur un mot de la recherche")); + menu_frame->AppendCheckItem(Menu_ShowPlus1, _("&Rack + 1"), _("Letters of the rack plus one")); + menu_frame->AppendCheckItem(Menu_ShowRacc, _("R&accords"), _("Raccords on a word of the search")); + menu_frame->AppendCheckItem(Menu_ShowBenj, _("&Benjamins"), _("Benjamins on a word of the search")); menu_frame->AppendSeparator(); - menu_frame->Append(Menu_ShowBag, _("Sac"), _("Lettres restantes dans le sac")); - menu_frame->AppendSeparator(); - menu_frame->Append(Menu_ShowGame, _("Partie"), _("Partie")); + menu_frame->AppendCheckItem(Menu_ShowGame, _("Game &history"), _("Game history")); #ifndef ENABLE_RESLIST_IN_MAIN - menu_frame->Append(Menu_ShowResult, _("Resultats"), _("Resultats")); + menu_frame->Append(Menu_ShowResult, _("R&esults"), _("Results")); #endif // - wxMenu *menu_quit = new wxMenu; - menu_quit->Append(Menu_Quit_Apropos, _("A propos..."), _("A propos d'Eliot")); - menu_quit->Append(Menu_Quit_Confirm, _("Quitter"), _("Quitter")); + wxMenu *menu_help = new wxMenu; + menu_help->Append(wxID_ABOUT, _("&About..."), _("About Eliot")); // wxMenuBar *menu_bar = new wxMenuBar; - menu_bar->Append(menu_game, _("Partie")); - menu_bar->Append(menu_conf, _("Configuration")); -// XXX: menu_bar->Append(menu_frame, _("Fenêtres")); - menu_bar->Append(menu_frame, _("Fenetres")); - menu_bar->Append(menu_quit, _("Quitter")); + menu_bar->Append(menu_game, _("&Game")); + menu_bar->Append(menu_conf, _("&Settings")); + menu_bar->Append(menu_frame, _("&Windows")); + menu_bar->Append(menu_help, _("&Help")); SetMenuBar(menu_bar); } // ******************* -// +// // ******************* void @@ -352,30 +354,43 @@ MainFrame::OnCloseWindow(wxCloseEvent&) // ******************* void -MainFrame::OnMenuGameNew(wxCommandEvent&) +MainFrame::OnMenuGameNew(wxCommandEvent& event) { if (m_dic == NULL) - { - wxMessageBox(_("Il n'y a pas de dictionnaire selectionne"), _("Eliot: erreur"), + { + wxMessageBox(_("No dictionary selected"), _("Eliot: error"), wxICON_INFORMATION | wxOK); return; - } + } // TODO("selection du type de partie dans OnMenuGameNew\n"); if (m_game != NULL) - { - GameFactory::Instance()->releaseGame(*m_game); - m_game = NULL; - } + { + GameFactory::Instance()->releaseGame(*m_game); + m_game = NULL; + } + + m_game = GameFactory::Instance()->createTraining(*m_dic); + // Joker game? + if (event.GetId() == Menu_Game_NewJoker) + m_game->setVariant(Game::kJOKER); - m_game = GameFactory::Instance()->createTraining(m_dic); m_game->start(); - rack->SetValue(wxU("")); + rack->SetValue(wxT("")); InitFrames(); #ifdef ENABLE_RESLIST_IN_MAIN reslist->SetGame(m_game); #endif + // Re-enable the main buttons + b_rackrandomset->Enable(true); + b_rackrandomnew->Enable(true); + b_search->Enable(true); + b_back->Enable(true); + b_play->Enable(true); + rack->Enable(true); + + UpdateStatusBar(); UpdateFrames(AuxFrame::FORCE_REFRESH); } @@ -387,63 +402,58 @@ MainFrame::OnMenuGameNew(wxCommandEvent&) void MainFrame::OnMenuGameOpen(wxCommandEvent&) { - wxString txt; - wxFileDialog dialog(this, _("Ouvrir une partie"), wxT(""), wxT(""), wxT("*"), wxOPEN); + wxFileDialog dialog(this, _("Load a game"), wxT(""), wxT(""), wxT("*"), wxOPEN); if (m_dic == NULL) { - wxMessageBox(_("Il n'y a pas de dictionnaire selectionne"), _("Eliot: erreur"), + wxMessageBox(_("No dictionary selected"), _("Eliot: error"), wxICON_INFORMATION | wxOK); return; } if (dialog.ShowModal() != wxID_OK) - { - return; - } + { + return; + } if (m_game != NULL) - { - GameFactory::Instance()->releaseGame(*m_game); - m_game = NULL; - } + { + GameFactory::Instance()->releaseGame(*m_game); + m_game = NULL; + } FILE* fin; if ((fin = fopen(dialog.GetPath().mb_str(), "rb")) == NULL) - { - txt << _("Impossible d'ouvrir") << dialog.GetPath(); - wxMessageDialog msg(this, txt, _("Ouverture d'une partie")); - msg.ShowModal(); - return ; - } + { + wxString txt; + txt << _("Cannot open ") << dialog.GetPath(); + wxMessageDialog msg(this, txt, _("Load a game")); + msg.ShowModal(); + return ; + } - m_game = Game::load(fin, m_dic); + m_game = Game::load(fin, *m_dic); fclose(fin); if (m_game == NULL) - { - wxMessageDialog msg(this, - _("Erreur pendant la lecture de la partie"), - _("chargement de partie")); - msg.ShowModal(); - return; - } + { + wxMessageDialog msg(this, + _("Error while loading the game"), + _("Invalid game")); + msg.ShowModal(); + return; + } if (m_game->getHistory().getSize() == 0) - { - wxMessageDialog msg(this, - _("Erreur pendant la lecture de la partie"), - _("La partie est vide")); - msg.ShowModal(); - return; - } - - std::wstring r; - - if (m_game->getHistory().getSize() >= 0) { - r = m_game->getCurrentPlayer().getCurrentRack().toString(); + wxMessageDialog msg(this, + _("Error while loading the game"), + _("The game is empty")); + msg.ShowModal(); + return; } + std::wstring r = m_game->getCurrentPlayer().getCurrentRack().toString(); + rack->SetValue(wxU(r.c_str())); // update gfxboard and all frames InitFrames(); @@ -462,16 +472,15 @@ MainFrame::OnMenuGameOpen(wxCommandEvent&) void MainFrame::OnMenuGameSave(wxCommandEvent& WXUNUSED(event)) { - wxFileDialog dialog(this, _("Sauver une partie"), wxT(""), wxT(""), wxT("*"), wxSAVE|wxOVERWRITE_PROMPT); + wxFileDialog dialog(this, _("Save the game"), wxT(""), wxT(""), wxT("*"), wxSAVE|wxOVERWRITE_PROMPT); if (dialog.ShowModal() == wxID_OK) { ofstream fout(dialog.GetPath().mb_str()); if (fout.rdstate() == ios::failbit) { wxString txt; -// XXX: txt << _("Impossible de créer ") << dialog.GetPath(); - txt << _("Impossible de creer ") << dialog.GetPath(); - wxMessageDialog msg(this, txt, _("Sauvegarde de la partie")); + txt << _("Cannot create ") << dialog.GetPath(); + wxMessageDialog msg(this, txt, _("Save the game")); msg.ShowModal(); return ; } @@ -490,7 +499,7 @@ MainFrame::OnMenuGamePrint(wxCommandEvent& WXUNUSED(event)) // TODO: gray out the menu instead... if (m_game == NULL) { - wxMessageBox(_("Pas de partie en cours"), _("Eliot: erreur"), + wxMessageBox(_("No on going game"), _("Eliot: error"), wxICON_INFORMATION | wxOK); return; } @@ -498,8 +507,10 @@ MainFrame::OnMenuGamePrint(wxCommandEvent& WXUNUSED(event)) wxPrinter printer(&printDialogData); GamePrintout printout(*m_game); if (!printer.Print(this, &printout, TRUE)) -// XXX: wxMessageBox(wxT("Impression non effectuée.")); - wxMessageBox(_("Impression non effectuee.")); + { + wxMessageBox(_("Printing not done"), _("Printing"), + wxOK | wxICON_ERROR); + } } void @@ -508,7 +519,7 @@ MainFrame::OnMenuGamePrintPreview(wxCommandEvent& WXUNUSED(event)) // TODO: gray out the menu instead... if (m_game == NULL) { - wxMessageBox(_("Pas de partie en cours"), _("Eliot: erreur"), + wxMessageBox(_("No on going game"), _("Eliot: error"), wxICON_INFORMATION | wxOK); return; } @@ -520,15 +531,12 @@ MainFrame::OnMenuGamePrintPreview(wxCommandEvent& WXUNUSED(event)) if (!preview->Ok()) { delete preview; -// XXX: msg << _("Problème de prévisualisation.\n") - msg << _("Probleme de previsualisation.\n") -// XXX: << _("Il se peut que l'imprimante par défaut soit mal initialisée"); - << _("Il se peut que l'imprimante par defaut soit mal initialisee"); -// XXX: wxMessageBox(msg, _("Impression (prévisualisation)"), wxOK); - wxMessageBox(msg, _("Impression (previsualisation)"), wxOK); + msg << _("Print preview problem.\n") + << _("The printer may not be correctly initialized"); + wxMessageBox(msg, _("Print preview"), wxOK); return; } - wxPreviewFrame *frame = new wxPreviewFrame(preview, this, _("Impression"), + wxPreviewFrame *frame = new wxPreviewFrame(preview, this, _("Printing"), wxPoint(-1, -1), wxSize(600, 550)); frame->Centre(wxBOTH); frame->Initialize(); @@ -542,12 +550,11 @@ MainFrame::OnMenuGamePrintPS(wxCommandEvent& WXUNUSED(event)) // TODO: gray out the menu instead... if (m_game == NULL) { - wxMessageBox(_("Pas de partie en cours"), _("Eliot: erreur"), + wxMessageBox(_("No on going game"), _("Eliot: error"), wxICON_INFORMATION | wxOK); return; } - wxString txt; - wxFileDialog dialog(this, _("Imprimer dans un fichier PostScript"), wxT(""), wxT(""), wxT("*.ps"), wxSAVE|wxOVERWRITE_PROMPT); + wxFileDialog dialog(this, _("Print to a PostScript file"), wxT(""), wxT(""), wxT("*.ps"), wxSAVE|wxOVERWRITE_PROMPT); if (dialog.ShowModal() == wxID_OK) { wxPrintData printdataPS; @@ -565,76 +572,54 @@ MainFrame::OnMenuGamePrintPS(wxCommandEvent& WXUNUSED(event)) GamePrintout printout(*m_game); if (!printer.Print(this, &printout, FALSE)) { -// XXX: wxMessageBox(_("Impression non effectuée.")); - wxMessageBox(_("Impression non effectuee.")); - } - else - { - wxString msg; -// XXX: msg << _("Dessin effectué dans ") << dialog.GetPath() << _("\n"); - msg << _("Dessin effectue dans ") << dialog.GetPath() << _("\n"); - wxMessageBox(msg, _("Sauvegarde PostScript"), wxOK); + wxMessageBox(_("Printing not done"), + _("PostScript printing"), wxOK | wxICON_ERROR); } } else { - wxString msg; - msg << _("impossible d'initialiser le traitement PostScript.\n"); - wxMessageBox(msg, _("Sauvegarde PostScript"), wxOK); + wxMessageBox(_("Cannot initialize PostScript printer"), + _("PostScript printing"), wxOK | wxICON_ERROR); } } #endif } +void +MainFrame::OnMenuGameQuit(wxCommandEvent& WXUNUSED(event)) +{ + Close(TRUE); +} + + + // ******************* -// Dictionnary Loading +// Dictionary Loading // ******************* void MainFrame::OnMenuConfGameDic(wxCommandEvent& WXUNUSED(event)) { - wxString txt, msg, dicpath; - wxFileDialog dialog(this, _("Choisir un dictionnaire"), wxT(""), wxT("*.dawg"), wxT("*.dawg"), wxOPEN); + wxFileDialog dialog(this, _("Choose a dictionary"), wxT(""), wxT("*.dawg"), wxT("*.dawg"), wxOPEN); if (dialog.ShowModal() == wxID_OK) { wxString dicpath = dialog.GetPath(); - Dictionary dic; - int res = Dic_load(&dic, dicpath.mb_str()); - if (res == 0) + try { - if (m_dic) - { - Dic_destroy(m_dic); - } - + Dictionary *dic = new Dictionary(dicpath.mb_str().data()); + delete m_dic; m_dic = dic; config.setDicPath(dialog.GetPath(), ::wxFileNameFromPath(dialog.GetPath())); + wxCommandEvent event; + OnMenuGameNew(event); } - else + catch (std::exception &e) { - switch (res) - { - case 0: /* cas normal */ break; -// XXX: case 1: msg << _("chargement: problème d'ouverture de ") << dicpath << _("\n"); break; - case 1: msg << _("chargement: probleme d'ouverture de ") << dicpath << _("\n"); break; -// XXX: case 2: msg << _("chargement: mauvais en-tête de dictionnaire\n"); break; - case 2: msg << _("chargement: mauvais en-tete de dictionnaire\n"); break; -// XXX: case 3: msg << _("chargement: problème 3 d'allocation mémoire\n"); break; - case 3: msg << _("chargement: probleme 3 d'allocation memoire\n"); break; -// XXX: case 4: msg << _("chargement: problème 4 d'allocation mémoire\n"); break; - case 4: msg << _("chargement: probleme 4 d'allocation memoire\n"); break; -// XXX: case 5: msg << _("chargement: problème de lecture des arcs du dictionnaire\n"); break; - case 5: msg << _("chargement: probleme de lecture des arcs du dictionnaire\n"); break; -// XXX: default: msg << _("chargement: problème non-répertorié\n"); break; - default: msg << _("chargement: probleme non-repertorie\n"); break; - } - wxMessageDialog dlg(NULL, msg, wxT(APPNAME)); + wxMessageDialog dlg(NULL, wxU(e.what()), wxT(APPNAME)); dlg.ShowModal(); } } - UpdateStatusBar(); - UpdateFrames(); } // **************** @@ -717,8 +702,8 @@ MainFrame::OnMenuConfAspectBoardColour(wxCommandEvent& event) case Menu_Conf_Aspect_BoardColour_Background: attr = wxString(BCOLOURBACKGROUND); break; case Menu_Conf_Aspect_BoardColour_Letters: attr = wxString(BCOLOURLETTERS); break; case Menu_Conf_Aspect_BoardColour_TestLetters: attr = wxString(BCOLOURTSTLETTERS); break; - case Menu_Conf_Aspect_BoardColour_TileBack: attr = wxString(BTILEBACKGROUND); break; - case Menu_Conf_Aspect_BoardColour_TestTileBack: attr = wxString(BTSTTILEBACKGROUND); break; + case Menu_Conf_Aspect_BoardColour_TileBack: attr = wxString(BTILEBACKGROUND); break; + case Menu_Conf_Aspect_BoardColour_TestTileBack: attr = wxString(BTSTTILEBACKGROUND); break; case Menu_Conf_Aspect_BoardColour_Default: attr = wxU("Default"); break; default: INCOMPLETE; break; } @@ -732,30 +717,22 @@ MainFrame::OnMenuConfAspectBoardColour(wxCommandEvent& event) } //************************************************************************************** -// MENU QUIT +// MENU HELP //************************************************************************************** void -MainFrame::OnMenuQuitApropos(wxCommandEvent& WXUNUSED(event)) +MainFrame::OnMenuHelpAbout(wxCommandEvent& WXUNUSED(event)) { wxString msg; - // XXX: msg << wxT("Eliot\n© Antoine Fraboulet 1999-2004\n\n"); - msg << wxT("Eliot\nCopyright Antoine Fraboulet 1999-2006\n\n"); - msg << wxT("This program is free software; you can redistribute it and/or modify\n"); - msg << wxT("it under the terms of the GNU General Public License as published by\n"); - msg << wxT("the Free Software Foundation; either version 2 of the License, or\n"); - msg << wxT("(at your option) any later version.\n\n"); - msg << wxT("Version ") << wxT(VERSION) << wxT("\n"); - wxMessageBox(msg, _("A propos d'Eliot"), wxICON_INFORMATION | wxOK); + msg.Printf(wxT("Eliot %s\n\n"), wxT(VERSION)); + msg << wxT("Copyright (C) 1999-2007 - Antoine Fraboulet & Olivier Teuliere\n\n"); + msg << _("This program is free software; you can redistribute it and/or modify " \ + "it under the terms of the GNU General Public License as published by " \ + "the Free Software Foundation; either version 2 of the License, or " \ + "(at your option) any later version."); + wxMessageBox(msg, _("About Eliot"), wxICON_INFORMATION | wxOK); } -void -MainFrame::OnMenuQuitConfirm(wxCommandEvent& WXUNUSED(event)) -{ - Close(TRUE); -} - - //************************************************************************************** // BUTTONS //************************************************************************************** @@ -768,22 +745,22 @@ MainFrame::OnSetRack(wxCommandEvent& event) Game::set_rack_mode mode = Game::RACK_NEW; debug("OnSetRack "); switch ((id = event.GetId())) - { + { case Button_SetRack: - mode = Game::RACK_ALL; - debug("PlayedRack::RACK_ALL\n"); - break; + mode = Game::RACK_ALL; + debug("PlayedRack::RACK_ALL\n"); + break; case Button_SetNew: - mode = Game::RACK_NEW; - debug("PlayedRack::RACK_NEW\n"); - break; + mode = Game::RACK_NEW; + debug("PlayedRack::RACK_NEW\n"); + break; case Button_SetManual: - mode = Game::RACK_MANUAL; - debug("PlayedRack::RACK_MANUAL\n"); - break; + mode = Game::RACK_MANUAL; + debug("PlayedRack::RACK_MANUAL\n"); + break; default: - return; - } + return; + } SetRack(mode); } @@ -810,16 +787,16 @@ MainFrame::OnPlay(wxCommandEvent& event) int dir = 1; int id = event.GetId(); switch (id) - { + { case Button_Play: - dir = 1; + dir = 1; break; case Button_PlayBack: - dir = -1; + dir = -1; break; default: break; - } + } Play(dir); } @@ -832,49 +809,59 @@ MainFrame::InitFrames() { debug("InitFrames start : \n"); if (m_game == NULL) - { - debug("m_game == NULL\n"); - return; - } - - for(int i=0 ; i < MAX_FRAME_ID; i++) - { - if (auxframes_ptr[i] != NULL) - { - debug(" delete frame %d\n",i); - delete auxframes_ptr[i]; - } - } - - auxframes_ptr[ ID_Frame_Verif ] = new VerifFrame (this, m_game->getDic()); + { + debug("m_game == NULL\n"); + return; + } + + for (int i = 0 ; i < MAX_FRAME_ID; i++) + { + debug(" delete frame %d\n",i); + delete auxframes_ptr[i]; + } + + auxframes_ptr[ID_Frame_Verif] = new VerifFrame (this, m_game->getDic()); debug("0 : Verif\n"); - auxframes_ptr[ ID_Frame_Search ] = new SearchFrame(this, m_game->getDic()); + auxframes_ptr[ID_Frame_Search] = new SearchFrame(this, m_game->getDic()); debug("1 : Search\n"); - auxframes_ptr[ ID_Frame_Plus1 ] = new Plus1Frame (this, m_game); + auxframes_ptr[ID_Frame_Plus1] = new Plus1Frame (this, m_game); debug("2 : Plus1\n"); - auxframes_ptr[ ID_Frame_Racc ] = new RaccFrame (this, m_game); + auxframes_ptr[ID_Frame_Racc] = new RaccFrame (this, m_game); debug("3 : Racc\n"); - auxframes_ptr[ ID_Frame_Benj ] = new BenjFrame (this, m_game); + auxframes_ptr[ID_Frame_Benj] = new BenjFrame (this, m_game); debug("4 : Benj\n"); - auxframes_ptr[ ID_Frame_Bag ] = new BagFrame (this, *m_game); + auxframes_ptr[ID_Frame_Bag] = new BagFrame (this, *m_game); debug("5 : Bag\n"); - auxframes_ptr[ ID_Frame_Board ] = new BoardFrame (this, *m_game); + auxframes_ptr[ID_Frame_Board] = new BoardFrame (this, *m_game); debug("6 : Board\n"); - auxframes_ptr[ ID_Frame_Game ] = new GameFrame (this, *m_game); + auxframes_ptr[ID_Frame_Game] = new GameFrame (this, *m_game); debug("7 : Game\n"); #ifndef ENABLE_RESLIST_IN_MAIN - auxframes_ptr[ ID_Frame_Result ] = new ResultFrame(this, m_game); + auxframes_ptr[ID_Frame_Result] = new ResultFrame(this, m_game); debug("8 : Result\n"); #endif - + for (int i = MIN_FRAME_ID; i < MAX_FRAME_ID; i++) { - if (auxframes_ptr[i] != NULL) - { - auxframes_ptr[i]->Reload(); - debug("reload %d\n",i); - } + if (auxframes_ptr[i] != NULL) + { + auxframes_ptr[i]->Reload(); + debug("reload %d\n",i); + } } + + // Check the corresponding menu item if the window is visible + GetMenuBar()->Check(Menu_ShowVerif, auxframes_ptr[ID_Frame_Verif]->IsShown()); + GetMenuBar()->Check(Menu_ShowSearch, auxframes_ptr[ID_Frame_Search]->IsShown()); + GetMenuBar()->Check(Menu_ShowPlus1, auxframes_ptr[ID_Frame_Plus1]->IsShown()); + GetMenuBar()->Check(Menu_ShowRacc, auxframes_ptr[ID_Frame_Racc]->IsShown()); + GetMenuBar()->Check(Menu_ShowBenj, auxframes_ptr[ID_Frame_Benj]->IsShown()); + GetMenuBar()->Check(Menu_ShowBag, auxframes_ptr[ID_Frame_Bag]->IsShown()); + GetMenuBar()->Check(Menu_ShowBoard, auxframes_ptr[ID_Frame_Board]->IsShown()); + GetMenuBar()->Check(Menu_ShowGame, auxframes_ptr[ID_Frame_Game]->IsShown()); +#ifndef ENABLE_RESLIST_IN_MAIN + GetMenuBar()->Check(Menu_ShowResult, auxframes_ptr[ID_Frame_Result]->IsShown()); +#endif debug("InitFrames end ok.\n"); } @@ -883,21 +870,21 @@ MainFrame::OnMenuShowFrame(wxCommandEvent& event) { int id; id = event.GetId(); - + if (!GetMenuBar()->FindItem(id)) return; id -= IDBASE; - + if ((id < 0) || (id >= MAX_FRAME_ID)) { - INCOMPLETE; - return; + INCOMPLETE; + return; } - + if (auxframes_ptr[id] == NULL) { - debug("ShowFrame: auxframes_ptr[%d] == NULL\n", id); - return; + debug("ShowFrame: auxframes_ptr[%d] == NULL\n", id); + return; } auxframes_ptr[id]->SwitchDisplay(); debug("ShowFrame: SwitchDisplay frame %d\n",id); @@ -912,15 +899,15 @@ MainFrame::UpdateFrames(AuxFrame::refresh_t force) { for (int id = 0; id < MAX_FRAME_ID; id++) { - if (auxframes_ptr[id]) - { - auxframes_ptr[id]->Refresh(force); - } + if (auxframes_ptr[id]) + { + auxframes_ptr[id]->Refresh(force); + } } #ifdef ENABLE_RESLIST_IN_MAIN if (reslist) { - reslist->Refresh(); + reslist->Refresh(); } #endif } @@ -931,16 +918,16 @@ MainFrame::UpdateStatusBar() wxString text; if (statusbar) { - text = config.getDicName() + wxT(" ") + config.getTileName(); - statusbar->SetStatusText(text, 0); - - if (m_game) - { - text = wxT(""); - text << _("coup:") << (m_game->getHistory().getSize() + 1) << wxT(" "); - text << _("points:") << (m_game->getCurrentPlayer().getPoints()); - statusbar->SetStatusText(text, 1); - } + text = config.getDicName() + wxT(" ") + config.getTileName(); + statusbar->SetStatusText(text, 0); + + if (m_game) + { + text = wxT(""); + text << _("turn:") << wxT(" ") << (m_game->getHistory().getSize() + 1) << wxT(" "); + text << _("points:") << wxT(" ") << (m_game->getCurrentPlayer().getPoints()); + statusbar->SetStatusText(text, 1); + } } } @@ -957,36 +944,37 @@ MainFrame::UpdateStatusBar() void MainFrame::SetRack(Game::set_rack_mode mode, wxString srack) { - int res = 0; wxString msg; bool check = config.getRackChecking(); + if (m_game == NULL) + { + return; + } static_cast(m_game)->removeTestPlay(); - std::wstring str = srack.c_str(); - res = static_cast(m_game)->setRack(mode, check, str); + int res = static_cast(m_game)->setRack(mode, check, srack.c_str()); switch (res) - { + { case 0x00: /* ok */ - debug("SetRack Ok :: "); - break; + debug("SetRack Ok :: "); + break; case 0x01: - msg = _("Le sac ne contient pas assez de lettres\npour assurer le tirage."); - wxMessageBox(msg, _("Correction du tirage"), wxICON_INFORMATION | wxOK); + msg = _("The bag doesn't contain enough letters\nfor a new rack."); + wxMessageBox(msg, _("Rack validation"), wxICON_ERROR | wxOK); return; case 0x02: - msg = _("Le tirage doit contenir au moins 2 consonnes et 2 voyelles.\n"); - wxMessageBox(msg, _("Correction du tirage"), wxICON_INFORMATION | wxOK); + msg = _("The rack must contain at least 2 consonants and 2 vowels."); + wxMessageBox(msg, _("Rack validation"), wxICON_ERROR | wxOK); return; case 0x03: - msg = _("Le tirage doit contenir au moins 2 consonnes et 2 voyelles\n"); - msg += _("mais le sac ne contient plus assez de lettres.\n\n"); - wxMessageBox(msg, _("Correction du tirage"), wxICON_INFORMATION | wxOK); + msg = _("The rack contains invalid letters for the current dictionary"); + wxMessageBox(msg, _("Rack validation"), wxICON_ERROR | wxOK); break; - default: - statusbar->SetStatusText(_("Le tirage a ete modifie manuellement"), 0); - break; - } + default: + statusbar->SetStatusText(_("The rack has been modified manually"), 0); + break; + } std::wstring r = m_game->getCurrentPlayer().getCurrentRack().toString(); debug("MainFrame::SetRack : setvalue %ls\n",r.c_str()); @@ -998,7 +986,7 @@ MainFrame::SetRack(Game::set_rack_mode mode, wxString srack) void MainFrame::Search() { - if (m_game == NULL || m_game->getDic() == NULL) + if (m_game == NULL) { return; } @@ -1007,9 +995,9 @@ MainFrame::Search() reslist->Search(); #else if (auxframes_ptr[ ID_Frame_Result ]) - { - ((ResultFrame*)(auxframes_ptr[ ID_Frame_Result ]))->Search(); - } + { + ((ResultFrame*)(auxframes_ptr[ ID_Frame_Result ]))->Search(); + } #endif UpdateFrames(); UpdateStatusBar(); @@ -1024,24 +1012,24 @@ MainFrame::Play(int n) } static_cast(m_game)->removeTestPlay(); if (n < 0) - { - debug("MainFrame::Play back %d\n",n); - m_game->back(- n); - } + { + debug("MainFrame::Play back %d\n",n); + m_game->back(- n); + } else - { - int n=0; - debug("MainFrame::Play +%d\n",n); + { + int n = 0; + debug("MainFrame::Play +%d\n",n); #ifdef ENABLE_RESLIST_IN_MAIN - n = reslist->GetSelected(); + n = reslist->GetSelected(); #else - n = ((ResultFrame*)auxframes_ptr[ ID_Frame_Result ])->GetSelected(); + n = ((ResultFrame*)auxframes_ptr[ ID_Frame_Result ])->GetSelected(); #endif - if (n > -1) - { - ((Training*)m_game)->playResult(n); - } - } + if (n > -1) + { + static_cast(m_game)->playResult(n); + } + } wxString r = wxU(m_game->getCurrentPlayer().getCurrentRack().toString().c_str()); rack->SetValue(r); UpdateFrames(); @@ -1064,9 +1052,5 @@ MainFrame::TestPlay(int n) /****************************************************************/ /****************************************************************/ -/// Local Variables: -/// mode: c++ -/// mode: hs-minor -/// c-basic-offset: 4 -/// indent-tabs-mode: nil -/// End: +/// Local Variables: / mode: c++ / mode: hs-minor / c-basic-offset: 4 / +//indent-tabs-mode: nil / End: diff --git a/wxwin/mainframe.h b/wxwin/mainframe.h index 3ba5d60..0e79d60 100644 --- a/wxwin/mainframe.h +++ b/wxwin/mainframe.h @@ -1,4 +1,4 @@ -/* Eliot */ +// Joker game?* Eliot */ /* Copyright (C) 1999 Antoine Fraboulet */ /* Antoine.Fraboulet@free.fr */ /* */ @@ -34,7 +34,7 @@ class MainFrame: public wxFrame { private: - Dictionary m_dic; + const Dictionary *m_dic; Game *m_game; ConfigDB config; AuxFrame *auxframes_ptr[MAX_FRAME_ID]; @@ -59,7 +59,7 @@ private: public: MainFrame(wxPoint,wxSize); virtual ~MainFrame(); - + // ******* // Actions // ******* @@ -69,7 +69,7 @@ public: void TestPlay(int); void UpdateFrames(AuxFrame::refresh_t force = AuxFrame::REFRESH); - + // ***** // Menus // ***** @@ -79,6 +79,7 @@ public: void OnMenuGamePrint (wxCommandEvent& event); void OnMenuGamePrintPreview (wxCommandEvent& event); void OnMenuGamePrintPS (wxCommandEvent& event); + void OnMenuGameQuit (wxCommandEvent& event); void OnMenuConfGameDic (wxCommandEvent& event); void OnMenuConfGameSearch (wxCommandEvent& event); @@ -90,8 +91,7 @@ public: void OnMenuShowFrame (wxCommandEvent& event); - void OnMenuQuitApropos (wxCommandEvent& event); - void OnMenuQuitConfirm (wxCommandEvent& event); + void OnMenuHelpAbout (wxCommandEvent& event); // ******* // Buttons diff --git a/wxwin/printout.cc b/wxwin/printout.cc index 64d3a12..31c68b4 100644 --- a/wxwin/printout.cc +++ b/wxwin/printout.cc @@ -176,14 +176,14 @@ GamePrintout::DrawTextLine(wxDC *dc, int numline, long basey, long heightT, floa DIM(2); if ((numline > 0) && (numline <= NRounds)) { - str = wxU(m_game.getHistory().getTurn(numline - 1).getRound().getWord().c_str()); + str = wxU(m_game.getHistory().getTurn(numline - 1).getMove().getRound().getWord().c_str()); DRW(2); } // pos DIM(3); if ((numline > 0) && (numline <= NRounds)) { - str = wxU(m_game.getHistory().getTurn(numline - 1).getRound().getCoord().toString().c_str()); + str = wxU(m_game.getHistory().getTurn(numline - 1).getMove().getRound().getCoord().toString().c_str()); DRW(3); } // pts @@ -191,7 +191,7 @@ GamePrintout::DrawTextLine(wxDC *dc, int numline, long basey, long heightT, floa if ((numline > 0) && (numline <= NRounds)) { str = wxT(""); - str << m_game.getHistory().getTurn(numline - 1).getRound().getPoints(); + str << m_game.getHistory().getTurn(numline - 1).getMove().getRound().getPoints(); DRW(4); } // total points diff --git a/wxwin/searchpanel.cc b/wxwin/searchpanel.cc index 0a00245..b2647e0 100644 --- a/wxwin/searchpanel.cc +++ b/wxwin/searchpanel.cc @@ -29,7 +29,6 @@ #include "ewx.h" #include "dic.h" -#include "dic_search.h" #include "regexp.h" #include "searchpanel.h" #include "tile.h" @@ -55,7 +54,7 @@ class SimpleSearchPanel : public wxPanel { protected: ConfigDB config; - Dictionary dic; + const Dictionary *dic; wxTextCtrl *t; wxListBox *l; wxBoxSizer *sizer; @@ -65,9 +64,9 @@ protected: void panel_build(); virtual void panel_options() = 0; public: - SimpleSearchPanel(wxWindow* parent, int id, Dictionary d) : wxPanel(parent,id) { dic = d; }; - virtual void compute_char(wxCommandEvent&) {}; - virtual void compute_enter(wxCommandEvent&) {}; + SimpleSearchPanel(wxWindow* parent, int id, const Dictionary &d) : wxPanel(parent,id) { dic = &d; } + virtual void compute_char(wxCommandEvent&) {} + virtual void compute_enter(wxCommandEvent&) {} DECLARE_EVENT_TABLE() }; @@ -105,7 +104,7 @@ SimpleSearchPanel::check_dic() if (dic == NULL) { l->Clear(); - msg << wxT("Pas de dictionnaire"); + msg << _("No dictionary"); l->Append(msg); return 0; } @@ -117,7 +116,7 @@ SimpleSearchPanel::check_end() { if (l->GetCount() == 0) { - l->Append(wxT("Aucun resultat")); + l->Append(_("No result")); } } @@ -128,41 +127,41 @@ SimpleSearchPanel::check_end() class PCross : public SimpleSearchPanel { protected: - virtual void panel_options() {}; + virtual void panel_options() {} public: - void compute_char(wxCommandEvent&) { }; + void compute_char(wxCommandEvent&) {} void compute_enter(wxCommandEvent&); - PCross(wxWindow* parent, int id, Dictionary d) : SimpleSearchPanel(parent,id,d) { panel_build(); }; + PCross(wxWindow* parent, int id, const Dictionary &d) : SimpleSearchPanel(parent,id,d) { panel_build(); } }; void PCross::compute_enter(wxCommandEvent&) { - int i; - wchar_t rack[DIC_WORD_MAX]; - wchar_t buff[RES_CROS_MAX][DIC_WORD_MAX]; + if (!check_dic()) + return; - if (!check_dic()) - return; - - if (t->GetValue().Len() >= DIC_WORD_MAX) + if (t->GetValue().Len() >= DIC_WORD_MAX) { - wxString msg = wxT(""); -// XXX: msg << wxT("La recherche est limitée à ") << DIC_WORD_MAX - 1 << wxT(" lettres"); - msg << wxT("La recherche est limitee a ") << DIC_WORD_MAX - 1 << wxT(" lettres"); - l->Append(msg); - return; + wxString msg; + msg.Printf(_("The search is limited to %d letters"), DIC_WORD_MAX - 1); + l->Append(msg); + return; } - wcsncpy(rack, t->GetValue().wc_str(), DIC_WORD_MAX); - Dic_search_Cros(dic,rack,buff); + wchar_t rack[DIC_WORD_MAX]; + wcsncpy(rack, t->GetValue().wc_str(), DIC_WORD_MAX); - int resnum = 0; - wxString res[RES_CROS_MAX]; - for(i=0; i < RES_CROS_MAX && buff[i][0]; i++) - res[resnum++] = wxU(buff[i]); - l->Set(resnum,res); - check_end(); + list wordList; + dic->searchCross(rack, wordList); + + int resnum = 0; + wxString *res = new wxString[wordList.size()]; + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) + res[resnum++] = wxU(it->c_str()); + l->Set(resnum,res); + delete[] res; + check_end(); } // ************************************************************ @@ -172,46 +171,56 @@ PCross::compute_enter(wxCommandEvent&) class PPlus1 : public SimpleSearchPanel { protected: - virtual void panel_options() {}; + virtual void panel_options() {} public: - void compute_char(wxCommandEvent&) { }; + void compute_char(wxCommandEvent&) {} void compute_enter(wxCommandEvent&); - PPlus1(wxWindow* parent, int id, Dictionary dic) : SimpleSearchPanel(parent,id,dic) { panel_build(); }; + PPlus1(wxWindow* parent, int id, const Dictionary &dic) : SimpleSearchPanel(parent,id,dic) { panel_build(); } }; void PPlus1::compute_enter(wxCommandEvent&) { - int i,j; - wchar_t rack[DIC_WORD_MAX]; - wchar_t buff[DIC_LETTERS][RES_7PL1_MAX][DIC_WORD_MAX]; + if (!check_dic()) + return; - if (!check_dic()) - return; - - if (t->GetValue().Len() >= DIC_WORD_MAX) + if (t->GetValue().Len() >= DIC_WORD_MAX) { - wxString msg = wxT(""); -// XXX: msg << wxT("La recherche est limitée à ") << DIC_WORD_MAX - 1 << wxT(" lettres"); - msg << wxT("La recherche est limitee a ") << DIC_WORD_MAX - 1 << wxT(" lettres"); - l->Append(msg); - return; + wxString msg; + msg.Printf(_("The search is limited to %d letters"), DIC_WORD_MAX - 1); + l->Append(msg); + return; } - wcsncpy(rack, t->GetValue().wc_str(), DIC_WORD_MAX); - Dic_search_7pl1(dic,rack,buff,TRUE); + wstring rack = t->GetValue().wc_str(); + map > wordList; + dic->search7pl1(rack, wordList, true); - int resnum = 0; - wxString res[DIC_LETTERS*(RES_7PL1_MAX+1)]; - for(i=0; i < DIC_LETTERS; i++) + // Count the results + int sum = 0; + map >::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) { - if (i && buff[i][0][0]) - res[resnum++] = wxString(wxT("+")) + (wxChar)(i+'A'-1); - for(j=0; j < RES_7PL1_MAX && buff[i][j][0]; j++) - res[resnum++] = wxString(wxT(" ")) + wxU(buff[i][j]); + if (it->first) + sum += 1; + sum += it->second.size(); } - l->Set(resnum,res); - check_end(); + + wxString *res = new wxString[sum]; + int resnum = 0; + for (it = wordList.begin(); it != wordList.end(); it++) + { + if (it->first) + res[resnum++] = wxString(wxT("+")) + wxU((wxString)it->first); + list::const_iterator itWord; + for (itWord = it->second.begin(); itWord != it->second.end(); itWord++) + { + res[resnum++] = wxString(wxT(" ")) + wxU(itWord->c_str()); + } + } + l->Set(resnum, res); + delete[] res; + check_end(); } // ************************************************************ @@ -228,9 +237,9 @@ protected: virtual void build_letter_lists(); virtual void panel_options(); public: - void compute_char(wxCommandEvent&) { }; + void compute_char(wxCommandEvent&) {} void compute_enter(wxCommandEvent&); - PRegExp(wxWindow* parent, int id, Dictionary d) : SimpleSearchPanel(parent,id,d) { panel_build(); }; + PRegExp(wxWindow* parent, int id, const Dictionary &d) : SimpleSearchPanel(parent,id,d) { panel_build(); } }; void @@ -261,8 +270,8 @@ PRegExp::build_letter_lists() memset(llist.letters[i],0,sizeof(llist.letters[i])); } - const std::list& allTiles = Tile::getAllTiles(); - std::list::const_iterator it; + const std::vector& allTiles = dic->getAllTiles(); + std::vector::const_iterator it; for (it = allTiles.begin(); it != allTiles.end(); it++) { if (! it->isJoker() && ! it->isEmpty()) @@ -289,9 +298,9 @@ PRegExp::panel_options() wxStaticText *otmin; wxStaticText *otmax; - otmin = new wxStaticText(this,wxID_ANY,wxT("Longueur min.")); - omin = new wxTextCtrl(this,ID_OPTION1,wxT( "1"),wxDefaultPosition,wxDefaultSize,wxTE_PROCESS_ENTER); - otmax = new wxStaticText(this,wxID_ANY,wxT("max.")); + otmin = new wxStaticText(this,wxID_ANY,_("Minimum length")); + omin = new wxTextCtrl(this,ID_OPTION1,wxT("1"),wxDefaultPosition,wxDefaultSize,wxTE_PROCESS_ENTER); + otmax = new wxStaticText(this,wxID_ANY,_("Maximum length")); omax = new wxTextCtrl(this,ID_OPTION2,wxT("15"),wxDefaultPosition,wxDefaultSize,wxTE_PROCESS_ENTER); wxBoxSizer *s = new wxBoxSizer( wxHORIZONTAL ); @@ -308,53 +317,55 @@ PRegExp::panel_options() void PRegExp::compute_enter(wxCommandEvent&) { - wchar_t re[DIC_RE_MAX]; - wchar_t buff[RES_REGE_MAX][DIC_WORD_MAX]; + if (!check_dic()) + return; - if (!check_dic()) - return; + build_letter_lists(); - build_letter_lists(); - wcsncpy(re, t->GetValue().wc_str(),DIC_RE_MAX); - debug("PRegExp::compute_enter for %ls",re); + wstring regexp = t->GetValue().wc_str(); + debug("PRegExp::compute_enter for %ls", regexp.c_str()); - int lmin = atoi((const char*)omin->GetValue().mb_str()); - int lmax = atoi((const char*)omax->GetValue().mb_str()); - if (lmax <= (DIC_WORD_MAX - 1) && lmin >= 1 && lmin <= lmax) + int lmin = atoi((const char*)omin->GetValue().mb_str()); + int lmax = atoi((const char*)omax->GetValue().mb_str()); + if (lmax <= (DIC_WORD_MAX - 1) && lmin >= 1 && lmin <= lmax) { - llist.minlength = lmin; - llist.maxlength = lmax; - debug(" length %d,%d",lmin,lmax); + llist.minlength = lmin; + llist.maxlength = lmax; + debug(" length %d,%d",lmin,lmax); } - else + else { - debug(" bad length -%s,%s-", - (const char*)omin->GetValue().mb_str(), - (const char*)omax->GetValue().mb_str()); + debug(" bad length -%s,%s-", + (const char*)omin->GetValue().mb_str(), + (const char*)omax->GetValue().mb_str()); } - debug("\n"); + debug("\n"); - Dic_search_RegE(dic,re,buff,&llist); + list wordList; + dic->searchRegExp(regexp, wordList, &llist); - int resnum = 0; - wxString res[RES_REGE_MAX]; - for(int i=0; i < RES_REGE_MAX && buff[i][0]; i++) - res[resnum++] = wxU(buff[i]); - - l->Set(resnum,res); - check_end(); + wxString *res = new wxString[wordList.size()]; + int resnum = 0; + list::const_iterator it; + for (it = wordList.begin(); it != wordList.end(); it++) + { + res[resnum++] = wxU(it->c_str()); + } + l->Set(resnum,res); + delete[] res; + check_end(); } // ************************************************************ // ************************************************************ // ************************************************************ -SearchPanel::SearchPanel(wxFrame *parent, Dictionary dic) : +SearchPanel::SearchPanel(wxFrame *parent, const Dictionary &dic) : wxNotebook(parent, -1) { - AddPage(new PCross (this,ID_PANEL_CROSS ,dic),wxT("Mots croises")); - AddPage(new PPlus1 (this,ID_PANEL_PLUS1 ,dic),wxT("Plus 1")); - AddPage(new PRegExp(this,ID_PANEL_REGEXP,dic),wxT("Exp. Rationnelle")); + AddPage(new PCross (this,ID_PANEL_CROSS ,dic), _("Cross words")); + AddPage(new PPlus1 (this,ID_PANEL_PLUS1 ,dic), _("Plus 1")); + AddPage(new PRegExp(this,ID_PANEL_REGEXP,dic), _("Regular expressions")); SetSelection(2); } @@ -366,7 +377,7 @@ SearchPanel::~SearchPanel() // ************************************************************ // ************************************************************ - + /// Local Variables: /// mode: hs-minor /// c-basic-offset: 4 diff --git a/wxwin/searchpanel.h b/wxwin/searchpanel.h index a96be9f..fe7528e 100644 --- a/wxwin/searchpanel.h +++ b/wxwin/searchpanel.h @@ -33,7 +33,7 @@ class SearchPanel : public wxNotebook { public: - SearchPanel(wxFrame*, Dictionary); + SearchPanel(wxFrame*, const Dictionary&); ~SearchPanel(); };