• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdegames API Reference
  • KDE Home
  • Contact Us
 

kmahjongg

  • sources
  • kde-4.14
  • kdegames
  • kmahjongg
GameData.cpp
Go to the documentation of this file.
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 
25 GameData::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 
47 GameData::~GameData()
48 {
49 }
50 
51 void 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 
59 bool 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 
69 bool GameData::partTile(int z, int y, int x)
70 {
71  return (BoardData(z, y, x) != 0);
72 }
73 
74 UCHAR 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 
84 UCHAR 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 
94 void 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 
104 UCHAR 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 
114 void 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 
124 POSITION& 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 
134 void GameData::setMoveListData(short i, POSITION &value)
135 {
136  if ((i >= MoveList.size()) || (i < 0)) {
137  return ;
138  }
139 
140  MoveList[i] = value;
141 }
142 
143 void 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 
165 void 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 
233 int 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 
254 bool 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 
340 bool 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 
403 int 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 
445 void 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 
478 void 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 
549 bool 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 
647 void 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 
658 void 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 
727 bool isFlower(UCHAR Tile)
728 {
729  return (Tile >= TILE_FLOWER && Tile <=TILE_FLOWER + 3);
730 }
731 
732 bool isSeason(UCHAR Tile)
733 {
734  return (Tile >= TILE_SEASON && Tile <= TILE_SEASON + 3);
735 }
736 
737 bool isBamboo(UCHAR t)
738 {
739  return (t >= TILE_BAMBOO && t < TILE_BAMBOO + 9);
740 }
741 
742 bool isCharacter(UCHAR t)
743 {
744  return (t < TILE_CHARACTER + 9);
745 }
746 
747 bool isRod(UCHAR t)
748 {
749  return (t >= TILE_ROD && t < TILE_ROD + 9);
750 }
751 
752 bool isDragon(UCHAR t)
753 {
754  return (t >= TILE_DRAGON && t < TILE_DRAGON + 3);
755 }
756 
757 bool isWind(UCHAR t)
758 {
759  return (t >= TILE_WIND && t < TILE_WIND + 4);
760 }
761 
762 bool 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 
777 void 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 
824 void 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 
871 void 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 
884 bool 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 
951 int 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 
1005 short 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 
1047 bool 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 
1073 bool 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 
1098 void 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 }
GameData::putTile
void putTile(short e, short y, short x, UCHAR f)
Method Description.
Definition: GameData.cpp:51
GameData::generateTilePositions
void generateTilePositions()
Definition: GameData.cpp:143
GameData::m_height
short m_height
Definition: GameData.h:194
QByteArray
QByteArray::at
char at(int i) const
BoardLayout::m_height
int m_height
Member Description.
Definition: BoardLayout.h:206
GameData::generatePositionDepends
void generatePositionDepends()
Definition: GameData.cpp:165
QDataStream
GameData::initialiseRemovedTiles
void initialiseRemovedTiles()
Definition: GameData.cpp:871
isBamboo
bool isBamboo(UCHAR t)
Definition: GameData.cpp:737
TILE_SEASON
#define TILE_SEASON
Definition: KmTypes.h:68
pos::e
USHORT e
Member Description.
Definition: KmTypes.h:36
TILE_WIND
#define TILE_WIND
Definition: KmTypes.h:69
GameData::m_depth
short m_depth
Definition: GameData.h:195
BoardLayout::copyBoardLayout
void copyBoardLayout(UCHAR *to, unsigned short &numTiles)
Description.
Definition: BoardLayout.cpp:266
GameData::getMaskBytes
char * getMaskBytes()
Method Description.
Definition: GameData.h:166
isDragon
bool isDragon(UCHAR t)
Definition: GameData.cpp:752
GameData::GameData
GameData(BoardLayout *boardlayout)
Constructor.
Definition: GameData.cpp:25
GameData::clearRemovedTilePair
void clearRemovedTilePair(POSITION &a, POSITION &b)
Definition: GameData.cpp:824
TILE_FLOWER
#define TILE_FLOWER
Definition: KmTypes.h:71
GameData::setMoveListData
void setMoveListData(short i, POSITION &value)
Method Description.
Definition: GameData.cpp:134
GameData::MoveListData
POSITION & MoveListData(short i)
Method Description.
Definition: GameData.cpp:124
TILE_DRAGON
#define TILE_DRAGON
Definition: KmTypes.h:70
GameData::~GameData
~GameData()
Default Destructor.
Definition: GameData.cpp:47
isCharacter
bool isCharacter(UCHAR t)
Definition: GameData.cpp:742
GameData::generateStartPosition2
bool generateStartPosition2()
Definition: GameData.cpp:549
pos::x
USHORT x
Member Description.
Definition: KmTypes.h:38
GameData::MaskData
UCHAR MaskData(short z, short y, short x)
Method Description.
Definition: GameData.cpp:74
GameData::BoardData
UCHAR BoardData(short z, short y, short x)
Method Description.
Definition: GameData.cpp:104
BoardLayout::m_width
int m_width
Member Description.
Definition: BoardLayout.h:205
GameData.h
GameData::allow_redo
int allow_redo
Definition: GameData.h:187
GameData::saveToStream
bool saveToStream(QDataStream &out)
Definition: GameData.cpp:1073
GameData::m_maxTiles
short m_maxTiles
Definition: GameData.h:196
GameData::MaxTileNum
USHORT MaxTileNum
Definition: GameData.h:190
GameData::loadFromStream
bool loadFromStream(QDataStream &in)
Definition: GameData.cpp:1047
pos::y
USHORT y
Member Description.
Definition: KmTypes.h:37
GameData::TileNum
USHORT TileNum
Definition: GameData.h:189
GameData::moveCount
int moveCount()
Definition: GameData.cpp:951
pos
struct pos POSITION
Definition: KmTypes.h:34
GameData::findAllMatchingTiles
short findAllMatchingTiles(POSITION &posA)
Definition: GameData.cpp:1005
GameData::tilePresent
bool tilePresent(int z, int y, int x)
Method Description.
Definition: GameData.cpp:59
GameData::isMatchingTile
bool isMatchingTile(POSITION &Pos1, POSITION &Pos2)
Definition: GameData.cpp:762
isRod
bool isRod(UCHAR t)
Definition: GameData.cpp:747
QVector
GameData::shuffle
void shuffle()
Definition: GameData.cpp:1098
BoardLayout::m_depth
int m_depth
Member Description.
Definition: BoardLayout.h:207
GameData::random
KRandomSequence random
Definition: GameData.h:198
GameData::setBoardData
void setBoardData(short z, short y, short x, UCHAR value)
Method Description.
Definition: GameData.cpp:114
GameData::findMove
bool findMove(POSITION &posA, POSITION &posB)
Definition: GameData.cpp:884
BoardLayout
This class implements methods for loading and manipulating board.
Definition: BoardLayout.h:36
TILE_ROD
#define TILE_ROD
Definition: KmTypes.h:67
UCHAR
unsigned char UCHAR
Definition: KmTypes.h:25
GameData::HighlightData
UCHAR HighlightData(short z, short y, short x)
Method Description.
Definition: GameData.cpp:84
GameData::setHighlightData
void setHighlightData(short z, short y, short x, UCHAR value)
Method Description.
Definition: GameData.cpp:94
isWind
bool isWind(UCHAR t)
Definition: GameData.cpp:757
GameData::partTile
bool partTile(int z, int y, int x)
Method Description.
Definition: GameData.cpp:69
GameData::allow_undo
int allow_undo
Definition: GameData.h:186
isFlower
bool isFlower(UCHAR Tile)
Definition: GameData.cpp:727
GameData::m_width
short m_width
Definition: GameData.h:193
isSeason
bool isSeason(UCHAR Tile)
Definition: GameData.cpp:732
pos::f
USHORT f
Member Description.
Definition: KmTypes.h:39
TILE_BAMBOO
#define TILE_BAMBOO
Definition: KmTypes.h:66
TILE_CHARACTER
#define TILE_CHARACTER
Definition: KmTypes.h:65
BYTE
unsigned char BYTE
Definition: KmTypes.h:26
GameData::setRemovedTilePair
void setRemovedTilePair(POSITION &a, POSITION &b)
Definition: GameData.cpp:777
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:18:33 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kmahjongg

Skip menu "kmahjongg"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdegames API Reference

Skip menu "kdegames API Reference"
  • granatier
  • kapman
  • kblackbox
  • kgoldrunner
  • kigo
  • kmahjongg
  • KShisen
  • ksquares
  • libkdegames
  •   highscore
  •   libkdegamesprivate
  •     kgame
  • libkmahjongg
  • palapeli
  •   libpala

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal