- Invalid moves should be in the display form

- Moved some code from Header to Dictionary
This commit is contained in:
Olivier Teulière 2009-07-03 21:40:14 +00:00
parent 8142c92694
commit 02e4cb2970
15 changed files with 156 additions and 125 deletions

View file

@ -27,6 +27,7 @@
#include <cstring>
#include <cerrno>
#include <cctype>
#include <boost/foreach.hpp>
// For ntohl & Co.
#ifdef WIN32
@ -51,7 +52,7 @@ const Dictionary *Dictionary::m_dic = NULL;
Dictionary::Dictionary(const string &iPath)
: m_dawg(NULL)
: m_dawg(NULL), m_hasDisplay(false)
{
ifstream file(iPath.c_str(), ios::in | ios::binary);
@ -86,6 +87,31 @@ Dictionary::Dictionary(const string &iPath)
std::transform(lower.begin(), lower.end(), lower.begin(), towlower);
m_allInputChars = m_header->getInputChars() + lower;
// Build the cache for the convertToDisplay() and convertFromInput()
// methods.
map<wchar_t, vector<wstring> >::const_iterator it;
for (it = m_header->getDisplayInputData().begin();
it != m_header->getDisplayInputData().end(); ++it)
{
// Save both the upper case and lower case versions
BOOST_FOREACH(wstring str, it->second)
{
// Make sure the string is in uppercase
std::transform(str.begin(), str.end(), str.begin(), towupper);
// Make a lowercase copy
wstring lower = str;
std::transform(lower.begin(), lower.end(), lower.begin(), towlower);
// Fill the cache
m_displayInputCache[towupper(it->first)].push_back(str);
m_displayInputCache[towlower(it->first)].push_back(lower);
}
// Update the m_hasDisplay flag
if (!m_hasDisplay && it->second[0] != wstring(1, it->first))
m_hasDisplay = true;
}
m_dic = this;
}
@ -137,6 +163,58 @@ bool Dictionary::validateInputChars(const wistring &iLetters,
}
wdstring Dictionary::convertToDisplay(const wstring &iWord) const
{
// Optimization for dictionaries without display nor input chars,
// which is the case in most languages.
if (!m_hasDisplay)
return iWord;
wdstring dispStr = iWord;
map<wchar_t, vector<wstring> >::const_iterator it;
for (it = m_displayInputCache.begin();
it != m_displayInputCache.end(); ++it)
{
const wstring &disp = it->second[0];
string::size_type pos = 0;
while (pos < dispStr.size() &&
(pos = dispStr.find(it->first, pos)) != string::npos)
{
dispStr.replace(pos, 1, disp);
pos += disp.size();
}
}
return dispStr;
}
wstring Dictionary::convertFromInput(const wistring &iWord) const
{
// Optimization for dictionaries without display nor input chars,
// which is the case in most languages.
if (m_displayInputCache.empty())
return iWord;
wstring str = iWord;
map<wchar_t, vector<wstring> >::const_iterator it;
for (it = m_displayInputCache.begin();
it != m_displayInputCache.end(); ++it)
{
BOOST_FOREACH(const wstring &input, it->second)
{
string::size_type pos = 0;
while (pos < str.size() &&
(pos = str.find(input, pos)) != string::npos)
{
str.replace(pos, input.size(), wstring(1, it->first));
pos += input.size();
}
}
}
return str;
}
dic_elt_t Dictionary::getNext(const dic_elt_t &e) const
{
if (!isLast(e))

View file

@ -54,6 +54,7 @@ class DicEdge;
* a bit more precisely the type of contents of the string.
*/
typedef wstring wdstring;
typedef wstring wistring;
class Dictionary
{
@ -102,6 +103,21 @@ public:
bool validateInputChars(const wstring &iLetters,
const wstring &iAccepted = L"") const;
/**
* Convert the given string (made of internal characters)
* into a string suitable for display
*/
wdstring convertToDisplay(const wstring &iWord) const;
/**
* Convert the given string (direct user input)
* into a string suitable for internal use in Eliot.
* For example, in Catalan, it will convert the L.L substring
* into the W character (because it is the internal character
* associated to the input string "L.L").
*/
wstring convertFromInput(const wistring &iWord) const;
/** Return a vector containing one of each possible tile */
const vector<Tile>& getAllTiles() const { return m_tilesVect; }
@ -265,6 +281,25 @@ private:
/// Vector of available tiles
vector<Tile> m_tilesVect;
/**
* Associate to some internal chars (both the lower case and
* upper case versions) all the corresponding input strings.
* The first one is always the display string.
*
* Note: only the chars which have more than 1 input string,
* or which have a display string different from the internal char,
* are present in the map.
*/
map<wchar_t, vector<wstring> > m_displayInputCache;
/**
* True if at least one display strings is different from the internal
* char, false otherwise. This flag is more precise than checking the size
* of m_displayInputCache, because a tile can have input strings even if
* its display string is equal to the internal char.
*/
bool m_hasDisplay;
static const Dictionary *m_dic;
void convertDataToArch();

View file

@ -23,6 +23,7 @@
#include <cstring>
#include <cwchar>
#include <cwctype>
#include <algorithm>
#include "dic_internals.h"
#include "dic_exception.h"
@ -104,7 +105,7 @@ void Dictionary::searchWordByLen(struct params_7plus1_t &params,
// Add the solution
vector<wdstring> &sols = (*params.results)[params.added_display];
if (sols.empty() || sols.back() != params.search_wordtst)
sols.push_back(getHeader().convertToDisplay(params.search_wordtst));
sols.push_back(convertToDisplay(params.search_wordtst));
}
}
else
@ -127,7 +128,7 @@ void Dictionary::searchWordByLen(struct params_7plus1_t &params,
// Add the solution
vector<wdstring> &sols = (*params.results)[params.added_display];
if (sols.empty() || sols.back() != params.search_wordtst)
sols.push_back(getHeader().convertToDisplay(params.search_wordtst));
sols.push_back(convertToDisplay(params.search_wordtst));
}
}
else
@ -230,7 +231,10 @@ void Dictionary::searchRacc(const wstring &iWord,
oWordList.reserve(DEFAULT_VECT_ALLOC);
// Transform the given word to make it suitable for display
const wdstring &displayWord = getHeader().convertToDisplay(iWord);
wdstring displayWord = convertToDisplay(iWord);
// Make it uppercase
std::transform(displayWord.begin(), displayWord.end(),
displayWord.begin(), towupper);
// Try to add a letter at the front
const wstring &letters = getHeader().getLetters();
@ -238,8 +242,7 @@ void Dictionary::searchRacc(const wstring &iWord,
{
if (searchWord(letters[i] + iWord))
{
const wdstring &chr =
getHeader().getDisplayStr(getHeader().getCodeFromChar(letters[i]));
const wdstring &chr = getHeader().getDisplayStr(getHeader().getCodeFromChar(letters[i]));
oWordList.push_back(chr + displayWord);
}
if (iMaxResults && oWordList.size() >= iMaxResults)
@ -283,7 +286,10 @@ void Dictionary::searchBenj(const wstring &iWord, vector<wdstring> &oWordList,
oWordList.reserve(DEFAULT_VECT_ALLOC);
// Transform the given word to make it suitable for display
const wdstring &displayWord = getHeader().convertToDisplay(iWord);
wdstring displayWord = convertToDisplay(iWord);
// Make it uppercase
std::transform(displayWord.begin(), displayWord.end(),
displayWord.begin(), towupper);
const DicEdge *edge0, *edge1, *edge2, *edgetst;
edge0 = getEdgeAt(getRoot());

View file

@ -250,26 +250,6 @@ void Header::buildCaches()
m_mapCodeFromChar[towupper(m_letters[i])] = i + 1;
}
// Build the cache for the convertToDisplay() and convertFromInput()
// methods. Also ensure that the strings in m_displayAndInputData
// are all in uppercase.
map<wchar_t, vector<wstring> >::iterator it;
for (it = m_displayAndInputData.begin();
it != m_displayAndInputData.end(); ++it)
{
BOOST_FOREACH(wstring &str, it->second)
{
// Make sure the string is in uppercase
std::transform(str.begin(), str.end(), str.begin(), towupper);
// Make a lowercase copy
wstring lower = str;
std::transform(lower.begin(), lower.end(), lower.begin(), towlower);
// Fill the cache
m_displayInputCache[it->first].push_back(str);
m_displayInputCache[towlower(it->first)].push_back(lower);
}
}
// Build the display strings cache
m_displayCache.assign(m_letters.size() + 1, L"");
for (unsigned int i = 0; i < m_letters.size(); ++i)
@ -368,58 +348,6 @@ vector<wistring> Header::getInputStr(unsigned int iCode) const
}
wdstring Header::convertToDisplay(const wstring &iWord) const
{
// Optimization for dictionaries without display nor input chars,
// which is the case in most languages.
if (m_displayInputCache.empty())
return iWord;
wdstring dispStr = iWord;
map<wchar_t, vector<wstring> >::const_iterator it;
for (it = m_displayInputCache.begin();
it != m_displayInputCache.end(); ++it)
{
const wstring &disp = it->second[0];
string::size_type pos = 0;
while (pos < dispStr.size() &&
(pos = dispStr.find(it->first, pos)) != string::npos)
{
dispStr.replace(pos, 1, disp);
pos += disp.size();
}
}
return dispStr;
}
wstring Header::convertFromInput(const wistring &iWord) const
{
// Optimization for dictionaries without display nor input chars,
// which is the case in most languages.
if (m_displayInputCache.empty())
return iWord;
wstring str = iWord;
map<wchar_t, vector<wstring> >::const_iterator it;
for (it = m_displayInputCache.begin();
it != m_displayInputCache.end(); ++it)
{
BOOST_FOREACH(const wstring &input, it->second)
{
string::size_type pos = 0;
while (pos < str.size() &&
(pos = str.find(input, pos)) != string::npos)
{
str.replace(pos, input.size(), wstring(1, it->first));
pos += input.size();
}
}
}
return str;
}
void Header::read(istream &iStream)
{
Dict_header_old aHeader;

View file

@ -117,6 +117,8 @@ public:
bool isConsonant(unsigned int iCode) const { return m_consonants[iCode - 1]; }
//@}
const map<wchar_t, vector<wstring> > & getDisplayInputData() const { return m_displayAndInputData; }
/**
* Return the letter corresponding to the given code
*/
@ -137,21 +139,6 @@ public:
*/
vector<wistring> getInputStr(unsigned int iCode) const;
/**
* Convert the given string (made of internal characters)
* into a string suitable for display
*/
wdstring convertToDisplay(const wstring &iWord) const;
/**
* Convert the given string (direct user input)
* into a string suitable for internal use in Eliot.
* For example, in Catalan, it will convert the L.L substring
* into the W character (because it is the internal character
* associated to the input string "L.L").
*/
wstring convertFromInput(const wistring &iWord) const;
/**
* Print a readable summary of the header on standard output
*/
@ -211,9 +198,6 @@ private:
/// Cache for the display string of each code
vector<wdstring> m_displayCache;
/// Same as m_displayAndInputData, but also contains lowercase mappings
map<wchar_t, vector<wstring> > m_displayInputCache;
/**
* Load the header from a file
* @param iStream: Input stream where to read the header

View file

@ -37,7 +37,6 @@
#include "dic.h"
#include "dic_exception.h"
#include "header.h"
#include "encoding.h"

View file

@ -73,8 +73,10 @@ int Duplicate::play(const wstring &iCoord, const wstring &iWord)
}
else
{
// Convert the invalid word for display
const wdstring &dispWord = getDic().convertToDisplay(iWord);
// Record the invalid move of the player
recordPlayerMove(Move(iWord, iCoord), currPlayer, true);
recordPlayerMove(Move(dispWord, iCoord), currPlayer, true);
}
// Little hack to handle duplicate games with only AI players.

View file

@ -71,7 +71,10 @@ int FreeGame::play(const wstring &iCoord, const wstring &iWord)
}
else
{
Move move(iWord, iCoord);
// Convert the invalid word for display
const wdstring &dispWord = getDic().convertToDisplay(iWord);
Move move(dispWord, iCoord);
// Record the invalid move of the player
recordPlayerMove(move, *m_players[m_currPlayer], true);

View file

@ -57,6 +57,7 @@ class Move
* Constructor taking a word and its coordinates, corresponding
* to an invalid move by the player (invalid word, invalid coordinates,
* letters not corresponding to the rack, ...)
* Note: the invalid word must be given in the display form.
*/
explicit Move(const wstring &iWord, const wstring &iCoord);

View file

@ -22,10 +22,10 @@
#include "turn.h"
// FIXME: move set to an invalid value. It would be better to get rid of this
// FIXME: move set to an arbitrary one (pass). It would be better to get rid of this
// constructor completely
Turn::Turn()
: m_playerId(0), m_move(L"", L"")
: m_playerId(0), m_move(L"")
{
}

View file

@ -158,11 +158,11 @@ void DicToolsWidget::refreshCheck()
return;
}
wstring input = m_dic->getHeader().convertFromInput(qtw(rack->text()));
wstring input = m_dic->convertFromInput(qtw(rack->text()));
bool res = m_dic->searchWord(input);
// Convert the input to uppercase
std::transform(input.begin(), input.end(), input.begin(), towupper);
const wdstring &dispStr = m_dic->getHeader().convertToDisplay(input);
const wdstring &dispStr = m_dic->convertToDisplay(input);
if (res)
{
labelCheck->setText(_q("The word '%1' exists").arg(qfw(dispStr)));
@ -192,8 +192,8 @@ void DicToolsWidget::refreshPlus1()
return;
}
const wstring &input = m_dic->getHeader().convertFromInput(qtw(rack->text().toUpper()));
const wdstring &disp = m_dic->getHeader().convertToDisplay(input);
const wstring &input = m_dic->convertFromInput(qtw(rack->text().toUpper()));
const wdstring &disp = m_dic->convertToDisplay(input);
model->setHeaderData(0, Qt::Horizontal,
_q("Rack: %1").arg(qfw(disp)),
Qt::DisplayRole);
@ -245,8 +245,8 @@ void DicToolsWidget::refreshRegexp()
return;
}
const wstring &input = m_dic->getHeader().convertFromInput(qtw(rack->text().toUpper()));
const wdstring &disp = m_dic->getHeader().convertToDisplay(input);
const wstring &input = m_dic->convertFromInput(qtw(rack->text().toUpper()));
const wdstring &disp = m_dic->convertToDisplay(input);
model->setHeaderData(0, Qt::Horizontal,
_q("Regular expression: %1").arg(qfw(disp)),
Qt::DisplayRole);
@ -302,7 +302,7 @@ void DicToolsWidget::refreshDicInfo()
{
const Header &header = m_dic->getHeader();
lineEditName->setText(qfw(header.getName()));
lineEditLetters->setText(qfw(header.convertToDisplay(header.getLetters())));
lineEditLetters->setText(qfw(m_dic->convertToDisplay(header.getLetters())));
spinBoxWords->setValue(header.getNbWords());
QStandardItemModel *model = m_dicInfoModel;
@ -362,7 +362,7 @@ QValidator::State DicRackValidator::validate(QString &input, int &) const
return Invalid;
// Convert the string to internal letters
const wstring &intInput = m_dic->getHeader().convertFromInput(winput);
const wstring &intInput = m_dic->convertFromInput(winput);
// The string is invalid if it contains characters not present
// in the dictionary
if (!m_dic->validateLetters(intInput))
@ -402,7 +402,7 @@ QValidator::State RegexpValidator::validate(QString &input, int &) const
return Invalid;
// Convert the string to internal letters
const wstring &intInput = m_dic->getHeader().convertFromInput(winput);
const wstring &intInput = m_dic->convertFromInput(winput);
// The string is invalid if it contains characters not present
// in the dictionary
if (!m_dic->validateLetters(intInput, authorizedChars))

View file

@ -28,7 +28,6 @@
#include "public_game.h"
#include "coord.h"
#include "dic.h"
#include "header.h"
#include "debug.h"
@ -111,7 +110,7 @@ void PlayWordMediator::lineEditPlay_returnPressed()
// Convert the jokers to lowercase
const wistring &inputWord = qtw(m_lineEditPlay.text().toUpper());
// Convert to internal representation, then back to QString
QString word = qfw(m_game->getDic().getHeader().convertFromInput(inputWord));
QString word = qfw(m_game->getDic().convertFromInput(inputWord));
int pos;
while ((pos = word.indexOf('(')) != -1)
@ -135,7 +134,7 @@ void PlayWordMediator::lineEditPlay_returnPressed()
// Convert the input string into an internal one
const wstring intWord =
m_game->getDic().getHeader().convertFromInput(qtw(word));
m_game->getDic().convertFromInput(qtw(word));
QString coords = m_lineEditCoord.text();
int res = m_game->play(intWord, qtw(coords));
@ -235,7 +234,7 @@ QValidator::State PlayWordValidator::validate(QString &input, int &) const
return Invalid;
// Convert the string to internal letters
const wstring &intInput = m_dic.getHeader().convertFromInput(winput);
const wstring &intInput = m_dic.convertFromInput(winput);
// The string is invalid if it contains characters not present
// in the dictionary (ignoring parentheses)
if (!m_dic.validateLetters(intInput, L"()"))

View file

@ -33,7 +33,6 @@
#include "coord.h"
#include "coord_model.h"
#include "dic.h"
#include "header.h"
#include "debug.h"
#include "encoding.h"
@ -153,7 +152,7 @@ void PlayerWidget::on_lineEditChange_returnPressed()
QString inputLetters = lineEditChange->text();
// Convert the input string into an internal one
const wstring &letters =
m_game->getDic().getHeader().convertFromInput(qtw(inputLetters));
m_game->getDic().convertFromInput(qtw(inputLetters));
// Pass the turn (and possibly change letters)
int res = m_game->freeGamePass(letters);
if (res == 0)
@ -188,13 +187,13 @@ QValidator::State ChangeValidator::validate(QString &input, int &) const
return Invalid;
// Convert the string to internal letters
const wstring &intInput = m_dic.getHeader().convertFromInput(winput);
const wstring &intInput = m_dic.convertFromInput(winput);
// The string is invalid if it contains characters not present
// in the dictionary
if (!m_dic.validateLetters(intInput))
return Intermediate;
const wstring &rack = m_dic.getHeader().convertFromInput(qtw(m_lineEdit.text()));
const wstring &rack = m_dic.convertFromInput(qtw(m_lineEdit.text()));
if (intInput.size() > rack.size())
return Intermediate;
// The letters to change must be in the rack

View file

@ -26,7 +26,6 @@
#include "play_word_mediator.h"
#include "dic.h"
#include "header.h"
#include "bag.h"
#include "public_game.h"
#include "game_exception.h"
@ -210,8 +209,7 @@ void TrainingWidget::on_lineEditRack_textEdited(const QString &iText)
try
{
lineEditRack->setPalette(blackPalette);
const Header &header = m_game->getDic().getHeader();
const wstring &input = header.convertFromInput(qtw(iText));
const wstring &input = m_game->getDic().convertFromInput(qtw(iText));
m_game->trainingSetRackManual(false, input);
pushButtonSearch->setEnabled(m_model->rowCount() == 0 &&
lineEditRack->text() != "");
@ -319,7 +317,7 @@ QValidator::State RackValidator::validate(QString &input, int &) const
return Invalid;
// Convert the string to internal letters
const wstring &intInput = dic.getHeader().convertFromInput(winput);
const wstring &intInput = dic.convertFromInput(winput);
// The string is invalid if it contains characters not present
// in the dictionary
if (!dic.validateLetters(intInput))

View file

@ -39,7 +39,6 @@
#include "dic.h"
#include "dic_exception.h"
#include "header.h"
#include "game_io.h"
#include "game_factory.h"
#include "public_game.h"
@ -139,7 +138,7 @@ wstring checkLettersToken(const vector<wstring> &tokens, uint8_t index,
{
if (tokens.size() <= index)
return L"";
return iDic.getHeader().convertFromInput(tokens[index]);
return iDic.convertFromInput(tokens[index]);
}