Diffstat (limited to 'noncore/games/zsame/StoneField.cpp') (more/less context) (show whitespace changes)
-rw-r--r-- | noncore/games/zsame/StoneField.cpp | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/noncore/games/zsame/StoneField.cpp b/noncore/games/zsame/StoneField.cpp new file mode 100644 index 0000000..49d8eca --- a/dev/null +++ b/noncore/games/zsame/StoneField.cpp | |||
@@ -0,0 +1,390 @@ | |||
1 | /* | ||
2 | * ksame 0.4 - simple Game | ||
3 | * Copyright (C) 1997,1998 Marcus Kreutzberger | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include "StoneField.h" | ||
22 | #include <assert.h> | ||
23 | |||
24 | StoneFieldState::StoneFieldState(const StoneField &stonefield) | ||
25 | { | ||
26 | field=new unsigned char[stonefield.maxstone]; | ||
27 | for (int i=0;i<stonefield.maxstone;i++) | ||
28 | field[i]=stonefield.field[i].color; | ||
29 | |||
30 | colors=stonefield.colors; | ||
31 | board=stonefield.board; | ||
32 | score=stonefield.score; | ||
33 | gameover=stonefield.gameover; | ||
34 | } | ||
35 | |||
36 | StoneFieldState::~StoneFieldState() { | ||
37 | delete[] field; | ||
38 | } | ||
39 | |||
40 | void | ||
41 | StoneFieldState::restore(StoneField &stonefield) const { | ||
42 | for (int i=0;i<stonefield.maxstone;i++) { | ||
43 | stonefield.field[i].color=field[i]; | ||
44 | stonefield.field[i].changed=true; | ||
45 | stonefield.field[i].marked=false; | ||
46 | } | ||
47 | |||
48 | stonefield.colors=colors; | ||
49 | stonefield.board=board; | ||
50 | stonefield.score=score; | ||
51 | stonefield.marked=0; | ||
52 | stonefield.gameover=gameover; | ||
53 | } | ||
54 | |||
55 | StoneField::StoneField(int width, int height, | ||
56 | int colors, unsigned int board, | ||
57 | bool undoenabled) | ||
58 | { | ||
59 | // Q_ASSERT(width>0); | ||
60 | // Q_ASSERT(height>0); | ||
61 | |||
62 | if (undoenabled) undolist=new QList<StoneFieldState>; | ||
63 | else undolist=0; | ||
64 | |||
65 | sizex=width; | ||
66 | sizey=height; | ||
67 | maxstone=sizex*sizey; | ||
68 | field=new Stone[maxstone]; | ||
69 | newGame(board,colors); | ||
70 | m_gotBonus= false; | ||
71 | } | ||
72 | |||
73 | StoneField::~StoneField() { | ||
74 | delete[] field; | ||
75 | delete undolist; | ||
76 | // kdDebug() << "~StoneField\n" << endl; | ||
77 | } | ||
78 | |||
79 | int | ||
80 | StoneField::width() const { | ||
81 | return sizex; | ||
82 | } | ||
83 | |||
84 | int | ||
85 | StoneField::height() const { | ||
86 | return sizey; | ||
87 | } | ||
88 | |||
89 | void | ||
90 | StoneField::newGame(unsigned int board,int colors) { | ||
91 | // kdDebug() << "StoneField::newgame board " | ||
92 | // << board << " colors " << colors << endl; | ||
93 | if (colors<1) colors=3; | ||
94 | if (colors>7) colors=7; | ||
95 | this->colors=colors; | ||
96 | this->board=board; | ||
97 | reset(); | ||
98 | } | ||
99 | |||
100 | void | ||
101 | StoneField::reset() { | ||
102 | random.setSeed(board); | ||
103 | |||
104 | Stone *stone=field; | ||
105 | for (int i=0;i<maxstone;i++,stone++) { | ||
106 | stone->color=1+random.getLong(colors); | ||
107 | stone->marked=false; | ||
108 | stone->changed=true; | ||
109 | } | ||
110 | |||
111 | gameover=-1; | ||
112 | score=0; | ||
113 | marked=0; | ||
114 | |||
115 | if (undolist) { | ||
116 | undolist->setAutoDelete(true); | ||
117 | undolist->clear(); | ||
118 | } | ||
119 | |||
120 | |||
121 | int c[7]; | ||
122 | int j; | ||
123 | for (j=0;j<7;j++) c[j]=0; | ||
124 | |||
125 | for (j=0,stone=field;j<maxstone;j++,stone++) { | ||
126 | c[stone->color]++; | ||
127 | } | ||
128 | // kdDebug() << "red " << c[1] << endl; | ||
129 | // kdDebug() << "blue " << c[2] << endl; | ||
130 | // kdDebug() << "yellow " << c[3] << endl; | ||
131 | // kdDebug() << "green " << c[4] << endl; | ||
132 | |||
133 | } | ||
134 | |||
135 | int | ||
136 | StoneField::map(int x,int y) { | ||
137 | assert (!(x<0||y<0||x>=sizex||y>=sizey)); | ||
138 | return x+y*sizex; | ||
139 | } | ||
140 | |||
141 | int | ||
142 | StoneField::mark(int x,int y,bool force) { | ||
143 | int index=map(x,y); | ||
144 | |||
145 | if (index<0) { | ||
146 | unmark(); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | if (field[index].marked) return -1; | ||
151 | unmark(); | ||
152 | |||
153 | mark(index,field[index].color); | ||
154 | |||
155 | if (marked==1&&!force) { | ||
156 | field[index].marked=false; | ||
157 | marked=0; | ||
158 | } | ||
159 | return marked; | ||
160 | } | ||
161 | |||
162 | void | ||
163 | StoneField::mark(int index,unsigned char color) { | ||
164 | if ( index<0 || index>=maxstone ) return; | ||
165 | |||
166 | Stone &stone=field[index]; | ||
167 | |||
168 | if (stone.marked) return; | ||
169 | |||
170 | if (!stone.color || stone.color!=color) return; | ||
171 | |||
172 | stone.changed=true; | ||
173 | stone.marked=true; | ||
174 | marked++; | ||
175 | |||
176 | // mark left | ||
177 | if ((index%sizex)!=0) mark(index-1,color); | ||
178 | // mark right | ||
179 | if (((index+1)%sizex)!=0) mark(index+1,color); | ||
180 | // mark upward | ||
181 | if (index>=sizex) mark(index-sizex,color); | ||
182 | // mark downward | ||
183 | if (index<(sizex-1)*sizey) mark(index+sizex,color); | ||
184 | } | ||
185 | |||
186 | void | ||
187 | StoneField::unmark() { | ||
188 | if (!marked) return; | ||
189 | |||
190 | Stone *stone=field; | ||
191 | for (int i=0;i<maxstone;i++,stone++) { | ||
192 | stone->marked=false; | ||
193 | stone->changed=true; | ||
194 | } | ||
195 | marked=0; | ||
196 | } | ||
197 | |||
198 | int | ||
199 | StoneField::remove(int x,int y,bool force) { | ||
200 | int index=map(x,y); | ||
201 | |||
202 | if (index<0) return 0; | ||
203 | |||
204 | if (!field[index].marked) { | ||
205 | mark(x,y,force); | ||
206 | } | ||
207 | |||
208 | if (!marked) return 0; | ||
209 | |||
210 | // remove a single stone?? | ||
211 | if (marked==1&&!force) return 0; | ||
212 | |||
213 | // add current field to undolist | ||
214 | if (undolist) | ||
215 | undolist->append(new StoneFieldState(*this)); | ||
216 | |||
217 | // increase score | ||
218 | if (marked>2) | ||
219 | score+=(marked-2)*(marked-2); | ||
220 | |||
221 | // remove marked stones | ||
222 | Stone *stone=field; | ||
223 | for (int i=0;i<maxstone;i++,stone++) { | ||
224 | if (stone->marked) { | ||
225 | stone->color=0; | ||
226 | stone->changed=true; | ||
227 | stone->marked=false; | ||
228 | } | ||
229 | } | ||
230 | int removed=marked; | ||
231 | marked=0; | ||
232 | |||
233 | for (int col=0;col<sizex;col++) { | ||
234 | int i1=col+maxstone-sizex; | ||
235 | while ( i1>=0 && field[i1].color ) i1-=sizex; | ||
236 | int i2=i1; | ||
237 | while (i2>=0) { | ||
238 | while ( i2>=0 && !field[i2].color ) i2-=sizex; | ||
239 | while ( i2>=0 && field[i2].color ) { | ||
240 | field[i1].color=field[i2].color; | ||
241 | field[i1].changed=true; | ||
242 | field[i2].color=0; | ||
243 | field[i2].changed=true; | ||
244 | i1-=sizex; | ||
245 | i2-=sizex; | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | // find the last column that has something | ||
251 | int lastcol = sizex; | ||
252 | while (lastcol > 0 && !field[map(lastcol-1, sizey-1)].color) { | ||
253 | lastcol--; | ||
254 | } | ||
255 | |||
256 | for (int col=0;col<lastcol-1;) { | ||
257 | bool empty = true; | ||
258 | for (int row = 0; row < sizey; row++) | ||
259 | if (field[map(col, row)].color) { | ||
260 | empty = false; | ||
261 | break; | ||
262 | } | ||
263 | if (!empty) { | ||
264 | col++; | ||
265 | continue; | ||
266 | } | ||
267 | int nextfullcol = col + 1; | ||
268 | while (nextfullcol < sizex && | ||
269 | !field[map(nextfullcol, sizey - 1)].color) | ||
270 | nextfullcol++; | ||
271 | |||
272 | if (nextfullcol > sizex - 1) | ||
273 | break; // we're ready | ||
274 | |||
275 | for (int row=0; row < sizey; row++) { | ||
276 | int source = map(nextfullcol, row); | ||
277 | int dest = map(col, row); | ||
278 | field[dest].color=field[source].color; | ||
279 | field[dest].changed=true; | ||
280 | field[source].color=0; | ||
281 | field[source].changed=true; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | // add a bonus, if field is empty | ||
286 | if (!field[map(0, sizey-1)].color) { | ||
287 | score+=1000; | ||
288 | m_gotBonus= true; | ||
289 | } | ||
290 | |||
291 | // gameover is undefined | ||
292 | gameover=-1; | ||
293 | return removed; | ||
294 | } | ||
295 | |||
296 | bool StoneField::undoPossible() const { | ||
297 | return !(!undolist||undolist->isEmpty()); | ||
298 | } | ||
299 | |||
300 | int | ||
301 | StoneField::undo(int count) { | ||
302 | if (!undoPossible()) | ||
303 | return 0; | ||
304 | if (count <= 0) | ||
305 | return 0; | ||
306 | int undocount=1; | ||
307 | StoneFieldState *state=0; | ||
308 | undolist->setAutoDelete(true); | ||
309 | while (--count>0) { | ||
310 | if (undolist->count()==1) break; | ||
311 | undolist->removeLast(); | ||
312 | undocount++; | ||
313 | } | ||
314 | state=undolist->getLast(); | ||
315 | // Q_ASSERT(state); | ||
316 | state->restore(*this); | ||
317 | undolist->removeLast(); | ||
318 | return undocount; | ||
319 | } | ||
320 | |||
321 | bool | ||
322 | StoneField::isGameover() const { | ||
323 | register int i=maxstone-1;; | ||
324 | register unsigned char color; | ||
325 | |||
326 | if (gameover>=0) return (bool)gameover; | ||
327 | // kdDebug() << "-->gameover" << endl; | ||
328 | |||
329 | while (i>=0) { | ||
330 | // kdDebug() << i << " " << field[i].color << endl; | ||
331 | // ignore empty fields | ||
332 | while ( i>=0 && field[i].color==0 ) i--; | ||
333 | // Wenn Stein gefunden, | ||
334 | // dann die Nachbarn auf gleiche Farbe pruefen. | ||
335 | while ( i>=0 && (color=field[i].color) ) { | ||
336 | // check left | ||
337 | if ( (i%sizex)!=0 && field[i-1].color==color) | ||
338 | goto check_gameover; | ||
339 | // check upward | ||
340 | if ( i>=sizex && field[i-sizex].color==color) | ||
341 | goto check_gameover; | ||
342 | i--; | ||
343 | } | ||
344 | } | ||
345 | check_gameover: | ||
346 | gameover=(i<0); | ||
347 | // kdDebug() << "<--gameover" << endl; | ||
348 | return (bool)gameover; | ||
349 | } | ||
350 | |||
351 | bool StoneField::gotBonus() const { | ||
352 | return m_gotBonus; | ||
353 | } | ||
354 | |||
355 | int | ||
356 | StoneField::getBoard() const { | ||
357 | return board; | ||
358 | } | ||
359 | |||
360 | int | ||
361 | StoneField::getScore() const { | ||
362 | return score; | ||
363 | } | ||
364 | |||
365 | int | ||
366 | StoneField::getColors() const { | ||
367 | return colors; | ||
368 | } | ||
369 | |||
370 | int | ||
371 | StoneField::getMarked() const { | ||
372 | return marked; | ||
373 | } | ||
374 | |||
375 | int | ||
376 | StoneField::getFieldSize() const { | ||
377 | return maxstone; | ||
378 | } | ||
379 | |||
380 | struct Stone * | ||
381 | StoneField::getField() const { | ||
382 | return field; | ||
383 | } | ||
384 | |||
385 | |||
386 | |||
387 | |||
388 | |||
389 | |||
390 | |||