You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kmines/solver/solver.cpp

250 lines
6.3 KiB

/*
* Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com)
* Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org)
*
* 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 "solver.h"
#include "solver.moc"
#include <algorithm>
#include <assert.h>
#include <tqtimer.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <kprogress.h>
#include <tdelocale.h>
#include "headerP.h"
//-----------------------------------------------------------------------------
class SolverPrivate
{
public:
SolverPrivate() : facts(0), rules(0) {}
~SolverPrivate() {
delete facts;
delete rules;
}
AdviseFast::FactSet *facts;
AdviseFast::RuleSet *rules;
#ifdef DEBUG
unsigned long t0, t;
#endif
};
Solver::Solver(TQObject *parent)
: TQObject(parent)
{
d = new SolverPrivate;
#ifdef DEBUG
#define PRINT_ELAPSED(purpose) \
d->t = time(0); \
cout << "Spent " << d->t - d->t0 << " seconds on " purpose << endl; \
d->t0 = d->t;
#endif
}
Solver::~Solver()
{
delete d;
}
Coord Solver::advise(BaseField &field, float &probability)
{
Coord point;
probability = 1;
delete d->facts;
d->facts = new AdviseFast::FactSet(&field);
delete d->rules;
d->rules = new AdviseFast::RuleSet(d->facts);
if( AdviseFast::adviseFast(&point, d->facts, d->rules) ) return point;
CoordSet surePoints;
AdviseFull::ProbabilityMap probabilities;
AdviseFull::adviseFull(d->facts, &surePoints, &probabilities);
// return one of the sure point (random choice to limit the tropism) [NH]
if( !surePoints.empty() ) {
KRandomSequence r;
uint k = r.getLong(surePoints.size());
CoordSet::iterator it = surePoints.begin();
for (uint i=0; i<k; i++) ++it;
return *it;
}
// Just a minimum probability logic here
if( !probabilities.empty() ){
probability = probabilities.begin()->first;
return probabilities.begin()->second;
}
// Otherwise the Field is already solved :)
return Coord(-1,-1);
}
void Solver::solve(BaseField &field, bool noGuess)
{
_field = &field;
initSolve(false, noGuess);
}
bool Solver::initSolve(bool oneStep, bool noGuess)
{
_inOneStep = oneStep;
_noGuess = noGuess;
delete d->facts;
d->facts = new AdviseFast::FactSet(_field);
delete d->rules;
d->rules = new AdviseFast::RuleSet(d->facts);
#ifdef DEBUG
d->t0 = time(0);
#endif
return solveStep();
}
bool Solver::solveStep()
{
if ( _field->isSolved() ) {
emit solvingDone(true);
return true;
}
d->rules->solve();
#ifdef DEBUG
PRINT_ELAPSED("fast rules")
#endif
if( _field->isSolved() ) {
emit solvingDone(true);
return true;
}
CoordSet surePoints;
AdviseFull::ProbabilityMap probabilities;
AdviseFull::adviseFull(d->facts, &surePoints, &probabilities);
#ifdef DEBUG
PRINT_ELAPSED("full rules")
#endif
if(!surePoints.empty()){
CoordSet::iterator i;
for(i=surePoints.begin(); i!=surePoints.end(); ++i) {
bool b = d->rules->reveal(*i);
assert(b);
}
} else if ( !_noGuess ) {
#ifdef DEBUG
cout << "Applying heuristics!" << endl;
cout << *_field << endl;
#endif
// Minimum probability logic
assert(!probabilities.empty());
#ifdef DEBUG
AdviseFull::ProbabilityMap::iterator i=probabilities.begin();
cout << "Probability is " << i->first << endl;
#endif
bool success = d->rules->reveal(probabilities.begin()->second);
if ( !success ) {
emit solvingDone(false);
return false;
}
}
if (_inOneStep) return solveStep();
else TQTimer::singleShot(0, this, TQ_SLOT(solveStep()));
return false;
}
bool Solver::solveOneStep(BaseField &field)
{
_field = &field;
return initSolve(true, false);
}
//-----------------------------------------------------------------------------
SolvingRateDialog::SolvingRateDialog(const BaseField &field, TQWidget *parent)
: KDialogBase(Plain, i18n("Compute Solving Rate"), Ok|Close,
Close, parent, "compute_solving_rate", true, true),
_refField(field)
{
connect(&_solver, TQ_SIGNAL(solvingDone(bool)), TQ_SLOT(solvingDone(bool)));
KGuiItem item = KStdGuiItem::ok();
item.setText(i18n("Start"));
setButtonOK(item);
TQVBoxLayout *top = new TQVBoxLayout(plainPage(), 0, spacingHint());
TQLabel *label = new TQLabel(i18n("Width: %1").arg(field.width()),
plainPage());
top->addWidget(label);
label = new TQLabel(i18n("Height: %1").arg(field.height()), plainPage());
top->addWidget(label);
label = new TQLabel(i18n("Mines: %1 (%2%)").arg(field.nbMines())
.arg( field.nbMines() * 100.0 / field.size()),
plainPage());
top->addWidget(label);
top->addSpacing(spacingHint());
_progress = new KProgress(NB_STEPS, plainPage());
_progress->setTextEnabled(true);
_progress->setFormat("%v");
top->addWidget(_progress);
_label = new TQLabel(i18n("Success rate:"), plainPage());
top->addWidget(_label);
}
void SolvingRateDialog::slotOk()
{
enableButtonOK(false);
_i = 0;
_success = 0;
_progress->setValue(0);
TQTimer::singleShot(0, this, TQ_SLOT(step()));
}
void SolvingRateDialog::step()
{
if ( _i==NB_STEPS ) {
enableButtonOK(true);
return;
}
_i++;
_field.reset(_refField.width(), _refField.height(), _refField.nbMines());
_solver.solve(_field, false);
}
void SolvingRateDialog::solvingDone(bool success)
{
if (success) _success++;
_label->setText(i18n("Success rate: %1%")
.arg(_success * 100.0 / _i, 0, 'f', 3));
_progress->advance(1);
TQTimer::singleShot(0, this, TQ_SLOT(step()));
}