/* * 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 { register int i=maxstone-1;; register 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; }