Commit c9fd0446 authored by Johannes Bergmeier's avatar Johannes Bergmeier

- Upgraded KSudoku's core to a new solver engine

- NOTE This engine seams to have some performance problems
- NOTE This is a huge change developed in a local git repository
== Important Changes according to git log ==
- Changed generator to to select values evenly from the possible range
- Replaced old generator with a simpler new one that tries to insert values in an empty game until it has exactly one solution
- Almost completely removed everything left from the old solver
- Added converter for problems to and from puzzles
- Changed SKSolver::remove_numbers() to use the new solver and to be more readable
- SKSolver now always require a valid SKGraph-instance in the constructor
- Removed from SKSolver all duplicates of values from SKGraph
- Removed dependeny to SKBase from SKGraph
- Removed methods from SKSolver that were not longer used (containing about 500 lines of code/comments)
- Removed not longer used class Solver, SolverState, PuzzleFactory and GroupLookup
- Moved ruleset initialization into the graphs
- Removed code for long outdated support of block/clique-borders
- Removed fragments from long outdated support for numbers and letters
- Small improvement in View2d to prevent crashes by having the upper left cell empty
- Added support for new solver to KSudoku itself

svn path=/trunk/KDE/kdegames/ksudoku/; revision=1054362
parent 11c67a32
......@@ -18,7 +18,10 @@ if(OPENGL_SUPPORT)
add_definitions(-DOPENGL_SUPPORT)
endif(OPENGL_SUPPORT)
add_subdirectory( src )
add_subdirectory( engine )
#message(STATUS "ksudoku: DEBUG: ${DEBUG}")
#message(STATUS "ksudoku: install prefix: ${CMAKE_INSTALL_PREFIX}")
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
)
set(liblogine_SRCS
choiceitem.cpp
constraint.cpp
constrainthelperstorage.cpp
eliminationstorage.cpp
item.cpp
markerstorage.cpp
mocs.cpp
problem.cpp
ruleset.cpp
solver.cpp
statearray.cpp
storage.cpp
sudokuconstraint.cpp
)
qt4_automoc(${liblogine_SRCS})
add_library(logine ${liblogine_SRCS})
target_link_libraries(logine
${QT_LIBRARIES}
)
add_subdirectory(script)
\ No newline at end of file
#include "choiceitem.h"
#include <QtCore/QVector>
#include <QtCore/QBitArray>
#include "statearray.h"
#include "ruleset.h" // TODO rm
#include "problem.h"
#include <QtCore/QtDebug>
ChoiceItem::ChoiceItem() {
m_minValue = 0;
m_maxValue = 0;
m_eliminator.setItem(this);
m_eliminator.setPossibilities(1);
m_markers.setSize(1);
}
ChoiceItem::ChoiceItem(int minValue, int maxValue) {
m_minValue = minValue;
m_maxValue = maxValue;
m_eliminator.setItem(this);
m_eliminator.setPossibilities(maxValue - minValue + 1);
m_markers.setSize(maxValue - minValue + 1);
}
Item *ChoiceItem::construct(const QVariantList &args) {
ChoiceItem *item = 0;
int min = 0, max = 0;
if(args.count() >= 2) {
min = args[0].toInt();
max = args[1].toInt();
if(max >= min) {
item = new ChoiceItem(min, max);
}
} else if(args.count() == 1) {
max = args[0].toInt();
if(max >= 0) {
item = new ChoiceItem(0, max);
}
}
return item;
}
void ChoiceItem::setMinValue(int value) {
Q_ASSERT(!isInitialized());
m_minValue = value;
if(m_maxValue < value) m_maxValue = value;
m_eliminator.setPossibilities(m_maxValue - m_minValue + 1);
m_markers.setSize(m_maxValue - m_minValue + 1);
}
void ChoiceItem::setMaxValue(int value) {
Q_ASSERT(!isInitialized());
m_maxValue = value;
if(m_minValue > value) m_minValue = value;
m_eliminator.setPossibilities(m_maxValue - m_minValue + 1);
m_markers.setSize(m_maxValue - m_minValue + 1);
}
int ChoiceItem::value(const Problem *problem) const {
return m_value.value(problem);
}
void ChoiceItem::setValue(Problem *problem, int value) const {
for(int i = m_minValue; i <= m_maxValue; ++i) {
setMarker(problem, i, value == i);
}
m_value.setValue(problem, value);
changed(problem);
}
bool ChoiceItem::marker(const Problem *problem, int value) const {
Q_ASSERT(value >= m_minValue && value <= m_maxValue);
return m_markers.marker(problem, value - m_minValue);
}
void ChoiceItem::setMarker(Problem *problem, int value, bool state) const {
Q_ASSERT(value >= m_minValue && value <= m_maxValue);
// TODO make sure allready set values will be removed
int index = value - m_minValue;
if(m_markers.marker(problem, index) != state) {
m_markers.setMarker(problem, index, state);
if(state) {
m_eliminator.incPossibilitiesLeft(problem);
} else {
m_eliminator.decPossibilitiesLeft(problem);
}
changed(problem);
}
}
void ChoiceItem::init(Ruleset *rules) {
Item::init(rules);
Constraint *constraint = new LastValueInNodeConstraint(this);
// TODO manage constraint localy
rules->addItem(constraint);
constraint->init(rules);
m_eliminator.setup(rules);
m_value.setup(rules);
m_markers.setup(rules);
}
QDebug ChoiceItem::debug(QDebug dbg, Problem *problem)
{
int value = m_value.value(problem);
dbg.nospace();
if(value) dbg << value; else dbg << '.';
dbg << '(';
for(int i = m_minValue; i <= m_maxValue; ++i) {
if(m_markers.marker(problem, i - m_minValue))
dbg << i;
else
dbg << ' ';
}
dbg << ')';
return dbg.space();
}
Item *ChoiceItem::item() {
return this;
}
int ChoiceItem::possibilities(Problem *problem) const {
return m_eliminator.possibilitiesLeft(problem);
}
int ChoiceItem::nextPossibility(Problem *problem, int current) const {
for(int i = ((current<m_minValue)?m_minValue:current+1); i <= m_maxValue; ++i) {
if(marker(problem, i)) {
return i;
}
}
return -1;
}
void ChoiceItem::applyPossibility(Problem *problem, int variant) {
setValue(problem, variant);
}
Storage::Instance *ChoiceStorage::Instance::clone() const {
Instance *storage = new Instance();
storage->m_values = m_values;
return storage;
}
void ChoiceStorage::Instance::clone(const Storage::Instance *other) {
const Instance *inst = static_cast<const Instance*>(other);
m_values = inst->m_values;
// m_markerCount = inst->m_markerCount;
}
class ChoiceStoragePrivate {
Q_DECLARE_PUBLIC(ChoiceStorage);
friend class ChoiceStorage::Entry;
public:
ChoiceStoragePrivate(ChoiceStorage *p);
protected:
ChoiceStorage *q_ptr;
private:
int itemCount;
};
ChoiceStorage::Entry::Entry() {
m_valueIndex = -1;
m_storage = 0;
}
void ChoiceStorage::Entry::setup(Ruleset *rules) {
Q_ASSERT(!m_storage);
ChoiceStorage *choiceStorage = storage<ChoiceStorage>(rules);
ChoiceStoragePrivate *d = static_cast<ChoiceStoragePrivate*>(choiceStorage->d_ptr);
m_valueIndex = d->itemCount++;
m_storage = choiceStorage;
}
int ChoiceStorage::Entry::value(const Problem *problem) const {
Q_ASSERT(m_storage);
Instance *instance = static_cast<Instance*>(problem->storage(m_storage));
return instance->m_values[m_valueIndex];
}
void ChoiceStorage::Entry::setValue(Problem *problem, int value) const {
Q_ASSERT(m_storage);
Instance *instance = static_cast<Instance*>(problem->storage(m_storage));
instance->m_values[m_valueIndex] = value;
}
ChoiceStoragePrivate::ChoiceStoragePrivate(ChoiceStorage *q) {
q_ptr = q;
itemCount = 0;
}
ChoiceStorage::ChoiceStorage() {
d_ptr = new ChoiceStoragePrivate(this);
}
ChoiceStorage::~ChoiceStorage() {
delete d_ptr;
}
Storage::Instance *ChoiceStorage::create() const {
Q_D(const ChoiceStorage);
Instance *instance = new Instance();
instance->m_values.fill(0, d->itemCount);
return instance;
}
bool LastValueInNodeHelper::apply(Problem* problem) const {
// Test whether value allready exists
if(m_item->value(problem)) return true;
int c = 0;
int val = 0;
for(int i = m_item->minValue(); i <= m_item->maxValue(); ++i) {
if(m_item->marker(problem, i)) { ++c; val = i; }
}
if(c == 1) {
m_item->setValue(problem, val);
} else if(c == 0) {
return false;
}
return true;
}
#ifndef _VALUEITEM_H_
#define _VALUEITEM_H_
#include <QtCore/QtGlobal>
#include <QtCore/QVariant>
#include "item.h"
#include "storage.h"
// TODO rm
#include <QBitArray>
#include "statearray.h"
#include "constraint.h"
#include "eliminationstorage.h"
#include "markerstorage.h"
class ChoiceItem;
class ChoiceStoragePrivate;
class ChoiceStorage : public Storage {
class Instance;
public:
class Entry {
// TODO this friend declaration is only temporary
friend class ValueMetaItem;
public:
Entry();
void setup(Ruleset *rules);
public:
int value(const Problem *problem) const;
void setValue(Problem *problem, int value) const;
private:
ChoiceStorage *m_storage;
int m_valueIndex;
};
public:
ChoiceStorage();
~ChoiceStorage();
public:
Storage::Instance *create() const;
static const char *name() { return "value-items"; }
protected:
ChoiceStoragePrivate *d_ptr;
private:
Q_DECLARE_PRIVATE(ChoiceStorage);
};
class ChoiceItem : public Item, public IVariableItem {
Q_OBJECT
Q_PROPERTY(int min READ minValue WRITE setMinValue)
Q_PROPERTY(int max READ maxValue WRITE setMaxValue)
public:
ChoiceItem();
ChoiceItem(int minValue, int maxValue);
public:
static Item *construct(const QVariantList &args);
public:
int minValue() const { return m_minValue; }
void setMinValue(int value);
int maxValue() const { return m_maxValue; }
void setMaxValue(int value);
public:
void init(Ruleset *graph);
QDebug debug(QDebug dbg, Problem *problem);
public slots:
int value(const Problem *problem) const;
void setValue(Problem *problem, int value) const;
bool marker(const Problem *problem, int value) const;
void setMarker(Problem *problem, int value, bool state) const;
public: // implementation of interface VariableItem
Item *item();
int possibilities(Problem *problem) const;
int nextPossibility(Problem *problem, int current) const;
void applyPossibility(Problem *problem, int variant);
private:
EliminationStorage::Entry m_eliminator;
ChoiceStorage::Entry m_value;
MarkerStorage::Entry m_markers;
int m_minValue;
int m_maxValue;
};
// TODO move back to valueitem.cpp
class ChoiceStorage::Instance : public Storage::Instance {
friend class ChoiceStorage;
friend class ChoiceStorage::Entry;
public:
Storage::Instance *clone() const;
void clone(const Storage::Instance *other);
private:
QVector<int> m_values;
};
class LastValueInNodeHelper : public ConstraintHelper {
public:
bool apply(Problem* problem) const;
Constraint* constraint() const { return m_constraint; }
ChoiceItem *m_item;
Constraint* m_constraint;
};
class LastValueInNodeConstraint : public Constraint {
Q_OBJECT
public:
LastValueInNodeConstraint(ChoiceItem *item) {
helper.m_item = item;
helper.m_constraint = this;
}
QVector<ConstraintHelper*> helpers() {
return QVector<ConstraintHelper*>() << &helper;
}
QVector<Item*> affectedItems() const {
return QVector<Item*>() << helper.m_item;
}
void init(Ruleset *rules) {
helper.setup(rules);
}
private:
LastValueInNodeHelper helper;
};
#endif
#include "constraint.h"
#include "ruleset.h"
#include <iostream>
/**
* \class ConstraintHelper
*
* \brief ConstraintHelper is the base for all constraint helpers.
*
* Constraint-helpers do the actual validation against the rules of a constraint
* and optionaly helps to resolve the constraint
*/
/**
* \fn void apply(IHelperTarget* target) const
* Applies the helper to the \a target (which is a solver in most cases).
*
* This includes validation and changing the targets problem in a way the limits
* the possible configurations towards the solution(s) of the problem.
* Needs to be implemented by derived classes.
*/
/**
* \fn Constraint* constraint() const
* Returns the constraint the helper belongs to.
*
* Needs to be implemented by derived classes.
*/
/**
* Constructs a new constraint-helper.
*/
ConstraintHelper::ConstraintHelper()
: m_entry(this)
{
}
/**
* Registers the helper at \a ruleset.
*/
void ConstraintHelper::setup(Ruleset* ruleset) {
ruleset->addHelper(this);
m_entry.setup(ruleset);
}
/**
* Called when an \a item associated with the helper changed.
*
* Reactivates the helper in \a problem if it is really affected
* by the change in item.
*/
void ConstraintHelper::itemChanged(Problem* problem, const Item* item) const {
if(!constraint()->reallyAffectsItem(item, problem)) return;
m_entry.activate(problem);
}
/**
* Helps to resolve the constraint.
*
* This method calls apply() and afterwards set this helper to idle
*/
bool ConstraintHelper::resolve(Problem *problem) const {
bool noerror = apply(problem);
m_entry.resolve(problem);
return noerror;
}
/**
* \class Constraint
*
* \brief Constraint is the base for all constraints in a graph.
*
* A constraint is the specific application of a rule. For example
* in sudoku a constraint may limit all cells in a specific row
* to contain each value only once.
*/
/**
* \fn QVector<int> Constraint::affectedNodes() const
* Returns a list of all nodes that may be affected by the constraint.
*/
/**
* Constructs a new constraint.
*/
Constraint::Constraint() {
}
/**
* Returns true if the constraint really affects \a item.
* Reimplement this if the nodes affected by constraint might change
* depending on the state of \a puzzle.
*/
bool Constraint::reallyAffectsItem(const Item *item, Problem *puzzle) const {
Q_UNUSED(item);
Q_UNUSED(puzzle);
return true;
}
/**
* Reimplement this to initilazies the constraint and registrater it
* to the graph.
*/
void Constraint::init(Ruleset *rules) {
Q_UNUSED(rules);
}
#ifndef _KSUDOKU_CONSTRAINT_H_
#define _KSUDOKU_CONSTRAINT_H_
#include <QVector>
#include "item.h"
#include "constrainthelperstorage.h"
class Ruleset;
class Constraint;
class Problem;
class ConstraintHelper;
class ConstraintHelper {
public:
ConstraintHelper();
virtual ~ConstraintHelper() {}
public:
virtual Constraint* constraint() const = 0;
void setup(Ruleset *ruleset);
void itemChanged(Problem* problem, const Item* item) const;
bool resolve(Problem *problem) const;
protected:
virtual bool apply(Problem* problem) const = 0;
public:
int index;
private:
ConstraintHelperStorage::Entry m_entry;
};
class Ruleset;
class Constraint : public Item {
Q_OBJECT
public:
Constraint();
virtual ~Constraint() {}
virtual QVector<ConstraintHelper*> helpers() = 0;
virtual QVector<Item*> affectedItems() const = 0;
virtual bool reallyAffectsItem(const Item *item, Problem *puzzle) const;
virtual void init(Ruleset *rules);
};
#endif
#include "constrainthelperstorage.h"
#include <QVector>
#include "statearray.h"
#include "ruleset.h"
#include "solver.h"
enum HelperState {
HelperIdle = 0,
HelperActive = 1
};
class ConstraintHelperStorage::Instance : public Storage::Instance {
friend class ConstraintHelperStorage;
friend class ConstraintHelperStorage::Entry;
public:
Storage::Instance *clone() const;
void clone(const Storage::Instance *other);
private:
StateArray states;
};
class ConstraintHelperStoragePrivate {
Q_DECLARE_PUBLIC(ConstraintHelperStorage);
public:
ConstraintHelperStoragePrivate(ConstraintHelperStorage *storage);
protected:
ConstraintHelperStorage *q_ptr;
private:
QVector<ConstraintHelper *> helpers;
};
ConstraintHelperStoragePrivate::ConstraintHelperStoragePrivate(ConstraintHelperStorage* storage) {
q_ptr = storage;
}
ConstraintHelperStorage::ConstraintHelperStorage() {
d_ptr = new ConstraintHelperStoragePrivate(this);
}
ConstraintHelperStorage::~ConstraintHelperStorage() {
delete d_ptr;
}
ConstraintHelper* ConstraintHelperStorage::firstActiveHelper(Problem* problem) const {
Q_D(const ConstraintHelperStorage);
Instance *storage = static_cast<Instance*>(problem->storage(this));
if(!storage->states.count(HelperActive))
return 0;
return d->helpers[storage->states.first(HelperActive)];
}
int ConstraintHelperStorage::activeHelpers(Problem* problem) const {
Instance *storage = static_cast<Instance*>(problem->storage(this));
return storage->states.count(HelperActive);
}
void ConstraintHelperStorage::reset(Problem* problem) const {