Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

StoneField.cpp

Go to the documentation of this file.
00001 /*
00002  *   ksame 0.4 - simple Game
00003  *   Copyright (C) 1997,1998  Marcus Kreutzberger
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00018  *
00019  */
00020 
00021 #include "StoneField.h"
00022 #include <assert.h>
00023 
00024 StoneFieldState::StoneFieldState(const StoneField &stonefield)
00025 {
00026     field=new unsigned char[stonefield.maxstone];
00027     for (int i=0;i<stonefield.maxstone;i++)
00028         field[i]=stonefield.field[i].color;
00029 
00030     colors=stonefield.colors;
00031     board=stonefield.board;
00032     score=stonefield.score;
00033     gameover=stonefield.gameover;
00034 }
00035 
00036 StoneFieldState::~StoneFieldState() {
00037     delete[] field;
00038 }
00039 
00040 void
00041 StoneFieldState::restore(StoneField &stonefield) const {
00042     for (int i=0;i<stonefield.maxstone;i++) {
00043         stonefield.field[i].color=field[i];
00044         stonefield.field[i].changed=true;
00045         stonefield.field[i].marked=false;
00046     }
00047 
00048     stonefield.colors=colors;
00049     stonefield.board=board;
00050     stonefield.score=score;
00051     stonefield.marked=0;
00052     stonefield.gameover=gameover;
00053 }
00054 
00055 StoneField::StoneField(int width, int height,
00056                        int colors, unsigned int board,
00057                        bool undoenabled)
00058 {
00059 //    Q_ASSERT(width>0);
00060 //    Q_ASSERT(height>0);
00061 
00062     if (undoenabled) undolist=new QList<StoneFieldState>;
00063     else undolist=0;
00064 
00065     sizex=width;
00066     sizey=height;
00067     maxstone=sizex*sizey;
00068     field=new Stone[maxstone];
00069     newGame(board,colors);
00070     m_gotBonus= false;
00071 }
00072 
00073 StoneField::~StoneField() {
00074     delete[] field;
00075     delete undolist;
00076 //    kdDebug() << "~StoneField\n" << endl;
00077 }
00078 
00079 int
00080 StoneField::width() const {
00081     return sizex;
00082 }
00083 
00084 int
00085 StoneField::height() const {
00086     return sizey;
00087 }
00088 
00089 void
00090 StoneField::newGame(unsigned int board,int colors) {
00091 //    kdDebug() << "StoneField::newgame board "
00092 //              << board << " colors " << colors << endl;
00093     if (colors<1) colors=3;
00094     if (colors>7) colors=7;
00095     this->colors=colors;
00096     this->board=board;
00097     reset();
00098 }
00099 
00100 void
00101 StoneField::reset() {
00102     random.setSeed(board);
00103 
00104     Stone *stone=field;
00105     for (int i=0;i<maxstone;i++,stone++) {
00106         stone->color=1+random.getLong(colors);
00107         stone->marked=false;
00108         stone->changed=true;
00109     }
00110 
00111     gameover=-1;
00112     score=0;
00113     marked=0;
00114 
00115     if (undolist) {
00116         undolist->setAutoDelete(true);
00117         undolist->clear();
00118     }
00119 
00120 
00121     int c[7];
00122     int j;
00123     for (j=0;j<7;j++) c[j]=0;
00124 
00125     for (j=0,stone=field;j<maxstone;j++,stone++) {
00126         c[stone->color]++;
00127     }
00128 //    kdDebug() << "red    " << c[1] << endl;
00129 //    kdDebug() << "blue   " << c[2] << endl;
00130 //    kdDebug() << "yellow " << c[3] << endl;
00131 //    kdDebug() << "green  " << c[4] << endl;
00132 
00133 }
00134 
00135 int
00136 StoneField::map(int x,int y) {
00137         assert (!(x<0||y<0||x>=sizex||y>=sizey));
00138         return x+y*sizex;
00139 }
00140 
00141 int
00142 StoneField::mark(int x,int y,bool force) {
00143         int index=map(x,y);
00144 
00145         if (index<0) {
00146                 unmark();
00147                 return 0;
00148         }
00149 
00150         if (field[index].marked) return -1;
00151         unmark();
00152 
00153         mark(index,field[index].color);
00154 
00155         if (marked==1&&!force) {
00156                 field[index].marked=false;
00157                 marked=0;
00158         }
00159         return marked;
00160 }
00161 
00162 void
00163 StoneField::mark(int index,unsigned char color) {
00164         if ( index<0 || index>=maxstone ) return;
00165 
00166         Stone &stone=field[index];
00167 
00168         if (stone.marked) return;
00169 
00170         if (!stone.color || stone.color!=color) return;
00171 
00172         stone.changed=true;
00173         stone.marked=true;
00174         marked++;
00175 
00176         // mark left
00177         if ((index%sizex)!=0) mark(index-1,color);
00178         // mark right
00179         if (((index+1)%sizex)!=0) mark(index+1,color);
00180         // mark upward
00181         if (index>=sizex) mark(index-sizex,color);
00182         // mark downward
00183         if (index<(sizex-1)*sizey) mark(index+sizex,color);
00184 }
00185 
00186 void
00187 StoneField::unmark() {
00188         if (!marked) return;
00189 
00190         Stone *stone=field;
00191         for (int i=0;i<maxstone;i++,stone++) {
00192                 stone->marked=false;
00193                 stone->changed=true;
00194         }
00195         marked=0;
00196 }
00197 
00198 int
00199 StoneField::remove(int x,int y,bool force) {
00200         int index=map(x,y);
00201 
00202         if (index<0) return 0;
00203 
00204         if (!field[index].marked) {
00205                 mark(x,y,force);
00206         }
00207 
00208         if (!marked) return 0;
00209 
00210         // remove a single stone??
00211         if (marked==1&&!force) return 0;
00212 
00213         // add current field to undolist
00214         if (undolist)
00215                 undolist->append(new StoneFieldState(*this));
00216 
00217         // increase score
00218         if (marked>2)
00219                 score+=(marked-2)*(marked-2);
00220 
00221         // remove marked stones
00222         Stone *stone=field;
00223         for (int i=0;i<maxstone;i++,stone++) {
00224                 if (stone->marked) {
00225                         stone->color=0;
00226                         stone->changed=true;
00227                         stone->marked=false;
00228                 }
00229         }
00230         int removed=marked;
00231         marked=0;
00232 
00233         for (int col=0;col<sizex;col++) {
00234                 int i1=col+maxstone-sizex;
00235                 while ( i1>=0 && field[i1].color ) i1-=sizex;
00236                 int i2=i1;
00237                 while (i2>=0) {
00238                         while ( i2>=0 && !field[i2].color ) i2-=sizex;
00239                         while ( i2>=0 && field[i2].color ) {
00240                                 field[i1].color=field[i2].color;
00241                                 field[i1].changed=true;
00242                                 field[i2].color=0;
00243                                 field[i2].changed=true;
00244                                 i1-=sizex;
00245                                 i2-=sizex;
00246                         }
00247                 }
00248         }
00249 
00250         // find the last column that has something
00251         int lastcol = sizex;
00252         while (lastcol > 0 && !field[map(lastcol-1, sizey-1)].color) {
00253                 lastcol--;
00254         }
00255 
00256         for (int col=0;col<lastcol-1;) {
00257                 bool empty = true;
00258                 for (int row = 0; row < sizey; row++)
00259                         if (field[map(col, row)].color) {
00260                                 empty = false;
00261                                 break;
00262                         }
00263                 if (!empty) {
00264                         col++;
00265                         continue;
00266                 }
00267                 int nextfullcol = col + 1;
00268                 while (nextfullcol < sizex &&
00269                            !field[map(nextfullcol, sizey - 1)].color)
00270                         nextfullcol++;
00271 
00272                 if (nextfullcol > sizex - 1)
00273                         break; // we're ready
00274 
00275                 for (int row=0; row < sizey; row++) {
00276                         int source = map(nextfullcol, row);
00277                         int dest = map(col, row);
00278                         field[dest].color=field[source].color;
00279                         field[dest].changed=true;
00280                         field[source].color=0;
00281                         field[source].changed=true;
00282                 }
00283         }
00284 
00285         // add a bonus, if field is empty
00286         if (!field[map(0, sizey-1)].color) {
00287                 score+=1000;
00288                 m_gotBonus= true;
00289         }
00290 
00291         // gameover is undefined
00292         gameover=-1;
00293         return removed;
00294 }
00295 
00296 bool StoneField::undoPossible() const {
00297         return !(!undolist||undolist->isEmpty());
00298 }
00299 
00300 int
00301 StoneField::undo(int count) {
00302         if (!undoPossible())
00303                 return 0;
00304         if (count <= 0)
00305                 return 0;
00306      int undocount=1;
00307      StoneFieldState *state=0;
00308      undolist->setAutoDelete(true);
00309      while (--count>0) {
00310           if (undolist->count()==1) break;
00311           undolist->removeLast();
00312           undocount++;
00313      }
00314      state=undolist->getLast();
00315 //     Q_ASSERT(state);
00316      state->restore(*this);
00317      undolist->removeLast();
00318      return undocount;
00319 }
00320 
00321 bool
00322 StoneField::isGameover() const {
00323      register int i=maxstone-1;;
00324      register unsigned char color;
00325 
00326      if (gameover>=0) return (bool)gameover;
00327      // kdDebug() << "-->gameover" << endl;
00328 
00329      while (i>=0) {
00330           // kdDebug() << i << " " << field[i].color << endl;
00331           // ignore empty fields
00332           while ( i>=0 && field[i].color==0 ) i--;
00333           // Wenn Stein gefunden,
00334           // dann die Nachbarn auf gleiche Farbe pruefen.
00335           while (  i>=0 && (color=field[i].color) ) {
00336                // check left
00337                if ( (i%sizex)!=0 && field[i-1].color==color)
00338                     goto check_gameover;
00339                // check upward
00340                if ( i>=sizex && field[i-sizex].color==color)
00341                     goto check_gameover;
00342                i--;
00343           }
00344      }
00345  check_gameover:
00346      gameover=(i<0);
00347      //  kdDebug() << "<--gameover" << endl;
00348      return (bool)gameover;
00349 }
00350 
00351 bool StoneField::gotBonus() const {
00352         return m_gotBonus;
00353 }
00354 
00355 void StoneField::clearBonus() {
00356     m_gotBonus = false;
00357 }
00358 
00359 int
00360 StoneField::getBoard() const {
00361      return board;
00362 }
00363 
00364 int
00365 StoneField::getScore() const {
00366      return score;
00367 }
00368 
00369 int
00370 StoneField::getColors() const {
00371      return colors;
00372 }
00373 
00374 int
00375 StoneField::getMarked() const {
00376      return marked;
00377 }
00378 
00379 int
00380 StoneField::getFieldSize() const {
00381      return maxstone;
00382 }
00383 
00384 struct Stone *
00385 StoneField::getField() const {
00386      return field;
00387 }
00388 
00389 
00390 
00391 
00392 
00393 
00394 

Generated on Sat Nov 5 16:17:28 2005 for OPIE by  doxygen 1.4.2