summaryrefslogtreecommitdiffstats
path: root/kmines/solver/bfield.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmines/solver/bfield.cpp')
-rw-r--r--kmines/solver/bfield.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/kmines/solver/bfield.cpp b/kmines/solver/bfield.cpp
new file mode 100644
index 00000000..d6c03643
--- /dev/null
+++ b/kmines/solver/bfield.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 1996-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 "bfield.h"
+
+
+using namespace KGrid2D;
+
+BaseField::BaseField(long seed)
+ : _nbUncovered(0), _nbMarked(0), _nbUncertain(0), _random(seed)
+{}
+
+CoordList BaseField::coveredNeighbours(const Coord &p) const
+{
+ CoordList n;
+ CoordList tmp = neighbours(p);
+ for (CoordList::const_iterator it=tmp.begin(); it!=tmp.end(); ++it)
+ if ( state(*it)!=Uncovered ) n.append(*it);
+ return n;
+}
+
+uint BaseField::nbMinesAround(const Coord &p) const
+{
+ uint nb = 0;
+ CoordList n = neighbours(p);
+ for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it)
+ if ( hasMine(*it) ) nb++;
+ return nb;
+}
+
+void BaseField::reset(uint width, uint height, uint nbMines)
+{
+ _firstReveal = true;
+ _nbMarked = 0;
+ _nbUncertain = 0;
+ _nbUncovered = 0;
+ _nbMines = nbMines;
+
+ Case tmp;
+ tmp.mine = false;
+ tmp.state = Covered;
+ resize(width, height);
+ fill(tmp);
+}
+
+bool BaseField::checkField(uint w, uint h, uint nb, const QString &s)
+{
+ if ( s.length()!=w*h ) return false;
+ uint n = 0;
+ unsigned int strLength(s.length());
+ for (uint i=0; i<strLength; i++)
+ if ( s[i]=="1" ) n++;
+ else if ( s[i]!="0" ) return false;
+ return ( n==nb );
+}
+
+void BaseField::initReplay(const QString &s)
+{
+ Q_ASSERT( checkField(width(), height(), _nbMines, s) );
+
+ _firstReveal = false;
+
+ Case tmp;
+ tmp.state = Covered;
+ unsigned int strLength(s.length());
+ for (uint i=0; i<strLength; i++) {
+ tmp.mine = ( s[i]=="1" );
+ at(i) = tmp;
+ }
+}
+
+void BaseField::changeState(KMines::CaseState state, int inc)
+{
+ switch (state) {
+ case Uncovered: _nbUncovered += inc; break;
+ case Uncertain: _nbUncertain += inc; break;
+ case Marked: _nbMarked += inc; break;
+ default: break;
+ }
+}
+
+void BaseField::changeCase(const Coord &p, KMines::CaseState newState)
+{
+ changeState(state(p), -1);
+ changeState(newState, 1);
+ (*this)[p].state = newState;
+}
+
+void BaseField::uncover(const Coord &p, CoordList *autorevealed)
+{
+ if ( state(p)!=Covered ) return;
+ changeCase(p, Uncovered);
+
+ if ( nbMinesAround(p)==0 ) {
+ CoordList n = coveredNeighbours(p);
+ if (autorevealed) *autorevealed += n;
+ for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it)
+ uncover(*it, autorevealed);
+ }
+}
+
+void BaseField::showAllMines(bool won)
+{
+ for (uint i=0; i<size(); i++) {
+ Coord p = coord(i);
+ if ( hasMine(p) && state(p)!=Exploded && state(p)!=Marked ) {
+ changeCase(p, won ? Marked : Uncovered);
+ if ( !won ) _nbUncovered--; // not an empty case ...
+ }
+ }
+}
+
+bool BaseField::autoReveal(const Coord &p, bool *caseUncovered)
+{
+ if ( state(p)!=Uncovered ) return true;
+
+ uint nb = nbMinesAround(p);
+ CoordList n = neighbours(p);
+ for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it)
+ if ( state(*it)==Marked ) nb--;
+ if ( nb==0 ) // number of surrounding mines == number of marks :)
+ for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it)
+ if ( !reveal(*it, 0, caseUncovered) ) return false;
+ return true;
+}
+
+bool BaseField::reveal(const Coord &p, CoordList *autorevealed,
+ bool *caseUncovered)
+{
+ if ( state(p)!=Covered ) return true;
+
+ if (_firstReveal) {
+ _firstReveal = false;
+ // set mines positions on field ; must avoid the first
+ // revealed case
+ uint n = size() - 1; // minus one case free
+ Q_ASSERT( _nbMines<n );
+ for(uint k=0; k<_nbMines; k++) {
+ uint pos = _random.getLong(n - k);
+ uint i = 0;
+ Coord tmp;
+ for (;;) {
+ tmp = coord(i);
+ if ( !(tmp==p) && !hasMine(tmp) ) {
+ if ( pos==0 ) break;
+ pos--;
+ }
+ i++;
+ }
+ (*this)[tmp].mine = true;
+ }
+ }
+
+ if ( !hasMine(p) ) {
+ uncover(p, autorevealed);
+ if (caseUncovered) *caseUncovered = true;
+ return true;
+ }
+
+ // explosion
+ changeCase(p, Exploded);
+
+ // find all errors
+ for (uint i=0; i<size(); i++) {
+ Coord p = coord(i);
+ if ( state(p)==Marked && !hasMine(p) ) changeCase(p, Error);
+ }
+ return false;
+}
+
+void BaseField::completeReveal()
+{
+ for (;;) {
+ bool changed = false;
+ for (uint i=0; i<size(); i++) {
+ Coord c = coord(i);
+ if ( state(c)!=Uncovered ) continue;
+ autoReveal(c, &changed);
+ uint nb = nbMinesAround(c);
+ CoordList n = neighbours(c);
+ for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it)
+ if ( state(*it)!=Uncovered ) nb--;
+ if (nb) continue;
+ for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it)
+ if ( state(*it)!=Uncovered && state(*it)!=Marked ) {
+ changed = true;
+ changeCase(*it, Marked);
+ }
+ }
+ if ( !changed ) break;
+ }
+}
+
+void BaseField::doMark(const Coord &c)
+{
+ if ( state(c)!=Covered ) return;
+ changeCase(c, Marked);
+}
+
+QCString BaseField::string() const
+{
+ QCString s(size());
+ for (uint i=0; i<size(); i++)
+ s[i] = (hasMine(coord(i)) ? '1' : '0');
+ return s;
+}