[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Eliot-dev] eliot dic/compdic.c dic/dic_search.c dic/dic_se... [multibyt
From: |
eliot-dev |
Subject: |
[Eliot-dev] eliot dic/compdic.c dic/dic_search.c dic/dic_se... [multibyte] |
Date: |
Wed, 28 Dec 2005 16:47:36 +0000 |
CVSROOT: /sources/eliot
Module name: eliot
Branch: multibyte
Changes by: Olivier Teulière <address@hidden> 05/12/28 16:47:35
Modified files:
dic : compdic.c dic_search.c dic_search.h listdic.c
game : Makefile.am bag.cpp board.cpp board.h coord.cpp
coord.h duplicate.cpp duplicate.h freegame.cpp
freegame.h game.cpp game.h history.cpp
history.h player.cpp player.h pldrack.cpp
pldrack.h round.cpp round.h tile.cpp tile.h
training.cpp training.h turn.cpp turn.h
utils : eliottxt.cpp game_io.cpp ncurses.cpp
Added files:
game : encoding.cpp encoding.h
Log message:
Use wide-character strings internally instead of chars.
- this is done on a branch first, because it has more impacts than
expected
- the Game library is 99% migrated, the regression scenario are
working, but
the loading of a game is probably more broken than on Head...
- the Dic library is still using char* strings internally, so there is
an
adaptation layer converting wchar_t into char... this won't work for
actual
multi-byte characters obviously, but it's not worse than before for
ASCII
characters
- the text and ncurses interfaces have been modified too, the wxWidgets
one
should be done soon
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/dic/compdic.c.diff?only_with_tag=multibyte&tr1=1.6&tr2=1.6.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/dic/dic_search.c.diff?only_with_tag=multibyte&tr1=1.14&tr2=1.14.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/dic/dic_search.h.diff?only_with_tag=multibyte&tr1=1.10&tr2=1.10.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/dic/listdic.c.diff?only_with_tag=multibyte&tr1=1.6&tr2=1.6.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/Makefile.am.diff?only_with_tag=multibyte&tr1=1.11&tr2=1.11.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/bag.cpp.diff?only_with_tag=multibyte&tr1=1.5&tr2=1.5.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/board.cpp.diff?only_with_tag=multibyte&tr1=1.11&tr2=1.11.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/board.h.diff?only_with_tag=multibyte&tr1=1.10&tr2=1.10.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/coord.cpp.diff?only_with_tag=multibyte&tr1=1.7&tr2=1.7.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/coord.h.diff?only_with_tag=multibyte&tr1=1.5&tr2=1.5.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/duplicate.cpp.diff?only_with_tag=multibyte&tr1=1.14&tr2=1.14.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/duplicate.h.diff?only_with_tag=multibyte&tr1=1.10&tr2=1.10.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/encoding.cpp?only_with_tag=multibyte&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/encoding.h?only_with_tag=multibyte&rev=1.1.2.1
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/freegame.cpp.diff?only_with_tag=multibyte&tr1=1.16&tr2=1.16.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/freegame.h.diff?only_with_tag=multibyte&tr1=1.9&tr2=1.9.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/game.cpp.diff?only_with_tag=multibyte&tr1=1.27&tr2=1.27.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/game.h.diff?only_with_tag=multibyte&tr1=1.26&tr2=1.26.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/history.cpp.diff?only_with_tag=multibyte&tr1=1.8&tr2=1.8.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/history.h.diff?only_with_tag=multibyte&tr1=1.8&tr2=1.8.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/player.cpp.diff?only_with_tag=multibyte&tr1=1.12&tr2=1.12.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/player.h.diff?only_with_tag=multibyte&tr1=1.16&tr2=1.16.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/pldrack.cpp.diff?only_with_tag=multibyte&tr1=1.7&tr2=1.7.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/pldrack.h.diff?only_with_tag=multibyte&tr1=1.10&tr2=1.10.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/round.cpp.diff?only_with_tag=multibyte&tr1=1.8&tr2=1.8.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/round.h.diff?only_with_tag=multibyte&tr1=1.10&tr2=1.10.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/tile.cpp.diff?only_with_tag=multibyte&tr1=1.5&tr2=1.5.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/tile.h.diff?only_with_tag=multibyte&tr1=1.6&tr2=1.6.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/training.cpp.diff?only_with_tag=multibyte&tr1=1.14&tr2=1.14.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/training.h.diff?only_with_tag=multibyte&tr1=1.13&tr2=1.13.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/turn.cpp.diff?only_with_tag=multibyte&tr1=1.9&tr2=1.9.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/game/turn.h.diff?only_with_tag=multibyte&tr1=1.7&tr2=1.7.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/utils/eliottxt.cpp.diff?only_with_tag=multibyte&tr1=1.12&tr2=1.12.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/utils/game_io.cpp.diff?only_with_tag=multibyte&tr1=1.7&tr2=1.7.2.1&r1=text&r2=text
http://cvs.savannah.gnu.org/viewcvs/eliot/eliot/utils/ncurses.cpp.diff?only_with_tag=multibyte&tr1=1.19&tr2=1.19.2.1&r1=text&r2=text
Patches:
Index: eliot/dic/compdic.c
diff -u /dev/null eliot/dic/compdic.c:1.6.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/dic/compdic.c Wed Dec 28 16:47:35 2005
@@ -0,0 +1,333 @@
+/* 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. */
+/* */
+/* 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 <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"
+
+//#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);
+ fwrite (header, sizeof(Dict_header), 1, outfile);
+}
+
+
+void
+print_header_info(Dict_header *header)
+{
+ printf("============================\n");
+ printf("keyword length %lu bytes\n", strlen(_COMPIL_KEYWORD_));
+ printf("keyword size %lu bytes\n", sizeof(_COMPIL_KEYWORD_));
+ printf("header size %lu bytes\n", 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",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};
+
+ 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);
+ }
+
+ dicsize = file_length (argv[1]);
+ if (dicsize < 0)
+ {
+ fprintf(stderr,"Cannot stat uncompressed dictionary %s\n",argv[1]);
+ exit(1);
+ }
+
+ 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;
+}
+
+
Index: eliot/dic/dic_search.c
diff -u /dev/null eliot/dic/dic_search.c:1.14.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/dic/dic_search.c Wed Dec 28 16:47:35 2005
@@ -0,0 +1,581 @@
+/* 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. */
+/* */
+/* 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
*/
+
+/**
+ * \file dic_search.c
+ * \brief Dictionary lookup functions
+ * \author Antoine Fraboulet
+ * \date 2002
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dic_internals.h"
+#include "dic.h"
+#include "regexp.h"
+#include "dic_search.h"
+#include "libdic_a-er.h" /* generated by bison */
+#include "scanner.h" /* generated by flex */
+#include "automaton.h"
+
+/*
+ * shut down the compiler
+ */
+static int yy_init_globals (yyscan_t yyscanner )
+{
+ yy_init_globals(yyscanner);
+ return 0;
+}
+
+/**
+ * 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;
+}
+
+
+/**
+ * This method is a wrapper around the Dic_search_word_inner function.
+ * It simply converts the wchar_t* string into a char* one.
+ * XXX: This is a temporary hack until the dictionaries can handle multibyte
+ * characters properly... the Dic_search_word_inner function should disappear!
+ */
+int Dic_search_word(const Dictionary dic, const wchar_t* word)
+{
+ int res;
+ char *tmp_word;
+ size_t len;
+
+ // Get the needed length (we _can't_ use wstring::size())
+ len = wcstombs(NULL, word, 0);
+ if (len == (size_t)-1)
+ tmp_word = "";
+
+ // Convert the string
+ tmp_word = malloc(len + 1);
+ len = wcstombs(tmp_word, word, len + 1);
+
+ // 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);
+}
+
+void
+Dic_search_7pl1(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)
+ return;
+
+ /*
+ * the letters are verified and changed to the dic internal
+ * representation (*r & DIC_CHAR_MASK)
+ */
+ for(wordlen=0; wordlen < DIC_WORD_MAX && *r; r++)
+ {
+ if (isalpha(*r))
+ {
+ params.search_letters[(int)*r & DIC_CHAR_MASK]++;
+ wordlen++;
+ }
+ else if (*r == '?')
+ {
+ if (joker)
+ {
+ params.search_letters[0]++;
+ wordlen++;
+ }
+ else
+ {
+ strncpy(buff[0][0],"** joker **",DIC_WORD_MAX);
+ return;
+ }
+ }
+ }
+
+ if (wordlen < 1)
+ return;
+
+ root_edge = dic->dawg + (dic->dawg[dic->root].ptr);
+
+ params.search_dic = dic;
+ params.search_wordlistlenmax = RES_7PL1_MAX;
+
+ /* search for all the words that can be done with the letters */
+ params.search_len = wordlen - 1;
+ params.search_wordtst[wordlen]='\0';
+ params.search_wordlist = & buff[0];
+ params.search_wordlistlen = 0;
+ Dic_search_word_by_len(¶ms,0,root_edge);
+
+ /* search for all the words that can be done with the letters +1 */
+ params.search_len = wordlen;
+ params.search_wordtst[wordlen + 1]='\0';
+ for(i='a'; i <= 'z'; i++)
+ {
+ params.search_letters[i & DIC_CHAR_MASK]++;
+
+ params.search_wordlist = & buff[i & DIC_CHAR_MASK];
+ params.search_wordlistlen = 0;
+ Dic_search_word_by_len(¶ms,0,root_edge);
+
+ params.search_letters[i & DIC_CHAR_MASK]--;
+ }
+}
+
+/****************************************/
+/****************************************/
+
+void
+Dic_search_Racc(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)
+ 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);
+ }
+}
+
+/****************************************/
+/****************************************/
+
+
+void
+Dic_search_Benj(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)
+ 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);
+}
+
+
+/****************************************/
+/****************************************/
+
+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);
+ }
+}
+
+
+
+void
+Dic_search_Cros(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)
+ return;
+
+ for(i=0; i < DIC_WORD_MAX && mask[i]; i++)
+ {
+ if (isalpha(mask[i]))
+ params.mask[i] = (mask[i] & DIC_CHAR_MASK) + 'A' - 1;
+ else
+ params.mask[i] = '.';
+ }
+ params.mask[i] = '\0';
+
+ params.dic = dic;
+ params.wordlen = 0;
+ params.wordlistlen = 0;
+ params.wordlistlenmax = RES_CROS_MAX;
+ Dic_search_cross_rec(¶ms, wordlist, dic->dawg + dic->root);
+}
+
+/****************************************/
+/****************************************/
+
+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);
+}
+
+
+ /**
+ * function prototype for parser generated by bison
+ */
+int regexpparse(yyscan_t scanner, NODE** root,
+ struct search_RegE_list_t *list,
+ struct regexp_error_report_t *err);
+
+void
+Dic_search_RegE(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)
+ return;
+
+ /* (expr)# */
+ sprintf(stringbuf,"(%s)#",re);
+ for(i=0; i < REGEXP_MAX; i++)
+ {
+ PS[i] = 0;
+ ptl[i] = 0;
+ }
+
+ report.pos1 = 0;
+ report.pos2 = 0;
+ report.msg[0] = '\0';
+
+ /* parsing */
+ regexplex_init( &scanner );
+ buf = regexp_scan_string( stringbuf, scanner );
+ root = NULL;
+ value = regexpparse( scanner , &root, list, &report);
+ regexp_delete_buffer(buf,scanner);
+ regexplex_destroy( scanner );
+
+ if (value)
+ {
+#ifdef DEBUG_FLEX_IS_BROKEN
+ fprintf(stderr,"parser error at pos %d - %d : %s\n",
+ report.pos1, report.pos2, report.msg);
+#endif
+ regexp_delete_tree(root);
+ return ;
+ }
+
+ n = 1;
+ p = 1;
+ regexp_parcours(root, &p, &n, ptl);
+ PS [0] = p - 1;
+ ptl[0] = p - 1;
+
+ regexp_possuivante(root,PS);
+
+ if ((a = automaton_build(root->PP,ptl,PS,list)) != NULL)
+ {
+ params.dic = dic;
+ params.minlength = list->minlength;
+ params.maxlength = list->maxlength;
+ params.automaton = a;
+ params.charlist = list;
+ memset(params.word,'\0',sizeof(params.word));
+ params.wordlen = 0;
+ params.wordlistlen = 0;
+ params.wordlistlenmax = RES_REGE_MAX;
+ Dic_search_regexp_rec(¶ms, automaton_get_init(a), dic->dawg +
dic->root, wordlist);
+
+ automaton_delete(a);
+ }
+ regexp_delete_tree(root);
+}
+
+/****************************************/
+/****************************************/
+
Index: eliot/dic/dic_search.h
diff -u /dev/null eliot/dic/dic_search.h:1.10.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/dic/dic_search.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,110 @@
+/* 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. */
+/* */
+/* 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
*/
+
+/**
+ * \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 char* rack, char
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 char* word, char
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 char* word, char
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 char* mask, char
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 char* re, char
wordlist[RES_REGE_MAX][DIC_WORD_MAX], struct search_RegE_list_t *list);
+
+#if defined(__cplusplus)
+ }
+#endif
+#endif /* _DIC_SEARCH_H_ */
Index: eliot/dic/listdic.c
diff -u /dev/null eliot/dic/listdic.c:1.6.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/dic/listdic.c Wed Dec 28 16:47:35 2005
@@ -0,0 +1,234 @@
+/* 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. */
+/* */
+/* 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 <string.h>
+#include <stdlib.h>
+#include <stdio.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);
+}
+
+char
+b2h(int i)
+{
+ if (i < 10)
+ return i+'0';
+ return i-10+'a';
+}
+
+char*
+hexb(unsigned char h)
+{
+ static char buf[3];
+ buf[0] = b2h((h & 0xf0) >> 4);
+ buf[1] = b2h((h & 0x0f));
+ buf[2] = 0;
+ return buf;
+}
+
+char*
+hexl(unsigned int h)
+{
+ static char buf[9];
+ int i;
+ for(i=0; i<4; i++)
+ {
+ int l = h >> (24 - i*8);
+ buf[i*2+0] = b2h((l & 0xf0) >> 4);
+ buf[i*2+1] = b2h((l & 0x0f));
+ }
+ buf[8] = 0;
+ return buf;
+}
+
+char*
+offset(void* base, void* off)
+{
+ static char buf[20];
+ int o = (char*)off - (char*)base;
+ sprintf(buf,"%s",hexb(o));
+ return buf;
+}
+
+void
+print_header(char* filename)
+{
+ FILE* file;
+ Dict_header header;
+
+ if ((file = fopen(filename,"rb")) == NULL)
+ return;
+ if (fread(&header,sizeof(Dict_header),1,file) != 1)
+ return;
+ fclose(file);
+
+ printf("Dictionary header information\n");
+ printf("0x%s ident : %s\n",offset(&header,&header.ident),header.ident);
+ printf("0x%s unused 1 : %6d %s\n",offset(&header,&header.unused_1)
,header.unused_1 ,hexl(header.unused_1));
+ printf("0x%s unused 2 : %6d %s\n",offset(&header,&header.unused_2)
,header.unused_2 ,hexl(header.unused_2));
+ printf("0x%s root : %6d %s\n",offset(&header,&header.root)
,header.root ,hexl(header.root));
+ printf("0x%s words : %6d %s\n",offset(&header,&header.nwords)
,header.nwords ,hexl(header.nwords));
+ printf("0x%s edges used : %6d %s\n",offset(&header,&header.edgesused)
,header.edgesused ,hexl(header.edgesused));
+ printf("0x%s nodes used : %6d %s\n",offset(&header,&header.nodesused)
,header.nodesused ,hexl(header.nodesused));
+ printf("0x%s nodes saved : %6d
%s\n",offset(&header,&header.nodessaved),header.nodessaved,hexl(header.nodessaved));
+ printf("0x%s edges saved : %6d
%s\n",offset(&header,&header.edgessaved),header.edgessaved,hexl(header.edgessaved));
+ printf("\n");
+ printf("sizeof(header) = 0x%s (%lu)\n", hexb(sizeof(header)),
sizeof(header));
+}
+
+void
+print_node_hex(int i, Dictionary dic)
+{
+ unsigned int* pe;
+ Dawg_edge e = dic->dawg[i];
+ pe = (unsigned int*)&e;
+ printf("0x%s %s |%2d ptr=%2d t=%d l=%d f=%d chr=%d (%c)\n",
+ offset(&(dic->dawg[0]),&(dic->dawg[i])),hexl(*pe),i,
+ e.ptr, e.term, e.last, e.fill, e.chr, 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(i,dic);
+ 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;
+}
Index: eliot/game/Makefile.am
diff -u /dev/null eliot/game/Makefile.am:1.11.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/Makefile.am Wed Dec 28 16:47:35 2005
@@ -0,0 +1,46 @@
+# Eliot
+# Copyright (C) 1999 Antoine Fraboulet
+# address@hidden
+#
+# 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
+
+noinst_LIBRARIES = libgame.a
+
+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 \
+ board.cpp board.h \
+ 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 \
+ player.cpp player.h \
+ pldrack.cpp pldrack.h \
+ rack.cpp rack.h \
+ results.cpp results.h \
+ round.cpp round.h \
+ training.cpp training.h \
+ turn.cpp turn.h \
+ history.cpp history.h
+
Index: eliot/game/bag.cpp
diff -u /dev/null eliot/game/bag.cpp:1.5.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/bag.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,138 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 <string>
+
+#include "tile.h"
+#include "bag.h"
+#include "debug.h"
+
+
+Bag::Bag()
+{
+ init();
+}
+
+
+void Bag::init()
+{
+ m_ntiles = 0;
+ const list<Tile>& allTiles = Tile::getAllTiles();
+ list<Tile>::const_iterator it;
+ for (it = allTiles.begin(); it != allTiles.end(); it++)
+ {
+ m_tilesMap[*it] = it->maxNumber();
+ m_ntiles += it->maxNumber();
+ }
+}
+
+
+unsigned int Bag::in(const Tile &iTile) const
+{
+ map<Tile, int>::const_iterator it = m_tilesMap.find(iTile);
+ if (it != m_tilesMap.end())
+ return (*it).second;
+ return 0;
+}
+
+
+unsigned int Bag::nVowels() const
+{
+ map<Tile, int>::const_iterator it;
+ int v = 0;
+
+ for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++)
+ {
+ if (it->first.isVowel())
+ v += it->second;
+ }
+ return v;
+}
+
+
+unsigned int Bag::nConsonants() const
+{
+ map<Tile, int>::const_iterator it;
+ int c = 0;
+
+ for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++)
+ {
+ if (it->first.isConsonant())
+ c += it->second;
+ }
+ return c;
+}
+
+
+void Bag::takeTile(const Tile &iTile)
+{
+ ASSERT(in(iTile),
+ (wstring(L"The bag does not contain the letter ") +
iTile.toChar()).c_str());
+
+ m_tilesMap[iTile]--;
+ m_ntiles--;
+}
+
+
+void Bag::replaceTile(const Tile &iTile)
+{
+ ASSERT(in(iTile) < iTile.maxNumber(),
+ (wstring(L"Cannot replace tile: ") + iTile.toChar()).c_str());
+
+ m_tilesMap[iTile]++;
+ m_ntiles++;
+}
+
+
+Tile Bag::selectRandom()
+{
+ map<Tile, int>::const_iterator it;
+ int n;
+ double max = m_ntiles;
+
+ n = (int)(max * rand() / (RAND_MAX + 1.0));
+ for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++)
+ {
+ if (n < it->second)
+ return it->first;
+ n -= it->second;
+ }
+ ASSERT(false, "We should not come here");
+ return Tile::dummy();
+}
+
+
+void Bag::operator=(const Bag &iOther)
+{
+ m_tilesMap = iOther.m_tilesMap;
+ m_ntiles = iOther.m_ntiles;
+}
+
+
+void Bag::dumpAll() const
+{
+ map<Tile, int>::const_iterator it;
+ for (it = m_tilesMap.begin(); it != m_tilesMap.end(); it++)
+ {
+ if (it->second)
+ fprintf(stderr, "%c[%i] ", it->first.toChar(), it->second);
+ }
+ fprintf(stderr, "\n");
+}
Index: eliot/game/board.cpp
diff -u /dev/null eliot/game/board.cpp:1.11.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/board.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,476 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 <wctype.h>
+#include "dic.h"
+#include "tile.h"
+#include "round.h"
+#include "bag.h"
+#include "rack.h"
+#include "results.h"
+#include "board.h"
+
+#define oo 0
+#define __ 1
+#define T2 2
+#define T3 3
+#define W2 2
+#define W3 3
+
+
+const int Board::m_tileMultipliers[BOARD_REALDIM][BOARD_REALDIM] =
+{
+ { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo },
+ { oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
+ { oo,__,__,__,__,__,T3,__,__,__,T3,__,__,__,__,__,oo },
+ { oo,__,__,__,__,__,__,T2,__,T2,__,__,__,__,__,__,oo },
+ { oo,T2,__,__,__,__,__,__,T2,__,__,__,__,__,__,T2,oo },
+ { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
+ { oo,__,T3,__,__,__,T3,__,__,__,T3,__,__,__,T3,__,oo },
+ { oo,__,__,T2,__,__,__,T2,__,T2,__,__,__,T2,__,__,oo },
+ { oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
+ { oo,__,__,T2,__,__,__,T2,__,T2,__,__,__,T2,__,__,oo },
+ { oo,__,T3,__,__,__,T3,__,__,__,T3,__,__,__,T3,__,oo },
+ { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
+ { oo,T2,__,__,__,__,__,__,T2,__,__,__,__,__,__,T2,oo },
+ { oo,__,__,__,__,__,__,T2,__,T2,__,__,__,__,__,__,oo },
+ { oo,__,__,__,__,__,T3,__,__,__,T3,__,__,__,__,__,oo },
+ { oo,__,__,__,T2,__,__,__,__,__,__,__,T2,__,__,__,oo },
+ { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo }
+};
+
+
+const int Board::m_wordMultipliers[BOARD_REALDIM][BOARD_REALDIM] =
+{
+ { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo },
+ { oo,W3,__,__,__,__,__,__,W3,__,__,__,__,__,__,W3,oo },
+ { oo,__,W2,__,__,__,__,__,__,__,__,__,__,__,W2,__,oo },
+ { oo,__,__,W2,__,__,__,__,__,__,__,__,__,W2,__,__,oo },
+ { oo,__,__,__,W2,__,__,__,__,__,__,__,W2,__,__,__,oo },
+ { oo,__,__,__,__,W2,__,__,__,__,__,W2,__,__,__,__,oo },
+ { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
+ { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
+ { oo,W3,__,__,__,__,__,__,W2,__,__,__,__,__,__,W3,oo },
+ { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
+ { oo,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,oo },
+ { oo,__,__,__,__,W2,__,__,__,__,__,W2,__,__,__,__,oo },
+ { oo,__,__,__,W2,__,__,__,__,__,__,__,W2,__,__,__,oo },
+ { oo,__,__,W2,__,__,__,__,__,__,__,__,__,W2,__,__,oo },
+ { oo,__,W2,__,__,__,__,__,__,__,__,__,__,__,W2,__,oo },
+ { oo,W3,__,__,__,__,__,__,W3,__,__,__,__,__,__,W3,oo },
+ { oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo,oo }
+};
+
+
+Board::Board():
+ m_tilesRow(BOARD_REALDIM, Tile::dummy()),
+ m_tilesCol(BOARD_REALDIM, Tile::dummy()),
+ m_jokerRow(BOARD_REALDIM, false),
+ m_jokerCol(BOARD_REALDIM, false),
+ m_crossRow(BOARD_REALDIM, Cross()),
+ m_crossCol(BOARD_REALDIM, Cross()),
+ m_pointRow(BOARD_REALDIM, -1),
+ m_pointCol(BOARD_REALDIM, -1),
+ m_testsRow(BOARD_REALDIM, 0)
+{
+ // No cross check allowed around the board
+ for (int i = 0; i < BOARD_REALDIM; i++)
+ {
+ m_crossRow[0][i].clear();
+ m_crossCol[0][i].clear();
+ m_crossRow[i][0].clear();
+ m_crossCol[i][0].clear();
+ m_crossRow[BOARD_REALDIM - 1][i].clear();
+ m_crossCol[BOARD_REALDIM - 1][i].clear();
+ m_crossRow[i][BOARD_REALDIM - 1].clear();
+ m_crossCol[i][BOARD_REALDIM - 1].clear();
+ }
+}
+
+
+wchar_t Board::getChar(int iRow, int iCol) const
+{
+ wchar_t letter = 0;
+ Tile tile = getTile(iRow, iCol);
+ if (!tile.isEmpty())
+ {
+ letter = tile.toChar();
+ if (isJoker(iRow, iCol))
+ letter = towlower(letter);
+ }
+ return letter;
+}
+
+int Board::getCharAttr(int iRow, int iCol) const
+{
+ int t = getTestChar(iRow, iCol);
+ int j = isJoker(iRow, iCol);
+ return (t << 1) | j;
+}
+
+
+Tile Board::getTile(int iRow, int iCol) const
+{
+ return m_tilesRow[iRow][iCol];
+}
+
+
+bool Board::isJoker(int iRow, int iCol) const
+{
+ return m_jokerRow[iRow][iCol];
+}
+
+
+bool Board::isVacant(int iRow, int iCol) const
+{
+ if (iRow < 1 || iRow > BOARD_DIM ||
+ iCol < 1 || iCol > BOARD_DIM)
+ {
+ return false;
+ }
+ return m_tilesRow[iRow][iCol].isEmpty();
+}
+
+
+void Board::addRound(const Dictionary &iDic, const Round &iRound)
+{
+ Tile t;
+ int row, col;
+
+ row = iRound.getCoord().getRow();
+ col = iRound.getCoord().getCol();
+ if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
+ {
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (m_tilesRow[row][col + i].isEmpty())
+ {
+ t = iRound.getTile(i);
+ m_tilesRow[row][col + i] = t;
+ m_jokerRow[row][col + i] = iRound.isJoker(i);
+ m_tilesCol[col + i][row] = t;
+ m_jokerCol[col + i][row] = iRound.isJoker(i);
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (m_tilesRow[row + i][col].isEmpty())
+ {
+ t = iRound.getTile(i);
+ m_tilesRow[row + i][col] = t;
+ m_jokerRow[row + i][col] = iRound.isJoker(i);
+ m_tilesCol[col][row + i] = t;
+ m_jokerCol[col][row + i] = iRound.isJoker(i);
+ }
+ }
+ }
+ buildCross(iDic);
+#ifdef DEBUG
+ checkDouble();
+#endif
+}
+
+
+void Board::removeRound(const Dictionary &iDic, const Round &iRound)
+{
+ int row, col;
+
+ row = iRound.getCoord().getRow();
+ col = iRound.getCoord().getCol();
+ if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
+ {
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (iRound.isPlayedFromRack(i))
+ {
+ m_tilesRow[row][col + i] = Tile::dummy();
+ m_jokerRow[row][col + i] = false;
+ m_tilesCol[col + i][row] = Tile::dummy();
+ m_jokerCol[col + i][row] = false;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (iRound.isPlayedFromRack(i))
+ {
+ m_tilesRow[row + i][col] = Tile::dummy();
+ m_jokerRow[row + i][col] = false;
+ m_tilesCol[col][row + i] = Tile::dummy();
+ m_jokerCol[col][row + i] = false;
+ }
+ }
+ }
+ buildCross(iDic);
+#ifdef DEBUG
+ checkDouble();
+#endif
+}
+
+
+/* XXX: There is duplicated code with board_search.c.
+ * We could probably factorize something... */
+int Board::checkRoundAux(Matrix<Tile> &iTilesMx,
+ Matrix<Cross> &iCrossMx,
+ Matrix<int> &iPointsMx,
+ Matrix<bool> &iJokerMx,
+ Round &iRound,
+ bool firstturn)
+{
+ Tile t;
+ int row, col, i;
+ int l, p, fromrack;
+ int pts, ptscross, wordmul;
+ bool isolated = true;
+
+ fromrack = 0;
+ pts = 0;
+ ptscross = 0;
+ wordmul = 1;
+ row = iRound.getCoord().getRow();
+ col = iRound.getCoord().getCol();
+
+ /* Is the word an extension of another word? */
+ if (!iTilesMx[row][col - 1].isEmpty() ||
+ !iTilesMx[row][col + iRound.getWordLen()].isEmpty())
+ {
+ return 1;
+ }
+
+ for (i = 0; i < iRound.getWordLen(); i++)
+ {
+ t = iRound.getTile(i);
+ if (!iTilesMx[row][col + i].isEmpty())
+ {
+ /* There is already a letter on the board */
+ if (iTilesMx[row][col + i] != t)
+ return 2;
+
+ isolated = false;
+ iRound.setFromBoard(i);
+
+ if (!iJokerMx[row][col + i])
+ pts += t.getPoints();
+ }
+ else
+ {
+ /* The letter is not yet on the board */
+ if (iCrossMx[row][col + i].check(t))
+ {
+ /* A non-trivial cross-check means an anchor square */
+ if (!iCrossMx[row][col + i].isAny())
+ isolated = false;
+
+ if (!iRound.isJoker(i))
+ l = t.getPoints() * m_tileMultipliers[row][col + i];
+ else
+ l = 0;
+ pts += l;
+ wordmul *= m_wordMultipliers[row][col + i];
+
+ p = iPointsMx[row][col + i];
+ if (p >= 0)
+ {
+ ptscross += (p + l) * m_wordMultipliers[row][col + i];
+ }
+ fromrack++;
+ iRound.setFromRack(i);
+ }
+ else
+ {
+ /* The letter is not in the crosscheck */
+ return 3;
+ }
+ }
+ }
+
+ /* There must be at least 1 letter from the rack */
+ if (fromrack == 0)
+ return 4;
+
+ /* The word must cover at least one anchor square, except
+ * for the first turn */
+ if (isolated && !firstturn)
+ return 5;
+ /* The first word must be horizontal */
+ if (firstturn && iRound.getCoord().getDir() == Coord::VERTICAL)
+ return 6;
+ /* The first word must cover the H8 square */
+ if (firstturn
+ && (row != 8 || col > 8 || col + iRound.getWordLen() <= 8))
+ {
+ return 7;
+ }
+
+ /* Set the iPointsMx and bonus */
+ pts = ptscross + pts * wordmul + 50 * (fromrack == 7);
+ iRound.setPoints(pts);
+ iRound.setBonus(fromrack == 7);
+
+ return 0;
+}
+
+
+int Board::checkRound(Round &iRound, bool firstturn)
+{
+ if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
+ {
+ return checkRoundAux(m_tilesRow, m_crossRow,
+ m_pointRow, m_jokerRow,
+ iRound, firstturn);
+ }
+ else
+ {
+ // XXX: ugly!
+ // Exchange the coordinates temporarily
+ iRound.accessCoord().swap();
+
+ int res = checkRoundAux(m_tilesCol, m_crossCol,
+ m_pointCol, m_jokerCol,
+ iRound, firstturn);
+
+ // Restore the coordinates
+ iRound.accessCoord().swap();
+
+ return res;
+ }
+}
+
+
+void Board::testRound(const Round &iRound)
+{
+ Tile t;
+ int row, col;
+
+ row = iRound.getCoord().getRow();
+ col = iRound.getCoord().getCol();
+ if (iRound.getCoord().getDir() == Coord::HORIZONTAL)
+ {
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (m_tilesRow[row][col + i].isEmpty())
+ {
+ t = iRound.getTile(i);
+ m_tilesRow[row][col + i] = t;
+ m_jokerRow[row][col + i] = iRound.isJoker(i);
+ m_testsRow[row][col + i] = 1;
+
+ m_tilesCol[col + i][row] = t;
+ m_jokerCol[col + i][row] = iRound.isJoker(i);
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (m_tilesRow[row + i][col].isEmpty())
+ {
+ t = iRound.getTile(i);
+ m_tilesRow[row + i][col] = t;
+ m_jokerRow[row + i][col] = iRound.isJoker(i);
+ m_testsRow[row + i][col] = 1;
+
+ m_tilesCol[col][row + i] = t;
+ m_jokerCol[col][row + i] = iRound.isJoker(i);
+ }
+ }
+ }
+}
+
+
+void Board::removeTestRound()
+{
+ for (int row = 1; row <= BOARD_DIM; row++)
+ {
+ for (int col = 1; col <= BOARD_DIM; col++)
+ {
+ if (m_testsRow[row][col])
+ {
+ m_tilesRow[row][col] = Tile::dummy();
+ m_testsRow[row][col] = 0;
+ m_jokerRow[row][col] = false;
+
+ m_tilesCol[col][row] = Tile::dummy();
+ m_jokerCol[col][row] = false;
+ }
+ }
+ }
+}
+
+
+char Board::getTestChar(int iRow, int iCol) const
+{
+ return m_testsRow[iRow][iCol];
+}
+
+
+int Board::getWordMultiplier(int iRow, int iCol) const
+{
+ if (iRow < BOARD_MIN || iRow > BOARD_MAX ||
+ iCol < BOARD_MIN || iCol > BOARD_MAX)
+ return 0;
+ return m_wordMultipliers[iRow][iCol];
+}
+
+
+int Board::getLetterMultiplier(int iRow, int iCol) const
+{
+ if (iRow < BOARD_MIN || iRow > BOARD_MAX ||
+ iCol < BOARD_MIN || iCol > BOARD_MAX)
+ return 0;
+ return m_tileMultipliers[iRow][iCol];
+}
+
+
+#ifdef DEBUG
+void Board::checkDouble()
+{
+ for (int row = BOARD_MIN; row <= BOARD_MAX; row++)
+ {
+ for (int col = BOARD_MIN; col <= BOARD_MAX; col++)
+ {
+ if (m_tilesRow[row][col] != m_tilesCol[col][row])
+ printf("tiles diff %d %d\n", row, col);
+
+ // The crossckecks and the points have no reason to be the same
+ // in both directions
+ /*
+ if (m_crossRow[row][col] != m_crossCol[col][row])
+ {
+ printf("cross diff %d %d\n",row,col);
+ }
+
+ if (m_pointRow[row][col] != m_pointCol[col][row])
+ printf("point diff %d %d\n",row,col);
+ */
+
+ if (m_jokerRow[row][col] != m_jokerCol[col][row])
+ printf("joker diff %d %d\n", row, col);
+ }
+ }
+}
+#endif
+
Index: eliot/game/board.h
diff -u /dev/null eliot/game/board.h:1.10.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/board.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 _BOARD_H_
+#define _BOARD_H_
+
+#include "tile.h"
+#include "cross.h"
+#include <string>
+#include <vector>
+
+typedef struct _Dictionary*Dictionary;
+class Rack;
+class Round;
+class Results;
+
+using namespace std;
+
+#define BOARD_MIN 1
+#define BOARD_MAX 15
+#define BOARD_DIM 15
+#define BOARD_REALDIM (BOARD_DIM + 2)
+
+
+// Template matrix class for convenience.
+template <class T>
+class Matrix: public vector<vector<T> >
+{
+public:
+ // Construct a matrix with an initial value
+ Matrix(int iSize1, int iSize2, const T &iValue)
+ {
+ resize(iSize1, vector<T>(iSize2, iValue));
+ }
+ // Construct a square matrix with an initial value
+ Matrix(int iSize, const T &iValue)
+ {
+ resize(iSize, vector<T>(iSize, iValue));
+ }
+};
+
+
+class Board
+{
+public:
+ Board();
+ virtual ~Board() {}
+
+ /*************************
+ * Coordinates have to be BOARD_MIN <= int <= BOARD_MAX
+ *
+ * getChar returns an upper case letter for normal tiles and a
+ * lower case letter for jokers.
+ *
+ * getCharAttr tells the attributes of the tile
+ * 0 : normal played tile
+ * 1 : joker tile
+ * 2 : test tile for preview purpose
+ * Attributes can be combined with the or (|) operator
+ *************************/
+#define ATTR_NORMAL 0
+#define ATTR_JOKER 1
+#define ATTR_TEST 2
+
+ wchar_t getChar (int iRow, int iCol) const;
+ int getCharAttr(int iRow, int iCol) const;
+
+ Tile getTile(int iRow, int iCol) const;
+ bool isJoker(int iRow, int iCol) const;
+ bool isVacant(int iRow, int iCol) const;
+
+ void addRound(const Dictionary &iDic, const Round &iRound);
+ void removeRound(const Dictionary &iDic, const Round &iRound);
+ int checkRound(Round &iRound, bool iFirstTurn);
+
+ /*************************
+ *
+ *
+ *************************/
+ void testRound(const Round &iRound);
+ void removeTestRound();
+ char getTestChar(int iRow, int iCol) const;
+
+ /*************************
+ *
+ * board_search.c
+ *************************/
+ void search(const Dictionary &iDic, const Rack &iRack, Results &oResults);
+ void searchFirst(const Dictionary &iDic, const Rack &iRack, Results
&oResults);
+
+ /*************************
+ *
+ * board_cross.c
+ *************************/
+ void buildCross(const Dictionary &iDic);
+
+ /*************************
+ *
+ *
+ *************************/
+ int getWordMultiplier(int iRow, int iCol) const;
+ int getLetterMultiplier(int iRow, int iCol) const;
+
+private:
+
+ Matrix<Tile> m_tilesRow;
+ Matrix<Tile> m_tilesCol;
+
+ Matrix<bool> m_jokerRow;
+ Matrix<bool> m_jokerCol;
+
+ Matrix<Cross> m_crossRow;
+ Matrix<Cross> m_crossCol;
+
+ Matrix<int> m_pointRow;
+ Matrix<int> m_pointCol;
+
+ Matrix<char> m_testsRow;
+
+ static const int m_tileMultipliers[BOARD_REALDIM][BOARD_REALDIM];
+ static const int m_wordMultipliers[BOARD_REALDIM][BOARD_REALDIM];
+
+ int checkRoundAux(Matrix<Tile> &iTilesMx,
+ Matrix<Cross> &iCrossMx,
+ Matrix<int> &iPointsMx,
+ Matrix<bool> &iJokerMx,
+ Round &iRound,
+ bool firstturn);
+#ifdef DEBUG
+ void checkDouble();
+#endif
+
+};
+
+#endif
Index: eliot/game/coord.cpp
diff -u /dev/null eliot/game/coord.cpp:1.7.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/coord.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,117 @@
+/* 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 coord.cpp
+ * \brief Eliot coordinate system
+ * \author Antoine Fraboulet
+ * \date 2005
+ */
+
+#include <string>
+#include <wchar.h>
+#include "coord.h"
+#include "board.h" // for BOARD_MIN and BOARD_MAX (TODO: remove this include)
+#include "debug.h"
+#include "encoding.h"
+
+
+Coord::Coord(int iRow, int iCol, Direction iDir)
+{
+ m_row = iRow;
+ m_col = iCol;
+ m_dir = iDir;
+}
+
+Coord::Coord(const wstring &iStr)
+{
+ setFromString(iStr);
+}
+
+bool Coord::isValid() const
+{
+ return (m_row >= BOARD_MIN && m_row <= BOARD_MAX &&
+ m_col >= BOARD_MIN && m_col <= BOARD_MAX);
+}
+
+void Coord::operator=(const Coord &iOther)
+{
+ m_dir = iOther.m_dir;
+ m_row = iOther.m_row;
+ m_col = iOther.m_col;
+}
+
+void Coord::swap()
+{
+ int tmp = m_col;
+ m_col = m_row;
+ m_row = tmp;
+}
+
+
+void Coord::setFromString(const wstring &iWStr)
+{
+ // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
+ // Temporary implementation: convert the wchar_t* string into a char* one
+ string iStr = convertToMb(iWStr);
+
+ char l[4];
+ int col;
+
+ if (sscanf(iStr.c_str(), "%1[a-oA-O]%2d", l, &col) == 2)
+ {
+ setDir(HORIZONTAL);
+ }
+ else if (sscanf(iStr.c_str(), "%2d%1[a-oA-O]", &col, l) == 2)
+ {
+ setDir(VERTICAL);
+ }
+ else
+ {
+ col = -1;
+ l[0] = 'A' - 1;
+ }
+ int row = toupper(*l) - 'A' + 1;
+ setCol(col);
+ setRow(row);
+}
+
+wstring Coord::toString() const
+{
+ ASSERT(isValid(), "Invalid coordinates");
+
+ wstring res;
+ wchar_t s[5];
+ swprintf(s, 4, L"%d", m_col);
+ if (getDir() == HORIZONTAL)
+ {
+ res = wstring(1, m_row + 'A' - 1) + s;
+ }
+ else
+ {
+ res = s + wstring(1, m_row + 'A' - 1);
+ }
+ return res;
+}
+
+
+/// Local Variables:
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/coord.h
diff -u /dev/null eliot/game/coord.h:1.5.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/coord.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,73 @@
+/* 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 coord.h
+ * \brief Game coordinates system
+ * \author Antoine Fraboulet
+ * \date 2005
+ */
+
+#ifndef _COORD_H
+#define _COORD_H
+
+using std::string;
+using std::wstring;
+
+class Coord
+{
+public:
+
+ enum Direction {VERTICAL, HORIZONTAL};
+
+ // Construction, destruction
+ Coord(int iRow = -1, int iCol = -1, Direction iDir = HORIZONTAL);
+ Coord(const wstring &iStr);
+ virtual ~Coord() {}
+
+ // Accessors
+ void setRow(int iRow) { m_row = iRow; }
+ void setCol(int iCol) { m_col = iCol; }
+ void setDir(Direction iDir) { m_dir = iDir; }
+ int getRow() const { return m_row; }
+ int getCol() const { return m_col; }
+ Direction getDir() const { return m_dir; }
+
+ bool isValid() const;
+ void operator=(const Coord &iOther);
+
+ // Swap the coordinates (without changing the direction)
+ void swap();
+
+ void setFromString(const wstring &iStr);
+ wstring toString() const;
+
+private:
+ Direction m_dir;
+ int m_row, m_col;
+
+};
+
+#endif
+
+
+/// Local Variables:
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/duplicate.cpp
diff -u /dev/null eliot/game/duplicate.cpp:1.14.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/duplicate.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,286 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 "tile.h"
+#include "rack.h"
+#include "round.h"
+#include "pldrack.h"
+#include "results.h"
+#include "player.h"
+#include "ai_player.h"
+#include "duplicate.h"
+#include "debug.h"
+
+
+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 */
+ Round round;
+ int res = checkPlayedWord(iCoord, iWord, round);
+ if (res != 0)
+ {
+ return res;
+ }
+
+ /* Everything is OK, we can play the word */
+ playRound(round, m_currPlayer);
+
+ /* Next turn */
+ // XXX: Should it be done by the interface instead?
+ endTurn();
+
+ return 0;
+}
+
+
+void Duplicate::duplicateAI(int n)
+{
+ ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
+ ASSERT(!m_players[n]->isHuman(), "AI requested for a human player");
+
+ AIPlayer *player = static_cast<AIPlayer*>(m_players[n]);
+ player->compute(*m_dic, m_board, m_history.getSize());
+
+ if (player->changesLetters())
+ {
+ // 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);
+ }
+}
+
+
+int Duplicate::start()
+{
+ ASSERT(getNPlayers(), "Cannot start a game without any player");
+
+ 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? */
+ if (res == 1)
+ {
+ end();
+ return 1;
+ }
+
+ const PlayedRack& pld = m_players[m_currPlayer]->getCurrentRack();
+ /* All the players have the same rack */
+ for (int i = 0; i < getNPlayers(); i++)
+ {
+ if (i != m_currPlayer)
+ {
+ m_players[i]->setCurrentRack(pld);
+ }
+ /* Nobody has played yet in this round */
+ m_hasPlayed[i] = false;
+ }
+
+ /* Next turn */
+ // XXX: Should it be done by the interface instead?
+ endTurn();
+
+ 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()
+{
+ int i;
+ for (i = 0; i < getNPlayers(); i++)
+ {
+ if (m_players[i]->isHuman() && !m_hasPlayed[i])
+ {
+ /* A human player has not played... */
+ m_currPlayer = i;
+ // XXX: check return code meaning
+ return 1;
+ }
+ }
+
+ /* If all the human players have played */
+ if (i == getNPlayers())
+ {
+ /* Make AI players play their turn */
+ for (i = 0; i < getNPlayers(); i++)
+ {
+ if (!m_players[i]->isHuman())
+ {
+ duplicateAI(i);
+ }
+ }
+
+ /* Next turn */
+ endTurnForReal();
+ }
+
+ // XXX: check return code meaning
+ return 0;
+}
+
+
+void Duplicate::playRound(const Round &iRound, int n)
+{
+ ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
+ Player *player = m_players[n];
+
+ /* Update the rack and the score of the current player */
+ player->addPoints(iRound.getPoints());
+ player->endTurn(iRound, m_history.getSize());
+
+ m_hasPlayed[n] = 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()
+{
+ int res, i, imax;
+
+ /* Play the best word on the board */
+ imax = 0;
+ for (i = 1; i < getNPlayers(); i++)
+ {
+ if (m_players[i]->getLastRound().getPoints() >
+ m_players[imax]->getLastRound().getPoints())
+ {
+ 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)
+ {
+ end();
+ return 1;
+ }
+
+ const PlayedRack& pld = m_players[imax]->getCurrentRack();
+ /* All the players have the same rack */
+ for (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;
+}
+
+
+void Duplicate::end()
+{
+ m_finished = true;
+}
+
+
+int Duplicate::setPlayer(int n)
+{
+ ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
+
+ /* Forbid switching to an AI player */
+ if (!m_players[n]->isHuman())
+ return 1;
+
+ m_currPlayer = n;
+ return 0;
+}
+
+
+void Duplicate::prevHumanPlayer()
+{
+ if (getNHumanPlayers() == 0)
+ return;
+ // FIXME: possible infinite loop...
+ do
+ {
+ prevPlayer();
+ } while (!m_players[m_currPlayer]->isHuman() ||
+ m_hasPlayed[m_currPlayer]);
+}
+
+
+void Duplicate::nextHumanPlayer()
+{
+ if (getNHumanPlayers() == 0)
+ return;
+ // FIXME: possible infinite loop...
+ do
+ {
+ nextPlayer();
+ } while (!m_players[m_currPlayer]->isHuman() ||
+ m_hasPlayed[m_currPlayer]);
+}
+
Index: eliot/game/duplicate.h
diff -u /dev/null eliot/game/duplicate.h:1.10.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/duplicate.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,85 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 _DUPLICATE_H_
+#define _DUPLICATE_H_
+
+#include "game.h"
+
+using std::string;
+using std::wstring;
+
+
+/**
+ * This class handles the logic specific to a duplicate game.
+ * The trick in this mode is that the players will not necessarily play they
+ * word always in the same order, so we need to implement a "synchronization":
+ * - when a human player wants to play a word, he plays it, and its score
+ * 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
+ * (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:
+ * the best word is played on the board, the history of the game is
+ * updated, and the new rack is chosen.
+ *
+ * AI players play after human ones, because with the current implementation
+ * of the interfaces it is too easy for a player to see the rack of other
+ * players, and in particular a human player could take advantage of that to
+ * have more clues about the best word.
+ * TODO: better isolation of the players...
+ */
+class Duplicate: public Game
+{
+ friend class GameFactory;
+public:
+ virtual GameMode getMode() const { return kDUPLICATE; }
+ virtual string getModeAsString() const { return "Duplicate"; }
+
+ /*************************
+ * Game handling
+ *************************/
+ 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
+ void prevHumanPlayer();
+ // Switch to the next human player who has not played yet
+ void nextHumanPlayer();
+
+private:
+ // Private constructor and destructor 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);
+
+ // m_hasPlayed[p] is true iff player p has played for this turn
+ map<int, bool> m_hasPlayed;
+};
+
+#endif /* _DUPLICATE_H_ */
Index: eliot/game/encoding.cpp
diff -u /dev/null eliot/game/encoding.cpp:1.1.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/encoding.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,86 @@
+/* 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 <wctype.h>
+#include "encoding.h"
+
+
+int _wtoi(const wchar_t *iWStr)
+{
+ int res = 0;
+ while (iswdigit(iWStr[0]))
+ {
+ res = 10 * res + (iWStr[0] - '0');
+ iWStr++;
+ }
+ 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;
+}
+
Index: eliot/game/encoding.h
diff -u /dev/null eliot/game/encoding.h:1.1.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/encoding.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,49 @@
+/* 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);
+
+// 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
+
Index: eliot/game/freegame.cpp
diff -u /dev/null eliot/game/freegame.cpp:1.16.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/freegame.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,257 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 <iomanip>
+#include <wctype.h>
+#include "dic.h"
+#include "tile.h"
+#include "rack.h"
+#include "round.h"
+#include "pldrack.h"
+#include "results.h"
+#include "player.h"
+#include "ai_player.h"
+#include "freegame.h"
+
+#include "debug.h"
+
+
+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 */
+ Round round;
+
+ int res = checkPlayedWord(iCoord, iWord, round);
+ if (res != 0)
+ {
+ 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());
+
+ /* Everything is OK, we can play the word */
+ helperPlayRound(round);
+
+ /* Next turn */
+ // XXX: Should it be done by the interface instead?
+ endTurn();
+
+ return 0;
+}
+
+
+void FreeGame::freegameAI(int n)
+{
+ ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
+ ASSERT(!m_players[n]->isHuman(), "AI requested for a human player");
+
+ AIPlayer *player = static_cast<AIPlayer*>(m_players[n]);
+
+ player->compute(*m_dic, m_board, m_history.getSize());
+ if (player->changesLetters())
+ {
+ helperPass(player->getChangedLetters(), n);
+ endTurn();
+ }
+ 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();
+ }
+}
+
+
+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++)
+ {
+ setRackRandom(i, false, RACK_NEW);
+ }
+
+ // XXX
+ m_currPlayer = 0;
+
+ /* If the first player is an AI, make it play now */
+ if (!m_players[0]->isHuman())
+ {
+ freegameAI(0);
+ }
+
+ return 0;
+}
+
+
+int FreeGame::endTurn()
+{
+ /* Complete the rack for the player that just played */
+ if (setRackRandom(m_currPlayer, false, RACK_NEW) == 1)
+ {
+ /* End of the game */
+ end();
+ return 1;
+ }
+
+ /* Next player */
+ nextPlayer();
+
+ /* If this player is an AI, make it play now */
+ if (!m_players[m_currPlayer]->isHuman())
+ {
+ freegameAI(m_currPlayer);
+ }
+
+ return 0;
+}
+
+
+// Adjust the scores of the players with the points of the remaining tiles
+void FreeGame::end()
+{
+ vector<Tile> tiles;
+
+ // TODO: According to the rules of the game in the ODS, a game can end in 3
+ // cases:
+ // 1) no more letter in the bag, and one player has no letter in his rack
+ // 2) the game is "blocked", no one can play
+ // 3) the players have used all the time they had (for example: 30 min
+ // in total, for each player)
+ // 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).
+
+ /* 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++)
+ {
+ if (i != m_currPlayer)
+ {
+ const PlayedRack &pld = m_players[i]->getCurrentRack();
+ pld.getAllTiles(tiles);
+ for (unsigned int j = 0; j < tiles.size(); j++)
+ {
+ m_players[i]->addPoints(- tiles[j].getPoints());
+ m_players[m_currPlayer]->addPoints(tiles[j].getPoints());
+ }
+ }
+ }
+
+ /* Lock game */
+ m_finished = true;
+}
+
+
+int FreeGame::pass(const wstring &iToChange, int n)
+{
+ if (m_finished)
+ return 3;
+
+ // 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).
+
+ // 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;
+}
+
+
+int FreeGame::helperPass(const vector<Tile> &iToChange, int n)
+{
+ ASSERT(0 <= n && n < getNPlayers(), "Wrong player number");
+
+ // 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;
+ }
+
+ 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!
+
+ return 0;
+}
+
Index: eliot/game/freegame.h
diff -u /dev/null eliot/game/freegame.h:1.9.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/freegame.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 _FREEGAME_H_
+#define _FREEGAME_H_
+
+#include "game.h"
+#include "tile.h"
+
+using std::string;
+using std::wstring;
+using std::vector;
+
+
+/**
+ * This class handles the logic specific to a "free" game.
+ *
+ * The algorithm is simple: players play at their turn, and they can either
+ * play a word or change letters (changing letters implies passing its turn).
+ *
+ * When a player has no more letters (end of the game), the points of the
+ * letters left in the racks of his opponents are added to his score, and
+ * removed from the score of the latters.
+ */
+class FreeGame: public Game
+{
+ friend class GameFactory;
+public:
+ virtual GameMode getMode() const { return kFREEGAME; }
+ virtual string getModeAsString() const { return "Free game"; }
+
+ /*************************
+ * Game handling
+ *************************/
+ virtual int start();
+ virtual int setRackRandom(int, bool, set_rack_mode);
+ virtual int play(const wstring &iCoord, const wstring &iWord);
+ virtual int endTurn();
+ int pass(const wstring &iToChange, int n);
+
+private:
+ // Private constructor and destructor 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);
+};
+
+#endif /* _FREEGAME_H_ */
Index: eliot/game/game.cpp
diff -u /dev/null eliot/game/game.cpp:1.27.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/game.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,855 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 "dic_search.h"
+#include "tile.h"
+#include "rack.h"
+#include "round.h"
+#include "pldrack.h"
+#include "results.h"
+#include "player.h"
+#include "ai_percent.h"
+#include "game.h"
+#include "game_factory.h"
+#include "turn.h"
+#include "encoding.h"
+
+#include "debug.h"
+
+
+const int Game::RACK_SIZE = 7;
+
+
+Game::Game(const Dictionary &iDic):
+ m_dic(&iDic)
+{
+ m_variant = kNONE;
+ m_points = 0;
+ m_currPlayer = -1;
+ m_finished = false;
+}
+
+
+Game::~Game()
+{
+ for (int i = 0; i < getNPlayers(); i++)
+ {
+ delete m_players[i];
+ }
+}
+
+
+const Player& Game::getPlayer(int iNum) const
+{
+ ASSERT(0 <= iNum && iNum < (int)m_players.size(), "Wrong player number");
+ return *(m_players[iNum]);
+}
+
+
+Game * Game::load(FILE *fin, const Dictionary &iDic)
+{
+ char buff[4096];
+ char delim[] = " \t\n|";
+ char *token;
+
+ // Check characteristic string
+ if (fgets(buff, sizeof(buff), fin) == NULL)
+ return NULL;
+ if ((token = strtok(buff, delim)) == NULL)
+ return NULL;
+ if (string(token) != IDENT_STRING)
+ return NULL;
+
+ int num;
+ char rack[20];
+ char word[20];
+ char ref[4];
+ int pts;
+ int player;
+ char *pos;
+ Tile tile;
+ Game *pGame = NULL;
+
+ while (fgets(buff, sizeof(buff), fin))
+ {
+ // Indication of game type
+ pos = strstr(buff, "Game type: ");
+ if (pos != NULL)
+ {
+ // No Game object should have been created yet
+ if (pGame != NULL)
+ {
+ delete pGame;
+ return NULL;
+ }
+ // Create the correct Game object
+ if (strstr(buff, "Training"))
+ pGame = GameFactory::Instance()->createTraining(iDic);
+ else if (strstr(buff, "Free game"))
+ pGame = GameFactory::Instance()->createFreeGame(iDic);
+ else if (strstr(buff, "Duplicate"))
+ pGame = GameFactory::Instance()->createDuplicate(iDic);
+ else
+ return NULL;
+ // Read next line
+ continue;
+ }
+
+ // Players type
+ pos = strstr(buff, "Player ");
+ if (pos != NULL && pGame != NULL)
+ {
+ int nb = 0;
+ char type[20];
+ if (sscanf(pos, "Player %d: %19s", &nb, type) > 1)
+ {
+ if (string(type) == "Human")
+ pGame->addHumanPlayer();
+ else if (string(type) == "Computer")
+ pGame->addAIPlayer();
+ else
+ ;
+ }
+ // Read next line
+ continue;
+ }
+
+ // Last racks
+ pos = strstr(buff, "Rack ");
+ if (pos != NULL && pGame != NULL)
+ {
+ int nb = 0;
+ char letters[20];
+ if (sscanf(pos, "Rack %d: %19s", &nb, letters) > 1)
+ {
+ // Create the played rack
+ PlayedRack pldrack;
+ char *r = letters;
+ if (strchr(r, '+'))
+ {
+ while (*r != '+')
+ {
+ pldrack.addOld(Tile(*r));
+ r++;
+ }
+ r++;
+ }
+ while (*r)
+ {
+ pldrack.addNew(Tile(*r));
+ r++;
+ }
+
+ // Give the rack to the player
+ pGame->m_players[nb]->setCurrentRack(pldrack);
+ }
+ // Read next line
+ continue;
+ }
+
+ // Skip columns title
+ if (strstr(buff, "==") != NULL ||
+ strstr(buff, "| PTS | P |") != NULL)
+ {
+ continue;
+ }
+
+ if (string(buff) != "\n" && pGame != NULL)
+ {
+ char bonus = 0;
+ int res = sscanf(buff, " %2d | %8s | %s | %3s | %3d | %1d | %c",
+ &num, rack, word, ref, &pts, &player, &bonus);
+ if (res < 6)
+ continue;
+
+ // Integrity checks
+ // TODO: add more checks
+ if (pts < 0)
+ continue;
+ if (player < 0 || player > pGame->getNPlayers())
+ continue;
+ if (bonus && bonus != '*')
+ continue;
+
+ // Build a rack for the correct player
+ PlayedRack pldrack;
+ char *r = rack;
+ if (strchr(r, '+'))
+ {
+ while (*r != '+')
+ {
+ pldrack.addOld(Tile(*r));
+ r++;
+ }
+ r++;
+ }
+
+ while (*r)
+ {
+ pldrack.addNew(Tile(*r));
+ r++;
+ }
+
+ // Build a round
+ Round round;
+ // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
+// round.accessCoord().setFromString(ref);
+ if (!round.getCoord().isValid())
+ continue;
+
+ round.setPoints(pts);
+ if (bonus == '*')
+ round.setBonus(1);
+
+ for (unsigned int i = 0; i < strlen(word); i++)
+ {
+ tile = Tile(word[i]);
+
+ if (round.getCoord().getDir() == Coord::HORIZONTAL)
+ {
+ if (!pGame->m_board.getTile(round.getCoord().getRow(),
+ round.getCoord().getCol() +
i).isEmpty())
+ {
+ round.addRightFromBoard(tile);
+ }
+ else
+ {
+ round.addRightFromRack(tile, islower(word[i]));
+ pGame->m_bag.takeTile((islower(word[i])) ?
Tile::Joker() : tile);
+ }
+ }
+ else
+ {
+ if (!pGame->m_board.getTile(round.getCoord().getRow() + i,
+
round.getCoord().getCol()).isEmpty())
+ {
+ round.addRightFromBoard(tile);
+ }
+ else
+ {
+ round.addRightFromRack(tile, islower(word[i]));
+ pGame->m_bag.takeTile((islower(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);
+ // Add the points
+ pGame->m_players[player]->addPoints(pts);
+ // Play the round
+ pGame->helperPlayRound(round);
+ }
+ }
+
+ // Finalize the game
+ if (pGame)
+ {
+ // 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++)
+ {
+ if (pGame->getPlayer(i).isHuman())
+ {
+ pGame->m_currPlayer = i;
+ break;
+ }
+ }
+ }
+ return pGame;
+}
+
+
+void Game::save(ostream &out) const
+{
+ const string decal = " ";
+ // "Header" of the game
+ out << IDENT_STRING << endl << endl;
+ out << "Game type: " << getModeAsString() << endl;
+ for (int i = 0; i < getNPlayers(); i++)
+ {
+ out << "Player " << i << ": ";
+ if (getPlayer(i).isHuman())
+ out << "Human" << endl;
+ else
+ out << "Computer" << endl;
+ }
+ out << endl;
+
+ // Title of the columns
+ char line[100];
+ out << decal << " N | RACK | SOLUTION | REF | PTS | P | BONUS"
<< endl;
+ out << decal << "===|==========|=================|=====|=====|===|======"
<< endl;
+
+ // Print the game itself
+ for (int i = 0; i < m_history.getSize(); i++)
+ {
+ const Turn& t = m_history.getTurn(i);
+ wstring word = t.getRound().getWord();
+ wstring coord = t.getRound().getCoord().toString();
+ sprintf(line, "%2d | %8s | %s%s | %3s | %3d | %1d | %c",
+ i + 1, convertToMb(t.getPlayedRack().toString()).c_str(),
+ convertToMb(word).c_str(),
+ string(15 - word.size(), ' ').c_str(),
+ convertToMb(coord).c_str(), t.getRound().getPoints(),
+ t.getPlayer(), t.getRound().getBonus() ? '*' : ' ');
+
+ out << decal << line << endl;
+ }
+ out << endl << decal << "Total: " << m_points << endl;
+
+ // Print current rack for all the players
+ out << endl;
+ for (int i = 0; i < getNPlayers(); i++)
+ {
+ wstring rack = getPlayer(i).getCurrentRack().toString();
+ out << "Rack " << i << ": " << convertToMb(rack) << endl;
+ }
+}
+
+
+/* This function plays a round on the board */
+int Game::helperPlayRound(const Round &iRound)
+{
+ /*
+ * 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_points += iRound.getPoints();
+
+ // 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++)
+ {
+ 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...
+ Tile t(toupper(iRound.getTile(i).toChar()));
+ Bag bag;
+ realBag(bag);
+ // FIXME: realBag() does not give us a real bag in this
+ // particular case! This is because Player::endTurn() is called
+ // before Game::helperPlayRound(), which means that the rack
+ // of the player is updated, while the word is not actually
+ // played on the board yet. Since realBag() relies on
+ // Player::getCurrentRack(), it doesn't remove the letters of
+ // the current player, which are in fact available through
+ // Player::getLastRack().
+ // That's why we have to replace the letters of the current
+ // rack and remove the ones from the previous rack...
+ // 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);
+ for (unsigned int j = 0; j < tiles.size(); j++)
+ {
+ bag.replaceTile(tiles[j]);
+ }
+ getPlayer(m_currPlayer).getLastRack().getAllTiles(tiles);
+ for (unsigned int j = 0; j < tiles.size(); j++)
+ {
+ bag.takeTile(tiles[j]);
+ }
+
+ if (bag.in(t))
+ {
+ // FIXME: A const_cast sucks too...
+ const_cast<Round&>(iRound).setTile(i, t);
+ // FIXME: This shouldn't be necessary either, this is only
+ // needed because of the stupid way of handling jokers in
+ // rounds
+ const_cast<Round&>(iRound).setJoker(i, false);
+ }
+ }
+ }
+ }
+
+ // Update the bag and the board
+ for (int i = 0; i < iRound.getWordLen(); i++)
+ {
+ if (iRound.isPlayedFromRack(i))
+ {
+ if (iRound.isJoker(i))
+ {
+ m_bag.takeTile(Tile::Joker());
+ }
+ else
+ m_bag.takeTile(iRound.getTile(i));
+ }
+ }
+ m_board.addRound(*m_dic, iRound);
+
+ return 0;
+}
+
+
+int Game::back(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++)
+ {
+ if (m_history.getSize() > 0)
+ {
+ prevPlayer();
+ player = m_players[m_currPlayer];
+ const Round &lastround = m_history.getPreviousTurn().getRound();
+// debug("Game::back last round
%s\n",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.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;
+ }
+ }
+ return 0;
+}
+
+
+/*********************************************************
+ *********************************************************/
+
+void Game::realBag(Bag &ioBag) const
+{
+ vector<Tile> tiles;
+
+ /* Copy the bag */
+ ioBag = m_bag;
+
+ /* The real content of the bag depends on the game mode */
+ if (getMode() == kFREEGAME)
+ {
+ /* In freegame mode, replace the letters from all the racks */
+ for (int i = 0; i < getNPlayers(); i++)
+ {
+ getPlayer(i).getCurrentRack().getAllTiles(tiles);
+ for (unsigned int j = 0; j < tiles.size(); j++)
+ {
+ ioBag.takeTile(tiles[j]);
+ }
+ }
+ }
+ else
+ {
+ /* In training or duplicate mode, replace the rack of the current
+ * player only */
+ getPlayer(m_currPlayer).getCurrentRack().getAllTiles(tiles);
+ for (unsigned int j = 0; j < tiles.size(); j++)
+ {
+ ioBag.takeTile(tiles[j]);
+ }
+ }
+}
+
+
+bool Game::rackInBag(const Rack &iRack, const Bag &iBag) const
+{
+ const list<Tile>& allTiles = Tile::getAllTiles();
+ list<Tile>::const_iterator it;
+ for (it = allTiles.begin(); it != allTiles.end(); it++)
+ {
+ if (iRack.in(*it) > iBag.in(*it))
+ return false;
+ }
+ return true;
+}
+
+
+int Game::helperSetRackRandom(int p, bool iCheck, set_rack_mode mode)
+{
+ ASSERT(0 <= p && p < getNPlayers(), "Wrong player number");
+
+ int nold, min;
+
+ // Make a copy of the player's rack
+ PlayedRack pld = getPlayer(p).getCurrentRack();
+ nold = pld.nOld();
+
+ // Create a copy of the bag in which we can do everything we want,
+ // and remove from it the tiles of the racks
+ Bag bag;
+ realBag(bag);
+
+ // We may have removed too many letters from the bag (i.e. the 'new'
+ // letters of the player)
+ if (mode == RACK_NEW && nold != 0)
+ {
+ vector<Tile> tiles;
+ pld.getNewTiles(tiles);
+ for (unsigned int i = 0; i < tiles.size(); i++)
+ {
+ bag.replaceTile(tiles[i]);
+ }
+ pld.resetNew();
+ }
+ else
+ {
+ // 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;
+ }
+
+ // Nothing in the rack, nothing in the bag --> end of the game
+ if (bag.nTiles() == 0 && pld.nTiles() == 0)
+ {
+ return 1;
+ }
+
+ // 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
+ // 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;
+ if (iCheck)
+ {
+ int oldc, oldv;
+
+ if (bag.nVowels() == 0 || bag.nConsonants() == 0)
+ {
+ return 1;
+ }
+ // 2 vowels and 2 consonants are needed up to the 15th turn
+ if (bag.nVowels() > 1 && bag.nConsonants() > 1
+ && m_history.getSize() < 15)
+ min = 2;
+ else
+ min = 1;
+
+ // Count the remaining consonants and vowels in the rack
+ vector<Tile> tiles;
+ pld.getOldTiles(tiles);
+ oldc = 0;
+ oldv = 0;
+ for (unsigned int i = 0; i < tiles.size(); i++)
+ {
+ if (tiles[i].isConsonant())
+ oldc++;
+ if (tiles[i].isVowel())
+ oldv++;
+ }
+
+ // RACK_SIZE - nold is the number of letters to add
+ if (min > oldc + RACK_SIZE - nold ||
+ min > oldv + RACK_SIZE - nold)
+ {
+ // We cannot fill the rack with enough vowels or consonants!
+ return 3;
+ }
+ }
+
+ // 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;
+ vector<Tile> tiles;
+ pld.getOldTiles(tiles);
+ 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()))
+ {
+ bag.takeTile(Tile::Joker());
+ pld.addNew(Tile::Joker());
+ }
+
+ // 3) Complete the rack normally... but without any joker!
+ Tile l;
+ while (bag.nTiles() != 0 && pld.nTiles() != RACK_SIZE)
+ {
+ l = bag.selectRandom();
+ if (!l.isJoker())
+ {
+ bag.takeTile(l);
+ pld.addNew(l);
+ }
+ }
+ }
+ else // Normal game
+ {
+ // Get new tiles from the bag
+ Tile l;
+ while (bag.nTiles() != 0 && pld.nTiles() != RACK_SIZE)
+ {
+ l = bag.selectRandom();
+ bag.takeTile(l);
+ pld.addNew(l);
+ }
+ }
+
+ if (iCheck && !pld.checkRack(min))
+ return 2;
+
+ m_players[p]->setCurrentRack(pld);
+
+ return 0;
+}
+
+
+int Game::helperSetRackManual(int p, bool iCheck, const wstring &iLetters)
+{
+ unsigned int i;
+ int min;
+
+ PlayedRack pld = getPlayer(p).getCurrentRack();
+ pld.reset();
+
+ if (iLetters.size() == 0)
+ {
+ return 0;
+ }
+
+ for (i = 0; i < iLetters.size() && iLetters[i] != '+'; i++)
+ {
+ Tile tile(iLetters[i]);
+ if (tile.isEmpty())
+ {
+ return 1;
+ }
+ pld.addOld(tile);
+ }
+
+ if (i < iLetters.size() && iLetters[i] == '+')
+ {
+ for (i++; i < iLetters.size(); i++)
+ {
+ Tile tile(iLetters[i]);
+ if (tile.isEmpty())
+ {
+ return 1;
+ }
+ pld.addNew(tile);
+ }
+ }
+
+ Rack rack;
+ pld.getRack(rack);
+ if (!rackInBag(rack, m_bag))
+ {
+ pld.reset();
+ return 1;
+ }
+
+ if (iCheck)
+ {
+ if (m_bag.nVowels() > 1 && m_bag.nConsonants() > 1
+ && m_history.getSize() < 15)
+ min = 2;
+ else
+ min = 1;
+ if (!pld.checkRack(min))
+ return 2;
+ }
+
+ m_players[p]->setCurrentRack(pld);
+
+ return 0;
+}
+
+/*********************************************************
+ *********************************************************/
+
+
+int Game::getNHumanPlayers() const
+{
+ int count = 0;
+ for (int i = 0; i < getNPlayers(); i++)
+ count += (getPlayer(i).isHuman() ? 1 : 0);
+ return count;
+}
+
+
+void Game::addHumanPlayer()
+{
+ // The ID of the player is its position in the m_players vector
+ m_players.push_back(new HumanPlayer(getNPlayers()));
+}
+
+
+void Game::addAIPlayer()
+{
+ m_players.push_back(new AIPercent(getNPlayers(), 0));
+}
+
+
+void Game::prevPlayer()
+{
+ ASSERT(getNPlayers() != 0, "Expected at least one player");
+
+ if (m_currPlayer == 0)
+ m_currPlayer = getNPlayers() - 1;
+ else
+ m_currPlayer--;
+}
+
+
+void Game::nextPlayer()
+{
+ ASSERT(getNPlayers() != 0, "Expected at least one player");
+
+ if (m_currPlayer == getNPlayers() - 1)
+ m_currPlayer = 0;
+ else
+ m_currPlayer++;
+}
+
+
+/*
+ * 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;
+
+ /* Init the round with the given coordinates */
+ oRound.init();
+ oRound.accessCoord().setFromString(iCoord);
+ if (!oRound.getCoord().isValid())
+ return 2;
+
+ /* Check the existence of the word */
+ if (Dic_search_word(*m_dic, iWord.c_str()) == 0)
+ return 3;
+
+ /* 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...
+ for (unsigned int i = 0; i < iWord.size(); i++)
+ {
+ tiles.push_back(Tile(iWord[i]));
+ }
+ oRound.setWord(tiles);
+ for (unsigned int i = 0; i < iWord.size(); i++)
+ {
+ if (islower(iWord[i]))
+ 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);
+ 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 */
+ Rack rack;
+ Player *player = m_players[m_currPlayer];
+ player->getCurrentRack().getRack(rack);
+
+ for (int i = 0; i < oRound.getWordLen(); i++)
+ {
+ if (oRound.isPlayedFromRack(i))
+ {
+ if (oRound.isJoker(i))
+ t = Tile::Joker();
+ else
+ t = oRound.getTile(i);
+
+ if (!rack.in(t))
+ {
+ return 4;
+ }
+ rack.remove(t);
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************/
+/****************************************************************/
+
+/// Local Variables:
+/// mode: c++
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/game.h
diff -u /dev/null eliot/game/game.h:1.26.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/game.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,199 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 _GAME_H_
+#define _GAME_H_
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include "bag.h"
+#include "board.h"
+#include "history.h"
+
+class Player;
+class PlayedRack;
+class Round;
+class Rack;
+class Turn;
+typedef struct _Dictionary * Dictionary;
+
+using namespace std;
+
+
+/*************************
+ * Ident string used to identify saved Eliot games
+ *************************/
+#define IDENT_STRING "Eliot"
+
+/**
+ * Parent class of all the Game types.
+ * It offers the common attributes (Board, Bag, etc...) as well as useful
+ * "helper" methods to factorize some code.
+ */
+class Game
+{
+public:
+ Game(const Dictionary &iDic);
+ virtual ~Game();
+
+ /// Game mode: each one of these modes is implemented in an inherited class
+ enum GameMode
+ {
+ kTRAINING,
+ kFREEGAME,
+ kDUPLICATE
+ };
+ virtual GameMode getMode() const = 0;
+ virtual string getModeAsString() const = 0;
+
+ /// Game variant: it slightly modifies the rules of the game
+ enum GameVariant
+ {
+ kNONE, // Normal game rules
+ kJOKER // Joker game
+ };
+
+ /**
+ * Accessors for the variant of the game.
+ * The variant can be changed during a game without any problem
+ * (though it seems rather useless...)
+ */
+ 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; }
+
+ 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;
+ const Player& getCurrentPlayer() const { return getPlayer(currPlayer()); };
+
+ /**
+ * 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
+ * handle "hand written" files
+ */
+ static Game * load(FILE *fin, const Dictionary &iDic);
+ void save(ostream &out) const;
+
+ /*************************
+ * Playing the game
+ * the int parameter should be 0 <= int < getNTurns
+ *************************/
+ int back(int);
+
+ /*************************
+ * 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)
+ *************************/
+ static const int RACK_SIZE;
+ enum set_rack_mode {RACK_ALL, RACK_NEW, RACK_MANUAL};
+ int setRack(int player, set_rack_mode mode, bool check, const wstring&
str);
+
+ /** Getter for the history of the game */
+ 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;
+
+// TODO: check what should be private and what should be protected
+// private:
+
+ /// Variant
+ GameVariant m_variant;
+
+ /// Dictionary currently associated to the game
+ const Dictionary * m_dic;
+
+ /// Bag
+ Bag m_bag;
+
+ /// Board
+ Board m_board;
+
+ /**
+ * History of the game.
+ * The vector is indexed by the number of turns in the game
+ */
+ History m_history;
+
+ int m_points;
+
+ bool m_finished;
+
+ /*********************************************************
+ * 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);
+
+ void prevPlayer();
+ void nextPlayer();
+ bool rackInBag(const Rack &iRack, const Bag &iBag) const;
+ void realBag(Bag &iBag) const;
+ int checkPlayedWord(const wstring &iCoord,
+ const wstring &iWord, Round &oRound);
+};
+
+#endif /* _GAME_H_ */
Index: eliot/game/history.cpp
diff -u /dev/null eliot/game/history.cpp:1.8.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/history.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,181 @@
+/* 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 history.cpp
+ * \brief Game history system
+ * \author Antoine Fraboulet
+ * \date 2005
+ */
+
+#include <string>
+#include "rack.h"
+#include "pldrack.h"
+#include "round.h"
+#include "turn.h"
+#include "debug.h"
+#include "history.h"
+
+/* ******************************************************** */
+/* ******************************************************** */
+/* ******************************************************** */
+
+
+History::History()
+{
+ Turn* t = new Turn();
+ m_history.clear();
+ m_history.push_back(t);
+}
+
+
+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;
+ }
+ }
+}
+
+
+int History::getSize() const
+{
+ return m_history.size() - 1;
+}
+
+
+const PlayedRack& History::getCurrentRack() const
+{
+ return m_history.back()->getPlayedRack();
+}
+
+
+void History::setCurrentRack(const PlayedRack &iPld)
+{
+ m_history.back()->setPlayedRack(iPld);
+}
+
+
+const Turn& History::getPreviousTurn() const
+{
+ int idx = m_history.size() - 2;
+ ASSERT(0 <= idx , "No previous turn");
+ return *(m_history[idx]);
+}
+
+
+const Turn& History::getTurn(unsigned int n) const
+{
+ ASSERT(0 <= n && 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)
+{
+ 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 (round.isPlayedFromRack(i))
+ {
+ if (round.isJoker(i))
+ rack.remove(Tile::Joker());
+ else
+ rack.remove(round.getTile(i));
+ }
+ }
+
+ /* create a new turn */
+ Turn * next_turn = new Turn();
+ PlayedRack pldrack;
+ pldrack.setOld(rack);
+ next_turn->setPlayedRack(pldrack);
+ m_history.push_back(next_turn);
+}
+
+
+void History::removeLastTurn()
+{
+ int idx = m_history.size();
+ ASSERT(0 < idx , "Wrong turn number");
+
+ if (idx > 1)
+ {
+ Turn *t = m_history.back();
+ m_history.pop_back();
+ delete t;
+ }
+
+ // Now we have the previous played round in back()
+ Turn* t = m_history.back();
+ t->setNum(0);
+ t->setPlayer(0);
+ t->setRound(Round());
+#ifdef BACK_REMOVE_RACK_NEW_PART
+ t->getPlayedRound().setNew(Rack());
+#endif
+}
+
+
+wstring History::toString() const
+{
+ wstring rs;
+#ifdef DEBUG
+ wchar_t buff[5];
+ swprintf(buff, 4, L"%4ld", m_history.size());
+ rs = L"history size = " + wstring(buff) + L"\n\n";
+#endif
+ for (unsigned int i = 0; i < m_history.size(); i++)
+ {
+ Turn *t = m_history[i];
+ rs += t->toString() + L"\n";
+ }
+ return rs;
+}
+
+/* ******************************************************** */
+/* ******************************************************** */
+/* ******************************************************** */
+
+
+/// Local Variables:
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/history.h
diff -u /dev/null eliot/game/history.h:1.8.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/history.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,104 @@
+/* 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 history.h
+ * \brief Game history system
+ * \author Antoine Fraboulet
+ * \date 2005
+ */
+
+#ifndef _HISTORY_H
+#define _HISTORY_H
+
+#include <string>
+#include <vector>
+
+using std::wstring;
+using std::vector;
+
+class Round;
+class Turn;
+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
+ *
+ * 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.
+ *
+ * getCurrentRack() can/should be used to store the current played rack.
+ * setCurrentRack must be called whenever the current played rack is
+ * modified.
+ *
+ * History owns the turns that it stores. Do not delete a turn referenced by
History
+ */
+
+class History
+{
+public:
+ History();
+ virtual ~History();
+
+ /// get the size of the history
+ int getSize() const;
+
+ /// Get the (possibly incomplete) rack
+ const PlayedRack& getCurrentRack() const;
+
+ /// Set the current rack
+ void setCurrentRack(const PlayedRack &iPld);
+
+ /// Get the previous turn
+ const Turn& getPreviousTurn() const;
+
+ /// Get turn 'n'
+ 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);
+
+ /// Remove last turn
+ void removeLastTurn();
+
+ /// String handling
+ wstring toString() const;
+
+private:
+ vector<Turn*> m_history;
+};
+
+#endif
+
+
+/// Local Variables:
+/// mode: c++
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/player.cpp
diff -u /dev/null eliot/game/player.cpp:1.12.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/player.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,99 @@
+/*****************************************************************************
+ * Copyright (C) 2004-2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 "rack.h"
+#include "pldrack.h"
+#include "round.h"
+#include "results.h"
+#include "board.h"
+#include "player.h"
+#include "turn.h"
+#include "history.h"
+
+#include "debug.h"
+
+
+Player::Player(int iId)
+{
+ m_id = iId;
+ m_score = 0;
+}
+
+
+Player::~Player()
+{
+}
+
+
+const PlayedRack & Player::getCurrentRack() const
+{
+ return m_history.getCurrentRack();
+}
+
+
+void Player::setCurrentRack(const PlayedRack &iPld)
+{
+ m_history.setCurrentRack(iPld);
+}
+
+
+const PlayedRack & Player::getLastRack() const
+{
+ return m_history.getPreviousTurn().getPlayedRack();
+}
+
+
+const Round & Player::getLastRound() const
+{
+ return m_history.getPreviousTurn().getRound();
+}
+
+
+void Player::endTurn(const Round &iRound, int iTurn)
+{
+ m_history.playRound(m_id,iTurn,iRound);
+}
+
+void Player::removeLastTurn()
+{
+ m_history.removeLastTurn();
+}
+
+wstring Player::toString() const
+{
+ wstring res;
+
+ wchar_t buff[6];
+ swprintf(buff, 5, L"Player %5d\n", m_id);
+ res = wstring(buff);
+ res += m_history.toString() + L"\n";
+ swprintf(buff, 5, L"score %5d\n", m_score);
+ res += wstring(buff);
+ return res;
+}
+
+/****************************************************************/
+/****************************************************************/
+
+/// Local Variables:
+/// mode: c++
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/player.h
diff -u /dev/null eliot/game/player.h:1.16.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/player.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * Copyright (C) 2004-2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 _PLAYER_H_
+#define _PLAYER_H_
+
+#include <vector>
+#include <string>
+#include "pldrack.h"
+#include "history.h"
+
+class Turn;
+
+
+/**
+ * This class is the parent class for all the players involved in a game.
+ * It defines the common methods to update the rack, score, etc...
+ */
+class Player
+{
+public:
+ Player(int iId);
+ virtual ~Player();
+
+ // Pseudo RTTI
+ virtual bool isHuman() const = 0;
+
+ /**************************
+ * General getters
+ **************************/
+ // Get the (possibly incomplete) rack of the player
+ 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;
+
+ void setCurrentRack(const PlayedRack &iPld);
+
+ const History& getHistory() const { return m_history; }
+ /// Remove last turn
+ void removeLastTurn();
+
+ /**************************
+ * Acessors for the score of the player
+ **************************/
+ // Add (or remove, if iPoints is negative) points to the score
+ // of the player
+ 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);
+
+ wstring toString() const;
+
+private:
+ /// ID of the player
+ int m_id;
+
+ /// Score of the player
+ int m_score;
+
+ /// History of the racks and rounds for the player
+ History m_history;
+};
+
+
+/**
+ * Human player.
+ */
+class HumanPlayer: public Player
+{
+public:
+ HumanPlayer(int iId): Player(iId) {}
+ virtual ~HumanPlayer() {}
+
+ // Pseudo RTTI
+ virtual bool isHuman() const { return true; }
+};
+
+#endif
+
Index: eliot/game/pldrack.cpp
diff -u /dev/null eliot/game/pldrack.cpp:1.7.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/pldrack.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,181 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 "rack.h"
+#include "pldrack.h"
+
+#include "debug.h"
+
+
+void PlayedRack::addOld(const Tile &t)
+{
+ m_oldTiles.push_back(t);
+}
+
+
+void PlayedRack::addNew(const Tile &t)
+{
+ m_newTiles.push_back(t);
+}
+
+
+void PlayedRack::getOldTiles(vector<Tile> &oTiles) const
+{
+ oTiles.clear();
+ for (int i = 0; i < nOld(); i++)
+ oTiles.push_back(m_oldTiles[i]);
+}
+
+
+void PlayedRack::getNewTiles(vector<Tile> &oTiles) const
+{
+ oTiles.clear();
+ for (int i = 0; i < nNew(); i++)
+ oTiles.push_back(m_newTiles[i]);
+}
+
+
+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]);
+}
+
+
+void PlayedRack::reset()
+{
+ m_oldTiles.clear();
+ m_newTiles.clear();
+}
+
+
+void PlayedRack::resetNew()
+{
+ m_newTiles.clear();
+}
+
+
+void PlayedRack::getOld(Rack &oRack) const
+{
+ vector<Tile>::const_iterator it;
+ oRack.clear();
+ for (it = m_oldTiles.begin(); it != m_oldTiles.end(); it++)
+ {
+ oRack.add(*it);
+ }
+}
+
+
+void PlayedRack::getNew(Rack &oRack) const
+{
+ vector<Tile>::const_iterator it;
+ oRack.clear();
+ for (it = m_newTiles.begin(); it != m_newTiles.end(); it++)
+ {
+ oRack.add(*it);
+ }
+}
+
+
+void PlayedRack::getRack(Rack &oRack) const
+{
+ vector<Tile>::const_iterator it;
+ getOld(oRack);
+ for (it = m_newTiles.begin(); it != m_newTiles.end(); it++)
+ {
+ oRack.add(*it);
+ }
+}
+
+
+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);
+ }
+}
+
+
+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);
+ }
+}
+
+
+bool PlayedRack::checkRack(int iMin) const
+{
+ vector<Tile>::const_iterator it;
+ int v = 0;
+ int c = 0;
+
+ for (it = m_oldTiles.begin(); it != m_oldTiles.end(); it++)
+ {
+ if (it->isVowel()) v++;
+ if (it->isConsonant()) c++;
+ }
+ for (it = m_newTiles.begin(); it != m_newTiles.end(); it++)
+ {
+ if (it->isVowel()) v++;
+ if (it->isConsonant()) c++;
+ }
+ return (v >= iMin) && (c >= iMin);
+}
+
+
+void PlayedRack::operator=(const PlayedRack &iOther)
+{
+ m_oldTiles = iOther.m_oldTiles;
+ m_newTiles = iOther.m_newTiles;
+}
+
+
+wstring PlayedRack::toString(bool iShowExtraSigns) const
+{
+ vector<Tile>::const_iterator it;
+ wstring s;
+
+ for (it = m_oldTiles.begin(); it != m_oldTiles.end(); it++)
+ s += it->toChar();
+
+ if (iShowExtraSigns && nOld() > 0 && nNew() > 0)
+ s += L"+";
+
+ for (it = m_newTiles.begin(); it != m_newTiles.end(); it++)
+ s += it->toChar();
+
+ return s;
+}
Index: eliot/game/pldrack.h
diff -u /dev/null eliot/game/pldrack.h:1.10.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/pldrack.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 _PLAYEDRACK_H_
+#define _PLAYEDRACK_H_
+
+#include <vector>
+#include <string>
+#include "tile.h"
+
+class Rack;
+
+using namespace std;
+
+
+/**
+ * A Playedrack is an "improved" rack, allowing to differentiate new letters
+ * from letters that are left from the previous rack.
+ * This is useful, to be able to write a rack on the form ABC+DEFG, where
+ * A, B, C are the "old" letters and D, E, F, G are the "new" ones.
+ */
+class PlayedRack
+{
+public:
+ PlayedRack() {}
+ virtual ~PlayedRack() {}
+
+ void reset();
+ void resetNew();
+
+ void getOld(Rack &oRack) const;
+ void getNew(Rack &oRack) const;
+ void getRack(Rack &oRack) const;
+
+ void setOld(const Rack &iRack);
+ void setNew(const Rack &iRack);
+
+ int nTiles() const { return nNew() + nOld(); }
+ int nNew() const { return m_newTiles.size(); }
+ int nOld() const { return m_oldTiles.size(); }
+
+ void addNew(const Tile &t);
+ void addOld(const Tile &t);
+ void getNewTiles(vector<Tile> &oTiles) const;
+ void getOldTiles(vector<Tile> &oTiles) const;
+ void getAllTiles(vector<Tile> &oTiles) const;
+
+ bool checkRack(int iMin) const;
+
+ void operator=(const PlayedRack &iOther);
+ wstring toString(bool iShowExtraSigns = true) const;
+
+private:
+ vector<Tile> m_oldTiles;
+ vector<Tile> m_newTiles;
+};
+
+#endif
Index: eliot/game/round.cpp
diff -u /dev/null eliot/game/round.cpp:1.8.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/round.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,182 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 <string>
+#include <wctype.h>
+#include <wchar.h>
+#include "tile.h"
+#include "round.h"
+
+
+#define FROMBOARD 0x1
+#define FROMRACK 0x2
+#define JOKER 0x4
+
+
+Round::Round()
+{
+ init();
+}
+
+
+void Round::init()
+{
+ m_word.clear();
+ m_tileOrigin.clear();
+ m_coord.setRow(1);
+ m_coord.setCol(1);
+ m_coord.setDir(Coord::HORIZONTAL);
+ m_points = 0;
+ m_bonus = 0;
+}
+
+
+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);
+ }
+}
+
+
+void Round::setFromRack(int iIndex)
+{
+ m_tileOrigin[iIndex] &= ~FROMBOARD;
+ m_tileOrigin[iIndex] |= FROMRACK;
+}
+
+
+void Round::setFromBoard(int iIndex)
+{
+ m_tileOrigin[iIndex] &= ~FROMRACK;
+ m_tileOrigin[iIndex] |= FROMBOARD;
+}
+
+
+void Round::setJoker(int iIndex, bool value)
+{
+ if (value)
+ m_tileOrigin[iIndex] |= JOKER;
+ else
+ m_tileOrigin[iIndex] &= ~JOKER;
+}
+
+
+bool Round::isJoker(int iIndex) const
+{
+ return m_tileOrigin[iIndex] & JOKER;
+}
+
+
+const Tile& Round::getTile(int iIndex) const
+{
+ return m_word[iIndex];
+}
+
+
+int Round::getWordLen() const
+{
+ return m_word.size();
+}
+
+
+bool Round::isPlayedFromRack(int iIndex) const
+{
+ return m_tileOrigin[iIndex] & FROMRACK;
+}
+
+
+void Round::addRightFromBoard(Tile c)
+{
+ m_word.push_back(c);
+ m_tileOrigin.push_back(FROMBOARD);
+}
+
+
+void Round::removeRightToBoard(Tile c)
+{
+ // c is unused.
+ m_word.pop_back();
+ m_tileOrigin.pop_back();
+}
+
+
+void Round::addRightFromRack(Tile c, bool iJoker)
+{
+ m_word.push_back(c);
+ char origin = FROMRACK;
+ if (iJoker)
+ {
+ origin |= JOKER;
+ }
+ m_tileOrigin.push_back(origin);
+}
+
+
+void Round::removeRightToRack(Tile c, bool iJoker)
+{
+ // c is unused.
+ m_word.pop_back();
+ m_tileOrigin.pop_back();
+}
+
+wstring Round::getWord() const
+{
+ wchar_t c;
+ wstring s;
+
+ for (int i = 0; i < getWordLen(); i++)
+ {
+ c = getTile(i).toChar();
+ if (isJoker(i))
+ c = towlower(c);
+ s += c;
+ }
+ return s;
+}
+
+wstring Round::toString() const
+{
+ wstring rs = L" ";
+
+ if (getWord().size() > 0)
+ {
+ rs = getWord();
+ rs += wstring(16 - getWord().size(), ' ');
+ rs += getBonus() ? L'*' : L' ';
+ wchar_t buff[5];
+ swprintf(buff, 4, L"%4d", getPoints());
+ rs += buff;
+ rs += L" " + getCoord().toString();
+ }
+
+ return rs;
+}
+
+/// Local Variables:
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/round.h
diff -u /dev/null eliot/game/round.h:1.10.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/round.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 _ROUND_H_
+#define _ROUND_H_
+
+#include <vector>
+#include "tile.h"
+#include "coord.h"
+
+using namespace std;
+
+
+/**
+ * A Round is the representation of a played word (or going to be played).
+ * It contains the word itself, of course, but also information of position
+ * on the board, origin of letters (board for a letter already placed, rack
+ * for a letter just being played), points, etc...
+ */
+class Round
+{
+public:
+
+ /*************************
+ *
+ *************************/
+ Round();
+ virtual ~Round() {}
+ void init();
+
+ /*************************
+ *
+ *************************/
+ void addRightFromBoard(Tile);
+ void removeRightToBoard(Tile);
+ void addRightFromRack(Tile, bool);
+ void removeRightToRack(Tile, bool);
+
+ /*************************
+ * General setters
+ *************************/
+ 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 setWord(const vector<Tile> &iTiles);
+ void setFromRack(int iIndex);
+ void setFromBoard(int iIndex);
+ void setJoker(int iIndex, bool value = true);
+
+ /*************************
+ * General getters
+ *************************/
+ bool isJoker (int iIndex) const;
+ bool isPlayedFromRack(int iIndex) const;
+ const Tile& getTile (int iIndex) const;
+
+ wstring getWord() const;
+ int getWordLen() const;
+ int getPoints() const { return m_points; }
+ int getBonus() const { return m_bonus; }
+
+ /*************************
+ * Coordinates
+ *************************/
+ const Coord& getCoord() const { return m_coord; }
+ Coord& accessCoord() { return m_coord; }
+
+ wstring toString() const;
+
+private:
+ vector<Tile> m_word;
+ vector<char> m_tileOrigin;
+ Coord m_coord;
+ int m_points;
+ int m_bonus;
+};
+
+#endif
Index: eliot/game/tile.cpp
diff -u /dev/null eliot/game/tile.cpp:1.5.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/tile.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,212 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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
+};
+
+/***************************
+ ***************************/
+
+list<Tile> Tile::m_tilesList;
+const Tile Tile::m_TheJoker(TILE_JOKER);
+const Tile Tile::m_TheDummy(0);
+
+
+Tile::Tile(wchar_t c)
+{
+ if (c == TILE_JOKER)
+ {
+ m_joker = true;
+ m_dummy = false;
+ m_char = TILE_JOKER;
+ }
+ else if (isalpha(c))
+ {
+ m_joker = islower(c);
+ m_dummy = false;
+ m_char = toupper(c);
+ }
+ else
+ {
+ m_joker = false;
+ m_dummy = true;
+ m_char = 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;
+}
+
+
+wchar_t Tile::toChar() const
+{
+ if (m_dummy)
+ return TILE_DUMMY;
+ if (m_joker)
+ {
+ if (iswalpha(m_char))
+ return tolower(m_char);
+ else
+ return TILE_JOKER;
+ }
+ return m_char;
+}
+
+int Tile::toCode() const
+{
+ if (m_dummy)
+ return TILE_IDX_DUMMY;
+ if (m_joker)
+ return TILE_IDX_DUMMY;
+ return (TILE_IDX_START + m_char - TILE_START);
+}
+
+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);
+}
+
Index: eliot/game/tile.h
diff -u /dev/null eliot/game/tile.h:1.6.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/tile.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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 _TILE_H_
+#define _TILE_H_
+
+#include <list>
+
+using namespace std;
+
+/*************************
+ * A Tile is the internal representation
+ * used within the game library to
+ * handle letters
+ *************************/
+
+class Tile
+{
+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
+
+ Tile(wchar_t c = 0);
+ virtual ~Tile() {}
+
+ bool isEmpty() const { return m_dummy; }
+ bool isJoker() const { return m_joker; }
+ bool isVowel() const;
+ bool isConsonant() const;
+ unsigned int maxNumber() const;
+ unsigned int getPoints() const;
+ wchar_t toChar() const;
+ int toCode() const;
+
+ static const Tile &dummy() { return m_TheDummy; }
+ static const Tile &Joker() { return m_TheJoker; }
+ static const list<Tile>& getAllTiles();
+
+ bool operator <(const Tile &iOther) const;
+ bool operator ==(const Tile &iOther) const;
+ bool operator !=(const Tile &iOther) const;
+
+private:
+ wchar_t m_char;
+ bool m_joker;
+ bool m_dummy;
+
+ // Special tiles are declared static
+ static const Tile m_TheJoker;
+ static const Tile m_TheDummy;
+
+ // List of available tiles
+ static list<Tile> m_tilesList;
+};
+
+#endif
Index: eliot/game/training.cpp
diff -u /dev/null eliot/game/training.cpp:1.14.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/training.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,212 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 "tile.h"
+#include "rack.h"
+#include "round.h"
+#include "pldrack.h"
+#include "player.h"
+#include "training.h"
+
+#include "debug.h"
+
+
+Training::Training(const Dictionary &iDic): Game(iDic)
+{
+}
+
+
+Training::~Training()
+{
+}
+
+int Training::setRackRandom(int p, bool iCheck, set_rack_mode mode)
+{
+ int res;
+ m_results.clear();
+ do
+ {
+ res = helperSetRackRandom(p, iCheck, mode);
+ } while (res == 2);
+ // 0 : ok
+ // 1 : not enough tiles
+ // 2 : check failed (number of voyels before round 15)
+ 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
+ // 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)
+ return res;
+}
+
+int Training::setRack(set_rack_mode iMode, bool iCheck, const wstring
&iLetters)
+{
+ int res = 0;
+ switch(iMode)
+ {
+ case RACK_MANUAL:
+ res = setRackManual(iCheck, iLetters);
+ break;
+ case RACK_ALL:
+ res = setRackRandom(m_currPlayer, iCheck, iMode);
+ break;
+ case RACK_NEW:
+ res = setRackRandom(m_currPlayer, iCheck, iMode);
+ break;
+ }
+ return res;
+}
+
+int Training::play(const wstring &iCoord, const wstring &iWord)
+{
+ /* Perform all the validity checks, and fill a round */
+ Round round;
+ int res = checkPlayedWord(iCoord, iWord, round);
+ if (res != 0)
+ {
+ 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());
+
+ /* Everything is OK, we can play the word */
+ helperPlayRound(round);
+
+ /* Next turn */
+ // XXX: Should it be done by the interface instead?
+ endTurn();
+
+ return 0;
+}
+
+
+int Training::start()
+{
+ if (getNPlayers() != 0)
+ return 1;
+
+ // Training mode implicitly uses 1 human player
+ Game::addHumanPlayer();
+ m_currPlayer = 0;
+ return 0;
+}
+
+int Training::endTurn()
+{
+ // Nothing to do?
+ return 0;
+}
+
+
+void Training::search()
+{
+ // Search for the current player
+ Rack r;
+ m_players[m_currPlayer]->getCurrentRack().getRack(r);
+// debug("Training::search for %s\n",r.toString().c_str());
+ m_results.search(*m_dic, m_board, r, m_history.getSize());
+}
+
+
+int Training::playResult(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());
+
+ int res = helperPlayRound(round);
+
+ if (res == 0)
+ m_results.clear();
+
+ /* Next turn */
+ // XXX: Should it be done by the interface instead?
+ endTurn();
+
+ return res;
+}
+
+
+void Training::addHumanPlayer()
+{
+ // We are not supposed to be here...
+ ASSERT(false, "Trying to add a human player in Training mode");
+}
+
+
+void Training::addAIPlayer()
+{
+ // We are not supposed to be here...
+ ASSERT(false, "Trying to add a AI player in Training mode");
+}
+
+
+void Training::testPlay(int num)
+{
+ ASSERT(0 <= num && num < m_results.size(), "Wrong result number");
+ m_testRound = m_results.get(num);
+ m_board.testRound(m_results.get(num));
+}
+
+
+void Training::removeTestPlay()
+{
+ m_board.removeTestRound();
+ m_testRound = Round();
+}
+
+wstring Training::getTestPlayWord() const
+{
+ return m_testRound.getWord();
+}
+
+/****************************************************************/
+/****************************************************************/
+
+/// Local Variables:
+/// mode: c++
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/training.h
diff -u /dev/null eliot/game/training.h:1.13.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/training.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 _TRAINING_H_
+#define _TRAINING_H_
+
+#include <string>
+
+#include "game.h"
+#include "results.h"
+
+using std::string;
+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)
+ */
+class Training: public Game
+{
+ friend class GameFactory;
+public:
+ virtual GameMode getMode() const { return kTRAINING; }
+ virtual string getModeAsString() const { return "Training"; }
+
+ /*************************
+ * Game handling
+ *************************/
+ virtual int start();
+ virtual int play(const wstring &iCoord, const wstring &iWord);
+ virtual int endTurn();
+ void search();
+ int playResult(int);
+
+ virtual int setRackRandom(int, bool, set_rack_mode);
+ int setRackManual(bool iCheck, const wstring &iLetters);
+ int setRack(set_rack_mode iMode, bool iCheck, const wstring &iLetters);
+
+ /*************************
+ * Override the default behaviour of these methods, because in training
+ * we only want a human player
+ *************************/
+ virtual void addHumanPlayer();
+ virtual void addAIPlayer();
+
+ /*************************
+ * 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)
+ void removeTestPlay();
+ /// Get the temporary word
+ wstring getTestPlayWord() const;
+
+private:
+ // 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;
+ Results m_results;
+};
+
+#endif /* _TRAINING_H_ */
Index: eliot/game/turn.cpp
diff -u /dev/null eliot/game/turn.cpp:1.9.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/turn.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,71 @@
+/* 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 turn.cpp
+ * \brief Game turn (= id + pldrack + round)
+ * \author Antoine Fraboulet
+ * \date 2005
+ */
+
+#include <string>
+#include "pldrack.h"
+#include "round.h"
+#include "turn.h"
+
+
+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)
+{
+}
+
+#if 0
+void Turn::operator=(const Turn &iOther)
+{
+ m_num = iOther.m_num;
+ m_pldrack = iOther.m_pldrack;
+ m_round = iOther.m_round;
+}
+#endif
+
+wstring Turn::toString(bool iShowExtraSigns) const
+{
+ wstring rs = L"";
+ if (iShowExtraSigns)
+ {
+ // TODO
+ }
+ rs = rs + m_pldrack.toString() + L" " + m_round.toString();
+ return rs;
+}
+
+
+/// Local Variables:
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/game/turn.h
diff -u /dev/null eliot/game/turn.h:1.7.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/game/turn.h Wed Dec 28 16:47:35 2005
@@ -0,0 +1,67 @@
+/* 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 turn.h
+ * \brief Game turn (= id + pldrack + round)
+ * \author Antoine Fraboulet
+ * \date 2005
+ */
+
+#ifndef _TURN_H
+#define _TURN_H
+
+class Turn
+{
+public:
+ Turn();
+ Turn(int iNum, int iPlayerId,
+ const PlayedRack& iPldRack, const Round& iRound);
+ virtual ~Turn() {};
+
+ void setNum(int iNum) { m_num = iNum; }
+ void setPlayer(int iPlayerId) { m_playerId = iPlayerId; }
+ void setPlayedRack(const PlayedRack& iPldRack) { m_pldrack = iPldRack; }
+ void setRound(const Round& iRound) { m_round = iRound; }
+
+ int getNum() const { return m_num; }
+ int getPlayer() const { return m_playerId; }
+ const PlayedRack& getPlayedRack() const { return m_pldrack; }
+ const Round& getRound() const { return m_round; }
+
+#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;
+
+};
+
+#endif
+
+
+/// Local Variables:
+/// mode: hs-minor
+/// c-basic-offset: 4
+/// End:
Index: eliot/utils/eliottxt.cpp
diff -u /dev/null eliot/utils/eliottxt.cpp:1.12.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/utils/eliottxt.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,894 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <locale.h>
+#include <wctype.h>
+#include <wchar.h>
+#include <fstream>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "dic.h"
+#include "dic_search.h"
+#include "game_io.h"
+#include "game_factory.h"
+#include "training.h"
+#include "duplicate.h"
+#include "freegame.h"
+#include "encoding.h"
+
+
+/* A static variable for holding the line. */
+static char *line_read = NULL;
+/* Wide version of the line */
+static wchar_t *wline_read = NULL;
+
+/**
+ * Read a string, and return a pointer to it.
+ * Returns NULL on EOF.
+ */
+wchar_t *rl_gets()
+{
+ // If the buffer has already been allocated, return the memory to the free
+ // pool
+ if (line_read)
+ {
+ free(line_read);
+ line_read = NULL;
+ }
+ if (wline_read)
+ {
+ free(wline_read);
+ wline_read = NULL;
+ }
+
+ // Get a line from the user
+ line_read = readline("commande> ");
+
+ // If the line has any text in it, save it on the history
+ if (line_read && *line_read)
+ add_history(line_read);
+
+ // Convert the line into wide characters
+ // Get the needed length (we _can't_ use string::size())
+ size_t len = mbstowcs(NULL, line_read, 0);
+ if (len == (size_t)-1)
+ return NULL;
+
+ wline_read = new wchar_t[len + 1];
+ len = mbstowcs(wline_read, line_read, len + 1);
+
+ return wline_read;
+}
+
+
+wchar_t * next_token_alpha(wchar_t *cmd, const wchar_t *delim, wchar_t **state)
+{
+ wchar_t *token = wcstok(cmd, delim, state);
+ if (token == NULL)
+ return NULL;
+ int i;
+ for (i = 0; token[i] && iswalpha(token[i]); i++)
+ ;
+ token[i] = L'\0';
+ return token;
+}
+
+
+wchar_t * next_token_alphanum(wchar_t *cmd, const wchar_t *delim, wchar_t
**state)
+{
+ wchar_t *token = wcstok(cmd, delim, state);
+ if (token == NULL)
+ return NULL;
+ int i;
+ for (i = 0; token[i] && iswalnum(token[i]); i++)
+ ;
+ token[i] = L'\0';
+ return token;
+}
+
+
+wchar_t * next_token_alphaplusjoker(wchar_t *cmd, const wchar_t *delim,
wchar_t **state)
+{
+ wchar_t *token = wcstok(cmd, delim, state);
+ if (token == NULL)
+ return NULL;
+ int i;
+ for (i = 0; token[i] && (iswalpha(token[i]) ||
+ token[i] == L'?' ||
+ token[i] == L'+');
+ i++)
+ ;
+ token[i] = L'\0';
+ return token;
+}
+
+
+wchar_t * next_token_digit(wchar_t *cmd, const wchar_t *delim, wchar_t **state)
+{
+ wchar_t *token = wcstok(cmd, delim, state);
+ if (token == NULL)
+ return NULL;
+ int i;
+ for (i = 0; token[i] && (iswdigit(token[i]) || token[i] == L'-'); i++)
+ ;
+ token[i] = L'\0';
+ return token;
+}
+
+
+wchar_t * next_token_cross(wchar_t *cmd, const wchar_t *delim, wchar_t **state)
+{
+ wchar_t *token = wcstok(cmd, delim, state);
+ if (token == NULL)
+ return NULL;
+ int i;
+ for (i = 0; token[i] &&
+ (iswalpha(token[i]) || token[i] == L'.');
+ i++)
+ ;
+ token[i] = L'\0';
+ return token;
+}
+
+
+wchar_t * next_token_filename(wchar_t *cmd, const wchar_t *delim, wchar_t
**state)
+{
+ wchar_t *token = wcstok(cmd, delim, state);
+ if (token == NULL)
+ return NULL;
+ int i;
+ for (i = 0; token[i] && (iswalnum(token[i]) ||
+ token[i] == L'.' ||
+ token[i] == L'_'); i++)
+ ;
+ token[i] = L'\0';
+ return token;
+}
+
+
+void eliottxt_get_cross(const Dictionary &iDic, wchar_t *cros)
+{
+ // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
+#if 0
+ wchar_t wordlist[RES_CROS_MAX][DIC_WORD_MAX];
+ Dic_search_Cros(iDic, cros, wordlist);
+ for (int i = 0; i < RES_CROS_MAX && wordlist[i][0]; i++)
+ {
+ printf(" %s\n", convertToMb(wordlist[i]).c_str());
+ }
+#endif
+}
+
+
+void help_training()
+{
+ printf(" ? : aide -- cette page\n");
+ printf(" a [g|l|p|r|t] : afficher :\n");
+ printf(" g -- grille\n");
+ printf(" gj -- grille + jokers\n");
+ printf(" gm -- grille + valeur des cases\n");
+ printf(" gn -- grille + valeur des cases (variante)\n");
+ printf(" l -- lettres non jouées\n");
+ printf(" p -- partie\n");
+ printf(" r -- recherche\n");
+ printf(" s -- score\n");
+ printf(" S -- score de tous les joueurs\n");
+ printf(" t -- tirage\n");
+ printf(" d [] : vérifier le mot []\n");
+ printf(" * : tirage aléatoire\n");
+ printf(" + : tirage aléatoire ajouts\n");
+ printf(" t [] : changer le tirage\n");
+ printf(" j [] {} : jouer le mot [] aux coordonnées {}\n");
+ printf(" n [] : jouer le résultat numéro []\n");
+ printf(" r : rechercher les meilleurs résultats\n");
+ printf(" s [] : sauver la partie en cours dans le fichier []\n");
+ printf(" q : quitter le mode entraînement\n");
+}
+
+
+void help_freegame()
+{
+ printf(" ? : aide -- cette page\n");
+ printf(" a [g|l|p|s|t] : afficher :\n");
+ printf(" g -- grille\n");
+ printf(" gj -- grille + jokers\n");
+ printf(" gm -- grille + valeur des cases\n");
+ printf(" gn -- grille + valeur des cases (variante)\n");
+ printf(" j -- joueur courant\n");
+ printf(" l -- lettres non jouées\n");
+ printf(" p -- partie\n");
+ printf(" s -- score\n");
+ printf(" S -- score de tous les joueurs\n");
+ printf(" t -- tirage\n");
+ printf(" T -- tirage de tous les joueurs\n");
+ printf(" d [] : vérifier le mot []\n");
+ printf(" j [] {} : jouer le mot [] aux coordonnées {}\n");
+ printf(" p [] : passer son tour en changeant les lettres []\n");
+ printf(" s [] : sauver la partie en cours dans le fichier []\n");
+ printf(" c [] : charger la partie du fichier []\n");
+ printf(" q : quitter le mode partie libre\n");
+}
+
+
+void help_duplicate()
+{
+ printf(" ? : aide -- cette page\n");
+ printf(" a [g|l|p|s|t] : afficher :\n");
+ printf(" g -- grille\n");
+ printf(" gj -- grille + jokers\n");
+ printf(" gm -- grille + valeur des cases\n");
+ printf(" gn -- grille + valeur des cases (variante)\n");
+ printf(" j -- joueur courant\n");
+ printf(" l -- lettres non jouées\n");
+ printf(" p -- partie\n");
+ printf(" s -- score\n");
+ printf(" S -- score de tous les joueurs\n");
+ printf(" t -- tirage\n");
+ printf(" d [] : vérifier le mot []\n");
+ printf(" j [] {} : jouer le mot [] aux coordonnées {}\n");
+ printf(" n [] : passer au joueur n°[]\n");
+ printf(" s [] : sauver la partie en cours dans le fichier []\n");
+ printf(" c [] : charger la partie du fichier []\n");
+ printf(" q : quitter le mode duplicate\n");
+}
+
+
+void help()
+{
+ printf(" ? : aide -- cette page\n");
+ printf(" e : démarrer le mode entraînement\n");
+ printf(" d [] {} : démarrer une partie duplicate avec\n");
+ printf(" [] joueurs humains et {} joueurs IA\n");
+ printf(" l [] {} : démarrer une partie libre avec\n");
+ printf(" [] joueurs humains et {} joueurs IA\n");
+ printf(" D : raccourci pour d 1 1\n");
+ printf(" L : raccourci pour l 1 1\n");
+ printf(" q : quitter\n");
+}
+
+
+void display_data(const Game &iGame, const wchar_t *delim, wchar_t **state)
+{
+ wchar_t *token;
+
+ token = next_token_alpha(NULL, delim, state);
+ if (token == NULL)
+ {
+ cout << "commande incomplète\n";
+ return;
+ }
+ switch (token[0])
+ {
+ case L'g':
+ switch (token[1])
+ {
+ case L'\0':
+ GameIO::printBoard(cout, iGame);
+ break;
+ case L'j':
+ GameIO::printBoardJoker(cout, iGame);
+ break;
+ case L'm':
+ GameIO::printBoardMultipliers(cout, iGame);
+ break;
+ case L'n':
+ GameIO::printBoardMultipliers2(cout, iGame);
+ break;
+ default:
+ printf("commande inconnue\n");
+ break;
+ }
+ break;
+ case L'j':
+ cout << "Joueur " << iGame.currPlayer() << endl;
+ break;
+ case L'l':
+ GameIO::printNonPlayed(cout, iGame);
+ break;
+ case L'p':
+ iGame.save(cout);
+ break;
+ case L'r':
+ token = next_token_digit(NULL, delim, state);
+ if (token == NULL)
+ GameIO::printSearchResults(cout,
+ static_cast<const Training&>(iGame),
+ 10);
+ else
+ GameIO::printSearchResults(cout,
+ static_cast<const Training&>(iGame),
+ _wtoi(token));
+ break;
+ case L's':
+ GameIO::printPoints(cout, iGame);
+ break;
+ case L'S':
+ GameIO::printAllPoints(cout, iGame);
+ break;
+ case L't':
+ GameIO::printPlayedRack(cout, iGame, iGame.getHistory().getSize());
+ break;
+ case L'T':
+ GameIO::printAllRacks(cout, iGame);
+ break;
+ default:
+ cout << "commande inconnue\n";
+ break;
+ }
+}
+
+
+void loop_training(Training &iGame)
+{
+ wchar_t *token;
+ wchar_t *state;
+ wchar_t *commande = NULL;
+ wchar_t delim[] = L" \t";
+ int quit = 0;
+
+ cout << "mode entraînement\n";
+ cout << "[?] pour l'aide\n";
+ while (quit == 0)
+ {
+ commande = rl_gets();
+ token = wcstok(commande, delim, &state);
+ if (token)
+ {
+ switch (token[0])
+ {
+ case L'?':
+ help_training();
+ break;
+ case L'a':
+ display_data(iGame, delim, &state);
+ break;
+ case L'd':
+ token = next_token_alpha(NULL, delim, &state);
+ if (token == NULL)
+ help_training();
+ else
+ {
+ if (Dic_search_word(iGame.getDic(), token))
+ {
+ printf("le mot -%s- existe\n",
+ convertToMb(token).c_str());
+ }
+ else
+ {
+ printf("le mot -%s- n'existe pas\n",
+ convertToMb(token).c_str());
+ }
+ }
+ break;
+ case L'j':
+ token = next_token_alpha(NULL, delim, &state);
+ if (token == NULL)
+ help_training();
+ else
+ {
+ int res;
+ wchar_t *coord = next_token_alphanum(NULL, delim,
&state);
+ if (coord == NULL)
+ {
+ help_training();
+ break;
+ }
+ if ((res = iGame.play(coord, token)) != 0)
+ {
+ fprintf(stderr, "Mot incorrect ou mal placé
(%i)\n",
+ res);
+ break;
+ }
+ }
+ break;
+ case L'n':
+ token = next_token_digit(NULL, delim, &state);
+ if (token == NULL)
+ help_training();
+ else
+ {
+ int n = _wtoi(token);
+ if (n <= 0)
+ iGame.back(n == 0 ? 1 : -n);
+ else
+ {
+ if (iGame.playResult(--n))
+ printf("mauvais argument\n");
+ }
+ }
+ break;
+ case L'r':
+ iGame.search();
+ break;
+ case L't':
+ token = next_token_alphaplusjoker(NULL, delim, &state);
+ if (token == NULL)
+ help_training();
+ else
+ if (iGame.setRackManual(0, token))
+ printf("le sac ne contient pas assez de
lettres\n");
+ break;
+ case L'x':
+ token = next_token_cross(NULL, delim, &state);
+ if (token == NULL)
+ help_training();
+ else
+ eliottxt_get_cross(iGame.getDic(), token);
+ break;
+ case L'*':
+ iGame.setRackRandom(0, false, Game::RACK_ALL);
+ break;
+ case L'+':
+ iGame.setRackRandom(0, false, Game::RACK_NEW);
+ break;
+ case L's':
+ token = next_token_filename(NULL, delim, &state);
+ if (token != NULL)
+ {
+ string filename = convertToMb(token);
+ ofstream fout(filename.c_str());
+ if (fout.rdstate() == ios::failbit)
+ {
+ printf("impossible d'ouvrir %s\n",
+ filename.c_str());
+ break;
+ }
+ iGame.save(fout);
+ fout.close();
+ }
+ break;
+ case L'q':
+ quit = 1;
+ break;
+ default:
+ printf("commande inconnue\n");
+ break;
+ }
+ }
+ }
+ printf("fin du mode entraînement\n");
+}
+
+
+void loop_freegame(FreeGame &iGame)
+{
+ wchar_t *token;
+ wchar_t *state;
+ wchar_t *commande = NULL;
+ wchar_t delim[] = L" \t";
+ int quit = 0;
+
+ printf("mode partie libre\n");
+ printf("[?] pour l'aide\n");
+ while (quit == 0)
+ {
+ commande = rl_gets();
+ token = wcstok(commande, delim, &state);
+ if (token)
+ {
+ switch (token[0])
+ {
+ case L'?':
+ help_freegame();
+ break;
+ case L'a':
+ display_data(iGame, delim, &state);
+ break;
+ case L'd':
+ token = next_token_alpha(NULL, delim, &state);
+ if (token == NULL)
+ help_freegame();
+ else
+ {
+ if (Dic_search_word(iGame.getDic(), token))
+ {
+ printf("le mot -%s- existe\n",
+ convertToMb(token).c_str());
+ }
+ else
+ {
+ printf("le mot -%s- n'existe pas\n",
+ convertToMb(token).c_str());
+ }
+ }
+ break;
+ case L'j':
+ token = next_token_alpha(NULL, delim, &state);
+ if (token == NULL)
+ help_freegame();
+ else
+ {
+ int res;
+ wchar_t *coord = next_token_alphanum(NULL, delim,
&state);
+ if (coord == NULL)
+ {
+ help_freegame();
+ break;
+ }
+ if ((res = iGame.play(coord, token)) != 0)
+ {
+ fprintf(stderr, "Mot incorrect ou mal placé
(%i)\n",
+ res);
+ break;
+ }
+ }
+ break;
+ case L'p':
+ token = next_token_alpha(NULL, delim, &state);
+ /* You can pass your turn without changing any letter */
+ if (token == NULL)
+ token = L"";
+
+ if (iGame.pass(token, iGame.currPlayer()) != 0)
+ break;
+ break;
+ case L's':
+ token = next_token_filename(NULL, delim, &state);
+ if (token != NULL)
+ {
+ string filename = convertToMb(token);
+ ofstream fout(filename.c_str());
+ if (fout.rdstate() == ios::failbit)
+ {
+ printf("impossible d'ouvrir %s\n",
+ filename.c_str());
+ break;
+ }
+ iGame.save(fout);
+ fout.close();
+ }
+ break;
+ case L'q':
+ quit = 1;
+ break;
+ default:
+ printf("commande inconnue\n");
+ break;
+ }
+ }
+ }
+ printf("fin du mode partie libre\n");
+}
+
+
+void loop_duplicate(Duplicate &iGame)
+{
+ wchar_t *token;
+ wchar_t *state;
+ wchar_t *commande = NULL;
+ wchar_t delim[] = L" \t";
+ int quit = 0;
+
+ printf("mode duplicate\n");
+ printf("[?] pour l'aide\n");
+ while (quit == 0)
+ {
+ commande = rl_gets();
+ token = wcstok(commande, delim, &state);
+ if (token)
+ {
+ switch (token[0])
+ {
+ case L'?':
+ help_duplicate();
+ break;
+ case L'a':
+ display_data(iGame, delim, &state);
+ break;
+ case L'd':
+ token = next_token_alpha(NULL, delim, &state);
+ if (token == NULL)
+ help_duplicate();
+ else
+ {
+ if (Dic_search_word(iGame.getDic(), token))
+ {
+ printf("le mot -%s- existe\n",
+ convertToMb(token).c_str());
+ }
+ else
+ {
+ printf("le mot -%s- n'existe pas\n",
+ convertToMb(token).c_str());
+ }
+ }
+ break;
+ case L'j':
+ token = next_token_alpha(NULL, delim, &state);
+ if (token == NULL)
+ help_duplicate();
+ else
+ {
+ int res;
+ wchar_t *coord = next_token_alphanum(NULL, delim,
&state);
+ if (coord == NULL)
+ {
+ help_duplicate();
+ break;
+ }
+ if ((res = iGame.play(coord, token)) != 0)
+ {
+ fprintf(stderr, "Mot incorrect ou mal placé
(%i)\n",
+ res);
+ break;
+ }
+ }
+ break;
+ case L'n':
+ token = next_token_digit(NULL, delim, &state);
+ if (token == NULL)
+ help_duplicate();
+ else
+ {
+ int res = iGame.setPlayer(_wtoi(token));
+ if (res == 1)
+ fprintf(stderr, "Numéro de joueur invalide\n");
+ else if (res == 2)
+ fprintf(stderr, "Impossible de choisir un joueur
non humain\n");
+ }
+ break;
+ case L's':
+ token = next_token_filename(NULL, delim, &state);
+ if (token != NULL)
+ {
+ string filename = convertToMb(token);
+ ofstream fout(filename.c_str());
+ if (fout.rdstate() == ios::failbit)
+ {
+ printf("impossible d'ouvrir %s\n",
+ filename.c_str());
+ break;
+ }
+ iGame.save(fout);
+ fout.close();
+ }
+ break;
+ case L'q':
+ quit = 1;
+ break;
+ default:
+ printf("commande inconnue\n");
+ break;
+ }
+ }
+ }
+ printf("fin du mode duplicate\n");
+}
+
+
+void main_loop(const Dictionary &iDic)
+{
+ wchar_t *token;
+ wchar_t *state;
+ wchar_t *commande = NULL;
+ wchar_t delim[] = L" \t";
+ int quit = 0;
+
+ printf("[?] pour l'aide\n");
+ while (quit == 0)
+ {
+ commande = rl_gets();
+ token = wcstok(commande, delim, &state);
+ if (token)
+ {
+ switch (token[0])
+ {
+ case L'?':
+ help();
+ break;
+ case L'c':
+ token = next_token_filename(NULL, delim, &state);
+ if (token == NULL)
+ {}
+ else
+ {
+ string filename = convertToMb(token);
+ fprintf(stderr, "chargement de -%s-\n",
+ filename.c_str());
+ FILE* fin;
+ if ((fin = fopen(filename.c_str(), "r")) == NULL)
+ {
+ printf("impossible d'ouvrir %s\n",
+ filename.c_str());
+ break;
+ }
+ Game *game = Game::load(fin, iDic);
+ fclose(fin);
+ if (game == NULL)
+ {
+ fprintf(stderr, "erreur pendant le chargement\n");
+ }
+ else
+ {
+ if (game->getMode() == Game::kTRAINING)
+ loop_training((Training&)*game);
+ else if (game->getMode() == Game::kFREEGAME)
+ loop_freegame((FreeGame&)*game);
+ else
+ loop_duplicate((Duplicate&)*game);
+ }
+ }
+ break;
+ case L'e':
+ {
+ // New training game
+ Training *game =
GameFactory::Instance()->createTraining(iDic);
+ game->start();
+ loop_training(*game);
+ GameFactory::Instance()->releaseGame(*game);
+ break;
+ }
+ case L'd':
+ {
+ int i;
+ // New duplicate game
+ token = next_token_digit(NULL, delim, &state);
+ if (token == NULL)
+ {
+ help();
+ break;
+ }
+ Duplicate *game =
GameFactory::Instance()->createDuplicate(iDic);
+ for (i = 0; i < _wtoi(token); i++)
+ game->addHumanPlayer();
+ token = next_token_digit(NULL, delim, &state);
+ if (token == NULL)
+ {
+ help();
+ break;
+ }
+ for (i = 0; i < _wtoi(token); i++)
+ game->addAIPlayer();
+ game->start();
+ loop_duplicate(*game);
+ GameFactory::Instance()->releaseGame(*game);
+ break;
+ }
+ case L'l':
+ {
+ int i;
+ // New free game
+ token = next_token_digit(NULL, delim, &state);
+ if (token == NULL)
+ {
+ help();
+ break;
+ }
+ FreeGame *game =
GameFactory::Instance()->createFreeGame(iDic);
+ for (i = 0; i < _wtoi(token); i++)
+ game->addHumanPlayer();
+ token = next_token_digit(NULL, delim, &state);
+ if (token == NULL)
+ {
+ help();
+ break;
+ }
+ for (i = 0; i < _wtoi(token); i++)
+ game->addAIPlayer();
+ game->start();
+ loop_freegame(*game);
+ GameFactory::Instance()->releaseGame(*game);
+ break;
+ }
+ case L'D':
+ {
+ // New duplicate game
+ Duplicate *game =
GameFactory::Instance()->createDuplicate(iDic);
+ game->addHumanPlayer();
+ game->addAIPlayer();
+ game->start();
+ loop_duplicate(*game);
+ GameFactory::Instance()->releaseGame(*game);
+ break;
+ }
+ case L'L':
+ {
+ // New free game
+ FreeGame *game =
GameFactory::Instance()->createFreeGame(iDic);
+ game->addHumanPlayer();
+ game->addAIPlayer();
+ game->start();
+ loop_freegame(*game);
+ GameFactory::Instance()->releaseGame(*game);
+ break;
+ }
+ case L'q':
+ quit = 1;
+ break;
+ default:
+ printf("commande inconnue\n");
+ break;
+ }
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ char dic_path[100];
+
+ // Let the user choose the locale
+ setlocale(LC_ALL, "");
+
+ Dictionary dic = NULL;
+
+ if (argc != 2 && argc != 3)
+ {
+ fprintf(stdout, "Usage: eliot /chemin/vers/ods4.dawg [random_seed]\n");
+ exit(1);
+ }
+ else
+ strcpy(dic_path, argv[1]);
+
+ switch (Dic_load(&dic, dic_path))
+ {
+ case 0:
+ /* Normal case */
+ break;
+ case 1:
+ printf("chargement: problème d'ouverture de %s\n", argv[1]);
+ exit(1);
+ break;
+ case 2:
+ printf("chargement: mauvais en-tete de dictionnaire\n");
+ exit(2);
+ break;
+ case 3:
+ printf("chargement: problème 3 d'allocation mémoire\n");
+ exit(3);
+ break;
+ case 4:
+ printf("chargement: problème 4 d'alocation mémoire\n");
+ exit(4);
+ break;
+ case 5:
+ printf("chargement: problème de lecture des arcs du
dictionnaire\n");
+ exit(5);
+ break;
+ default:
+ printf("chargement: problème non-repertorié\n");
+ exit(6);
+ break;
+ }
+
+ if (argc == 3)
+ srand(atoi(argv[2]));
+ else
+ srand(time(NULL));
+
+ main_loop(dic);
+ GameFactory::Destroy();
+
+ Dic_destroy(dic);
+
+ // Free the readline static variable and its wide equivalent
+ if (line_read)
+ free(line_read);
+ if (wline_read)
+ free(wline_read);
+
+ return 0;
+}
Index: eliot/utils/game_io.cpp
diff -u /dev/null eliot/utils/game_io.cpp:1.7.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/utils/game_io.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,223 @@
+/*****************************************************************************
+ * Copyright (C) 1999-2005 Eliot
+ * Authors: Antoine Fraboulet <address@hidden>
+ * Olivier Teuliere <address@hidden>
+ *
+ * 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 <iomanip>
+#include <string>
+#include "stdlib.h"
+
+#include "game_io.h"
+#include "game.h"
+#include "training.h"
+#include "player.h"
+#include "encoding.h"
+
+using namespace std;
+
+
+void GameIO::printBoard(ostream &out, const Game &iGame)
+{
+ int row, col;
+
+ out << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ out << setw(3) << col - BOARD_MIN + 1;
+ out << endl;
+ for (row = BOARD_MIN; row <= BOARD_MAX; row++)
+ {
+ out << " " << (char)(row - BOARD_MIN + 'A') << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ {
+ char l = iGame.getBoard().getChar(row, col);
+ out << setw(3) << (l ? l : '-');
+ }
+ out << endl;
+ }
+}
+
+
+void GameIO::printBoardJoker(ostream &out, const Game &iGame)
+{
+ int row,col;
+
+ out << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ out << setw(3) << col - BOARD_MIN + 1;
+ out << endl;
+
+ for (row = BOARD_MIN; row <= BOARD_MAX; row++)
+ {
+ out << " " << (char)(row - BOARD_MIN + 'A') << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ {
+ char l = iGame.getBoard().getChar(row, col);
+ bool j = (iGame.getBoard().getCharAttr(row, col) & ATTR_JOKER);
+
+ out << " " << (j ? '.' : (l ? ' ' : '-'));
+ out << (l ? l : '-');
+ }
+ out << endl;
+ }
+}
+
+
+void GameIO::printBoardMultipliers(ostream &out, const Game &iGame)
+{
+ int row, col;
+
+ out << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ out << setw(3) << col - BOARD_MIN + 1;
+ out << endl;
+
+ for (row = BOARD_MIN; row <= BOARD_MAX; row++)
+ {
+ out << " " << (char)(row - BOARD_MIN + 'A') << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ {
+ char l = iGame.getBoard().getChar(row, col);
+ if (l != 0)
+ out << " " << l;
+ else
+ {
+ int wm = iGame.getBoard().getWordMultiplier(row, col);
+ int tm = iGame.getBoard().getLetterMultiplier(row, col);
+
+ if (wm > 1)
+ out << " " << ((wm == 3) ? '@' : '#');
+ else if (tm > 1)
+ out << " " << ((tm == 3) ? '*' : '+');
+ else
+ out << " -";
+ }
+ }
+ out << endl;
+ }
+}
+
+
+void GameIO::printBoardMultipliers2(ostream &out, const Game &iGame)
+{
+ int row, col;
+
+ out << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ out << setw(3) << col - BOARD_MIN + 1;
+ out << endl;
+
+ for (row = BOARD_MIN; row <= BOARD_MAX; row++)
+ {
+ out << " " << (char)(row - BOARD_MIN + 'A') << " ";
+ for (col = BOARD_MIN; col <= BOARD_MAX; col++)
+ {
+ wchar_t l = iGame.getBoard().getChar(row, col);
+ int wm = iGame.getBoard().getWordMultiplier(row, col);
+ int tm = iGame.getBoard().getLetterMultiplier(row, col);
+
+ if (wm > 1)
+ out << " " << ((wm == 3) ? '@' : '#');
+ else if (tm > 1)
+ out << " " << ((tm == 3) ? '*' : '+');
+ else
+ out << " -";
+ out << (l ? convertToMb(l) : "-");
+ }
+ out << endl;
+ }
+}
+
+
+void GameIO::printNonPlayed(ostream &out, const Game &iGame)
+{
+ const list<Tile>& allTiles = Tile::getAllTiles();
+ list<Tile>::const_iterator it;
+
+ for (it = allTiles.begin(); it != allTiles.end(); it++)
+ {
+ if (iGame.getBag().in(it->toChar()) > 9)
+ out << " ";
+ out << setw(2) << convertToMb(it->toChar());
+ }
+ out << endl;
+
+ for (it = allTiles.begin(); it != allTiles.end(); it++)
+ {
+ out << " " << iGame.getBag().in(it->toChar());
+ }
+ out << endl;
+}
+
+
+void GameIO::printPlayedRack(ostream &out, const Game &iGame, int n)
+{
+ out <<
convertToMb(iGame.getCurrentPlayer().getCurrentRack().toString(false)) << endl;
+}
+
+
+void GameIO::printAllRacks(ostream &out, const Game &iGame)
+{
+ for (int j = 0; j < iGame.getNPlayers(); j++)
+ {
+ out << "Joueur " << j << ": ";
+ out <<
convertToMb(iGame.getPlayer(j).getCurrentRack().toString(false)) << endl;
+ }
+}
+
+
+static void searchResultLine(ostream &out, const Training &iGame, int num)
+{
+ const Results &res = iGame.getResults();
+ Round r = res.get(num);
+ wstring word = r.getWord();
+ if (word.size() == 0)
+ return;
+ out << convertToMb(word) << string(16 - word.size(), ' ')
+ << (r.getBonus() ? '*' : ' ')
+ << setw(4) << r.getPoints()
+ << ' ' << convertToMb(r.getCoord().toString());
+}
+
+
+void GameIO::printSearchResults(ostream &out, const Training &iGame, int num)
+{
+ int size = (int) iGame.getResults().size();
+ for (int i = 0; i < num && i < size; i++)
+ {
+ out << setw(3) << i + 1 << ": ";
+ searchResultLine(out, iGame, i);
+ out << endl;
+ }
+}
+
+
+void GameIO::printPoints(ostream &out, const Game &iGame)
+{
+ out << iGame.getPlayer(0).getPoints() << endl;
+}
+
+
+void GameIO::printAllPoints(ostream &out, const Game &iGame)
+{
+ for (int i = 0; i < iGame.getNPlayers(); i++)
+ {
+ out << "Joueur " << i << ": "
+ << setw(4) << iGame.getPlayer(i).getPoints() << endl;
+ }
+}
+
Index: eliot/utils/ncurses.cpp
diff -u /dev/null eliot/utils/ncurses.cpp:1.19.2.1
--- /dev/null Wed Dec 28 16:47:36 2005
+++ eliot/utils/ncurses.cpp Wed Dec 28 16:47:35 2005
@@ -0,0 +1,892 @@
+/*****************************************************************************
+ * Copyright (C) 2005 Eliot
+ * Authors: Olivier Teuliere <address@hidden>
+ *
+ * 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"
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) gettext(String)
+#else
+# define _(String) String
+#endif
+
+#include <ctype.h>
+#include <fstream>
+
+#include "ncurses.h"
+#include "dic.h"
+#include "dic_search.h"
+#include "game_factory.h"
+#include "training.h"
+#include "duplicate.h"
+#include "freegame.h"
+#include "player.h"
+#include "history.h"
+#include "turn.h"
+#include "encoding.h"
+
+using namespace std;
+
+
+CursesIntf::CursesIntf(WINDOW *win, Game& iGame)
+ : m_win(win), m_game(&iGame), m_state(DEFAULT), m_dying(false),
+ m_boxStart(0), m_boxLines(0), m_boxLinesData(0), m_boxY(0),
+ m_showDots(false)
+{
+}
+
+
+CursesIntf::~CursesIntf()
+{
+ GameFactory::Instance()->releaseGame(*m_game);
+}
+
+
+void CursesIntf::drawBox(WINDOW *win, int y, int x, int h, int w,
+ const string& iTitle)
+{
+ if (w > 3 && h > 2)
+ {
+ int i_len = iTitle.size();
+
+ if (i_len > w - 2) i_len = w - 2;
+
+ mvwaddch(win, y, x, ACS_ULCORNER);
+ mvwhline(win, y, x+1, ACS_HLINE, ( w-i_len-2)/2);
+ mvwprintw(win,y, x+1+(w-i_len-2)/2, "%s", iTitle.c_str());
+ mvwhline(win, y, x+(w-i_len)/2+i_len,
+ ACS_HLINE, w - 1 - ((w-i_len)/2+i_len));
+ mvwaddch(win, y, x+w-1,ACS_URCORNER);
+
+ mvwvline(win, y+1, x, ACS_VLINE, h-2);
+ mvwvline(win, y+1, x+w-1, ACS_VLINE, h-2);
+
+ mvwaddch(win, y+h-1, x, ACS_LLCORNER);
+ mvwhline(win, y+h-1, x+1, ACS_HLINE, w - 2);
+ mvwaddch(win, y+h-1, x+w-1, ACS_LRCORNER);
+ }
+}
+
+
+void CursesIntf::clearRect(WINDOW *win, int y, int x, int h, int w)
+{
+ for (int i = 0; i < h; i++)
+ {
+ mvwhline(win, y + i, x, ' ', w);
+ }
+}
+
+
+void CursesIntf::boxPrint(WINDOW *win, int y, int x, const char *fmt, ...)
+{
+ if (y < m_boxStart || y - m_boxStart >= m_boxLines)
+ return;
+
+ va_list vl_args;
+ char *buf = NULL;
+ va_start(vl_args, fmt);
+ vasprintf(&buf, fmt, vl_args);
+ va_end(vl_args);
+
+ if (buf == NULL)
+ {
+ return;
+ }
+ mvwprintw(win, m_boxY + y - m_boxStart, x, "%s", buf);
+}
+
+
+void CursesIntf::drawStatus(WINDOW *win, int y, int x,
+ const string& iMessage, bool error)
+{
+ if (error)
+ wattron(win, COLOR_PAIR(COLOR_YELLOW));
+ mvwprintw(win, y, x, iMessage.c_str());
+ whline(win, ' ', COLS - x - 1 - iMessage.size());
+ if (error)
+ wattron(win, COLOR_PAIR(COLOR_WHITE));
+}
+
+
+void CursesIntf::drawBoard(WINDOW *win, int y, int x) const
+{
+ // Box around the board
+ drawBox(win, y + 1, x + 3, 17, 47, "");
+
+ // Print the coordinates
+ for (int i = 0; i < 15; i++)
+ {
+ mvwaddch(win, y + i + 2, x + 1, 'A' + i);
+ mvwaddch(win, y + i + 2, x + 51, 'A' + i);
+ mvwprintw(win, y, x + 3 * i + 5, "%d", i + 1);
+ mvwprintw(win, y + 18, x + 3 * i + 5, "%d", i + 1);
+ }
+
+ // The board itself
+ for (int row = 1; row < 16; row++)
+ {
+ for (int col = 1; col < 16; col++)
+ {
+ // Handle colors
+ int wm = m_game->getBoard().getWordMultiplier(row, col);
+ int lm = m_game->getBoard().getLetterMultiplier(row, col);
+ if (wm == 3)
+ wattron(win, COLOR_PAIR(COLOR_RED));
+ else if (wm == 2)
+ wattron(win, COLOR_PAIR(COLOR_MAGENTA));
+ else if (lm == 3)
+ wattron(win, COLOR_PAIR(COLOR_BLUE));
+ else if (lm == 2)
+ wattron(win, COLOR_PAIR(COLOR_CYAN));
+ else
+ wattron(win, COLOR_PAIR(COLOR_WHITE));
+
+ // Empty square
+ mvwprintw(win, y + row + 1, x + 3 * col + 1, " ");
+
+ // Now add the letter
+ char c = m_game->getBoard().getChar(row, col);
+ if (c)
+ {
+ if (islower(c))
+ mvwaddch(win, y + row + 1, x + 3 * col + 2,
+ c | A_BOLD | COLOR_PAIR(COLOR_GREEN));
+ else
+ mvwaddch(win, y + row + 1, x + 3 * col + 2, c);
+ }
+ else
+ {
+ // Empty square... should we display a dot?
+ if (m_showDots)
+ mvwaddch(win, y + row + 1, x + 3 * col + 2, '.');
+ }
+ }
+ }
+ wattron(win, COLOR_PAIR(COLOR_WHITE));
+}
+
+
+void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const
+{
+ drawBox(win, y, x, m_game->getNPlayers() + 2, 25, _(" Scores "));
+ for (int i = 0; i < m_game->getNPlayers(); i++)
+ {
+ if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
+ attron(A_BOLD);
+ mvwprintw(win, y + i + 1, x + 2,
+ _("Player %d: %d"), i, m_game->getPlayer(i).getPoints());
+ if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
+ attroff(A_BOLD);
+ }
+
+ // Distance between the 2 boxes
+ int yOff = m_game->getNPlayers() + 3;
+
+ drawBox(win, y + yOff, x, m_game->getNPlayers() + 2, 25, _(" Racks "));
+ for (int i = 0; i < m_game->getNPlayers(); i++)
+ {
+ if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
+ attron(A_BOLD);
+ string rack =
convertToMb(m_game->getPlayer(i).getCurrentRack().toString(false));
+ mvwprintw(win, y + yOff + i + 1, x + 2,
+ _("Player %d: %s"), i, rack.c_str());
+ if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
+ attroff(A_BOLD);
+ // Force to refresh the whole rack
+ whline(win, ' ', 7 - rack.size());
+ }
+
+ // Display a message when the search is complete
+ if (m_game->getMode() == Game::kTRAINING &&
+ static_cast<Training*>(m_game)->getHistory().getSize())
+ {
+ mvwprintw(win, y + 2*yOff - 1, x + 2, _("Search complete"));
+ }
+ else
+ mvwhline(win, y + 2*yOff - 1, x + 2, ' ', strlen(_("Search
complete")));
+}
+
+
+void CursesIntf::drawResults(WINDOW *win, int y, int x)
+{
+ if (m_game->getMode() != Game::kTRAINING)
+ return;
+ Training *tr_game = static_cast<Training*>(m_game);
+
+ int h = 17;
+ drawBox(win, y, x, h, 25, _(" Search results "));
+ m_boxY = y + 1;
+ m_boxLines = h - 2;
+ m_boxLinesData = tr_game->getHistory().getSize();
+
+ int i;
+ const Results& res = tr_game->getResults();
+ for (i = m_boxStart; i < res.size() &&
+ i < m_boxStart + m_boxLines; i++)
+ {
+ const Round &r = res.get(i);
+ wstring coord = r.getCoord().toString();
+ boxPrint(win, i, x + 1, "%3d %s%s %3s",
+ r.getPoints(),
+ r.getWord().c_str(),
+ string(h - 3 - r.getWordLen(), ' ').c_str(),
+ convertToMb(coord).c_str());
+ }
+ // Complete the list with empty lines, to avoid trails
+ for (; i < m_boxStart + m_boxLines; i++)
+ {
+ boxPrint(win, i, x + 1, string(23, ' ').c_str());
+ }
+}
+
+
+void CursesIntf::drawHistory(WINDOW *win, int y, int x)
+{
+ // To allow pseudo-scrolling, without leaving trails
+ clear();
+
+ drawBox(win, y, x, LINES - y, COLS - x, _(" History of the game "));
+ m_boxY = y + 1;
+ m_boxLines = LINES - y - 2;
+ m_boxLinesData = m_game->getHistory().getSize();
+
+ // Heading
+ boxPrint(win, m_boxStart, x + 2,
+ _(" N | RACK | SOLUTION | REF | PTS | P | BONUS"));
+ mvwhline(win, y + 2, x + 2, ACS_HLINE, 55);
+
+ int i;
+ for (i = m_boxStart + 0; i < m_game->getHistory().getSize() &&
+ i < m_boxStart + m_boxLines; i++)
+ {
+ const Turn& t = m_game->getHistory().getTurn(i);
+ const Round& r = t.getRound();
+ string word = convertToMb(r.getWord());
+ string coord = convertToMb(r.getCoord().toString());
+ boxPrint(win, i + 2, x + 2,
+ "%2d %8s %s%s %3s %3d %1d %c",
+ i + 1, convertToMb(t.getPlayedRack().toString()).c_str(),
+ word.c_str(), string(15 - word.size(), ' ').c_str(),
+ coord.c_str(), r.getPoints(),
+ t.getPlayer(), r.getBonus() ? '*' : ' ');
+ }
+ mvwvline(win, y + 1, x + 5, ACS_VLINE, min(i + 2 - m_boxStart,
m_boxLines));
+ mvwvline(win, y + 1, x + 16, ACS_VLINE, min(i + 2 - m_boxStart,
m_boxLines));
+ mvwvline(win, y + 1, x + 34, ACS_VLINE, min(i + 2 - m_boxStart,
m_boxLines));
+ mvwvline(win, y + 1, x + 40, ACS_VLINE, min(i + 2 - m_boxStart,
m_boxLines));
+ mvwvline(win, y + 1, x + 46, ACS_VLINE, min(i + 2 - m_boxStart,
m_boxLines));
+ mvwvline(win, y + 1, x + 50, ACS_VLINE, min(i + 2 - m_boxStart,
m_boxLines));
+}
+
+
+void CursesIntf::drawHelp(WINDOW *win, int y, int x)
+{
+ // To allow pseudo-scrolling, without leaving trails
+ clear();
+
+ drawBox(win, y, x, LINES - y, COLS - x, _(" Help "));
+ m_boxY = y + 1;
+ m_boxLines = LINES - y - 2;
+
+ int n = 0;
+ boxPrint(win, n++, x + 2, _("[Global]"));
+ boxPrint(win, n++, x + 2, _(" h, H, ? Show/hide help box"));
+ boxPrint(win, n++, x + 2, _(" y, Y Show/hide history of the
game"));
+ boxPrint(win, n++, x + 2, _(" e, E Show/hide dots on empty
squares of the board"));
+ boxPrint(win, n++, x + 2, _(" d, D Check the existence of a
word in the dictionary"));
+ boxPrint(win, n++, x + 2, _(" j, J Play a word"));
+ boxPrint(win, n++, x + 2, _(" s, S Save the game"));
+ boxPrint(win, n++, x + 2, _(" l, L Load a game"));
+ boxPrint(win, n++, x + 2, _(" q, Q Quit"));
+ boxPrint(win, n++, x + 2, "");
+
+ boxPrint(win, n++, x + 2, _("[Training mode]"));
+ boxPrint(win, n++, x + 2, _(" * Take a random rack"));
+ boxPrint(win, n++, x + 2, _(" + Complete the current rack
randomly"));
+ boxPrint(win, n++, x + 2, _(" t, T Set the rack manually"));
+ boxPrint(win, n++, x + 2, _(" c, C Compute all the possible
words"));
+ boxPrint(win, n++, x + 2, _(" r, R Show/hide search
results"));
+ boxPrint(win, n++, x + 2, "");
+
+ boxPrint(win, n++, x + 2, _("[Duplicate mode]"));
+ boxPrint(win, n++, x + 2, _(" n, N Switch to the next human
player"));
+ boxPrint(win, n++, x + 2, "");
+
+ boxPrint(win, n++, x + 2, _("[Free game mode]"));
+ boxPrint(win, n++, x + 2, _(" p, P Pass your turn (with or
without changing letters)"));
+ boxPrint(win, n++, x + 2, "");
+
+ boxPrint(win, n++, x + 2, _("[Miscellaneous]"));
+ boxPrint(win, n++, x + 2, _(" <up>, <down> Navigate in a box line by
line"));
+ boxPrint(win, n++, x + 2, _(" <pgup>, <pgdown> Navigate in a box page by
page"));
+ boxPrint(win, n++, x + 2, _(" Ctrl-l Refresh the screen"));
+
+ m_boxLinesData = n;
+}
+
+
+void CursesIntf::playWord(WINDOW *win, int y, int x)
+{
+ drawBox(win, y, x, 4, 32, _(" Play a word "));
+ mvwprintw(win, y + 1, x + 2, _("Played word:"));
+ mvwprintw(win, y + 2, x + 2, _("Coordinates:"));
+ wrefresh(win);
+
+ // TRANSLATORS: Align the : when translating "Played word:" and
+ // "Coordinates:". For example:
+ // Pl. word :
+ // Coordinates:
+ int l1 = strlen(_("Played word:"));
+ int l2 = strlen(_("Coordinates:"));
+ int xOff;
+ if (l1 > l2)
+ xOff = l1 + 3;
+ else
+ xOff = l2 + 3;
+
+ string word, coord;
+ if (readString(win, y + 1, x + xOff, 15, word) &&
+ readString(win, y + 2, x + xOff, 3, coord))
+ {
+ int res = m_game->play(convertToWc(coord), convertToWc(word));
+ if (res)
+ {
+ drawStatus(win, LINES - 1, 0, _("Incorrect or misplaced word"));
+ }
+ }
+ m_state = DEFAULT;
+ clearRect(win, y, x, 4, 32);
+}
+
+
+void CursesIntf::checkWord(WINDOW *win, int y, int x)
+{
+ drawBox(win, y, x, 4, 32, _(" Dictionary "));
+ mvwprintw(win, y + 1, x + 2, _("Enter the word to check:"));
+ wrefresh(win);
+
+ string word;
+ if (readString(win, y + 2, x + 2, 15, word))
+ {
+ int res = Dic_search_word(m_game->getDic(), convertToWc(word).c_str());
+ char s[100];
+ if (res)
+ snprintf(s, 100, _("The word '%s' exists"), word.c_str());
+ else
+ snprintf(s, 100, _("The word '%s' does not exist"), word.c_str());
+ drawStatus(win, LINES - 1, 0, s);
+ }
+ m_state = DEFAULT;
+ clearRect(win, y, x, 4, 32);
+}
+
+
+void CursesIntf::saveGame(WINDOW *win, int y, int x)
+{
+ drawBox(win, y, x, 4, 32, _(" Save the game "));
+ mvwprintw(win, y + 1, x + 2, _("Enter the file name:"));
+ wrefresh(win);
+
+ string filename;
+ if (readString(win, y + 2, x + 2, 28, filename, kFILENAME))
+ {
+ ofstream fout(filename.c_str());
+ char s[100];
+ if (fout.rdstate() == ios::failbit)
+ {
+ snprintf(s, 100, _("Cannot open file %s for writing"),
+ filename.c_str());
+ }
+ else
+ {
+ m_game->save(fout);
+ fout.close();
+ snprintf(s, 100, _("Game saved in %s"), filename.c_str());
+ }
+ drawStatus(win, LINES - 1, 0, s);
+ }
+ m_state = DEFAULT;
+ clearRect(win, y, x, 4, 32);
+}
+
+
+void CursesIntf::loadGame(WINDOW *win, int y, int x)
+{
+ drawBox(win, y, x, 4, 32, _(" Load a game "));
+ mvwprintw(win, y + 1, x + 2, _("Enter the file name:"));
+ wrefresh(win);
+
+ string filename;
+ if (readString(win, y + 2, x + 2, 28, filename, kFILENAME))
+ {
+ char s[100];
+ FILE *fin;
+ if ((fin = fopen(filename.c_str(), "r")) == NULL)
+ {
+ snprintf(s, 100, _("Cannot open file %s for reading"),
+ filename.c_str());
+ }
+ else
+ {
+ Game *loaded = Game::load(fin, m_game->getDic());
+ if (loaded == NULL)
+ {
+ snprintf(s, 100, _("Invalid saved game"));
+ }
+ else
+ {
+ snprintf(s, 100, _("Game loaded"));
+ GameFactory::Instance()->releaseGame(*m_game);
+ m_game = loaded;
+ }
+ fclose(fin);
+ }
+ drawStatus(win, LINES - 1, 0, s);
+ }
+ m_state = DEFAULT;
+ clearRect(win, y, x, 4, 32);
+}
+
+
+void CursesIntf::passTurn(WINDOW *win, int y, int x, FreeGame &iGame)
+{
+ drawBox(win, y, x, 4, 32, _(" Pass your turn "));
+ mvwprintw(win, y + 1, x + 2, _("Enter the letters to change:"));
+ wrefresh(win);
+
+ string letters;
+ if (readString(win, y + 2, x + 2, 7, letters))
+ {
+ int res = iGame.pass(convertToWc(letters), m_game->currPlayer());
+ if (res)
+ {
+ drawStatus(win, LINES - 1, 0, _("Cannot pass the turn"));
+ }
+ }
+ m_state = DEFAULT;
+ clearRect(win, y, x, 4, 32);
+}
+
+
+void CursesIntf::setRack(WINDOW *win, int y, int x, Training &iGame)
+{
+ drawBox(win, y, x, 4, 32, _(" Set rack "));
+ mvwprintw(win, y + 1, x + 2, _("Enter the new letters:"));
+ wrefresh(win);
+
+ string letters;
+ if (readString(win, y + 2, x + 2, 7, letters, kJOKER))
+ {
+ iGame.setRackManual(false, convertToWc(letters));
+ }
+ m_state = DEFAULT;
+ clearRect(win, y, x, 4, 32);
+}
+
+
+bool CursesIntf::readString(WINDOW *win, int y, int x, int n, string &oString,
+ unsigned int flag)
+{
+ int c;
+ wmove(win, y, x);
+ curs_set(1);
+ while ((c = getch()) != 0)
+ {
+ if (c == 0x1b ) // Esc
+ {
+ curs_set(0);
+ return false;
+ }
+ else if (c == KEY_ENTER || c == 0xD)
+ {
+ curs_set(0);
+ return true;
+ }
+ else if (c == 0x0c) // Ctrl-L
+ {
+// clear();
+ redraw(win);
+ wmove(win, y, x);
+ }
+ else if (c == KEY_BACKSPACE && oString.size() > 0)
+ {
+ x--;
+ mvwprintw(win, y, x, " ");
+ wmove(win, y, x);
+ oString.erase(oString.size() - 1);
+ }
+ else if (isalnum(c) && oString.size() < (unsigned int)n)
+ {
+ mvwprintw(win, y, x, "%c", c);
+ x++;
+ oString += (char)c;
+ }
+ else
+ {
+ if (flag & kJOKER && c == '?')
+ {
+ mvwprintw(win, y, x, "%c", c);
+ x++;
+ oString += (char)c;
+ }
+ if (flag & kFILENAME)
+ {
+ if (c == '/' || c == '.' || c == '-' || c == '_' || c == ' ')
+ {
+ mvwprintw(win, y, x, "%c", c);
+ x++;
+ oString += (char)c;
+ }
+ }
+ }
+// else
+// mvwprintw(win, 0, 0, "%3d", c);
+ }
+ curs_set(0);
+ return 0;
+}
+
+
+int CursesIntf::handleKeyForGame(int iKey, Training &iGame)
+{
+ switch (iKey)
+ {
+ case '*':
+ iGame.setRackRandom(0, false, Game::RACK_ALL);
+ return 1;
+
+ case '+':
+ iGame.setRackRandom(0, false, Game::RACK_NEW);
+ return 1;
+
+ case 't':
+ case 'T':
+ setRack(m_win, 22, 10, iGame);
+ return 1;
+
+ case 'c':
+ case 'C':
+ iGame.search();
+ return 1;
+
+ default:
+ return 2;
+ }
+}
+
+
+int CursesIntf::handleKeyForGame(int iKey, Duplicate &iGame)
+{
+ switch (iKey)
+ {
+ case 'n':
+ case 'N':
+ iGame.nextHumanPlayer();
+ return 1;
+
+ default:
+ return 2;
+ }
+}
+
+
+int CursesIntf::handleKeyForGame(int iKey, FreeGame &iGame)
+{
+ switch (iKey)
+ {
+ case 'p':
+ case 'P':
+ passTurn(m_win, 22, 10, iGame);
+ return 1;
+
+ default:
+ return 2;
+ }
+}
+
+
+int CursesIntf::handleKey(int iKey)
+{
+ if (m_state == DEFAULT)
+ {
+ int res;
+ if (m_game->getMode() == Game::kTRAINING)
+ {
+ res = handleKeyForGame(iKey, (Training&)*m_game);
+ }
+ else if (m_game->getMode() == Game::kDUPLICATE)
+ {
+ res = handleKeyForGame(iKey, (Duplicate&)*m_game);
+ }
+ else
+ {
+ res = handleKeyForGame(iKey, (FreeGame&)*m_game);
+ }
+
+ if (res != 2)
+ return res;
+ }
+ else // m_state is in {HELP, RESULTS, HISTORY}
+ {
+ switch (iKey)
+ {
+ case KEY_HOME:
+ if (m_boxLinesData <= m_boxLines && m_boxStart > 0)
+ return 0;
+ m_boxStart = 0;
+ return 1;
+ case KEY_END:
+ if (m_boxLinesData <= m_boxLines &&
+ m_boxStart < m_boxLinesData - 1)
+ return 0;
+ m_boxStart = m_boxLinesData - 1;
+ return 1;
+ case KEY_UP:
+ if (m_boxLinesData <= m_boxLines || m_boxStart <= 0)
+ return 0;
+ m_boxStart--;
+ return 1;
+ case KEY_DOWN:
+ if (m_boxLinesData <= m_boxLines ||
+ m_boxStart >= m_boxLinesData - 1)
+ return 0;
+ m_boxStart++;
+ return 1;
+ case KEY_PPAGE:
+ if (m_boxLinesData <= m_boxLines)
+ return 0;
+ m_boxStart -= m_boxLines;
+ if (m_boxStart < 0)
+ m_boxStart = 0;
+ return 1;
+ case KEY_NPAGE:
+ if (m_boxLinesData <= m_boxLines)
+ return 0;
+ m_boxStart += m_boxLines;
+ if (m_boxStart > m_boxLinesData - 1)
+ m_boxStart = m_boxLinesData - 1;
+ return 1;
+ }
+ }
+
+ switch (iKey)
+ {
+ // Toggle help
+ case 'h':
+ case 'H':
+ case '?':
+ if (m_state == HELP)
+ m_state = DEFAULT;
+ else
+ m_state = HELP;
+ m_boxStart = 0;
+ clear();
+ return 1;
+
+ // Toggle history
+ case 'y':
+ case 'Y':
+ if (m_state == HISTORY)
+ m_state = DEFAULT;
+ else
+ m_state = HISTORY;
+ m_boxStart = 0;
+ clear();
+ return 1;
+
+ // Toggle results (training mode only)
+ case 'r':
+ case 'R':
+ if (m_game->getMode() != Game::kTRAINING)
+ return 0;
+ if (m_state == RESULTS)
+ m_state = DEFAULT;
+ else
+ m_state = RESULTS;
+ m_boxStart = 0;
+ clear();
+ return 1;
+
+ // Toggle dots display
+ case 'e':
+ case 'E':
+ m_showDots = !m_showDots;
+ return 1;
+
+ // Check a word in the dictionary
+ case 'd':
+ case 'D':
+ if (m_state != DEFAULT)
+ return 0;
+ checkWord(m_win, 22, 10);
+ return 1;
+
+ // Play a word
+ case 'j':
+ case 'J':
+ if (m_state != DEFAULT)
+ return 0;
+ playWord(m_win, 22, 10);
+ return 1;
+
+ // Ctrl-L should clear and redraw the screen
+ case 0x0c:
+ clear();
+ return 1;
+
+ case 'l':
+ case 'L':
+ if (m_state != DEFAULT)
+ return 0;
+ loadGame(m_win, 22, 10);
+ return 1;
+
+ case 's':
+ case 'S':
+ if (m_state != DEFAULT)
+ return 0;
+ saveGame(m_win, 22, 10);
+ return 1;
+
+ // Quit
+ case 'q':
+ case 'Q':
+ case 0x1b: // Esc
+ m_dying = true;
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+
+void CursesIntf::redraw(WINDOW *win)
+{
+ if (m_state == DEFAULT)
+ {
+ drawScoresRacks(win, 3, 54);
+ drawBoard(win, 2, 0);
+ }
+ else if (m_state == RESULTS)
+ {
+ drawResults(win, 3, 54);
+ drawBoard(win, 2, 0);
+ }
+ else if (m_state == HELP)
+ {
+ drawHelp(win, 1, 0);
+ }
+ else if (m_state == HISTORY)
+ {
+ drawHistory(win, 1, 0);
+ }
+
+ // Title
+ attron(A_REVERSE);
+ string mode;
+ if (m_game->getMode() == Game::kTRAINING)
+ mode = _("Training mode");
+ else if (m_game->getMode() == Game::kFREEGAME)
+ mode = _("Free game mode");
+ else if (m_game->getMode() == Game::kDUPLICATE)
+ mode = _("Duplicate mode");
+ string variant = "";
+ if (m_game->getVariant() == Game::kJOKER)
+ variant = string(" - ") + _("Joker game");
+ string title = "Eliot (" + mode + variant + ") " + _("[h for help]");
+ mvwprintw(win, 0, 0, title.c_str());
+ whline(win, ' ', COLS - title.size());
+ attroff(A_REVERSE);
+
+ wrefresh(win);
+}
+
+
+int main(int argc, char ** argv)
+{
+#ifdef HAVE_SETLOCALE
+ // Set locale via LC_ALL
+ setlocale(LC_ALL, "");
+#endif
+
+#if ENABLE_NLS
+ // Set the message domain
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ srand(time(NULL));
+
+ Game *game = GameFactory::Instance()->createFromCmdLine(argc, argv);
+ if (game == NULL)
+ return 1;
+
+ game->start();
+
+ // Initialize the ncurses library
+ WINDOW *wBoard = initscr();
+ keypad(wBoard, true);
+ // Take input chars one at a time
+ cbreak();
+ // Do not do NL -> NL/CR
+ nonl();
+ // Hide the cursor
+ curs_set(0);
+
+ if (has_colors())
+ {
+ start_color();
+
+ // Simple color assignment
+ init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
+ init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
+ init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
+ init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
+
+ init_pair(COLOR_BLUE, COLOR_BLACK, COLOR_BLUE);
+ init_pair(COLOR_CYAN, COLOR_BLACK, COLOR_CYAN);
+ init_pair(COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA);
+ init_pair(COLOR_RED, COLOR_BLACK, COLOR_RED);
+ }
+
+ // Do not echo
+ noecho();
+
+ // mainIntf will take care of destroying game for us.
+ CursesIntf mainIntf(wBoard, *game);
+ mainIntf.redraw(wBoard);
+
+ while (!mainIntf.isDying())
+ {
+ int c = getch();
+ if (mainIntf.handleKey(c) == 1)
+ {
+ mainIntf.redraw(wBoard);
+ }
+ }
+
+ delwin(wBoard);
+
+ // Exit the ncurses library
+ endwin();
+
+ GameFactory::Destroy();
+
+ return 0;
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Eliot-dev] eliot dic/compdic.c dic/dic_search.c dic/dic_se... [multibyte],
eliot-dev <=