From 02e4cb2970a82a7fe11c5ae4d8855186c8e9a9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Teuli=C3=A8re?= Date: Fri, 3 Jul 2009 21:40:14 +0000 Subject: [PATCH] - Invalid moves should be in the display form - Moved some code from Header to Dictionary --- dic/dic.cpp | 80 ++++++++++++++++++++++++++++++++++++++- dic/dic.h | 35 +++++++++++++++++ dic/dic_search.cpp | 18 ++++++--- dic/header.cpp | 72 ----------------------------------- dic/header.h | 20 +--------- dic/regexpmain.cpp | 1 - game/duplicate.cpp | 4 +- game/freegame.cpp | 5 ++- game/move.h | 1 + game/turn.cpp | 4 +- qt/dic_tools_widget.cpp | 18 ++++----- qt/play_word_mediator.cpp | 7 ++-- qt/player_widget.cpp | 7 ++-- qt/training_widget.cpp | 6 +-- utils/eliottxt.cpp | 3 +- 15 files changed, 156 insertions(+), 125 deletions(-) diff --git a/dic/dic.cpp b/dic/dic.cpp index 27a62be..5fe84fc 100644 --- a/dic/dic.cpp +++ b/dic/dic.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // 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 >::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 >::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 >::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)) diff --git a/dic/dic.h b/dic/dic.h index 12d9d27..b909d41 100644 --- a/dic/dic.h +++ b/dic/dic.h @@ -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& getAllTiles() const { return m_tilesVect; } @@ -265,6 +281,25 @@ private: /// Vector of available tiles vector 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 > 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(); diff --git a/dic/dic_search.cpp b/dic/dic_search.cpp index dabc129..e581dd6 100644 --- a/dic/dic_search.cpp +++ b/dic/dic_search.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "dic_internals.h" #include "dic_exception.h" @@ -104,7 +105,7 @@ void Dictionary::searchWordByLen(struct params_7plus1_t ¶ms, // Add the solution vector &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 ¶ms, // Add the solution vector &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 &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()); diff --git a/dic/header.cpp b/dic/header.cpp index 8709e87..f1a60aa 100644 --- a/dic/header.cpp +++ b/dic/header.cpp @@ -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 >::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 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 >::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 >::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; diff --git a/dic/header.h b/dic/header.h index 432f9d7..7086092 100644 --- a/dic/header.h +++ b/dic/header.h @@ -117,6 +117,8 @@ public: bool isConsonant(unsigned int iCode) const { return m_consonants[iCode - 1]; } //@} + const map > & getDisplayInputData() const { return m_displayAndInputData; } + /** * Return the letter corresponding to the given code */ @@ -137,21 +139,6 @@ public: */ vector 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 m_displayCache; - /// Same as m_displayAndInputData, but also contains lowercase mappings - map > m_displayInputCache; - /** * Load the header from a file * @param iStream: Input stream where to read the header diff --git a/dic/regexpmain.cpp b/dic/regexpmain.cpp index 0784e85..cabbd3f 100644 --- a/dic/regexpmain.cpp +++ b/dic/regexpmain.cpp @@ -37,7 +37,6 @@ #include "dic.h" #include "dic_exception.h" -#include "header.h" #include "encoding.h" diff --git a/game/duplicate.cpp b/game/duplicate.cpp index 7e5e59e..7b56d21 100644 --- a/game/duplicate.cpp +++ b/game/duplicate.cpp @@ -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. diff --git a/game/freegame.cpp b/game/freegame.cpp index a130bbc..6681317 100644 --- a/game/freegame.cpp +++ b/game/freegame.cpp @@ -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); diff --git a/game/move.h b/game/move.h index a79a067..8b19bd0 100644 --- a/game/move.h +++ b/game/move.h @@ -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); diff --git a/game/turn.cpp b/game/turn.cpp index 5e9dc40..cd15cf8 100644 --- a/game/turn.cpp +++ b/game/turn.cpp @@ -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"") { } diff --git a/qt/dic_tools_widget.cpp b/qt/dic_tools_widget.cpp index 39e4c74..797a944 100644 --- a/qt/dic_tools_widget.cpp +++ b/qt/dic_tools_widget.cpp @@ -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)) diff --git a/qt/play_word_mediator.cpp b/qt/play_word_mediator.cpp index 8b4b3fb..4208790 100644 --- a/qt/play_word_mediator.cpp +++ b/qt/play_word_mediator.cpp @@ -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"()")) diff --git a/qt/player_widget.cpp b/qt/player_widget.cpp index 6f26b63..f5807df 100644 --- a/qt/player_widget.cpp +++ b/qt/player_widget.cpp @@ -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 diff --git a/qt/training_widget.cpp b/qt/training_widget.cpp index 302f1eb..46a7aae 100644 --- a/qt/training_widget.cpp +++ b/qt/training_widget.cpp @@ -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)) diff --git a/utils/eliottxt.cpp b/utils/eliottxt.cpp index dec7c9c..973b5a0 100644 --- a/utils/eliottxt.cpp +++ b/utils/eliottxt.cpp @@ -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 &tokens, uint8_t index, { if (tokens.size() <= index) return L""; - return iDic.getHeader().convertFromInput(tokens[index]); + return iDic.convertFromInput(tokens[index]); }