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:
Olivier Teulière 2008-01-08 13:52:32 +00:00
parent d86bd2af4a
commit e7a8d01a8d
143 changed files with 12900 additions and 8013 deletions

58
INSTALL
View file

@ -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
View file

@ -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)

View file

@ -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 --------------------------------------------------------------

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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
View 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

View file

@ -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_ */

View file

@ -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
View 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
View file

@ -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
View 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
View file

@ -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
View 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
View 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

View file

@ -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 */

View file

@ -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(&params,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(&params,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(&params, 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(&params, 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
View 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(&params, 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(&params, 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(&params, oWordList,
getEdgeAt<DicEdgeOld>(getRoot()));
}
else
{
searchCrossRecTempl(&params, 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(&params, a->getInitId(),
getEdgeAt<DicEdgeOld>(getRoot()), oWordList);
}
else
{
searchRegexpRecTempl(&params, 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));
}
}

View file

@ -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
View 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
View 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

View file

@ -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
View 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;}
%%

View file

@ -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"

View file

@ -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
View 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;
}

View file

@ -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
View 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
View 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
View 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:

View file

@ -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
View 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;
}
}

View file

@ -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
View 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:

View file

@ -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++

View file

@ -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
View 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(&regList);
list<wstring> wordList;
dic.searchRegExp(convertToWc(er), wordList, &regList);
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
View 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:

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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:

View file

@ -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;
};

View file

@ -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

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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;
}
}

View file

@ -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

View file

@ -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();
}
}
}
}
}

View file

@ -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:

View file

@ -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

View file

@ -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; }

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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))
{

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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 */
//@{

View file

@ -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;

View file

@ -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

View file

@ -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
View 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
View 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

View file

@ -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();
}

View file

@ -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() {}

View file

@ -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();

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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))

View file

@ -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
View 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
View 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:

View file

@ -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:

View file

@ -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();

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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

View file

@ -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
View 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
])

View file

@ -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

View file

@ -1 +1 @@
fr
fr en@quot en@boldquot

View file

@ -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

File diff suppressed because it is too large Load diff

1232
po/fr.po

File diff suppressed because it is too large Load diff

View file

@ -1 +1,2 @@
*.run
load_saved_game.elt

View file

@ -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)

View file

@ -3,6 +3,7 @@ a S
a T
a l
a p
a g
q
q

View file

@ -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