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/ksame/StoneField.cpp

400 lines
8.1 KiB

/*
* ksame 0.4 - simple Game
* Copyright (C) 1997,1998 Marcus Kreutzberger
*
* 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 "StoneField.h"
#include <kdebug.h>
#include <assert.h>
StoneFieldState::StoneFieldState(const StoneField &stonefield)
{
field=new unsigned char[stonefield.maxstone];
for (int i=0;i<stonefield.maxstone;i++)
field[i]=stonefield.field[i].color;
colors=stonefield.colors;
board=stonefield.board;
score=stonefield.score;
gameover=stonefield.gameover;
}
StoneFieldState::~StoneFieldState() {
delete[] field;
}
void
StoneFieldState::restore(StoneField &stonefield) const {
for (int i=0;i<stonefield.maxstone;i++) {
stonefield.field[i].color=field[i];
stonefield.field[i].changed=true;
stonefield.field[i].marked=false;
}
stonefield.colors=colors;
stonefield.board=board;
stonefield.score=score;
stonefield.marked=0;
stonefield.gameover=gameover;
}
StoneField::StoneField(int width, int height,
int colors, unsigned int board,
bool undoenabled)
{
Q_ASSERT(width>0);
Q_ASSERT(height>0);
if (undoenabled) undolist=new TQPtrList<StoneFieldState>;
else undolist=0;
sizex=width;
sizey=height;
maxstone=sizex*sizey;
field=new Stone[maxstone];
newGame(board,colors);
m_gotBonus= false;
}
StoneField::~StoneField() {
delete[] field;
delete undolist;
kdDebug() << "~StoneField\n" << endl;
}
int StoneField::count(int color){
int c = 0;
Stone *stone=field;
for (int i=0;i<maxstone;i++,stone++) {
if(stone->color == color)
c++;
}
return c;
}
int
StoneField::width() const {
return sizex;
}
int
StoneField::height() const {
return sizey;
}
void
StoneField::newGame(unsigned int board,int colors) {
kdDebug() << "StoneField::newgame board "
<< board << " colors " << colors << endl;
if (colors<1) colors=3;
if (colors>7) colors=7;
this->colors=colors;
this->board=board;
reset();
}
void
StoneField::reset() {
random.setSeed(board);
Stone *stone=field;
for (int i=0;i<maxstone;i++,stone++) {
stone->color=1+random.getLong(colors);
stone->marked=false;
stone->changed=true;
}
gameover=-1;
score=0;
marked=0;
if (undolist) {
undolist->setAutoDelete(true);
undolist->clear();
}
int c[7];
int j;
for (j=0;j<7;j++) c[j]=0;
for (j=0,stone=field;j<maxstone;j++,stone++) {
c[stone->color]++;
}
kdDebug() << "red " << c[1] << endl;
kdDebug() << "blue " << c[2] << endl;
kdDebug() << "yellow " << c[3] << endl;
kdDebug() << "green " << c[4] << endl;
}
int
StoneField::map(int x,int y) {
assert (!(x<0||y<0||x>=sizex||y>=sizey));
return x+y*sizex;
}
int
StoneField::mark(int x,int y,bool force) {
int index=map(x,y);
if (index<0) {
unmark();
return 0;
}
if (field[index].marked) return -1;
unmark();
mark(index,field[index].color);
if (marked==1&&!force) {
field[index].marked=false;
marked=0;
}
return marked;
}
void
StoneField::mark(int index,unsigned char color) {
if ( index<0 || index>=maxstone ) return;
Stone &stone=field[index];
if (stone.marked) return;
if (!stone.color || stone.color!=color) return;
stone.changed=true;
stone.marked=true;
marked++;
// mark left
if ((index%sizex)!=0) mark(index-1,color);
// mark right
if (((index+1)%sizex)!=0) mark(index+1,color);
// mark upward
if (index>=sizex) mark(index-sizex,color);
// mark downward
if (index<(sizex-1)*sizey) mark(index+sizex,color);
}
void
StoneField::unmark() {
if (!marked) return;
Stone *stone=field;
for (int i=0;i<maxstone;i++,stone++) {
stone->marked=false;
stone->changed=true;
}
marked=0;
}
int
StoneField::remove(int x,int y,bool force) {
int index=map(x,y);
if (index<0) return 0;
if (!field[index].marked) {
mark(x,y,force);
}
if (!marked) return 0;
// remove a single stone??
if (marked==1&&!force) return 0;
// add current field to undolist
if (undolist)
undolist->append(new StoneFieldState(*this));
// increase score
if (marked>2)
score+=(marked-2)*(marked-2);
// remove marked stones
Stone *stone=field;
for (int i=0;i<maxstone;i++,stone++) {
if (stone->marked) {
stone->color=0;
stone->changed=true;
stone->marked=false;
}
}
int removed=marked;
marked=0;
for (int col=0;col<sizex;col++) {
int i1=col+maxstone-sizex;
while ( i1>=0 && field[i1].color ) i1-=sizex;
int i2=i1;
while (i2>=0) {
while ( i2>=0 && !field[i2].color ) i2-=sizex;
while ( i2>=0 && field[i2].color ) {
field[i1].color=field[i2].color;
field[i1].changed=true;
field[i2].color=0;
field[i2].changed=true;
i1-=sizex;
i2-=sizex;
}
}
}
// find the last column that has something
int lastcol = sizex;
while (lastcol > 0 && !field[map(lastcol-1, sizey-1)].color) {
lastcol--;
}
for (int col=0;col<lastcol-1;) {
bool empty = true;
for (int row = 0; row < sizey; row++)
if (field[map(col, row)].color) {
empty = false;
break;
}
if (!empty) {
col++;
continue;
}
int nextfullcol = col + 1;
while (nextfullcol < sizex &&
!field[map(nextfullcol, sizey - 1)].color)
nextfullcol++;
if (nextfullcol > sizex - 1)
break; // we're ready
for (int row=0; row < sizey; row++) {
int source = map(nextfullcol, row);
int dest = map(col, row);
field[dest].color=field[source].color;
field[dest].changed=true;
field[source].color=0;
field[source].changed=true;
}
}
// add a bonus, if field is empty
if (!field[map(0, sizey-1)].color) {
score+=1000;
m_gotBonus= true;
}
// gameover is undefined
gameover=-1;
return removed;
}
bool StoneField::undoPossible() const {
return !(!undolist||undolist->isEmpty());
}
int
StoneField::undo(int count) {
if (!undoPossible())
return 0;
if (count <= 0)
return 0;
int undocount=1;
StoneFieldState *state=0;
undolist->setAutoDelete(true);
while (--count>0) {
if (undolist->count()==1) break;
undolist->removeLast();
undocount++;
}
state=undolist->getLast();
Q_ASSERT(state);
state->restore(*this);
undolist->removeLast();
return undocount;
}
bool
StoneField::isGameover() const {
int i=maxstone-1;;
unsigned char color;
if (gameover>=0) return (bool)gameover;
// kdDebug() << "-->gameover" << endl;
while (i>=0) {
// kdDebug() << i << " " << field[i].color << endl;
// ignore empty fields
while ( i>=0 && field[i].color==0 ) i--;
// Wenn Stein gefunden,
// dann die Nachbarn auf gleiche Farbe pruefen.
while ( i>=0 && (color=field[i].color) ) {
// check left
if ( (i%sizex)!=0 && field[i-1].color==color)
goto check_gameover;
// check upward
if ( i>=sizex && field[i-sizex].color==color)
goto check_gameover;
i--;
}
}
check_gameover:
gameover=(i<0);
// kdDebug() << "<--gameover" << endl;
return (bool)gameover;
}
bool StoneField::gotBonus() const {
return m_gotBonus;
}
int
StoneField::getBoard() const {
return board;
}
int
StoneField::getScore() const {
return score;
}
int
StoneField::getColors() const {
return colors;
}
int
StoneField::getMarked() const {
return marked;
}
int
StoneField::getFieldSize() const {
return maxstone;
}
struct Stone *
StoneField::getField() const {
return field;
}