diff --git a/game/move_selector.cpp b/game/move_selector.cpp index 20a29f0..48a909b 100644 --- a/game/move_selector.cpp +++ b/game/move_selector.cpp @@ -18,10 +18,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ +#include + #include "move_selector.h" #include "round.h" #include "results.h" #include "bag.h" +#include "rack.h" #include "dic.h" #include "debug.h" @@ -30,11 +33,16 @@ INIT_LOGGER(game, MoveSelector); #define PLAYED_JOKER (-1000) +#define LETTER_REPEATED_3_TIMES (-3) +#define LETTER_REPEATED_4_TIMES (-12) +#define LETTER_REPEATED_5_TIMES (-40) +#define LETTER_REPEATED_6_TIMES (-100) #define EXTENSION_1 50 -MoveSelector::MoveSelector(const Bag &iBag, const Dictionary &iDic) - : m_bag(iBag), m_dic(iDic) +MoveSelector::MoveSelector(const Bag &iBag, const Dictionary &iDic, + const Board &iBoard, const Rack &iRack) + : m_bag(iBag), m_dic(iDic), m_board(iBoard), m_rack(iRack) { } @@ -69,6 +77,7 @@ int MoveSelector::evalScore(const Round &iRound) const { int score = 0; score += evalForJokersInRack(iRound); + score += evalForRemainingLetters(iRound); // Deactivated for now, as it breaks a few non-regression tests, // and I don't have time to fix them at the moment... :) #if 0 @@ -85,6 +94,42 @@ int MoveSelector::evalForJokersInRack(const Round &iRound) const } +int MoveSelector::evalForRemainingLetters(const Round &iRound) const +{ + // Compute the rack remaining after playing the round + Rack remaining = m_rack; + for (unsigned i = 0; i < iRound.getWordLen(); ++i) + { + if (iRound.isPlayedFromRack(i)) + { + remaining.remove(iRound.isJoker(i) ? Tile::Joker() : iRound.getTile(i)); + } + } + + // If a letter is present at least 3 times in the rack, consider it bad + // Note: the repetitions are only relevant if the rack is not rejected for + // lack of vowels and/or consonants... + int score = 0; + BOOST_FOREACH(const Tile &t, m_dic.getAllTiles()) + { + const int count = remaining.count(t); + if (count >= 3) + { + if (count == 3) + score += LETTER_REPEATED_3_TIMES; + else if (count == 4) + score += LETTER_REPEATED_4_TIMES; + else if (count == 5) + score += LETTER_REPEATED_5_TIMES; + else if (count == 6) + score += LETTER_REPEATED_6_TIMES; + } + } + + return score; +} + + int MoveSelector::evalForExtensions(const Round &iRound) const { // Find front and back extensions to the given round diff --git a/game/move_selector.h b/game/move_selector.h index 76a463c..c008af7 100644 --- a/game/move_selector.h +++ b/game/move_selector.h @@ -27,6 +27,8 @@ class Round; class BestResults; class Bag; class Dictionary; +class Board; +class Rack; /** @@ -40,7 +42,8 @@ class MoveSelector DEFINE_LOGGER(); public: - MoveSelector(const Bag &iBag, const Dictionary &iDic); + MoveSelector(const Bag &iBag, const Dictionary &iDic, + const Board &iBoard, const Rack &iRack); /** * Return a move to be used as "master move" in a duplicate game. @@ -58,9 +61,12 @@ public: private: const Bag &m_bag; const Dictionary &m_dic; + const Board &m_board; + const Rack &m_rack; int evalScore(const Round &iRound) const; int evalForJokersInRack(const Round &iRound) const; + int evalForRemainingLetters(const Round &iRound) const; int evalForExtensions(const Round &iRound) const; }; diff --git a/game/results.cpp b/game/results.cpp index 204f5de..ee4f521 100644 --- a/game/results.cpp +++ b/game/results.cpp @@ -326,7 +326,7 @@ void MasterResults::search(const Dictionary &iDic, const Board &iBoard, return; // Find the best round, according to the heuristics in MoveSelector - MoveSelector selector(m_bag, iDic); + MoveSelector selector(m_bag, iDic, iBoard, iRack); const Round &round = selector.selectMaster(m_bestResults); m_rounds.push_back(round); }