1/* kmahjongg, the classic mahjongg game for KDE project
2 *
3 * Copyright (C) 1997 Mathias Mueller <in5y158@public.uni-hamburg.de>
4 * Copyright (C) 2006-2007 Mauricio Piacentini <mauricio@tabuleiro.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20#include "GameData.h"
21
22#include <KDebug>
23
24
25GameData::GameData(BoardLayout *boardlayout)
26{
27 m_width = boardlayout->m_width;
28 m_height = boardlayout->m_height;
29 m_depth = boardlayout->m_depth;
30 m_maxTiles = (m_width * m_height * m_depth) / 4;
31
32 Highlight = QByteArray(m_width * m_height * m_depth, 0);
33 Mask = QByteArray(m_width * m_height * m_depth, 0);
34 Board = QByteArray(m_width * m_height * m_depth, 0);
35
36 POSITION e; //constructor initializes it to 0
37
38 MoveList = QVector<POSITION>(m_maxTiles, e);
39 tilePositions = QVector<POSITION>(m_maxTiles, e);
40 PosTable = QVector<POSITION>(m_maxTiles, e);
41 positionDepends = QVector<DEPENDENCY>(m_maxTiles);
42
43 //Copy board layout over
44 boardlayout->copyBoardLayout((UCHAR *) getMaskBytes(), MaxTileNum);
45}
46
47GameData::~GameData()
48{
49}
50
51void GameData::putTile(short e, short y, short x, UCHAR f)
52{
53 setBoardData(e, y, x, f);
54 setBoardData(e, y + 1, x, f);
55 setBoardData(e, y + 1, x + 1, f);
56 setBoardData(e, y, x + 1, f);
57}
58
59bool GameData::tilePresent(int z, int y, int x)
60{
61 if ((y < 0) || (x < 0) || (z < 0) || (y > m_height - 1) || (x > m_width - 1)
62 || (z > m_depth - 1)) {
63 return false;
64 }
65
66 return (BoardData(z, y, x) != 0 && MaskData(z, y, x) == '1');
67}
68
69bool GameData::partTile(int z, int y, int x)
70{
71 return (BoardData(z, y, x) != 0);
72}
73
74UCHAR GameData::MaskData(short z, short y, short x)
75{
76 if ((y < 0) || (x < 0) || (z < 0) || (y > m_height - 1) || (x > m_width - 1)
77 || (z > m_depth - 1)) {
78 return 0;
79 }
80
81 return Mask.at((z * m_width * m_height) + (y * m_width) + x);
82}
83
84UCHAR GameData::HighlightData(short z, short y, short x)
85{
86 if ((y < 0) || (x < 0) || (z < 0) || (y > m_height - 1) || (x > m_width - 1)
87 || (z > m_depth - 1)) {
88 return 0;
89 }
90
91 return Highlight.at((z * m_width * m_height) + (y * m_width) + x);
92}
93
94void GameData::setHighlightData(short z, short y, short x, UCHAR value)
95{
96 if ((y < 0) || (x < 0) || (z < 0) || (y > m_height - 1) || (x > m_width - 1)
97 || (z > m_depth - 1)) {
98 return;
99 }
100
101 Highlight[(z * m_width * m_height) + (y * m_width) + x] = value;
102}
103
104UCHAR GameData::BoardData(short z, short y, short x)
105{
106 if ((y < 0) || (x < 0) || (z < 0) || (y > m_height - 1) || (x > m_width - 1)
107 || (z > m_depth - 1)) {
108 return 0;
109 }
110
111 return Board.at((z * m_width * m_height) + (y * m_width) + x);
112}
113
114void GameData::setBoardData(short z, short y, short x, UCHAR value)
115{
116 if ((y < 0) || (x < 0) || (z < 0) || (y > m_height - 1) || (x > m_width - 1)
117 || (z > m_depth - 1)) {
118 return;
119 }
120
121 Board[(z * m_width * m_height) + (y * m_width) + x] = value;
122}
123
124POSITION& GameData::MoveListData(short i)
125{
126 if ((i >= MoveList.size()) || (i < 0)) {
127 qDebug() << "Attempt to access GameData::MoveListData with invalid index";
128 i = 0 ;
129 }
130
131 return MoveList[i];
132}
133
134void GameData::setMoveListData(short i, POSITION &value)
135{
136 if ((i >= MoveList.size()) || (i < 0)) {
137 return ;
138 }
139
140 MoveList[i] = value;
141}
142
143void GameData::generateTilePositions()
144{
145 // Generate the position data for the layout from contents of Game.Map.
146
147 numTilesToGenerate = 0;
148
149 for (int z = 0; z < m_depth; z++) {
150 for (int y = 0; y < m_height; y++) {
151 for (int x = 0; x < m_width; x++) {
152 setBoardData(z, y, x, 0);
153 if (MaskData(z,y,x) == '1') {
154 tilePositions[numTilesToGenerate].x = x;
155 tilePositions[numTilesToGenerate].y = y;
156 tilePositions[numTilesToGenerate].e = z;
157 tilePositions[numTilesToGenerate].f = 254;
158 numTilesToGenerate++;
159 }
160 }
161 }
162 }
163}
164
165void GameData::generatePositionDepends()
166{
167 // Generate the dependency data for the layout from the position data.
168 // Note that the coordinates of each tile in tilePositions are those of
169 // the upper left quarter of the tile.
170
171 // For each tile,
172 for (int i = 0; i < numTilesToGenerate; i++) {
173
174 // Get its basic position data
175 int x = tilePositions[i].x;
176 int y = tilePositions[i].y;
177 int z = tilePositions[i].e;
178
179 // LHS dependencies
180 positionDepends[i].lhs_dep[0] = tileAt(x - 1, y, z);
181 positionDepends[i].lhs_dep[1] = tileAt(x - 1, y + 1, z);
182
183 // Make them unique
184 if (positionDepends[i].lhs_dep[1] == positionDepends[i].lhs_dep[0]) {
185 positionDepends[i].lhs_dep[1] = -1;
186 }
187
188 // RHS dependencies
189 positionDepends[i].rhs_dep[0] = tileAt(x + 2, y, z);
190 positionDepends[i].rhs_dep[1] = tileAt(x + 2, y + 1, z);
191
192 // Make them unique
193 if (positionDepends[i].rhs_dep[1] == positionDepends[i].rhs_dep[0]) {
194 positionDepends[i].rhs_dep[1] = -1;
195 }
196
197 // Turn dependencies
198 positionDepends[i].turn_dep[0] = tileAt(x, y, z + 1);
199 positionDepends[i].turn_dep[1] = tileAt(x + 1, y, z + 1);
200 positionDepends[i].turn_dep[2] = tileAt(x + 1, y + 1, z + 1);
201 positionDepends[i].turn_dep[3] = tileAt(x, y + 1, z + 1);
202
203 // Make them unique
204 for (int j = 0; j < 3; j++) {
205 for (int k = j + 1; k < 4; k++) {
206 if (positionDepends[i].turn_dep[j] == positionDepends[i].turn_dep[k]) {
207 positionDepends[i].turn_dep[k] = -1;
208 }
209 }
210 }
211
212 // Placement dependencies
213 positionDepends[i].place_dep[0] = tileAt(x, y, z - 1);
214 positionDepends[i].place_dep[1] = tileAt(x + 1, y, z - 1);
215 positionDepends[i].place_dep[2] = tileAt(x + 1, y + 1, z - 1);
216 positionDepends[i].place_dep[3] = tileAt(x, y + 1, z - 1);
217
218 // Make them unique
219 for (int j = 0; j < 3; j++) {
220 for (int k = j + 1; k < 4; k++) {
221 if (positionDepends[i].place_dep[j] == positionDepends[i].place_dep[k]) {
222 positionDepends[i].place_dep[k] = -1;
223 }
224 }
225 }
226
227 // Filled and free indicators.
228 positionDepends[i].filled = false;
229 positionDepends[i].free = false;
230 }
231}
232
233int GameData::tileAt(int x, int y, int z)
234{
235 // x, y, z are the coordinates of a *quarter* tile. This returns the
236 // index (in positions) of the tile at those coordinates or -1 if there
237 // is no tile at those coordinates. Note that the coordinates of each
238 // tile in positions are those of the upper left quarter of the tile.
239
240 for (int i = 0; i < numTilesToGenerate; i++) {
241 if (tilePositions[i].e == z) {
242 if ((tilePositions[i].x == x && tilePositions[i].y == y)
243 || (tilePositions[i].x == x - 1 && tilePositions[i].y == y)
244 || (tilePositions[i].x == x - 1 && tilePositions[i].y == y - 1)
245 || (tilePositions[i].x == x && tilePositions[i].y == y - 1)) {
246 return i;
247 }
248 }
249 }
250
251 return -1;
252}
253
254bool GameData::generateSolvableGame()
255{
256 // Initially we want to mark positions on layer 0 so that we have only
257 // one free position per apparent horizontal line.
258 for (int i = 0; i < numTilesToGenerate; i++) {
259 // Pick a random tile on layer 0
260 int position, cnt = 0;
261
262 do {
263 position = (int) random.getLong(numTilesToGenerate);
264
265 if (cnt++ > (numTilesToGenerate*numTilesToGenerate)) {
266 return false; // bail
267 }
268 } while (tilePositions[position].e != 0);
269
270 // If there are no other free positions on the same apparent
271 // horizontal line, we can mark that position as free.
272 if (onlyFreeInLine(position)) {
273 positionDepends[position].free = true;
274 }
275 }
276
277 // Check to make sure we really got them all. Very important for
278 // this algorithm.
279 for (int i = 0; i < numTilesToGenerate; i++) {
280 if (tilePositions[i].e == 0 && onlyFreeInLine(i)) {
281 positionDepends[i].free = true;
282 }
283 }
284
285 // Get ready to place the tiles
286 int lastPosition = -1;
287 int position = -1;
288 int position2 = -1;
289
290 // For each position,
291 for (int i = 0; i < numTilesToGenerate; i++) {
292
293 // If this is the first tile in a 144 tile set,
294 if ((i % 144) == 0) {
295
296 // Initialise the faces to allocate. For the classic
297 // dragon board there are 144 tiles. So we allocate and
298 // randomise the assignment of 144 tiles. If there are > 144
299 // tiles we will reallocate and re-randomise as we run out.
300 // One advantage of this method is that the pairs to assign are
301 // non-linear. In kmahjongg 0.4, If there were > 144 the same
302 // allocation series was followed. So 154 = 144 + 10 rods.
303 // 184 = 144 + 40 rods (20 pairs) which overwhemed the board
304 // with rods and made deadlock games more likely.
305 randomiseFaces();
306 }
307
308 // If this is the first half of a pair, there is no previous
309 // position for the pair.
310 if ((i & 1) == 0) {
311 lastPosition = -1;
312 }
313
314 // Select a position for the tile, relative to the position of
315 // the last tile placed.
316 if ((position = selectPosition(lastPosition)) < 0) {
317 return false; // bail
318 }
319
320 if (i < numTilesToGenerate - 1) {
321 if ((position2 = selectPosition(lastPosition)) < 0) {
322 return false; // bail
323 }
324 if (tilePositions[position2].e > tilePositions[position].e) {
325 position = position2; // higher is better
326 }
327 }
328
329 // Place the tile.
330 placeTile(position, tilePair[i % 144]);
331
332 // Remember the position
333 lastPosition = position;
334 }
335
336 // The game is solvable.
337 return true;
338}
339
340bool GameData::onlyFreeInLine(int position)
341{
342 // Determines whether it is ok to mark this position as "free" because
343 // there are no other positions marked "free" in its apparent horizontal
344 // line.
345
346 int i, i0, w;
347 int lin, rin, out;
348 //static int nextLeft[m_maxTiles];
349 //static int nextRight[m_maxTiles];
350
351 QVector<int> nextLeft = QVector<int>(m_maxTiles, 0);
352 QVector<int> nextRight = QVector<int>(m_maxTiles, 0);
353
354 /* Check left, starting at position */
355 lin = 0;
356 out = 0;
357 nextLeft[lin++] = position;
358
359 do {
360 w = nextLeft[out++];
361 if (positionDepends[w].free || positionDepends[w].filled) {
362 return false;
363 }
364
365 if ((i = positionDepends[w].lhs_dep[0]) != -1) {
366 nextLeft[lin++] = i;
367 }
368
369 i0 = i;
370
371 if ((i = positionDepends[w].lhs_dep[1]) != -1 && i0 != i) {
372 nextLeft[lin++] = i;
373 }
374 } while (lin > out);
375
376 /* Check right, starting at position */
377 rin = 0;
378 out = 0;
379 nextRight[rin++] = position;
380
381 do {
382 w = nextRight[out++];
383
384 if (positionDepends[w].free || positionDepends[w].filled) {
385 return false;
386 }
387
388 if ((i = positionDepends[w].rhs_dep[0]) != -1) {
389 nextRight[rin++] = i;
390 }
391
392 i0 = i;
393
394 if ((i = positionDepends[w].rhs_dep[1]) != -1 && i0 != i) {
395 nextRight[rin++] = i;
396 }
397 } while (rin > out);
398
399 // Here, the position can be marked "free"
400 return true;
401}
402
403int GameData::selectPosition(int lastPosition)
404{
405 int position, cnt = 0;
406 bool goodPosition = false;
407
408 // while a good position has not been found,
409 while (!goodPosition) {
410
411 // Select a random, but free, position.
412 do {
413 position = random.getLong(numTilesToGenerate);
414
415 if (cnt++ > (numTilesToGenerate*numTilesToGenerate)) {
416 return -1; // bail
417 }
418 } while (!positionDepends[position].free);
419
420 // Found one.
421 goodPosition = true;
422
423 // If there is a previous position to take into account,
424 if (lastPosition != -1) {
425
426 // Check the new position against the last one.
427 for (int i = 0; i < 4; i++) {
428 if (positionDepends[position].place_dep[i] == lastPosition) {
429 goodPosition = false; // not such a good position
430 }
431 }
432
433 for (int i = 0; i < 2; i++) {
434 if ((positionDepends[position].lhs_dep[i] == lastPosition)
435 || (positionDepends[position].rhs_dep[i] == lastPosition)) {
436 goodPosition = false; // not such a good position
437 }
438 }
439 }
440 }
441
442 return position;
443}
444
445void GameData::placeTile(int position, int tile)
446{
447 // Install the tile in the specified position
448 tilePositions[position].f = tile;
449 putTile(tilePositions[position]);
450
451 //added highlight?
452 //setHighlightData(E,Y,X,0);
453
454 // Update position dependency data
455 positionDepends[position].filled = true;
456 positionDepends[position].free = false;
457
458 // Now examine the tiles near this to see if this makes them "free".
459 int depend;
460
461 for (int i = 0; i < 4; i++) {
462 if ((depend = positionDepends[position].turn_dep[i]) != -1) {
463 updateDepend(depend);
464 }
465 }
466
467 for (int i = 0; i < 2; i++) {
468 if ((depend = positionDepends[position].lhs_dep[i]) != -1) {
469 updateDepend(depend);
470 }
471
472 if ((depend = positionDepends[position].rhs_dep[i]) != -1) {
473 updateDepend(depend);
474 }
475 }
476}
477
478void GameData::updateDepend(int position)
479{
480 // Updates the free indicator in the dependency data for a position
481 // based on whether the positions on which it depends are filled.
482
483 // If the position is valid and not filled
484 if (position >= 0 && !positionDepends[position].filled) {
485
486 // Check placement depends. If they are not filled, the
487 // position cannot become free.
488 int depend;
489 for (int i = 0; i < 4; i++) {
490 if ((depend = positionDepends[position].place_dep[i]) != -1) {
491 if (!positionDepends[depend].filled) {
492 return ;
493 }
494 }
495 }
496
497 // If position is first free on apparent horizontal, it is
498 // now free to be filled.
499 if (onlyFreeInLine(position)) {
500 positionDepends[position].free = true;
501
502 return;
503 }
504
505 // Assume no LHS positions to fill
506 bool lfilled = false;
507
508 // If positions to LHS
509 if ((positionDepends[position].lhs_dep[0] != -1)
510 || (positionDepends[position].lhs_dep[1] != -1)) {
511
512 // Assume LHS positions filled
513 lfilled = true;
514
515 for (int i = 0; i < 2; i++) {
516 if ((depend = positionDepends[position].lhs_dep[i]) != -1) {
517 if (!positionDepends[depend].filled) {
518 lfilled = false;
519 }
520 }
521 }
522 }
523
524 // Assume no RHS positions to fill
525 bool rfilled = false;
526
527 // If positions to RHS
528 if ((positionDepends[position].rhs_dep[0] != -1)
529 || (positionDepends[position].rhs_dep[1] != -1)) {
530
531 // Assume LHS positions filled
532 rfilled = true;
533
534 for (int i = 0; i < 2; i++) {
535 if ((depend = positionDepends[position].rhs_dep[i]) != -1) {
536 if (!positionDepends[depend].filled) {
537 rfilled = false;
538 }
539 }
540 }
541 }
542
543 // If positions to left or right are filled, this position
544 // is now free to be filled.
545 positionDepends[position].free = (lfilled || rfilled);
546 }
547}
548
549bool GameData::generateStartPosition2()
550{
551 // For each tile,
552 for (int i = 0; i < numTilesToGenerate; i++) {
553
554 // Get its basic position data
555 int x = tilePositions[i].x;
556 int y = tilePositions[i].y;
557 int z = tilePositions[i].e;
558
559 // Clear Game.Board at that position
560 setBoardData(z, y, x, 0);
561
562 // Clear tile placed/free indicator(s).
563 positionDepends[i].filled = false;
564 positionDepends[i].free = false;
565
566 // Set tile face blank
567 tilePositions[i].f = 254;
568 }
569
570 // If solvable games should be generated,
571// if (Prefs::solvableGames()) {
572
573 if (generateSolvableGame()) {
574 TileNum = MaxTileNum;
575 return true;
576 } else {
577 return false;
578 }
579
580// }
581
582 // Initialise the faces to allocate. For the classic
583 // dragon board there are 144 tiles. So we allocate and
584 // randomise the assignment of 144 tiles. If there are > 144
585 // tiles we will reallocate and re-randomise as we run out.
586 // One advantage of this method is that the pairs to assign are
587 // non-linear. In kmahjongg 0.4, If there were > 144 the same
588 // allocation series was followed. So 154 = 144 + 10 rods.
589 // 184 = 144 + 40 rods (20 pairs) which overwhemed the board
590 // with rods and made deadlock games more likely.
591
592 int remaining = numTilesToGenerate;
593 randomiseFaces();
594
595 for (int tile = 0; tile < numTilesToGenerate; tile += 2) {
596 int p1;
597 int p2;
598
599 if (remaining > 2) {
600 p2 = p1 = random.getLong(remaining - 2);
601 int bail = 0;
602
603 while (p1 == p2) {
604 p2 = random.getLong(remaining - 2);
605
606 if (bail >= 100) {
607 if (p1 != p2) {
608 break;
609 }
610 }
611
612 if ((tilePositions[p1].y == tilePositions[p2].y)
613 && (tilePositions[p1].e == tilePositions[p2].e)) {
614 // skip if on same y line
615 bail++;
616 p2=p1;
617
618 continue;
619 }
620 }
621 } else {
622 p1 = 0;
623 p2 = 1;
624 }
625
626 POSITION a;
627 POSITION b;
628
629 a = tilePositions[p1];
630 b = tilePositions[p2];
631
632 tilePositions[p1] = tilePositions[remaining - 1];
633 tilePositions[p2] = tilePositions[remaining - 2];
634
635 remaining -= 2;
636
637 getFaces(a, b);
638 putTile(a);
639 putTile(b);
640 }
641
642 TileNum = MaxTileNum;
643
644 return 1;
645}
646
647void GameData::getFaces(POSITION &a, POSITION &b)
648{
649 a.f = tilePair[tilesUsed];
650 b.f = tilePair[tilesUsed + 1];
651 tilesUsed += 2;
652
653 if (tilesUsed >= 144) {
654 randomiseFaces();
655 }
656}
657
658void GameData::randomiseFaces()
659{
660 int nr;
661 int numAlloced = 0;
662 // stick in 144 tiles in pairsa.
663
664 for (nr = 0; nr < 9 * 4; ++nr) {
665 tilePair[numAlloced++] = TILE_CHARACTER + (nr / 4); // 4*9 Tiles
666 }
667
668 for (nr = 0; nr < 9 * 4; ++nr) {
669 tilePair[numAlloced++] = TILE_BAMBOO + (nr / 4); // 4*9 Tiles
670 }
671
672 for (nr = 0; nr < 9 * 4; ++nr) {
673 tilePair[numAlloced++] = TILE_ROD + (nr / 4); // 4*9 Tiles
674 }
675
676 for (nr = 0; nr < 4; ++nr) {
677 tilePair[numAlloced++] = TILE_FLOWER + nr; // 4 Tiles
678 }
679
680 for (nr = 0; nr < 4; ++nr) {
681 tilePair[numAlloced++] = TILE_SEASON + nr; // 4 Tiles
682 }
683
684 for (nr = 0; nr < 4 * 4; ++nr) {
685 tilePair[numAlloced++] = TILE_WIND + (nr / 4); // 4*4 Tiles
686 }
687
688 for (nr = 0; nr < 3 * 4; ++nr) {
689 tilePair[numAlloced++] = TILE_DRAGON + (nr / 4); // 3*4 Tiles
690 }
691
692 //randomise. Keep pairs together. Ie take two random
693 //odd numbers (n,x) and swap n, n+1 with x, x+1
694
695 int at = 0;
696 int to = 0;
697 for (int r = 0; r < 200; r++) {
698 to = at;
699
700 while (to==at) {
701 to = random.getLong(144);
702
703 if ((to & 1) != 0) {
704 to--;
705 }
706
707 }
708
709 UCHAR tmp = tilePair[at];
710 tilePair[at] = tilePair[to];
711 tilePair[to] = tmp;
712 tmp = tilePair[at + 1];
713 tilePair[at + 1] = tilePair[to + 1];
714 tilePair[to + 1] = tmp;
715
716 at += 2;
717
718 if (at >= 144) {
719 at = 0;
720 }
721 }
722
723 tilesAllocated = numAlloced;
724 tilesUsed = 0;
725}
726
727bool isFlower(UCHAR Tile)
728{
729 return (Tile >= TILE_FLOWER && Tile <=TILE_FLOWER + 3);
730}
731
732bool isSeason(UCHAR Tile)
733{
734 return (Tile >= TILE_SEASON && Tile <= TILE_SEASON + 3);
735}
736
737bool isBamboo(UCHAR t)
738{
739 return (t >= TILE_BAMBOO && t < TILE_BAMBOO + 9);
740}
741
742bool isCharacter(UCHAR t)
743{
744 return (t < TILE_CHARACTER + 9);
745}
746
747bool isRod(UCHAR t)
748{
749 return (t >= TILE_ROD && t < TILE_ROD + 9);
750}
751
752bool isDragon(UCHAR t)
753{
754 return (t >= TILE_DRAGON && t < TILE_DRAGON + 3);
755}
756
757bool isWind(UCHAR t)
758{
759 return (t >= TILE_WIND && t < TILE_WIND + 4);
760}
761
762bool GameData::isMatchingTile(POSITION &Pos1, POSITION &Pos2)
763{
764 // don't compare 'equal' positions
765 if (memcmp(&Pos1, &Pos2, sizeof(POSITION))) {
766 UCHAR FA = Pos1.f;
767 UCHAR FB = Pos2.f;
768
769 if ((FA == FB) || (isFlower(FA) && isFlower(FB)) || (isSeason(FA) && isSeason(FB))) {
770 return true;
771 }
772 }
773
774 return false;
775}
776
777void GameData::setRemovedTilePair(POSITION &a, POSITION &b)
778{
779 if (isFlower(a.f)) {
780 removedFlower[a.f - TILE_FLOWER]++;
781 removedFlower[b.f - TILE_FLOWER]++;
782
783 return;
784 }
785
786 if (isSeason(a.f)) {
787 removedSeason[a.f - TILE_SEASON]++;
788 removedSeason[b.f - TILE_SEASON]++;
789
790 return;
791 }
792
793 if (isCharacter(a.f)) {
794 removedCharacter[a.f - TILE_CHARACTER] += 2;
795
796 return;
797 }
798
799 if (isBamboo(a.f)) {
800 removedBamboo[a.f - TILE_BAMBOO] += 2;
801
802 return;
803 }
804
805 if (isRod(a.f)) {
806 removedRod[a.f - TILE_ROD] += 2;
807
808 return;
809 }
810
811 if (isDragon(a.f)){
812 removedDragon[a.f - TILE_DRAGON] += 2;
813
814 return;
815 }
816
817 if (isWind(a.f)){
818 removedWind[a.f - TILE_WIND] += 2;
819
820 return;
821 }
822}
823
824void GameData::clearRemovedTilePair(POSITION &a, POSITION &b)
825{
826 if (isFlower(a.f)) {
827 removedFlower[a.f - TILE_FLOWER]--;
828 removedFlower[b.f - TILE_FLOWER]--;
829
830 return;
831 }
832
833 if (isSeason(a.f)) {
834 removedSeason[a.f - TILE_SEASON]--;
835 removedSeason[b.f - TILE_SEASON]--;
836
837 return;
838 }
839
840 if (isCharacter(a.f)) {
841 removedCharacter[a.f - TILE_CHARACTER] -= 2;
842
843 return;
844 }
845
846 if (isBamboo(a.f)) {
847 removedBamboo[a.f - TILE_BAMBOO] -= 2;
848
849 return;
850 }
851
852 if (isRod(a.f)) {
853 removedRod[a.f - TILE_ROD] -= 2;
854
855 return;
856 }
857
858 if (isDragon(a.f)) {
859 removedDragon[a.f - TILE_DRAGON] -= 2;
860
861 return;
862 }
863
864 if (isWind(a.f)) {
865 removedWind[a.f - TILE_WIND] -= 2;
866
867 return;
868 }
869}
870
871void GameData::initialiseRemovedTiles()
872{
873 for (int pos = 0; pos < 9; pos++) {
874 removedCharacter[pos] = 0;
875 removedBamboo[pos] = 0;
876 removedRod[pos] = 0;
877 removedDragon[pos % 3] = 0;
878 removedFlower[pos % 4] = 0;
879 removedWind[pos % 4] = 0;
880 removedSeason[pos % 4] = 0;
881 }
882}
883
884bool GameData::findMove(POSITION &posA, POSITION &posB)
885{
886 short Pos_Ende = MaxTileNum; // Ende der PosTable
887
888 for (short E = 0; E < m_depth; E++) {
889 for (short Y = 0; Y < m_height - 1; Y++) {
890 for (short X = 0; X < m_width - 1; X++) {
891 if (MaskData(E, Y, X) != (UCHAR) '1') {
892 continue;
893 }
894
895 if (!BoardData(E, Y, X)) {
896 continue;
897 }
898
899 if (E < m_depth - 1) {
900 if (BoardData(E + 1, Y, X) || BoardData(E + 1, Y + 1, X)
901 || BoardData(E + 1, Y, X + 1) || BoardData(E + 1, Y + 1, X + 1)) {
902 continue;
903 }
904 }
905
906 if (X < m_width - 2 && (BoardData(E, Y, X - 1) || BoardData(E, Y + 1, X - 1))
907 && (BoardData(E, Y, X + 2) || BoardData(E, Y + 1, X + 2))) {
908 continue;
909 }
910
911 Pos_Ende--;
912 PosTable[Pos_Ende].e = E;
913 PosTable[Pos_Ende].y = Y;
914 PosTable[Pos_Ende].x = X;
915 PosTable[Pos_Ende].f = BoardData(E, Y, X);
916 }
917 }
918 }
919
920 short iPosCount = 0; // Hier Anzahl der gefunden Paare merken
921
922 // The new tile layout with non-contiguos horizantle spans
923 // can lead to huge numbers of matching pairs being exposed.
924 // we alter the loop to bail out when BoardLayout::maxTiles/2 pairs are found
925 // (or less);
926 while (Pos_Ende < MaxTileNum - 1 && iPosCount < m_maxTiles - 2) {
927 for (short Pos = Pos_Ende + 1; Pos < MaxTileNum; Pos++) {
928 if (isMatchingTile(PosTable[Pos], PosTable[Pos_Ende])) {
929 if (iPosCount < m_maxTiles - 2) {
930 PosTable[iPosCount++] = PosTable[Pos_Ende];
931 PosTable[iPosCount++] = PosTable[Pos];
932 }
933 }
934 }
935
936 Pos_Ende++;
937 }
938
939 if (iPosCount >= 2) {
940 random.setSeed(0); // WABA: Why is the seed reset?
941 short Pos = random.getLong(iPosCount) & -2; // Gerader Wert
942 posA = PosTable[Pos];
943 posB = PosTable[Pos + 1];
944
945 return true;
946 } else {
947 return false;
948 }
949}
950
951int GameData::moveCount()
952{
953 short Pos_Ende = MaxTileNum; // end of PosTable
954
955 for (short E = 0; E < m_depth; E++) {
956 for (short Y = 0; Y < m_height - 1; Y++) {
957 for (short X = 0; X < m_width - 1; X++) {
958 if (MaskData(E, Y, X) != (UCHAR) '1') {
959 continue;
960 }
961
962 if (!BoardData(E, Y, X)) {
963 continue;
964 }
965
966 if (E < m_depth - 1) {
967 if (BoardData(E + 1, Y, X) || BoardData(E + 1, Y + 1,X)
968 || BoardData(E + 1, Y, X + 1) || BoardData(E + 1, Y + 1, X + 1)) {
969 continue;
970 }
971 }
972
973 if (X < m_width - 2 && (BoardData(E, Y, X - 1) || BoardData(E, Y + 1, X - 1))
974 && (BoardData(E, Y, X + 2) || BoardData(E, Y + 1, X + 2))) {
975 continue;
976 }
977
978 Pos_Ende--;
979 PosTable[Pos_Ende].e = E;
980 PosTable[Pos_Ende].y = Y;
981 PosTable[Pos_Ende].x = X;
982 PosTable[Pos_Ende].f = BoardData(E, Y, X);
983 }
984 }
985 }
986
987 short iPosCount = 0; // store number of pairs found
988
989 while (Pos_Ende < MaxTileNum - 1 && iPosCount < m_maxTiles - 2) {
990 for (short Pos = Pos_Ende + 1; Pos < MaxTileNum; Pos++) {
991 if (isMatchingTile(PosTable[Pos], PosTable[Pos_Ende])) {
992 if (iPosCount < m_maxTiles - 2) {
993 PosTable[iPosCount++] = PosTable[Pos_Ende];
994 PosTable[iPosCount++] = PosTable[Pos];
995 }
996 }
997 }
998
999 Pos_Ende++;
1000 }
1001
1002 return iPosCount / 2;
1003}
1004
1005short GameData::findAllMatchingTiles(POSITION &posA)
1006{
1007 short Pos = 0;
1008
1009 for (short E = 0; E < m_depth; E++) {
1010 for (short Y = 0; Y < m_height - 1; Y++) {
1011 for (short X = 0; X < m_width - 1; X++) {
1012 if (MaskData(E, Y, X) != (UCHAR) '1') {
1013 continue;
1014 }
1015
1016 if (!BoardData(E, Y, X)) {
1017 continue;
1018 }
1019
1020 if (E < m_depth - 1) {
1021 if (BoardData(E + 1, Y, X) || BoardData(E + 1, Y + 1, X)
1022 || BoardData(E + 1, Y, X + 1) || BoardData(E + 1, Y + 1, X + 1)) {
1023 continue;
1024 }
1025 }
1026
1027 if (X < m_width - 2 && (BoardData(E, Y, X - 1) || BoardData(E, Y + 1, X - 1))
1028 && (BoardData(E, Y, X + 2) || BoardData(E, Y + 1, X + 2))) {
1029 continue;
1030 }
1031
1032 PosTable[Pos].e = E;
1033 PosTable[Pos].y = Y;
1034 PosTable[Pos].x = X;
1035 PosTable[Pos].f = BoardData(E, Y, X);
1036
1037 if (isMatchingTile(posA, PosTable[Pos])) {
1038 Pos++;
1039 }
1040 }
1041 }
1042 }
1043
1044 return Pos;
1045}
1046
1047bool GameData::loadFromStream(QDataStream &in)
1048{
1049 in >> Board;
1050 in >> Mask;
1051 in >> Highlight;
1052 in >> allow_undo;
1053 in >> allow_redo;
1054 in >> TileNum;
1055 in >> MaxTileNum;
1056
1057 //Read list count
1058 in >> m_maxTiles;
1059
1060 //Reconstruct the MoveList
1061 for (int i = 0; i < m_maxTiles; ++i) {
1062 POSITION thispos;
1063 in >> thispos.e;
1064 in >> thispos.y;
1065 in >> thispos.x;
1066 in >> thispos.f;
1067 setMoveListData(i, thispos);
1068 }
1069
1070 return true;
1071}
1072
1073bool GameData::saveToStream(QDataStream &out)
1074{
1075 out << Board;
1076 out << Mask;
1077 out << Highlight;
1078 out << allow_undo;
1079 out << allow_redo;
1080 out << TileNum;
1081 out << MaxTileNum;
1082
1083 //write the size of our lists
1084 out << m_maxTiles;
1085
1086 //and then write all position components for the MoveList
1087 for (int i = 0; i < m_maxTiles; ++i) {
1088 POSITION thispos = MoveList.at(i);
1089 out << (quint16) thispos.e;
1090 out << (quint16) thispos.y;
1091 out << (quint16) thispos.x;
1092 out << (quint16) thispos.f;
1093 }
1094
1095 return true;
1096}
1097
1098void GameData::shuffle()
1099{
1100 int count = 0;
1101
1102 // copy positions and faces of the remaining tiles into
1103 // the pos table
1104 for (int e = 0; e < m_depth; e++) {
1105 for (int y = 0; y < m_height; y++) {
1106 for (int x = 0; x < m_width; x++) {
1107 if (BoardData(e, y, x) && MaskData(e, y, x) == '1') {
1108 PosTable[count].e = e;
1109 PosTable[count].y = y;
1110 PosTable[count].x = x;
1111 PosTable[count].f = BoardData(e, y, x);
1112 count++;
1113 }
1114 }
1115 }
1116 }
1117
1118 // now lets randomise the faces, selecting 400 pairs at random and
1119 // swapping the faces.
1120 for (int ran = 0; ran < 400; ran++) {
1121 int pos1 = random.getLong(count);
1122 int pos2 = random.getLong(count);
1123
1124 if (pos1 == pos2) {
1125 continue;
1126 }
1127
1128 BYTE f = PosTable[pos1].f;
1129 PosTable[pos1].f = PosTable[pos2].f;
1130 PosTable[pos2].f = f;
1131 }
1132
1133 // put the rearranged tiles back.
1134 for (int p = 0; p < count; p++) {
1135 putTile(PosTable[p]);
1136 }
1137}
1138