mirror of
git://git.savannah.nongnu.org/eliot.git
synced 2024-12-25 21:59:30 +01:00
Merged the "cppdic" branch back into HEAD.
There are too many change to list properly, here is an overview of the main changes: - the dictionary is now in C++ - the dictionary has a new format, where it is possible to specify the letters, their points, their frequency, ... It is backwards compatible. - Eliot now supports non-ASCII characters everywhere - i18n of the compdic, listdic, regexpmain binaries - i18n of the wxWidgets interface (now in english by default)
This commit is contained in:
parent
d86bd2af4a
commit
e7a8d01a8d
143 changed files with 12900 additions and 8013 deletions
58
INSTALL
58
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'.
|
||||
|
||||
|
|
30
TODO
30
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)
|
||||
|
||||
|
|
82
configure.in
82
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 --------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
103
dic/Makefile.am
103
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 <antoine.fraboulet@free.fr>
|
||||
# Olivier Teulière <ipkiss @@ gmail.com>
|
||||
#
|
||||
# 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
|
||||
|
||||
|
|
201
dic/alist.c
201
dic/alist.c
|
@ -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 <stdlib.h>
|
||||
#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;
|
||||
}
|
98
dic/alist.h
98
dic/alist.h
|
@ -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_ */
|
679
dic/automaton.c
679
dic/automaton.c
|
@ -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 <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#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
|
||||
|
||||
/* ************************************************** *
|
||||
* ************************************************** *
|
||||
* ************************************************** */
|
||||
|
603
dic/automaton.cpp
Normal file
603
dic/automaton.cpp
Normal file
|
@ -0,0 +1,603 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Antoine Fraboulet
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <set>
|
||||
#include <list>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#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<astate> m_states;
|
||||
|
||||
private:
|
||||
/// Initial state of the automaton
|
||||
astate m_initState;
|
||||
|
||||
void addState(astate s);
|
||||
astate getState(const set<int> &iId) const;
|
||||
void printNodes(FILE* f) const;
|
||||
void printEdges(FILE* f) const;
|
||||
void setAccept(astate s) const;
|
||||
set<int> getSuccessor(const set<int> &S, int letter, struct search_RegE_list_t *iList) const;
|
||||
};
|
||||
|
||||
|
||||
/* ************************************************** *
|
||||
State handling
|
||||
* ************************************************** */
|
||||
|
||||
static set<int> s_state_id_create(int id);
|
||||
static string s_state_id_to_str(const set<int> &iId);
|
||||
static astate s_state_create (const set<int> &iId);
|
||||
|
||||
struct automaton_state_t
|
||||
{
|
||||
set<int> 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<astate>::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<int> s_state_id_create(int id)
|
||||
{
|
||||
set<int> l;
|
||||
l.insert(id);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
static string s_state_id_to_str(const set<int> &iId)
|
||||
{
|
||||
string s;
|
||||
set<int>::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<int> &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<astate>::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<int> &iId) const
|
||||
{
|
||||
list<astate>::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<int> 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<astate> 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<int> 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<astate>::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<int> AutomatonHelper::getSuccessor(const set<int> &S,
|
||||
int letter,
|
||||
struct search_RegE_list_t *iList) const
|
||||
{
|
||||
set<int> R, r;
|
||||
set<int>::const_iterator it;
|
||||
for (it = S.begin(); it != S.end(); it++) /* \forall y \in S */
|
||||
{
|
||||
astate y, z;
|
||||
|
||||
set<int> t = s_state_id_create(*it);
|
||||
assert(y = getState(t));
|
||||
|
||||
set<int> 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<astate>::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<astate> L;
|
||||
|
||||
// Clone the list
|
||||
set<int> 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<int> 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<astate>::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<astate>::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<astate>::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
|
||||
|
104
dic/automaton.h
104
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 <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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_ */
|
||||
|
|
350
dic/compdic.c
350
dic/compdic.c
|
@ -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 <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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<num; i++)
|
||||
{
|
||||
#ifdef DEBUG_OUTPUT_L2
|
||||
printf("ptr=%2d t=%d l=%d f=%d chr=%2d (%c)\n",
|
||||
edges[i].ptr, edges[i].term, edges[i].last,
|
||||
edges[i].fill, edges[i].chr, edges[i].chr -1 +'a');
|
||||
#endif
|
||||
fwrite (edges+i, sizeof(Dawg_edge), 1, outfile);
|
||||
}
|
||||
#else
|
||||
fwrite (edges, size, num, outfile);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define MAX_STRING_LENGTH 200
|
||||
|
||||
|
||||
#define MAX_EDGES 2000
|
||||
/* ods3: ?? */
|
||||
/* ods4: 1746 */
|
||||
|
||||
/* global variables */
|
||||
FILE* global_outfile;
|
||||
Dict_header global_header;
|
||||
Hash_table global_hashtable;
|
||||
|
||||
char global_stringbuf[MAX_STRING_LENGTH]; /* Space for current string */
|
||||
char* global_endstring; /* Marks END of current string */
|
||||
char* global_input;
|
||||
char* global_endofinput;
|
||||
|
||||
/*
|
||||
* 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 prefix is matched in the
|
||||
* input.
|
||||
*/
|
||||
#ifdef CHECK_RECURSION
|
||||
int current_rec =0;
|
||||
int max_rec = 0;
|
||||
#endif
|
||||
|
||||
unsigned int
|
||||
makenode(char *prefix)
|
||||
{
|
||||
int numedges;
|
||||
Dawg_edge edges[MAX_EDGES];
|
||||
Dawg_edge *edgeptr = edges;
|
||||
unsigned *saved_position;
|
||||
|
||||
#ifdef CHECK_RECURSION
|
||||
current_rec++;
|
||||
if (current_rec > 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;
|
||||
}
|
||||
|
||||
|
563
dic/compdic.cpp
Normal file
563
dic/compdic.cpp
Normal file
|
@ -0,0 +1,563 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <getopt.h>
|
||||
#include <ctime>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <cwctype>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
// For ntohl & Co.
|
||||
#ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
#else
|
||||
# if HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
# endif
|
||||
# if HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
# 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<char> 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<boost::char_separator<char> > Tokenizer;
|
||||
|
||||
int lineNb = 1;
|
||||
string line;
|
||||
while (getline(in, line))
|
||||
{
|
||||
// Split the lines on space characters
|
||||
vector<string> tokens;
|
||||
boost::char_separator<char> 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<DicEdge> &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<vector<DicEdge>, 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<int, vector<DicEdge> > 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<DicEdge> &edges = global_mapfordepth[current_rec];
|
||||
edges.reserve(MAX_EDGES);
|
||||
edges.clear();
|
||||
#else
|
||||
vector<DicEdge> 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<uint32_t*>(&edges.front()),
|
||||
numedges, outfile);
|
||||
|
||||
return node_pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printUsage(const string &iBinaryName)
|
||||
{
|
||||
cout << "Usage: " << iBinaryName << " [options]" << endl
|
||||
<< _("Mandatory options:") << endl
|
||||
<< _(" -d, --dicname <string> Set the dictionary name and version") << endl
|
||||
<< _(" -l, --letters <string> Path to the file containing the letters (see below)") << endl
|
||||
<< _(" -i, --input <string> Path to the uncompressed dictionary file (encoded in UTF-8)") << endl
|
||||
<< _(" -o, --output <string Path to the generated compressed dictionary file") << endl
|
||||
<< _("Other options:") << endl
|
||||
<< _(" -h, --help Print this help and exit") << endl
|
||||
<< _("Example:") << endl
|
||||
<< " " << iBinaryName << _(" -d 'ODS 5.0' -l letters.txt -i ods5.txt -o ods5.dawg") << endl
|
||||
<< endl
|
||||
<< _("The file containing the letters (--letters switch) must be UTF-8 encoded.") << endl
|
||||
<< _("Each line corresponds to one letter, and must contain 5 fields separated with ") << endl
|
||||
<< _("one or more space(s).") << endl
|
||||
<< _(" - 1st field: the letter itself") << endl
|
||||
<< _(" - 2nd field: the points of the letter") << endl
|
||||
<< _(" - 3rd field: the frequency of the letter (how many letters of this kind in the game)") << endl
|
||||
<< _(" - 4th field: 1 if the letter is considered as a vowel in Scrabble game, 0 otherwise") << endl
|
||||
<< _(" - 5th field: 1 if the letter is considered as a consonant in Scrabble game, 0 otherwise") << endl
|
||||
<< _("Example for french:") << endl
|
||||
<< _("A 1 9 1 0") << endl
|
||||
<< _("[...]") << endl
|
||||
<< _("Z 10 1 0 1") << endl
|
||||
<< _("? 0 2 1 1") << 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
|
||||
|
||||
static const struct option long_options[] =
|
||||
{
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"dicname", required_argument, NULL, 'd'},
|
||||
{"letters", required_argument, NULL, 'l'},
|
||||
{"input", required_argument, NULL, 'i'},
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static const char short_options[] = "hd:l:i:o:";
|
||||
|
||||
bool found_d = false;
|
||||
bool found_l = false;
|
||||
bool found_i = false;
|
||||
bool found_o = false;
|
||||
string inFileName;
|
||||
string outFileName;
|
||||
DictHeaderInfo headerInfo;
|
||||
|
||||
int res;
|
||||
int option_index = 1;
|
||||
try
|
||||
{
|
||||
while ((res = getopt_long(argc, argv, short_options,
|
||||
long_options, &option_index)) != -1)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
case 'd':
|
||||
found_d = true;
|
||||
headerInfo.dicName = convertToWc(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
found_l = true;
|
||||
readLetters(optarg, headerInfo);
|
||||
break;
|
||||
case 'i':
|
||||
found_i = true;
|
||||
inFileName = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
found_o = true;
|
||||
outFileName = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check mandatory options
|
||||
if (!found_d || !found_l || !found_i || !found_o)
|
||||
{
|
||||
cerr << _("A mandatory option is missing") << endl;
|
||||
printUsage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct stat stat_buf;
|
||||
if (stat(inFileName.c_str(), &stat_buf) < 0)
|
||||
{
|
||||
cerr << _("Cannot stat uncompressed dictionary ") << inFileName << endl;
|
||||
exit(1);
|
||||
}
|
||||
unsigned int dicsize = (unsigned int)stat_buf.st_size;
|
||||
|
||||
ofstream outfile(outFileName.c_str(), ios::out | ios::binary | ios::trunc);
|
||||
if (!outfile.is_open())
|
||||
{
|
||||
cerr << _("Cannot open output file ") << outFileName << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
clock_t startLoadTime = clock();
|
||||
// FIXME: not exception safe
|
||||
const wchar_t *uncompressed = load_uncompressed(inFileName, dicsize);
|
||||
clock_t endLoadTime = clock();
|
||||
|
||||
global_input = uncompressed;
|
||||
global_endofinput = global_input + dicsize;
|
||||
|
||||
#define SCALE 0.6
|
||||
global_hashtable = new HashTable<vector<DicEdge>, 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<uint32_t*>(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<uint32_t*>(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
282
dic/dic.c
282
dic/dic.c
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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;
|
||||
}
|
276
dic/dic.cpp
Normal file
276
dic/dic.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <fstream>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cctype>
|
||||
|
||||
// For ntohl & Co.
|
||||
#ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
#else
|
||||
# if HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
# endif
|
||||
# if HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
# 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<const DicEdgeOld*>(m_dawg + e)->ptr;
|
||||
else
|
||||
return reinterpret_cast<const DicEdge*>(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<const DicEdgeOld*>(m_dawg + e)->chr;
|
||||
else
|
||||
return reinterpret_cast<const DicEdge*>(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<const DicEdgeOld*>(m_dawg + e)->last;
|
||||
else
|
||||
return reinterpret_cast<const DicEdge*>(m_dawg + e)->last;
|
||||
}
|
||||
|
||||
|
||||
bool Dictionary::isEndOfWord(const dic_elt_t &e) const
|
||||
{
|
||||
if (m_header->getVersion() == 0)
|
||||
return reinterpret_cast<const DicEdgeOld*>(m_dawg + e)->term;
|
||||
else
|
||||
return reinterpret_cast<const DicEdge*>(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;
|
||||
}
|
||||
|
287
dic/dic.h
287
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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#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<Tile>& 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<wstring> &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<wstring> &oWordList) const;
|
||||
|
||||
/**
|
||||
* Search for crosswords
|
||||
* @param iMask: letters
|
||||
* @param oWordList: results
|
||||
*/
|
||||
void searchCross(const wstring &iMask, list<wstring> &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<wchar_t, list<wstring> > &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<wstring> &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<Tile> m_tilesVect;
|
||||
|
||||
static const Dictionary *m_dic;
|
||||
|
||||
void convertDataToArch();
|
||||
void initializeTiles();
|
||||
|
||||
/// Template getter for the edge at the given position
|
||||
template <typename DAWG_EDGE>
|
||||
const DAWG_EDGE * getEdgeAt(const dic_elt_t &iElt) const
|
||||
{
|
||||
return reinterpret_cast<const DAWG_EDGE*>(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 <typename DAWG_EDGE>
|
||||
const DAWG_EDGE * seekEdgePtr(const wchar_t *s, const DAWG_EDGE *eptr) const;
|
||||
|
||||
/// Helper for searchBenj()
|
||||
template <typename DAWG_EDGE>
|
||||
void searchBenjTempl(const wstring &iWord, list<wstring> &oWordList) const;
|
||||
|
||||
/// Helper for searchRacc()
|
||||
template <typename DAWG_EDGE>
|
||||
void searchRaccTempl(const wstring &iWord, list<wstring> &oWordList) const;
|
||||
|
||||
/// Helper for searchCross()
|
||||
template <typename DAWG_EDGE>
|
||||
void searchCrossRecTempl(struct params_cross_t *params,
|
||||
list<wstring> &oWordList,
|
||||
const DAWG_EDGE *edgeptr) const;
|
||||
|
||||
/// Helper for search7pl1()
|
||||
template <typename DAWG_EDGE>
|
||||
void search7pl1Templ(const wstring &iRack,
|
||||
map<wchar_t, list<wstring> > &oWordList,
|
||||
bool joker) const;
|
||||
|
||||
/// Second helper for search7pl1()
|
||||
template <typename DAWG_EDGE>
|
||||
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<string> &oWordList,
|
||||
struct search_RegE_list_t *iList) const;
|
||||
|
||||
/// Helper for searchRegExp()
|
||||
template <typename DAWG_EDGE>
|
||||
void searchRegexpRecTempl(struct params_regexp_t *params,
|
||||
int state,
|
||||
const DAWG_EDGE *edgeptr,
|
||||
list<string> &oWordList) const;
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
#endif /* _DIC_H_ */
|
||||
|
||||
/// Local Variables:
|
||||
|
|
36
dic/dic_exception.cpp
Normal file
36
dic/dic_exception.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
|
44
dic/dic_exception.h
Normal file
44
dic/dic_exception.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <exception>
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -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 <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#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 */
|
||||
|
||||
|
|
690
dic/dic_search.c
690
dic/dic_search.c
|
@ -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 <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#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; i<DIC_LETTERS; i++)
|
||||
params.search_letters[i] = 0;
|
||||
|
||||
if (dic == NULL || rack == NULL || *rack == '\0')
|
||||
return;
|
||||
|
||||
/*
|
||||
* the letters are verified and changed to the dic internal
|
||||
* representation (*r & DIC_CHAR_MASK)
|
||||
*/
|
||||
for(wordlen=0; wordlen < DIC_WORD_MAX && *r; r++)
|
||||
{
|
||||
if (isalpha(*r))
|
||||
{
|
||||
params.search_letters[(int)*r & DIC_CHAR_MASK]++;
|
||||
wordlen++;
|
||||
}
|
||||
else if (*r == '?')
|
||||
{
|
||||
if (joker)
|
||||
{
|
||||
params.search_letters[0]++;
|
||||
wordlen++;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(buff[0][0],"** joker **",DIC_WORD_MAX);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wordlen < 1)
|
||||
return;
|
||||
|
||||
root_edge = dic->dawg + (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);
|
||||
}
|
||||
|
||||
/****************************************/
|
||||
/****************************************/
|
||||
|
566
dic/dic_search.cpp
Normal file
566
dic/dic_search.cpp
Normal file
|
@ -0,0 +1,566 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
|
||||
#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 <typename DAWG_EDGE>
|
||||
const DAWG_EDGE* Dictionary::seekEdgePtr(const wchar_t* s, const DAWG_EDGE *eptr) const
|
||||
{
|
||||
if (*s)
|
||||
{
|
||||
const DAWG_EDGE *p = getEdgeAt<DAWG_EDGE>(eptr->ptr);
|
||||
do
|
||||
{
|
||||
if (p->chr == getHeader().getCodeFromChar(*s))
|
||||
return seekEdgePtr(s + 1, p);
|
||||
} while (!(*p++).last);
|
||||
return getEdgeAt<DAWG_EDGE>(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<DicEdgeOld>(getRoot()));
|
||||
return e->term;
|
||||
}
|
||||
else
|
||||
{
|
||||
const DicEdge *e =
|
||||
seekEdgePtr(iWord.c_str(), getEdgeAt<DicEdge>(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<wchar_t, list<wstring> > *results;
|
||||
int search_len;
|
||||
wchar_t search_wordtst[DIC_WORD_MAX];
|
||||
char search_letters[63];
|
||||
};
|
||||
|
||||
template <typename DAWG_EDGE>
|
||||
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<DAWG_EDGE>(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<DAWG_EDGE>(edgeptr->ptr));
|
||||
}
|
||||
params->search_letters[0] ++;
|
||||
params->search_wordtst[i] = L'\0';
|
||||
}
|
||||
}
|
||||
} while (! (*edgeptr++).last);
|
||||
}
|
||||
|
||||
|
||||
template <typename DAWG_EDGE>
|
||||
void Dictionary::search7pl1Templ(const wstring &iRack,
|
||||
map<wchar_t, list<wstring> > &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<DAWG_EDGE>(getRoot());
|
||||
root_edge = getEdgeAt<DAWG_EDGE>(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<wchar_t, list<wstring> > &oWordList,
|
||||
bool joker) const
|
||||
{
|
||||
if (getHeader().getVersion() == 0)
|
||||
search7pl1Templ<DicEdgeOld>(iRack, oWordList, joker);
|
||||
else
|
||||
search7pl1Templ<DicEdge>(iRack, oWordList, joker);
|
||||
}
|
||||
|
||||
/****************************************/
|
||||
/****************************************/
|
||||
|
||||
template <typename DAWG_EDGE>
|
||||
void Dictionary::searchRaccTempl(const wstring &iWord, list<wstring> &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<DAWG_EDGE>(getRoot()));
|
||||
|
||||
/* points to what the next letter can be */
|
||||
const DAWG_EDGE *edge = getEdgeAt<DAWG_EDGE>(edge_seek->ptr);
|
||||
|
||||
if (edge != getEdgeAt<DAWG_EDGE>(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<wstring> &oWordList) const
|
||||
{
|
||||
if (getHeader().getVersion() == 0)
|
||||
searchRaccTempl<DicEdgeOld>(iWord, oWordList);
|
||||
else
|
||||
searchRaccTempl<DicEdge>(iWord, oWordList);
|
||||
}
|
||||
|
||||
/****************************************/
|
||||
/****************************************/
|
||||
|
||||
template <typename DAWG_EDGE>
|
||||
void Dictionary::searchBenjTempl(const wstring &iWord, list<wstring> &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<DAWG_EDGE>(getRoot());
|
||||
edge0 = getEdgeAt<DAWG_EDGE>(edge0->ptr);
|
||||
do
|
||||
{
|
||||
wordtst[0] = getHeader().getCharFromCode(edge0->chr);
|
||||
edge1 = getEdgeAt<DAWG_EDGE>(edge0->ptr);
|
||||
do
|
||||
{
|
||||
wordtst[1] = getHeader().getCharFromCode(edge1->chr);
|
||||
edge2 = getEdgeAt<DAWG_EDGE>(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<wstring> &oWordList) const
|
||||
{
|
||||
if (getHeader().getVersion() == 0)
|
||||
searchBenjTempl<DicEdgeOld>(iWord, oWordList);
|
||||
else
|
||||
searchBenjTempl<DicEdge>(iWord, oWordList);
|
||||
}
|
||||
|
||||
/****************************************/
|
||||
/****************************************/
|
||||
|
||||
struct params_cross_t
|
||||
{
|
||||
int wordlen;
|
||||
wchar_t mask[DIC_WORD_MAX];
|
||||
};
|
||||
|
||||
|
||||
template <typename DAWG_EDGE>
|
||||
void Dictionary::searchCrossRecTempl(struct params_cross_t *params,
|
||||
list<wstring> &oWordList,
|
||||
const DAWG_EDGE *edgeptr) const
|
||||
{
|
||||
const DAWG_EDGE *current = getEdgeAt<DAWG_EDGE>(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<wstring> &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<DicEdgeOld>(getRoot()));
|
||||
}
|
||||
else
|
||||
{
|
||||
searchCrossRecTempl(¶ms, oWordList,
|
||||
getEdgeAt<DicEdge>(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 <typename DAWG_EDGE>
|
||||
void Dictionary::searchRegexpRecTempl(struct params_regexp_t *params,
|
||||
int state,
|
||||
const DAWG_EDGE *edgeptr,
|
||||
list<string> &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<DAWG_EDGE>(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<string> &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<DicEdgeOld>(getRoot()), oWordList);
|
||||
}
|
||||
else
|
||||
{
|
||||
searchRegexpRecTempl(¶ms, a->getInitId(),
|
||||
getEdgeAt<DicEdge>(getRoot()), oWordList);
|
||||
}
|
||||
|
||||
delete a;
|
||||
}
|
||||
regexp_delete_tree(root);
|
||||
}
|
||||
|
||||
|
||||
void Dictionary::searchRegExp(const wstring &iRegexp,
|
||||
list<wstring> &oWordList,
|
||||
struct search_RegE_list_t *iList) const
|
||||
{
|
||||
if (iRegexp == L"")
|
||||
return;
|
||||
|
||||
list<string> tmpWordList;
|
||||
// Do the actual work
|
||||
searchRegExpInner(convertToMb(iRegexp), tmpWordList, iList);
|
||||
|
||||
list<string>::const_iterator it;
|
||||
for (it = tmpWordList.begin(); it != tmpWordList.end(); it++)
|
||||
{
|
||||
oWordList.push_back(convertToWc(*it));
|
||||
}
|
||||
}
|
||||
|
131
dic/dic_search.h
131
dic/dic_search.h
|
@ -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_ */
|
396
dic/encoding.cpp
Normal file
396
dic/encoding.cpp
Normal file
|
@ -0,0 +1,396 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
#include <cerrno>
|
||||
#include <iconv.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#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<ICONV_CONST char*>(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
|
||||
}
|
||||
|
122
dic/encoding.h
Normal file
122
dic/encoding.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
|
||||
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
|
||||
|
58
dic/erl.l
58
dic/erl.l
|
@ -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;}
|
||||
%%
|
||||
|
59
dic/erl.lpp
Normal file
59
dic/erl.lpp
Normal file
|
@ -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;}
|
||||
%%
|
||||
|
|
@ -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 <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dic.h"
|
||||
#include "regexp.h"
|
163
dic/hashtable.c
163
dic/hashtable.c
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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; i<hashtable->size; 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;
|
||||
}
|
||||
|
||||
|
52
dic/hashtable.cpp
Normal file
52
dic/hashtable.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<const unsigned int*>(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<const unsigned char*>(iPtr);
|
||||
for (unsigned int i = 0; i < iSize; ++i)
|
||||
key ^= (key << 3) ^ (key >> 1) ^ ptr[i];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
100
dic/hashtable.h
100
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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
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_ */
|
||||
|
|
88
dic/hashtable.i
Normal file
88
dic/hashtable.i
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <cstdlib>
|
||||
|
||||
#include "hashtable.h"
|
||||
|
||||
|
||||
template<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
HashTable<KEY, VALUE, HASH_FCN>::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<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
HashTable<KEY, VALUE, HASH_FCN>::~HashTable()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_size; ++i)
|
||||
delete m_nodes[i];
|
||||
delete[] m_nodes;
|
||||
}
|
||||
|
||||
|
||||
template<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
HashTable<KEY, VALUE, HASH_FCN>::Node::Node(const KEY &iKey, const VALUE &iValue, const Node *iNext)
|
||||
: m_key(iKey), m_value(iValue), m_next(iNext)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
HashTable<KEY, VALUE, HASH_FCN>::Node::~Node()
|
||||
{
|
||||
delete m_next;
|
||||
}
|
||||
|
||||
|
||||
template<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
const VALUE *HashTable<KEY, VALUE, HASH_FCN>::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<typename KEY, typename VALUE, typename HASH_FCN>
|
||||
void HashTable<KEY, VALUE, HASH_FCN>::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
|
526
dic/header.cpp
Normal file
526
dic/header.cpp
Normal file
|
@ -0,0 +1,526 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
// For ntohl & Co.
|
||||
#ifdef WIN32
|
||||
# include <winsock2.h>
|
||||
#else
|
||||
# if HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
# endif
|
||||
# if HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
# 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<wchar_t, unsigned int>::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");
|
||||
}
|
||||
|
191
dic/header.h
Normal file
191
dic/header.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <iosfwd>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
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<uint8_t> points;
|
||||
vector<uint8_t> frequency;
|
||||
vector<bool> vowels;
|
||||
vector<bool> 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<uint8_t> m_points;
|
||||
|
||||
/// Frequency of the letters
|
||||
vector<uint8_t> m_frequency;
|
||||
|
||||
/// Vowels
|
||||
vector<bool> m_vowels;
|
||||
|
||||
/// Consonants
|
||||
vector<bool> m_consonants;
|
||||
|
||||
map<wchar_t, unsigned int> 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:
|
207
dic/listdic.c
207
dic/listdic.c
|
@ -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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#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;
|
||||
}
|
187
dic/listdic.cpp
Normal file
187
dic/listdic.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <fstream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstddef>
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
# 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 <typename DAWG_EDGE>
|
||||
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<const DAWG_EDGE*>(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 <typename DAWG_EDGE>
|
||||
void print_dic_list(const Dictionary &iDic)
|
||||
{
|
||||
static wchar_t buf[80];
|
||||
print_dic_rec(cout, iDic, buf, buf, *reinterpret_cast<const DAWG_EDGE*>(iDic.getEdgeAt(iDic.getRoot())));
|
||||
}
|
||||
|
||||
|
||||
template <typename DAWG_EDGE>
|
||||
static void print_node_hex(const Dictionary &dic, int i)
|
||||
{
|
||||
union edge_t
|
||||
{
|
||||
DAWG_EDGE e;
|
||||
uint32_t s;
|
||||
} ee;
|
||||
|
||||
ee.e = *reinterpret_cast<const DAWG_EDGE*>(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 <typename DAWG_EDGE>
|
||||
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<DAWG_EDGE>(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<DicEdgeOld>(dic);
|
||||
else
|
||||
print_dic_hex<DicEdge>(dic);
|
||||
}
|
||||
if (option_print_dic_list || option_print_all)
|
||||
{
|
||||
if (dic.getHeader().getVersion() == 0)
|
||||
print_dic_list<DicEdgeOld>(dic);
|
||||
else
|
||||
print_dic_list<DicEdge>(dic);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
382
dic/regexp.c
382
dic/regexp.c
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#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:
|
384
dic/regexp.cpp
Normal file
384
dic/regexp.cpp
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2006 Antoine Fraboulet
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#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:
|
79
dic/regexp.h
79
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 <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <cstdio>
|
||||
|
||||
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++
|
||||
|
|
140
dic/regexpmain.c
140
dic/regexpmain.c
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<RES_REGE_MAX && wordlist[i][0]; i++)
|
||||
{
|
||||
fprintf(stderr,"%s\n",wordlist[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Dic_destroy(dic);
|
||||
return 0;
|
||||
}
|
169
dic/regexpmain.cpp
Normal file
169
dic/regexpmain.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Antoine Fraboulet
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <exception>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
# 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<wstring> wordList;
|
||||
dic.searchRegExp(convertToWc(er), wordList, ®List);
|
||||
|
||||
cout << _("result:") << endl;
|
||||
list<wstring>::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;
|
||||
}
|
||||
}
|
150
dic/tile.cpp
Normal file
150
dic/tile.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <sstream>
|
||||
#include <string>
|
||||
#include <wctype.h>
|
||||
#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:
|
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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<Tile>& 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<Tile> m_tilesList;
|
||||
/// Vector of tiles indexed by their code, for fast look-up
|
||||
static vector<Tile> 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
|
208
doc/dic.txt
208
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.
|
||||
|
||||
|
|
|
@ -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 <antoine.fraboulet@free.fr>
|
||||
# Olivier Teulière <ipkiss @@ gmail.com>
|
||||
#
|
||||
# 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
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> AIPercent::getChangedLetters() const
|
||||
{
|
||||
return vector<Tile>();
|
||||
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:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> 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;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> 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
|
||||
|
|
84
game/bag.cpp
84
game/bag.cpp
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
|
||||
#include "tile.h"
|
||||
#include <dic.h>
|
||||
#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<Tile>& allTiles = Tile::getAllTiles();
|
||||
list<Tile>::const_iterator it;
|
||||
const vector<Tile>& allTiles = m_dic.getAllTiles();
|
||||
vector<Tile>::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<Tile, int>::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<Tile, int>::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<Tile, int>::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<Tile, int>::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<Tile, int>::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<Tile, int>::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();
|
||||
}
|
||||
|
||||
|
||||
|
|
41
game/bag.h
41
game/bag.h
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <map>
|
||||
#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<Tile, int> m_tilesMap;
|
||||
|
||||
/// Total number of tiles in the bag
|
||||
int m_ntiles;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> &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<Tile> &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;
|
||||
}
|
||||
}
|
||||
|
|
15
game/board.h
15
game/board.h
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
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
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <wctype.h>
|
||||
|
||||
#include <dic.h>
|
||||
#include "tile.h"
|
||||
#include "board.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
static void Board_checkout_tile(const Dictionary &iDic,
|
||||
vector<Tile>& iTiles,
|
||||
vector<bool> & 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<bool> &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<Tile> rackTiles;
|
||||
vector<Tile> rackTiles;
|
||||
iRack.getTiles(rackTiles);
|
||||
list<Tile>::const_iterator it;
|
||||
bool match;
|
||||
vector<Tile>::const_iterator it;
|
||||
vector<Tile>::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:
|
||||
|
|
|
@ -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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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
|
||||
|
|
49
game/coord.h
49
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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
|
||||
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; }
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
|
13
game/cross.h
13
game/cross.h
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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;
|
||||
|
||||
|
|
10
game/debug.h
10
game/debug.h
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2004-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<AIPlayer*>(m_players[n]);
|
||||
player->compute(*m_dic, m_board, m_history.getSize());
|
||||
AIPlayer *player = dynamic_cast<AIPlayer*>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<int, bool> m_hasPlayed;
|
||||
|
|
|
@ -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 <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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 <string>
|
||||
|
||||
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
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <iomanip>
|
||||
#include <wctype.h>
|
||||
|
||||
#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<AIPlayer*>(m_players[n]);
|
||||
AIPlayer *player = static_cast<AIPlayer*>(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<Tile> 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<Tile> 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<Tile> &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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> &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_ */
|
||||
|
|
493
game/game.cpp
493
game/game.cpp
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> 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<Round&>(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<Tile> 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<Tile> 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<Tile> 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<Tile> 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<Tile> 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<Tile>& allTiles = Tile::getAllTiles();
|
||||
list<Tile>::const_iterator it;
|
||||
const vector<Tile>& allTiles = m_dic.getAllTiles();
|
||||
vector<Tile>::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<Tile> 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<Tile>)
|
||||
// Adding a getter on the word could help too...
|
||||
vector<Tile> 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))
|
||||
{
|
||||
|
|
260
game/game.h
260
game/game.h
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Player*> 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_ */
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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 <getopt.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <exception>
|
||||
|
||||
#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 <string> 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 <string> 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
|
||||
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 */
|
||||
//@{
|
||||
|
|
171
game/game_io.cpp
171
game/game_io.cpp
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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;
|
||||
|
|
124
game/history.cpp
124
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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#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
|
||||
|
|
|
@ -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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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();
|
||||
|
|
107
game/move.cpp
Normal file
107
game/move.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <algorithm>
|
||||
#include <wctype.h>
|
||||
|
||||
#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"";
|
||||
}
|
||||
|
134
game/move.h
Normal file
134
game/move.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
|
||||
#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
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2004-2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2004-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2004-2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2004-2007 Olivier Teulière & Antoine Fraboulet
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
* Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
*
|
||||
* 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() {}
|
||||
|
||||
|
|
104
game/pldrack.cpp
104
game/pldrack.cpp
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <algorithm>
|
||||
#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<Tile> &oTiles) const
|
||||
{
|
||||
oTiles.clear();
|
||||
for (int i = 0; i < nOld(); i++)
|
||||
oTiles.push_back(m_oldTiles[i]);
|
||||
oTiles = m_oldTiles;
|
||||
}
|
||||
|
||||
|
||||
void PlayedRack::getNewTiles(vector<Tile> &oTiles) const
|
||||
{
|
||||
oTiles.clear();
|
||||
for (int i = 0; i < nNew(); i++)
|
||||
oTiles.push_back(m_newTiles[i]);
|
||||
oTiles = m_newTiles;
|
||||
}
|
||||
|
||||
|
||||
void PlayedRack::getAllTiles(vector<Tile> &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<Tile> l;
|
||||
iRack.getTiles(l);
|
||||
|
||||
m_oldTiles.clear();
|
||||
list<Tile>::const_iterator it;
|
||||
for (it = l.begin(); it != l.end(); it++)
|
||||
{
|
||||
addOld(*it);
|
||||
}
|
||||
iRack.getTiles(m_oldTiles);
|
||||
}
|
||||
|
||||
|
||||
void PlayedRack::setNew(const Rack &iRack)
|
||||
{
|
||||
list<Tile> l;
|
||||
iRack.getTiles(l);
|
||||
|
||||
m_newTiles.clear();
|
||||
list<Tile>::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<Tile>::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<Tile>::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();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> &oTiles) const;
|
||||
void getAllTiles(vector<Tile> &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<Tile> m_oldTiles;
|
||||
vector<Tile> m_newTiles;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> &oTiles) const
|
||||
void Rack::getTiles(vector<Tile> &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<Tile> &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;
|
||||
}
|
||||
|
|
24
game/rack.h
24
game/rack.h
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2002-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <set>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#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<Tile> &oTiles) const;
|
||||
void getTiles(vector<Tile> &oTiles) const;
|
||||
|
||||
wstring toString();
|
||||
|
||||
private:
|
||||
/// Vector indexed by tile codes, containing the number of tiles
|
||||
vector<unsigned int> m_tiles;
|
||||
int m_ntiles;
|
||||
unsigned int m_ntiles;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<const Round&,
|
|||
};
|
||||
|
||||
|
||||
const Round & Results::get(int i) const
|
||||
const Round & Results::get(unsigned int i) const
|
||||
{
|
||||
ASSERT(0 <= i && i < size(), "Results index out of bounds");
|
||||
ASSERT(i < size(), "Results index out of bounds");
|
||||
return m_rounds[i];
|
||||
}
|
||||
|
||||
|
||||
void Results::search(const Dictionary &iDic, Board &iBoard,
|
||||
const Rack &iRack, int iTurn)
|
||||
const Rack &iRack, bool iFirstWord)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (iTurn == 0)
|
||||
if (iFirstWord)
|
||||
{
|
||||
iBoard.searchFirst(iDic, iRack, *this);
|
||||
}
|
||||
|
@ -67,11 +68,11 @@ void Results::search(const Dictionary &iDic, Board &iBoard,
|
|||
iBoard.search(iDic, iRack, *this);
|
||||
}
|
||||
|
||||
sort_by_points();
|
||||
sortByPoints();
|
||||
}
|
||||
|
||||
|
||||
void Results::sort_by_points()
|
||||
void Results::sortByPoints()
|
||||
{
|
||||
less_points lp;
|
||||
std::sort(m_rounds.begin(), m_rounds.end(), lp);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 2005-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Round> m_rounds;
|
||||
|
||||
void sortByPoints();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> &iTiles)
|
||||
{
|
||||
m_word.clear();
|
||||
|
||||
vector<Tile>::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<char>(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))
|
||||
|
|
28
game/round.h
28
game/round.h
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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<Tile> &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
|
||||
|
|
134
game/settings.cpp
Normal file
134
game/settings.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <cstdlib>
|
||||
|
||||
#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 <typename T>
|
||||
void Settings::OptionsHandler<T>::addOption(const string &iName, const T &iValue)
|
||||
{
|
||||
m_options[iName] = iValue;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void Settings::OptionsHandler<T>::setOption(const string &iName, const T &iValue)
|
||||
{
|
||||
typename map<string, T>::iterator it = m_options.find(iName);
|
||||
if (it == m_options.end())
|
||||
{
|
||||
// FIXME: throw an exception object instead
|
||||
throw 1;
|
||||
}
|
||||
it->second = iValue;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
const T& Settings::OptionsHandler<T>::getOption(const string &iName) const
|
||||
{
|
||||
typename map<string, T>::const_iterator it = m_options.find(iName);
|
||||
if (it == m_options.end())
|
||||
{
|
||||
// FIXME: throw an exception object instead
|
||||
throw 1;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
109
game/settings.h
Normal file
109
game/settings.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*****************************************************************************
|
||||
* Eliot
|
||||
* Copyright (C) 2007 Olivier Teulière
|
||||
* Authors: Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#include <map>
|
||||
|
||||
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 <typename T>
|
||||
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<string, T> m_options;
|
||||
};
|
||||
|
||||
|
||||
/// Singleton instance
|
||||
static Settings *m_instance;
|
||||
Settings();
|
||||
|
||||
/// The settings can be of various types
|
||||
OptionsHandler<bool> m_boolHandler;
|
||||
OptionsHandler<int> m_intHandler;
|
||||
// Add types as needed...
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// Local Variables:
|
||||
/// mode: c++
|
||||
/// mode: hs-minor
|
||||
/// c-basic-offset: 4
|
||||
/// indent-tabs-mode: nil
|
||||
/// End:
|
233
game/tile.cpp
233
game/tile.cpp
|
@ -1,233 +0,0 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
*
|
||||
* 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 <wctype.h>
|
||||
|
||||
|
||||
/*************************
|
||||
* 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> Tile::m_tilesList;
|
||||
vector<Tile> 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>& 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:
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <algorithm>
|
||||
|
||||
#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();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 1999-2005 Eliot
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet@free.fr>
|
||||
* Olivier Teuliere <ipkiss@via.ecp.fr>
|
||||
* Eliot
|
||||
* Copyright (C) 1999-2007 Antoine Fraboulet & Olivier Teulière
|
||||
* Authors: Antoine Fraboulet <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
|
||||
#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_ */
|
||||
|
|
|
@ -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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
84
game/turn.h
84
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 <antoine.fraboulet @@ free.fr>
|
||||
* Olivier Teulière <ipkiss @@ gmail.com>
|
||||
*
|
||||
* 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 <string>
|
||||
#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
|
||||
|
|
|
@ -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
|
||||
|
|
198
m4/ax_boost_base.m4
Normal file
198
m4/ax_boost_base.m4
Normal file
|
@ -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 <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# 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 <thomas@randspringer.de>
|
||||
#
|
||||
# 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 <boost/version.hpp>
|
||||
]], [[
|
||||
#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 <boost/version.hpp>
|
||||
]], [[
|
||||
#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 <boost/version.hpp>. 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
|
||||
|
||||
])
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
fr
|
||||
fr en@quot en@boldquot
|
||||
|
|
|
@ -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
|
||||
|
|
1068
po/eliot.pot
1068
po/eliot.pot
File diff suppressed because it is too large
Load diff
|
@ -1 +1,2 @@
|
|||
*.run
|
||||
load_saved_game.elt
|
||||
|
|
20
test/driver
20
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)
|
||||
|
|
|
@ -3,6 +3,7 @@ a S
|
|||
a T
|
||||
a l
|
||||
a p
|
||||
a g
|
||||
q
|
||||
q
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue