summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--alphabetparameters.cpp6
-rw-r--r--board.cpp4
-rw-r--r--board.h25
-rw-r--r--data/lexica/copyrights.txt2
-rw-r--r--data/lexica/csw12.dawgbin1600543 -> 1600616 bytes
-rw-r--r--data/lexica/csw12.gaddagbin25846356 -> 0 bytes
-rw-r--r--data/lexica/csw15.dawgbin0 -> 1459769 bytes
-rw-r--r--data/lexica/cswapr07.dawgbin2641009 -> 2641082 bytes
-rw-r--r--data/lexica/greek.dawgbin1248807 -> 1248900 bytes
-rw-r--r--data/lexica/korean.dawgbin1014944 -> 1015145 bytes
-rw-r--r--data/lexica/norwegian.dawgbin1701427 -> 1701518 bytes
-rw-r--r--data/lexica/ods5.dawgbin899171 -> 899244 bytes
-rw-r--r--data/lexica/osps.dawgbin2570477 -> 2570571 bytes
-rw-r--r--data/lexica/russian.dawgbin240528 -> 0 bytes
-rw-r--r--data/lexica/sowpods.dawgbin1473486 -> 1473559 bytes
-rw-r--r--data/lexica/tuvan.dawgbin15492 -> 0 bytes
-rw-r--r--data/lexica/twl06.dawgbin1816584 -> 1816657 bytes
-rw-r--r--data/lexica/twl06.gaddagbin16811716 -> 0 bytes
-rw-r--r--data/lexica/twl06_wild.dawgbin6215056 -> 0 bytes
-rw-r--r--data/lexica/twl98.dawgbin938070 -> 938143 bytes
-rw-r--r--datamanager.cpp48
-rw-r--r--datamanager.h22
-rw-r--r--encodeleaves/encodeleaves.pro10
-rw-r--r--fixedstring.h9
-rw-r--r--gaddagize/.gitignore1
-rw-r--r--generator.cpp81
-rw-r--r--generator.h46
-rw-r--r--installer.iss4
-rw-r--r--lexiconparameters.cpp200
-rw-r--r--lexiconparameters.h83
-rw-r--r--makegaddag/.gitignore2
-rw-r--r--makegaddag/makegaddag.cpp202
-rwxr-xr-xmakegaddag/makegaddag.pro10
-rw-r--r--makeminidawg/makeminidawg.cpp514
-rw-r--r--makeminidawg/makeminidawg.pro14
-rw-r--r--makeminidawg/makeminidawgmain.cpp126
-rw-r--r--makeminidawg/minidawgmaker.cpp516
-rw-r--r--makeminidawg/minidawgmaker.h37
-rw-r--r--quacker/Info.plist2
-rw-r--r--quacker/boardsetupdialog.cpp23
-rw-r--r--quacker/boardsetupdialog.h2
-rw-r--r--quacker/graphicalreporter.cpp1
-rw-r--r--quacker/lexicondialog.cpp306
-rw-r--r--quacker/lexicondialog.h86
-rw-r--r--quacker/lister.cpp3
-rw-r--r--quacker/quacker.cpp36
-rw-r--r--quacker/quacker.plist2
-rw-r--r--quacker/quacker.pro17
-rw-r--r--quacker/settings.cpp380
-rw-r--r--quacker/settings.h32
-rw-r--r--quackle.pro2
-rw-r--r--quackle.sublime-project7
-rw-r--r--quackleio/dawgfactory.cpp326
-rw-r--r--quackleio/dawgfactory.h94
-rw-r--r--quackleio/flexiblealphabet.h2
-rw-r--r--quackleio/gaddagfactory.cpp200
-rw-r--r--quackleio/gaddagfactory.h74
-rw-r--r--quackleio/iotest/iotest.pro10
-rw-r--r--quackleio/quackleio.pro2
-rw-r--r--quackletest.cpp6
-rw-r--r--test/test.pro10
-rw-r--r--test/testharness.cpp10
63 files changed, 1966 insertions, 1630 deletions
diff --git a/.gitignore b/.gitignore
index a345851..43da009 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ ChangeLog
dawginput.raw
playabilities.raw
smaller.raw
+DerivedData
diff --git a/alphabetparameters.cpp b/alphabetparameters.cpp
index 851d8ff..4830313 100644
--- a/alphabetparameters.cpp
+++ b/alphabetparameters.cpp
@@ -214,7 +214,7 @@ LetterString AlphabetParameters::encode(const UVString &word, UVString *leftover
if (lookupIt == lookupEnd)
{
for (alphabetIt = m_alphabet.begin(); alphabetIt != alphabetEnd; ++alphabetIt)
- if ((*alphabetIt).blankText() == query)
+ if (alphabetIt->blankText() == query)
break;
if (alphabetIt == alphabetEnd)
@@ -224,7 +224,7 @@ LetterString AlphabetParameters::encode(const UVString &word, UVString *leftover
}
blank = true;
- ret += blank? setBlankness((*alphabetIt).letter()) : (*alphabetIt).letter();
+ ret += blank? setBlankness(alphabetIt->letter()) : alphabetIt->letter();
} else {
ret += blank? setBlankness(m_alphabet[lookupIt->second].letter()) : m_alphabet[lookupIt->second].letter();
}
@@ -241,7 +241,7 @@ LetterString AlphabetParameters::encode(const UVString &word, UVString *leftover
string AlphabetParameters::findAlphabetFile(const string &alphabet)
{
- return DataManager::self()->findDataFile("alphabets", alphabet);
+ return DataManager::self()->findDataFile("alphabets", alphabet + ".quackle_alphabet");
}
////////
diff --git a/board.cpp b/board.cpp
index bf60e84..452501b 100644
--- a/board.cpp
+++ b/board.cpp
@@ -744,8 +744,8 @@ void Board::prepareEmptyBoard()
{
m_letters[i][j] = QUACKLE_NULL_MARK;
m_isBlank[i][j] = false;
- m_vcross[i][j] = Utility::Max;
- m_hcross[i][j] = Utility::Max;
+ m_vcross[i][j].set();
+ m_hcross[i][j].set();
}
}
}
diff --git a/board.h b/board.h
index 30c6dcf..ce5aa8f 100644
--- a/board.h
+++ b/board.h
@@ -20,6 +20,7 @@
#define QUACKLE_BOARD_H
#include <vector>
+#include <bitset>
#include "alphabetparameters.h"
#include "bag.h"
@@ -27,7 +28,9 @@
#include "rack.h"
using namespace std;
-
+
+typedef bitset<QUACKLE_MAXIMUM_ALPHABET_SIZE> LetterBitset;
+
#define QUACKLE_MAXIMUM_BOARD_SIZE LETTER_STRING_MAXIMUM_LENGTH
#define QUACKLE_MINIMUM_BOARD_SIZE 7
@@ -122,11 +125,11 @@ public:
bool isBlank(int row, int col) const;
bool isBritish(int row, int col) const;
- int vcross(int row, int col) const;
- void setVCross(int row, int col, int vcross);
+ const LetterBitset &vcross(int row, int col) const;
+ void setVCross(int row, int col, const LetterBitset &vcross);
- int hcross(int row, int col) const;
- void setHCross(int row, int col, int hcross);
+ const LetterBitset &hcross(int row, int col) const;
+ void setHCross(int row, int col, const LetterBitset &hcross);
protected:
int m_width;
@@ -137,8 +140,8 @@ protected:
bool m_isBlank[QUACKLE_MAXIMUM_BOARD_SIZE][QUACKLE_MAXIMUM_BOARD_SIZE];
bool m_isBritish[QUACKLE_MAXIMUM_BOARD_SIZE][QUACKLE_MAXIMUM_BOARD_SIZE];
- int m_vcross[QUACKLE_MAXIMUM_BOARD_SIZE][QUACKLE_MAXIMUM_BOARD_SIZE];
- int m_hcross[QUACKLE_MAXIMUM_BOARD_SIZE][QUACKLE_MAXIMUM_BOARD_SIZE];
+ LetterBitset m_vcross[QUACKLE_MAXIMUM_BOARD_SIZE][QUACKLE_MAXIMUM_BOARD_SIZE];
+ LetterBitset m_hcross[QUACKLE_MAXIMUM_BOARD_SIZE][QUACKLE_MAXIMUM_BOARD_SIZE];
inline bool isNonempty(int row, int column) const;
};
@@ -163,22 +166,22 @@ inline bool Board::isBritish(int row, int col) const
return m_isBritish[row][col];
}
-inline int Board::vcross(int row, int col) const
+inline const LetterBitset &Board::vcross(int row, int col) const
{
return m_vcross[row][col];
}
-inline void Board::setVCross(int row, int col, int vcross)
+inline void Board::setVCross(int row, int col, const LetterBitset &vcross)
{
m_vcross[row][col] = vcross;
}
-inline int Board::hcross(int row, int col) const
+inline const LetterBitset &Board::hcross(int row, int col) const
{
return m_hcross[row][col];
}
-inline void Board::setHCross(int row, int col, int hcross)
+inline void Board::setHCross(int row, int col, const LetterBitset &hcross)
{
m_hcross[row][col] = hcross;
}
diff --git a/data/lexica/copyrights.txt b/data/lexica/copyrights.txt
new file mode 100644
index 0000000..1e3e553
--- /dev/null
+++ b/data/lexica/copyrights.txt
@@ -0,0 +1,2 @@
+eea8dfe5:Collins Scrabble™ Words 2012, ©HarperCollins Publishers Ltd 2015
+48dea2c8:Collins Scrabble™ Words 2015, ©HarperCollins Publishers Ltd 2015
diff --git a/data/lexica/csw12.dawg b/data/lexica/csw12.dawg
index f98ef8b..822bd95 100644
--- a/data/lexica/csw12.dawg
+++ b/data/lexica/csw12.dawg
Binary files differ
diff --git a/data/lexica/csw12.gaddag b/data/lexica/csw12.gaddag
deleted file mode 100644
index 511c99f..0000000
--- a/data/lexica/csw12.gaddag
+++ /dev/null
Binary files differ
diff --git a/data/lexica/csw15.dawg b/data/lexica/csw15.dawg
new file mode 100644
index 0000000..da4defa
--- /dev/null
+++ b/data/lexica/csw15.dawg
Binary files differ
diff --git a/data/lexica/cswapr07.dawg b/data/lexica/cswapr07.dawg
index a2863ac..6e6dca7 100644
--- a/data/lexica/cswapr07.dawg
+++ b/data/lexica/cswapr07.dawg
Binary files differ
diff --git a/data/lexica/greek.dawg b/data/lexica/greek.dawg
index 90835e5..b8666ec 100644
--- a/data/lexica/greek.dawg
+++ b/data/lexica/greek.dawg
Binary files differ
diff --git a/data/lexica/korean.dawg b/data/lexica/korean.dawg
index 6aa5d3e..c55769e 100644
--- a/data/lexica/korean.dawg
+++ b/data/lexica/korean.dawg
Binary files differ
diff --git a/data/lexica/norwegian.dawg b/data/lexica/norwegian.dawg
index 0acdb33..2db2269 100644
--- a/data/lexica/norwegian.dawg
+++ b/data/lexica/norwegian.dawg
Binary files differ
diff --git a/data/lexica/ods5.dawg b/data/lexica/ods5.dawg
index eabc68a..8d440d9 100644
--- a/data/lexica/ods5.dawg
+++ b/data/lexica/ods5.dawg
Binary files differ
diff --git a/data/lexica/osps.dawg b/data/lexica/osps.dawg
index 6c02ecd..6d81d3d 100644
--- a/data/lexica/osps.dawg
+++ b/data/lexica/osps.dawg
Binary files differ
diff --git a/data/lexica/russian.dawg b/data/lexica/russian.dawg
deleted file mode 100644
index 3b01c8e..0000000
--- a/data/lexica/russian.dawg
+++ /dev/null
Binary files differ
diff --git a/data/lexica/sowpods.dawg b/data/lexica/sowpods.dawg
index b5d89df..e052222 100644
--- a/data/lexica/sowpods.dawg
+++ b/data/lexica/sowpods.dawg
Binary files differ
diff --git a/data/lexica/tuvan.dawg b/data/lexica/tuvan.dawg
deleted file mode 100644
index f5f31b3..0000000
--- a/data/lexica/tuvan.dawg
+++ /dev/null
Binary files differ
diff --git a/data/lexica/twl06.dawg b/data/lexica/twl06.dawg
index 9388487..e0afed7 100644
--- a/data/lexica/twl06.dawg
+++ b/data/lexica/twl06.dawg
Binary files differ
diff --git a/data/lexica/twl06.gaddag b/data/lexica/twl06.gaddag
deleted file mode 100644
index db93e2e..0000000
--- a/data/lexica/twl06.gaddag
+++ /dev/null
Binary files differ
diff --git a/data/lexica/twl06_wild.dawg b/data/lexica/twl06_wild.dawg
deleted file mode 100644
index 34e78a0..0000000
--- a/data/lexica/twl06_wild.dawg
+++ /dev/null
Binary files differ
diff --git a/data/lexica/twl98.dawg b/data/lexica/twl98.dawg
index a835b7c..e6f451e 100644
--- a/data/lexica/twl98.dawg
+++ b/data/lexica/twl98.dawg
Binary files differ
diff --git a/datamanager.cpp b/datamanager.cpp
index e188668..916610a 100644
--- a/datamanager.cpp
+++ b/datamanager.cpp
@@ -41,7 +41,8 @@ DataManager::DataManager()
: m_evaluator(0), m_parameters(0), m_alphabetParameters(0), m_boardParameters(0), m_lexiconParameters(0), m_strategyParameters(0)
{
m_self = this;
- setDataDirectory(".");
+ setAppDataDirectory(".");
+ setUserDataDirectory(".");
seedRandomNumbers((int)time(NULL));
m_alphabetParameters = new EnglishAlphabetParameters;
@@ -122,6 +123,7 @@ void DataManager::cleanupComputerPlayers()
bool DataManager::fileExists(const string &filename)
{
+ // fixme: convert to wchar
struct stat buf;
int i = stat(filename.c_str(), &buf);
if (i == 0)
@@ -130,36 +132,44 @@ bool DataManager::fileExists(const string &filename)
return false;
}
-string DataManager::findDataFile(const string &subDirectory, const string &lexicon, string file)
+string DataManager::findDataFile(const string &subDirectory, const string &lexicon, const string &file)
{
- string firstTry = makeDataFilename(subDirectory, lexicon, file);
- if (fileExists(firstTry))
- return firstTry;
-
- string secondTry = makeDataFilename(subDirectory, m_backupLexicon, file);
- if (fileExists(secondTry))
- return secondTry;
+ string fname = makeDataFilename(subDirectory, lexicon, file, true);
+ if (!fileExists(fname))
+ fname = makeDataFilename(subDirectory, lexicon, file, false);
+ if (!fileExists(fname))
+ fname = makeDataFilename(subDirectory, m_backupLexicon, file, false);
+ if (!fileExists(fname))
+ fname = string();
- return string();
+ return fname;
}
-string DataManager::findDataFile(const string &subDirectory, string file)
+string DataManager::findDataFile(const string &subDirectory, const string &file)
{
- string firstTry = makeDataFilename(subDirectory, file);
- if (fileExists(firstTry))
- return firstTry;
+ string fname = makeDataFilename(subDirectory, file, true);
+ if (!fileExists(fname))
+ fname = makeDataFilename(subDirectory, file, false);
+ if (!fileExists(fname))
+ fname = string();
+
+ return fname;
+}
- return string();
+bool DataManager::hasUserDataFile(const string &subDirectory, const string &file)
+{
+ string fname = makeDataFilename(subDirectory, file, true);
+ return fileExists(fname);
}
-string DataManager::makeDataFilename(const string &subDirectory, const string &lexicon, string file)
+string DataManager::makeDataFilename(const string &subDirectory, const string &lexicon, const string &file, bool user)
{
- return m_dataDirectory + "/" + subDirectory + "/" + lexicon + "/" + file;
+ return (user ? m_userDataDirectory : m_appDataDirectory) + "/" + subDirectory + "/" + lexicon + "/" + file;
}
-string DataManager::makeDataFilename(const string &subDirectory, string file)
+string DataManager::makeDataFilename(const string &subDirectory, const string &file, bool user)
{
- return m_dataDirectory + "/" + subDirectory + "/" + file;
+ return (user ? m_userDataDirectory : m_appDataDirectory) + "/" + subDirectory + "/" + file;
}
void DataManager::seedRandomNumbers(unsigned int seed)
diff --git a/datamanager.h b/datamanager.h
index 15e793a..75bce54 100644
--- a/datamanager.h
+++ b/datamanager.h
@@ -99,21 +99,27 @@ public:
// Find a file at datadir/subdir/lexicon/file.
// If this doesn't exist, tries backupLexicon instead of lexicon.
// Returns empty string if the file is not found.
- string findDataFile(const string &subDirectory, const string &lexicon, string file);
+ string findDataFile(const string &subDirectory, const string &lexicon, const string &file);
// Find a file at datadir/subdir/file.
// Returns empty string if the file is not found.
- string findDataFile(const string &subDirectory, string file);
+ string findDataFile(const string &subDirectory, const string &file);
+
+ // Returns true if the data file is in user-land.
+ bool hasUserDataFile(const string &subDirectory, const string &file);
// returns similarly-named file
- string makeDataFilename(const string &subDirectory, const string &lexicon, string file);
- string makeDataFilename(const string &subDirectory, string file);
+ string makeDataFilename(const string &subDirectory, const string &lexicon, const string &file, bool user);
+ string makeDataFilename(const string &subDirectory, const string &file, bool user);
void setBackupLexicon(string backupLexicon) { m_backupLexicon = backupLexicon; }
string backupLexicon() { return m_backupLexicon; }
- void setDataDirectory(string directory) { m_dataDirectory = directory; }
- string dataDirectory() { return m_dataDirectory; }
+ void setAppDataDirectory(string directory) { m_appDataDirectory = directory; }
+ string appDataDirectory() { return m_appDataDirectory; }
+
+ void setUserDataDirectory(string directory) { m_userDataDirectory = directory; }
+ string userDataDirectory() { return m_userDataDirectory; }
void seedRandomNumbers(unsigned int seed);
int randomNumber();
@@ -123,7 +129,9 @@ private:
bool fileExists(const string &filename);
- string m_dataDirectory;
+ string m_appDataDirectory;
+
+ string m_userDataDirectory;
// lexicon that has all data files
string m_backupLexicon;
diff --git a/encodeleaves/encodeleaves.pro b/encodeleaves/encodeleaves.pro
index f1a51bb..559df28 100644
--- a/encodeleaves/encodeleaves.pro
+++ b/encodeleaves/encodeleaves.pro
@@ -11,16 +11,20 @@ CONFIG -= app_bundle
debug {
OBJECTS_DIR = obj/debug
+ QMAKE_LIBDIR += ../lib/debug ../quackleio/lib/debug
}
release {
OBJECTS_DIR = obj/release
+ QMAKE_LIBDIR += ../lib/release ../quackleio/lib/release
}
-LIBS += -lquackleio -lquackle
+win32:!win32-g++ {
+ LIBS += -lquackleio -llibquackle
+} else {
+ LIBS += -lquackleio -lquackle
+}
-QMAKE_LFLAGS_RELEASE += -L../lib/release -L../quackleio/lib/release
-QMAKE_LFLAGS_DEBUG += -L../lib/debug -L../quackleio/lib/debug
# Input
SOURCES += encodeleaves.cpp
diff --git a/fixedstring.h b/fixedstring.h
index 46d1011..a31ecd6 100644
--- a/fixedstring.h
+++ b/fixedstring.h
@@ -54,6 +54,8 @@ class FixedLengthString
size_type size() const { return length(); }
void clear() { m_end = m_data; }
void push_back(char c);
+ void pop_back();
+ const char* constData() const { return m_data; }
int compare(const FixedLengthString& s) const;
@@ -220,6 +222,13 @@ FixedLengthString::push_back(char c)
*this += c;
}
+inline void
+FixedLengthString::pop_back()
+{
+ assert(size() > 0);
+ m_end--;
+}
+
inline int
FixedLengthString::compare(const FixedLengthString& s) const
{
diff --git a/gaddagize/.gitignore b/gaddagize/.gitignore
index f878785..3c70113 100644
--- a/gaddagize/.gitignore
+++ b/gaddagize/.gitignore
@@ -4,3 +4,4 @@ Makefile.Debug
Makefile.Release
debug
release
+gaddagize
diff --git a/generator.cpp b/generator.cpp
index 2bfc199..d20b320 100644
--- a/generator.cpp
+++ b/generator.cpp
@@ -132,7 +132,7 @@ void Generator::allCrosses()
int col = vcols[i];
if (QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col))) {
- board().setVCross(row, col, 0);
+ board().setVCross(row, col, LetterBitset());
}
else {
LetterString pre;
@@ -167,7 +167,7 @@ void Generator::allCrosses()
#endif
if (pre.empty() && suf.empty()) {
- board().setVCross(row, col, Utility::Max);
+ board().setVCross(row, col, LetterBitset().set());
}
else {
board().setVCross(row, col, fitbetween(pre, suf));
@@ -184,7 +184,7 @@ void Generator::allCrosses()
int col = hcols[i];
if (QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col))) {
- board().setHCross(row, col, 0);
+ board().setHCross(row, col, LetterBitset());
}
else {
LetterString pre;
@@ -214,7 +214,7 @@ void Generator::allCrosses()
}
}
if (pre.empty() && suf.empty()) {
- board().setHCross(row, col, Utility::Max);
+ board().setHCross(row, col, LetterBitset().set());
}
else {
board().setHCross(row, col, fitbetween(pre, suf));
@@ -340,7 +340,7 @@ void Generator::makeMove(const Move &move, bool regenerateCrosses)
int row = vrows[i];
int col = vcols[i];
if (QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col))) {
- board().setVCross(row, col, 0);
+ board().setVCross(row, col, LetterBitset());
}
else {
LetterString pre;
@@ -374,7 +374,7 @@ void Generator::makeMove(const Move &move, bool regenerateCrosses)
#endif
if ((pre.empty()) && (suf.empty())) {
- board().setVCross(row, col, Utility::Max);
+ board().setVCross(row, col, LetterBitset().set());
}
else {
board().setVCross(row, col, fitbetween(pre, suf));
@@ -391,7 +391,7 @@ void Generator::makeMove(const Move &move, bool regenerateCrosses)
int col = hcols[i];
if (QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col))) {
- board().setHCross(row, col, 0);
+ board().setHCross(row, col, LetterBitset());
}
else {
LetterString pre;
@@ -425,7 +425,7 @@ void Generator::makeMove(const Move &move, bool regenerateCrosses)
#endif
if ((pre.empty()) && (suf.empty())) {
- board().setHCross(row, col, Utility::Max);
+ board().setHCross(row, col, LetterBitset().set());
}
else {
board().setHCross(row, col, fitbetween(pre, suf));
@@ -440,16 +440,7 @@ void Generator::makeMove(const Move &move, bool regenerateCrosses)
void Generator::readFromDawg(int index, unsigned int &p, Letter &letter, bool &t, bool &lastchild, bool &british, int &playability) const
{
- index *= 7;
- p = (QUACKLE_LEXICON_PARAMETERS->dawgAt(index) << 16) + (QUACKLE_LEXICON_PARAMETERS->dawgAt(index + 1) << 8) + (QUACKLE_LEXICON_PARAMETERS->dawgAt(index + 2));
- letter = QUACKLE_LEXICON_PARAMETERS->dawgAt(index + 3);
-
- t = (letter & 32) != 0;
- lastchild = (letter & 64) != 0;
- british = !(letter & 128);
- letter = (letter & 31) + QUACKLE_FIRST_LETTER;
-
- playability = (QUACKLE_LEXICON_PARAMETERS->dawgAt(index + 4) << 16) + (QUACKLE_LEXICON_PARAMETERS->dawgAt(index + 5) << 8) + (QUACKLE_LEXICON_PARAMETERS->dawgAt(index + 6));
+ QUACKLE_LEXICON_PARAMETERS->dawgAt(index, p, letter, t, lastchild, british, playability);
}
bool Generator::checksuffix(int i, const LetterString &suffix) {
@@ -495,13 +486,13 @@ bool Generator::checksuffix(int i, const LetterString &suffix) {
}
}
-int Generator::gaddagFitbetween(const LetterString &pre, const LetterString &suf)
+LetterBitset Generator::gaddagFitbetween(const LetterString &pre, const LetterString &suf)
{
// UVcout << "fit "
// << QUACKLE_ALPHABET_PARAMETERS->userVisible(pre)
// << "_"
// << QUACKLE_ALPHABET_PARAMETERS->userVisible(suf) << endl;
- int crosses = 0;
+ LetterBitset crosses;
/* process the suffix once */
const GaddagNode *sufNode = QUACKLE_LEXICON_PARAMETERS->gaddagRoot();
int sufLen = suf.length();
@@ -527,13 +518,13 @@ int Generator::gaddagFitbetween(const LetterString &pre, const LetterString &suf
}
if (n && n->isTerminal()) {
- crosses |= Utility::Bits[(int)(childLetter - QUACKLE_FIRST_LETTER)];
+ crosses.set(childLetter - QUACKLE_FIRST_LETTER);
}
}
return crosses;
}
-int Generator::fitbetween(const LetterString &pre, const LetterString &suf)
+LetterBitset Generator::fitbetween(const LetterString &pre, const LetterString &suf)
{
if (QUACKLE_LEXICON_PARAMETERS->hasGaddag()) {
return gaddagFitbetween(pre, suf);
@@ -542,7 +533,7 @@ int Generator::fitbetween(const LetterString &pre, const LetterString &suf)
//UVcout << QUACKLE_ALPHABET_PARAMETERS->userVisible(pre) << "_" <<
// QUACKLE_ALPHABET_PARAMETERS->userVisible(suf) << endl;
- int crosses = 0;
+ LetterBitset crosses;
for (Letter c = QUACKLE_FIRST_LETTER; c <= QUACKLE_ALPHABET_PARAMETERS->lastLetter(); c++) {
/*
@@ -553,7 +544,7 @@ int Generator::fitbetween(const LetterString &pre, const LetterString &suf)
*/
if (checksuffix(1, pre + c + suf)) {
// subtract first letter because crosses hold values starting from zero
- crosses |= Utility::Bits[c - QUACKLE_FIRST_LETTER];
+ crosses.set(c - QUACKLE_FIRST_LETTER);
//UVcout << " that's a word" << endl;
// UVcout << c;
}
@@ -574,12 +565,12 @@ UVString Generator::counts2string()
return ret;
}
-UVString Generator::cross2string(int cross)
+UVString Generator::cross2string(const LetterBitset &cross)
{
UVString ret;
for (int i = 0; i < QUACKLE_ALPHABET_PARAMETERS->length(); i++)
- if (cross & Utility::Bits[i])
+ if (cross.test(i))
ret += QUACKLE_ALPHABET_PARAMETERS->userVisible(QUACKLE_FIRST_LETTER + i);
return ret;
@@ -820,7 +811,7 @@ void Generator::gordongen(int pos, const LetterString &word, const GaddagNode *n
currow += pos;
}
- int cross;
+ LetterBitset cross;
if (m_gordonhoriz) {
cross = board().vcross(currow, curcol);
}
@@ -844,7 +835,7 @@ void Generator::gordongen(int pos, const LetterString &word, const GaddagNode *n
Letter childLetter = child->letter();
if ((m_counts[childLetter] <= 0)
- || !(cross & Utility::Bits[(int)(childLetter - QUACKLE_FIRST_LETTER)])) {
+ || !cross.test(childLetter - QUACKLE_FIRST_LETTER)) {
continue;
}
@@ -871,7 +862,7 @@ void Generator::gordongen(int pos, const LetterString &word, const GaddagNode *n
break;
}
- if (cross & Utility::Bits[(int)(childLetter - QUACKLE_FIRST_LETTER)]) {
+ if (cross.test(childLetter - QUACKLE_FIRST_LETTER)) {
m_counts[QUACKLE_BLANK_MARK]--;
m_laid++;
// UVcout << " yeah that'll work" << endl;
@@ -926,14 +917,14 @@ void Generator::extendright(const LetterString &partial, int i,
if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(rowpos, colpos))) {
if (m_counts[c] >= 1) {
- int cross;
+ LetterBitset cross;
if (horizontal) {
cross = board().vcross(rowpos, colpos);
}
else {
cross = board().hcross(rowpos, colpos);
}
- if (cross & Utility::Bits[(int)(c - QUACKLE_FIRST_LETTER)]) {
+ if (cross.test(c - QUACKLE_FIRST_LETTER)) {
if (t) {
bool couldend = true;
if (dirpos < edgeDirpos) {
@@ -962,7 +953,7 @@ void Generator::extendright(const LetterString &partial, int i,
int laid = move.wordTilesWithNoPlayThru().length();
bool onetilevert = (!move.horizontal) && (laid == 1);
- bool ignore = onetilevert && (board().hcross(row, col) != Utility::Max);
+ bool ignore = onetilevert && !board().hcross(row, col).all();
if (1 || !ignore)
{
@@ -991,14 +982,14 @@ void Generator::extendright(const LetterString &partial, int i,
}
}
if ((m_counts[QUACKLE_BLANK_MARK] >= 1)) {
- int cross;
+ LetterBitset cross;
if (horizontal) {
cross = board().vcross(rowpos, colpos);
}
else {
cross = board().hcross(rowpos, colpos);
}
- if (cross & Utility::Bits[(int)(c - QUACKLE_FIRST_LETTER)]) {
+ if (cross.test(c - QUACKLE_FIRST_LETTER)) {
if (t) {
bool couldend = true;
if (dirpos < edgeDirpos) {
@@ -1024,7 +1015,7 @@ void Generator::extendright(const LetterString &partial, int i,
int laid = move.wordTilesWithNoPlayThru().length();
bool onetilevert = (!move.horizontal) && (laid == 1);
- bool ignore = onetilevert && (board().hcross(row, col) != Utility::Max);
+ bool ignore = onetilevert && !board().hcross(row, col).all();
if (1 || !ignore)
{
@@ -1115,7 +1106,7 @@ void Generator::extendright(const LetterString &partial, int i,
int laid = move.wordTilesWithNoPlayThru().length();
bool onetilevert = (!move.horizontal) && (laid == 1);
- bool ignore = onetilevert && (board().hcross(row, col) != Utility::Max);
+ bool ignore = onetilevert && !board().hcross(row, col).all();
if (1 || !ignore)
{
@@ -1215,7 +1206,7 @@ Move Generator::generate()
// what defines an anchor square?
bool anchor = false;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && (board().vcross(row, col) != Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && !board().vcross(row, col).all()) {
if (col == 0) {
anchor = true;
}
@@ -1238,7 +1229,7 @@ Move Generator::generate()
{
// UVcout << "board().vcross[" << row << "][" << i << "] = " << board().vcross(row, i) << endl;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, i)) && (board().vcross(row, i) == Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, i)) && board().vcross(row, i).all()) {
if (i == 0) {
k++;
}
@@ -1265,7 +1256,7 @@ Move Generator::generate()
// what defines an anchor square?
anchor = false;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && (board().hcross(row, col) != Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && !board().hcross(row, col).all()) {
if (row == 0) {
anchor = true;
}
@@ -1286,7 +1277,7 @@ Move Generator::generate()
int k = 0;
for (int i = row - 1; i >= 0; i--) {
// UVcout << "board().vcross[" << row << "][" << i << "] = " << board().vcross(row, i) << endl;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(i, col)) && (board().hcross(i, col) == Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(i, col)) && board().hcross(i, col).all()) {
if (i == 0) {
k++;
}
@@ -1322,7 +1313,7 @@ Move Generator::gordongenerate()
// generate horizontal plays
// what defines an anchor square?
bool anchor = false;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && (board().vcross(row, col) != Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && !board().vcross(row, col).all()) {
if (col == 0) {
if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col + 1))) {
anchor = true;
@@ -1359,7 +1350,7 @@ Move Generator::gordongenerate()
}
for (int i = col - k - 1; i >= 0; i--) {
// UVcout << "board().vcross[" << row << "][" << i << "] = " << board().vcross(row, i) << endl;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, i)) && (board().vcross(row, i) == Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, i)) && board().vcross(row, i).all()) {
if (i == 0) {
k++;
}
@@ -1389,7 +1380,7 @@ Move Generator::gordongenerate()
// what defines an anchor square?
anchor = false;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && (board().hcross(row, col) != Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row, col)) && !board().hcross(row, col).all()) {
if (row == 0) {
if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(row + 1, col))) {
anchor = true;
@@ -1426,7 +1417,7 @@ Move Generator::gordongenerate()
}
for (int i = row - k - 1; i >= 0; i--) {
// UVcout << "board().vcross[" << row << "][" << i << "] = " << board().vcross(row, i) << endl;
- if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(i, col)) && (board().hcross(i, col) == Utility::Max)) {
+ if (!QUACKLE_ALPHABET_PARAMETERS->isSomeLetter(board().letter(i, col)) && board().hcross(i, col).all()) {
if (i == 0) {
k++;
}
@@ -1618,7 +1609,7 @@ Move Generator::exchange()
{
LetterString thrown;
for (int j = 0; j < rackSize; j++)
- if (i & Utility::Bits[j])
+ if (i & (1 << j))
thrown += rack().tiles()[j];
Move move;
diff --git a/generator.h b/generator.h
index 70908b1..6d207e0 100644
--- a/generator.h
+++ b/generator.h
@@ -125,7 +125,7 @@ private:
void readFromDawg(int index, unsigned int &p, Letter &letter, bool &t, bool &lastchild, bool &british, int &playability) const;
bool checksuffix(int i, const LetterString &suffix);
- int fitbetween(const LetterString &pre, const LetterString &suf);
+ LetterBitset fitbetween(const LetterString &pre, const LetterString &suf);
void extendright(const LetterString &partial, int i,
int row, int col, int edge, int righttiles,
bool horizontal);
@@ -134,7 +134,7 @@ private:
void spit(int i, const LetterString &prefix, int flags);
void wordspit(int i, const LetterString &prefix, int flags);
- int gaddagFitbetween(const LetterString &pre, const LetterString &suf);
+ LetterBitset gaddagFitbetween(const LetterString &pre, const LetterString &suf);
void gaddagAnagram(const GaddagNode *node, const LetterString &prefix, int flags);
void gordongen(int pos, const LetterString &word, const GaddagNode *node);
void gordongoon(int pos, char L, LetterString word, const GaddagNode *node);
@@ -143,7 +143,7 @@ private:
// debug stuff
UVString counts2string();
- UVString cross2string(int cross);
+ UVString cross2string(const LetterBitset &cross);
Move best;
@@ -167,46 +167,6 @@ private:
int m_anchorrow, m_anchorcol;
};
-namespace Utility
-{
- const int Max = 0xFFFFFFFF;
-
- const int Bits[32] =
- {
- 1 << 0,
- 1 << 1,
- 1 << 2,
- 1 << 3,
- 1 << 4,
- 1 << 5,
- 1 << 6,
- 1 << 7,
- 1 << 8,
- 1 << 9,
- 1 << 10,
- 1 << 11,
- 1 << 12,
- 1 << 13,
- 1 << 14,
- 1 << 15,
- 1 << 16,
- 1 << 17,
- 1 << 18,
- 1 << 19,
- 1 << 20,
- 1 << 21,
- 1 << 22,
- 1 << 23,
- 1 << 24,
- 1 << 25,
- 1 << 26,
- 1 << 27,
- 1 << 28,
- 1 << 29,
- 1 << 30,
- 1 << 31
- };
-};
inline void Generator::setPosition(const GamePosition &position)
{
diff --git a/installer.iss b/installer.iss
index c022434..23e770f 100644
--- a/installer.iss
+++ b/installer.iss
@@ -3,14 +3,14 @@
[Setup]
AppName=Quackle
-AppVerName=Quackle 0.98
+AppVerName=Quackle 1.0
DefaultDirName={pf}\Quackle
DefaultGroupName=Quackle
ChangesAssociations=yes
UninstallDisplayIcon={app}\Quackle.exe
[Icons]
-Name: "{group}\Quackle 0.98"; Filename: "{app}\Quackle.exe"; WorkingDir: "{app}"
+Name: "{group}\Quackle 1.0"; Filename: "{app}\Quackle.exe"; WorkingDir: "{app}"
[Registry]
Root: HKCU; Subkey: "Software\Quackle.org"
diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp
index b32f632..f04a941 100644
--- a/lexiconparameters.cpp
+++ b/lexiconparameters.cpp
@@ -19,15 +19,122 @@
#include <iostream>
#include <fstream>
+
#include "datamanager.h"
#include "lexiconparameters.h"
#include "uv.h"
using namespace Quackle;
+class Quackle::V0LexiconInterpreter : public LexiconInterpreter
+{
+
+ virtual void loadDawg(ifstream &file, LexiconParameters &lexparams)
+ {
+ int i = 0;
+ while (!file.eof())
+ {
+ file.read((char*)(lexparams.m_dawg) + i, 7);
+ i += 7;
+ }
+ }
+
+ virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams)
+ {
+ int i = 0;
+ while (!file.eof())
+ {
+ file.read((char*)(lexparams.m_gaddag) + i, 4);
+ i += 4;
+ }
+ }
+
+ virtual void dawgAt(const unsigned char *dawg, int index, unsigned int &p, Letter &letter, bool &t, bool &lastchild, bool &british, int &playability) const
+ {
+ index *= 7;
+ p = (dawg[index] << 16) + (dawg[index + 1] << 8) + (dawg[index + 2]);
+ letter = dawg[index + 3];
+
+ t = (letter & 32) != 0;
+ lastchild = (letter & 64) != 0;
+ british = !(letter & 128);
+ letter = (letter & 31) + QUACKLE_FIRST_LETTER;
+
+ playability = (dawg[index + 4] << 16) + (dawg[index + 5] << 8) + (dawg[index + 6]);
+ }
+ virtual int versionNumber() const { return 0; }
+};
+
+class Quackle::V1LexiconInterpreter : public LexiconInterpreter
+{
+
+ virtual void loadDawg(ifstream &file, LexiconParameters &lexparams)
+ {
+ int i = 0;
+ unsigned char bytes[3];
+ file.get(); // skip past version byte
+ file.read(lexparams.m_hash, sizeof(lexparams.m_hash));
+ file.read((char*)bytes, 3);
+
+ lexparams.m_utf8Alphabet.resize(file.get());
+ for (size_t i = 0; i < lexparams.m_utf8Alphabet.size(); i++)
+ {
+ file >> lexparams.m_utf8Alphabet[i];
+ file.get(); // separator space
+ }
+ while (!file.eof())
+ {
+ file.read((char*)(lexparams.m_dawg) + i, 7);
+ i += 7;
+ }
+ }
+
+ virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams)
+ {
+ char hash[16];
+ file.get(); // skip past version byte
+ file.read(hash, sizeof(hash));
+ if (memcmp(hash, lexparams.m_hash, sizeof(hash)))
+ {
+ // If we're using a v0 DAWG, then ignore the hash
+ for (size_t i = 0; i < sizeof(lexparams.m_hash); i++)
+ {
+ if (lexparams.m_hash[0] != 0)
+ {
+ lexparams.unloadGaddag(); // don't use a mismatched gaddag
+ return;
+ }
+ }
+ }
+
+ size_t i = 0;
+ while (!file.eof())
+ {
+ file.read((char*)(lexparams.m_gaddag) + i, 4);
+ i += 4;
+ }
+ }
+
+ virtual void dawgAt(const unsigned char *dawg, int index, unsigned int &p, Letter &letter, bool &t, bool &lastchild, bool &british, int &playability) const
+ {
+ index *= 7;
+ p = (dawg[index] << 16) + (dawg[index + 1] << 8) + (dawg[index + 2]);
+ letter = dawg[index + 3];
+
+ lastchild = ((letter & 64) != 0);
+ british = !(letter & 128);
+ letter = (letter & 63) + QUACKLE_FIRST_LETTER;
+
+ playability = (dawg[index + 4] << 16) + (dawg[index + 5] << 8) + (dawg[index + 6]);
+ t = (playability != 0);
+ }
+ virtual int versionNumber() const { return 1; }
+};
+
LexiconParameters::LexiconParameters()
- : m_dawg(0), m_gaddag(0)
+ : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL)
{
+ memset(m_hash, 0, sizeof(m_hash));
}
LexiconParameters::~LexiconParameters()
@@ -44,13 +151,15 @@ void LexiconParameters::unloadAll()
void LexiconParameters::unloadDawg()
{
delete[] m_dawg;
- m_dawg = 0;
+ m_dawg = NULL;
+ delete m_interpreter;
+ m_interpreter = NULL;
}
void LexiconParameters::unloadGaddag()
{
delete[] m_gaddag;
- m_gaddag = 0;
+ m_gaddag = NULL;
}
void LexiconParameters::loadDawg(const string &filename)
@@ -64,14 +173,19 @@ void LexiconParameters::loadDawg(const string &filename)
return;
}
- m_dawg = new unsigned char[7000000];
-
- int i = 0;
- while (!file.eof())
+ char versionByte = file.get();
+ m_interpreter = createInterpreter(versionByte);
+ if (m_interpreter == NULL)
{
- file.read((char*)(m_dawg) + i, 7);
- i += 7;
+ UVcout << "couldn't open file " << filename.c_str() << endl;
+ return;
}
+
+ file.seekg(0, ios_base::end);
+ m_dawg = new unsigned char[file.tellg()];
+ file.seekg(0, ios_base::beg);
+
+ m_interpreter->loadDawg(file, *this);
}
void LexiconParameters::loadGaddag(const string &filename)
@@ -86,18 +200,74 @@ void LexiconParameters::loadGaddag(const string &filename)
return;
}
- m_gaddag = new unsigned char[40000000];
+ char versionByte = file.get();
+ if (versionByte < m_interpreter->versionNumber())
+ return;
+ file.seekg(0, ios_base::end);
+ m_gaddag = new unsigned char[file.tellg()];
+ file.seekg(0, ios_base::beg);
- int i = 0;
- while (!file.eof())
+ // must create a local interpreter because dawg/gaddag versions might not match
+ LexiconInterpreter* interpreter = createInterpreter(versionByte);
+ if (interpreter != NULL)
{
- file.read((char*)(m_gaddag) + i, 4);
- i += 4;
+ interpreter->loadGaddag(file, *this);
+ delete interpreter;
}
+ else
+ unloadGaddag();
}
string LexiconParameters::findDictionaryFile(const string &lexicon)
{
- return DataManager::self()->findDataFile("lexica", lexicon);
+ return QUACKLE_DATAMANAGER->findDataFile("lexica", lexicon);
+}
+
+bool LexiconParameters::hasUserDictionaryFile(const string &lexicon)
+{
+ return QUACKLE_DATAMANAGER->hasUserDataFile("lexica", lexicon);
+}
+
+string LexiconParameters::hashString(bool shortened) const
+{
+ const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ string hashStr;
+ for (size_t i = 0; i < sizeof(m_hash); i++)
+ {
+ hashStr.push_back(hex[(m_hash[i] & 0xF0) >> 4]);
+ hashStr.push_back(hex[m_hash[i] & 0x0F]);
+ if (shortened && i == 3)
+ break;
+ }
+ return hashStr;
+}
+
+string LexiconParameters::copyrightString() const
+{
+ string copyrightsFilename = QUACKLE_DATAMANAGER->makeDataFilename("lexica", "copyrights.txt", false);
+ fstream copyrightsFile(copyrightsFilename, ios_base::in);
+ while (copyrightsFile.good() && !copyrightsFile.eof())
+ {
+ string line;
+ getline(copyrightsFile, line);
+ if (line.size() < 9 || line.find_first_of(':') != 8)
+ continue;
+ if (hashString(true).compare(line.substr(0,8)) != 0)
+ continue;
+ return line.substr(9, line.size());
+ }
+ return string();
}
+LexiconInterpreter* LexiconParameters::createInterpreter(char version) const
+{
+ switch(version)
+ {
+ case 0:
+ return new V0LexiconInterpreter();
+ case 1:
+ return new V1LexiconInterpreter();
+ default:
+ return NULL;
+ }
+}
diff --git a/lexiconparameters.h b/lexiconparameters.h
index 612c103..d19f695 100644
--- a/lexiconparameters.h
+++ b/lexiconparameters.h
@@ -19,14 +19,33 @@
#ifndef QUACKLE_LEXICONPARAMETERS_H
#define QUACKLE_LEXICONPARAMETERS_H
-#include "alphabetparameters.h"
+#include <vector>
+
#include "gaddag.h"
namespace Quackle
{
+class LexiconParameters;
+
+class LexiconInterpreter
+{
+public:
+ virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) = 0;
+ virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams) = 0;
+ virtual void dawgAt(const unsigned char *dawg, int index, unsigned int &p, Letter &letter, bool &t, bool &lastchild, bool &british, int &playability) const = 0;
+ virtual int versionNumber() const = 0;
+ virtual ~LexiconInterpreter() {};
+};
+
+class V0LexiconInterpreter;
+class V1LexiconInterpreter;
+
class LexiconParameters
{
+ friend class Quackle::V0LexiconInterpreter;
+ friend class Quackle::V1LexiconInterpreter;
+
public:
LexiconParameters();
~LexiconParameters();
@@ -34,68 +53,46 @@ public:
void unloadAll();
// true if we have a dawg or a gaddag
- bool hasSomething() const;
+ bool hasSomething() const { return hasDawg() || hasGaddag(); };
// loadDawg unloads the dawg if filename can't be opened
void loadDawg(const string &filename);
void unloadDawg();
- bool hasDawg() const;
+ bool hasDawg() const { return m_dawg != NULL; };
+ int dawgVersion() const { return m_interpreter->versionNumber(); };
// loadGaddag unloads the gaddag if filename can't be opened
void loadGaddag(const string &filename);
void unloadGaddag();
- bool hasGaddag() const;
+ bool hasGaddag() const { return m_gaddag != NULL; };
// finds a file in the lexica data directory
static string findDictionaryFile(const string &lexicon);
+ static bool hasUserDictionaryFile(const string &lexicon);
// a convenience field; this is unused by libquackle
- string lexiconName() const;
- void setLexiconName(const string &name);
+ string lexiconName() const { return m_lexiconName; };
+ void setLexiconName(const string &name) { m_lexiconName = name; };
- unsigned char dawgAt(int index) const;
- const GaddagNode *gaddagRoot() const;
+ void dawgAt(int index, unsigned int &p, Letter &letter, bool &t, bool &lastchild, bool &british, int &playability) const
+ {
+ m_interpreter->dawgAt(m_dawg, index, p, letter, t, lastchild, british, playability);
+ }
+ const GaddagNode *gaddagRoot() const { return (GaddagNode *) &m_gaddag[0]; };
+
+ string hashString(bool shortened) const;
+ string copyrightString() const;
protected:
unsigned char *m_dawg;
unsigned char *m_gaddag;
string m_lexiconName;
-};
-
-inline bool LexiconParameters::hasSomething() const
-{
- return hasDawg() || hasGaddag();
-}
-
-inline bool LexiconParameters::hasDawg() const
-{
- return m_dawg != NULL;
-}
+ LexiconInterpreter *m_interpreter;
+ char m_hash[16];
+ vector<string> m_utf8Alphabet;
-inline bool LexiconParameters::hasGaddag() const
-{
- return m_gaddag != NULL;
-}
-
-inline unsigned char LexiconParameters::dawgAt(int index) const
-{
- return m_dawg[index];
-}
-
-inline const GaddagNode *LexiconParameters::gaddagRoot() const
-{
- return (GaddagNode *) &m_gaddag[0];
-}
-
-inline string LexiconParameters::lexiconName() const
-{
- return m_lexiconName;
-}
-
-inline void LexiconParameters::setLexiconName(const string &name)
-{
- m_lexiconName = name;
-}
+ LexiconInterpreter* createInterpreter(char version) const;
+};
}
#endif
diff --git a/makegaddag/.gitignore b/makegaddag/.gitignore
index f878785..dbd3941 100644
--- a/makegaddag/.gitignore
+++ b/makegaddag/.gitignore
@@ -4,3 +4,5 @@ Makefile.Debug
Makefile.Release
debug
release
+makegaddag
+
diff --git a/makegaddag/makegaddag.cpp b/makegaddag/makegaddag.cpp
index 8e2ffac..ef2c439 100644
--- a/makegaddag/makegaddag.cpp
+++ b/makegaddag/makegaddag.cpp
@@ -29,79 +29,12 @@
#include <QtCore>
-#include <gaddag.h>
-#include <quackleio/flexiblealphabet.h>
-#include <quackleio/froggetopt.h>
-#include <quackleio/util.h>
+#include "quackleio/froggetopt.h"
+#include "quackleio/gaddagfactory.h"
+#include "quackleio/util.h"
using namespace std;
-class Node {
- public:
- Quackle::Letter c;
- bool t;
- vector<Node> children;
- int pointer;
- bool lastchild;
- void pushword(Quackle::LetterString word);
- void print(Quackle::LetterString prefix);
-};
-
-vector< Node* > nodelist;
-
-void Node::print(Quackle::LetterString prefix) {
- if (t) {
- //UVcout << QUACKLE_ALPHABET_PARAMETERS->userVisible(prefix)) << endl;
- }
-
- // UVcout << "prefix: " << QUACKLE_ALPHABET_PARAMETERS->userVisible(prefix) << ", children: " << children.size() << endl;
-
- if (children.size() > 0) {
- pointer = nodelist.size();
- children[children.size() - 1].lastchild = true;
- }
-
- for (size_t i = 0; i < children.size(); i++) {
- nodelist.push_back(&children[i]);
- }
-
- for (size_t i = 0; i < children.size(); i++) {
- children[i].print(prefix + children[i].c);
- }
-}
-
-
-void Node::pushword(Quackle::LetterString word) {
- if (word.length() == 0) {
- t = true;
- }
- else {
- Quackle::Letter first = Quackle::String::front(word);
- Quackle::LetterString rest = Quackle::String::allButFront(word);
- int index = -1;
-
- // cout << "first: " << first << ", rest: " << rest << endl;
-
- for (size_t i = 0; i < children.size(); i++) {
- if (children[i].c == first) {
- index = i;
- i = children.size();
- }
- }
-
- if (index == -1) {
- Node n;
- n.c = first;
- n.t = false;
- n.pointer = 0;
- n.lastchild = false;
- children.push_back(n);
- index = children.size() - 1;
- }
-
- children[index].pushword(rest);
- }
-}
int main(int argc, char **argv)
@@ -127,21 +60,9 @@ int main(int argc, char **argv)
if (outputFilename.isNull())
outputFilename = "output.gaddag";
- Quackle::AlphabetParameters *alphas = 0;
QString alphabetFile = QString("../data/alphabets/%1.quackle_alphabet").arg(alphabet);
UVcout << "Using alphabet file: " << QuackleIO::Util::qstringToString(alphabetFile) << endl;
- QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
- flexure->load(alphabetFile);
- alphas = flexure;
-
- // So the separator is sorted to last.
- Quackle::Letter internalSeparatorRepresentation = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE;
-
- Node root;
- root.t = false;
- root.c = QUACKLE_NULL_MARK; // "_"
- root.pointer = 0;
- root.lastchild = true;
+ GaddagFactory factory(QuackleIO::Util::qstringToString(alphabetFile));
QFile file(inputFilename);
if (!file.exists())
@@ -159,11 +80,6 @@ int main(int argc, char **argv)
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
- int encodableWords = 0;
- int unencodableWords = 0;
-
- Quackle::WordList gaddagizedWords;
-
while (!stream.atEnd())
{
QString originalQString;
@@ -172,115 +88,27 @@ int main(int argc, char **argv)
if (stream.atEnd())
break;
- UVString originalString = QuackleIO::Util::qstringToString(originalQString);
-
- UVString leftover;
- Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover);
- if (leftover.empty())
- {
- //for (Quackle::LetterString::iterator it = encodedWord.begin(); it != encodedWord.end(); ++it)
- //UVcout << "got encoded letter: " << (int)(*it) << endl;
-
- ++encodableWords;
-
- for (unsigned i = 1; i <= encodedWord.length(); i++) {
- Quackle::LetterString newword;
-
- for (int j = i - 1; j >= 0; j--) {
- newword.push_back(encodedWord[j]);
- }
-
- if (i < encodedWord.length()) {
- newword.push_back(internalSeparatorRepresentation); // "^"
- for (unsigned j = i; j < encodedWord.length(); j++) {
- newword.push_back(encodedWord[j]);
- }
- }
- gaddagizedWords.push_back(newword);
- }
- }
- else
- {
- UVcout << "not encodable without leftover: " << originalString << endl;
- ++unencodableWords;
- }
+ if (!factory.pushWord(QuackleIO::Util::qstringToString(originalQString)))
+ UVcout << "not encodable without leftover: " << QuackleIO::Util::qstringToString(originalQString) << endl;
}
- UVcout << "Sorting " << gaddagizedWords.size () << " words..." << endl;
- sort(gaddagizedWords.begin(), gaddagizedWords.end());
+ UVcout << "Sorting " << factory.wordCount() << " words..." << endl;
+ factory.sortWords();
UVcout << "Generating nodes...";
- Quackle::WordList::const_iterator wordsEnd = gaddagizedWords.end();
- for (Quackle::WordList::const_iterator wordsIt = gaddagizedWords.begin(); wordsIt != wordsEnd; ++wordsIt)
- {
- root.pushword(*wordsIt);
- }
+ factory.generate();
UVcout << "Writing index...";
-
- nodelist.push_back(&root);
-
- root.print("");
-
- ofstream out(QuackleIO::Util::qstringToStdString(outputFilename).c_str(), ios::out | ios::binary);
-
- for (size_t i = 0; i < nodelist.size(); i++) {
- // UVcout << nodelist[i]->c << " " << nodelist[i]->pointer << " " << nodelist[i]->t << " " << nodelist[i]->lastchild << endl;
-
- unsigned int p = (unsigned int)(nodelist[i]->pointer);
- if (p != 0) {
- p -= i; // offset indexing
- }
-
- char bytes[4];
- unsigned char n1 = (p & 0x00FF0000) >> 16;
- /*
- UVcout << "byte 1: " << ((p & 0xFF000000) >> 24);
- UVcout << ", byte 2: " << ((p & 0x00FF0000) >> 8);
- UVcout << ", byte 3: " << ((p & 0x0000FF00) >> 8);
- UVcout << ", byte 4: " << ((p & 0x000000FF) >> 0) << endl;
- */
-
- unsigned char n2 = (p & 0x0000FF00) >> 8;
- unsigned char n3 = (p & 0x000000FF) >> 0;
- unsigned char n4;
-
- /*
- UVcout << "p: " << p << ", crap: " << (((unsigned int)(n1) << 24) |
- ((unsigned int)(n2) << 16) |
- ((unsigned int)(n3) << 8)) << endl;
- */
- n4 = nodelist[i]->c;
- if (n4 == internalSeparatorRepresentation)
- n4 = QUACKLE_GADDAG_SEPARATOR;
-
- if (nodelist[i]->t) {
- n4 |= 64;
- }
- if (nodelist[i]->lastchild) {
- n4 |= 128;
- }
-
- /*
- UVcout << "p: " << p << endl;;
- UVcout << "n4:" << (int)(n4) <<
- ", n1: " << (int)(n1) <<
- ", n2: " << (int)(n2) <<
- ", n3: " << (int)(n3) << endl;
- */
-
- //bytes[0] = n4; bytes[1] = n1; bytes[2] = n2; bytes[3] = n3;
- bytes[0] = n1; bytes[1] = n2; bytes[2] = n3; bytes[3] = n4;
- //out.write((const char*) &p, 4);
- out.write(bytes, 4);
- }
+ factory.writeIndex(outputFilename.toUtf8().constData());
UVcout << endl;
- UVcout << "Wrote " << encodableWords << " words over " << nodelist.size() << " nodes to " << QuackleIO::Util::qstringToString(outputFilename) << "." << endl;
+ UVcout << "Wrote " << factory.encodableWords() << " words over " << factory.nodeCount() << " nodes to " << QuackleIO::Util::qstringToString(outputFilename) << "." << endl;
+
+ UVcout << "Hash: " << QString(QByteArray(factory.hashBytes(), 16).toHex()).toStdString() << endl;
- if (unencodableWords > 0)
- UVcout << "There were " << unencodableWords << " words left out." << endl;
+ if (factory.unencodableWords() > 0)
+ UVcout << "There were " << factory.unencodableWords() << " words left out." << endl;
return 0;
}
diff --git a/makegaddag/makegaddag.pro b/makegaddag/makegaddag.pro
index dfd1259..9895c99 100755
--- a/makegaddag/makegaddag.pro
+++ b/makegaddag/makegaddag.pro
@@ -5,10 +5,12 @@ CONFIG += release
debug {
OBJECTS_DIR = obj/debug
+ QMAKE_LIBDIR += ../lib/debug ../quackleio/lib/debug
}
release {
OBJECTS_DIR = obj/release
+ QMAKE_LIBDIR += ../lib/release ../quackleio/lib/release
}
MOC_DIR = moc
@@ -19,10 +21,12 @@ MOC_DIR = moc
CONFIG += console
CONFIG -= app_bundle
-LIBS += -lquackleio -lquackle
+win32:!win32-g++ {
+ LIBS += -lquackleio -llibquackle
+} else {
+ LIBS += -lquackleio -lquackle
+}
-QMAKE_LFLAGS_RELEASE += -L../lib/release -L../quackleio/lib/release
-QMAKE_LFLAGS_DEBUG += -L../lib/debug -L../quackleio/lib/debug
# Input
SOURCES += makegaddag.cpp
diff --git a/makeminidawg/makeminidawg.cpp b/makeminidawg/makeminidawg.cpp
deleted file mode 100644
index 4973ec8..0000000
--- a/makeminidawg/makeminidawg.cpp
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * Quackle -- Crossword game artificial intelligence and analysis tool
- * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string>
-#include <iostream>
-#include <iomanip>
-#include <vector>
-#include <map>
-
-#include <QtCore>
-
-#include <quackleio/flexiblealphabet.h>
-#include <quackleio/froggetopt.h>
-#include <quackleio/util.h>
-
-using namespace std;
-
-class Node {
-public:
- void pushword(Quackle::LetterString word, bool inSmaller, int pb);
- void print(Quackle::LetterString prefix);
-
- int depth();
- int subtreesize();
- int lettersum();
- bool equals(Node &n);
-
- Quackle::Letter c;
- bool t;
- bool insmallerdict;
- int playability;
-
- vector<Node> children;
- Node* parent;
- int pointer;
- int location;
- int oldpointer;
-
- bool lastchild;
-
- bool dexplored;
- bool sexplored;
- bool lexplored;
-
- int d;
- int s;
- int l;
-
- bool deleted;
- Node* cloneof;
- bool written;
-};
-
-
-vector< Node* > nodelist;
-Node root;
-
-vector< int > ofdepth[20][100];
-
-map< QString, bool> smallerMap;
-map< QString, int> playabilityMap;
-
-bool Node::equals(Node &n)
-{
- if (playability != n.playability)
- return false;
- if (c != n.c)
- return false;
- if (children.size() != n.children.size())
- return false;
- if (t != n.t)
- return false;
- if (insmallerdict != n.insmallerdict)
- return false;
- if (l != n.l)
- return false;
- if (s != n.s)
- return false;
- if (d != n.d)
- return false;
-
- for (unsigned int i = 0; i < children.size(); i++)
- if (!children[i].equals(n.children[i]))
- return false;
-
- return true;
-}
-
-int Node::depth()
-{
- if (dexplored)
- return d;
-
- dexplored = true;
-
- if (children.size() == 0)
- {
- d = 1;
- return d;
- }
-
- int childmax = 0;
-
- for (unsigned int i = 0; i < children.size(); i++)
- {
- int d = children[i].depth();
- if (d > childmax)
- childmax = d;
- }
-
- d = 1 + childmax;
- return d;
-}
-
-
-int Node::subtreesize()
-{
- if (sexplored)
- return s;
-
- sexplored = true;
-
- if (children.size() == 0)
- {
- s = 1;
- return s;
- }
-
- int childsum = 0;
-
- for (unsigned int i = 0; i < children.size(); i++)
- {
- int s = children[i].subtreesize();
- childsum += s;
- }
-
- s = 1 + childsum;
- return s;
-}
-
-
-int Node::lettersum()
-{
- if (lexplored)
- return l;
-
- lexplored = true;
-
- int thisletter = c;
-
- if (children.size() == 0)
- {
- l = thisletter;
- return l;
- }
-
- int childsum = 0;
-
- for (unsigned int i = 0; i < children.size(); i++)
- {
- int s = children[i].lettersum();
- childsum += s;
- }
-
- l = thisletter + childsum;
- return l;
-}
-
-
-void Node::print(Quackle::LetterString prefix) {
-
- written = true;
-
- if (t) {
- //cout << prefix << endl;
- }
-
- //cout << "prefix: " << prefix << ", children: " << children.size() << ", deleted: " << deleted << ", oldpointer: " << oldpointer << ", nthChild: " << nthChild << endl;
-
- if (children.size() == 0)
- {
- //cout << " no children. nothing to do." << endl;
- return;
- }
-
- if (!deleted)
- {
- //cout << " Setting pointer to " << nodelist.size() << " before I push_back the children." << endl;
- pointer = nodelist.size();
- }
- else
- {
- pointer = cloneof->pointer;
- //cout << " Setting pointer to clone's (" << pointer << ") and not pushing anything." << endl;
- }
-
- if (!deleted)
- {
- for (unsigned int i = 0; i < children.size(); i++) {
- children[i].parent = this;
- nodelist.push_back(&children[i]);
- }
-
- for (unsigned int i = 0; i < children.size(); i++) {
- if (!children[i].deleted)
- children[i].print(prefix + children[i].c);
- else if (!children[i].cloneof->written)
- children[i].cloneof->print(prefix + children[i].c);
- }
- }
-
- if (children.size() > 0)
- children[children.size() - 1].lastchild = true;
-}
-
-
-void Node::pushword(Quackle::LetterString word, bool inSmaller, int pb) {
- if (word.length() == 0) {
- t = true;
- playability = pb;
- insmallerdict = inSmaller;
- }
- else {
- char first = word[0];
- Quackle::LetterString rest = word.substr(1, word.length() - 1);
- int index = -1;
-
- // cout << "first: " << first << ", rest: " << rest << endl;
-
- for (unsigned int i = 0; i < children.size(); i++) {
- if (children[i].c == first) {
- index = i;
- i = children.size();
- }
- }
-
- if (index == -1) {
- Node n;
- n.c = first;
- n.t = false;
- n.playability = 0;
- n.insmallerdict = false;
- n.pointer = 0;
- n.oldpointer = 0;
- n.lastchild = false;
- children.push_back(n);
- index = children.size() - 1;
- }
-
- children[index].pushword(rest, inSmaller, pb);
- }
-
- dexplored = false;
- sexplored = false;
- lexplored = false;
- deleted = false;
- written = false;
-}
-
-
-void minimize()
-{
- nodelist[0]->depth();
- nodelist[0]->subtreesize();
- nodelist[0]->lettersum();
-
- for (unsigned int i = 0; i < nodelist.size(); i++)
- {
- int d = nodelist[i]->d;
- if ((d >= 1) & (d <= 20))
- ofdepth[d - 1][nodelist[i]->l%100].push_back(i);
- }
-
- for (int d = 0; d < 20; d++)
- {
- for (int l = 0; l < 100; l++)
- {
- //cout << "l: " << l << endl;
- if (ofdepth[d][l].size() > 0)
- for (vector<int>::iterator it = ofdepth[d][l].begin(); it != ofdepth[d][l].end() - 1; it++)
- {
- if (!nodelist[(*it)]->deleted)
- {
- for (vector<int>::iterator jt = it + 1; jt != ofdepth[d][l].end(); jt++)
- {
- if (!nodelist[(*jt)]->deleted)
- {
- // cout << "Comparing " << (*it) << " and " << (*jt) << endl;
- if (nodelist[(*it)]->equals(nodelist[(*jt)][0]))
- {
- //cout << "Hey! " << (*it) << " == " << (*jt) << endl;
- // ones[l].erase(jt);
- nodelist[(*jt)]->deleted = true;
- nodelist[(*jt)]->cloneof = nodelist[(*it)];
- }
- }
- }
- }
- }
- }
-
- for (unsigned int i = 0; i < nodelist.size(); i++)
- {
- nodelist[i]->oldpointer = nodelist[i]->pointer;
- nodelist[i]->pointer = 0;
- nodelist[i]->written = false;
- }
-
- }
-
- nodelist.clear();
- nodelist.push_back(&root);
- root.print("");
- UVcout << "nodelist.size(): " << nodelist.size() << endl;
-}
-
-int main(int argc, char **argv) {
- QCoreApplication a(argc, argv);
-
- GetOpt opts;
- QString alphabet;
- opts.addOption('a', "alphabet", &alphabet);
- if (!opts.parse())
- return 1;
-
- if (alphabet.isNull())
- alphabet = "english";
-
- Quackle::AlphabetParameters *alphas = 0;
- QString alphabetFile = QString("../data/alphabets/%1.quackle_alphabet").arg(alphabet);
- UVcout << "Using alphabet file: " << QuackleIO::Util::qstringToString(alphabetFile) << endl;
- QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
- flexure->load(alphabetFile);
- alphas = flexure;
-
- root.t = false;
- root.insmallerdict = false;
- root.playability = 0;
- root.c = QUACKLE_BLANK_MARK;
- root.pointer = 0;
- root.lastchild = true;
-
- QString smallerDictFilename = "smaller.raw";
- QFile smallerDict(smallerDictFilename);
- if (!smallerDict.exists())
- {
- UVcout << "smaller dictionary does not exist: " << QuackleIO::Util::qstringToString(smallerDictFilename) << endl;
- return false;
- }
-
- if (!smallerDict.open(QIODevice::ReadOnly | QIODevice::Text))
- {
- UVcout << "Could not open " << QuackleIO::Util::qstringToString(smallerDictFilename) << endl;
- return false;
- }
-
- QTextStream smallerStream(&smallerDict);
- smallerStream.setCodec(QTextCodec::codecForName("UTF-8"));
-
- while (!smallerStream.atEnd())
- {
- QString originalQString;
- smallerStream >> originalQString;
- //UVcout << "this word is in the smaller dictionary: " << QuackleIO::Util::qstringToString(originalQString) << endl;
- smallerMap[originalQString] = true;
- }
-
- QString playabilityFilename = "playabilities.raw";
- QFile playability(playabilityFilename);
- if (!playability.exists())
- {
- UVcout << "playability does not exist: " << QuackleIO::Util::qstringToString(playabilityFilename) << endl;
- return false;
- }
-
- if (!playability.open(QIODevice::ReadOnly | QIODevice::Text))
- {
- UVcout << "Could not open " << QuackleIO::Util::qstringToString(playabilityFilename) << endl;
- return false;
- }
-
- QTextStream playabilityStream(&playability);
- playabilityStream.setCodec(QTextCodec::codecForName("UTF-8"));
-
- while (!playabilityStream.atEnd())
- {
- int pb;
- playabilityStream >> pb;
- QString originalQString;
- playabilityStream >> originalQString;
- //UVcout << "playability: " << QuackleIO::Util::qstringToString(originalQString) << " " << pb << endl;
- playabilityMap[originalQString] = pb;
- }
-
- QString dawgFilename = "dawginput.raw";
- QFile file(dawgFilename);
- if (!file.exists())
- {
- UVcout << "dawg does not exist: " << QuackleIO::Util::qstringToString(dawgFilename) << endl;
- return false;
- }
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
- {
- UVcout << "Could not open " << QuackleIO::Util::qstringToString(dawgFilename) << endl;
- return false;
- }
-
- QTextStream stream(&file);
- stream.setCodec(QTextCodec::codecForName("UTF-8"));
-
- int encodableWords = 0;
- int unencodableWords = 0;
-
- while (!stream.atEnd())
- {
- QString originalQString;
- stream >> originalQString;
-
- bool inSmaller = smallerMap[originalQString];
- int pb = playabilityMap[originalQString];
-
- if (stream.atEnd())
- break;
-
- UVString originalString = QuackleIO::Util::qstringToString(originalQString);
-
- //UVcout << "read original string: " << originalString;
- //if (!inSmaller) UVcout << "#";
- //UVcout << endl;
-
- UVString leftover;
- Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover);
- if (leftover.empty())
- {
- //for (Quackle::LetterString::iterator it = encodedWord.begin(); it != encodedWord.end(); ++it)
- //UVcout << "got encoded letter: " << (int)(*it) << endl;
-
- root.pushword(encodedWord, inSmaller, pb);
- ++encodableWords;
- }
- else
- {
- UVcout << "not encodable without leftover: " << originalString << endl;
- ++unencodableWords;
- }
- }
-
- file.close();
- delete alphas;
-
- UVcout << "encodable words: " << encodableWords << ", unencodable words: " << unencodableWords << endl;
-
- nodelist.push_back(&root);
- root.print("");
- UVcout << "nodelist.size(): " << nodelist.size() << endl;
-
- minimize();
-
- ofstream out("output.dawg", ios::out | ios::binary);
-
- for (unsigned int i = 0; i < nodelist.size(); i++) {
- //cout << nodelist[i]->c << " " << nodelist[i]->pointer << " " << nodelist[i]->t << " " << nodelist[i]->lastchild << endl;
- Node* n = nodelist[i];
- unsigned int p;
- if (nodelist[i]->deleted)
- {
- p = (unsigned int)(nodelist[i]->cloneof->pointer);
- // n = nodelist[i]->cloneof;
- }
- else
- p = (unsigned int)(nodelist[i]->pointer);
-
- char bytes[7];
- unsigned char n1 = (p & 0x00FF0000) >> 16;
- unsigned char n2 = (p & 0x0000FF00) >> 8;
- unsigned char n3 = (p & 0x000000FF);
- unsigned char n4 = n->c - QUACKLE_FIRST_LETTER;
-
- unsigned int pb = n->playability;
- unsigned char n5 = (pb & 0x00FF0000) >> 16;
- unsigned char n6 = (pb & 0x0000FF00) >> 8;
- unsigned char n7 = (pb & 0x000000FF);
-
- if (n->t) {
- n4 |= 32;
- }
- if (n->lastchild) {
- n4 |= 64;
- }
- if (n->insmallerdict) {
- n4 |= 128;
- }
-
- bytes[0] = n1; bytes[1] = n2; bytes[2] = n3; bytes[3] = n4;
- bytes[4] = n5; bytes[5] = n6; bytes[6] = n7;
- out.write(bytes, 7);
- }
-}
diff --git a/makeminidawg/makeminidawg.pro b/makeminidawg/makeminidawg.pro
index 8af3bb2..e728a8a 100644
--- a/makeminidawg/makeminidawg.pro
+++ b/makeminidawg/makeminidawg.pro
@@ -12,20 +12,24 @@ CONFIG -= app_bundle
debug {
OBJECTS_DIR = obj/debug
+ QMAKE_LIBDIR += ../lib/debug ../quackleio/lib/debug
}
release {
OBJECTS_DIR = obj/release
+ QMAKE_LIBDIR += ../lib/release ../quackleio/lib/release
}
-LIBS += -lquackleio -lquackle
+win32:!win32-g++ {
+ LIBS += -lquackleio -llibquackle
+} else {
+ LIBS += -lquackleio -lquackle
+}
-QMAKE_LFLAGS_RELEASE += -L../lib/release -L../quackleio/lib/release
-QMAKE_LFLAGS_DEBUG += -L../lib/debug -L../quackleio/lib/debug
# Input
-HEADERS += minidawgmaker.h
-SOURCES += minidawgmaker.cpp makeminidawgmain.cpp
+HEADERS +=
+SOURCES += makeminidawgmain.cpp
macx-g++ {
diff --git a/makeminidawg/makeminidawgmain.cpp b/makeminidawg/makeminidawgmain.cpp
index 82871be..dfd3c82 100644
--- a/makeminidawg/makeminidawgmain.cpp
+++ b/makeminidawg/makeminidawgmain.cpp
@@ -16,16 +16,132 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <QCoreApplication>
-#include <QStringList>
+#include <map>
+#include <QtCore>
-#include "minidawgmaker.h"
+#include "quackleio/dawgfactory.h"
+#include "quackleio/froggetopt.h"
+#include "quackleio/util.h"
+
+std::map< QString, bool> smallerMap;
+std::map< QString, int> playabilityMap;
int main(int argc, char **argv)
{
QCoreApplication a(argc, argv);
- MiniDawgMaker maker;
- return maker.executeFromArguments();
+ GetOpt opts;
+ QString alphabet;
+ opts.addOption('a', "alphabet", &alphabet);
+ if (!opts.parse())
+ return 1;
+
+ if (alphabet.isNull())
+ alphabet = "english";
+
+ QString alphabetFile = QString("../data/alphabets/%1.quackle_alphabet").arg(alphabet);
+ UVcout << "Using alphabet file: " << QuackleIO::Util::qstringToString(alphabetFile) << endl;
+
+ DawgFactory factory(alphabetFile);
+
+
+ QString smallerDictFilename = "smaller.raw";
+ QFile smallerDict(smallerDictFilename);
+ if (!smallerDict.exists())
+ {
+ UVcout << "smaller dictionary does not exist: " << QuackleIO::Util::qstringToString(smallerDictFilename) << endl;
+ return false;
+ }
+
+ if (!smallerDict.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ UVcout << "Could not open " << QuackleIO::Util::qstringToString(smallerDictFilename) << endl;
+ return false;
+ }
+
+ QTextStream smallerStream(&smallerDict);
+ smallerStream.setCodec(QTextCodec::codecForName("UTF-8"));
+
+ while (!smallerStream.atEnd())
+ {
+ QString originalQString;
+ smallerStream >> originalQString;
+ //UVcout << "this word is in the smaller dictionary: " << QuackleIO::Util::qstringToString(originalQString) << endl;
+ smallerMap[originalQString] = true;
+ }
+
+ QString playabilityFilename = "playabilities.raw";
+ QFile playability(playabilityFilename);
+ if (!playability.exists())
+ {
+ UVcout << "playability does not exist: " << QuackleIO::Util::qstringToString(playabilityFilename) << endl;
+ return false;
+ }
+
+ if (!playability.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ UVcout << "Could not open " << QuackleIO::Util::qstringToString(playabilityFilename) << endl;
+ return false;
+ }
+
+ QTextStream playabilityStream(&playability);
+ playabilityStream.setCodec(QTextCodec::codecForName("UTF-8"));
+
+ while (!playabilityStream.atEnd())
+ {
+ int pb;
+ playabilityStream >> pb;
+ QString originalQString;
+ playabilityStream >> originalQString;
+ //UVcout << "playability: " << QuackleIO::Util::qstringToString(originalQString) << " " << pb << endl;
+ playabilityMap[originalQString] = pb;
+ }
+
+ QString dawgFilename = "dawginput.raw";
+ QFile file(dawgFilename);
+ if (!file.exists())
+ {
+ UVcout << "dawg does not exist: " << QuackleIO::Util::qstringToString(dawgFilename) << endl;
+ return false;
+ }
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ {
+ UVcout << "Could not open " << QuackleIO::Util::qstringToString(dawgFilename) << endl;
+ return false;
+ }
+
+ QTextStream stream(&file);
+ stream.setCodec(QTextCodec::codecForName("UTF-8"));
+
+ while (!stream.atEnd())
+ {
+ QString word;
+ stream >> word;
+
+ bool inSmaller = smallerMap[word];
+ int pb = playabilityMap[word];
+
+ if (stream.atEnd())
+ break;
+
+ if (!factory.pushWord(QuackleIO::Util::qstringToString(word), inSmaller, pb))
+ UVcout << "not encodable without leftover: " << QuackleIO::Util::qstringToString(word) << endl;
+ }
+
+ file.close();
+
+ UVcout << "encodable words: " << factory.encodableWords() << ", unencodable words: " << factory.unencodableWords() << endl;
+
+ UVcout << "nodelist.size(): " << factory.nodeCount() << endl;
+
+ factory.generate();
+ UVcout << "Compressed nodelist.size(): " << factory.nodeCount() << endl;
+
+ UVcout << "Hash: " << QString(QByteArray(factory.hashBytes(), 16).toHex()).toStdString() << endl;
+
+ factory.writeIndex("output.dawg");
+
+ return 0;
}
diff --git a/makeminidawg/minidawgmaker.cpp b/makeminidawg/minidawgmaker.cpp
deleted file mode 100644
index fa4df2f..0000000
--- a/makeminidawg/minidawgmaker.cpp
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Quackle -- Crossword game artificial intelligence and analysis tool
- * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string>
-#include <iostream>
-#include <iomanip>
-#include <vector>
-#include <map>
-
-#include <QtCore>
-
-#include <quackleio/flexiblealphabet.h>
-#include <quackleio/froggetopt.h>
-#include <quackleio/util.h>
-
-#include "minidawgmaker.h"
-
-using namespace std;
-
-class Node {
-public:
- void pushword(Quackle::LetterString word, bool inSmaller, int pb);
- void print(Quackle::LetterString prefix);
-
- int depth();
- int subtreesize();
- int lettersum();
- bool equals(Node &n);
-
- Quackle::Letter c;
- bool t;
- bool insmallerdict;
- int playability;
-
- vector<Node> children;
- Node* parent;
- int pointer;
- int location;
- int oldpointer;
-
- bool lastchild;
-
- bool dexplored;
- bool sexplored;
- bool lexplored;
-
- int d;
- int s;
- int l;
-
- bool deleted;
- Node* cloneof;
- bool written;
-};
-
-
-vector< Node* > nodelist;
-Node root;
-
-vector< int > ofdepth[20][100];
-
-map< QString, bool> smallerMap;
-map< QString, int> playabilityMap;
-
-bool Node::equals(Node &n)
-{
- if (playability != n.playability)
- return false;
- if (c != n.c)
- return false;
- if (children.size() != n.children.size())
- return false;
- if (t != n.t)
- return false;
- if (insmallerdict != n.insmallerdict)
- return false;
- if (l != n.l)
- return false;
- if (s != n.s)
- return false;
- if (d != n.d)
- return false;
-
- for (unsigned int i = 0; i < children.size(); i++)
- if (!children[i].equals(n.children[i]))
- return false;
-
- return true;
-}
-
-int Node::depth()
-{
- if (dexplored)
- return d;
-
- dexplored = true;
-
- if (children.size() == 0)
- {
- d = 1;
- return d;
- }
-
- int childmax = 0;
-
- for (unsigned int i = 0; i < children.size(); i++)
- {
- int d = children[i].depth();
- if (d > childmax)
- childmax = d;
- }
-
- d = 1 + childmax;
- return d;
-}
-
-
-int Node::subtreesize()
-{
- if (sexplored)
- return s;
-
- sexplored = true;
-
- if (children.size() == 0)
- {
- s = 1;
- return s;
- }
-
- int childsum = 0;
-
- for (unsigned int i = 0; i < children.size(); i++)
- {
- int s = children[i].subtreesize();
- childsum += s;
- }
-
- s = 1 + childsum;
- return s;
-}
-
-
-int Node::lettersum()
-{
- if (lexplored)
- return l;
-
- lexplored = true;
-
- int thisletter = c;
-
- if (children.size() == 0)
- {
- l = thisletter;
- return l;
- }
-
- int childsum = 0;
-
- for (unsigned int i = 0; i < children.size(); i++)
- {
- int s = children[i].lettersum();
- childsum += s;
- }
-
- l = thisletter + childsum;
- return l;
-}
-
-
-void Node::print(Quackle::LetterString prefix) {
-
- written = true;
-
- if (t) {
- //cout << prefix << endl;
- }
-
- //cout << "prefix: " << prefix << ", children: " << children.size() << ", deleted: " << deleted << ", oldpointer: " << oldpointer << ", nthChild: " << nthChild << endl;
-
- if (children.size() == 0)
- {
- //cout << " no children. nothing to do." << endl;
- return;
- }
-
- if (!deleted)
- {
- //cout << " Setting pointer to " << nodelist.size() << " before I push_back the children." << endl;
- pointer = nodelist.size();
- }
- else
- {
- pointer = cloneof->pointer;
- //cout << " Setting pointer to clone's (" << pointer << ") and not pushing anything." << endl;
- }
-
- if (!deleted)
- {
- for (unsigned int i = 0; i < children.size(); i++) {
- children[i].parent = this;
- nodelist.push_back(&children[i]);
- }
-
- for (unsigned int i = 0; i < children.size(); i++) {
- if (!children[i].deleted)
- children[i].print(prefix + children[i].c);
- else if (!children[i].cloneof->written)
- children[i].cloneof->print(prefix + children[i].c);
- }
- }
-
- if (children.size() > 0)
- children[children.size() - 1].lastchild = true;
-}
-
-
-void Node::pushword(Quackle::LetterString word, bool inSmaller, int pb) {
- if (word.length() == 0) {
- t = true;
- playability = pb;
- insmallerdict = inSmaller;
- }
- else {
- char first = word[0];
- Quackle::LetterString rest = word.substr(1, word.length() - 1);
- int index = -1;
-
- // cout << "first: " << first << ", rest: " << rest << endl;
-
- for (unsigned int i = 0; i < children.size(); i++) {
- if (children[i].c == first) {
- index = i;
- i = children.size();
- }
- }
-
- if (index == -1) {
- Node n;
- n.c = first;
- n.t = false;
- n.playability = 0;
- n.insmallerdict = false;
- n.pointer = 0;
- n.oldpointer = 0;
- n.lastchild = false;
- children.push_back(n);
- index = children.size() - 1;
- }
-
- children[index].pushword(rest, inSmaller, pb);
- }
-
- dexplored = false;
- sexplored = false;
- lexplored = false;
- deleted = false;
- written = false;
-}
-
-
-void minimize()
-{
- nodelist[0]->depth();
- nodelist[0]->subtreesize();
- nodelist[0]->lettersum();
-
- for (unsigned int i = 0; i < nodelist.size(); i++)
- {
- int d = nodelist[i]->d;
- if ((d >= 1) & (d <= 20))
- ofdepth[d - 1][nodelist[i]->l%100].push_back(i);
- }
-
- for (int d = 0; d < 20; d++)
- {
- for (int l = 0; l < 100; l++)
- {
- //cout << "l: " << l << endl;
- if (ofdepth[d][l].size() > 0)
- for (vector<int>::iterator it = ofdepth[d][l].begin(); it != ofdepth[d][l].end() - 1; it++)
- {
- if (!nodelist[(*it)]->deleted)
- {
- for (vector<int>::iterator jt = it + 1; jt != ofdepth[d][l].end(); jt++)
- {
- if (!nodelist[(*jt)]->deleted)
- {
- // cout << "Comparing " << (*it) << " and " << (*jt) << endl;
- if (nodelist[(*it)]->equals(nodelist[(*jt)][0]))
- {
- //cout << "Hey! " << (*it) << " == " << (*jt) << endl;
- // ones[l].erase(jt);
- nodelist[(*jt)]->deleted = true;
- nodelist[(*jt)]->cloneof = nodelist[(*it)];
- }
- }
- }
- }
- }
- }
-
- for (unsigned int i = 0; i < nodelist.size(); i++)
- {
- nodelist[i]->oldpointer = nodelist[i]->pointer;
- nodelist[i]->pointer = 0;
- nodelist[i]->written = false;
- }
-
- }
-
- nodelist.clear();
- nodelist.push_back(&root);
- root.print("");
- UVcout << "nodelist.size(): " << nodelist.size() << endl;
-}
-
-int MiniDawgMaker::executeFromArguments()
-{
- GetOpt opts;
- QString alphabet;
- opts.addOption('a', "alphabet", &alphabet);
- if (!opts.parse())
- return 1;
-
- if (alphabet.isNull())
- alphabet = "english";
-
- Quackle::AlphabetParameters *alphas = 0;
- QString alphabetFile = QString("../data/alphabets/%1.quackle_alphabet").arg(alphabet);
- UVcout << "Using alphabet file: " << QuackleIO::Util::qstringToString(alphabetFile) << endl;
- QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
- flexure->load(alphabetFile);
- alphas = flexure;
-
- root.t = false;
- root.insmallerdict = false;
- root.playability = 0;
- root.c = QUACKLE_BLANK_MARK;
- root.pointer = 0;
- root.lastchild = true;
-
- QString smallerDictFilename = "smaller.raw";
- QFile smallerDict(smallerDictFilename);
- if (!smallerDict.exists())
- {
- UVcout << "smaller dictionary does not exist: " << QuackleIO::Util::qstringToString(smallerDictFilename) << endl;
- return false;
- }
-
- if (!smallerDict.open(QIODevice::ReadOnly | QIODevice::Text))
- {
- UVcout << "Could not open " << QuackleIO::Util::qstringToString(smallerDictFilename) << endl;
- return false;
- }
-
- QTextStream smallerStream(&smallerDict);
- smallerStream.setCodec(QTextCodec::codecForName("UTF-8"));
-
- while (!smallerStream.atEnd())
- {
- QString originalQString;
- smallerStream >> originalQString;
- //UVcout << "this word is in the smaller dictionary: " << QuackleIO::Util::qstringToString(originalQString) << endl;
- smallerMap[originalQString] = true;
- }
-
- QString playabilityFilename = "playabilities.raw";
- QFile playability(playabilityFilename);
- if (!playability.exists())
- {
- UVcout << "playability does not exist: " << QuackleIO::Util::qstringToString(playabilityFilename) << endl;
- return false;
- }
-
- if (!playability.open(QIODevice::ReadOnly | QIODevice::Text))
- {
- UVcout << "Could not open " << QuackleIO::Util::qstringToString(playabilityFilename) << endl;
- return false;
- }
-
- QTextStream playabilityStream(&playability);
- playabilityStream.setCodec(QTextCodec::codecForName("UTF-8"));
-
- while (!playabilityStream.atEnd())
- {
- int pb;
- playabilityStream >> pb;
- QString originalQString;
- playabilityStream >> originalQString;
- //UVcout << "playability: " << QuackleIO::Util::qstringToString(originalQString) << " " << pb << endl;
- playabilityMap[originalQString] = pb;
- }
-
- QString dawgFilename = "dawginput.raw";
- QFile file(dawgFilename);
- if (!file.exists())
- {
- UVcout << "dawg does not exist: " << QuackleIO::Util::qstringToString(dawgFilename) << endl;
- return false;
- }
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
- {
- UVcout << "Could not open " << QuackleIO::Util::qstringToString(dawgFilename) << endl;
- return false;
- }
-
- QTextStream stream(&file);
- stream.setCodec(QTextCodec::codecForName("UTF-8"));
-
- int encodableWords = 0;
- int unencodableWords = 0;
-
- while (!stream.atEnd())
- {
- QString originalQString;
- stream >> originalQString;
-
- bool inSmaller = smallerMap[originalQString];
- int pb = playabilityMap[originalQString];
-
- if (stream.atEnd())
- break;
-
- UVString originalString = QuackleIO::Util::qstringToString(originalQString);
-
- //UVcout << "read original string: " << originalString;
- //if (!inSmaller) UVcout << "#";
- //UVcout << endl;
-
- UVString leftover;
- Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover);
- if (leftover.empty())
- {
- //for (Quackle::LetterString::iterator it = encodedWord.begin(); it != encodedWord.end(); ++it)
- //UVcout << "got encoded letter: " << (int)(*it) << endl;
-
- root.pushword(encodedWord, inSmaller, pb);
- ++encodableWords;
- }
- else
- {
- UVcout << "not encodable without leftover: " << originalString << endl;
- ++unencodableWords;
- }
- }
-
- file.close();
- delete alphas;
-
- UVcout << "encodable words: " << encodableWords << ", unencodable words: " << unencodableWords << endl;
-
- nodelist.push_back(&root);
- root.print("");
- UVcout << "nodelist.size(): " << nodelist.size() << endl;
-
- minimize();
-
- ofstream out("output.dawg", ios::out | ios::binary);
-
- for (unsigned int i = 0; i < nodelist.size(); i++) {
- //cout << nodelist[i]->c << " " << nodelist[i]->pointer << " " << nodelist[i]->t << " " << nodelist[i]->lastchild << endl;
- Node* n = nodelist[i];
- unsigned int p;
- if (nodelist[i]->deleted)
- {
- p = (unsigned int)(nodelist[i]->cloneof->pointer);
- // n = nodelist[i]->cloneof;
- }
- else
- p = (unsigned int)(nodelist[i]->pointer);
-
- char bytes[7];
- unsigned char n1 = (p & 0x00FF0000) >> 16;
- unsigned char n2 = (p & 0x0000FF00) >> 8;
- unsigned char n3 = (p & 0x000000FF);
- unsigned char n4 = n->c - QUACKLE_FIRST_LETTER;
-
- unsigned int pb = n->playability;
- unsigned char n5 = (pb & 0x00FF0000) >> 16;
- unsigned char n6 = (pb & 0x0000FF00) >> 8;
- unsigned char n7 = (pb & 0x000000FF);
-
- if (n->t) {
- n4 |= 32;
- }
- if (n->lastchild) {
- n4 |= 64;
- }
- if (n->insmallerdict) {
- n4 |= 128;
- }
-
- bytes[0] = n1; bytes[1] = n2; bytes[2] = n3; bytes[3] = n4;
- bytes[4] = n5; bytes[5] = n6; bytes[6] = n7;
- out.write(bytes, 7);
- }
- return 0;
-}
diff --git a/makeminidawg/minidawgmaker.h b/makeminidawg/minidawgmaker.h
deleted file mode 100644
index 7c8deee..0000000
--- a/makeminidawg/minidawgmaker.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Quackle -- Crossword game artificial intelligence and analysis tool
- * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
- *
- * 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 3 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, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef QUACKLE_MINIDAWGMAKER_H
-#define QUACKLE_MINIDAWGMAKER_H
-
-#include <QStringList>
-
-class MiniDawgMaker
-{
-public:
- MiniDawgMaker() {}
- ~MiniDawgMaker() {}
-
- // parse and execute commands specified on command line
- int executeFromArguments();
-
- // TOOD further classify this code
-};
-
-#endif
-
diff --git a/quacker/Info.plist b/quacker/Info.plist
index 015f161..854c8a9 100644
--- a/quacker/Info.plist
+++ b/quacker/Info.plist
@@ -26,7 +26,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>0.98</string>
+ <string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
diff --git a/quacker/boardsetupdialog.cpp b/quacker/boardsetupdialog.cpp
index 7198b17..2bfd5ff 100644
--- a/quacker/boardsetupdialog.cpp
+++ b/quacker/boardsetupdialog.cpp
@@ -33,7 +33,7 @@
BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent)
{
- resize(600,450);
+ resize(700,550);
setSizeGripEnabled(true);
// construct the board
@@ -58,6 +58,7 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent)
m_saveChanges = new QPushButton(tr("&Save Changes"));
m_cancel = new QPushButton(tr("&Cancel"));
m_undoAll = new QPushButton(tr("&Undo All Changes"));
+ m_deleteBoard = new QPushButton(tr("&Delete Board"));
QVBoxLayout * superLayout = new QVBoxLayout;
Geometry::setupFramedLayout(superLayout);
@@ -96,6 +97,8 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent)
leftSideLayout->addWidget(dimensionGroup);
leftSideLayout->addWidget(symmetryGroup);
leftSideLayout->addWidget(m_undoAll);
+ if (!m_originalName.isEmpty())
+ leftSideLayout->addWidget(m_deleteBoard);
leftSideLayout->addStretch();
mainLayout->addLayout(leftSideLayout);
@@ -118,6 +121,7 @@ BoardSetupDialog::BoardSetupDialog(QWidget *parent) : QDialog(parent)
connect(m_saveChanges, SIGNAL(clicked()), this, SLOT(accept()));
connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject()));
connect(m_undoAll, SIGNAL(clicked()), this, SLOT(undoAllChanges()));
+ connect(m_deleteBoard, SIGNAL(clicked()), this, SLOT(deleteBoard()));
connect(m_horizontalSymmetry, SIGNAL(stateChanged(int)), this, SLOT(symmetryChanged()));
connect(m_verticalSymmetry, SIGNAL(stateChanged(int)), this, SLOT(symmetryChanged()));
connect(m_diagonalSymmetry, SIGNAL(stateChanged(int)), this, SLOT(symmetryChanged()));
@@ -239,3 +243,20 @@ void BoardSetupDialog::undoAllChanges()
QUACKLE_DATAMANAGER->setBoardParameters(Quackle::BoardParameters::Deserialize(boardStream));
parametersChanged(QString());
}
+
+void BoardSetupDialog::deleteBoard()
+{
+ QString message = "Do you really want to delete the game board \"";
+ message += m_originalName;
+ message += "\"?";
+ if (QMessageBox::warning(NULL, QString("Confirm Deletion"), message,
+ QMessageBox::Yes | QMessageBox::Default,
+ QMessageBox::No | QMessageBox::Escape) == QMessageBox::Yes)
+ {
+ CustomQSettings settings;
+ settings.beginGroup("quackle/boardparameters");
+ settings.remove(m_originalName);
+ QDialog::reject();
+ }
+}
+
diff --git a/quacker/boardsetupdialog.h b/quacker/boardsetupdialog.h
index e5432cc..0176d1a 100644
--- a/quacker/boardsetupdialog.h
+++ b/quacker/boardsetupdialog.h
@@ -48,6 +48,7 @@ protected slots:
void parametersChanged(const QString &unused);
void symmetryChanged();
void undoAllChanges();
+ void deleteBoard();
private:
QCheckBox *m_horizontalSymmetry;
@@ -62,6 +63,7 @@ private:
QPushButton *m_saveChanges;
QPushButton *m_cancel;
QPushButton *m_undoAll;
+ QPushButton *m_deleteBoard;
Quackle::Game m_game;
BoardSetupFrame * m_boardFrame;
diff --git a/quacker/graphicalreporter.cpp b/quacker/graphicalreporter.cpp
index f7c9ce7..2fb0842 100644
--- a/quacker/graphicalreporter.cpp
+++ b/quacker/graphicalreporter.cpp
@@ -227,5 +227,6 @@ void GraphicalReporter::openIndex()
m_indexStream.setDevice(&m_indexFile);
}
+ m_indexStream.setCodec(QTextCodec::codecForName("UTF-8"));
}
diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp
new file mode 100644
index 0000000..91eb676
--- /dev/null
+++ b/quacker/lexicondialog.cpp
@@ -0,0 +1,306 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sstream>
+#include <QtGui>
+#include <datamanager.h>
+#include <quackleio/util.h>
+
+#include "lexicondialog.h"
+#include "customqsettings.h"
+#include "settings.h"
+#include "geometry.h"
+#include "quackleio/dawgfactory.h"
+
+class FileNameValidator : public QValidator
+{
+public:
+ virtual State validate(QString &input, int &pos) const
+ {
+ for (QString::ConstIterator i = input.begin(); i != input.end(); ++i)
+ if (*i == '/' || *i == '?' || *i == '\\' || *i == '*')
+ return Invalid;
+ return Acceptable;
+ }
+};
+
+LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent),
+ m_deleted(false), m_wordFactory(NULL)
+{
+ m_originalName = originalName;
+
+ resize(450,350);
+
+ // construct the UI elements
+ m_lexiconName = new QLineEdit();
+ m_alphabetCombo = new QComboBox();
+ m_fileNameValidator = new FileNameValidator();
+
+ m_addWordsFromFile = new QPushButton(tr("Add words from &file..."));
+ m_clearAllWords = new QPushButton(tr("Clear &words and start again"));
+
+
+ m_lexiconInformation = new QLabel("");
+ m_lexiconInformation->setWordWrap(true);
+ m_lexiconInformation->setTextInteractionFlags(Qt::TextBrowserInteraction);
+
+ m_saveChanges = new QPushButton(tr("&Save Changes"));
+ m_cancel = new QPushButton(tr("&Cancel"));
+ m_deleteLexicon = new QPushButton(tr("&Delete Lexicon"));
+
+ QLabel * lexiconNameLabel = new QLabel(tr("&Lexicon name:"));
+ QLabel * alphabetLabel = new QLabel(tr("&Alphabet:"));
+ lexiconNameLabel->setBuddy(m_lexiconName);
+ alphabetLabel->setBuddy(m_alphabetCombo);
+
+ QVBoxLayout * layout = new QVBoxLayout;
+ Geometry::setupFramedLayout(layout);
+ QHBoxLayout * lexiconRow = new QHBoxLayout;
+ Geometry::setupInnerLayout(lexiconRow);
+ QHBoxLayout * addRemoveWordsRow = new QHBoxLayout;
+ Geometry::setupInnerLayout(addRemoveWordsRow);
+ QHBoxLayout * buttonRow = new QHBoxLayout;
+ Geometry::setupInnerLayout(buttonRow);
+ QGroupBox * lexiconInformationGroup = new QGroupBox(tr("Lexicon information"));
+ QVBoxLayout * lexiconInformationLayout = new QVBoxLayout(lexiconInformationGroup);
+
+ // build the layout
+ lexiconRow->addWidget(lexiconNameLabel);
+ lexiconRow->addWidget(m_lexiconName);
+ lexiconRow->addStretch();
+ lexiconRow->addWidget(alphabetLabel);
+ lexiconRow->addWidget(m_alphabetCombo);
+
+ addRemoveWordsRow->addWidget(m_addWordsFromFile);
+ addRemoveWordsRow->addWidget(m_clearAllWords);
+
+ lexiconInformationLayout->addWidget(m_lexiconInformation);
+
+ buttonRow->addWidget(m_deleteLexicon);
+ buttonRow->addStretch();
+ buttonRow->addWidget(m_cancel);
+ buttonRow->addWidget(m_saveChanges);
+
+ layout->addLayout(lexiconRow);
+ layout->addLayout(addRemoveWordsRow);
+ layout->addWidget(lexiconInformationGroup);
+ layout->addStretch();
+ layout->addLayout(buttonRow);
+
+ setLayout(layout);
+ m_saveChanges->setDefault(true);
+
+ // hook up signals and slots
+ connect(m_lexiconName, SIGNAL(textEdited(const QString &)), this, SLOT(parametersChanged(const QString &)));
+ connect(m_addWordsFromFile, SIGNAL(clicked()), this, SLOT(addWordsFromFile()));
+ connect(m_clearAllWords, SIGNAL(clicked()), this, SLOT(loadOriginalDictionary()));
+ connect(m_saveChanges, SIGNAL(clicked()), this, SLOT(accept()));
+ connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject()));
+ connect(m_deleteLexicon, SIGNAL(clicked()), this, SLOT(deleteLexicon()));
+ connect(m_alphabetCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &)));
+
+ setWindowTitle(tr("Configure Lexicon - Quackle"));
+
+ Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ".quackle_alphabet", "");
+ m_alphabetCombo->setCurrentIndex(m_alphabetCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName())));
+ alphabetChanged(m_alphabetCombo->currentText());
+
+ m_lexiconName->setValidator(m_fileNameValidator);
+ m_lexiconName->setText(m_originalName);
+
+ loadOriginalDictionary();
+}
+
+LexiconDialog::~LexiconDialog()
+{
+ delete m_fileNameValidator;
+ delete m_wordFactory;
+}
+
+void LexiconDialog::deleteLexicon()
+{
+ string lexiconNameStr = m_originalName.toStdString();
+ string filename = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".dawg", true);
+ QFile(QString::fromStdString(filename)).remove();
+ m_deleted = true;
+ QDialog::accept();
+}
+
+void LexiconDialog::addWordsFromFile()
+{
+ QFileDialog browser(this, tr("Choose a file containing words to be added to the lexicon..."));
+ QStringList filters;
+ filters << "Dictionary files (*.txt *.dawg *.raw)"
+ << "All files (*.*)";
+ browser.setNameFilters(filters);
+ browser.setFileMode(QFileDialog::ExistingFiles);
+ browser.exec();
+
+ QStringList files = browser.selectedFiles();
+ for (QList<QString>::const_iterator it = files.begin(); it != files.end(); it++)
+ {
+ if (it->endsWith(".dawg", Qt::CaseInsensitive))
+ addWordsFromDawgFile(*it);
+ else
+ addWordsFromTextFile(*it);
+ }
+ updateLexiconInformation();
+}
+
+void LexiconDialog::alphabetChanged(const QString &alphabet)
+{
+ delete m_wordFactory;
+ m_wordFactory = NULL;
+ updateLexiconInformation();
+ m_alphabetFileName = QString::fromStdString(AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(alphabet)));
+}
+
+void LexiconDialog::addWordsFromDawgFile(const QString &dawgfile)
+{
+ if (!m_wordFactory)
+ m_wordFactory = new DawgFactory(m_alphabetFileName);
+ LexiconParameters lexParams;
+ lexParams.loadDawg(QuackleIO::Util::qstringToStdString(dawgfile));
+ if (!lexParams.hasDawg())
+ return;
+
+ Quackle::LetterString word;
+
+ addWordsFromDawgRecursive(lexParams, word, 1);
+}
+
+void LexiconDialog::addWordsFromDawgRecursive(const LexiconParameters &lexParams, Quackle::LetterString &word, int index)
+{
+ unsigned int p;
+ Quackle::Letter letter;
+ bool t;
+ bool lastchild;
+ bool british;
+ int playability;
+
+ do
+ {
+ lexParams.dawgAt(index, p, letter, t, lastchild, british, playability);
+ word.push_back(letter);
+ if (t)
+ m_wordFactory->pushWord(word, !british, playability);
+ if (p)
+ addWordsFromDawgRecursive(lexParams, word, p);
+ index++;
+ word.pop_back();
+ } while (!lastchild);
+}
+
+void LexiconDialog::addWordsFromTextFile(const QString &textFile)
+{
+ if (!m_wordFactory)
+ m_wordFactory = new DawgFactory(m_alphabetFileName);
+
+ QFile file(textFile);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+ return;
+
+ QTextStream stream(&file);
+ stream.setCodec("UTF-8");
+ QString word;
+ while (!stream.atEnd())
+ {
+ stream >> word;
+ word = word.trimmed().toUpper();
+ if (word.isEmpty())
+ continue;
+ QChar firstChar = word[0];
+ if (firstChar < 'A')
+ continue; // allows the usage of most punctuation characters as comments
+ int playability = 0;
+ for (int i = word.size() - 1; i > 0; i--)
+ {
+ if (word[i].isDigit())
+ playability = playability * 10 + word[i].digitValue();
+ }
+ m_wordFactory->pushWord(QuackleIO::Util::qstringToString(word), true, playability);
+ }
+}
+
+void LexiconDialog::loadOriginalDictionary()
+{
+ delete m_wordFactory;
+ m_wordFactory = NULL;
+ string dawgFileName = m_originalName.toStdString() + ".dawg";
+ QString dawgFullFileName;
+ if (!m_originalName.isEmpty())
+ dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName));
+
+ if (!dawgFullFileName.isEmpty())
+ {
+ m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName));
+ m_lexiconInformation->setText(tr("Loading dictionary..."));
+ show();
+ qApp->processEvents();
+ addWordsFromDawgFile(dawgFullFileName);
+ }
+ else
+ m_deleteLexicon->setEnabled(false);
+
+ updateLexiconInformation(true);
+}
+
+void LexiconDialog::accept()
+{
+ string lexiconNameStr = m_lexiconName->text().toStdString();
+ string filename = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".dawg", true);
+ m_lexiconInformation->setText(tr("Compressing and writing dictionary file...\nThis may take a few minutes."));
+ qApp->processEvents();
+ m_wordFactory->generate();
+ m_lexiconInformation->setText(tr("Writing dictionary file..."));
+ qApp->processEvents();
+ m_wordFactory->writeIndex(filename);
+ m_finalLexiconName = m_lexiconName->text();
+ QDialog::accept();
+}
+
+void LexiconDialog::updateLexiconInformation(bool firstTime)
+{
+ QByteArray hash = m_wordFactory ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : "";
+ QString text;
+ QString lengthText;
+
+ int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0;
+ if (wordCount == 0)
+ {
+ delete m_wordFactory;
+ m_wordFactory = NULL;
+ }
+ if (m_wordFactory)
+ lengthText = QString::fromStdString(m_wordFactory->letterCountString());
+
+ if (firstTime)
+ m_originalHash = hash;
+
+ text.append(tr("Word count: "));
+ text.append(QString("%L1").arg(wordCount));
+ text.append("\n");
+ text.append(lengthText);
+ text.append(tr("\nLexicon hash: "));
+ text.append(hash.left(8));
+
+ m_lexiconInformation->setText(text);
+
+ m_saveChanges->setEnabled(true/*hash != m_originalHash && !m_lexiconName->text().isEmpty()*/);
+ m_clearAllWords->setEnabled(hash != m_originalHash);
+}
diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h
new file mode 100644
index 0000000..1f1605b
--- /dev/null
+++ b/quacker/lexicondialog.h
@@ -0,0 +1,86 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QUACKER_LEXICONDIALOG_H
+#define QUACKER_LEXICONDIALOG_H
+
+#include <string>
+#include "game.h"
+#include "lexiconparameters.h"
+
+#include <QWidget>
+#include <QDialog>
+
+using namespace std;
+using namespace Quackle;
+
+class QComboBox;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class DawgFactory;
+class FileNameValidator;
+
+class LexiconDialog : public QDialog
+{
+Q_OBJECT
+
+public:
+ LexiconDialog(QWidget *parent = 0, const QString &originalName = QString());
+ ~LexiconDialog();
+ virtual void accept();
+
+ bool itemWasDeleted() { return m_deleted; };
+ const QString &lexiconName() { return m_finalLexiconName; };
+
+ void updateLexiconInformation(bool firstTime = false);
+
+protected slots:
+ void parametersChanged(const QString &) { updateLexiconInformation(); };
+ void deleteLexicon();
+ void addWordsFromFile();
+ void alphabetChanged(const QString &);
+ void loadOriginalDictionary();
+
+protected:
+ void addWordsFromDawgFile(const QString &dawgfile);
+ void addWordsFromDawgRecursive(const LexiconParameters &lexParams, Quackle::LetterString &word, int index);
+ void addWordsFromTextFile(const QString &textFile);
+
+private:
+ QLineEdit *m_lexiconName;
+ QComboBox *m_alphabetCombo;
+ QPushButton *m_addWordsFromFile;
+ QPushButton *m_clearAllWords;
+ QLabel *m_lexiconInformation;
+ FileNameValidator * m_fileNameValidator;
+
+ QPushButton *m_saveChanges;
+ QPushButton *m_cancel;
+ QPushButton *m_deleteLexicon;
+
+ QString m_originalName;
+ QString m_alphabetFileName;
+ QByteArray m_originalHash;
+ QString m_finalLexiconName;
+ bool m_deleted;
+
+ DawgFactory *m_wordFactory;
+};
+
+#endif
diff --git a/quacker/lister.cpp b/quacker/lister.cpp
index 278bff1..be3e335 100644
--- a/quacker/lister.cpp
+++ b/quacker/lister.cpp
@@ -667,7 +667,6 @@ void NumAnagramsFilter::apply()
{
int twl = 0;
int british = 0;
- int playability = 0;
QString alphagram(QuackleIO::DictFactory::querier()->alphagram((*it).word));
for (Dict::WordList::Iterator word = map[alphagram].begin(); word != map[alphagram].end(); ++word)
@@ -676,8 +675,6 @@ void NumAnagramsFilter::apply()
british++;
else
twl++;
-
- playability += (*word).playability;
}
if ((twl == numTwlAnagrams) && (british == numOswOnlyAnagrams))
diff --git a/quacker/quacker.cpp b/quacker/quacker.cpp
index 098ff84..2f407fa 100644
--- a/quacker/quacker.cpp
+++ b/quacker/quacker.cpp
@@ -155,13 +155,10 @@ void TopLevel::finishInitialization()
void TopLevel::introduceToUser()
{
CustomQSettings settings;
- QString lexiconName = settings.value("quackle/settings/lexicon-name", QString("twl06")).toString();
- if (lexiconName == "csw12") {
- statusMessage(tr("The WESPA wordlist (CSW12) is copyright Harper Collins 2011."));
- } else {
- statusMessage(tr("Enjoy your quackling. Choose \"New game...\" from the Game menu to begin."));
- }
-
+ QString statusText = QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str());
+ if (statusText.isEmpty())
+ statusText = tr("Enjoy your quackling. Choose \"New game...\" from the Game menu to begin.");
+ statusMessage(statusText);
parseCommandLineOptions();
if (!CustomQSettings().contains("quackle/hasBeenRun"))
@@ -2120,17 +2117,34 @@ void TopLevel::firstTimeRun()
void TopLevel::about()
{
- QMessageBox::about(this, tr("About Quackle 0.98"), dialogText(tr(
-"<p><b>Quackle</b> 0.98 is a crossword game playing, analysis, and study tool. Visit the Quackle homepage at <tt><a href=\"http://quackle.org\">http://quackle.org</a></tt> for more information.</p>"
+ QString aboutText = tr(
+"<p><b>Quackle</b> 1.0 is a crossword game playing, analysis, and study tool. Visit the Quackle homepage at <tt><a href=\"http://quackle.org\">http://quackle.org</a></tt> for more information.</p>"
"<p>Quackle was written by Jason Katz-Brown, John O'Laughlin, John Fultz, Matt Liberty, and Anand Buddhdev. We thank the anonymous donor who made this software free.</p>"
-"<p>Copyright 2005-2014 by</p>"
+"<p>Copyright 2005-2015 by</p>"
"<ul>"
"<li>Jason Katz-Brown &lt;jasonkatzbrown@gmail.com&gt;</li>"
"<li>John O'Laughlin &lt;olaughlin@gmail.com&gt;</li>"
"</ul>"
"<p>Quackle is free, open-source software licensed under the terms of the GNU General Public License Version 3. See</p>"
"<p><tt><a href=\"http://quackle.org/LICENSE\">http://quackle.org/LICENSE</a></tt></p>"
-)));
+"<p>Dictionary copyrights</p><ul>"
+);
+
+ FILE* file = fopen(QUACKLE_DATAMANAGER->makeDataFilename("lexica", "copyrights.txt", false).c_str(), "r");
+ if (file)
+ {
+ QTextStream strm(file);
+ while (!strm.atEnd())
+ {
+ QString line = strm.readLine();
+ int pos = line.indexOf(':');
+ if (pos != -1 && pos + 1 < line.size())
+ aboutText += "<li>" + line.mid(pos + 1) + "</li>";
+ }
+ fclose(file);
+ aboutText += "</ul>";
+ }
+ QMessageBox::about(this, tr("About Quackle 1.0"), dialogText(aboutText));
}
void TopLevel::hints()
diff --git a/quacker/quacker.plist b/quacker/quacker.plist
index bf6b31e..eef0ac9 100644
--- a/quacker/quacker.plist
+++ b/quacker/quacker.plist
@@ -21,6 +21,6 @@
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHumanReadableCopyright</key>
- <string>Copyright &copy; 2005-2013 by Jason Katz-Brown & John O'Laughlin</string>
+ <string>Copyright &copy; 2005-2015 by Jason Katz-Brown & John O'Laughlin</string>
</dict>
</plist>
diff --git a/quacker/quacker.pro b/quacker/quacker.pro
index 9906e45..6e3f2e0 100644
--- a/quacker/quacker.pro
+++ b/quacker/quacker.pro
@@ -1,5 +1,5 @@
TEMPLATE = app
-VERSION = 0.99
+VERSION = 1.0
TARGET = Quackle
DEPENDPATH += .. ../quackleio
INCLUDEPATH += . ..
@@ -10,19 +10,18 @@ MOC_DIR = moc
#CONFIG += debug
CONFIG += release
+#Um, why is this necessary? I don't know. But if this isn't here,
+#qmake messes up resulting Visual Studio project files.
+CONFIG -= debug
+
debug {
OBJECTS_DIR = obj/debug
+ QMAKE_LIBDIR += ../lib/debug ../quackleio/lib/debug
}
release {
OBJECTS_DIR = obj/release
-}
-
-debug {
- QMAKE_LIBDIR += ../lib/debug ../quackleio/lib/debug
-}
-release {
- QMAKE_LIBDIR += ../lib/release ../quackleio/lib/release
+ QMAKE_LIBDIR += ../lib/release ../quackleio/lib/release
}
win32:!win32-g++ {
@@ -32,6 +31,8 @@ win32:!win32-g++ {
}
macx:LIBS += -framework CoreFoundation
+QMAKE_CXXFLAGS += -std=c++11
+
# Input
HEADERS += *.h
SOURCES += *.cpp
diff --git a/quacker/settings.cpp b/quacker/settings.cpp
index 301608e..e22a29e 100644
--- a/quacker/settings.cpp
+++ b/quacker/settings.cpp
@@ -45,6 +45,7 @@
#include "boardsetupdialog.h"
#include "customqsettings.h"
#include "graphicalboard.h"
+#include "lexicondialog.h"
Settings *Settings::m_self = 0;
Settings *Settings::self()
@@ -80,17 +81,20 @@ Settings::Settings(QWidget *parent)
#endif
if (QFile::exists("data"))
- m_dataDir = "data";
+ m_appDataDir = "data";
else if (QFile::exists("../data"))
- m_dataDir = "../data";
+ m_appDataDir = "../data";
else if (QFile::exists("Quackle.app/Contents/data"))
- m_dataDir = "Quackle.app/Contents/data";
+ m_appDataDir = "Quackle.app/Contents/data";
else
{
if (!directory.cd("data") || !directory.cd("../data"))
QMessageBox::critical(0, tr("Error Initializing Data Files - Quacker"), tr("<p>Could not open data directory. Quackle will be useless. Try running the quacker executable with quackle/quacker/ as the current directory.</p>"));
- m_dataDir = directory.absolutePath();
+ m_appDataDir = directory.absolutePath();
}
+ m_userDataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ QDir qdir(m_userDataDir);
+ qdir.mkpath("lexica");
}
void Settings::createGUI()
@@ -98,93 +102,88 @@ void Settings::createGUI()
if (m_lexiconNameCombo != 0)
return;
- QVBoxLayout *vlayout = new QVBoxLayout(this);
+ QGridLayout *layout = new QGridLayout(this);
m_lexiconNameCombo = new QComboBox;
connect(m_lexiconNameCombo, SIGNAL(activated(const QString &)), this, SLOT(lexiconChanged(const QString &)));
- QStringList items;
- populateListFromFilenames(items, m_dataDir + "/lexica");
- m_lexiconNameCombo->addItems(items);
+ populateComboFromFilenames(m_lexiconNameCombo, "lexica", ".dawg", "lexicon");
- QLabel *cswText = new QLabel(tr("The WESPA wordlist (CSW12) is copyright Harper Collins 2011."));
-
- QHBoxLayout *lexiconLayout = new QHBoxLayout;
QLabel *lexiconNameLabel = new QLabel(tr("&Lexicon:"));
lexiconNameLabel->setBuddy(m_lexiconNameCombo);
-
- lexiconLayout->addWidget(lexiconNameLabel);
- lexiconLayout->addWidget(m_lexiconNameCombo);
+ m_editLexicon = new QPushButton(tr("Edit..."));
+ m_editLexicon->setMaximumWidth(60);
+ connect(m_editLexicon, SIGNAL(clicked()), this, SLOT(editLexicon()));
m_alphabetNameCombo = new QComboBox;
connect(m_alphabetNameCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &)));
- QStringList alphabetItems;
- populateListFromFilenames(alphabetItems, m_dataDir + "/alphabets");
- m_alphabetNameCombo->addItems(alphabetItems);
+ populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ".quackle_alphabet", "");
- QHBoxLayout *alphabetLayout = new QHBoxLayout;
QLabel *alphabetNameLabel = new QLabel(tr("&Alphabet:"));
alphabetNameLabel->setBuddy(m_alphabetNameCombo);
-
- alphabetLayout->addWidget(alphabetNameLabel);
- alphabetLayout->addWidget(m_alphabetNameCombo);
+ m_editAlphabet = new QPushButton(tr("Edit..."));
+ m_editAlphabet->setMaximumWidth(60);
+ connect(m_editAlphabet, SIGNAL(clicked()), this, SLOT(editAlphabet()));
m_themeNameCombo = new QComboBox;
connect(m_themeNameCombo, SIGNAL(activated(const QString &)), this, SLOT(themeChanged(const QString &)));
- QStringList themeItems;
- populateListFromFilenames(themeItems, m_dataDir + "/themes");
- m_themeNameCombo->addItems(themeItems);
+ populateComboFromFilenames(m_themeNameCombo, "themes", ".ini", "");
- QHBoxLayout *themeLayout = new QHBoxLayout;
QLabel *themeNameLabel = new QLabel(tr("&Theme:"));
themeNameLabel->setBuddy(m_themeNameCombo);
+ m_editTheme = new QPushButton(tr("Edit..."));
+ m_editTheme->setMaximumWidth(60);
+ connect(m_editTheme, SIGNAL(clicked()), this, SLOT(editTheme()));
- themeLayout->addWidget(themeNameLabel);
- themeLayout->addWidget(m_themeNameCombo);
-
- m_boardNameCombo = new QComboBox();
+ m_boardNameCombo = new QComboBox;
connect(m_boardNameCombo, SIGNAL(activated(const QString &)), this, SLOT(boardChanged(const QString &)));
- m_addBoard = new QPushButton(tr("Add Board"));
- connect(m_addBoard, SIGNAL(clicked()), this, SLOT(addBoard()));
+ populateComboFromFilenames(m_boardNameCombo, "boards", "", "board");
- m_editBoard = new QPushButton(tr("&Edit Board"));
+ QLabel *boardNameLabel = new QLabel(tr("&Board:"));
+ boardNameLabel->setBuddy(m_boardNameCombo);
+ m_editBoard = new QPushButton(tr("Edit..."));
+ m_editBoard->setMaximumWidth(60);
connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard()));
- m_deleteBoard = new QPushButton(tr("&Delete Board"));
- connect(m_deleteBoard, SIGNAL(clicked()), this, SLOT(deleteBoard()));
+ m_copyrightLabel = new QLabel();
- loadBoardNameCombo();
+ layout->addWidget(lexiconNameLabel, 0, 0, Qt::AlignRight);
+ layout->addWidget(m_lexiconNameCombo, 0, 1);
+ layout->addWidget(m_editLexicon, 0, 2);
+ layout->addWidget(alphabetNameLabel, 1, 0, Qt::AlignRight);
+ layout->addWidget(m_alphabetNameCombo, 1, 1);
+ // layout->addWidget(m_editAlphabet, 1, 2);
+ layout->addWidget(themeNameLabel, 2, 0, Qt::AlignRight);
+ layout->addWidget(m_themeNameCombo, 2, 1);
+ // layout->addWidget(m_editTheme, 2, 2);
+ layout->addWidget(boardNameLabel, 3, 0, Qt::AlignRight);
+ layout->addWidget(m_boardNameCombo, 3, 1);
+ layout->addWidget(m_editBoard, 3, 2);
+ layout->addWidget(m_copyrightLabel, 4, 0, 1, -1, Qt::AlignTop);
- QGroupBox *boardGroup = new QGroupBox("Game Board Definitions");
- QGridLayout *boardLayout = new QGridLayout(boardGroup);
- QLabel *boardNameLabel = new QLabel(tr("&Board:"));
- boardNameLabel->setBuddy(m_boardNameCombo);
+ layout->setColumnMinimumWidth(3, 0);
+ layout->setColumnStretch(3, 1);
+ layout->setRowMinimumHeight(4, 0);
+ layout->setRowStretch(4, 1);
- boardLayout->addWidget(boardNameLabel, 0, 0);
- boardLayout->addWidget(m_boardNameCombo, 0, 1, 1, -1);
- boardLayout->addWidget(m_addBoard, 1, 0);
- boardLayout->addWidget(m_editBoard, 1, 1);
- boardLayout->addWidget(m_deleteBoard, 1, 2);
-
- vlayout->addWidget(cswText);
- vlayout->addLayout(lexiconLayout);
- vlayout->addLayout(alphabetLayout);
- vlayout->addLayout(themeLayout);
- vlayout->addWidget(boardGroup);
- vlayout->addStretch();
load();
}
void Settings::load()
{
- m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName())));
- m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName())));
+ m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->lexiconName().c_str())));
+ if (m_lexiconNameCombo->currentIndex() == -1)
+ m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->lexiconName().c_str()) + "*"));
+ m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex();
+ m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QString::fromUtf8(QUACKLE_ALPHABET_PARAMETERS->alphabetName().c_str())));
m_themeNameCombo->setCurrentIndex(m_themeNameCombo->findText(m_themeName));
m_boardNameCombo->setCurrentIndex(m_boardNameCombo->findText(QuackleIO::Util::uvStringToQString(QUACKLE_BOARD_PARAMETERS->name())));
+ m_lastGoodBoardValue = m_boardNameCombo->currentIndex();
+ m_copyrightLabel->setText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str()));
}
void Settings::preInitialize()
@@ -198,7 +197,8 @@ void Settings::initialize()
CustomQSettings settings;
QUACKLE_DATAMANAGER->setBackupLexicon("twl06");
- QUACKLE_DATAMANAGER->setDataDirectory(m_dataDir.toStdString());
+ QUACKLE_DATAMANAGER->setAppDataDirectory(m_appDataDir.toStdString());
+ QUACKLE_DATAMANAGER->setUserDataDirectory(m_userDataDir.toStdString());
QString lexiconName = settings.value("quackle/settings/lexicon-name", QString("twl06")).toString();
@@ -206,50 +206,98 @@ void Settings::initialize()
if (lexiconName == "cswfeb07")
lexiconName = "cswapr07";
- setQuackleToUseLexiconName(QuackleIO::Util::qstringToStdString(lexiconName));
- setQuackleToUseAlphabetName(QuackleIO::Util::qstringToStdString(settings.value("quackle/settings/alphabet-name", QString("english")).toString()));
+ setQuackleToUseLexiconName(lexiconName);
+ setQuackleToUseAlphabetName(settings.value("quackle/settings/alphabet-name", QString("english")).toString());
setQuackleToUseThemeName(settings.value("quackle/settings/theme-name", QString("traditional")).toString());
setQuackleToUseBoardName(settings.value("quackle/settings/board-name", QString("")).toString());
}
-void Settings::setQuackleToUseLexiconName(const string &lexiconName)
+void Settings::buildGaddag(const string &filename)
+{
+ GaddagFactory factory((UVString()));
+ Quackle::LetterString word;
+
+ pushIndex(factory, word, 1);
+ factory.generate();
+ factory.writeIndex(filename);
+}
+
+void Settings::pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index)
{
- if (QUACKLE_LEXICON_PARAMETERS->lexiconName() != lexiconName)
+ unsigned int p;
+ Quackle::Letter letter;
+ bool t;
+ bool lastchild;
+ bool british;
+ int playability;
+
+ do
{
- QUACKLE_LEXICON_PARAMETERS->setLexiconName(lexiconName);
+ QUACKLE_LEXICON_PARAMETERS->dawgAt(index, p, letter, t, lastchild, british, playability);
+ word.push_back(letter);
+ if (t)
+ factory.pushWord(word);
+ if (p)
+ pushIndex(factory, word, p);
+ index++;
+ word.pop_back();
+ } while (!lastchild);
+}
- string gaddagFile = Quackle::LexiconParameters::findDictionaryFile(lexiconName + ".gaddag");
- if (gaddagFile.empty())
- {
- UVcout << "Gaddag for lexicon '" << lexiconName << "' does not exist." << endl;
- QUACKLE_LEXICON_PARAMETERS->unloadGaddag();
- }
- else
- QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile);
+void Settings::setQuackleToUseLexiconName(const QString &lexiconName)
+{
+ string lexiconNameStr = lexiconName.toStdString();
+ if (QUACKLE_LEXICON_PARAMETERS->lexiconName() != lexiconNameStr)
+ {
+ QUACKLE_LEXICON_PARAMETERS->setLexiconName(lexiconNameStr);
- string dawgFile = Quackle::LexiconParameters::findDictionaryFile(lexiconName + ".dawg");
+ string dawgFile = Quackle::LexiconParameters::findDictionaryFile(lexiconNameStr + ".dawg");
if (dawgFile.empty())
{
- UVcout << "Dawg for lexicon '" << lexiconName << "' does not exist." << endl;
+ UVcout << "Dawg for lexicon '" << lexiconNameStr << "' does not exist." << endl;
QUACKLE_LEXICON_PARAMETERS->unloadDawg();
}
else
QUACKLE_LEXICON_PARAMETERS->loadDawg(dawgFile);
- QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconName);
+ if (!QUACKLE_LEXICON_PARAMETERS->hasDawg())
+ {
+ QUACKLE_LEXICON_PARAMETERS->unloadGaddag();
+ return;
+ }
+
+ string gaddagFile = Quackle::LexiconParameters::findDictionaryFile(lexiconNameStr + ".gaddag");
+ if (gaddagFile.empty())
+ {
+ UVcout << "Gaddag for lexicon '" << lexiconNameStr << "' does not exist." << endl;
+ QUACKLE_LEXICON_PARAMETERS->unloadGaddag();
+ }
+ else
+ QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile);
+
+ // if (!QUACKLE_LEXICON_PARAMETERS->hasGaddag())
+ // {
+ // gaddagFile = QUACKLE_DATAMANAGER->makeDataFilename("lexica", lexiconNameStr + ".gaddag", true);
+ // buildGaddag(gaddagFile);
+ // QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile);
+ // }
+
+ QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconNameStr);
+ m_copyrightLabel->setText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str()));
}
}
-void Settings::setQuackleToUseAlphabetName(const string &alphabetName)
+void Settings::setQuackleToUseAlphabetName(const QString &alphabetName)
{
- if (QUACKLE_ALPHABET_PARAMETERS->alphabetName() != alphabetName)
+ string alphabetNameStr = alphabetName.toStdString();
+ if (QUACKLE_ALPHABET_PARAMETERS->alphabetName() != alphabetNameStr)
{
- QString alphabetFile = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(alphabetName + ".quackle_alphabet"));
+ QString alphabetFileStr = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(alphabetNameStr));
QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
- flexure->setAlphabetName(alphabetName);
- if (flexure->load(alphabetFile))
+ flexure->setAlphabetName(alphabetNameStr);
+ if (flexure->load(alphabetFileStr))
{
if (flexure->length() != QUACKLE_ALPHABET_PARAMETERS->length() && QUACKLE_ALPHABET_PARAMETERS->alphabetName() != "default")
{
@@ -269,11 +317,13 @@ void Settings::setQuackleToUseAlphabetName(const string &alphabetName)
void Settings::setQuackleToUseThemeName(const QString &themeName)
{
m_themeName = themeName;
- QString themeFile = m_dataDir + "/themes/" + themeName + ".ini";
+ QString themeFile = m_userDataDir + "/themes/" + themeName + ".ini";
+ if (!QFile::exists(themeFile))
+ themeFile = m_appDataDir + "/themes/" + themeName + ".ini";
if (!QFile::exists(themeFile))
{
m_themeName = "traditional";
- themeFile = m_dataDir + "/themes/traditional.ini";
+ themeFile = m_appDataDir + "/themes/traditional.ini";
}
PixmapCacher::self()->readTheme(themeFile);
}
@@ -300,19 +350,34 @@ void Settings::setQuackleToUseBoardName(const QString &boardName)
void Settings::lexiconChanged(const QString &lexiconName)
{
- string lexiconNameString = QuackleIO::Util::qstringToStdString(lexiconName);
- setQuackleToUseLexiconName(lexiconNameString);
+ QString lexicon = lexiconName;
+ if (lexicon.endsWith("*"))
+ lexicon.truncate(lexicon.size() - 1);
+ if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1)
+ {
+ editLexicon();
+ if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1 &&
+ m_lexiconNameCombo->currentIndex() != 0)
+ m_lexiconNameCombo->setCurrentIndex(m_lastGoodLexiconValue);
+ return;
+ }
+ setQuackleToUseLexiconName(lexicon);
+ m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex();
CustomQSettings settings;
- settings.setValue("quackle/settings/lexicon-name", lexiconName);
+ settings.setValue("quackle/settings/lexicon-name", lexicon);
emit refreshViews();
}
void Settings::alphabetChanged(const QString &alphabetName)
{
- string alphabetNameString = QuackleIO::Util::qstringToStdString(alphabetName);
- setQuackleToUseAlphabetName(alphabetNameString);
+ if (m_alphabetNameCombo->currentIndex() == m_alphabetNameCombo->count() - 1)
+ {
+ editAlphabet();
+ return;
+ }
+ setQuackleToUseAlphabetName(alphabetName);
CustomQSettings settings;
settings.setValue("quackle/settings/alphabet-name", alphabetName);
@@ -322,6 +387,11 @@ void Settings::alphabetChanged(const QString &alphabetName)
void Settings::themeChanged(const QString &themeName)
{
+ if (m_themeNameCombo->currentIndex() == m_themeNameCombo->count() - 1)
+ {
+ editTheme();
+ return;
+ }
setQuackleToUseThemeName(themeName);
CustomQSettings settings;
@@ -332,6 +402,14 @@ void Settings::themeChanged(const QString &themeName)
void Settings::boardChanged(const QString &boardName)
{
+ if (m_boardNameCombo->currentIndex() == m_boardNameCombo->count() - 1)
+ {
+ addBoard();
+ if (m_boardNameCombo->currentIndex() == m_boardNameCombo->count() - 1 &&
+ m_boardNameCombo->currentIndex() != 0)
+ m_boardNameCombo->setCurrentIndex(m_lastGoodBoardValue);
+ return;
+ }
CustomQSettings settings;
settings.setValue("quackle/settings/board-name", boardName);
@@ -358,6 +436,7 @@ void Settings::addBoard()
(const uchar *)boardParameterStream.str().data(),
boardParameterStream.str().size());
settings.setValue(boardName, QVariant(boardParameterBytes));
+ m_boardNameCombo->setCurrentIndex(-1);
boardChanged(boardName);
}
else
@@ -391,35 +470,12 @@ void Settings::editBoard()
boardChanged(newBoardName);
}
PixmapCacher::self()->invalidate();
-}
-
-void Settings::deleteBoard()
-{
- int oldIndex = m_boardNameCombo->currentIndex();
- QString boardName = m_boardNameCombo->currentText();
- QString message = "Do you really want to delete the game board \"";
- message += boardName;
- message += "\"?";
- if (QMessageBox::warning(NULL, QString("Confirm Deletion"), message,
- QMessageBox::Yes | QMessageBox::Default,
- QMessageBox::No | QMessageBox::Escape) == QMessageBox::Yes)
- {
- CustomQSettings settings;
- settings.beginGroup("quackle/boardparameters");
- settings.remove(boardName);
- loadBoardNameCombo();
- if (oldIndex != 0)
- oldIndex--;
- m_boardNameCombo->setCurrentIndex(oldIndex);
- boardChanged(m_boardNameCombo->currentText());
- }
+ loadBoardNameCombo();
+ emit refreshViews();
}
void Settings::loadBoardNameCombo()
{
- if (m_lexiconNameCombo == 0)
- return;
-
while (m_boardNameCombo->count() > 0)
m_boardNameCombo->removeItem(0);
@@ -428,26 +484,100 @@ void Settings::loadBoardNameCombo()
QStringList boardNames = settings.childKeys();
boardNames.sort();
m_boardNameCombo->addItems(boardNames);
+ m_boardNameCombo->addItem("Add new board...");
settings.endGroup();
QString currentItem = settings.value("quackle/settings/board-name", QString("")).toString();
- m_boardNameCombo->setCurrentIndex(m_boardNameCombo->findText(currentItem));
+ int currentItemIndex = m_boardNameCombo->findText(currentItem);
+ if (m_boardNameCombo->count() > 0 && currentItemIndex < 0)
+ currentItemIndex = 0;
+ m_boardNameCombo->setCurrentIndex(currentItemIndex);
+}
+
+void Settings::editLexicon()
+{
+ QString name = m_lexiconNameCombo->currentText();
+ if (name.endsWith("*"))
+ name.truncate(name.size() - 1);
+ if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1)
+ name = "";
+ LexiconDialog dialog(this, name);
+ if (dialog.exec())
+ {
+ populateComboFromFilenames(m_lexiconNameCombo, "lexica", ".dawg", "lexicon");
+ qApp->processEvents();
+ if (dialog.itemWasDeleted())
+ {
+ m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(name));
+ QUACKLE_LEXICON_PARAMETERS->setLexiconName(""); // force lexicon to reload
+ QUACKLE_LEXICON_PARAMETERS->unloadAll();
+ if (m_lexiconNameCombo->currentIndex() != -1)
+ setQuackleToUseLexiconName(name);
+ }
+ else if (!dialog.lexiconName().isEmpty())
+ {
+ QUACKLE_LEXICON_PARAMETERS->setLexiconName(""); // force lexicon to reload
+ QUACKLE_LEXICON_PARAMETERS->unloadAll();
+ setQuackleToUseLexiconName(dialog.lexiconName());
+ m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(name + "*"));
+ }
+ load();
+ }
+}
- m_editBoard->setEnabled(boardNames.count() > 0);
- m_deleteBoard->setEnabled(boardNames.count() > 0);
+void Settings::editAlphabet()
+{
+#if 0
+ QString name = m_alphabetNameCombo->currentText();
+ if (m_alphabetNameCombo->currentIndex() == m_alphabetNameCombo->count() - 1)
+ name = "";
+ AlphabetDialog dialog(this);
+ if (dialog.exec())
+ {
+ populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ".quackle_alphabet", "alphabet");
+ load();
+ }
+#endif // 0
+}
+
+void Settings::editTheme()
+{
+#if 0
+ QString name = m_themeNameCombo->currentText();
+ if (m_themeNameCombo->currentIndex() == m_themeNameCombo->count() - 1)
+ name = "";
+ ThemeDialog dialog(this);
+ if (dialog.exec())
+ {
+ populateThemeFromFilenames(m_themeNameCombo, "themes", "theme");
+ load();
+ }
+#endif // 0
}
-void Settings::populateListFromFilenames(QStringList& list, const QString &path)
+void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &extension, const QString &label)
{
- QDir dir(path);
- QStringList fileList = dir.entryList(QDir::Files | QDir::Readable, QDir::Name);
- QStringListIterator i(fileList);
+ while (combo->count() > 0)
+ combo->removeItem(0);
+
+ QStringList fileList;
+ QDir dir(self()->m_appDataDir);
+ if (dir.cd(path))
+ fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name);
+ dir = QDir(self()->m_userDataDir);
+ if (dir.cd(path))
+ fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name);
+
+ QStringList::iterator i;
QString fileName;
+ QStringList list;
int periodPos;
- while (i.hasNext())
+ for (i = fileList.begin(); i != fileList.end(); ++i)
{
- fileName = i.next();
+ fileName = *i;
+ if (!fileName.endsWith(extension))
+ continue;
periodPos = fileName.indexOf('.');
if (periodPos)
{
@@ -455,5 +585,21 @@ void Settings::populateListFromFilenames(QStringList& list, const QString &path)
list << fileName;
}
}
- list.removeDuplicates();
+
+ for (i = list.begin(); i != list.end(); ++i)
+ {
+ for (QStringList::iterator j = i + 1; j != list.end(); ++j)
+ {
+ if (*i == *j)
+ {
+ *i = *i + "*";
+ list.erase(j);
+ break;
+ }
+ }
+ }
+
+ combo->addItems(list);
+ if (label.size() > 0)
+ combo->addItem(QString(tr("Add new ")).append(path).append("..."));
}
diff --git a/quacker/settings.h b/quacker/settings.h
index 785b726..ef3449e 100644
--- a/quacker/settings.h
+++ b/quacker/settings.h
@@ -24,9 +24,12 @@
#include <QWidget>
#include <QSettings>
+#include "quackleio/gaddagfactory.h"
+
class QComboBox;
class QCheckBox;
class QPushButton;
+class QLabel;
using namespace std;
@@ -39,6 +42,9 @@ public:
static Settings *self();
+ // load up an item list based on a list of filenames
+ static void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &extension, const QString &label);
+
signals:
void refreshViews();
@@ -64,10 +70,13 @@ protected slots:
void addBoard();
void editBoard();
- void deleteBoard();
- void setQuackleToUseLexiconName(const string &lexiconName);
- void setQuackleToUseAlphabetName(const string &alphabetName);
+ void editLexicon();
+ void editAlphabet();
+ void editTheme();
+
+ void setQuackleToUseLexiconName(const QString &lexiconName);
+ void setQuackleToUseAlphabetName(const QString &alphabetName);
void setQuackleToUseThemeName(const QString &themeName);
void setQuackleToUseBoardName(const QString &lexiconName);
@@ -76,20 +85,25 @@ protected:
QComboBox *m_alphabetNameCombo;
QComboBox *m_themeNameCombo;
QComboBox *m_boardNameCombo;
- QPushButton *m_addBoard;
+ QPushButton *m_editLexicon;
+ QPushButton *m_editAlphabet;
+ QPushButton *m_editTheme;
QPushButton *m_editBoard;
- QPushButton *m_deleteBoard;
- QString m_dataDir;
+ QLabel *m_copyrightLabel;
+ QString m_appDataDir;
+ QString m_userDataDir;
QString m_themeName;
private:
// populate the popup based on what's in QSettings
void loadBoardNameCombo();
- // load up an item list based on a list of filenames
- void populateListFromFilenames(QStringList& list, const QString &path);
-
+ void buildGaddag(const string &filename);
+ void pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index);
+
static Settings *m_self;
+ int m_lastGoodLexiconValue;
+ int m_lastGoodBoardValue;
};
#endif
diff --git a/quackle.pro b/quackle.pro
index 18d9a33..4bd40f4 100644
--- a/quackle.pro
+++ b/quackle.pro
@@ -17,6 +17,8 @@ release {
DESTDIR = lib/release
}
+QMAKE_CXXFLAGS += -std=c++11
+
# enable/disable debug symbols
#CONFIG += debug staticlib
CONFIG += release staticlib
diff --git a/quackle.sublime-project b/quackle.sublime-project
index 167db4b..c7f47cd 100644
--- a/quackle.sublime-project
+++ b/quackle.sublime-project
@@ -5,9 +5,10 @@
"path": ".",
"file_exclude_patterns" : ["*.tgz", "*.sublime-workspace", ".tags*", "dawginput.raw",
"playabilities.raw", "smaller.raw", ".gitattributes",
- "*.Debug", "*.Release", "*.pfx", "*.cer"],
- "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "lexica",
- "strategy", "debug", "release", "makeswelexicon", "lisp"]
+ "*.Debug", "*.Release", "*.pfx", "*.cer",
+ "makegaddag", "makeminidawg", "gaddagize", "Makefile", "*.dawg", "*.gaddag"],
+ "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib",
+ "strategy", "debug", "release", "makeswelexicon", "lisp", "DerivedData"]
}
]
}
diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp
new file mode 100644
index 0000000..4dd4ec3
--- /dev/null
+++ b/quackleio/dawgfactory.cpp
@@ -0,0 +1,326 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <QtCore>
+#include <QCryptographicHash>
+
+#include "dawgfactory.h"
+#include "util.h"
+
+
+DawgFactory::DawgFactory(const QString &alphabetFile)
+ : m_encodableWords(0), m_unencodableWords(0), m_duplicateWords(0),
+ m_countsByLength(Quackle::FixedLengthString::maxSize, 0)
+{
+ QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
+ flexure->load(alphabetFile);
+ m_alphas = flexure;
+
+ m_root.insmallerdict = false;
+ m_root.playability = 0;
+ m_root.c = QUACKLE_BLANK_MARK;
+ m_root.pointer = 0;
+ m_root.lastchild = true;
+
+ m_hash.int32ptr[0] = m_hash.int32ptr[1] = m_hash.int32ptr[2] = m_hash.int32ptr[3] = 0;
+}
+
+DawgFactory::~DawgFactory()
+{
+ delete m_alphas;
+}
+
+bool DawgFactory::pushWord(const UVString &word, bool inSmaller, int playability)
+{
+ UVString leftover;
+ Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover);
+ if (leftover.empty())
+ return pushWord(encodedWord, inSmaller, playability);
+
+ ++m_unencodableWords;
+ return false;
+}
+
+bool DawgFactory::pushWord(const Quackle::LetterString &word, bool inSmaller, int playability)
+{
+ if (m_root.pushWord(word, inSmaller, playability))
+ {
+ ++m_encodableWords;
+ ++m_countsByLength[word.length()];
+ hashWord(word);
+ return true;
+ }
+ ++m_duplicateWords;
+ return false;
+}
+
+void DawgFactory::hashWord(const Quackle::LetterString &word)
+{
+ QCryptographicHash wordhash(QCryptographicHash::Md5);
+ wordhash.addData(word.constData(), word.length());
+ QByteArray wordhashbytes = wordhash.result();
+ m_hash.int32ptr[0] ^= ((const int32_t*)wordhashbytes.constData())[0];
+ m_hash.int32ptr[1] ^= ((const int32_t*)wordhashbytes.constData())[1];
+ m_hash.int32ptr[2] ^= ((const int32_t*)wordhashbytes.constData())[2];
+ m_hash.int32ptr[3] ^= ((const int32_t*)wordhashbytes.constData())[3];
+}
+
+void DawgFactory::generate()
+{
+ const int bucketcount = 2000;
+ vector< int > bucket[bucketcount];
+
+ m_nodelist.clear();
+ m_nodelist.push_back(&m_root);
+ m_root.print(m_nodelist);
+
+ m_nodelist[0]->letterSum();
+
+ for (unsigned int i = 0; i < m_nodelist.size(); i++)
+ {
+ bucket[m_nodelist[i]->sum % bucketcount].push_back(i);
+ m_nodelist[i]->pointer = 0;
+ m_nodelist[i]->written = false;
+ m_nodelist[i]->deleted = false;
+ m_nodelist[i]->cloneof = NULL;
+ }
+
+ for (int b = 0; b < bucketcount; b++)
+ {
+ if (bucket[b].size() == 0)
+ continue;
+ for (vector<int>::iterator it = bucket[b].begin(); it != bucket[b].end() - 1; it++)
+ {
+ if (!m_nodelist[(*it)]->deleted)
+ {
+ for (vector<int>::iterator jt = it + 1; jt != bucket[b].end(); jt++)
+ {
+ if (!m_nodelist[(*jt)]->deleted)
+ {
+ // cout << "Comparing " << (*it) << " and " << (*jt) << endl;
+ if (m_nodelist[(*it)]->equals(m_nodelist[(*jt)][0]))
+ {
+ //cout << "Hey! " << (*it) << " == " << (*jt) << endl;
+ // ones[l].erase(jt);
+ m_nodelist[(*jt)]->deleted = true;
+ m_nodelist[(*jt)]->cloneof = m_nodelist[(*it)];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_nodelist.clear();
+ m_nodelist.push_back(&m_root);
+ m_root.print(m_nodelist);
+}
+
+void DawgFactory::writeIndex(const string &filename)
+{
+ ofstream out(filename.c_str(), ios::out | ios::binary);
+ unsigned char bytes[7];
+
+ bytes[0] = (m_encodableWords & 0x00FF0000) >> 16;
+ bytes[1] = (m_encodableWords & 0x0000FF00) >> 8;
+ bytes[2] = (m_encodableWords & 0x000000FF);
+
+ out.put(1); // DAWG format version 1
+ out.write(m_hash.charptr, sizeof(m_hash.charptr));
+ out.write((char*)bytes, 3);
+ out.put((char)m_alphas->length());
+ for (Quackle::Letter i = m_alphas->firstLetter(); i <= m_alphas->lastLetter(); i++)
+ {
+ QString letterText = QuackleIO::Util::uvStringToQString(m_alphas->letterParameter(i).text());
+ QByteArray utf8bytes = letterText.toUtf8();
+ string utf8LetterText(utf8bytes.constData());
+ out << utf8LetterText << ' ';
+ }
+
+ for (unsigned int i = 0; i < m_nodelist.size(); i++) {
+ //cout << m_nodelist[i]->c << " " << m_nodelist[i]->pointer << " " << m_nodelist[i]->t << " " << m_nodelist[i]->lastchild << endl;
+ Node* n = m_nodelist[i];
+ unsigned int p;
+ if (m_nodelist[i]->deleted)
+ {
+ p = (unsigned int)(m_nodelist[i]->cloneof->pointer);
+ // n = m_nodelist[i]->cloneof;
+ }
+ else
+ p = (unsigned int)(m_nodelist[i]->pointer);
+
+ bytes[0] = (p & 0x00FF0000) >> 16;
+ bytes[1] = (p & 0x0000FF00) >> 8;
+ bytes[2] = (p & 0x000000FF);
+ bytes[3] = n->c - QUACKLE_FIRST_LETTER;
+
+ unsigned int pb = n->playability;
+ bytes[4] = (pb & 0x00FF0000) >> 16;
+ bytes[5] = (pb & 0x0000FF00) >> 8;
+ bytes[6] = (pb & 0x000000FF);
+
+ if (n->lastchild) {
+ bytes[3] |= 64;
+ }
+ if (n->insmallerdict) {
+ bytes[3] |= 128;
+ }
+
+ out.write((char*)bytes, 7);
+ }
+}
+
+string DawgFactory::letterCountString() const
+{
+ ostringstream str;
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ const int letterCount = j * 4 + i + 2;
+ if (j != 0)
+ str << "\t";
+ if (m_countsByLength[letterCount] > 0)
+ str << letterCount << "s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[letterCount];
+ }
+ str << "\n";
+ }
+ return str.str();
+}
+
+
+void DawgFactory::Node::print(vector< Node* > &nodelist)
+{
+ written = true;
+
+ if (children.size() == 0)
+ return;
+
+ if (!deleted)
+ {
+ //cout << " Setting pointer to " << nodelist.size() << " before I push_back the children." << endl;
+ pointer = nodelist.size();
+ }
+ else
+ {
+ pointer = cloneof->pointer;
+ //cout << " Setting pointer to clone's (" << pointer << ") and not pushing anything." << endl;
+ }
+
+ if (!deleted)
+ {
+ for (unsigned int i = 0; i < children.size(); i++) {
+ nodelist.push_back(&children[i]);
+ }
+
+ for (unsigned int i = 0; i < children.size(); i++) {
+ if (!children[i].deleted)
+ children[i].print(nodelist);
+ else if (!children[i].cloneof->written)
+ children[i].cloneof->print(nodelist);
+ }
+ }
+
+ if (children.size() > 0)
+ children[children.size() - 1].lastchild = true;
+}
+
+
+// returns true if the word was actually added...false if it's a duplicate.
+bool DawgFactory::Node::pushWord(const Quackle::LetterString &word, bool inSmaller, int pb)
+{
+ bool added;
+ if (word.length() == 0) {
+ added = (playability == 0);
+ playability = (pb == 0) ? 1 : pb; // word terminators nodes are marked by nonzero playability in the v1 DAWG format
+ insmallerdict = inSmaller;
+ }
+ else {
+ char first = word[0];
+ Quackle::LetterString rest = word.substr(1, word.length() - 1);
+ int index = -1;
+
+ // cout << "first: " << first << ", rest: " << rest << endl;
+
+ for (unsigned int i = 0; i < children.size(); i++) {
+ if (children[i].c == first) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ Node n;
+ n.c = first;
+ n.playability = 0;
+ n.insmallerdict = false;
+ n.pointer = 0;
+ n.lastchild = false;
+ children.push_back(n);
+ index = children.size() - 1;
+ }
+
+ added = children[index].pushWord(rest, inSmaller, pb);
+ }
+
+ sumexplored = false;
+ deleted = false;
+ written = false;
+ return added;
+}
+
+
+bool DawgFactory::Node::equals(const Node &n) const
+{
+ if (playability != n.playability)
+ return false;
+ if (c != n.c)
+ return false;
+ if (children.size() != n.children.size())
+ return false;
+ if (insmallerdict != n.insmallerdict)
+ return false;
+ if (sum != n.sum)
+ return false;
+
+ for (unsigned int i = 0; i < children.size(); i++)
+ if (!children[i].equals(n.children[i]))
+ return false;
+
+ return true;
+}
+
+int DawgFactory::Node::letterSum() const
+{
+ if (sumexplored)
+ return sum;
+
+ sumexplored = true;
+
+ // djb2 checksum
+ sum = 5381 * 33 + (int) c;
+
+ for (unsigned int i = 0; i < children.size(); i++)
+ sum = (sum << 5) + sum + children[i].letterSum();
+
+ return sum;
+}
diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h
new file mode 100644
index 0000000..7af4d68
--- /dev/null
+++ b/quackleio/dawgfactory.h
@@ -0,0 +1,94 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QUACKLE_DAWGFACTORY_H
+#define QUACKLE_DAWGFACTORY_H
+
+#include <cstdint>
+#include <string>
+#include <vector>
+#include "flexiblealphabet.h"
+
+
+class DawgFactory {
+public:
+
+ DawgFactory(const QString &alphabetFile);
+ ~DawgFactory();
+
+ int wordCount() const { return m_encodableWords; };
+ string letterCountString() const;
+ int nodeCount() const { return m_nodelist.size(); };
+ int encodableWords() const { return m_encodableWords; };
+ int unencodableWords() const { return m_unencodableWords; };
+ int duplicateWords() const { return m_duplicateWords; };
+
+ bool pushWord(const UVString &word, bool inSmaller, int playability);
+ bool pushWord(const Quackle::LetterString &word, bool inSmaller, int playability);
+ void hashWord(const Quackle::LetterString &word);
+ void generate();
+ void writeIndex(const string &filename);
+
+ const char* hashBytes() { return m_hash.charptr; };
+
+private:
+ class Node {
+ public:
+ bool pushWord(const Quackle::LetterString& word, bool inSmaller, int pb);
+ void print(vector< Node* > &m_nodelist);
+
+ int letterSum() const;
+ bool equals(const Node &n) const;
+
+ Quackle::Letter c;
+ bool insmallerdict;
+ int playability; // if nonzero, then terminates word
+
+ vector<Node> children;
+ int pointer;
+ int location;
+
+ bool lastchild;
+
+ mutable bool sumexplored;
+ mutable unsigned int sum;
+ mutable vector<int> counts;
+
+ bool deleted;
+ Node* cloneof;
+ bool written;
+ };
+
+ int m_encodableWords;
+ int m_unencodableWords;
+ int m_duplicateWords;
+ vector< Node* > m_nodelist;
+ vector<unsigned int> m_countsByLength;
+ Quackle::AlphabetParameters *m_alphas;
+ Node m_root;
+ union {
+ char charptr[16];
+ std::int32_t int32ptr[4];
+ } m_hash;
+
+ static const char m_versionNumber = 1;
+};
+
+#endif
+
+
diff --git a/quackleio/flexiblealphabet.h b/quackleio/flexiblealphabet.h
index 89bd1f4..d5db68a 100644
--- a/quackleio/flexiblealphabet.h
+++ b/quackleio/flexiblealphabet.h
@@ -21,8 +21,6 @@
#include "alphabetparameters.h"
-class QString;
-
namespace QuackleIO
{
diff --git a/quackleio/gaddagfactory.cpp b/quackleio/gaddagfactory.cpp
new file mode 100644
index 0000000..53ccf04
--- /dev/null
+++ b/quackleio/gaddagfactory.cpp
@@ -0,0 +1,200 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <iostream>
+#include <QtCore>
+#include <QCryptographicHash>
+
+#include "gaddagfactory.h"
+#include "util.h"
+
+GaddagFactory::GaddagFactory(const UVString &alphabetFile)
+ : m_encodableWords(0), m_unencodableWords(0), m_alphas(NULL)
+{
+ if (!alphabetFile.empty())
+ {
+ QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
+ flexure->load(QuackleIO::Util::uvStringToQString(alphabetFile));
+ m_alphas = flexure;
+ }
+
+ // So the separator is sorted to last.
+ m_root.t = false;
+ m_root.c = QUACKLE_NULL_MARK; // "_"
+ m_root.pointer = 0;
+ m_root.lastchild = true;
+
+ m_hash.int32ptr[0] = m_hash.int32ptr[1] = m_hash.int32ptr[2] = m_hash.int32ptr[3] = 0;
+}
+
+GaddagFactory::~GaddagFactory()
+{
+ delete m_alphas;
+}
+
+bool GaddagFactory::pushWord(const UVString &word)
+{
+ UVString leftover;
+ Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover);
+ if (leftover.empty())
+ {
+ pushWord(encodedWord);
+ return true;
+ }
+
+ ++m_unencodableWords;
+ return false;
+}
+
+bool GaddagFactory::pushWord(const Quackle::LetterString &word)
+{
+ ++m_encodableWords;
+ hashWord(word);
+ // FIXME: This hash will fail if duplicate words are passed in.
+ // But testing for duplicate words isn't so easy without keeping
+ // an entirely separate list.
+
+ for (unsigned i = 1; i <= word.length(); i++)
+ {
+ Quackle::LetterString newword;
+
+ for (int j = i - 1; j >= 0; j--)
+ newword.push_back(word[j]);
+
+ if (i < word.length())
+ {
+ newword.push_back(internalSeparatorRepresentation); // "^"
+ for (unsigned j = i; j < word.length(); j++)
+ newword.push_back(word[j]);
+ }
+ m_gaddagizedWords.push_back(newword);
+ }
+ return true;
+}
+
+void GaddagFactory::hashWord(const Quackle::LetterString &word)
+{
+ QCryptographicHash wordhash(QCryptographicHash::Md5);
+ wordhash.addData(word.constData(), word.length());
+ QByteArray wordhashbytes = wordhash.result();
+ m_hash.int32ptr[0] ^= ((const int32_t*)wordhashbytes.constData())[0];
+ m_hash.int32ptr[1] ^= ((const int32_t*)wordhashbytes.constData())[1];
+ m_hash.int32ptr[2] ^= ((const int32_t*)wordhashbytes.constData())[2];
+ m_hash.int32ptr[3] ^= ((const int32_t*)wordhashbytes.constData())[3];
+}
+
+void GaddagFactory::generate()
+{
+ sort(m_gaddagizedWords.begin(), m_gaddagizedWords.end());
+ Quackle::WordList::const_iterator wordsEnd = m_gaddagizedWords.end();
+ for (Quackle::WordList::const_iterator wordsIt = m_gaddagizedWords.begin(); wordsIt != wordsEnd; ++wordsIt)
+ m_root.pushWord(*wordsIt);
+ // for (const auto& words : gaddaggizedWords)
+ // m_root.pushWord(words);
+}
+
+void GaddagFactory::writeIndex(const string &fname)
+{
+ m_nodelist.push_back(&m_root);
+
+ m_root.print(m_nodelist);
+
+ ofstream out(fname.c_str(), ios::out | ios::binary);
+
+ out.put(1); // GADDAG format version 1
+ out.write(m_hash.charptr, sizeof(m_hash.charptr));
+
+ for (size_t i = 0; i < m_nodelist.size(); i++)
+ {
+ unsigned int p = (unsigned int)(m_nodelist[i]->pointer);
+ if (p != 0)
+ p -= i; // offset indexing
+
+ char bytes[4];
+ unsigned char n1 = (p & 0x00FF0000) >> 16;
+ unsigned char n2 = (p & 0x0000FF00) >> 8;
+ unsigned char n3 = (p & 0x000000FF) >> 0;
+ unsigned char n4;
+
+ n4 = m_nodelist[i]->c;
+ if (n4 == internalSeparatorRepresentation)
+ n4 = QUACKLE_NULL_MARK;
+
+ if (m_nodelist[i]->t)
+ n4 |= 64;
+
+ if (m_nodelist[i]->lastchild)
+ n4 |= 128;
+
+ bytes[0] = n1; bytes[1] = n2; bytes[2] = n3; bytes[3] = n4;
+ out.write(bytes, 4);
+ }
+}
+
+
+void GaddagFactory::Node::print(vector< Node* >& nodelist)
+{
+ if (children.size() > 0)
+ {
+ pointer = nodelist.size();
+ children[children.size() - 1].lastchild = true;
+ }
+
+ for (size_t i = 0; i < children.size(); i++)
+ nodelist.push_back(&children[i]);
+
+ for (size_t i = 0; i < children.size(); i++)
+ children[i].print(nodelist);
+}
+
+
+void GaddagFactory::Node::pushWord(const Quackle::LetterString& word)
+{
+ if (word.length() == 0)
+ {
+ t = true;
+ return;
+ }
+
+ Quackle::Letter first = Quackle::String::front(word);
+ Quackle::LetterString rest = Quackle::String::allButFront(word);
+ int index = -1;
+
+ for (size_t i = 0; i < children.size(); i++)
+ {
+ if (children[i].c == first)
+ {
+ index = i;
+ i = children.size();
+ }
+ }
+
+ if (index == -1)
+ {
+ Node n;
+ n.c = first;
+ n.t = false;
+ n.pointer = 0;
+ n.lastchild = false;
+ children.push_back(n);
+ index = children.size() - 1;
+ }
+
+ children[index].pushWord(rest);
+}
diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h
new file mode 100644
index 0000000..3017085
--- /dev/null
+++ b/quackleio/gaddagfactory.h
@@ -0,0 +1,74 @@
+/*
+ * Quackle -- Crossword game artificial intelligence and analysis tool
+ * Copyright (C) 2005-2014 Jason Katz-Brown and John O'Laughlin.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QUACKLE_GADDAGFACTORY_H
+#define QUACKLE_GADDAGFACTORY_H
+
+#include <cstdint>
+#include "flexiblealphabet.h"
+
+
+class GaddagFactory {
+public:
+
+ static const Quackle::Letter internalSeparatorRepresentation = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE;
+
+ GaddagFactory(const UVString &alphabetFile);
+ ~GaddagFactory();
+
+ int wordCount() const { return m_gaddagizedWords.size(); };
+ int nodeCount() const { return m_nodelist.size(); };
+ int encodableWords() const { return m_encodableWords; };
+ int unencodableWords() const { return m_unencodableWords; };
+
+ bool pushWord(const UVString &word);
+ bool pushWord(const Quackle::LetterString &word);
+ void hashWord(const Quackle::LetterString &word);
+ void sortWords() { sort(m_gaddagizedWords.begin(), m_gaddagizedWords.end()); };
+ void generate();
+ void writeIndex(const string &fname);
+
+ const char* hashBytes() { return m_hash.charptr; };
+
+
+private:
+ class Node {
+ public:
+ Quackle::Letter c;
+ bool t;
+ vector<Node> children;
+ int pointer;
+ bool lastchild;
+ void pushWord(const Quackle::LetterString& word);
+ void print(vector< Node* >& m_nodelist);
+ };
+
+ int m_encodableWords;
+ int m_unencodableWords;
+ Quackle::WordList m_gaddagizedWords;
+ vector< Node* > m_nodelist;
+ Quackle::AlphabetParameters *m_alphas;
+ Node m_root;
+ union {
+ char charptr[16];
+ std::int32_t int32ptr[4];
+ } m_hash;
+};
+
+#endif
+
diff --git a/quackleio/iotest/iotest.pro b/quackleio/iotest/iotest.pro
index 0bf472e..c7e994f 100644
--- a/quackleio/iotest/iotest.pro
+++ b/quackleio/iotest/iotest.pro
@@ -8,16 +8,20 @@ CONFIG += release
debug {
OBJECTS_DIR = obj/debug
+ QMAKE_LIBDIR += ../../lib/debug ../../quackleio/lib/debug
}
release {
OBJECTS_DIR = obj/release
+ QMAKE_LIBDIR += ../../lib/release ../../quackleio/lib/release
}
-LIBS += -lquackleio -lquackle
+win32:!win32-g++ {
+ LIBS += -lquackleio -llibquackle
+} else {
+ LIBS += -lquackleio -lquackle
+}
-QMAKE_LFLAGS_RELEASE += -L../../lib/release -L../../quackleio/lib/release
-QMAKE_LFLAGS_DEBUG += -L../../lib/debug -L../../quackleio/lib/debug
# Input
HEADERS += trademarkedboards.h
diff --git a/quackleio/quackleio.pro b/quackleio/quackleio.pro
index 195b0ad..8e43d29 100644
--- a/quackleio/quackleio.pro
+++ b/quackleio/quackleio.pro
@@ -15,6 +15,8 @@ release {
MOC_DIR = moc
+QMAKE_CXXFLAGS += -std=c++11
+
# enable/disable debug symbols
#CONFIG += debug staticlib
CONFIG += release staticlib
diff --git a/quackletest.cpp b/quackletest.cpp
index 5f3066c..7ea5d10 100644
--- a/quackletest.cpp
+++ b/quackletest.cpp
@@ -45,9 +45,9 @@ int main()
{
Quackle::DataManager dataManager;
- dataManager.setDataDirectory("data");
+ dataManager.setAppDataDirectory("data");
dataManager.lexiconParameters()->loadDawg(Quackle::LexiconParameters::findDictionaryFile("twl06.dawg"));
- dataManager.lexiconParameters()->loadGaddag(Quackle::LexiconParameters::findDictionaryFile("twl06.gaddag"));
+ dataManager.lexiconParameters()->loadGaddag(Quackle::LexiconParameters::findDictionaryFile("twl06.gaddag"));
dataManager.strategyParameters()->initialize("twl06");
dataManager.setBoardParameters(new Quackle::EnglishBoard());
@@ -58,7 +58,7 @@ int main()
const int gameCnt = 1000;
//const int gameCnt = 1;
for (int game = 0; game < gameCnt; ++game) {
- testGame();
+ testGame();
}
return 0;
diff --git a/test/test.pro b/test/test.pro
index 0284f47..99a378e 100644
--- a/test/test.pro
+++ b/test/test.pro
@@ -12,16 +12,20 @@ CONFIG += release
debug {
OBJECTS_DIR = obj/debug
+ QMAKE_LIBDIR += ../lib/debug ../quackleio/lib/debug
}
release {
OBJECTS_DIR = obj/release
+ QMAKE_LIBDIR += ../lib/release ../quackleio/lib/release
}
-LIBS += -lquackleio -lquackle
+win32:!win32-g++ {
+ LIBS += -lquackleio -llibquackle
+} else {
+ LIBS += -lquackleio -lquackle
+}
-QMAKE_LFLAGS_RELEASE += -L../lib/release -L../quackleio/lib/release
-QMAKE_LFLAGS_DEBUG += -L../lib/debug -L../quackleio/lib/debug
# Input
HEADERS += testharness.h trademarkedboards.h
diff --git a/test/testharness.cpp b/test/testharness.cpp
index 64847d1..172e403 100644
--- a/test/testharness.cpp
+++ b/test/testharness.cpp
@@ -191,9 +191,9 @@ void TestHarness::startUp()
UVcout << "Starting up.";
m_dataManager.setBackupLexicon("twl06");
- m_dataManager.setDataDirectory("../data");
+ m_dataManager.setAppDataDirectory("../data");
- QString alphabetFile = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(m_alphabet) + ".quackle_alphabet"));
+ QString alphabetFile = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(m_alphabet)));
QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters;
if (flexure->load(alphabetFile))
{
@@ -207,13 +207,13 @@ void TestHarness::startUp()
m_dataManager.setBoardParameters(new ScrabbleBoard());
- m_dataManager.lexiconParameters()->loadGaddag(Quackle::LexiconParameters::findDictionaryFile(QuackleIO::Util::qstringToStdString(m_lexicon + ".gaddag")));
+ m_dataManager.lexiconParameters()->loadDawg(Quackle::LexiconParameters::findDictionaryFile(QuackleIO::Util::qstringToStdString(m_lexicon + ".dawg")));
UVcout << ".";
- m_dataManager.lexiconParameters()->loadDawg(Quackle::LexiconParameters::findDictionaryFile(QuackleIO::Util::qstringToStdString(m_lexicon + ".dawg")));
+ m_dataManager.lexiconParameters()->loadGaddag(Quackle::LexiconParameters::findDictionaryFile(QuackleIO::Util::qstringToStdString(m_lexicon + ".gaddag")));
+ UVcout << ".";
m_dataManager.strategyParameters()->initialize(QuackleIO::Util::qstringToStdString(m_lexicon));
- UVcout << ".";
UVcout << endl;