From 9eada6e94f345d0c1a2d93de5dc56613a93a3ea0 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 5 Jan 2015 00:31:05 -0600 Subject: Begin unifying interface for customizable settings. --- quacker/settings.cpp | 90 ++++++++++++++++++++++++++++++++++------------------ quacker/settings.h | 5 +-- 2 files changed, 62 insertions(+), 33 deletions(-) diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 301608e..67804ad 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -103,77 +103,102 @@ void Settings::createGUI() 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); - - QLabel *cswText = new QLabel(tr("The WESPA wordlist (CSW12) is copyright Harper Collins 2011.")); + QStringList lexiconItems; + populateListFromFilenames(lexiconItems, "lexica"); + m_lexiconNameCombo->addItems(lexiconItems); QHBoxLayout *lexiconLayout = new QHBoxLayout; QLabel *lexiconNameLabel = new QLabel(tr("&Lexicon:")); lexiconNameLabel->setBuddy(m_lexiconNameCombo); + m_editLexicon = new QPushButton(tr("Edit...")); + m_editLexicon->setMaximumWidth(60); + connect(m_editLexicon, SIGNAL(clicked()), this, SLOT(editLexicon())); lexiconLayout->addWidget(lexiconNameLabel); lexiconLayout->addWidget(m_lexiconNameCombo); + lexiconLayout->addWidget(m_editLexicon); m_alphabetNameCombo = new QComboBox; connect(m_alphabetNameCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &))); QStringList alphabetItems; - populateListFromFilenames(alphabetItems, m_dataDir + "/alphabets"); + populateListFromFilenames(alphabetItems, "alphabets"); m_alphabetNameCombo->addItems(alphabetItems); QHBoxLayout *alphabetLayout = new QHBoxLayout; QLabel *alphabetNameLabel = new QLabel(tr("&Alphabet:")); alphabetNameLabel->setBuddy(m_alphabetNameCombo); + m_editAlphabet = new QPushButton(tr("Edit...")); + m_editAlphabet->setMaximumWidth(60); + connect(m_editAlphabet, SIGNAL(clicked()), this, SLOT(editAlphabet())); alphabetLayout->addWidget(alphabetNameLabel); alphabetLayout->addWidget(m_alphabetNameCombo); + alphabetLayout->addWidget(m_editAlphabet); m_themeNameCombo = new QComboBox; connect(m_themeNameCombo, SIGNAL(activated(const QString &)), this, SLOT(themeChanged(const QString &))); QStringList themeItems; - populateListFromFilenames(themeItems, m_dataDir + "/themes"); + populateListFromFilenames(themeItems, "themes"); m_themeNameCombo->addItems(themeItems); 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); + themeLayout->addWidget(m_editTheme); - 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())); + QStringList boardItems; + populateListFromFilenames(boardItems, "boards"); + m_boardNameCombo->addItems(boardItems); - m_editBoard = new QPushButton(tr("&Edit Board")); - connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); + QHBoxLayout *boardLayout = new QHBoxLayout; + 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())); + boardLayout->addWidget(boardNameLabel); + boardLayout->addWidget(m_boardNameCombo); + boardLayout->addWidget(m_editBoard); - loadBoardNameCombo(); + // m_addBoard = new QPushButton(tr("Add Board")); + // connect(m_addBoard, SIGNAL(clicked()), this, SLOT(addBoard())); - QGroupBox *boardGroup = new QGroupBox("Game Board Definitions"); - QGridLayout *boardLayout = new QGridLayout(boardGroup); - QLabel *boardNameLabel = new QLabel(tr("&Board:")); - boardNameLabel->setBuddy(m_boardNameCombo); + // m_editBoard = new QPushButton(tr("&Edit Board")); + // connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); + + // m_deleteBoard = new QPushButton(tr("&Delete Board")); + // connect(m_deleteBoard, SIGNAL(clicked()), this, SLOT(deleteBoard())); + + // loadBoardNameCombo(); + + // QGroupBox *boardGroup = new QGroupBox("Game Board Definitions"); + // QGridLayout *boardLayout = new QGridLayout(boardGroup); + // QLabel *boardNameLabel = new QLabel(tr("&Board:")); + // boardNameLabel->setBuddy(m_boardNameCombo); + + // 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); - 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->addLayout(boardLayout); vlayout->addStretch(); load(); @@ -432,15 +457,18 @@ void Settings::loadBoardNameCombo() QString currentItem = settings.value("quackle/settings/board-name", QString("")).toString(); m_boardNameCombo->setCurrentIndex(m_boardNameCombo->findText(currentItem)); - - m_editBoard->setEnabled(boardNames.count() > 0); - m_deleteBoard->setEnabled(boardNames.count() > 0); } void Settings::populateListFromFilenames(QStringList& list, const QString &path) { - QDir dir(path); - QStringList fileList = dir.entryList(QDir::Files | QDir::Readable, QDir::Name); + QStringList fileList; + QDir dir(m_dataDir); + if (dir.cd(path)) + fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); + dir = QDir(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); + if (dir.cd(path)) + fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); + QStringListIterator i(fileList); QString fileName; int periodPos; diff --git a/quacker/settings.h b/quacker/settings.h index 785b726..5a05452 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -76,9 +76,10 @@ 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; QString m_themeName; -- cgit v1.2.3 From b8024ae268e49c17c40da105b9e22aaa41fcffeb Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 3 Aug 2015 19:30:11 -0500 Subject: Finish Add/Edit/Remove for boards. Delete functionality is now inside of the edit dialog. Which will make things less messy when other edit dialogs are introduced. --- .gitignore | 1 + quacker/boardsetupdialog.cpp | 21 +++++++++++++++ quacker/boardsetupdialog.h | 2 ++ quacker/settings.cpp | 64 +++++++++++--------------------------------- quacker/settings.h | 1 - quackle.sublime-project | 2 +- 6 files changed, 41 insertions(+), 50 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/quacker/boardsetupdialog.cpp b/quacker/boardsetupdialog.cpp index 7198b17..379f79d 100644 --- a/quacker/boardsetupdialog.cpp +++ b/quacker/boardsetupdialog.cpp @@ -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/settings.cpp b/quacker/settings.cpp index 67804ad..15448f5 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -148,7 +148,7 @@ void Settings::createGUI() themeNameLabel->setBuddy(m_themeNameCombo); m_editTheme = new QPushButton(tr("Edit...")); m_editTheme->setMaximumWidth(60); - connect(m_editTheme, SIGNAL(clicked()), this, SLOT(editTheme)); + connect(m_editTheme, SIGNAL(clicked()), this, SLOT(editTheme())); themeLayout->addWidget(themeNameLabel); themeLayout->addWidget(m_themeNameCombo); @@ -160,41 +160,19 @@ void Settings::createGUI() QStringList boardItems; populateListFromFilenames(boardItems, "boards"); m_boardNameCombo->addItems(boardItems); + m_boardNameCombo->addItem(tr("Add new board...")); QHBoxLayout *boardLayout = new QHBoxLayout; 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)); + connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); boardLayout->addWidget(boardNameLabel); boardLayout->addWidget(m_boardNameCombo); boardLayout->addWidget(m_editBoard); - // m_addBoard = new QPushButton(tr("Add Board")); - // connect(m_addBoard, SIGNAL(clicked()), this, SLOT(addBoard())); - - // m_editBoard = new QPushButton(tr("&Edit Board")); - // connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); - - // m_deleteBoard = new QPushButton(tr("&Delete Board")); - // connect(m_deleteBoard, SIGNAL(clicked()), this, SLOT(deleteBoard())); - - // loadBoardNameCombo(); - - // QGroupBox *boardGroup = new QGroupBox("Game Board Definitions"); - // QGridLayout *boardLayout = new QGridLayout(boardGroup); - // QLabel *boardNameLabel = new QLabel(tr("&Board:")); - // boardNameLabel->setBuddy(m_boardNameCombo); - - // 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->addLayout(lexiconLayout); vlayout->addLayout(alphabetLayout); vlayout->addLayout(themeLayout); @@ -357,6 +335,11 @@ void Settings::themeChanged(const QString &themeName) void Settings::boardChanged(const QString &boardName) { + if (m_boardNameCombo->currentIndex() == m_boardNameCombo->count() - 1) + { + addBoard(); + return; + } CustomQSettings settings; settings.setValue("quackle/settings/board-name", boardName); @@ -383,6 +366,7 @@ void Settings::addBoard() (const uchar *)boardParameterStream.str().data(), boardParameterStream.str().size()); settings.setValue(boardName, QVariant(boardParameterBytes)); + m_boardNameCombo->setCurrentIndex(-1); boardChanged(boardName); } else @@ -416,28 +400,8 @@ 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() @@ -453,10 +417,14 @@ 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::populateListFromFilenames(QStringList& list, const QString &path) diff --git a/quacker/settings.h b/quacker/settings.h index 5a05452..bc0e8fa 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -64,7 +64,6 @@ protected slots: void addBoard(); void editBoard(); - void deleteBoard(); void setQuackleToUseLexiconName(const string &lexiconName); void setQuackleToUseAlphabetName(const string &alphabetName); diff --git a/quackle.sublime-project b/quackle.sublime-project index 167db4b..a219eb1 100644 --- a/quackle.sublime-project +++ b/quackle.sublime-project @@ -7,7 +7,7 @@ "playabilities.raw", "smaller.raw", ".gitattributes", "*.Debug", "*.Release", "*.pfx", "*.cer"], "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "lexica", - "strategy", "debug", "release", "makeswelexicon", "lisp"] + "strategy", "debug", "release", "makeswelexicon", "lisp", "DerivedData"] } ] } -- cgit v1.2.3 From dc92d571f4f97f6420fdf1a94cc41c1d2808d71b Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sun, 9 Aug 2015 05:13:19 -0500 Subject: Progress on edit lexicon dialog. * Files can now be loaded from user directory as well as app directory. * Edit lexicon dialog has been added, and pretty much all of the GUI elements framed out. Not actually implemented, yet. * Embiggen the board configuration dialog. * Some bits of code refactoring. --- datamanager.cpp | 42 ++++++------ datamanager.h | 19 ++++-- quacker/boardsetupdialog.cpp | 2 +- quacker/lexicondialog.cpp | 134 ++++++++++++++++++++++++++++++++++++++ quacker/lexicondialog.h | 64 +++++++++++++++++++ quacker/settings.cpp | 149 +++++++++++++++++++++++++++++-------------- quacker/settings.h | 9 ++- quackletest.cpp | 2 +- test/testharness.cpp | 2 +- 9 files changed, 343 insertions(+), 80 deletions(-) create mode 100644 quacker/lexicondialog.cpp create mode 100644 quacker/lexicondialog.h diff --git a/datamanager.cpp b/datamanager.cpp index e188668..eb65afd 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,38 @@ 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 string(); + return 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..196d525 100644 --- a/datamanager.h +++ b/datamanager.h @@ -99,21 +99,24 @@ 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 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 +126,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/quacker/boardsetupdialog.cpp b/quacker/boardsetupdialog.cpp index 379f79d..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 diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp new file mode 100644 index 0000000..f812eed --- /dev/null +++ b/quacker/lexicondialog.cpp @@ -0,0 +1,134 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +#include "lexicondialog.h" +#include "customqsettings.h" +#include "geometry.h" + + +LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent) +{ + m_originalName = originalName; + + resize(450,350); + + // construct the UI elements + m_lexiconName = new QLineEdit(); + m_alphabetCombo = new QComboBox(); + + 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_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_saveChanges, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(m_deleteLexicon, SIGNAL(clicked()), this, SLOT(deleteLexicon())); + + setWindowTitle(tr("Configure Lexicon - Quackle")); + updateLexiconInformation(); + + // sync game board with control states and draw board +} + +LexiconDialog::~LexiconDialog() +{ + +} + +void LexiconDialog::deleteLexicon() +{ + +} + +void LexiconDialog::addWordsFromFile() +{ + +} + +void LexiconDialog::accept() +{ + QDialog::accept(); +} + +void LexiconDialog::updateLexiconInformation() +{ + QString text; + text.append(tr("File name:")); + text.append(tr("\n\nFile size:")); + text.append(tr("\n\nWord count:")); + text.append(tr("\n\nLexicon hash:")); + + m_lexiconInformation->setText(text); +} diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h new file mode 100644 index 0000000..cdc0a59 --- /dev/null +++ b/quacker/lexicondialog.h @@ -0,0 +1,64 @@ +/* + * 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 . + */ + +#ifndef QUACKER_LEXICONDIALOG_H +#define QUACKER_LEXICONDIALOG_H + +#include +#include + +#include +#include + +using namespace std; + +class QComboBox; +class QLabel; +class QLineEdit; +class QPushButton; + +class LexiconDialog : public QDialog +{ +Q_OBJECT + +public: + LexiconDialog(QWidget *parent = 0, const QString &originalName = QString()); + ~LexiconDialog(); + virtual void accept(); + + void updateLexiconInformation(); + +protected slots: + void deleteLexicon(); + void addWordsFromFile(); + +private: + QLineEdit *m_lexiconName; + QComboBox *m_alphabetCombo; + QPushButton *m_addWordsFromFile; + QPushButton *m_clearAllWords; + QLabel *m_lexiconInformation; + + QPushButton *m_saveChanges; + QPushButton *m_cancel; + QPushButton *m_deleteLexicon; + + QString m_originalName; +}; + +#endif diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 15448f5..c516b91 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,18 @@ 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("

Could not open data directory. Quackle will be useless. Try running the quacker executable with quackle/quacker/ as the current directory.

")); - m_dataDir = directory.absolutePath(); + m_appDataDir = directory.absolutePath(); } + m_userDataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); } void Settings::createGUI() @@ -98,86 +100,69 @@ 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 lexiconItems; - populateListFromFilenames(lexiconItems, "lexica"); - m_lexiconNameCombo->addItems(lexiconItems); + populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); - QHBoxLayout *lexiconLayout = new QHBoxLayout; QLabel *lexiconNameLabel = new QLabel(tr("&Lexicon:")); lexiconNameLabel->setBuddy(m_lexiconNameCombo); m_editLexicon = new QPushButton(tr("Edit...")); m_editLexicon->setMaximumWidth(60); connect(m_editLexicon, SIGNAL(clicked()), this, SLOT(editLexicon())); - lexiconLayout->addWidget(lexiconNameLabel); - lexiconLayout->addWidget(m_lexiconNameCombo); - lexiconLayout->addWidget(m_editLexicon); - m_alphabetNameCombo = new QComboBox; connect(m_alphabetNameCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &))); - QStringList alphabetItems; - populateListFromFilenames(alphabetItems, "alphabets"); - m_alphabetNameCombo->addItems(alphabetItems); + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ""); - QHBoxLayout *alphabetLayout = new QHBoxLayout; QLabel *alphabetNameLabel = new QLabel(tr("&Alphabet:")); alphabetNameLabel->setBuddy(m_alphabetNameCombo); m_editAlphabet = new QPushButton(tr("Edit...")); m_editAlphabet->setMaximumWidth(60); connect(m_editAlphabet, SIGNAL(clicked()), this, SLOT(editAlphabet())); - alphabetLayout->addWidget(alphabetNameLabel); - alphabetLayout->addWidget(m_alphabetNameCombo); - alphabetLayout->addWidget(m_editAlphabet); - m_themeNameCombo = new QComboBox; connect(m_themeNameCombo, SIGNAL(activated(const QString &)), this, SLOT(themeChanged(const QString &))); - QStringList themeItems; - populateListFromFilenames(themeItems, "themes"); - m_themeNameCombo->addItems(themeItems); + populateComboFromFilenames(m_themeNameCombo, "themes", ""); - 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); - themeLayout->addWidget(m_editTheme); - m_boardNameCombo = new QComboBox; connect(m_boardNameCombo, SIGNAL(activated(const QString &)), this, SLOT(boardChanged(const QString &))); - QStringList boardItems; - populateListFromFilenames(boardItems, "boards"); - m_boardNameCombo->addItems(boardItems); - m_boardNameCombo->addItem(tr("Add new board...")); + populateComboFromFilenames(m_boardNameCombo, "boards", "board"); - QHBoxLayout *boardLayout = new QHBoxLayout; 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())); - boardLayout->addWidget(boardNameLabel); - boardLayout->addWidget(m_boardNameCombo); - boardLayout->addWidget(m_editBoard); - - vlayout->addLayout(lexiconLayout); - vlayout->addLayout(alphabetLayout); - vlayout->addLayout(themeLayout); - vlayout->addLayout(boardLayout); - vlayout->addStretch(); + 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->setColumnMinimumWidth(3, 0); + layout->setColumnStretch(3, 1); + layout->setRowMinimumHeight(4, 0); + layout->setRowStretch(4, 1); load(); } @@ -201,7 +186,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(); @@ -272,11 +258,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); } @@ -303,6 +291,11 @@ void Settings::setQuackleToUseBoardName(const QString &boardName) void Settings::lexiconChanged(const QString &lexiconName) { + if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) + { + editLexicon(); + return; + } string lexiconNameString = QuackleIO::Util::qstringToStdString(lexiconName); setQuackleToUseLexiconName(lexiconNameString); @@ -314,6 +307,11 @@ void Settings::lexiconChanged(const QString &lexiconName) void Settings::alphabetChanged(const QString &alphabetName) { + if (m_alphabetNameCombo->currentIndex() == m_alphabetNameCombo->count() - 1) + { + editAlphabet(); + return; + } string alphabetNameString = QuackleIO::Util::qstringToStdString(alphabetName); setQuackleToUseAlphabetName(alphabetNameString); @@ -325,6 +323,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; @@ -427,18 +430,62 @@ void Settings::loadBoardNameCombo() m_boardNameCombo->setCurrentIndex(currentItemIndex); } -void Settings::populateListFromFilenames(QStringList& list, const QString &path) +void Settings::editLexicon() +{ + QString name = m_lexiconNameCombo->currentText(); + if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) + name = ""; + LexiconDialog dialog(this, name); + if (dialog.exec()) + { + populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); + load(); + } +} + +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", "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::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label) { QStringList fileList; - QDir dir(m_dataDir); + QDir dir(m_appDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); - dir = QDir(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); + dir = QDir(m_userDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); QStringListIterator i(fileList); QString fileName; + QStringList list; int periodPos; while (i.hasNext()) @@ -452,4 +499,8 @@ void Settings::populateListFromFilenames(QStringList& list, const QString &path) } } list.removeDuplicates(); + + 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 bc0e8fa..ee1b59a 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -65,6 +65,10 @@ protected slots: void addBoard(); void editBoard(); + void editLexicon(); + void editAlphabet(); + void editTheme(); + void setQuackleToUseLexiconName(const string &lexiconName); void setQuackleToUseAlphabetName(const string &alphabetName); void setQuackleToUseThemeName(const QString &themeName); @@ -79,7 +83,8 @@ protected: QPushButton *m_editAlphabet; QPushButton *m_editTheme; QPushButton *m_editBoard; - QString m_dataDir; + QString m_appDataDir; + QString m_userDataDir; QString m_themeName; private: @@ -87,7 +92,7 @@ private: void loadBoardNameCombo(); // load up an item list based on a list of filenames - void populateListFromFilenames(QStringList& list, const QString &path); + void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label); static Settings *m_self; }; diff --git a/quackletest.cpp b/quackletest.cpp index 5f3066c..e69c2cb 100644 --- a/quackletest.cpp +++ b/quackletest.cpp @@ -45,7 +45,7 @@ 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.strategyParameters()->initialize("twl06"); diff --git a/test/testharness.cpp b/test/testharness.cpp index 64847d1..683443f 100644 --- a/test/testharness.cpp +++ b/test/testharness.cpp @@ -191,7 +191,7 @@ 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")); QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; -- cgit v1.2.3 From f814628b3aabef20db3320a5e58217f758fa2760 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Tue, 18 Aug 2015 10:30:10 -0500 Subject: Populate alphabet popup in lexicon dialog. Moved Settings::populateComboFromFileNames() to be a static method, then invoked it from the lexicon dialog. --- quacker/lexicondialog.cpp | 13 ++++++++----- quacker/settings.cpp | 4 ++-- quacker/settings.h | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index f812eed..9d1998c 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -23,6 +23,7 @@ #include "lexicondialog.h" #include "customqsettings.h" +#include "settings.h" #include "geometry.h" @@ -60,7 +61,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi Geometry::setupInnerLayout(addRemoveWordsRow); QHBoxLayout * buttonRow = new QHBoxLayout; Geometry::setupInnerLayout(buttonRow); - QGroupBox * lexiconInformationGroup = new QGroupBox(tr("Lexicon information:")); + QGroupBox * lexiconInformationGroup = new QGroupBox(tr("Lexicon information")); QVBoxLayout * lexiconInformationLayout = new QVBoxLayout(lexiconInformationGroup); // build the layout @@ -97,6 +98,8 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi connect(m_deleteLexicon, SIGNAL(clicked()), this, SLOT(deleteLexicon())); setWindowTitle(tr("Configure Lexicon - Quackle")); + + Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ""); updateLexiconInformation(); // sync game board with control states and draw board @@ -125,10 +128,10 @@ void LexiconDialog::accept() void LexiconDialog::updateLexiconInformation() { QString text; - text.append(tr("File name:")); - text.append(tr("\n\nFile size:")); - text.append(tr("\n\nWord count:")); - text.append(tr("\n\nLexicon hash:")); + text.append(tr("File name: ")); + text.append(tr("\n\nFile size: ")); + text.append(tr("\n\nWord count: ")); + text.append(tr("\n\nLexicon hash: ")); m_lexiconInformation->setText(text); } diff --git a/quacker/settings.cpp b/quacker/settings.cpp index c516b91..3c42a39 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -476,10 +476,10 @@ void Settings::editTheme() void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label) { QStringList fileList; - QDir dir(m_appDataDir); + QDir dir(self()->m_appDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); - dir = QDir(m_userDataDir); + dir = QDir(self()->m_userDataDir); if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); diff --git a/quacker/settings.h b/quacker/settings.h index ee1b59a..cee0562 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -39,6 +39,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 &label); + signals: void refreshViews(); @@ -91,9 +94,6 @@ private: // populate the popup based on what's in QSettings void loadBoardNameCombo(); - // load up an item list based on a list of filenames - void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label); - static Settings *m_self; }; -- cgit v1.2.3 From 0c941df84d544e2e676d9a8acfbe345f8156e161 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Tue, 18 Aug 2015 10:35:05 -0500 Subject: Cleanups and bump the version number. --- installer.iss | 4 +- makegaddag/.gitignore | 2 + makeminidawg/makeminidawg.cpp | 514 ------------------------------------------ quacker/Info.plist | 2 +- quacker/quacker.cpp | 6 +- quacker/quacker.plist | 2 +- 6 files changed, 9 insertions(+), 521 deletions(-) delete mode 100644 makeminidawg/makeminidawg.cpp 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/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/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 . - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -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 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::iterator it = ofdepth[d][l].begin(); it != ofdepth[d][l].end() - 1; it++) - { - if (!nodelist[(*it)]->deleted) - { - for (vector::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/quacker/Info.plist b/quacker/Info.plist index 015f161..854c8a9 100644 --- a/quacker/Info.plist +++ b/quacker/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.98 + 1.0 CFBundleSignature ???? CFBundleVersion diff --git a/quacker/quacker.cpp b/quacker/quacker.cpp index 098ff84..a1f7064 100644 --- a/quacker/quacker.cpp +++ b/quacker/quacker.cpp @@ -2120,10 +2120,10 @@ void TopLevel::firstTimeRun() void TopLevel::about() { - QMessageBox::about(this, tr("About Quackle 0.98"), dialogText(tr( -"

Quackle 0.98 is a crossword game playing, analysis, and study tool. Visit the Quackle homepage at http://quackle.org for more information.

" + QMessageBox::about(this, tr("About Quackle 1.0"), dialogText(tr( +"

Quackle 1.0 is a crossword game playing, analysis, and study tool. Visit the Quackle homepage at http://quackle.org for more information.

" "

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.

" -"

Copyright 2005-2014 by

" +"

Copyright 2005-2015 by

" "
    " "
  • Jason Katz-Brown <jasonkatzbrown@gmail.com>
  • " "
  • John O'Laughlin <olaughlin@gmail.com>
  • " 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 @@ NSPrincipalClass NSApplication NSHumanReadableCopyright - Copyright © 2005-2013 by Jason Katz-Brown & John O'Laughlin + Copyright © 2005-2015 by Jason Katz-Brown & John O'Laughlin -- cgit v1.2.3 From 909c37b77534b88eeafac7a03286692c31cbb1ef Mon Sep 17 00:00:00 2001 From: John Fultz Date: Tue, 18 Aug 2015 10:37:11 -0500 Subject: Migrate gaddag maker into quackleio. Prepping to build the gaddag maker into the quacker ui. Built a new class called GaddagFactory to do this and cleaned up the code a bit. makegaddag still builds exactly as it did before. --- makegaddag/makegaddag.cpp | 199 +++----------------------------------------- quackleio/gaddagfactory.cpp | 166 ++++++++++++++++++++++++++++++++++++ quackleio/gaddagfactory.h | 59 +++++++++++++ 3 files changed, 237 insertions(+), 187 deletions(-) create mode 100644 quackleio/gaddagfactory.cpp create mode 100644 quackleio/gaddagfactory.h diff --git a/makegaddag/makegaddag.cpp b/makegaddag/makegaddag.cpp index 8e2ffac..b8fe276 100644 --- a/makegaddag/makegaddag.cpp +++ b/makegaddag/makegaddag.cpp @@ -29,79 +29,12 @@ #include -#include -#include -#include -#include +#include "quackleio/froggetopt.h" +#include "quackleio/gaddagfactory.h" +#include "quackleio/util.h" using namespace std; -class Node { - public: - Quackle::Letter c; - bool t; - vector 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(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,24 @@ 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; - } + factory.pushWord(originalQString); } - 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); 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; - 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/quackleio/gaddagfactory.cpp b/quackleio/gaddagfactory.cpp new file mode 100644 index 0000000..3a608f0 --- /dev/null +++ b/quackleio/gaddagfactory.cpp @@ -0,0 +1,166 @@ +/* + * 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 . + */ + + +#include +#include + +#include "gaddagfactory.h" +#include "util.h" + +GaddagFactory::GaddagFactory(const QString& alphabetFile) +{ + QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; + flexure->load(alphabetFile); + alphas = flexure; + + // So the separator is sorted to last. + root.t = false; + root.c = QUACKLE_NULL_MARK; // "_" + root.pointer = 0; + root.lastchild = true; +} + +void GaddagFactory::pushWord(const QString& word) +{ + UVString originalString = QuackleIO::Util::qstringToString(word); + + UVString leftover; + Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover); + if (leftover.empty()) + { + ++m_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; + ++m_unencodableWords; + } +} + +void GaddagFactory::generate() +{ + Quackle::WordList::const_iterator wordsEnd = gaddagizedWords.end(); + for (Quackle::WordList::const_iterator wordsIt = gaddagizedWords.begin(); wordsIt != wordsEnd; ++wordsIt) + root.pushWord(*wordsIt); + // for (const auto& words : gaddaggizedWords) + // root.pushWord(words); +} + +void GaddagFactory::writeIndex(const QString& fname) +{ + nodelist.push_back(&root); + + root.print(nodelist, ""); + + ofstream out(QuackleIO::Util::qstringToStdString(fname).c_str(), ios::out | ios::binary); + + for (size_t i = 0; i < nodelist.size(); i++) + { + unsigned int p = (unsigned int)(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 = nodelist[i]->c; + if (n4 == internalSeparatorRepresentation) + n4 = QUACKLE_NULL_MARK; + + if (nodelist[i]->t) + n4 |= 64; + + if (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, Quackle::LetterString prefix) +{ + 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, prefix + children[i].c); +} + + +void GaddagFactory::Node::pushWord(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..ca3bc40 --- /dev/null +++ b/quackleio/gaddagfactory.h @@ -0,0 +1,59 @@ +/* + * 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 . + */ + +#include "flexiblealphabet.h" + + +class GaddagFactory { +public: + + static const Quackle::Letter internalSeparatorRepresentation = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE; + + GaddagFactory(const QString& alphabetFile); + + int wordCount() const { return gaddagizedWords.size(); }; + int nodeCount() const { return nodelist.size(); }; + int encodableWords() const { return m_encodableWords; }; + int unencodableWords() const { return m_unencodableWords; }; + + void pushWord(const QString& word); + void sortWords() { sort(gaddagizedWords.begin(), gaddagizedWords.end()); }; + void generate(); + void writeIndex(const QString& fname); + +private: + class Node { + public: + Quackle::Letter c; + bool t; + vector children; + int pointer; + bool lastchild; + void pushWord(Quackle::LetterString word); + void print(vector< Node* > nodelist, Quackle::LetterString prefix); + }; + + int m_encodableWords; + int m_unencodableWords; + Quackle::WordList gaddagizedWords; + vector< Node* > nodelist; + Quackle::AlphabetParameters *alphas; + Node root; + + +}; -- cgit v1.2.3 From 4ef5b33708a4ff0435d5c8254b860cd03a264c66 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Thu, 20 Aug 2015 04:49:46 -0500 Subject: Bug fixes to GaddagFactory A few things from my last commit needed to be fixed or improved. --- gaddagize/.gitignore | 1 + makegaddag/makegaddag.cpp | 3 ++- quackle.sublime-project | 3 ++- quackleio/gaddagfactory.cpp | 24 ++++++++++++++---------- quackleio/gaddagfactory.h | 13 ++++++++++--- 5 files changed, 29 insertions(+), 15 deletions(-) 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/makegaddag/makegaddag.cpp b/makegaddag/makegaddag.cpp index b8fe276..dc38a5f 100644 --- a/makegaddag/makegaddag.cpp +++ b/makegaddag/makegaddag.cpp @@ -88,7 +88,8 @@ int main(int argc, char **argv) if (stream.atEnd()) break; - factory.pushWord(originalQString); + if (!factory.pushWord(originalQString)) + UVcout << "not encodable without leftover: " << QuackleIO::Util::qstringToString(originalQString) << endl; } UVcout << "Sorting " << factory.wordCount() << " words..." << endl; diff --git a/quackle.sublime-project b/quackle.sublime-project index a219eb1..2ca8db5 100644 --- a/quackle.sublime-project +++ b/quackle.sublime-project @@ -5,7 +5,8 @@ "path": ".", "file_exclude_patterns" : ["*.tgz", "*.sublime-workspace", ".tags*", "dawginput.raw", "playabilities.raw", "smaller.raw", ".gitattributes", - "*.Debug", "*.Release", "*.pfx", "*.cer"], + "*.Debug", "*.Release", "*.pfx", "*.cer", + "makegaddag", "makeminidawg", "gaddagize", "Makefile"], "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "lexica", "strategy", "debug", "release", "makeswelexicon", "lisp", "DerivedData"] } diff --git a/quackleio/gaddagfactory.cpp b/quackleio/gaddagfactory.cpp index 3a608f0..e2c726d 100644 --- a/quackleio/gaddagfactory.cpp +++ b/quackleio/gaddagfactory.cpp @@ -36,7 +36,12 @@ GaddagFactory::GaddagFactory(const QString& alphabetFile) root.lastchild = true; } -void GaddagFactory::pushWord(const QString& word) +GaddagFactory::~GaddagFactory() +{ + delete alphas; +} + +bool GaddagFactory::pushWord(const QString& word) { UVString originalString = QuackleIO::Util::qstringToString(word); @@ -61,12 +66,11 @@ void GaddagFactory::pushWord(const QString& word) } gaddagizedWords.push_back(newword); } + return true; } - else - { - UVcout << "not encodable without leftover: " << originalString << endl; - ++m_unencodableWords; - } + + ++m_unencodableWords; + return false; } void GaddagFactory::generate() @@ -82,7 +86,7 @@ void GaddagFactory::writeIndex(const QString& fname) { nodelist.push_back(&root); - root.print(nodelist, ""); + root.print(nodelist); ofstream out(QuackleIO::Util::qstringToStdString(fname).c_str(), ios::out | ios::binary); @@ -114,7 +118,7 @@ void GaddagFactory::writeIndex(const QString& fname) } -void GaddagFactory::Node::print(vector< Node* > nodelist, Quackle::LetterString prefix) +void GaddagFactory::Node::print(vector< Node* >& nodelist) { if (children.size() > 0) { @@ -126,11 +130,11 @@ void GaddagFactory::Node::print(vector< Node* > nodelist, Quackle::LetterString nodelist.push_back(&children[i]); for (size_t i = 0; i < children.size(); i++) - children[i].print(nodelist, prefix + children[i].c); + children[i].print(nodelist); } -void GaddagFactory::Node::pushWord(Quackle::LetterString word) +void GaddagFactory::Node::pushWord(const Quackle::LetterString& word) { if (word.length() == 0) { diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h index ca3bc40..9eb8d72 100644 --- a/quackleio/gaddagfactory.h +++ b/quackleio/gaddagfactory.h @@ -16,6 +16,9 @@ * along with this program. If not, see . */ +#ifndef QUACKLE_GADDAGFACTORY_H +#define QUACKLE_GADDAGFACTORY_H + #include "flexiblealphabet.h" @@ -25,13 +28,14 @@ public: static const Quackle::Letter internalSeparatorRepresentation = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE; GaddagFactory(const QString& alphabetFile); + ~GaddagFactory(); int wordCount() const { return gaddagizedWords.size(); }; int nodeCount() const { return nodelist.size(); }; int encodableWords() const { return m_encodableWords; }; int unencodableWords() const { return m_unencodableWords; }; - void pushWord(const QString& word); + bool pushWord(const QString& word); void sortWords() { sort(gaddagizedWords.begin(), gaddagizedWords.end()); }; void generate(); void writeIndex(const QString& fname); @@ -44,8 +48,8 @@ private: vector children; int pointer; bool lastchild; - void pushWord(Quackle::LetterString word); - void print(vector< Node* > nodelist, Quackle::LetterString prefix); + void pushWord(const Quackle::LetterString& word); + void print(vector< Node* >& nodelist); }; int m_encodableWords; @@ -57,3 +61,6 @@ private: }; + +#endif + -- cgit v1.2.3 From 6613f3fd45b4ecf6821ee7bb07c95f86f43b0db2 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Thu, 20 Aug 2015 04:59:36 -0500 Subject: Move DAWG generation into quackleio. Same thing I just did for the gaddag code I'm now doing for the dawg code. While I was at it, I made some improvements to the dawg code... * Instead of adding multiple cross-checks for various kinds of node metadata, there's now only one cross- check...a hash applied to each node. * Some useless variables/members have been excised. * Add ability to do a word count (cryptohash coming soon). * Make it possible to call generate() and writeIndex() multiple times without corrupting the dictionary. --- makeminidawg/makeminidawg.pro | 4 +- makeminidawg/makeminidawgmain.cpp | 124 ++++++++- makeminidawg/minidawgmaker.cpp | 516 -------------------------------------- makeminidawg/minidawgmaker.h | 37 --- quackleio/dawgfactory.cpp | 282 +++++++++++++++++++++ quackleio/dawgfactory.h | 79 ++++++ 6 files changed, 482 insertions(+), 560 deletions(-) delete mode 100644 makeminidawg/minidawgmaker.cpp delete mode 100644 makeminidawg/minidawgmaker.h create mode 100644 quackleio/dawgfactory.cpp create mode 100644 quackleio/dawgfactory.h diff --git a/makeminidawg/makeminidawg.pro b/makeminidawg/makeminidawg.pro index 8af3bb2..729e6b4 100644 --- a/makeminidawg/makeminidawg.pro +++ b/makeminidawg/makeminidawg.pro @@ -24,8 +24,8 @@ 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..89afb68 100644 --- a/makeminidawg/makeminidawgmain.cpp +++ b/makeminidawg/makeminidawgmain.cpp @@ -16,16 +16,130 @@ * along with this program. If not, see . */ -#include -#include +#include +#include -#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(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; + + 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 . - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#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 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::iterator it = ofdepth[d][l].begin(); it != ofdepth[d][l].end() - 1; it++) - { - if (!nodelist[(*it)]->deleted) - { - for (vector::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 . - */ - -#ifndef QUACKLE_MINIDAWGMAKER_H -#define QUACKLE_MINIDAWGMAKER_H - -#include - -class MiniDawgMaker -{ -public: - MiniDawgMaker() {} - ~MiniDawgMaker() {} - - // parse and execute commands specified on command line - int executeFromArguments(); - - // TOOD further classify this code -}; - -#endif - diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp new file mode 100644 index 0000000..8a37766 --- /dev/null +++ b/quackleio/dawgfactory.cpp @@ -0,0 +1,282 @@ +/* + * 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 . + */ + + +#include +#include + +#include "dawgfactory.h" +#include "util.h" + + +DawgFactory::DawgFactory(const QString& alphabetFile) +{ + 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; +} + +DawgFactory::~DawgFactory() +{ + delete alphas; +} + +bool DawgFactory::pushWord(const QString& word, bool inSmaller, int playability) +{ + UVString originalString = QuackleIO::Util::qstringToString(word); + + UVString leftover; + Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover); + if (leftover.empty()) + { + ++m_encodableWords; + root.pushWord(encodedWord, inSmaller, playability); + return true; + } + + ++m_unencodableWords; + return false; +} + +void DawgFactory::generate() +{ + const int bucketcount = 2000; + vector< int > bucket[bucketcount]; + + nodelist.clear(); + nodelist.push_back(&root); + root.print(nodelist); + + nodelist[0]->letterSum(); + + for (unsigned int i = 0; i < nodelist.size(); i++) + { + bucket[nodelist[i]->sum % bucketcount].push_back(i); + nodelist[i]->pointer = 0; + nodelist[i]->written = false; + nodelist[i]->deleted = false; + nodelist[i]->cloneof = NULL; + } + + for (int b = 0; b < bucketcount; b++) + { + if (bucket[b].size() == 0) + continue; + for (vector::iterator it = bucket[b].begin(); it != bucket[b].end() - 1; it++) + { + if (!nodelist[(*it)]->deleted) + { + for (vector::iterator jt = it + 1; jt != bucket[b].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)]; + } + } + } + } + } + } + + nodelist.clear(); + nodelist.push_back(&root); + root.print(nodelist); +} + +void DawgFactory::writeIndex(const QString& filename) +{ + ofstream out(QuackleIO::Util::qstringToStdString(filename).c_str(), 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); + } +} + + + +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; +} + + +void DawgFactory::Node::pushWord(const 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; + break; + } + } + + if (index == -1) { + Node n; + n.c = first; + n.t = false; + n.playability = 0; + n.insmallerdict = false; + n.pointer = 0; + n.lastchild = false; + children.push_back(n); + index = children.size() - 1; + } + + children[index].pushWord(rest, inSmaller, pb); + } + + sumexplored = false; + deleted = false; + written = false; +} + + +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 (t != n.t) + 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::wordCount() const +{ + int wordCount = (t ? 0 : 1); + for (size_t i = 0; i < children.size(); i++) + wordCount += children[i].wordCount(); + return wordCount; +} + +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..b2cfb76 --- /dev/null +++ b/quackleio/dawgfactory.h @@ -0,0 +1,79 @@ +/* + * 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 . + */ + +#ifndef QUACKLE_DAWGFACTORY_H +#define QUACKLE_DAWGFACTORY_H + +#include +#include "flexiblealphabet.h" + + +class DawgFactory { +public: + + DawgFactory(const QString& alphabetFile); + ~DawgFactory(); + + int wordCount() const { return root.wordCount(); }; + int nodeCount() const { return nodelist.size(); }; + int encodableWords() const { return m_encodableWords; }; + int unencodableWords() const { return m_unencodableWords; }; + + bool pushWord(const QString& word, bool inSmaller, int playability); + void generate(); + void writeIndex(const QString& fname); + +private: + class Node { + public: + void pushWord(const Quackle::LetterString& word, bool inSmaller, int pb); + void print(vector< Node* >& nodelist); + + int letterSum() const; + int wordCount() const; + bool equals(const Node &n) const; + + Quackle::Letter c; + bool t; + bool insmallerdict; + int playability; + + vector children; + int pointer; + int location; + + bool lastchild; + + mutable bool sumexplored; + mutable int sum; + + bool deleted; + Node* cloneof; + bool written; + }; + + int m_encodableWords; + int m_unencodableWords; + vector< Node* > nodelist; + Quackle::AlphabetParameters *alphas; + Node root; +}; + +#endif + + -- cgit v1.2.3 From 5e5d414c57d5c7dd8a3037dda1555db5fb7eb486 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sat, 22 Aug 2015 01:39:47 -0500 Subject: Add versioning of DAWGs. If we're going to start writing these into user directories, then we'd better start versioning them so we don't end up generating bugs in the future. LexiconParameters::loadDawg() implements a tiny class factory which allows backward compatibility of DAWGs. I'll soon be adding a "version 1" in addition to the legacy "version 0". For now, version 1 is just dummied in. --- generator.cpp | 11 +------ lexiconparameters.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++---- lexiconparameters.h | 64 ++++++++++++++-------------------------- 3 files changed, 98 insertions(+), 58 deletions(-) diff --git a/generator.cpp b/generator.cpp index 2bfc199..f2923b8 100644 --- a/generator.cpp +++ b/generator.cpp @@ -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) { diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index b32f632..9826ca3 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -25,6 +25,64 @@ using namespace Quackle; +class V0DawgInterpreter : public DawgInterpreter +{ + + virtual void loadDawg(ifstream &file, unsigned char *dawg) + { + int i = 0; + while (!file.eof()) + { + file.read((char*)(dawg) + i, 7); + i += 7; + } + } + + 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 V1DawgInterpreter : public DawgInterpreter +{ + + virtual void loadDawg(ifstream &file, unsigned char *dawg) + { + int i = 0; + while (!file.eof()) + { + file.read((char*)(dawg) + i, 7); + i += 7; + } + } + + 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 1; } +}; + LexiconParameters::LexiconParameters() : m_dawg(0), m_gaddag(0) { @@ -45,6 +103,7 @@ void LexiconParameters::unloadDawg() { delete[] m_dawg; m_dawg = 0; + delete m_interpreter; } void LexiconParameters::unloadGaddag() @@ -64,14 +123,24 @@ void LexiconParameters::loadDawg(const string &filename) return; } - m_dawg = new unsigned char[7000000]; - - int i = 0; - while (!file.eof()) + char versionByte = file.get(); + file.unget(); + switch(versionByte) { - file.read((char*)(m_dawg) + i, 7); - i += 7; + case 0: + m_interpreter = new V0DawgInterpreter(); + break; + case 1: + m_interpreter = new V1DawgInterpreter(); + break; + default: + UVcout << "couldn't open dawg " << filename.c_str() << endl; + return; } + + m_dawg = new unsigned char[7000000]; + + m_interpreter->loadDawg(file, m_dawg); } void LexiconParameters::loadGaddag(const string &filename) diff --git a/lexiconparameters.h b/lexiconparameters.h index 612c103..4c77cd1 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -25,6 +25,16 @@ namespace Quackle { +class DawgInterpreter +{ +public: + virtual void loadDawg(ifstream &file, unsigned char *dawg) = 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 ~DawgInterpreter() {}; +}; + + class LexiconParameters { public: @@ -34,68 +44,38 @@ 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); // 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]; }; protected: unsigned char *m_dawg; unsigned char *m_gaddag; string m_lexiconName; + DawgInterpreter *m_interpreter; }; -inline bool LexiconParameters::hasSomething() const -{ - return hasDawg() || hasGaddag(); -} - -inline bool LexiconParameters::hasDawg() const -{ - return m_dawg != NULL; -} - -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; -} - } #endif -- cgit v1.2.3 From d1f5f768764d439f02520d9c6c017fcd3ae96b83 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 24 Aug 2015 00:51:48 -0500 Subject: Add a new DAWG format. Make reader and writer for the new format, while maintaing compatibility with the old. Things to note of the new format... * Now has a header, with version number, MD5, and word count. * No longer has terminator bit. Nodes are terminated by a non-zero playability. * Which means letters have one more bit. So we can now support more than 32 letters. Important for Slovak alphabet. Also, various cleanups and refactorings. --- fixedstring.h | 1 + lexiconparameters.cpp | 29 ++++++---- lexiconparameters.h | 9 ++- quackleio/dawgfactory.cpp | 144 ++++++++++++++++++++++++++-------------------- quackleio/dawgfactory.h | 26 ++++++--- 5 files changed, 125 insertions(+), 84 deletions(-) diff --git a/fixedstring.h b/fixedstring.h index 46d1011..e8db0bf 100644 --- a/fixedstring.h +++ b/fixedstring.h @@ -54,6 +54,7 @@ class FixedLengthString size_type size() const { return length(); } void clear() { m_end = m_data; } void push_back(char c); + const char* constData() const { return m_data; } int compare(const FixedLengthString& s) const; diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index 9826ca3..ca09fa5 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -25,15 +25,16 @@ using namespace Quackle; -class V0DawgInterpreter : public DawgInterpreter +class Quackle::V0DawgInterpreter : public DawgInterpreter { - virtual void loadDawg(ifstream &file, unsigned char *dawg) + virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) { int i = 0; + file.unget(); // version 0 doesn't have a version byte...it's just the node byte which is always set to 0 while (!file.eof()) { - file.read((char*)(dawg) + i, 7); + file.read((char*)(lexparams.m_dawg) + i, 7); i += 7; } } @@ -54,15 +55,19 @@ class V0DawgInterpreter : public DawgInterpreter virtual int versionNumber() const { return 0; } }; -class V1DawgInterpreter : public DawgInterpreter +class Quackle::V1DawgInterpreter : public DawgInterpreter { - virtual void loadDawg(ifstream &file, unsigned char *dawg) + virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) { int i = 0; + unsigned char bytes[3]; + file.read(lexparams.m_hash, sizeof(lexparams.m_hash)); + file.read((char*)bytes, 3); + lexparams.m_wordcount = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; while (!file.eof()) { - file.read((char*)(dawg) + i, 7); + file.read((char*)(lexparams.m_dawg) + i, 7); i += 7; } } @@ -73,10 +78,10 @@ class V1DawgInterpreter : public DawgInterpreter p = (dawg[index] << 16) + (dawg[index + 1] << 8) + (dawg[index + 2]); letter = dawg[index + 3]; - t = (letter & 32) != 0; - lastchild = (letter & 64) != 0; + t = (p != 0); + lastchild = ((letter & 64) != 0); british = !(letter & 128); - letter = (letter & 31) + QUACKLE_FIRST_LETTER; + letter = (letter & 63) + QUACKLE_FIRST_LETTER; playability = (dawg[index + 4] << 16) + (dawg[index + 5] << 8) + (dawg[index + 6]); } @@ -84,8 +89,9 @@ class V1DawgInterpreter : public DawgInterpreter }; LexiconParameters::LexiconParameters() - : m_dawg(0), m_gaddag(0) + : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordcount(0) { + memset(m_hash, 0, sizeof(m_hash)); } LexiconParameters::~LexiconParameters() @@ -124,7 +130,6 @@ void LexiconParameters::loadDawg(const string &filename) } char versionByte = file.get(); - file.unget(); switch(versionByte) { case 0: @@ -140,7 +145,7 @@ void LexiconParameters::loadDawg(const string &filename) m_dawg = new unsigned char[7000000]; - m_interpreter->loadDawg(file, m_dawg); + m_interpreter->loadDawg(file, *this); } void LexiconParameters::loadGaddag(const string &filename) diff --git a/lexiconparameters.h b/lexiconparameters.h index 4c77cd1..4b6369d 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -28,15 +28,20 @@ namespace Quackle class DawgInterpreter { public: - virtual void loadDawg(ifstream &file, unsigned char *dawg) = 0; + virtual void loadDawg(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 ~DawgInterpreter() {}; }; +class V0DawgInterpreter; +class V1DawgInterpreter; class LexiconParameters { + friend class Quackle::V0DawgInterpreter; + friend class Quackle::V1DawgInterpreter; + public: LexiconParameters(); ~LexiconParameters(); @@ -75,6 +80,8 @@ protected: unsigned char *m_gaddag; string m_lexiconName; DawgInterpreter *m_interpreter; + char m_hash[16]; + int m_wordcount; }; } diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 8a37766..6fb5be0 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "dawgfactory.h" #include "util.h" @@ -28,19 +29,20 @@ DawgFactory::DawgFactory(const QString& alphabetFile) { 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; + 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 alphas; + delete m_alphas; } bool DawgFactory::pushWord(const QString& word, bool inSmaller, int playability) @@ -48,36 +50,52 @@ bool DawgFactory::pushWord(const QString& word, bool inSmaller, int playability) UVString originalString = QuackleIO::Util::qstringToString(word); UVString leftover; - Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover); + Quackle::LetterString encodedWord = m_alphas->encode(originalString, &leftover); if (leftover.empty()) { - ++m_encodableWords; - root.pushWord(encodedWord, inSmaller, playability); - return true; + if (m_root.pushWord(encodedWord, inSmaller, playability)) + { + ++m_encodableWords; + hashWord(encodedWord); + return true; + } + ++m_duplicateWords; + return false; } ++m_unencodableWords; 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]; - nodelist.clear(); - nodelist.push_back(&root); - root.print(nodelist); + m_nodelist.clear(); + m_nodelist.push_back(&m_root); + m_root.print(m_nodelist); - nodelist[0]->letterSum(); + m_nodelist[0]->letterSum(); - for (unsigned int i = 0; i < nodelist.size(); i++) + for (unsigned int i = 0; i < m_nodelist.size(); i++) { - bucket[nodelist[i]->sum % bucketcount].push_back(i); - nodelist[i]->pointer = 0; - nodelist[i]->written = false; - nodelist[i]->deleted = false; - nodelist[i]->cloneof = NULL; + 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++) @@ -86,19 +104,19 @@ void DawgFactory::generate() continue; for (vector::iterator it = bucket[b].begin(); it != bucket[b].end() - 1; it++) { - if (!nodelist[(*it)]->deleted) + if (!m_nodelist[(*it)]->deleted) { for (vector::iterator jt = it + 1; jt != bucket[b].end(); jt++) { - if (!nodelist[(*jt)]->deleted) + if (!m_nodelist[(*jt)]->deleted) { // cout << "Comparing " << (*it) << " and " << (*jt) << endl; - if (nodelist[(*it)]->equals(nodelist[(*jt)][0])) + if (m_nodelist[(*it)]->equals(m_nodelist[(*jt)][0])) { //cout << "Hey! " << (*it) << " == " << (*jt) << endl; // ones[l].erase(jt); - nodelist[(*jt)]->deleted = true; - nodelist[(*jt)]->cloneof = nodelist[(*it)]; + m_nodelist[(*jt)]->deleted = true; + m_nodelist[(*jt)]->cloneof = m_nodelist[(*it)]; } } } @@ -106,51 +124,53 @@ void DawgFactory::generate() } } - nodelist.clear(); - nodelist.push_back(&root); - root.print(nodelist); + m_nodelist.clear(); + m_nodelist.push_back(&m_root); + m_root.print(m_nodelist); } void DawgFactory::writeIndex(const QString& filename) { ofstream out(QuackleIO::Util::qstringToStdString(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); - 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]; + out.write(m_hash.charptr, sizeof(m_hash.charptr)); + out.write((char*)bytes, 3); + + 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 (nodelist[i]->deleted) + if (m_nodelist[i]->deleted) { - p = (unsigned int)(nodelist[i]->cloneof->pointer); - // n = nodelist[i]->cloneof; + p = (unsigned int)(m_nodelist[i]->cloneof->pointer); + // n = m_nodelist[i]->cloneof; } else - p = (unsigned int)(nodelist[i]->pointer); + p = (unsigned int)(m_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; + 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; - unsigned char n5 = (pb & 0x00FF0000) >> 16; - unsigned char n6 = (pb & 0x0000FF00) >> 8; - unsigned char n7 = (pb & 0x000000FF); + bytes[4] = (pb & 0x00FF0000) >> 16; + bytes[5] = (pb & 0x0000FF00) >> 8; + bytes[6] = (pb & 0x000000FF); - if (n->t) { - n4 |= 32; - } if (n->lastchild) { - n4 |= 64; + bytes[3] |= 64; } if (n->insmallerdict) { - n4 |= 128; + bytes[3] |= 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); + out.write((char*)bytes, 7); } } @@ -193,11 +213,13 @@ void DawgFactory::Node::print(vector< Node* >& nodelist) } -void DawgFactory::Node::pushWord(const Quackle::LetterString& word, bool inSmaller, int pb) +// 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) { - t = true; - playability = pb; + added = (playability == 0); + playability = (pb == 0) ? 1 : pb; // word terminators nodes are marked by nonzero playability in the v1 DAWG format insmallerdict = inSmaller; } else { @@ -217,7 +239,6 @@ void DawgFactory::Node::pushWord(const Quackle::LetterString& word, bool inSmall if (index == -1) { Node n; n.c = first; - n.t = false; n.playability = 0; n.insmallerdict = false; n.pointer = 0; @@ -226,12 +247,13 @@ void DawgFactory::Node::pushWord(const Quackle::LetterString& word, bool inSmall index = children.size() - 1; } - children[index].pushWord(rest, inSmaller, pb); + added = children[index].pushWord(rest, inSmaller, pb); } sumexplored = false; deleted = false; written = false; + return added; } @@ -243,8 +265,6 @@ bool DawgFactory::Node::equals(const Node &n) const return false; if (children.size() != n.children.size()) return false; - if (t != n.t) - return false; if (insmallerdict != n.insmallerdict) return false; if (sum != n.sum) @@ -259,7 +279,7 @@ bool DawgFactory::Node::equals(const Node &n) const int DawgFactory::Node::wordCount() const { - int wordCount = (t ? 0 : 1); + int wordCount = ((playability == 0) ? 0 : 1); for (size_t i = 0; i < children.size(); i++) wordCount += children[i].wordCount(); return wordCount; diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index b2cfb76..13837c4 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -29,29 +29,30 @@ public: DawgFactory(const QString& alphabetFile); ~DawgFactory(); - int wordCount() const { return root.wordCount(); }; - int nodeCount() const { return nodelist.size(); }; + int wordCount() const { return m_root.wordCount(); }; + 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 QString& word, bool inSmaller, int playability); + void hashWord(const Quackle::LetterString &word); void generate(); void writeIndex(const QString& fname); private: class Node { public: - void pushWord(const Quackle::LetterString& word, bool inSmaller, int pb); - void print(vector< Node* >& nodelist); + bool pushWord(const Quackle::LetterString& word, bool inSmaller, int pb); + void print(vector< Node* >& m_nodelist); int letterSum() const; int wordCount() const; bool equals(const Node &n) const; Quackle::Letter c; - bool t; bool insmallerdict; - int playability; + int playability; // if nonzero, then terminates word vector children; int pointer; @@ -69,9 +70,16 @@ private: int m_encodableWords; int m_unencodableWords; - vector< Node* > nodelist; - Quackle::AlphabetParameters *alphas; - Node root; + int m_duplicateWords; + vector< Node* > m_nodelist; + Quackle::AlphabetParameters *m_alphas; + Node m_root; + union { + char charptr[16]; + int32_t int32ptr[4]; + } m_hash; + + static const char m_versionNumber = 1; }; #endif -- cgit v1.2.3 From 8c7ffef1b6c669592e979fb6038dd634df7f95fc Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 24 Aug 2015 00:52:49 -0500 Subject: Fix encoding of HTML output. Should be UTF8. Thanks to Vlado Makys for pointing this out. --- quacker/graphicalreporter.cpp | 1 + 1 file changed, 1 insertion(+) 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")); } -- cgit v1.2.3 From 1f7b8ef6f96e1d5a2c50565a0f52cc633215e485 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 24 Aug 2015 04:45:27 -0500 Subject: Version the GADDAGs. Basically the same thing I just did to the DAWG files, now done to GADDAGs. Also, add hashing, and make sure GADDAGs only load if their hash matches that of the DAWG files. --- lexiconparameters.cpp | 52 +++++++++++++++++++++++++++++--------- lexiconparameters.h | 15 +++++------ quacker/settings.cpp | 19 +++++++------- quackleio/dawgfactory.cpp | 1 + quackleio/gaddagfactory.cpp | 61 ++++++++++++++++++++++++++++++--------------- quackleio/gaddagfactory.h | 23 +++++++++-------- quackletest.cpp | 4 +-- test/testharness.cpp | 6 ++--- 8 files changed, 117 insertions(+), 64 deletions(-) diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index ca09fa5..e014048 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -19,13 +19,14 @@ #include #include + #include "datamanager.h" #include "lexiconparameters.h" #include "uv.h" using namespace Quackle; -class Quackle::V0DawgInterpreter : public DawgInterpreter +class Quackle::V0LexiconInterpreter : public LexiconInterpreter { virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) @@ -39,6 +40,17 @@ class Quackle::V0DawgInterpreter : public DawgInterpreter } } + virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams) + { + int i = 0; + file.unget(); + 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; @@ -55,7 +67,7 @@ class Quackle::V0DawgInterpreter : public DawgInterpreter virtual int versionNumber() const { return 0; } }; -class Quackle::V1DawgInterpreter : public DawgInterpreter +class Quackle::V1LexiconInterpreter : public LexiconInterpreter { virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) @@ -72,6 +84,24 @@ class Quackle::V1DawgInterpreter : public DawgInterpreter } } + virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams) + { + char hash[16]; + file.read(hash, sizeof(hash)); + if (memcmp(hash, lexparams.m_hash, sizeof(hash))) + { + lexparams.unloadGaddag(); // don't use a mismatched gaddag + return; + } + + 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; @@ -108,14 +138,14 @@ void LexiconParameters::unloadAll() void LexiconParameters::unloadDawg() { delete[] m_dawg; - m_dawg = 0; + m_dawg = NULL; delete m_interpreter; } void LexiconParameters::unloadGaddag() { delete[] m_gaddag; - m_gaddag = 0; + m_gaddag = NULL; } void LexiconParameters::loadDawg(const string &filename) @@ -133,10 +163,10 @@ void LexiconParameters::loadDawg(const string &filename) switch(versionByte) { case 0: - m_interpreter = new V0DawgInterpreter(); + m_interpreter = new V0LexiconInterpreter(); break; case 1: - m_interpreter = new V1DawgInterpreter(); + m_interpreter = new V1LexiconInterpreter(); break; default: UVcout << "couldn't open dawg " << filename.c_str() << endl; @@ -160,14 +190,12 @@ void LexiconParameters::loadGaddag(const string &filename) return; } + char versionByte = file.get(); + if (versionByte != m_interpreter->versionNumber()) + return; m_gaddag = new unsigned char[40000000]; - int i = 0; - while (!file.eof()) - { - file.read((char*)(m_gaddag) + i, 4); - i += 4; - } + m_interpreter->loadGaddag(file, *this); } string LexiconParameters::findDictionaryFile(const string &lexicon) diff --git a/lexiconparameters.h b/lexiconparameters.h index 4b6369d..04ad4e7 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -25,22 +25,23 @@ namespace Quackle { -class DawgInterpreter +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 ~DawgInterpreter() {}; + virtual ~LexiconInterpreter() {}; }; -class V0DawgInterpreter; -class V1DawgInterpreter; +class V0LexiconInterpreter; +class V1LexiconInterpreter; class LexiconParameters { - friend class Quackle::V0DawgInterpreter; - friend class Quackle::V1DawgInterpreter; + friend class Quackle::V0LexiconInterpreter; + friend class Quackle::V1LexiconInterpreter; public: LexiconParameters(); @@ -79,7 +80,7 @@ protected: unsigned char *m_dawg; unsigned char *m_gaddag; string m_lexiconName; - DawgInterpreter *m_interpreter; + LexiconInterpreter *m_interpreter; char m_hash[16]; int m_wordcount; }; diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 3c42a39..362e916 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -207,16 +207,6 @@ void Settings::setQuackleToUseLexiconName(const string &lexiconName) { QUACKLE_LEXICON_PARAMETERS->setLexiconName(lexiconName); - 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); - string dawgFile = Quackle::LexiconParameters::findDictionaryFile(lexiconName + ".dawg"); if (dawgFile.empty()) { @@ -226,6 +216,15 @@ void Settings::setQuackleToUseLexiconName(const string &lexiconName) else QUACKLE_LEXICON_PARAMETERS->loadDawg(dawgFile); + 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); + QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconName); } } diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 6fb5be0..74b4346 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -138,6 +138,7 @@ void DawgFactory::writeIndex(const QString& filename) 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); diff --git a/quackleio/gaddagfactory.cpp b/quackleio/gaddagfactory.cpp index e2c726d..7f666cb 100644 --- a/quackleio/gaddagfactory.cpp +++ b/quackleio/gaddagfactory.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "gaddagfactory.h" #include "util.h" @@ -27,18 +28,20 @@ GaddagFactory::GaddagFactory(const QString& alphabetFile) { QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; flexure->load(alphabetFile); - alphas = flexure; + m_alphas = flexure; // So the separator is sorted to last. - root.t = false; - root.c = QUACKLE_NULL_MARK; // "_" - root.pointer = 0; - root.lastchild = true; + 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 alphas; + delete m_alphas; } bool GaddagFactory::pushWord(const QString& word) @@ -46,10 +49,14 @@ bool GaddagFactory::pushWord(const QString& word) UVString originalString = QuackleIO::Util::qstringToString(word); UVString leftover; - Quackle::LetterString encodedWord = alphas->encode(originalString, &leftover); + Quackle::LetterString encodedWord = m_alphas->encode(originalString, &leftover); if (leftover.empty()) { ++m_encodableWords; + hashWord(encodedWord); + // 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 <= encodedWord.length(); i++) { @@ -64,7 +71,7 @@ bool GaddagFactory::pushWord(const QString& word) for (unsigned j = i; j < encodedWord.length(); j++) newword.push_back(encodedWord[j]); } - gaddagizedWords.push_back(newword); + m_gaddagizedWords.push_back(newword); } return true; } @@ -73,26 +80,40 @@ bool GaddagFactory::pushWord(const QString& word) return false; } +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() { - Quackle::WordList::const_iterator wordsEnd = gaddagizedWords.end(); - for (Quackle::WordList::const_iterator wordsIt = gaddagizedWords.begin(); wordsIt != wordsEnd; ++wordsIt) - root.pushWord(*wordsIt); + 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) - // root.pushWord(words); + // m_root.pushWord(words); } -void GaddagFactory::writeIndex(const QString& fname) +void GaddagFactory::writeIndex(const QString &fname) { - nodelist.push_back(&root); + m_nodelist.push_back(&m_root); - root.print(nodelist); + m_root.print(m_nodelist); ofstream out(QuackleIO::Util::qstringToStdString(fname).c_str(), ios::out | ios::binary); - for (size_t i = 0; i < nodelist.size(); i++) + 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)(nodelist[i]->pointer); + unsigned int p = (unsigned int)(m_nodelist[i]->pointer); if (p != 0) p -= i; // offset indexing @@ -102,14 +123,14 @@ void GaddagFactory::writeIndex(const QString& fname) unsigned char n3 = (p & 0x000000FF) >> 0; unsigned char n4; - n4 = nodelist[i]->c; + n4 = m_nodelist[i]->c; if (n4 == internalSeparatorRepresentation) n4 = QUACKLE_NULL_MARK; - if (nodelist[i]->t) + if (m_nodelist[i]->t) n4 |= 64; - if (nodelist[i]->lastchild) + if (m_nodelist[i]->lastchild) n4 |= 128; bytes[0] = n1; bytes[1] = n2; bytes[2] = n3; bytes[3] = n4; diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h index 9eb8d72..2d21192 100644 --- a/quackleio/gaddagfactory.h +++ b/quackleio/gaddagfactory.h @@ -30,13 +30,14 @@ public: GaddagFactory(const QString& alphabetFile); ~GaddagFactory(); - int wordCount() const { return gaddagizedWords.size(); }; - int nodeCount() const { return nodelist.size(); }; + 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 QString& word); - void sortWords() { sort(gaddagizedWords.begin(), gaddagizedWords.end()); }; + void hashWord(const Quackle::LetterString &word); + void sortWords() { sort(m_gaddagizedWords.begin(), m_gaddagizedWords.end()); }; void generate(); void writeIndex(const QString& fname); @@ -49,17 +50,19 @@ private: int pointer; bool lastchild; void pushWord(const Quackle::LetterString& word); - void print(vector< Node* >& nodelist); + void print(vector< Node* >& m_nodelist); }; int m_encodableWords; int m_unencodableWords; - Quackle::WordList gaddagizedWords; - vector< Node* > nodelist; - Quackle::AlphabetParameters *alphas; - Node root; - - + Quackle::WordList m_gaddagizedWords; + vector< Node* > m_nodelist; + Quackle::AlphabetParameters *m_alphas; + Node m_root; + union { + char charptr[16]; + int32_t int32ptr[4]; + } m_hash; }; #endif diff --git a/quackletest.cpp b/quackletest.cpp index e69c2cb..7ea5d10 100644 --- a/quackletest.cpp +++ b/quackletest.cpp @@ -47,7 +47,7 @@ int main() 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/testharness.cpp b/test/testharness.cpp index 683443f..3f390c1 100644 --- a/test/testharness.cpp +++ b/test/testharness.cpp @@ -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; -- cgit v1.2.3 From 9ea9637922ca68d24d7517cf61870d8cee31f6c5 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sun, 30 Aug 2015 09:21:26 -0500 Subject: Add hash query methods for dawgs, gaddags. --- lexiconparameters.cpp | 4 ++++ lexiconparameters.h | 3 +++ makegaddag/makegaddag.cpp | 2 ++ makeminidawg/makeminidawgmain.cpp | 2 ++ quackleio/dawgfactory.h | 2 ++ quackleio/gaddagfactory.h | 3 +++ 6 files changed, 16 insertions(+) diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index e014048..f6e646b 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -203,3 +203,7 @@ string LexiconParameters::findDictionaryFile(const string &lexicon) return DataManager::self()->findDataFile("lexica", lexicon); } +QString hashString() const +{ + return QString(QByteArray(m_hash, sizeof(m_hash)).toHex()); +} diff --git a/lexiconparameters.h b/lexiconparameters.h index 04ad4e7..b5bc564 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -19,6 +19,7 @@ #ifndef QUACKLE_LEXICONPARAMETERS_H #define QUACKLE_LEXICONPARAMETERS_H +#include #include "alphabetparameters.h" #include "gaddag.h" @@ -76,6 +77,8 @@ public: } const GaddagNode *gaddagRoot() const { return (GaddagNode *) &m_gaddag[0]; }; + QString hashString() const; + protected: unsigned char *m_dawg; unsigned char *m_gaddag; diff --git a/makegaddag/makegaddag.cpp b/makegaddag/makegaddag.cpp index dc38a5f..7a768cd 100644 --- a/makegaddag/makegaddag.cpp +++ b/makegaddag/makegaddag.cpp @@ -105,6 +105,8 @@ int main(int argc, char **argv) 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 (factory.unencodableWords() > 0) UVcout << "There were " << factory.unencodableWords() << " words left out." << endl; diff --git a/makeminidawg/makeminidawgmain.cpp b/makeminidawg/makeminidawgmain.cpp index 89afb68..eee2b37 100644 --- a/makeminidawg/makeminidawgmain.cpp +++ b/makeminidawg/makeminidawgmain.cpp @@ -138,6 +138,8 @@ int main(int argc, char **argv) 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/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 13837c4..23bb4f5 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -40,6 +40,8 @@ public: void generate(); void writeIndex(const QString& fname); + const char* hashBytes() { return m_hash.charptr; }; + private: class Node { public: diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h index 2d21192..03cb546 100644 --- a/quackleio/gaddagfactory.h +++ b/quackleio/gaddagfactory.h @@ -41,6 +41,9 @@ public: void generate(); void writeIndex(const QString& fname); + const char* hashBytes() { return m_hash.charptr; }; + + private: class Node { public: -- cgit v1.2.3 From 5350a57f1be22b28914fca14225c73dac5b30b24 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 7 Sep 2015 14:19:46 -0500 Subject: Auto-generate gaddags Need to add a user interface, but gaddags are now auto-generated if they can't be found. Some specific improvements here: * FixedLengthString gained a pop_back member. * Add code to allow v1 gaddags and v0 dawgs to work together. * Change memory allocation of dawgs and gaddags to be dynamic (the old limit didn't accommodate the ridiculously large Polish dictionary in the gaddag) * The Settings class now knows a bit about generating gaddags. This will be important for giving UI feedback. * Fixed several places using filenames which should be using string, not UVString. * Dawg/GaddagFactory should have been using UVString, not QString. My misunderstanding. --- fixedstring.h | 8 +++++ lexiconparameters.cpp | 81 +++++++++++++++++++++++++++++------------ lexiconparameters.h | 6 ++-- quacker/settings.cpp | 86 ++++++++++++++++++++++++++++++++++---------- quacker/settings.h | 9 +++-- quackleio/dawgfactory.cpp | 14 ++++---- quackleio/dawgfactory.h | 6 ++-- quackleio/flexiblealphabet.h | 2 -- quackleio/gaddagfactory.cpp | 71 ++++++++++++++++++++---------------- quackleio/gaddagfactory.h | 7 ++-- 10 files changed, 197 insertions(+), 93 deletions(-) diff --git a/fixedstring.h b/fixedstring.h index e8db0bf..a31ecd6 100644 --- a/fixedstring.h +++ b/fixedstring.h @@ -54,6 +54,7 @@ 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; @@ -221,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/lexiconparameters.cpp b/lexiconparameters.cpp index f6e646b..6761fc1 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -32,7 +32,6 @@ class Quackle::V0LexiconInterpreter : public LexiconInterpreter virtual void loadDawg(ifstream &file, LexiconParameters &lexparams) { int i = 0; - file.unget(); // version 0 doesn't have a version byte...it's just the node byte which is always set to 0 while (!file.eof()) { file.read((char*)(lexparams.m_dawg) + i, 7); @@ -43,7 +42,6 @@ class Quackle::V0LexiconInterpreter : public LexiconInterpreter virtual void loadGaddag(ifstream &file, LexiconParameters &lexparams) { int i = 0; - file.unget(); while (!file.eof()) { file.read((char*)(lexparams.m_gaddag) + i, 4); @@ -74,6 +72,7 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter { 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_wordcount = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; @@ -87,14 +86,22 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter 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))) { - lexparams.unloadGaddag(); // don't use a mismatched gaddag - return; + // 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; + } + } } - int i = 0; + size_t i = 0; while (!file.eof()) { file.read((char*)(lexparams.m_gaddag) + i, 4); @@ -160,20 +167,16 @@ void LexiconParameters::loadDawg(const string &filename) } char versionByte = file.get(); - switch(versionByte) + m_interpreter = createInterpreter(versionByte); + if (m_interpreter == NULL) { - case 0: - m_interpreter = new V0LexiconInterpreter(); - break; - case 1: - m_interpreter = new V1LexiconInterpreter(); - break; - default: - UVcout << "couldn't open dawg " << filename.c_str() << endl; - return; + UVcout << "couldn't open file " << filename.c_str() << endl; + return; } - m_dawg = new unsigned char[7000000]; + file.seekg(0, ios_base::end); + m_dawg = new unsigned char[file.tellg()]; + file.seekg(0, ios_base::beg); m_interpreter->loadDawg(file, *this); } @@ -191,19 +194,53 @@ void LexiconParameters::loadGaddag(const string &filename) } char versionByte = file.get(); - if (versionByte != m_interpreter->versionNumber()) + if (versionByte < m_interpreter->versionNumber()) return; - m_gaddag = new unsigned char[40000000]; + file.seekg(0, ios_base::end); + m_gaddag = new unsigned char[file.tellg()]; + file.seekg(0, ios_base::beg); - m_interpreter->loadGaddag(file, *this); + // must create a local interpreter because dawg/gaddag versions might not match + LexiconInterpreter* interpreter = createInterpreter(versionByte); + if (interpreter != NULL) + { + 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); +} + +UVString 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 == 5) + break; + if (i % 2 == 1) + hashStr.push_back('-'); + } + return hashStr; } -QString hashString() const +LexiconInterpreter* LexiconParameters::createInterpreter(char version) const { - return QString(QByteArray(m_hash, sizeof(m_hash)).toHex()); + switch(version) + { + case 0: + return new V0LexiconInterpreter(); + case 1: + return new V1LexiconInterpreter(); + default: + return NULL; + } } diff --git a/lexiconparameters.h b/lexiconparameters.h index b5bc564..3890d8d 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -19,8 +19,6 @@ #ifndef QUACKLE_LEXICONPARAMETERS_H #define QUACKLE_LEXICONPARAMETERS_H -#include -#include "alphabetparameters.h" #include "gaddag.h" namespace Quackle @@ -77,7 +75,7 @@ public: } const GaddagNode *gaddagRoot() const { return (GaddagNode *) &m_gaddag[0]; }; - QString hashString() const; + UVString hashString(bool shortened) const; protected: unsigned char *m_dawg; @@ -86,6 +84,8 @@ protected: LexiconInterpreter *m_interpreter; char m_hash[16]; int m_wordcount; + + LexiconInterpreter* createInterpreter(char version) const; }; } diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 362e916..1febdb5 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -93,6 +93,8 @@ Settings::Settings(QWidget *parent) m_appDataDir = directory.absolutePath(); } m_userDataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + QDir qdir(m_userDataDir); + qdir.mkpath("lexica"); } void Settings::createGUI() @@ -195,49 +197,97 @@ 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) { - if (QUACKLE_LEXICON_PARAMETERS->lexiconName() != lexiconName) + 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) +{ + unsigned int p; + Quackle::Letter letter; + bool t; + bool lastchild; + bool british; + int playability; + + do + { + 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); +} + + +void Settings::setQuackleToUseLexiconName(const QString &lexiconName) +{ + string lexiconNameStr = lexiconName.toStdString(); + if (QUACKLE_LEXICON_PARAMETERS->lexiconName() != lexiconNameStr) { - QUACKLE_LEXICON_PARAMETERS->setLexiconName(lexiconName); + 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); - string gaddagFile = Quackle::LexiconParameters::findDictionaryFile(lexiconName + ".gaddag"); + if (!QUACKLE_LEXICON_PARAMETERS->hasDawg()) + { + QUACKLE_LEXICON_PARAMETERS->unloadGaddag(); + return; + } + + string gaddagFile = Quackle::LexiconParameters::findDictionaryFile(lexiconNameStr + ".gaddag"); if (gaddagFile.empty()) { - UVcout << "Gaddag for lexicon '" << lexiconName << "' does not exist." << endl; + UVcout << "Gaddag for lexicon '" << lexiconNameStr << "' does not exist." << endl; QUACKLE_LEXICON_PARAMETERS->unloadGaddag(); } else QUACKLE_LEXICON_PARAMETERS->loadGaddag(gaddagFile); - QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconName); + 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); } } -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 + ".quackle_alphabet")); 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") { @@ -295,8 +345,7 @@ void Settings::lexiconChanged(const QString &lexiconName) editLexicon(); return; } - string lexiconNameString = QuackleIO::Util::qstringToStdString(lexiconName); - setQuackleToUseLexiconName(lexiconNameString); + setQuackleToUseLexiconName(lexiconName); CustomQSettings settings; settings.setValue("quackle/settings/lexicon-name", lexiconName); @@ -311,8 +360,7 @@ void Settings::alphabetChanged(const QString &alphabetName) editAlphabet(); return; } - string alphabetNameString = QuackleIO::Util::qstringToStdString(alphabetName); - setQuackleToUseAlphabetName(alphabetNameString); + setQuackleToUseAlphabetName(alphabetName); CustomQSettings settings; settings.setValue("quackle/settings/alphabet-name", alphabetName); diff --git a/quacker/settings.h b/quacker/settings.h index cee0562..fab2f3f 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -24,6 +24,8 @@ #include #include +#include "quackleio/gaddagfactory.h" + class QComboBox; class QCheckBox; class QPushButton; @@ -72,8 +74,8 @@ protected slots: void editAlphabet(); void editTheme(); - void setQuackleToUseLexiconName(const string &lexiconName); - void setQuackleToUseAlphabetName(const string &alphabetName); + void setQuackleToUseLexiconName(const QString &lexiconName); + void setQuackleToUseAlphabetName(const QString &alphabetName); void setQuackleToUseThemeName(const QString &themeName); void setQuackleToUseBoardName(const QString &lexiconName); @@ -94,6 +96,9 @@ private: // populate the popup based on what's in QSettings void loadBoardNameCombo(); + void buildGaddag(const string &filename); + void pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index); + static Settings *m_self; }; diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 74b4346..3a971a3 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -25,10 +25,10 @@ #include "util.h" -DawgFactory::DawgFactory(const QString& alphabetFile) +DawgFactory::DawgFactory(const UVString& alphabetFile) { QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; - flexure->load(alphabetFile); + flexure->load(QuackleIO::Util::uvStringToQString(alphabetFile)); m_alphas = flexure; m_root.insmallerdict = false; @@ -45,12 +45,10 @@ DawgFactory::~DawgFactory() delete m_alphas; } -bool DawgFactory::pushWord(const QString& word, bool inSmaller, int playability) +bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability) { - UVString originalString = QuackleIO::Util::qstringToString(word); - UVString leftover; - Quackle::LetterString encodedWord = m_alphas->encode(originalString, &leftover); + Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); if (leftover.empty()) { if (m_root.pushWord(encodedWord, inSmaller, playability)) @@ -129,9 +127,9 @@ void DawgFactory::generate() m_root.print(m_nodelist); } -void DawgFactory::writeIndex(const QString& filename) +void DawgFactory::writeIndex(const UVString& filename) { - ofstream out(QuackleIO::Util::qstringToStdString(filename).c_str(), ios::out | ios::binary); + ofstream out(filename.c_str(), ios::out | ios::binary); unsigned char bytes[7]; bytes[0] = (m_encodableWords & 0x00FF0000) >> 16; diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 23bb4f5..051e632 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -26,7 +26,7 @@ class DawgFactory { public: - DawgFactory(const QString& alphabetFile); + DawgFactory(const UVString& alphabetFile); ~DawgFactory(); int wordCount() const { return m_root.wordCount(); }; @@ -35,10 +35,10 @@ public: int unencodableWords() const { return m_unencodableWords; }; int duplicateWords() const { return m_duplicateWords; }; - bool pushWord(const QString& word, bool inSmaller, int playability); + bool pushWord(const UVString& word, bool inSmaller, int playability); void hashWord(const Quackle::LetterString &word); void generate(); - void writeIndex(const QString& fname); + void writeIndex(const UVString& filename); const char* hashBytes() { return m_hash.charptr; }; 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 index 7f666cb..53ccf04 100644 --- a/quackleio/gaddagfactory.cpp +++ b/quackleio/gaddagfactory.cpp @@ -24,11 +24,15 @@ #include "gaddagfactory.h" #include "util.h" -GaddagFactory::GaddagFactory(const QString& alphabetFile) +GaddagFactory::GaddagFactory(const UVString &alphabetFile) + : m_encodableWords(0), m_unencodableWords(0), m_alphas(NULL) { - QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; - flexure->load(alphabetFile); - m_alphas = flexure; + 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; @@ -44,35 +48,13 @@ GaddagFactory::~GaddagFactory() delete m_alphas; } -bool GaddagFactory::pushWord(const QString& word) +bool GaddagFactory::pushWord(const UVString &word) { - UVString originalString = QuackleIO::Util::qstringToString(word); - UVString leftover; - Quackle::LetterString encodedWord = m_alphas->encode(originalString, &leftover); + Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); if (leftover.empty()) { - ++m_encodableWords; - hashWord(encodedWord); - // 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 <= 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]); - } - m_gaddagizedWords.push_back(newword); - } + pushWord(encodedWord); return true; } @@ -80,6 +62,32 @@ bool GaddagFactory::pushWord(const QString& word) 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); @@ -93,6 +101,7 @@ void GaddagFactory::hashWord(const Quackle::LetterString &word) 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); @@ -100,13 +109,13 @@ void GaddagFactory::generate() // m_root.pushWord(words); } -void GaddagFactory::writeIndex(const QString &fname) +void GaddagFactory::writeIndex(const string &fname) { m_nodelist.push_back(&m_root); m_root.print(m_nodelist); - ofstream out(QuackleIO::Util::qstringToStdString(fname).c_str(), ios::out | ios::binary); + 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)); diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h index 03cb546..415baff 100644 --- a/quackleio/gaddagfactory.h +++ b/quackleio/gaddagfactory.h @@ -27,7 +27,7 @@ public: static const Quackle::Letter internalSeparatorRepresentation = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE; - GaddagFactory(const QString& alphabetFile); + GaddagFactory(const UVString &alphabetFile); ~GaddagFactory(); int wordCount() const { return m_gaddagizedWords.size(); }; @@ -35,11 +35,12 @@ public: int encodableWords() const { return m_encodableWords; }; int unencodableWords() const { return m_unencodableWords; }; - bool pushWord(const QString& word); + 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 QString& fname); + void writeIndex(const string &fname); const char* hashBytes() { return m_hash.charptr; }; -- cgit v1.2.3 From ed46987403dd923d3ba14df6eb676e1e163d1d8d Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 7 Sep 2015 17:23:14 -0500 Subject: Fix another alphabet length limitation The 'crosses' code for checking "fit between" plays was using a 32-bit integer as a bitfield. Now it's using C++ bitfields (including the C++11 all() operation...hopefully that doesn't cause any problems). This removes another place in the code that was limiting alphabets to 32 letters. --- board.cpp | 4 ++-- board.h | 25 +++++++++++---------- generator.cpp | 70 +++++++++++++++++++++++++++++------------------------------ generator.h | 46 +++------------------------------------ 4 files changed, 54 insertions(+), 91 deletions(-) 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 +#include #include "alphabetparameters.h" #include "bag.h" @@ -27,7 +28,9 @@ #include "rack.h" using namespace std; - + +typedef bitset 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/generator.cpp b/generator.cpp index f2923b8..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)); @@ -486,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(); @@ -518,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); @@ -533,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++) { /* @@ -544,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; } @@ -565,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; @@ -811,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); } @@ -835,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; } @@ -862,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; @@ -917,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) { @@ -953,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) { @@ -982,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) { @@ -1015,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) { @@ -1106,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) { @@ -1206,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; } @@ -1229,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++; } @@ -1256,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; } @@ -1277,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++; } @@ -1313,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; @@ -1350,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++; } @@ -1380,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; @@ -1417,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++; } @@ -1609,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) { -- cgit v1.2.3 From 1214533715a1acfbc35ebe29ff78afee2f850226 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sat, 26 Sep 2015 10:47:07 -0500 Subject: Work on DAWG generation. V1 DAWGs now include an alphabet. Begin creating DAWGs which extend other DAWGs. In general, laying the groundwork for plain text import to DAWG. --- lexiconparameters.cpp | 8 ++++++++ lexiconparameters.h | 5 +++++ quacker/lexicondialog.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- quacker/lexicondialog.h | 11 ++++++++++- quackleio/dawgfactory.cpp | 31 +++++++++++++++++++++---------- quackleio/dawgfactory.h | 1 + 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index 6761fc1..9da3b70 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -75,6 +75,14 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter 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 + } + file.get(); // whitespace separator lexparams.m_wordcount = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; while (!file.eof()) { diff --git a/lexiconparameters.h b/lexiconparameters.h index 3890d8d..9f34be6 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -19,11 +19,15 @@ #ifndef QUACKLE_LEXICONPARAMETERS_H #define QUACKLE_LEXICONPARAMETERS_H +#include + #include "gaddag.h" namespace Quackle { +class LexiconParameters; + class LexiconInterpreter { public: @@ -84,6 +88,7 @@ protected: LexiconInterpreter *m_interpreter; char m_hash[16]; int m_wordcount; + vector m_utf8Alphabet; LexiconInterpreter* createInterpreter(char version) const; }; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index 9d1998c..f9c6399 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -25,9 +25,10 @@ #include "customqsettings.h" #include "settings.h" #include "geometry.h" +#include "quackleio/dawgfactory.h" - -LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent) +LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent), + m_wordFactory(NULL) { m_originalName = originalName; @@ -117,7 +118,45 @@ void LexiconDialog::deleteLexicon() void LexiconDialog::addWordsFromFile() { + QFileDialog browser(this, tr("Choose a file containing words to be added to the lexicon...")); +} + +void LexiconDialog::addWordsFromDawg(const string &dawgfile, const string &alphabetfile) +{ + delete m_wordFactory; + m_wordFactory = NULL; + + LexiconParameters lexParams; + lexParams.loadDawg(dawgfile); + if (!lexParams.hasDawg()) + return; + m_wordFactory = new DawgFactory(alphabetfile); + 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::accept() diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index cdc0a59..573d48b 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -20,17 +20,20 @@ #define QUACKER_LEXICONDIALOG_H #include -#include +#include "game.h" +#include "lexiconparameters.h" #include #include using namespace std; +using namespace Quackle; class QComboBox; class QLabel; class QLineEdit; class QPushButton; +class DawgFactory; class LexiconDialog : public QDialog { @@ -47,6 +50,10 @@ protected slots: void deleteLexicon(); void addWordsFromFile(); +protected: + void addWordsFromDawg(const string &dawgfile, const string &alphabetfile); + void addWordsFromDawgRecursive(const LexiconParameters &lexParams, Quackle::LetterString &word, int index); + private: QLineEdit *m_lexiconName; QComboBox *m_alphabetCombo; @@ -59,6 +66,8 @@ private: QPushButton *m_deleteLexicon; QString m_originalName; + + DawgFactory *m_wordFactory; }; #endif diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 3a971a3..565778a 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -50,21 +50,24 @@ bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability UVString leftover; Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); if (leftover.empty()) - { - if (m_root.pushWord(encodedWord, inSmaller, playability)) - { - ++m_encodableWords; - hashWord(encodedWord); - return true; - } - ++m_duplicateWords; - return false; - } + 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; + hashWord(word); + return true; + } + ++m_duplicateWords; + return false; +} + void DawgFactory::hashWord(const Quackle::LetterString &word) { QCryptographicHash wordhash(QCryptographicHash::Md5); @@ -139,6 +142,14 @@ void DawgFactory::writeIndex(const UVString& filename) 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; diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 051e632..2a55461 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -36,6 +36,7 @@ public: 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 UVString& filename); -- cgit v1.2.3 From 03b9132b3f540bfb3713400774a9bfb832b200b2 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 28 Sep 2015 12:58:30 -0500 Subject: Some minor refactoring. --- alphabetparameters.cpp | 6 +++--- quacker/lister.cpp | 3 --- quacker/settings.cpp | 2 +- test/testharness.cpp | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) 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/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/settings.cpp b/quacker/settings.cpp index 1febdb5..ce8583f 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -283,7 +283,7 @@ void Settings::setQuackleToUseAlphabetName(const QString &alphabetName) string alphabetNameStr = alphabetName.toStdString(); if (QUACKLE_ALPHABET_PARAMETERS->alphabetName() != alphabetNameStr) { - QString alphabetFileStr = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(alphabetNameStr + ".quackle_alphabet")); + QString alphabetFileStr = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(alphabetNameStr)); QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; flexure->setAlphabetName(alphabetNameStr); diff --git a/test/testharness.cpp b/test/testharness.cpp index 3f390c1..172e403 100644 --- a/test/testharness.cpp +++ b/test/testharness.cpp @@ -193,7 +193,7 @@ void TestHarness::startUp() m_dataManager.setBackupLexicon("twl06"); 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)) { -- cgit v1.2.3 From 81554a201cc5e0748110add6eca05cc16c18850c Mon Sep 17 00:00:00 2001 From: John Fultz Date: Mon, 28 Sep 2015 13:00:57 -0500 Subject: Now able to load text and dawg files. Words are loaded and hashed. Duplicates are discovered. Alphabets are dealt with. Merging of multiple word lists works. This is good stuff. Saving the resulting dictionaries has not been tried, yet, and the gui code for saving needs to be finished off, yet. --- quacker/lexicondialog.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++---- quacker/lexicondialog.h | 5 +++- quackleio/dawgfactory.cpp | 4 +-- quackleio/dawgfactory.h | 2 +- 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index f9c6399..a7566c6 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -97,10 +97,12 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi 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", ""); + alphabetChanged(m_alphabetCombo->currentText()); updateLexiconInformation(); // sync game board with control states and draw board @@ -113,25 +115,49 @@ LexiconDialog::~LexiconDialog() void LexiconDialog::deleteLexicon() { - + delete m_wordFactory; + m_wordFactory = NULL; + updateLexiconInformation(); } 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::const_iterator it = files.begin(); it != files.end(); it++) + { + if (it->endsWith(".dawg", Qt::CaseInsensitive)) + addWordsFromDawgFile(*it, m_alphabetCombo->currentText()); + else + addWordsFromTextFile(*it, m_alphabetCombo->currentText()); + } + updateLexiconInformation(); } -void LexiconDialog::addWordsFromDawg(const string &dawgfile, const string &alphabetfile) +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, const QString &alphabetfile) +{ + if (!m_wordFactory) + m_wordFactory = new DawgFactory(m_alphabetFileName); LexiconParameters lexParams; - lexParams.loadDawg(dawgfile); + lexParams.loadDawg(QuackleIO::Util::qstringToStdString(dawgfile)); if (!lexParams.hasDawg()) return; - m_wordFactory = new DawgFactory(alphabetfile); Quackle::LetterString word; addWordsFromDawgRecursive(lexParams, word, 1); @@ -159,6 +185,37 @@ void LexiconDialog::addWordsFromDawgRecursive(const LexiconParameters &lexParams } while (!lastchild); } +void LexiconDialog::addWordsFromTextFile(const QString &textFile, const QString &alphabetfile) +{ + 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::accept() { QDialog::accept(); @@ -166,11 +223,15 @@ void LexiconDialog::accept() void LexiconDialog::updateLexiconInformation() { + int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0; + QByteArray hash = m_wordFactory ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; QString text; text.append(tr("File name: ")); text.append(tr("\n\nFile size: ")); text.append(tr("\n\nWord count: ")); + text.append(QString("%L1").arg(wordCount)); text.append(tr("\n\nLexicon hash: ")); + text.append(hash); m_lexiconInformation->setText(text); } diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index 573d48b..4df6138 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -49,10 +49,12 @@ public: protected slots: void deleteLexicon(); void addWordsFromFile(); + void alphabetChanged(const QString &); protected: - void addWordsFromDawg(const string &dawgfile, const string &alphabetfile); + void addWordsFromDawgFile(const QString &dawgfile, const QString &alphabetfile); void addWordsFromDawgRecursive(const LexiconParameters &lexParams, Quackle::LetterString &word, int index); + void addWordsFromTextFile(const QString &textFile, const QString &alphabetfile); private: QLineEdit *m_lexiconName; @@ -66,6 +68,7 @@ private: QPushButton *m_deleteLexicon; QString m_originalName; + QString m_alphabetFileName; DawgFactory *m_wordFactory; }; diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 565778a..e7ada85 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -25,10 +25,10 @@ #include "util.h" -DawgFactory::DawgFactory(const UVString& alphabetFile) +DawgFactory::DawgFactory(const QString &alphabetFile) { QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; - flexure->load(QuackleIO::Util::uvStringToQString(alphabetFile)); + flexure->load(alphabetFile); m_alphas = flexure; m_root.insmallerdict = false; diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 2a55461..1a1aa7d 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -26,7 +26,7 @@ class DawgFactory { public: - DawgFactory(const UVString& alphabetFile); + DawgFactory(const QString &alphabetFile); ~DawgFactory(); int wordCount() const { return m_root.wordCount(); }; -- cgit v1.2.3 From 69e3dcefb882c743b136df8e5c81b4182b135f6b Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sat, 10 Oct 2015 19:09:36 -0500 Subject: Progress on the lexicon dialog. Now prints better stats. Now loads the dictionary you're editing. Now disables the Delete button at appropriate times. --- datamanager.cpp | 6 ++++++ datamanager.h | 3 +++ lexiconparameters.cpp | 5 +++++ lexiconparameters.h | 1 + quacker/lexicondialog.cpp | 36 +++++++++++++++++++++++++++--------- quacker/lexicondialog.h | 4 ++-- quacker/settings.cpp | 30 ++++++++++++++++++++++++++---- quacker/settings.h | 2 ++ quackleio/dawgfactory.cpp | 37 +++++++++++++++++++++++++++++++++++-- quackleio/dawgfactory.h | 8 ++++++-- 10 files changed, 113 insertions(+), 19 deletions(-) diff --git a/datamanager.cpp b/datamanager.cpp index eb65afd..916610a 100644 --- a/datamanager.cpp +++ b/datamanager.cpp @@ -156,6 +156,12 @@ string DataManager::findDataFile(const string &subDirectory, const string &file) return fname; } +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, const string &file, bool user) { return (user ? m_userDataDirectory : m_appDataDirectory) + "/" + subDirectory + "/" + lexicon + "/" + file; diff --git a/datamanager.h b/datamanager.h index 196d525..75bce54 100644 --- a/datamanager.h +++ b/datamanager.h @@ -105,6 +105,9 @@ public: // Returns empty string if the file is not found. 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, const string &file, bool user); string makeDataFilename(const string &subDirectory, const string &file, bool user); diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index 9da3b70..bc10773 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -224,6 +224,11 @@ string LexiconParameters::findDictionaryFile(const string &lexicon) return QUACKLE_DATAMANAGER->findDataFile("lexica", lexicon); } +bool LexiconParameters::hasUserDictionaryFile(const string &lexicon) +{ + return QUACKLE_DATAMANAGER->hasUserDataFile("lexica", lexicon); +} + UVString LexiconParameters::hashString(bool shortened) const { const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; diff --git a/lexiconparameters.h b/lexiconparameters.h index 9f34be6..4eda4f3 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -68,6 +68,7 @@ public: // 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 { return m_lexiconName; }; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index a7566c6..e11ae41 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -103,9 +103,21 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ""); alphabetChanged(m_alphabetCombo->currentText()); - updateLexiconInformation(); - // sync game board with control states and draw board + string dawgFileName = originalName.toStdString() + ".dawg"; + QString dawgFullFileName; + if (!originalName.isEmpty()) + dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName)); + + if (!dawgFullFileName.isEmpty()) + { + m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName)); + addWordsFromDawgFile(dawgFullFileName); + } + else + m_deleteLexicon->setEnabled(false); + + updateLexiconInformation(); } LexiconDialog::~LexiconDialog() @@ -134,9 +146,9 @@ void LexiconDialog::addWordsFromFile() for (QList::const_iterator it = files.begin(); it != files.end(); it++) { if (it->endsWith(".dawg", Qt::CaseInsensitive)) - addWordsFromDawgFile(*it, m_alphabetCombo->currentText()); + addWordsFromDawgFile(*it); else - addWordsFromTextFile(*it, m_alphabetCombo->currentText()); + addWordsFromTextFile(*it); } updateLexiconInformation(); } @@ -149,7 +161,7 @@ void LexiconDialog::alphabetChanged(const QString &alphabet) m_alphabetFileName = QString::fromStdString(AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(alphabet))); } -void LexiconDialog::addWordsFromDawgFile(const QString &dawgfile, const QString &alphabetfile) +void LexiconDialog::addWordsFromDawgFile(const QString &dawgfile) { if (!m_wordFactory) m_wordFactory = new DawgFactory(m_alphabetFileName); @@ -185,7 +197,7 @@ void LexiconDialog::addWordsFromDawgRecursive(const LexiconParameters &lexParams } while (!lastchild); } -void LexiconDialog::addWordsFromTextFile(const QString &textFile, const QString &alphabetfile) +void LexiconDialog::addWordsFromTextFile(const QString &textFile) { if (!m_wordFactory) m_wordFactory = new DawgFactory(m_alphabetFileName); @@ -224,14 +236,20 @@ void LexiconDialog::accept() void LexiconDialog::updateLexiconInformation() { int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0; - QByteArray hash = m_wordFactory ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; + QByteArray hash = (m_wordFactory && wordCount) ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; QString text; + QString lengthText; + if (m_wordFactory) + lengthText = QString::fromStdString(m_wordFactory->letterCountString()); + text.append(tr("File name: ")); text.append(tr("\n\nFile size: ")); text.append(tr("\n\nWord count: ")); text.append(QString("%L1").arg(wordCount)); - text.append(tr("\n\nLexicon hash: ")); - text.append(hash); + text.append("\n"); + text.append(lengthText); + text.append(tr("\nLexicon hash: ")); + text.append(hash.left(8)); m_lexiconInformation->setText(text); } diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index 4df6138..39cd546 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -52,9 +52,9 @@ protected slots: void alphabetChanged(const QString &); protected: - void addWordsFromDawgFile(const QString &dawgfile, const QString &alphabetfile); + void addWordsFromDawgFile(const QString &dawgfile); void addWordsFromDawgRecursive(const LexiconParameters &lexParams, Quackle::LetterString &word, int index); - void addWordsFromTextFile(const QString &textFile, const QString &alphabetfile); + void addWordsFromTextFile(const QString &textFile); private: QLineEdit *m_lexiconName; diff --git a/quacker/settings.cpp b/quacker/settings.cpp index ce8583f..3319955 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -172,9 +172,11 @@ void Settings::createGUI() void Settings::load() { m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()))); + m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); 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(); } void Settings::preInitialize() @@ -343,9 +345,13 @@ void Settings::lexiconChanged(const QString &lexiconName) 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(lexiconName); + m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); CustomQSettings settings; settings.setValue("quackle/settings/lexicon-name", lexiconName); @@ -388,6 +394,9 @@ 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; @@ -530,14 +539,14 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, if (dir.cd(path)) fileList << dir.entryList(QDir::Files | QDir::Readable, QDir::Name); - QStringListIterator i(fileList); + 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; periodPos = fileName.indexOf('.'); if (periodPos) { @@ -545,7 +554,20 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, list << fileName; } } - list.removeDuplicates(); + + for (i = fileList.begin(); i != fileList.end(); ++i) + { + QStringList::iterator j = i; + for (++j; j != fileList.end(); ++j) + { + if (*i == *j) + { + *i = "* " + *i; + list.erase(j); + break; + } + } + } combo->addItems(list); if (label.size() > 0) diff --git a/quacker/settings.h b/quacker/settings.h index fab2f3f..7c5738e 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -100,6 +100,8 @@ private: void pushIndex(GaddagFactory &factory, Quackle::LetterString &word, int index); static Settings *m_self; + int m_lastGoodLexiconValue; + int m_lastGoodBoardValue; }; #endif diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index e7ada85..362dfdc 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -17,6 +17,8 @@ */ +#include +#include #include #include #include @@ -184,6 +186,34 @@ void DawgFactory::writeIndex(const UVString& filename) } } +int DawgFactory::wordCount() const +{ + m_countsByLength.resize(0); + return m_root.wordCount(0, m_countsByLength); +} + +string DawgFactory::letterCountString() const +{ + ostringstream str; + if (m_countsByLength.size() < 16) + m_countsByLength.resize(16, 0); + str << "2s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[2]; + str << "\t6s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[6]; + str << "\t10s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[10]; + str << "\t14s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[14]; + str << "\n3s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[3]; + str << "\t7s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[7]; + str << "\t11s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[11]; + str << "\t15s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[15]; + str << "\n4s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[4]; + str << "\t8s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[8]; + str << "\t12s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[12]; + str << "\n5s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[5]; + str << "\t9s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[9]; + str << "\t13s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[13]; + str << "\n"; + return str.str(); +} void DawgFactory::Node::print(vector< Node* >& nodelist) @@ -287,11 +317,14 @@ bool DawgFactory::Node::equals(const Node &n) const return true; } -int DawgFactory::Node::wordCount() const +int DawgFactory::Node::wordCount(unsigned int depth, vector &countsByLength) const { int wordCount = ((playability == 0) ? 0 : 1); + if (countsByLength.size() < depth + 1) + countsByLength.resize(depth + 1, 0); + countsByLength[depth] += wordCount; for (size_t i = 0; i < children.size(); i++) - wordCount += children[i].wordCount(); + wordCount += children[i].wordCount(depth + 1, countsByLength); return wordCount; } diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 1a1aa7d..8dd6e03 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -19,6 +19,7 @@ #ifndef QUACKLE_DAWGFACTORY_H #define QUACKLE_DAWGFACTORY_H +#include #include #include "flexiblealphabet.h" @@ -29,7 +30,8 @@ public: DawgFactory(const QString &alphabetFile); ~DawgFactory(); - int wordCount() const { return m_root.wordCount(); }; + int wordCount() const; + string letterCountString() const; int nodeCount() const { return m_nodelist.size(); }; int encodableWords() const { return m_encodableWords; }; int unencodableWords() const { return m_unencodableWords; }; @@ -50,7 +52,7 @@ private: void print(vector< Node* >& m_nodelist); int letterSum() const; - int wordCount() const; + int wordCount(unsigned int depth, vector &countsByLength) const; bool equals(const Node &n) const; Quackle::Letter c; @@ -65,6 +67,7 @@ private: mutable bool sumexplored; mutable int sum; + mutable vector counts; bool deleted; Node* cloneof; @@ -75,6 +78,7 @@ private: int m_unencodableWords; int m_duplicateWords; vector< Node* > m_nodelist; + mutable vector m_countsByLength; Quackle::AlphabetParameters *m_alphas; Node m_root; union { -- cgit v1.2.3 From 6339dec22e2190fd341500206c80425593324bdc Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sun, 11 Oct 2015 18:19:20 -0500 Subject: Fix up lexicon dialog box checks. Get enables and disables right, efficient computation of word counts, etc. --- data/lexica/csw12.gaddag | Bin 25846356 -> 0 bytes data/lexica/twl06.gaddag | Bin 16811716 -> 0 bytes quacker/lexicondialog.cpp | 46 ++++++++++++++++++++++++++++++++++++++++------ quacker/lexicondialog.h | 7 ++++++- quackleio/dawgfactory.cpp | 5 +++-- quackleio/dawgfactory.h | 4 +++- 6 files changed, 52 insertions(+), 10 deletions(-) delete mode 100644 data/lexica/csw12.gaddag delete mode 100644 data/lexica/twl06.gaddag diff --git a/data/lexica/csw12.gaddag b/data/lexica/csw12.gaddag deleted file mode 100644 index 511c99f..0000000 Binary files a/data/lexica/csw12.gaddag and /dev/null differ diff --git a/data/lexica/twl06.gaddag b/data/lexica/twl06.gaddag deleted file mode 100644 index db93e2e..0000000 Binary files a/data/lexica/twl06.gaddag and /dev/null differ diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index e11ae41..f92efb1 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -27,6 +27,18 @@ #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_wordFactory(NULL) { @@ -37,6 +49,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi // 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")); @@ -92,7 +105,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi m_saveChanges->setDefault(true); // hook up signals and slots - // connect(m_lexiconName, SIGNAL(textEdited(const QString &)), this, SLOT(parametersChanged(const QString &))); + connect(m_lexiconName, SIGNAL(textEdited(const QString &)), this, SLOT(parametersChanged(const QString &))); connect(m_addWordsFromFile, SIGNAL(clicked()), this, SLOT(addWordsFromFile())); connect(m_saveChanges, SIGNAL(clicked()), this, SLOT(accept())); connect(m_cancel, SIGNAL(clicked()), this, SLOT(reject())); @@ -109,6 +122,9 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi if (!originalName.isEmpty()) dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName)); + m_lexiconName->setValidator(m_fileNameValidator); + m_lexiconName->setText(m_originalName); + if (!dawgFullFileName.isEmpty()) { m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName)); @@ -117,12 +133,13 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi else m_deleteLexicon->setEnabled(false); - updateLexiconInformation(); + updateLexiconInformation(true); } LexiconDialog::~LexiconDialog() { - + delete m_fileNameValidator; + delete m_wordFactory; } void LexiconDialog::deleteLexicon() @@ -233,15 +250,30 @@ void LexiconDialog::accept() QDialog::accept(); } -void LexiconDialog::updateLexiconInformation() +void LexiconDialog::updateLexiconInformation(bool firstTime) { - int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0; - QByteArray hash = (m_wordFactory && wordCount) ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; + QByteArray hash = m_wordFactory ? QByteArray(m_wordFactory->hashBytes(), 16).toHex() : ""; QString text; QString lengthText; + + // only recompute word count when the dictionary changes + if (m_wordFactory && hash != m_previousHash) + { + m_wordFactory->computeWordCount(); + m_previousHash = hash; + } + 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("File name: ")); text.append(tr("\n\nFile size: ")); text.append(tr("\n\nWord count: ")); @@ -252,4 +284,6 @@ void LexiconDialog::updateLexiconInformation() text.append(hash.left(8)); m_lexiconInformation->setText(text); + + m_saveChanges->setEnabled(hash != m_originalHash && !m_lexiconName->text().isEmpty()); } diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index 39cd546..fa80ec1 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -34,6 +34,7 @@ class QLabel; class QLineEdit; class QPushButton; class DawgFactory; +class FileNameValidator; class LexiconDialog : public QDialog { @@ -44,9 +45,10 @@ public: ~LexiconDialog(); virtual void accept(); - void updateLexiconInformation(); + void updateLexiconInformation(bool firstTime = false); protected slots: + void parametersChanged(const QString &) { updateLexiconInformation(); }; void deleteLexicon(); void addWordsFromFile(); void alphabetChanged(const QString &); @@ -62,6 +64,7 @@ private: QPushButton *m_addWordsFromFile; QPushButton *m_clearAllWords; QLabel *m_lexiconInformation; + FileNameValidator * m_fileNameValidator; QPushButton *m_saveChanges; QPushButton *m_cancel; @@ -69,6 +72,8 @@ private: QString m_originalName; QString m_alphabetFileName; + QByteArray m_originalHash; + QByteArray m_previousHash; DawgFactory *m_wordFactory; }; diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 362dfdc..869ef8e 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -40,6 +40,7 @@ DawgFactory::DawgFactory(const QString &alphabetFile) m_root.lastchild = true; m_hash.int32ptr[0] = m_hash.int32ptr[1] = m_hash.int32ptr[2] = m_hash.int32ptr[3] = 0; + m_encodableWords = m_unencodableWords = m_duplicateWords = m_wordCount = 0; } DawgFactory::~DawgFactory() @@ -186,10 +187,10 @@ void DawgFactory::writeIndex(const UVString& filename) } } -int DawgFactory::wordCount() const +void DawgFactory::computeWordCount() const { m_countsByLength.resize(0); - return m_root.wordCount(0, m_countsByLength); + m_wordCount = m_root.wordCount(0, m_countsByLength); } string DawgFactory::letterCountString() const diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 8dd6e03..5872dc3 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -30,7 +30,8 @@ public: DawgFactory(const QString &alphabetFile); ~DawgFactory(); - int wordCount() const; + void computeWordCount() const; + int wordCount() const { return m_wordCount; }; string letterCountString() const; int nodeCount() const { return m_nodelist.size(); }; int encodableWords() const { return m_encodableWords; }; @@ -78,6 +79,7 @@ private: int m_unencodableWords; int m_duplicateWords; vector< Node* > m_nodelist; + mutable int m_wordCount; mutable vector m_countsByLength; Quackle::AlphabetParameters *m_alphas; Node m_root; -- cgit v1.2.3 From ef4273ba47a2da9cea0aed59235e2d0a86bb8d7e Mon Sep 17 00:00:00 2001 From: John Fultz Date: Tue, 13 Oct 2015 12:00:13 -0500 Subject: Saving custom dictionaries now really works. * Fix a number of remaining bugs in the lexicon dialog. * Fix an error reading the v1 DAWG. * Improve the word counting mechanism. * Make sure the lexicn dialog properly selects and loads its dictionary after it's done. * Implement deleting of user dictionaries. * Clean up dictionary info text in lexicon dialog. * Disable gaddag generation...still have to fix that up to happen at sensible times and with user notification. --- lexiconparameters.cpp | 7 ++--- lexiconparameters.h | 2 +- quacker/lexicondialog.cpp | 72 +++++++++++++++++++++++++++++------------------ quacker/lexicondialog.h | 7 ++++- quacker/settings.cpp | 68 +++++++++++++++++++++++++++++--------------- quacker/settings.h | 2 +- quackleio/dawgfactory.cpp | 60 +++++++++++++-------------------------- quackleio/dawgfactory.h | 17 +++++------ 8 files changed, 128 insertions(+), 107 deletions(-) diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index bc10773..74de78f 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -82,8 +82,6 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter file >> lexparams.m_utf8Alphabet[i]; file.get(); // separator space } - file.get(); // whitespace separator - lexparams.m_wordcount = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; while (!file.eof()) { file.read((char*)(lexparams.m_dawg) + i, 7); @@ -123,18 +121,18 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter p = (dawg[index] << 16) + (dawg[index + 1] << 8) + (dawg[index + 2]); letter = dawg[index + 3]; - t = (p != 0); 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(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordcount(0) + : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordCount(0) { memset(m_hash, 0, sizeof(m_hash)); } @@ -155,6 +153,7 @@ void LexiconParameters::unloadDawg() delete[] m_dawg; m_dawg = NULL; delete m_interpreter; + m_interpreter = NULL; } void LexiconParameters::unloadGaddag() diff --git a/lexiconparameters.h b/lexiconparameters.h index 4eda4f3..f29a589 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -88,7 +88,7 @@ protected: string m_lexiconName; LexiconInterpreter *m_interpreter; char m_hash[16]; - int m_wordcount; + int m_wordCount; vector m_utf8Alphabet; LexiconInterpreter* createInterpreter(char version) const; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index f92efb1..6630e1f 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -40,7 +40,7 @@ public: }; LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDialog(parent), - m_wordFactory(NULL) + m_deleted(false), m_wordFactory(NULL) { m_originalName = originalName; @@ -57,6 +57,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi 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")); @@ -107,6 +108,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi // 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())); @@ -114,26 +116,13 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi setWindowTitle(tr("Configure Lexicon - Quackle")); - Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ""); + Settings::populateComboFromFilenames(m_alphabetCombo, "alphabets", ".quackle_alphabet", ""); alphabetChanged(m_alphabetCombo->currentText()); - string dawgFileName = originalName.toStdString() + ".dawg"; - QString dawgFullFileName; - if (!originalName.isEmpty()) - dawgFullFileName = QString::fromStdString(Quackle::LexiconParameters::findDictionaryFile(dawgFileName)); - m_lexiconName->setValidator(m_fileNameValidator); m_lexiconName->setText(m_originalName); - if (!dawgFullFileName.isEmpty()) - { - m_deleteLexicon->setEnabled(Quackle::LexiconParameters::hasUserDictionaryFile(dawgFileName)); - addWordsFromDawgFile(dawgFullFileName); - } - else - m_deleteLexicon->setEnabled(false); - - updateLexiconInformation(true); + loadOriginalDictionary(); } LexiconDialog::~LexiconDialog() @@ -144,9 +133,11 @@ LexiconDialog::~LexiconDialog() void LexiconDialog::deleteLexicon() { - delete m_wordFactory; - m_wordFactory = NULL; - updateLexiconInformation(); + 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() @@ -245,8 +236,40 @@ void LexiconDialog::addWordsFromTextFile(const QString &textFile) } } +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(); } @@ -256,12 +279,6 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) QString text; QString lengthText; - // only recompute word count when the dictionary changes - if (m_wordFactory && hash != m_previousHash) - { - m_wordFactory->computeWordCount(); - m_previousHash = hash; - } int wordCount = m_wordFactory ? m_wordFactory->wordCount() : 0; if (wordCount == 0) { @@ -274,9 +291,7 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) if (firstTime) m_originalHash = hash; - text.append(tr("File name: ")); - text.append(tr("\n\nFile size: ")); - text.append(tr("\n\nWord count: ")); + text.append(tr("Word count: ")); text.append(QString("%L1").arg(wordCount)); text.append("\n"); text.append(lengthText); @@ -286,4 +301,5 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) m_lexiconInformation->setText(text); m_saveChanges->setEnabled(hash != m_originalHash && !m_lexiconName->text().isEmpty()); + m_clearAllWords->setEnabled(hash != m_originalHash); } diff --git a/quacker/lexicondialog.h b/quacker/lexicondialog.h index fa80ec1..1f1605b 100644 --- a/quacker/lexicondialog.h +++ b/quacker/lexicondialog.h @@ -45,6 +45,9 @@ public: ~LexiconDialog(); virtual void accept(); + bool itemWasDeleted() { return m_deleted; }; + const QString &lexiconName() { return m_finalLexiconName; }; + void updateLexiconInformation(bool firstTime = false); protected slots: @@ -52,6 +55,7 @@ protected slots: void deleteLexicon(); void addWordsFromFile(); void alphabetChanged(const QString &); + void loadOriginalDictionary(); protected: void addWordsFromDawgFile(const QString &dawgfile); @@ -73,7 +77,8 @@ private: QString m_originalName; QString m_alphabetFileName; QByteArray m_originalHash; - QByteArray m_previousHash; + QString m_finalLexiconName; + bool m_deleted; DawgFactory *m_wordFactory; }; diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 3319955..6435605 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -107,7 +107,7 @@ void Settings::createGUI() m_lexiconNameCombo = new QComboBox; connect(m_lexiconNameCombo, SIGNAL(activated(const QString &)), this, SLOT(lexiconChanged(const QString &))); - populateComboFromFilenames(m_lexiconNameCombo, "lexica", "lexicon"); + populateComboFromFilenames(m_lexiconNameCombo, "lexica", ".dawg", "lexicon"); QLabel *lexiconNameLabel = new QLabel(tr("&Lexicon:")); lexiconNameLabel->setBuddy(m_lexiconNameCombo); @@ -118,7 +118,7 @@ void Settings::createGUI() m_alphabetNameCombo = new QComboBox; connect(m_alphabetNameCombo, SIGNAL(activated(const QString &)), this, SLOT(alphabetChanged(const QString &))); - populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ""); + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ".quackle_alphabet", ""); QLabel *alphabetNameLabel = new QLabel(tr("&Alphabet:")); alphabetNameLabel->setBuddy(m_alphabetNameCombo); @@ -129,7 +129,7 @@ void Settings::createGUI() m_themeNameCombo = new QComboBox; connect(m_themeNameCombo, SIGNAL(activated(const QString &)), this, SLOT(themeChanged(const QString &))); - populateComboFromFilenames(m_themeNameCombo, "themes", ""); + populateComboFromFilenames(m_themeNameCombo, "themes", ".ini", ""); QLabel *themeNameLabel = new QLabel(tr("&Theme:")); themeNameLabel->setBuddy(m_themeNameCombo); @@ -140,7 +140,7 @@ void Settings::createGUI() m_boardNameCombo = new QComboBox; connect(m_boardNameCombo, SIGNAL(activated(const QString &)), this, SLOT(boardChanged(const QString &))); - populateComboFromFilenames(m_boardNameCombo, "boards", "board"); + populateComboFromFilenames(m_boardNameCombo, "boards", "", "board"); QLabel *boardNameLabel = new QLabel(tr("&Board:")); boardNameLabel->setBuddy(m_boardNameCombo); @@ -172,6 +172,8 @@ void Settings::createGUI() void Settings::load() { m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()))); + if (m_lexiconNameCombo->currentIndex() == -1) + m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()) + "*")); m_lastGoodLexiconValue = m_lexiconNameCombo->currentIndex(); m_alphabetNameCombo->setCurrentIndex(m_alphabetNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); m_themeNameCombo->setCurrentIndex(m_themeNameCombo->findText(m_themeName)); @@ -269,12 +271,12 @@ void Settings::setQuackleToUseLexiconName(const QString &lexiconName) 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); - } + // 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); } @@ -342,6 +344,9 @@ void Settings::setQuackleToUseBoardName(const QString &boardName) void Settings::lexiconChanged(const QString &lexiconName) { + QString lexicon = lexiconName; + if (lexicon.endsWith("*")) + lexicon.truncate(lexicon.size() - 1); if (m_lexiconNameCombo->currentIndex() == m_lexiconNameCombo->count() - 1) { editLexicon(); @@ -350,11 +355,11 @@ void Settings::lexiconChanged(const QString &lexiconName) m_lexiconNameCombo->setCurrentIndex(m_lastGoodLexiconValue); return; } - setQuackleToUseLexiconName(lexiconName); + 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(); } @@ -465,9 +470,6 @@ void Settings::editBoard() void Settings::loadBoardNameCombo() { - if (m_lexiconNameCombo == 0) - return; - while (m_boardNameCombo->count() > 0) m_boardNameCombo->removeItem(0); @@ -489,12 +491,30 @@ void Settings::loadBoardNameCombo() 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", "lexicon"); + 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(); } } @@ -508,7 +528,7 @@ void Settings::editAlphabet() AlphabetDialog dialog(this); if (dialog.exec()) { - populateComboFromFilenames(m_alphabetNameCombo, "alphabets", "alphabet"); + populateComboFromFilenames(m_alphabetNameCombo, "alphabets", ".quackle_alphabet", "alphabet"); load(); } #endif // 0 @@ -529,8 +549,11 @@ void Settings::editTheme() #endif // 0 } -void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &label) +void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &extension, const QString &label) { + while (combo->count() > 0) + combo->removeItem(0); + QStringList fileList; QDir dir(self()->m_appDataDir); if (dir.cd(path)) @@ -547,6 +570,8 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, for (i = fileList.begin(); i != fileList.end(); ++i) { fileName = *i; + if (!fileName.endsWith(extension)) + continue; periodPos = fileName.indexOf('.'); if (periodPos) { @@ -555,14 +580,13 @@ void Settings::populateComboFromFilenames(QComboBox* combo, const QString &path, } } - for (i = fileList.begin(); i != fileList.end(); ++i) + for (i = list.begin(); i != list.end(); ++i) { - QStringList::iterator j = i; - for (++j; j != fileList.end(); ++j) + for (QStringList::iterator j = i + 1; j != list.end(); ++j) { if (*i == *j) { - *i = "* " + *i; + *i = *i + "*"; list.erase(j); break; } diff --git a/quacker/settings.h b/quacker/settings.h index 7c5738e..babea3c 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -42,7 +42,7 @@ 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 &label); + static void populateComboFromFilenames(QComboBox* combo, const QString &path, const QString &extension, const QString &label); signals: void refreshViews(); diff --git a/quackleio/dawgfactory.cpp b/quackleio/dawgfactory.cpp index 869ef8e..4dd4ec3 100644 --- a/quackleio/dawgfactory.cpp +++ b/quackleio/dawgfactory.cpp @@ -28,6 +28,8 @@ 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); @@ -40,7 +42,6 @@ DawgFactory::DawgFactory(const QString &alphabetFile) m_root.lastchild = true; m_hash.int32ptr[0] = m_hash.int32ptr[1] = m_hash.int32ptr[2] = m_hash.int32ptr[3] = 0; - m_encodableWords = m_unencodableWords = m_duplicateWords = m_wordCount = 0; } DawgFactory::~DawgFactory() @@ -48,7 +49,7 @@ DawgFactory::~DawgFactory() delete m_alphas; } -bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability) +bool DawgFactory::pushWord(const UVString &word, bool inSmaller, int playability) { UVString leftover; Quackle::LetterString encodedWord = m_alphas->encode(word, &leftover); @@ -59,11 +60,12 @@ bool DawgFactory::pushWord(const UVString& word, bool inSmaller, int playability return false; } -bool DawgFactory::pushWord(const Quackle::LetterString& word, bool inSmaller, int playability) +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; } @@ -133,7 +135,7 @@ void DawgFactory::generate() m_root.print(m_nodelist); } -void DawgFactory::writeIndex(const UVString& filename) +void DawgFactory::writeIndex(const string &filename) { ofstream out(filename.c_str(), ios::out | ios::binary); unsigned char bytes[7]; @@ -187,37 +189,26 @@ void DawgFactory::writeIndex(const UVString& filename) } } -void DawgFactory::computeWordCount() const -{ - m_countsByLength.resize(0); - m_wordCount = m_root.wordCount(0, m_countsByLength); -} - string DawgFactory::letterCountString() const { ostringstream str; - if (m_countsByLength.size() < 16) - m_countsByLength.resize(16, 0); - str << "2s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[2]; - str << "\t6s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[6]; - str << "\t10s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[10]; - str << "\t14s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[14]; - str << "\n3s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[3]; - str << "\t7s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[7]; - str << "\t11s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[11]; - str << "\t15s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[15]; - str << "\n4s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[4]; - str << "\t8s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[8]; - str << "\t12s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[12]; - str << "\n5s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[5]; - str << "\t9s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[9]; - str << "\t13s: " << std::setw(7) << std::right << std::setfill(' ') << m_countsByLength[13]; - str << "\n"; + 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) +void DawgFactory::Node::print(vector< Node* > &nodelist) { written = true; @@ -255,7 +246,7 @@ void DawgFactory::Node::print(vector< Node* >& nodelist) // 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 DawgFactory::Node::pushWord(const Quackle::LetterString &word, bool inSmaller, int pb) { bool added; if (word.length() == 0) { @@ -318,17 +309,6 @@ bool DawgFactory::Node::equals(const Node &n) const return true; } -int DawgFactory::Node::wordCount(unsigned int depth, vector &countsByLength) const -{ - int wordCount = ((playability == 0) ? 0 : 1); - if (countsByLength.size() < depth + 1) - countsByLength.resize(depth + 1, 0); - countsByLength[depth] += wordCount; - for (size_t i = 0; i < children.size(); i++) - wordCount += children[i].wordCount(depth + 1, countsByLength); - return wordCount; -} - int DawgFactory::Node::letterSum() const { if (sumexplored) diff --git a/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index 5872dc3..efcc455 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -30,19 +30,18 @@ public: DawgFactory(const QString &alphabetFile); ~DawgFactory(); - void computeWordCount() const; - int wordCount() const { return m_wordCount; }; + 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); + 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 UVString& filename); + void writeIndex(const string &filename); const char* hashBytes() { return m_hash.charptr; }; @@ -50,10 +49,9 @@ private: class Node { public: bool pushWord(const Quackle::LetterString& word, bool inSmaller, int pb); - void print(vector< Node* >& m_nodelist); + void print(vector< Node* > &m_nodelist); int letterSum() const; - int wordCount(unsigned int depth, vector &countsByLength) const; bool equals(const Node &n) const; Quackle::Letter c; @@ -67,7 +65,7 @@ private: bool lastchild; mutable bool sumexplored; - mutable int sum; + mutable unsigned int sum; mutable vector counts; bool deleted; @@ -79,8 +77,7 @@ private: int m_unencodableWords; int m_duplicateWords; vector< Node* > m_nodelist; - mutable int m_wordCount; - mutable vector m_countsByLength; + vector m_countsByLength; Quackle::AlphabetParameters *m_alphas; Node m_root; union { -- cgit v1.2.3 From fd6daafffbcce3bfc385d7508a1ca50174840912 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Wed, 14 Oct 2015 01:29:30 -0500 Subject: Convert dictionaries, add csw15. * All dictionaries are now in v1 DAWG format. * Three corrupt dictionaries were removed. * Implement a way to tag dictionaries for copyright purposes. * CSW15 is now a thing. --- data/lexica/copyrights.txt | 2 ++ data/lexica/csw12.dawg | Bin 1600543 -> 1600616 bytes data/lexica/csw15.dawg | Bin 0 -> 1459769 bytes data/lexica/cswapr07.dawg | Bin 2641009 -> 2641082 bytes data/lexica/greek.dawg | Bin 1248807 -> 1248900 bytes data/lexica/korean.dawg | Bin 1014944 -> 1015145 bytes data/lexica/norwegian.dawg | Bin 1701427 -> 1701518 bytes data/lexica/ods5.dawg | Bin 899171 -> 899244 bytes data/lexica/osps.dawg | Bin 2570477 -> 2570571 bytes data/lexica/russian.dawg | Bin 240528 -> 0 bytes data/lexica/sowpods.dawg | Bin 1473486 -> 1473559 bytes data/lexica/tuvan.dawg | Bin 15492 -> 0 bytes data/lexica/twl06.dawg | Bin 1816584 -> 1816657 bytes data/lexica/twl06_wild.dawg | Bin 6215056 -> 0 bytes data/lexica/twl98.dawg | Bin 938070 -> 938143 bytes lexiconparameters.cpp | 27 +++++++++++++++++++++------ lexiconparameters.h | 4 ++-- quacker/lexicondialog.cpp | 3 ++- quacker/quacker.cpp | 32 +++++++++++++++++++++++--------- quacker/settings.cpp | 12 +++++++++--- quacker/settings.h | 2 ++ quackle.sublime-project | 4 ++-- 22 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 data/lexica/copyrights.txt create mode 100644 data/lexica/csw15.dawg delete mode 100644 data/lexica/russian.dawg delete mode 100644 data/lexica/tuvan.dawg delete mode 100644 data/lexica/twl06_wild.dawg 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 Binary files a/data/lexica/csw12.dawg and b/data/lexica/csw12.dawg differ diff --git a/data/lexica/csw15.dawg b/data/lexica/csw15.dawg new file mode 100644 index 0000000..da4defa Binary files /dev/null and b/data/lexica/csw15.dawg differ diff --git a/data/lexica/cswapr07.dawg b/data/lexica/cswapr07.dawg index a2863ac..6e6dca7 100644 Binary files a/data/lexica/cswapr07.dawg and b/data/lexica/cswapr07.dawg differ diff --git a/data/lexica/greek.dawg b/data/lexica/greek.dawg index 90835e5..b8666ec 100644 Binary files a/data/lexica/greek.dawg and b/data/lexica/greek.dawg differ diff --git a/data/lexica/korean.dawg b/data/lexica/korean.dawg index 6aa5d3e..c55769e 100644 Binary files a/data/lexica/korean.dawg and b/data/lexica/korean.dawg differ diff --git a/data/lexica/norwegian.dawg b/data/lexica/norwegian.dawg index 0acdb33..2db2269 100644 Binary files a/data/lexica/norwegian.dawg and b/data/lexica/norwegian.dawg differ diff --git a/data/lexica/ods5.dawg b/data/lexica/ods5.dawg index eabc68a..8d440d9 100644 Binary files a/data/lexica/ods5.dawg and b/data/lexica/ods5.dawg differ diff --git a/data/lexica/osps.dawg b/data/lexica/osps.dawg index 6c02ecd..6d81d3d 100644 Binary files a/data/lexica/osps.dawg and b/data/lexica/osps.dawg differ diff --git a/data/lexica/russian.dawg b/data/lexica/russian.dawg deleted file mode 100644 index 3b01c8e..0000000 Binary files a/data/lexica/russian.dawg and /dev/null differ diff --git a/data/lexica/sowpods.dawg b/data/lexica/sowpods.dawg index b5d89df..e052222 100644 Binary files a/data/lexica/sowpods.dawg and b/data/lexica/sowpods.dawg differ diff --git a/data/lexica/tuvan.dawg b/data/lexica/tuvan.dawg deleted file mode 100644 index f5f31b3..0000000 Binary files a/data/lexica/tuvan.dawg and /dev/null differ diff --git a/data/lexica/twl06.dawg b/data/lexica/twl06.dawg index 9388487..e0afed7 100644 Binary files a/data/lexica/twl06.dawg and b/data/lexica/twl06.dawg differ diff --git a/data/lexica/twl06_wild.dawg b/data/lexica/twl06_wild.dawg deleted file mode 100644 index 34e78a0..0000000 Binary files a/data/lexica/twl06_wild.dawg and /dev/null differ diff --git a/data/lexica/twl98.dawg b/data/lexica/twl98.dawg index a835b7c..e6f451e 100644 Binary files a/data/lexica/twl98.dawg and b/data/lexica/twl98.dawg differ diff --git a/lexiconparameters.cpp b/lexiconparameters.cpp index 74de78f..f04a941 100644 --- a/lexiconparameters.cpp +++ b/lexiconparameters.cpp @@ -132,7 +132,7 @@ class Quackle::V1LexiconInterpreter : public LexiconInterpreter }; LexiconParameters::LexiconParameters() - : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL), m_wordCount(0) + : m_dawg(NULL), m_gaddag(NULL), m_interpreter(NULL) { memset(m_hash, 0, sizeof(m_hash)); } @@ -228,22 +228,37 @@ bool LexiconParameters::hasUserDictionaryFile(const string &lexicon) return QUACKLE_DATAMANAGER->hasUserDataFile("lexica", lexicon); } -UVString LexiconParameters::hashString(bool shortened) const +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' }; + 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 == 5) + if (shortened && i == 3) break; - if (i % 2 == 1) - hashStr.push_back('-'); } 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) diff --git a/lexiconparameters.h b/lexiconparameters.h index f29a589..d19f695 100644 --- a/lexiconparameters.h +++ b/lexiconparameters.h @@ -80,7 +80,8 @@ public: } const GaddagNode *gaddagRoot() const { return (GaddagNode *) &m_gaddag[0]; }; - UVString hashString(bool shortened) const; + string hashString(bool shortened) const; + string copyrightString() const; protected: unsigned char *m_dawg; @@ -88,7 +89,6 @@ protected: string m_lexiconName; LexiconInterpreter *m_interpreter; char m_hash[16]; - int m_wordCount; vector m_utf8Alphabet; LexiconInterpreter* createInterpreter(char version) const; diff --git a/quacker/lexicondialog.cpp b/quacker/lexicondialog.cpp index 6630e1f..91eb676 100644 --- a/quacker/lexicondialog.cpp +++ b/quacker/lexicondialog.cpp @@ -117,6 +117,7 @@ LexiconDialog::LexiconDialog(QWidget *parent, const QString &originalName) : QDi 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); @@ -300,6 +301,6 @@ void LexiconDialog::updateLexiconInformation(bool firstTime) m_lexiconInformation->setText(text); - m_saveChanges->setEnabled(hash != m_originalHash && !m_lexiconName->text().isEmpty()); + m_saveChanges->setEnabled(true/*hash != m_originalHash && !m_lexiconName->text().isEmpty()*/); m_clearAllWords->setEnabled(hash != m_originalHash); } diff --git a/quacker/quacker.cpp b/quacker/quacker.cpp index a1f7064..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,7 +2117,7 @@ void TopLevel::firstTimeRun() void TopLevel::about() { - QMessageBox::about(this, tr("About Quackle 1.0"), dialogText(tr( + QString aboutText = tr( "

    Quackle 1.0 is a crossword game playing, analysis, and study tool. Visit the Quackle homepage at http://quackle.org for more information.

    " "

    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.

    " "

    Copyright 2005-2015 by

    " @@ -2130,7 +2127,24 @@ void TopLevel::about() "
" "

Quackle is free, open-source software licensed under the terms of the GNU General Public License Version 3. See

" "

http://quackle.org/LICENSE

" -))); +"

Dictionary copyrights

    " +); + + 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 += "
  • " + line.mid(pos + 1) + "
  • "; + } + fclose(file); + aboutText += "
"; + } + QMessageBox::about(this, tr("About Quackle 1.0"), dialogText(aboutText)); } void TopLevel::hints() diff --git a/quacker/settings.cpp b/quacker/settings.cpp index 6435605..e22a29e 100644 --- a/quacker/settings.cpp +++ b/quacker/settings.cpp @@ -148,6 +148,8 @@ void Settings::createGUI() m_editBoard->setMaximumWidth(60); connect(m_editBoard, SIGNAL(clicked()), this, SLOT(editBoard())); + m_copyrightLabel = new QLabel(); + layout->addWidget(lexiconNameLabel, 0, 0, Qt::AlignRight); layout->addWidget(m_lexiconNameCombo, 0, 1); layout->addWidget(m_editLexicon, 0, 2); @@ -160,25 +162,28 @@ void Settings::createGUI() 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); layout->setColumnMinimumWidth(3, 0); layout->setColumnStretch(3, 1); layout->setRowMinimumHeight(4, 0); layout->setRowStretch(4, 1); + load(); } void Settings::load() { - m_lexiconNameCombo->setCurrentIndex(m_lexiconNameCombo->findText(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()))); + 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(QuackleIO::Util::stdStringToQString(QUACKLE_LEXICON_PARAMETERS->lexiconName()) + "*")); + 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(QuackleIO::Util::stdStringToQString(QUACKLE_ALPHABET_PARAMETERS->alphabetName()))); + 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() @@ -279,6 +284,7 @@ void Settings::setQuackleToUseLexiconName(const QString &lexiconName) // } QUACKLE_STRATEGY_PARAMETERS->initialize(lexiconNameStr); + m_copyrightLabel->setText(QString::fromUtf8(QUACKLE_LEXICON_PARAMETERS->copyrightString().c_str())); } } diff --git a/quacker/settings.h b/quacker/settings.h index babea3c..ef3449e 100644 --- a/quacker/settings.h +++ b/quacker/settings.h @@ -29,6 +29,7 @@ class QComboBox; class QCheckBox; class QPushButton; +class QLabel; using namespace std; @@ -88,6 +89,7 @@ protected: QPushButton *m_editAlphabet; QPushButton *m_editTheme; QPushButton *m_editBoard; + QLabel *m_copyrightLabel; QString m_appDataDir; QString m_userDataDir; QString m_themeName; diff --git a/quackle.sublime-project b/quackle.sublime-project index 2ca8db5..c7f47cd 100644 --- a/quackle.sublime-project +++ b/quackle.sublime-project @@ -6,8 +6,8 @@ "file_exclude_patterns" : ["*.tgz", "*.sublime-workspace", ".tags*", "dawginput.raw", "playabilities.raw", "smaller.raw", ".gitattributes", "*.Debug", "*.Release", "*.pfx", "*.cer", - "makegaddag", "makeminidawg", "gaddagize", "Makefile"], - "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "lexica", + "makegaddag", "makeminidawg", "gaddagize", "Makefile", "*.dawg", "*.gaddag"], + "folder_exclude_patterns" : ["obj", "moc", "build", "*.xcodeproj", "lib", "strategy", "debug", "release", "makeswelexicon", "lisp", "DerivedData"] } ] -- cgit v1.2.3 From 86e8d2a1247e8c6b00cefda10b2a96376a1540ca Mon Sep 17 00:00:00 2001 From: John Fultz Date: Wed, 14 Oct 2015 04:37:27 -0500 Subject: Windows build fixes. --- encodeleaves/encodeleaves.pro | 10 +++++++--- makegaddag/makegaddag.cpp | 6 +++--- makegaddag/makegaddag.pro | 10 +++++++--- makeminidawg/makeminidawg.pro | 10 +++++++--- makeminidawg/makeminidawgmain.cpp | 2 +- quacker/quacker.pro | 11 ++++------- quackle.pro | 2 ++ quackleio/dawgfactory.h | 3 ++- quackleio/gaddagfactory.h | 3 ++- quackleio/iotest/iotest.pro | 10 +++++++--- quackleio/quackleio.pro | 2 ++ test/test.pro | 10 +++++++--- 12 files changed, 51 insertions(+), 28 deletions(-) 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/makegaddag/makegaddag.cpp b/makegaddag/makegaddag.cpp index 7a768cd..ef2c439 100644 --- a/makegaddag/makegaddag.cpp +++ b/makegaddag/makegaddag.cpp @@ -62,7 +62,7 @@ int main(int argc, char **argv) QString alphabetFile = QString("../data/alphabets/%1.quackle_alphabet").arg(alphabet); UVcout << "Using alphabet file: " << QuackleIO::Util::qstringToString(alphabetFile) << endl; - GaddagFactory factory(alphabetFile); + GaddagFactory factory(QuackleIO::Util::qstringToString(alphabetFile)); QFile file(inputFilename); if (!file.exists()) @@ -88,7 +88,7 @@ int main(int argc, char **argv) if (stream.atEnd()) break; - if (!factory.pushWord(originalQString)) + if (!factory.pushWord(QuackleIO::Util::qstringToString(originalQString))) UVcout << "not encodable without leftover: " << QuackleIO::Util::qstringToString(originalQString) << endl; } @@ -99,7 +99,7 @@ int main(int argc, char **argv) factory.generate(); UVcout << "Writing index..."; - factory.writeIndex(outputFilename); + factory.writeIndex(outputFilename.toUtf8().constData()); UVcout << endl; 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.pro b/makeminidawg/makeminidawg.pro index 729e6b4..e728a8a 100644 --- a/makeminidawg/makeminidawg.pro +++ b/makeminidawg/makeminidawg.pro @@ -12,16 +12,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 HEADERS += diff --git a/makeminidawg/makeminidawgmain.cpp b/makeminidawg/makeminidawgmain.cpp index eee2b37..dfd3c82 100644 --- a/makeminidawg/makeminidawgmain.cpp +++ b/makeminidawg/makeminidawgmain.cpp @@ -125,7 +125,7 @@ int main(int argc, char **argv) if (stream.atEnd()) break; - if (!factory.pushWord(word, inSmaller, pb)) + if (!factory.pushWord(QuackleIO::Util::qstringToString(word), inSmaller, pb)) UVcout << "not encodable without leftover: " << QuackleIO::Util::qstringToString(word) << endl; } diff --git a/quacker/quacker.pro b/quacker/quacker.pro index 9906e45..8bd1e4f 100644 --- a/quacker/quacker.pro +++ b/quacker/quacker.pro @@ -12,17 +12,12 @@ CONFIG += release 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 +27,8 @@ win32:!win32-g++ { } macx:LIBS += -framework CoreFoundation +QMAKE_CXXFLAGS += -std=c++11 + # Input HEADERS += *.h SOURCES += *.cpp 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/quackleio/dawgfactory.h b/quackleio/dawgfactory.h index efcc455..7af4d68 100644 --- a/quackleio/dawgfactory.h +++ b/quackleio/dawgfactory.h @@ -19,6 +19,7 @@ #ifndef QUACKLE_DAWGFACTORY_H #define QUACKLE_DAWGFACTORY_H +#include #include #include #include "flexiblealphabet.h" @@ -82,7 +83,7 @@ private: Node m_root; union { char charptr[16]; - int32_t int32ptr[4]; + std::int32_t int32ptr[4]; } m_hash; static const char m_versionNumber = 1; diff --git a/quackleio/gaddagfactory.h b/quackleio/gaddagfactory.h index 415baff..3017085 100644 --- a/quackleio/gaddagfactory.h +++ b/quackleio/gaddagfactory.h @@ -19,6 +19,7 @@ #ifndef QUACKLE_GADDAGFACTORY_H #define QUACKLE_GADDAGFACTORY_H +#include #include "flexiblealphabet.h" @@ -65,7 +66,7 @@ private: Node m_root; union { char charptr[16]; - int32_t int32ptr[4]; + std::int32_t int32ptr[4]; } m_hash; }; 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/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 -- cgit v1.2.3 From 23f13f666c42068ed086c5a5791063465db653c7 Mon Sep 17 00:00:00 2001 From: John Fultz Date: Sun, 18 Oct 2015 23:34:39 -0500 Subject: Fix version, Windows Visual Studio build. --- quacker/quacker.pro | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/quacker/quacker.pro b/quacker/quacker.pro index 8bd1e4f..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,6 +10,10 @@ 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 -- cgit v1.2.3