diff options
Diffstat (limited to 'quackleio')
30 files changed, 2847 insertions, 0 deletions
diff --git a/quackleio/.gitignore b/quackleio/.gitignore new file mode 100644 index 0000000..f878785 --- /dev/null +++ b/quackleio/.gitignore @@ -0,0 +1,6 @@ +Makefile +obj +Makefile.Debug +Makefile.Release +debug +release diff --git a/quackleio/LICENSE b/quackleio/LICENSE new file mode 100644 index 0000000..8233446 --- /dev/null +++ b/quackleio/LICENSE @@ -0,0 +1,5 @@ +Copyright (c) 2005-2006, Jason Katz-Brown and John O'Laughlin. + +libquackleio, the Quackle input/output library, is under the same license +as libquackle in the parent directory. Please see quackle/LICENSE for license +information. diff --git a/quackleio/README b/quackleio/README new file mode 100644 index 0000000..d7ed61e --- /dev/null +++ b/quackleio/README @@ -0,0 +1,6 @@ +IO library for saving and loading Quackle data structures. + +Supported formats: + +- GCG + See http://www.poslfit.com/scrabble/gcg diff --git a/quackleio/TODO b/quackleio/TODO new file mode 100644 index 0000000..01e57b2 --- /dev/null +++ b/quackleio/TODO @@ -0,0 +1,5 @@ +GCG: + +challenge bonus +internation-rules endgame score adjustment +time penalties diff --git a/quackleio/dict.cpp b/quackleio/dict.cpp new file mode 100644 index 0000000..e949ef0 --- /dev/null +++ b/quackleio/dict.cpp @@ -0,0 +1,130 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <vector> +#include <map> + +#include "dict.h" +#include "util.h" + +using namespace std; +using namespace Dict; + +WordList::SortType WordList::sortType = Playability; + +WordList::WordList() +{ +} + +WordList::~WordList() +{ +} + +void WordList::setSortBy(SortType _sortType) +{ + sortType = _sortType; +} + +bool operator<(const Dict::Word &word1, const Dict::Word &word2) +{ + switch (Dict::WordList::sortType) + { + case Dict::WordList::Alphabetical: + return word1.word < word2.word; + + case Dict::WordList::Playability: + if (word1.playability != word2.playability) + return word1.playability > word2.playability; + + if (word1.word.length() != word2.word.length()) + return word1.word.length() < word2.word.length(); + + // fall through + + case Dict::WordList::Probability: + return word1.probability > word2.probability; + + case Dict::WordList::Length: + case Dict::WordList::LengthLongestFirst: + { + bool ret; + if (word1.word.length() != word2.word.length()) + ret = word1.word.length() < word2.word.length(); + else + ret = word1.word < word2.word; + + if (Dict::WordList::sortType == Dict::WordList::LengthLongestFirst) + return !ret; + return ret; + } + } + + return false; +} + +ExtensionList Word::extensionsByLength(int length, const ExtensionList &list) +{ + ExtensionList ret; + + for (ExtensionList::const_iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it).extensionLetterString.length() == length) + { + Extension extension(*it); + extension.word = QuackleIO::Util::letterStringToQString((*it).extensionLetterString); + ret.append(extension); + } + } + + return ret; +} + +ExtensionList Word::getFrontExtensionList() const +{ + return getExtensionList(true); +} + +ExtensionList Word::getBackExtensionList() const +{ + return getExtensionList(false); +} + +ExtensionList Word::getExtensionList(bool front) const +{ + ExtensionList ret; + + const vector<Quackle::ExtensionWithInfo> *list = front? &frontExtensions : &backExtensions; + + for (vector<Quackle::ExtensionWithInfo>::const_iterator it = list->begin(); it != list->end(); ++it) + { + Extension extension(*it); + extension.word = QuackleIO::Util::letterStringToQString((*it).extensionLetterString); + ret.append(extension); + } + + return ret; +} + +Extension::Extension(const Quackle::ExtensionWithInfo extensionWithInfo) +{ + playability = extensionWithInfo.playability; + probability = extensionWithInfo.probability; + british = extensionWithInfo.british; +} diff --git a/quackleio/dict.h b/quackleio/dict.h new file mode 100644 index 0000000..4935e4b --- /dev/null +++ b/quackleio/dict.h @@ -0,0 +1,91 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLEIO_DICT_H +#define QUACKLEIO_DICT_H + +#include <vector> + +#include <QStringList> + +#include <alphabetparameters.h> +#include <generator.h> + +namespace Dict +{ + +class Extension : public Quackle::ExtensionWithInfo +{ +public: + Extension() {} + Extension(const Quackle::ExtensionWithInfo extensionWithInfo); + QString word; +}; + +typedef QList<Extension> ExtensionList; + +class Word : public Quackle::WordWithInfo +{ +public: + QString word; + + static ExtensionList extensionsByLength(int length, const ExtensionList &list); + + ExtensionList getFrontExtensionList() const; + ExtensionList getBackExtensionList() const; + +private: + ExtensionList getExtensionList(bool front) const; +}; + +class WordList : public QList<Word> +{ +public: + WordList(); + ~WordList(); + + enum SortType { Alphabetical, Playability, Length, LengthLongestFirst, Probability }; + + void setSortBy(SortType sortType); + static SortType sortType; +}; + +typedef QList<WordList> WordListList; + +class Querier +{ +public: + virtual ~Querier() {}; + + enum QueryFlags { None = 0x0000, WithExtensions = 0x0001, NoRequireAllLetters = 0x0002, CallUpdate = 0x0004 }; + + virtual WordList query(const QString &query, int flags = None) = 0; + virtual QString alphagram(const QString &letters) const = 0; + virtual bool isBritish(const Quackle::LetterString &word) = 0; + virtual bool isLoaded() const = 0; +}; + +} + +// Returns true if word1 is less playable than word2; +// otherwise returns false. +bool operator<(const Dict::Word &word1, const Dict::Word &word2); + +#endif diff --git a/quackleio/dictfactory.cpp b/quackleio/dictfactory.cpp new file mode 100644 index 0000000..1166390 --- /dev/null +++ b/quackleio/dictfactory.cpp @@ -0,0 +1,36 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "dictfactory.h" +#include "dictimplementation.h" + +using namespace QuackleIO; + +Dict::Querier *DictFactory::m_querier = 0; + +Dict::Querier *DictFactory::querier() +{ + if (!m_querier) + { + m_querier = new DictImplementation; + } + + return m_querier; +} diff --git a/quackleio/dictfactory.h b/quackleio/dictfactory.h new file mode 100644 index 0000000..9693179 --- /dev/null +++ b/quackleio/dictfactory.h @@ -0,0 +1,40 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_DICTFACTORY_H +#define QUACKLE_DICTFACTORY_H + +#include "dict.h" + +namespace QuackleIO +{ + +class DictFactory +{ +public: + static Dict::Querier *querier(); + +private: + static Dict::Querier *m_querier; +}; + +} + +#endif diff --git a/quackleio/dictimplementation.cpp b/quackleio/dictimplementation.cpp new file mode 100644 index 0000000..5ea2e15 --- /dev/null +++ b/quackleio/dictimplementation.cpp @@ -0,0 +1,104 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <alphabetparameters.h> +#include <datamanager.h> +#include <generator.h> +#include <lexiconparameters.h> +#include <quackleio/util.h> + +#include "dictimplementation.h" + +QuackleIO::DictImplementation::DictImplementation() +{ +} + +QuackleIO::DictImplementation::~DictImplementation() +{ +} + +Dict::WordList QuackleIO::DictImplementation::query(const QString &query, int flags) +{ + QString modifiedQuery = query; + modifiedQuery.replace(".", "?"); + + int anagramFlags = Quackle::Generator::ClearBlanknesses; + + if (flags & Dict::Querier::NoRequireAllLetters) + anagramFlags |= Quackle::Generator::NoRequireAllLetters; + + QRegExp wildcardRegexp("[\\*/]"); + if (wildcardRegexp.indexIn(modifiedQuery) >= 0) + { + if (!(flags & Dict::Querier::NoRequireAllLetters)) + anagramFlags |= Quackle::Generator::AddAnyLetters; + + modifiedQuery.replace(wildcardRegexp, QString()); + } + + vector<Quackle::LetterString> words(m_generator.anagramLetters(QuackleIO::Util::encode(modifiedQuery), anagramFlags)); + Dict::WordList ret; + + vector<Quackle::LetterString>::const_iterator end = words.end(); + for (vector<Quackle::LetterString>::const_iterator it = words.begin(); it != end; ++it) + { + Dict::Word dictWord; + dictWord.word = QuackleIO::Util::letterStringToQString(*it); + dictWord.wordLetterString = (*it); + m_generator.storeWordInfo(&dictWord); + + if (flags & WithExtensions) + m_generator.storeExtensions(&dictWord); + + ret.push_back(dictWord); + } + + if (flags & NoRequireAllLetters) + { + ret.setSortBy(Dict::WordList::LengthLongestFirst); + } + else + { + ret.setSortBy(Dict::WordList::Alphabetical); + } + + qSort(ret); + + return ret; +} + +QString QuackleIO::DictImplementation::alphagram(const QString &letters) const +{ + return QuackleIO::Util::letterStringToQString(QuackleIO::Util::alphagram(QuackleIO::Util::encode(letters))); +} + +bool QuackleIO::DictImplementation::isLoaded() const +{ + return QUACKLE_LEXICON_PARAMETERS->hasSomething(); +} + +bool QuackleIO::DictImplementation::isBritish(const Quackle::LetterString &word) +{ + Quackle::WordWithInfo wordWithInfo; + wordWithInfo.wordLetterString = word; + m_generator.storeWordInfo(&wordWithInfo); + return wordWithInfo.british; +} + diff --git a/quackleio/dictimplementation.h b/quackleio/dictimplementation.h new file mode 100644 index 0000000..8773f6f --- /dev/null +++ b/quackleio/dictimplementation.h @@ -0,0 +1,49 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_DICTIMPLEMENTATION_H +#define QUACKLE_DICTIMPLEMENTATION_H + +#include <generator.h> + +#include "dict.h" + +namespace QuackleIO +{ + +class DictImplementation : public Dict::Querier +{ +public: + DictImplementation(); + virtual ~DictImplementation(); + + virtual Dict::WordList query(const QString &query, int flags = None); + virtual QString alphagram(const QString &letters) const; + virtual bool isBritish(const Quackle::LetterString &word); + virtual bool isLoaded() const; + +private: + Quackle::Generator m_generator; +}; + +} + +#endif + diff --git a/quackleio/flexiblealphabet.cpp b/quackleio/flexiblealphabet.cpp new file mode 100644 index 0000000..aca6e00 --- /dev/null +++ b/quackleio/flexiblealphabet.cpp @@ -0,0 +1,131 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QtCore> + +#include "flexiblealphabet.h" +#include "util.h" + +using namespace QuackleIO; + +FlexibleAlphabetParameters::FlexibleAlphabetParameters() +{ +} + +bool FlexibleAlphabetParameters::load(const QString &filename) +{ + QFile file(filename); + + if (!file.exists()) + { + UVcout << "alphabet parameters do not exist: " << QuackleIO::Util::qstringToString(filename) << endl; + return false; + } + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + UVcout << "Could not open " << QuackleIO::Util::qstringToString(filename) << endl; + return false; + } + + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + QString line; + Quackle::Letter letter = QUACKLE_FIRST_LETTER; + while (!stream.atEnd()) + { + line = stream.readLine().simplified(); + QStringList strings = line.split(QRegExp("\\s+")); + + if (line.startsWith("#")) + continue; + + if (strings.isEmpty()) + continue; + + QString text = strings.front(); + const UVString textUV = QuackleIO::Util::qstringToString(text); + strings.pop_front(); + + const bool isBlank = text.startsWith("blank", Qt::CaseInsensitive); + + if (isBlank) + { + if (strings.size() < 2) + { + UVcerr << "Error reading in alphabet: Blank specification does not specify count and score."; + continue; + } + } + else + { + if (strings.size() < 4) + { + UVcerr << "Error reading in alphabet: Letter specification does specify blank text, count, score, and vowelness."; + continue; + } + } + + QString blankText; + if (!isBlank) + { + blankText = strings.front(); + strings.pop_front(); + } + + + bool ok = false; + int score = strings.takeFirst().toInt(&ok); + if (!ok) + UVcerr << "Score of letter " << textUV << " is unparsable."; + + int count = strings.takeFirst().toInt(&ok); + if (!ok) + UVcerr << "Count of letter " << textUV << " is unparsable."; + + bool isVowel = false; + if (!isBlank) + { + isVowel = strings.takeFirst().toInt(&ok); + if (!ok) + UVcerr << "Vowelness of letter " << textUV << " is unparsable. (Must be 0 or 1.)"; + } + + if (isBlank) + { + setCount(QUACKLE_BLANK_MARK, count); + setScore(QUACKLE_BLANK_MARK, score); + } + else + { + const UVString blankTextUV = QuackleIO::Util::qstringToString(blankText); + + setLetterParameter(letter, Quackle::LetterParameter(letter, textUV, blankTextUV, score, count, isVowel)); + ++letter; + + //UVcout << "New letter " << textUV << " [" << (int)letter << "]" << endl; + } + } + + file.close(); + return true; +} + diff --git a/quackleio/flexiblealphabet.h b/quackleio/flexiblealphabet.h new file mode 100644 index 0000000..cd4abae --- /dev/null +++ b/quackleio/flexiblealphabet.h @@ -0,0 +1,49 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_FLEXIBLEALPHABET_H +#define QUACKLE_FLEXIBLEALPHABET_H + +#include "alphabetparameters.h" + +class QString; + +namespace QuackleIO +{ + +class FlexibleAlphabetParameters : public Quackle::AlphabetParameters +{ +public: + FlexibleAlphabetParameters(); + + // Loads alphabet in our format. + // Lines of file should have format: + // <text (utf8)> <blank text (utf8)> <score (int)> <count (int)> <isVowel (0 or 1)> + // OR + // blank <score (int)> <count (int)> + // OR + // # some comment + bool load(const QString &filename); +}; + +} + +#endif + diff --git a/quackleio/froggetopt.cpp b/quackleio/froggetopt.cpp new file mode 100644 index 0000000..68f20fe --- /dev/null +++ b/quackleio/froggetopt.cpp @@ -0,0 +1,660 @@ +/********************************************************************** + * Copyright (c) 2003, 2004, froglogic Porten & Stadlbauer GbR + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the froglogic nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +#define QT_NO_CAST_ASCII +#define QT_NO_ASCII_CAST + +#include <QtCore> + +#include <stdlib.h> +#include <assert.h> + +#include "froggetopt.h" + +/** + \class GetOpt + + \brief A command line option parser. + + This class helps to overcome the repetitive, tedious and + error-prone task of parsing the command line options passed to your + application by the user. Specify the acceptable syntax with a + minimum of statements in a readable way, check it against the + actual arguments passed and find the retrieved values in variables + of your program. The name \em GetOpt is based on similar utilities + build into the Unix shell and other languages. + + A command line that a user might have entered is: + + \code + app -v --config=my.cnf -Wall input.dat + \endcode + + The typical usage has three stages: + + -# Construct a parser specifying what arguments to parse + -# Set up the list of allowed and required options + -# Run the parser + + For the first step there are three different constructors that + either take arguments directly from \c main(), \c QApplication or a + user specified list. Setting up the accepted syntax is done by a + set of \c add functions like addSwitch(). The final step of running + the parser is simply done by calling parse(). + + A short example implementing a \c --verbose switch: + + \code + int main(int argc, char **argv) + { + GetOpt opts(argc, argv); + bool verbose; + opts.addSwitch("verbose", &verbose); + if (!opts.parse()) + return 1; + if (verbose) + cout << "VERBOSE mode on" << endl; + ... + \endcode + + For a better understanding of the function names we'll better + define some terms used in the API and its documentation: + + - \em Argument An argument is a plain text token like e.g. a file + name one typically passes to an editor when invoking it. + - \em Switch A switch is an on/off kind of argument without the need + of additional information. Example: \c --debug. + - \em Option An option is a normally optional argument with a key-value + syntax like \c --output=out.txt or \c -I/usr/include. + - \em Short \em Option A short option is a one letter option with a + preceding dash. Like \c -v. + - \em Long \em Option A long option has a more verbose, + multi-letter name like \c --debug. + . + + \author froglogic GbR <contact@froglogic.com> + */ + + +/** + Constructs a command line parser from the arguments stored in a + previously created QApplication instance. + + Example usage: + \code + QApplication a(argc, argv); + + GetOpt opt; + \endcode + + This constructor is probably the most convenient one to use in a + regular Qt application. Note that QApplication may already have + removed Qt (or X11) specific arguments. Also see + QApplication::argv() and QApplication::argc(). + */ +GetOpt::GetOpt() +{ + if ( !QCoreApplication::instance() ) + qFatal( "GetOpt: requires a QApplication instance to be constructed first" ); + + init( QCoreApplication::instance()->argc(), QCoreApplication::instance()->argv(), 1 ); +} + +/** + \internal + */ +GetOpt::GetOpt( int offset ) +{ + if ( !QCoreApplication::instance() ) + qFatal( "GetOpt: requires a QApplication instance to be constructed first" ); + + init( QCoreApplication::instance()->argc(), QCoreApplication::instance()->argv(), offset ); +} + +/** + Construct a command line parser from the array \a argv of string + pointers with the size \a argc. Those parameters have the form + typically found in the \c main() function. That means that you can + simply pass on the arguments specified by the user of your + application. + + Example usage: + + \code + int main(int argc, char **argv) { + GetOpt opt(argc, argv); + ... + } + \endcode + */ +GetOpt::GetOpt( int argc, char *argv[] ) +{ + init( argc, argv ); +} + +/** + Construct a command line parser from the arguments specified in the + list of arguments \a a. This constructor is convenient in those + cases where you want to parse a command line assembled on-the-fly + instead of relying on the \c argc and \c arg parameters passed to + the \c main() function. + */ + GetOpt::GetOpt( const QStringList &a ) +: args( a ) +{ + init( 0, 0 ); +} + +/** + \internal + */ +void GetOpt::init( int argc, char *argv[], int offset ) +{ + numReqArgs = numOptArgs = 0; + currArg = 1; // appname is not part of the arguments + if ( argc ) { + // application name + aname = QFileInfo( QString::fromUtf8( argv[0] ) ).fileName(); + // arguments + for ( int i = offset; i < argc; ++i ) + args.append( QString::fromUtf8( argv[i] ) ); + } +} + +/** + \fn bool GetOpt::parse() + + Parse the command line arguments specified in the constructor under + the conditions set by the various \c add*() functions. On success, + the given variable reference will be initialized with their + respective values and true will be returned. Returns false + otherwise. + + In the future there'll be a way to retrieve an error message. In + the current version the message will be printed to \c stderr. + */ + +/** + \internal + */ +bool GetOpt::parse( bool untilFirstSwitchOnly ) +{ + // qDebug( "parse(%s)", args.join( QString( "," ) ).toLocal8Bit().constData() ); + // push all arguments as we got them on a stack + // more pushes might following when parsing condensed arguments + // like --key=value. + QStack<QString> stack; + + if (!args.empty()) + { + QStringList::const_iterator it = args.end(); + while ( it != args.begin() ) { + --it; + stack.push( *it ); + } + } + + const OptionConstIterator obegin = options.begin(); + const OptionConstIterator oend = options.end(); + enum { StartState, ExpectingState, OptionalState } state = StartState; + Option currOpt; + enum TokenType { LongOpt, ShortOpt, Arg, End } t, currType = End; + bool extraLoop = true; // we'll do an extra round. fake an End argument + while ( !stack.isEmpty() || extraLoop ) { + QString a; + QString origA; + // identify argument type + if ( !stack.isEmpty() ) { + a = stack.pop(); + currArg++; + origA = a; + // qDebug( "popped %s", a.toLocal8Bit().constData() ); + if ( a.startsWith( QString::fromLatin1( "--" ) ) ) { + // recognized long option + a = a.mid( 2 ); + if ( a.isEmpty() ) { + qWarning( "'--' feature not supported, yet" ); + exit( 2 ); + } + t = LongOpt; + // split key=value style arguments + int equal = a.indexOf( '=' ); + if ( equal >= 0 ) { + stack.push( a.mid( equal + 1 ) ); + currArg--; + a = a.left( equal ); + } + } else if ( a.length() == 1 ) { + t = Arg; + } else if ( a[0] == '-' ) { +#if 0 // compat mode for -long style options + if ( a.length() == 2 ) { + t = ShortOpt; + a = a[1]; + } else { + a = a.mid( 1 ); + t = LongOpt; + // split key=value style arguments + int equal = a.find( '=' ); + if ( equal >= 0 ) { + stack.push( a.mid( equal + 1 ) ); + currArg--; + a = a.left( equal ); + } + } +#else + // short option + t = ShortOpt; + // followed by an argument ? push it for later processing. + if ( a.length() > 2 ) { + stack.push( a.mid( 2 ) ); + currArg--; + } + a = a[1]; +#endif + } else { + t = Arg; + } + } else { + // faked closing argument + t = End; + } + // look up among known list of options + Option opt; + if ( t != End ) { + OptionConstIterator oit = obegin; + while ( oit != oend ) { + const Option &o = *oit; + if ( ( t == LongOpt && a == o.lname ) || // ### check state + ( t == ShortOpt && a[0].unicode() == o.sname ) ) { + opt = o; + break; + } + ++oit; + } + if ( t == LongOpt && opt.type == OUnknown ) { + if ( currOpt.type != OVarLen ) { + qWarning( "Unknown option --%s", a.toLocal8Bit().constData() ); + return false; + } else { + // VarLength options support arguments starting with '-' + t = Arg; + } + } else if ( t == ShortOpt && opt.type == OUnknown ) { + if ( currOpt.type != OVarLen ) { + qWarning( "Unknown option -%c", a[0].unicode() ); + return false; + } else { + // VarLength options support arguments starting with '-' + t = Arg; + } + } + + } else { + opt = Option( OEnd ); + } + + // interpret result + switch ( state ) { + case StartState: + if ( opt.type == OSwitch ) { + setSwitch( opt ); + setOptions.insert( opt.lname, 1 ); + setOptions.insert( QString( QChar( opt.sname ) ), 1 ); + } else if ( opt.type == OArg1 || opt.type == ORepeat ) { + state = ExpectingState; + currOpt = opt; + currType = t; + setOptions.insert( opt.lname, 1 ); + setOptions.insert( QString( QChar( opt.sname ) ), 1 ); + } else if ( opt.type == OOpt || opt.type == OVarLen ) { + state = OptionalState; + currOpt = opt; + currType = t; + setOptions.insert( opt.lname, 1 ); + setOptions.insert( QString( QChar( opt.sname ) ), 1 ); + } else if ( opt.type == OEnd ) { + // we're done + } else if ( opt.type == OUnknown && t == Arg ) { + if ( numReqArgs > 0 ) { + if ( reqArg.stringValue->isNull() ) { // ### + *reqArg.stringValue = a; + } else { + qWarning( "Too many arguments" ); + return false; + } + } else if ( numOptArgs > 0 ) { + if ( optArg.stringValue->isNull() ) { // ### + *optArg.stringValue = a; + } else { + qWarning( "Too many arguments" ); + return false; + } + } + } else { + qFatal( "unhandled StartState case %d", opt.type ); + } + break; + case ExpectingState: + if ( t == Arg ) { + if ( currOpt.type == OArg1 ) { + *currOpt.stringValue = a; + state = StartState; + } else if ( currOpt.type == ORepeat ) { + currOpt.listValue->append( a ); + state = StartState; + } else { + abort(); + } + } else { + QString n = currType == LongOpt ? + currOpt.lname : QString( QChar( currOpt.sname ) ); + qWarning( "Expected an argument after '%s' option", n.toLocal8Bit().constData() ); + return false; + } + break; + case OptionalState: + if ( t == Arg ) { + if ( currOpt.type == OOpt ) { + *currOpt.stringValue = a; + state = StartState; + } else if ( currOpt.type == OVarLen ) { + currOpt.listValue->append( origA ); + // remain in this state + } else { + abort(); + } + } else { + // optional argument not specified + if ( currOpt.type == OOpt ) + *currOpt.stringValue = currOpt.def; + if ( t != End ) { + // re-evaluate current argument + stack.push( origA ); + currArg--; + } + state = StartState; + } + break; + } + + if ( untilFirstSwitchOnly && opt.type == OSwitch ) + return true; + + // are we in the extra loop ? if so, flag the final end + if ( t == End ) + extraLoop = false; + } + + if ( numReqArgs > 0 && reqArg.stringValue->isNull() ) { + qWarning( "Lacking required argument" ); + return false; + } + + return true; +} + +/** + \internal + */ +void GetOpt::addOption( Option o ) +{ + // ### check for conflicts + options.append( o ); +} + +/** + Adds a switch with the long name \a lname. If the switch is found + during parsing the bool \a *b will bet set to true. Otherwise the + bool will be initialized to false. + +Example: + +\code +GetOpt opt; +bool verbose; +opt.addSwitch("verbose", &verbose); +\endcode + +The boolean flag \c verbose will be set to true if \c --verbose has +been specified in the command line; false otherwise. + */ +void GetOpt::addSwitch( const QString &lname, bool *b ) +{ + Option opt( OSwitch, 0, lname ); + opt.boolValue = b; + addOption( opt ); + // ### could do all inits at the beginning of parse() + *b = false; +} + +/** + \internal + */ +void GetOpt::setSwitch( const Option &o ) +{ + assert( o.type == OSwitch ); + *o.boolValue = true; +} + +/** + Registers an option with the short name \a s and long name \a l to + the parser. If this option is found during parsing the value will + be stored in the string pointed to by \a v. By default \a *v will + be initialized to \c QString::null. + */ +void GetOpt::addOption( char s, const QString &l, QString *v ) +{ + Option opt( OArg1, s, l ); + opt.stringValue = v; + addOption( opt ); + *v = QString::null; +} + +/** + Registers a long option \a l that can have a variable number of + corresponding value parameters. As there currently is no way to + tell the end of the value list the only sensible use of this option + is at the end of the command line. + +Example: + +\code +QStringList args; +opt.addVarLengthOption("exec", &args); +\endcode + +Above code will lead to "-f" and "test.txt" being stored in \a args +upon + +\code +myapp --exec otherapp -f test.txt +\endcode + */ +void GetOpt::addVarLengthOption( const QString &l, QStringList *v ) +{ + Option opt( OVarLen, 0, l ); + opt.listValue = v; + addOption( opt ); + *v = QStringList(); +} + +/** + Registers an option with the short name \a s that can be specified + repeatedly in the command line. The option values will be stored in + the list pointed to by \a v. If no \a s option is found \a *v will + remain at its default value of an empty QStringList instance. + +Example: + +To parse the \c -I options in a command line like +\code +myapp -I/usr/include -I/usr/local/include +\endcode + +you can use code like this: + +\code +GetOpt opt; +QStringList includes; +opt.addRepeatableOption('I', &includes); +opt.parse(); +\endcode + */ +void GetOpt::addRepeatableOption( char s, QStringList *v ) +{ + Option opt( ORepeat, s, QString::null ); + opt.listValue = v; + addOption( opt ); + *v = QStringList(); +} + +/** + Registers an option with the long name \a l that can be specified + repeatedly in the command line. + + \sa addRepeatableOption( char, QStringList* ) + */ +void GetOpt::addRepeatableOption( const QString &l, QStringList *v ) +{ + Option opt( ORepeat, 0, l ); + opt.listValue = v; + addOption( opt ); + *v = QStringList(); +} + +/** + Adds a long option \a l that has an optional value parameter. If + the value is not specified by the user it will be set to \a def. + +Example: + +\code +GetOpt opt; +QString file; +opt.addOptionalOption("dump", &file, "<stdout>"); +\endcode + +\sa addOption + */ +void GetOpt::addOptionalOption( const QString &l, QString *v, + const QString &def ) +{ + addOptionalOption( 0, l, v, def ); +} + +/** + Adds a short option \a s that has an optional value parameter. If + the value is not specified by the user it will be set to \a def. + */ +void GetOpt::addOptionalOption( char s, const QString &l, + QString *v, const QString &def ) +{ + Option opt( OOpt, s, l ); + opt.stringValue = v; + opt.def = def; + addOption( opt ); + *v = QString::null; +} + +/** + Registers a required command line argument \a name. If the argument + is missing parse() will return false to indicate an error and \a *v + will remain with its default QString::null value. Otherwise \a *v + will be set to the value of the argument. + +Example: + +To accept simple arguments like + +\code +myeditor letter.txt +\endcode + +use a call like: + +\code +QString &file; +opt.addArgument("file", &file); +\endcode + +Note: the \a name parameter has a rather descriptive meaning for +now. It might be used for generating a usage or error message in +the future. Right now, the only current use is in relation with the +isSet() function. + */ +void GetOpt::addArgument( const QString &name, QString *v ) +{ + Option opt( OUnknown, 0, name ); + opt.stringValue = v; + reqArg = opt; + ++numReqArgs; + *v = QString::null; +} + +/** + Registers an optional command line argument \a name. For a more + detailed description see the addArgument() documentation. + + */ +void GetOpt::addOptionalArgument( const QString &name, QString *v ) +{ + Option opt( OUnknown, 0, name ); + opt.stringValue = v; + optArg = opt; + ++numOptArgs; + *v = QString::null; +} + +/** + Returns true if the (long) option or switch \a name has been found + in the command line; returns false otherwise. Leading hyphens are + not part of the name. + + As the set/not set decision can also be made depending on the value + of the variable reference used in the respective \c add*() call + there's generally little use for this function. + */ + +bool GetOpt::isSet( const QString &name ) const +{ + return setOptions.find( name ) != setOptions.end(); +} + +/** + \fn int GetOpt::currentArgument() const + \internal + */ diff --git a/quackleio/froggetopt.h b/quackleio/froggetopt.h new file mode 100644 index 0000000..7376dd4 --- /dev/null +++ b/quackleio/froggetopt.h @@ -0,0 +1,121 @@ +/********************************************************************** + * Copyright (c) 2003, 2004, froglogic Porten & Stadlbauer GbR + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the froglogic nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +#ifndef FROGGETOPT_H +#define FROGGETOPT_H + +#include <QStringList> +#include <QMap> + +class GetOpt { +public: + GetOpt(); + GetOpt( int offset ); + GetOpt( int argc, char *argv[] ); + GetOpt( const QStringList &a ); + + QString appName() const { return aname; } + + // switch (no arguments) + void addSwitch( const QString &lname, bool *b ); + + // options (with arguments, sometimes optional) + void addOption( char s, const QString &l, QString *v ); + void addVarLengthOption( const QString &l, QStringList *v ); + void addRepeatableOption( char s, QStringList *v ); + void addRepeatableOption( const QString &l, QStringList *v ); + void addOptionalOption( const QString &l, QString *v, + const QString &def ); + void addOptionalOption( char s, const QString &l, + QString *v, const QString &def ); + + // bare arguments + void addArgument( const QString &name, QString *v ); + void addOptionalArgument( const QString &name, QString *v ); + + bool parse( bool untilFirstSwitchOnly ); + bool parse() { return parse( false ); } + + bool isSet( const QString &name ) const; + + int currentArgument() const { return currArg; } + +private: + enum OptionType { OUnknown, OEnd, OSwitch, OArg1, OOpt, ORepeat, OVarLen }; + + struct Option; + friend struct Option; + + struct Option { + Option( OptionType t = OUnknown, + char s = 0, const QString &l = QString::null ) + : type( t ), + sname( s ), + lname( l ), + boolValue( 0 ) { } + + OptionType type; + char sname; // short option name (0 if none) + QString lname; // long option name (null if none) + union { + bool *boolValue; + QString *stringValue; + QStringList *listValue; + }; + QString def; + }; + + QList<Option> options; + typedef QList<Option>::const_iterator OptionConstIterator; + QMap<QString, int> setOptions; + + void init( int argc, char *argv[], int offset = 1 ); + void addOption( Option o ); + void setSwitch( const Option &o ); + + QStringList args; + QString aname; + + int numReqArgs; + int numOptArgs; + Option reqArg; + Option optArg; + + int currArg; +}; + +#endif + diff --git a/quackleio/gcgio.cpp b/quackleio/gcgio.cpp new file mode 100644 index 0000000..7c06e3b --- /dev/null +++ b/quackleio/gcgio.cpp @@ -0,0 +1,377 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QtCore> + +#include <datamanager.h> + +#include "game.h" +#include "gcgio.h" +#include "util.h" + +using namespace QuackleIO; + +GCGIO::GCGIO() +{ +} + +Quackle::Game *GCGIO::read(QTextStream &stream, int flags) +{ + Quackle::Game *ret = new Quackle::Game; + Quackle::PlayerList players; + + Quackle::Rack incompleteRack; + bool hasIncompleteRack = false;; + + const bool canMaintainCrosses = flags & Logania::MaintainBoardPreparation; + + bool gameStarted = false; + + QString line; + while (!stream.atEnd()) + { + line = stream.readLine(); + QStringList strings = line.split(QRegExp("\\s+"), QString::SkipEmptyParts); + + if (line.startsWith("#")) + { + if (line.startsWith("#player")) + { + QString firstChunk = strings.front(); + int id = firstChunk.right(1).toInt(); + + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": no player abbreviation in #player" << endl; + return ret; + } + + QString abbreviation = strings.front(); + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": no player name in #player" << endl; + return ret; + } + + QString name = strings.join(" "); + + Quackle::Player newPlayer(Util::qstringToString(name), Quackle::Player::HumanPlayerType); + newPlayer.setId(id); + newPlayer.setAbbreviatedName(Util::qstringToString(abbreviation)); + players.push_back(newPlayer); + } + else if (line.startsWith("#title")) + ret->setTitle(Util::qstringToString(line.right(line.length() - 7))); + else if (line.startsWith("#description")) + ret->setDescription(Util::qstringToString(line.right(line.length() - 13))); + else if (line.startsWith("#note") && ret->hasPositions()) + ret->currentPosition().setExplanatoryNote(Util::qstringToString(line.right(line.length() - 6))); + else if (line.startsWith("#rack") && ret->hasPositions()) + { + QString firstChunk = strings.front(); + int id = firstChunk.right(1).toInt(); + + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": no rack in #rack" << endl; + return ret; + } + + const QString rackString = strings.front(); + const Quackle::Rack rack(Util::encode(rackString)); + + ret->currentPosition().setPlayerRack(/* zero index */ id - 1, rack); + } + else if (line.startsWith("#incomplete")) + { + strings.pop_front(); + + const QString rackString = strings.isEmpty()? QString() : strings.front(); + incompleteRack = Util::encode(rackString); + hasIncompleteRack = true; + } + } + else if (line.startsWith(">")) + { + if (!gameStarted) + { + ret->setPlayers(players); + gameStarted = true; + } + + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": incomplete move" << endl; + return ret; + } + + const QString rackString = strings.front(); + strings.pop_front(); + + // end of game unused tiles bonus + if (rackString.startsWith("(") && rackString.endsWith(")")) + { + // end the game + if (ret->hasPositions()) + ret->commitCandidate(canMaintainCrosses); + else + ret->addPosition(); + continue; + } + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": incomplete move" << endl; + return ret; + } + + const Quackle::Rack rack(Util::encode(rackString)); + + Quackle::Move move = Quackle::Move::createNonmove(); + + const QString firstMoveBite = strings.front(); + if (firstMoveBite.startsWith("--")) + { + Quackle::Move lastMoveMade = ret->currentPosition().moveMade(); + lastMoveMade.setIsChallengedPhoney(true); + ret->currentPosition().setMoveMade(lastMoveMade); + } + else if (firstMoveBite.startsWith("-")) + { + const QString exchangedLetters = firstMoveBite.right(firstMoveBite.length() - 1); + if (exchangedLetters.isEmpty()) + move = Quackle::Move::createPassMove(); + else + move = Quackle::Move::createExchangeMove(Util::encode(exchangedLetters)); + } + else if (firstMoveBite.startsWith("(time)")) + { + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": incomplete move" << endl; + return ret; + } + } + else if (firstMoveBite.startsWith("(challenge)")) + { + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": incomplete move" << endl; + return ret; + } + + Quackle::Move move = ret->currentPosition().moveMade(); + move.setScoreAddition(readSignedInt(strings.front())); + ret->currentPosition().setMoveMade(move); + } + else + { + const QString positionString = firstMoveBite; + + strings.pop_front(); + + if (strings.isEmpty()) + { + UVcerr << "GCG error reading " << Util::qstringToString(line) << ": incomplete move" << endl; + return ret; + } + + const QString placeTiles = strings.front(); + strings.pop_front(); + + // if score is negative, it is rescored later to the proper score + int score = -1; + + if (!strings.isEmpty()) + score = readSignedInt(strings.front());; + + move = Quackle::Move::createPlaceMove(Util::qstringToString(positionString), Util::encode(placeTiles)); + move.score = score; + } + + if (move.isAMove()) + { + if (ret->hasPositions()) + ret->commitCandidate(canMaintainCrosses); + else + ret->addPosition(); + + ret->currentPosition().setCurrentPlayerRack(rack); + ret->currentPosition().ensureMovePrettiness(move); + ret->currentPosition().ensureMoveTilesDoNotIncludePlayThru(move); + + int correctScore = ret->currentPosition().calculateScore(move); + if (move.score < 0) + { + move.score = correctScore; + } + else + { + if (correctScore != move.score) + { + move.setScoreAddition(move.score - correctScore); + move.score = correctScore; + } + } + + ret->currentPosition().setMoveMade(move); + } + } + } + + if (!gameStarted || !ret->currentPosition().gameOver()) + { + if (ret->hasPositions()) + ret->commitCandidate(canMaintainCrosses); + else + ret->addPosition(); + + if (hasIncompleteRack) + { + ret->currentPosition().setCurrentPlayerRack(incompleteRack); + } + } + + return ret; +} + +int GCGIO::readSignedInt(const QString &intString) const +{ + QString bonus = intString; + int sign = 1; + + if (bonus.startsWith("+")) + { + bonus = bonus.right(bonus.length() - 1); + } + else if (bonus.startsWith("-")) + { + sign = -1; + bonus = bonus.right(bonus.length() - 1); + } + + return sign * bonus.toInt(); +} + +bool GCGIO::canRead(QTextStream &stream) const +{ + QString firstChunk; + stream >> firstChunk; + if (firstChunk.startsWith("#")) + return true; + + return false; +} + +void GCGIO::write(const Quackle::Game &game, QTextStream &stream) +{ + Quackle::PlayerList players = game.players(); + for (Quackle::PlayerList::iterator it = players.begin(); it != players.end(); ++it) + { + stream << "#player" << (*it).id() + 1 << " " << Util::uvStringToQString((*it).abbreviatedName()) << " " << Util::uvStringToQString((*it).name()) << endl; + } + + if (!game.title().empty()) + stream << "#title " << Util::uvStringToQString(game.title()) << endl; + + if (!game.description().empty()) + stream << "#description " << Util::uvStringToQString(game.description()) << endl; + + const Quackle::PositionList::const_iterator end(game.history().end()); + for (Quackle::PositionList::const_iterator it = game.history().begin(); it != end; ++it) + { + Quackle::Move move = (*it).committedMove(); + move.setPrettyTiles((*it).board().prettyTilesOfMove(move, /* don't mark playthru */ false)); + + if (move.isAMove()) + { + int outputScore = move.score; + int outputScoreAddition = move.scoreAddition(); + + // special case != 5 score additions; + // GCG has no way to specify them, so we roll the score addition + // into the regular score silently + if (outputScoreAddition != 5) + { + outputScore = move.score + move.scoreAddition(); + outputScoreAddition = 0; + } + + stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << Util::letterStringToQString((*it).currentPlayer().rack().alphaTiles()) << " " << Util::uvStringToQString(move.toString()) << " +" << outputScore << " " << outputScore + (*it).currentPlayer().score() << endl; + + if (move.isChallengedPhoney()) + { + stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << Util::letterStringToQString((*it).currentPlayer().rack().alphaTiles()) << " -- -" << outputScore << " " << move.effectiveScore() + (*it).currentPlayer().score() << endl; + } + + if (outputScoreAddition != 0) + { + QString nextRack = "UNKNOWN"; + bool foundOne = false; + for (Quackle::PositionList::const_iterator secondIterator = it; secondIterator != end; ++secondIterator) + { + if ((*secondIterator).currentPlayer().id() == (*it).currentPlayer().id()) + { + if (foundOne) + { + nextRack = Util::letterStringToQString((*secondIterator).currentPlayer().rack().alphaTiles()); + break; + } + else + { + foundOne = true; + continue; + } + } + } + + stream << ">" << Util::uvStringToQString((*it).currentPlayer().abbreviatedName()) << ": " << nextRack << " (challenge) " << ((outputScoreAddition > 0)? "+" : "") << outputScoreAddition << " " << (outputScoreAddition + outputScore + (*it).currentPlayer().score()) << endl; + } + } + + if (!(*it).explanatoryNote().empty()) + stream << "#note " << Util::uvStringToQString((*it).explanatoryNote()) << endl; + } + + const Quackle::GamePosition &lastPosition = game.history().lastPosition(); + + if (!lastPosition.gameOver()) + { + stream << "#rack" << lastPosition.currentPlayer().id() + 1 << " " << Util::letterStringToQString(lastPosition.currentPlayer().rack().alphaTiles()) << endl; + } +} + +QString GCGIO::filter() const +{ + return QString("*.gcg"); +} + diff --git a/quackleio/gcgio.h b/quackleio/gcgio.h new file mode 100644 index 0000000..67405ab --- /dev/null +++ b/quackleio/gcgio.h @@ -0,0 +1,47 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_GCGIO_H +#define QUACKLE_GCGIO_H + +#include "logania.h" + +namespace QuackleIO +{ + +class GCGIO : public Logania +{ +public: + GCGIO(); + ~GCGIO() {}; + + virtual Quackle::Game *read(QTextStream &stream, int flags); + virtual bool canRead(QTextStream &stream) const; + virtual void write(const Quackle::Game &game, QTextStream &stream); + virtual QString filter() const; + +private: + int readSignedInt(const QString &intString) const; +}; + +} + +#endif + diff --git a/quackleio/iotest/.gitignore b/quackleio/iotest/.gitignore new file mode 100644 index 0000000..f878785 --- /dev/null +++ b/quackleio/iotest/.gitignore @@ -0,0 +1,6 @@ +Makefile +obj +Makefile.Debug +Makefile.Release +debug +release diff --git a/quackleio/iotest/capp.gcg b/quackleio/iotest/capp.gcg new file mode 100644 index 0000000..7c1a05c --- /dev/null +++ b/quackleio/iotest/capp.gcg @@ -0,0 +1,34 @@ +#player1 Brian Brian Cappelletto +#player2 Pakorn Pakorn Nemitrmansuk +#title WSC 2001 Round 20: Brian Cappelletto vs. Pakorn Nemitrmansuk +#description WSC 2001 Round 20: Brian Cappelletto vs. Pakorn Nemitrmansuk at Table 1 +#style wsc2001 +#incomplete +>Brian: CNNTU?? 8b NoCTUrN +70 70 +>Pakorn: AFFIABE e8 TAFFIA +24 24 +>Brian: EEETVVY 12d VIVE +20 90 +>Pakorn: BEWWLYB 11g WEBBY +35 59 +>Brian: AEEETTY f8 UEY +32 122 +>Pakorn: LWGRAKE h10 WERGEL +45 104 +>Pakorn: LWGRAKE -- +0 59 +>Brian: AAEERTT b7 ANTEATER +70 192 +>Pakorn: LWGRAKE 7a KAW +22 81 +>Brian: NOPUUUX 13g XU +38 230 +>Pakorn: LEGGARI h2 REGALING +62 143 +>Brian: NOOPRRU a12 UPON +30 260 +>Pakorn: PHOLASR c13 HAP +35 178 +>Brian: EEMORSU 4h GRUESOME +74 334 +>Brian: ACDEJOQ (challenge) +5 339 +>Pakorn: DILTORS j1 DILUTORS +63 241 +>Brian: ACDEJOQ o1 JADE +36 375 +>Pakorn: ZSIDTLO m3 ZOOID +34 275 +>Brian: DEECOOQ n2 DEMO +35 410 +>Pakorn: NIRISTL 14h NITRILS +70 345 +>Brian: ACEHOOQ g1 OOH +21 431 +>Pakorn: SMORING 6m ISM +17 362 +>Brian: AACEENQ o6 MAC +21 452 +>Pakorn: LIGROIN h13 UNI +9 371 +>Brian: AEEINQT n11 QATS +13 465 +>Pakorn: LIGRONE o10 LIG +22 393 +>Brian: EEIN l7 EINE +8 473 +>Brian: (ENOR) +8 481 diff --git a/quackleio/iotest/iotest.cpp b/quackleio/iotest/iotest.cpp new file mode 100644 index 0000000..87d167d --- /dev/null +++ b/quackleio/iotest/iotest.cpp @@ -0,0 +1,78 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QtCore> + +#include "datamanager.h" +#include "game.h" +#include "gcgio.h" +#include "trademarkedboards.h" + +void testGCGIO(); + +int main() +{ + Quackle::DataManager dataManager; + dataManager.setBoardParameters(new ScrabbleBoard()); + + testGCGIO(); + return 0; +} + +void testGCGIO() +{ + QuackleIO::GCGIO io; + QFile file("capp.gcg"); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + cerr << "Could not open gcg" << endl; + return; + } + + QTextStream in(&file); + + Quackle::Game *game = io.read(in, QuackleIO::Logania::BasicLoad); + UVcout << game->history() << endl; + + UVcout << "Final scores: " << endl; + Quackle::PlayerList players = game->currentPosition().endgameAdjustedScores(); + const Quackle::PlayerList::const_iterator end(players.end()); + for (Quackle::PlayerList::const_iterator it = players.begin(); it != end; ++it) + UVcout << *it << endl; + + file.close(); + + QFile outFile("my-capp.gcg"); + + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + cerr << "Could not open gcg output file" << endl; + return; + } + + QTextStream out(&outFile); + io.write(*game, out); + + outFile.close(); + + delete game; +} + diff --git a/quackleio/iotest/iotest.pro b/quackleio/iotest/iotest.pro new file mode 100644 index 0000000..8550691 --- /dev/null +++ b/quackleio/iotest/iotest.pro @@ -0,0 +1,29 @@ +TEMPLATE = app +DEPENDPATH += . +INCLUDEPATH += . .. ../.. + +# enable/disable debug symbols +#CONFIG += debug + +debug { + OBJECTS_DIR = obj/debug + win32 { LIBS += -L../debug -L../../debug } +} + +release { + OBJECTS_DIR = obj/release + win32 { LIBS += -L../release -L../../release } +} + +LIBS += -L.. -L../.. -lquackle -lquackleio + +# Input +HEADERS += trademarkedboards.h +SOURCES += iotest.cpp trademarkedboards.cpp + +win32:!win32-g++ { + QMAKE_CFLAGS_DEBUG ~= s/-MDd/-MTd/ + QMAKE_CXXFLAGS_DEBUG ~= s/-MDd/-MTd/ + QMAKE_CFLAGS_RELEASE ~= s/-MD/-MT/ + QMAKE_CXXFLAGS_RELEASE ~= s/-MD/-MT/ +} diff --git a/quackleio/iotest/trademarkedboards.cpp b/quackleio/iotest/trademarkedboards.cpp new file mode 100644 index 0000000..b9eb37c --- /dev/null +++ b/quackleio/iotest/trademarkedboards.cpp @@ -0,0 +1,148 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "trademarkedboards.h" + +ScrabbleBoard::ScrabbleBoard() +{ + m_name = MARK_UV("Scrabble Board"); + + const int letterm[15][15] = + { + // A B C D E F G H I J K L M N O + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1}, + {1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1}, + {1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1} + }; + + const int wordm[15][15] = + { + // A B C D E F G H I J K L M N O + {3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3} + }; + + for (int i = 0; i < 15; ++i) + { + for (int j = 0; j < 15; ++j) + { + m_letterMultipliers[i][j] = letterm[i][j]; + m_wordMultipliers[i][j] = wordm[i][j]; + } + } +} + +////// + +SuperScrabbleBoard::SuperScrabbleBoard() +{ + m_height = 21; + m_width = 21; + + m_startRow = 10; + m_startColumn = 10; + + m_name = MARK_UV("Super Scrabble Board"); + + const int letterm[21][21] = + { + // A B C D E F G H I J K L M N O P Q R S T U + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2}, + {1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1}, + {1, 1, 4, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 4, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 4, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 4, 1, 1}, + {1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1}, + {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1} + }; + const int wordm[21][21] = + { + // A B C D E F G H I J K L M N O P Q R S T U + {4, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 4}, + {1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1}, + {3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3}, + {1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1}, + {4, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 4}, + }; + + for (int i = 0; i < 21; ++i) + { + for (int j = 0; j < 21; ++j) + { + m_letterMultipliers[i][j] = letterm[i][j]; + m_wordMultipliers[i][j] = wordm[i][j]; + } + } +} diff --git a/quackleio/iotest/trademarkedboards.h b/quackleio/iotest/trademarkedboards.h new file mode 100644 index 0000000..f596c8f --- /dev/null +++ b/quackleio/iotest/trademarkedboards.h @@ -0,0 +1,40 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_TRADEMARKEDBOARDS_H +#define QUACKLE_TRADEMARKEDBOARDS_H + +#include "boardparameters.h" + +// Name: Scrabble Board +class ScrabbleBoard : public Quackle::BoardParameters +{ +public: + ScrabbleBoard(); +}; + +// Name: Super Scrabble Board +class SuperScrabbleBoard : public Quackle::BoardParameters +{ +public: + SuperScrabbleBoard(); +}; + +#endif diff --git a/quackleio/logania.h b/quackleio/logania.h new file mode 100644 index 0000000..8273603 --- /dev/null +++ b/quackleio/logania.h @@ -0,0 +1,61 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_LOGANIA_H +#define QUACKLE_LOGANIA_H + +#include <QString> + +class QTextStream; + +namespace Quackle +{ + class Game; +} + +namespace QuackleIO +{ + +// Abstract interfaces for game log input/output + +class Logania +{ +public: + virtual ~Logania() {}; + + // if MaintainBoardPreparation is not set, boards will not be prepared + // for analysis and you'll have to do this manually. If you set this flag + // DataManager and a generator need to be set up. + enum ReadFlags { BasicLoad = 0x0000, MaintainBoardPreparation = 0x0001 }; + + // allocates a new Game, fills it up, and returns it. + // Returned game is either 0, or a game with at least one position. + // Boards of game are ready for analysis if there is a DataManager set + // up; otherwise they won't have their crosses prepared + virtual Quackle::Game *read(QTextStream &stream, int flags) = 0; + + virtual bool canRead(QTextStream &stream) const = 0; + virtual void write(const Quackle::Game &game, QTextStream &stream) = 0; + virtual QString filter() const = 0; +}; + +} + +#endif diff --git a/quackleio/quackleio.pro b/quackleio/quackleio.pro new file mode 100644 index 0000000..e883fb6 --- /dev/null +++ b/quackleio/quackleio.pro @@ -0,0 +1,34 @@ +TEMPLATE = lib +INCLUDEPATH += . .. +DEPENDPATH += . .. +VERSION = 0.9 +QT -= gui +debug { + OBJECTS_DIR = obj/debug + win32 { LIBS += -L../debug } +} + +release { + OBJECTS_DIR = obj/release + win32 { LIBS += -L../release } +} + +MOC_DIR = moc + +# enable/disable debug symbols +#CONFIG += debug staticlib +CONFIG += release staticlib +CONFIG -= x11 + +LIBS += -L.. -lquackle + +# Input +HEADERS += gcgio.h logania.h queenie.h streamingreporter.h flexiblealphabet.h util.h froggetopt.h dict.h dictfactory.h dictimplementation.h +SOURCES += gcgio.cpp queenie.cpp streamingreporter.cpp flexiblealphabet.cpp util.cpp froggetopt.cpp dict.cpp dictfactory.cpp dictimplementation.cpp + +win32:!win32-g++ { + QMAKE_CFLAGS_DEBUG ~= s/-MDd/-MTd/ + QMAKE_CXXFLAGS_DEBUG ~= s/-MDd/-MTd/ + QMAKE_CFLAGS_RELEASE ~= s/-MD/-MT/ + QMAKE_CXXFLAGS_RELEASE ~= s/-MD/-MT/ +} diff --git a/quackleio/queenie.cpp b/quackleio/queenie.cpp new file mode 100644 index 0000000..7b02c7e --- /dev/null +++ b/quackleio/queenie.cpp @@ -0,0 +1,87 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <iostream> + +#include <QtCore> + +#include "gcgio.h" +#include "queenie.h" + +using namespace QuackleIO; + +Queenie *Queenie::m_self = 0; + +Queenie *Queenie::self() +{ + if (m_self == 0) + m_self = new Queenie(); + + return m_self; +} + +void Queenie::cleanUp() +{ + delete m_self; + m_self = 0; +} + +Queenie::Queenie() +{ + m_loganias.push_back(new GCGIO); + + for (QList<Logania *>::const_iterator it = m_loganias.begin(); it != m_loganias.end(); ++it) + m_filters.push_back((*it)->filter()); +} + +Queenie::~Queenie() +{ + while (!m_loganias.isEmpty()) + delete m_loganias.takeFirst(); +} + +Logania *Queenie::loganiaForFile(const QString &filename) +{ + for (QList<Logania *>::const_iterator it = m_loganias.begin(); it != m_loganias.end(); ++it) + { + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return 0; + + QTextStream in(&file); + + if ((*it)->canRead(in)) + return (*it); + } + + return 0; +} + +const QStringList &Queenie::filters() const +{ + return m_filters; +} + +Logania *Queenie::defaultLogania() +{ + return m_loganias.front(); +} + diff --git a/quackleio/queenie.h b/quackleio/queenie.h new file mode 100644 index 0000000..a1b0efb --- /dev/null +++ b/quackleio/queenie.h @@ -0,0 +1,62 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_QUEENIE_H +#define QUACKLE_QUEENIE_H + +#include <QtCore/QStringList> + +namespace QuackleIO +{ + +class Logania; + +class Queenie +{ +public: + ~Queenie(); + + static Queenie *self(); + + // deletes self and resets + static void cleanUp(); + + // returns 0 if no handler found for the file type + Logania *loganiaForFile(const QString &filename); + + const QStringList &filters() const; + + Logania *defaultLogania(); + +protected: + Queenie(); + Queenie(const Queenie&); + Queenie& operator=(const Queenie &); + + QList<Logania *> m_loganias; + QStringList m_filters; + +private: + static Queenie *m_self; +}; + +} + +#endif diff --git a/quackleio/streamingreporter.cpp b/quackleio/streamingreporter.cpp new file mode 100644 index 0000000..eac7d6f --- /dev/null +++ b/quackleio/streamingreporter.cpp @@ -0,0 +1,53 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QtCore> + +#include "game.h" +#include "streamingreporter.h" +#include "util.h" + +using namespace QuackleIO; + +StreamingReporter::StreamingReporter() +{ +} + +void StreamingReporter::reportGame(const Quackle::Game &game, Quackle::ComputerPlayer *computerPlayer, QTextStream &stream) +{ + UVString header; + Quackle::Reporter::reportHeader(game, &header); + stream << Util::uvStringToQString(header); + + const Quackle::PositionList::const_iterator end(game.history().end()); + for (Quackle::PositionList::const_iterator it = game.history().begin(); it != end; ++it) + { + UVString subreport; + Quackle::Reporter::reportPosition((*it), computerPlayer, &subreport); + + // endl flushes the stream, which we want + stream << Util::uvStringToQString(subreport) << endl; + } + + UVString stats; + Quackle::Reporter::reportGameStatistics(game, &stats); + stream << Util::uvStringToQString(stats); +} + diff --git a/quackleio/streamingreporter.h b/quackleio/streamingreporter.h new file mode 100644 index 0000000..e4d1f37 --- /dev/null +++ b/quackleio/streamingreporter.h @@ -0,0 +1,50 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_STREAMINGREPORTER_H +#define QUACKLE_STREAMINGREPORTER_H + +#include "reporter.h" + +class QTextStream; + +namespace Quackle +{ + class Game; +} + +namespace QuackleIO +{ + +// Full-game reporter using Qt streaming. + +class StreamingReporter +{ +public: + StreamingReporter(); + + // duplicates functionality of Quackle::Reporter::reportGame but suitable + // for streaming so everything doesn't appear in the file at once. + static void reportGame(const Quackle::Game &game, Quackle::ComputerPlayer *computerPlayer, QTextStream &stream); +}; + +} + +#endif diff --git a/quackleio/util.cpp b/quackleio/util.cpp new file mode 100644 index 0000000..e3f947d --- /dev/null +++ b/quackleio/util.cpp @@ -0,0 +1,212 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QtCore> + +#include <move.h> +#include <rack.h> + +#include "dict.h" +#include "dictfactory.h" +#include "util.h" + +using namespace QuackleIO; + +UtilSettings *UtilSettings::m_self = 0; +UtilSettings *UtilSettings::self() +{ + return m_self; +} + +UtilSettings::UtilSettings() + : octothorpBritish(true), vowelFirst(false) +{ + m_self = this; +} + +QString Util::moveToDetailedString(const Quackle::Move &move) +{ + QString prettyTiles = letterStringToQString(move.prettyTiles()); + + QString ret; + + switch (move.action) + { + case Quackle::Move::Pass: + ret = QObject::tr("Pass"); + break; + + case Quackle::Move::Exchange: + ret = QObject::tr("Exch. %1").arg(prettyTiles); + break; + + case Quackle::Move::UnusedTilesBonus: + ret = QObject::tr("2*(%1)").arg(letterStringToQString(Util::alphagram(move.usedTiles()))); + break; + + case Quackle::Move::TimePenalty: + ret = QObject::tr("%1 point time penalty").arg(move.effectiveScore()); + break; + + case Quackle::Move::Nonmove: + ret = QObject::tr("None"); + break; + + case Quackle::Move::Place: + ret = uvStringToQString(move.positionString()) + " "; + ret += prettyTiles; + + if (UtilSettings::self()->octothorpBritish) + ret += symbolsFor(move.wordTiles()); + + if (move.isChallengedPhoney()) + ret = QObject::tr("%1 [Challenged Off]").arg(ret); + + break; + } + + if (move.scoreAddition() != 0) + ret = QString("%1 [and %2%3]").arg(ret).arg(move.scoreAddition() > 0? QObject::tr("+") : QString()).arg(move.scoreAddition()); + + return ret; +} + +QString Util::moveToSensitiveString(const Quackle::Move &move) +{ + QString ret; + + if (move.action == Quackle::Move::Exchange) + ret = QObject::tr("Exch. %1").arg(move.prettyTiles().length()); + else + ret = moveToDetailedString(move); + + return ret; +} + +UVString Util::qstringToString(const QString &qstring) +{ +#if QUACKLE_USE_WCHAR_FOR_USER_VISIBLE + return qstring.toStdWString(); +#else + return string(qstring.toUtf8().data()); +#endif +} + +QString Util::uvStringToQString(const UVString &uvString) +{ +#if QUACKLE_USE_WCHAR_FOR_USER_VISIBLE + return QString::fromStdWString(uvString); +#else + return QString::fromUtf8(uvString.c_str()); +#endif +} + +Quackle::LetterString Util::encode(const QString &qstring) +{ + return QUACKLE_ALPHABET_PARAMETERS->encode(qstringToString(qstring)); +} + +Quackle::LetterString Util::nonBlankEncode(const QString &qstring) +{ + return QUACKLE_ALPHABET_PARAMETERS->clearBlankness(encode(qstring)); +} + +QString Util::letterStringToQString(const Quackle::LetterString &letterString) +{ + return uvStringToQString(QUACKLE_ALPHABET_PARAMETERS->userVisible(letterString)); +} + +QString Util::letterToQString(const Quackle::Letter &letter) +{ + return uvStringToQString(QUACKLE_ALPHABET_PARAMETERS->userVisible(letter)); +} + +string Util::qstringToStdString(const QString &qstring) +{ + return string(qstring.toAscii()); +} + +QString Util::stdStringToQString(const string &stdString) +{ + return QString::fromAscii(stdString.c_str()); +} + +Quackle::LetterString Util::alphagram(const Quackle::LetterString &word) +{ + return Quackle::String::alphabetize(word); +} + +QString Util::alphagram(const QString &word) +{ + return letterStringToQString(Quackle::String::alphabetize(encode(word))); +} + +Quackle::LetterString Util::arrangeLettersForUser(const Quackle::LetterString &word) +{ + Quackle::LetterString alphabetized = Quackle::String::alphabetize(word); + + Quackle::LetterString vowels; + Quackle::LetterString nonvowels; + Quackle::LetterString blanks; + for (Quackle::LetterString::iterator it = alphabetized.begin(); it != alphabetized.end(); ++it) + { + if ((*it) == QUACKLE_BLANK_MARK) + blanks.push_back(*it); + else if (UtilSettings::self()->vowelFirst && QUACKLE_ALPHABET_PARAMETERS->isVowel(*it)) + vowels.push_back(*it); + else + nonvowels.push_back(*it); + } + + return vowels + nonvowels + blanks; +} + +Quackle::LetterString Util::arrangeLettersForUser(const Quackle::Rack &rack) +{ + return arrangeLettersForUser(rack.tiles()); +} + +Quackle::Rack Util::makeRack(const QString &letters) +{ + return Quackle::Rack(QuackleIO::Util::encode(letters.toUpper().replace('.', "?"))); +} + +QString Util::arrangeLettersForUser(const QString &word) +{ + return letterStringToQString(arrangeLettersForUser(encode(word))); +} + +QString Util::sanitizeUserVisibleLetterString(const QString &pipedString) +{ + QString pipedStringCopy = pipedString; + pipedStringCopy.replace("|", " "); + return pipedStringCopy.simplified(); +} + +QString Util::symbolsFor(const Quackle::LetterString &word) +{ + if (DictFactory::querier()->isBritish(word)) + { + return "#"; + } + + return ""; +} + diff --git a/quackleio/util.h b/quackleio/util.h new file mode 100644 index 0000000..4838d1e --- /dev/null +++ b/quackleio/util.h @@ -0,0 +1,96 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKER_UTIL_H +#define QUACKER_UTIL_H + +#include <QString> + +#include <alphabetparameters.h> +#include <datamanager.h> +#include <uv.h> + +namespace Quackle +{ + class Move; + class Rack; +} + +namespace QuackleIO +{ + +class UtilSettings +{ +public: + UtilSettings(); + static UtilSettings *self(); + + bool octothorpBritish; + bool vowelFirst; + +private: + static UtilSettings *m_self; +}; + +class Util +{ +public: + static UVString qstringToString(const QString &qstring); + static Quackle::LetterString encode(const QString &qstring); + + // encode as above but clear blankess of all letters + static Quackle::LetterString nonBlankEncode(const QString &qstring); + + static QString uvStringToQString(const UVString &stdWString); + static QString letterStringToQString(const Quackle::LetterString &letterString); + static QString letterToQString(const Quackle::Letter &letter); + + // non-ui strings + static string qstringToStdString(const QString &qstring); + static QString stdStringToQString(const string &stdString); + + static QString moveToDetailedString(const Quackle::Move &move); + static QString moveToSensitiveString(const Quackle::Move &move); + + // make alphagram + static Quackle::LetterString alphagram(const Quackle::LetterString &word); + static QString alphagram(const QString &word); + + // make pattern of letters user wants based on settings + static Quackle::LetterString arrangeLettersForUser(const Quackle::LetterString &word); + static Quackle::LetterString arrangeLettersForUser(const Quackle::Rack &rack); + static QString arrangeLettersForUser(const QString &word); + + // make rack, converting letters to uppercase and changing "." into blank + static Quackle::Rack makeRack(const QString &letters); + + // Some alphabets enclose some of their letters in pipes, like |TT|, if + // ambiguity could arise without letters being explicitly delimited. + // This method returns a string that has no pipes and each letter separated + // with a space. + static QString sanitizeUserVisibleLetterString(const QString &pipedString); + + // Returns a string of symbols, like an octothorp for a british word. + static QString symbolsFor(const Quackle::LetterString &word); +}; + +} + +#endif |