|
|
|
/*
|
|
|
|
* 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 TQString &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 TQString &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);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQCString BaseField::string() const
|
|
|
|
{
|
|
|
|
TQCString s(size());
|
|
|
|
for (uint i=0; i<size(); i++)
|
|
|
|
s[i] = (hasMine(coord(i)) ? '1' : '0');
|
|
|
|
return s;
|
|
|
|
}
|