File indexing completed on 2024-04-14 03:59:49

0001 /*
0002     SPDX-FileCopyrightText: 1997 Mathias Mueller <in5y158@public.uni-hamburg.de>
0003     SPDX-FileCopyrightText: 2006 Mauricio Piacentini <mauricio@tabuleiro.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // own
0009 #include "boardlayout.h"
0010 
0011 // Qt
0012 #include <QFile>
0013 #include <QTextStream>
0014 
0015 namespace {
0016 inline QString layoutMagic1_0() { return QStringLiteral("kmahjongg-layout-v1.0"); }
0017 inline QString layoutMagic1_1() { return QStringLiteral("kmahjongg-layout-v1.1"); }
0018 }
0019 
0020 BoardLayout::BoardLayout()
0021 {
0022     m_filename.clear();
0023     m_width = 32;
0024     m_height = 16;
0025     m_depth = 5;
0026     m_board = QByteArray(m_width * m_height * m_depth, 0);
0027     clearBoardLayout();
0028 }
0029 
0030 BoardLayout::BoardLayout(const BoardLayout & boardLayout)
0031 {
0032     m_width = boardLayout.m_width;
0033     m_height = boardLayout.m_height;
0034     m_depth = boardLayout.m_depth;
0035     m_maxTiles = boardLayout.m_maxTiles;
0036     m_maxTileNum = boardLayout.m_maxTileNum;
0037     m_filename = boardLayout.m_filename;
0038     m_board = boardLayout.m_board;
0039     m_loadedBoard = boardLayout.m_loadedBoard;
0040 }
0041 
0042 BoardLayout::~BoardLayout()
0043 {
0044 }
0045 
0046 void BoardLayout::clearBoardLayout()
0047 {
0048     m_loadedBoard.clear();
0049     initialiseBoard();
0050 }
0051 
0052 bool BoardLayout::saveBoardLayout(const QString & where) const
0053 {
0054     QFile f(where);
0055     if (!f.open(QIODevice::ReadWrite)) {
0056         return false;
0057     }
0058 
0059     QByteArray tmp = layoutMagic1_1().toUtf8();
0060     if (f.write(tmp) == -1) {
0061         return false;
0062     }
0063 
0064     tmp = QStringLiteral("\nw%1").arg(m_width).toUtf8();
0065     if (f.write(tmp) == -1) {
0066         return false;
0067     }
0068 
0069     tmp = QStringLiteral("\nh%1").arg(m_height).toUtf8();
0070     if (f.write(tmp) == -1) {
0071         return false;
0072     }
0073 
0074     tmp = QStringLiteral("\nd%1").arg(m_depth).toUtf8();
0075     if (f.write(tmp) == -1) {
0076         return false;
0077     }
0078 
0079     for (int z = 0; z < m_depth; ++z) {
0080         for (int y = 0; y < m_height; ++y) {
0081             if (!f.putChar('\n')) {
0082                 return false;
0083             }
0084 
0085             for (int x = 0; x < m_width; ++x) {
0086                 if (getBoardData(z, y, x)) {
0087                     if (!f.putChar(getBoardData(z, y, x))) {
0088                         return false;
0089                     }
0090                 } else if (!f.putChar('.')) {
0091                     return false;
0092                 }
0093             }
0094         }
0095     }
0096     return f.putChar('\n');
0097 }
0098 
0099 bool BoardLayout::loadBoardLayout_10(const QString & from)
0100 {
0101     if (from == m_filename) {
0102         return true;
0103     }
0104 
0105     QFile f(from);
0106 
0107     if (f.open(QIODevice::ReadOnly)) {
0108         QTextStream t(&f);
0109         QString s(t.readLine());
0110 
0111         if (s != layoutMagic1_0()) {
0112             f.close();
0113             return false;
0114         }
0115 
0116         //version 1.0 layouts used hardcoded board dimensions
0117         m_width = 32;
0118         m_height = 16;
0119         m_depth = 5;
0120         int lines = 0;
0121         QString all;
0122 
0123         while (!t.atEnd()) {
0124             s = t.readLine();
0125             if (s[0] == QLatin1Char('#')) {
0126                 continue;
0127             }
0128             all += s;
0129             ++lines;
0130         }
0131         f.close();
0132 
0133         if (all.length() == m_width * m_height * m_depth) {
0134             m_loadedBoard = all.toLatin1();
0135             initialiseBoard();
0136             m_filename = from;
0137             return true;
0138         } else {
0139             return false;
0140         }
0141         return true;
0142     } else {
0143         return false;
0144     }
0145 }
0146 
0147 bool BoardLayout::loadBoardLayout(const QString & from)
0148 {
0149     if (from == m_filename) {
0150         return true;
0151     }
0152 
0153     QFile f(from);
0154     if (f.open(QIODevice::ReadOnly)) {
0155         QTextStream t(&f);
0156         QString s(t.readLine());
0157 
0158         if (s != layoutMagic1_1()) {
0159             f.close();
0160             //maybe a version 1_0 layout?
0161             return (loadBoardLayout_10(from));
0162         }
0163 
0164         int lines = 0;
0165         m_width = m_height = m_depth = 0;
0166         QString all;
0167 
0168         while (!t.atEnd()) {
0169             s = t.readLine();
0170             if (s[0] == QLatin1Char('#')) {
0171                 continue;
0172             }
0173             if (s[0] == QLatin1Char('w')) {
0174                 m_width = QStringView(s).mid(1).toInt();
0175                 continue;
0176             }
0177             if (s[0] == QLatin1Char('h')) {
0178                 m_height = QStringView(s).mid(1).toInt();
0179                 continue;
0180             }
0181             if (s[0] == QLatin1Char('d')) {
0182                 m_depth = QStringView(s).mid(1).toInt();
0183                 continue;
0184             }
0185             all += s;
0186             ++lines;
0187         }
0188         f.close();
0189 
0190         if ((m_width > 0) && (m_height > 0) && (m_depth > 0)
0191             && (all.length() == m_width * m_height * m_depth)) {
0192             m_loadedBoard = all.toLatin1();
0193             initialiseBoard();
0194             m_filename = from;
0195             return true;
0196         } else {
0197             return false;
0198         }
0199         return true;
0200     } else {
0201         return false;
0202     }
0203 }
0204 
0205 void BoardLayout::initialiseBoard()
0206 {
0207     short z = 0;
0208     short x = 0;
0209     short y = 0;
0210     m_maxTileNum = 0;
0211 
0212     m_maxTiles = (m_width * m_height * m_depth) / 4;
0213     m_board.resize(m_width * m_height * m_depth);
0214     m_board.fill(0);
0215 
0216     if (m_loadedBoard.isEmpty()) {
0217         return;
0218     }
0219 
0220     int idx = 0;
0221 
0222     while (true) {
0223         BYTE c = m_loadedBoard.at(idx++);
0224         switch (c) {
0225             case static_cast<UCHAR>('1'):
0226                 ++m_maxTileNum;
0227                 [[fallthrough]];
0228             case static_cast<UCHAR>('2'):
0229             case static_cast<UCHAR>('3'):
0230             case static_cast<UCHAR>('4'):
0231                 setBoardData(z, y, x, c);
0232                 break;
0233 
0234             default:
0235                 setBoardData(z, y, x, 0);
0236                 break;
0237         }
0238         if (++x == m_width) {
0239             x = 0;
0240 
0241             if (++y == m_height) {
0242                 y = 0;
0243 
0244                 if (++z == m_depth) {
0245                     // number of tiles have to be even
0246                     if (m_maxTileNum & 1) {
0247                         break;
0248                     }
0249                     return;
0250                 }
0251             }
0252         }
0253     }
0254 }
0255 
0256 void BoardLayout::copyBoardLayout(UCHAR * to, unsigned short & n) const
0257 {
0258     memcpy(to, m_board.data(), m_width * m_height * m_depth);
0259     n = m_maxTileNum;
0260 }
0261 
0262 void BoardLayout::shiftLeft()
0263 {
0264     // Do not allow tiles to be shifted off the board.
0265     for (int y = 0; y < m_height - 1; ++y) {
0266         if (getBoardData(0, y, 0) == '1') {
0267             return;
0268         }
0269     }
0270 
0271     for (int z = 0; z < m_depth; ++z) {
0272         for (int y = 0; y < m_height; ++y) {
0273             for (int x = 0; x < m_width - 1; ++x) {
0274                 setBoardData(z, y, x, getBoardData(z, y, x + 1));
0275             }
0276             setBoardData(z, y, m_width - 1, 0);
0277         }
0278     }
0279 }
0280 
0281 
0282 void BoardLayout::shiftRight()
0283 {
0284     // Do not allow tiles to be shifted off the board.
0285     for (int y = 0; y < m_height - 1; ++y) {
0286         if (getBoardData(0, y, m_width - 2) == '1') {
0287             return;
0288         }
0289     }
0290 
0291     for (int z = 0; z < m_depth; ++z) {
0292         for (int y = 0; y < m_height; ++y) {
0293             for (int x = m_width - 1; x > 0; --x) {
0294                 setBoardData(z, y, x, getBoardData(z, y, x - 1));
0295             }
0296             setBoardData(z, y, 0, 0);
0297         }
0298     }
0299 }
0300 void BoardLayout::shiftUp()
0301 {
0302     // Do not allow tiles to be shifted off the board.
0303     for (int x = 0; x < m_width - 1; ++x) {
0304         if (getBoardData(0, 0, x) == '1') {
0305             return;
0306         }
0307     }
0308 
0309     for (int z = 0; z < m_depth; ++z) {
0310         for (int y = 0; y < m_height - 1; ++y) {
0311             for (int x = 0; x < m_width; ++x) {
0312                 setBoardData(z, y, x, getBoardData(z, y + 1, x));
0313             }
0314         }
0315     }
0316 
0317     // Clear row m_height - 1
0318     for (int z = 0; z < m_depth; ++z) {
0319         for (int x = 0; x < m_width; ++x) {
0320             setBoardData(z, m_height - 1, x, 0);
0321         }
0322     }
0323 }
0324 
0325 void BoardLayout::shiftDown()
0326 {
0327     // Do not allow tiles to be shifted off the board.
0328     for (int x = 0; x < m_width - 1; ++x) {
0329         if (getBoardData(0, m_height - 2, x) == '1') {
0330             return;
0331         }
0332     }
0333 
0334     for (int z = 0; z < m_depth; ++z) {
0335         for (int y = m_height - 1; y > 0; --y) {
0336             for (int x = 0; x < m_width; ++x) {
0337                 setBoardData(z, y, x, getBoardData(z, y - 1, x));
0338             }
0339         }
0340     }
0341 
0342     // Clear row 0
0343     for (int z = 0; z < m_depth; ++z) {
0344         for (int x = 0; x < m_width; ++x) {
0345             setBoardData(z, 0, x, 0);
0346         }
0347     }
0348 }
0349 
0350 
0351 bool BoardLayout::tileAbove(short z, short y, short x) const
0352 {
0353     if (z >= m_depth - 1) {
0354         return false;
0355     }
0356 
0357     if (getBoardData(z + 1, y, x) || getBoardData(z + 1, y + 1, x) || getBoardData(z + 1, y, x + 1) || getBoardData(z + 1, y + 1, x + 1)) {
0358         return true;
0359     }
0360 
0361     return false;
0362 }
0363 
0364 void BoardLayout::deleteTile(POSITION & p)
0365 {
0366     if (p.z < m_depth && getBoardData(p.z, p.y, p.x) == '1') {
0367         setBoardData(p.z, p.y, p.x, 0);
0368         setBoardData(p.z, p.y, p.x + 1, 0);
0369         setBoardData(p.z, p.y + 1, p.x, 0);
0370         setBoardData(p.z, p.y + 1, p.x + 1, 0);
0371         --m_maxTileNum;
0372     }
0373 }
0374 
0375 bool BoardLayout::anyFilled(POSITION & p) const
0376 {
0377     return (getBoardData(p.z, p.y, p.x) != 0 || getBoardData(p.z, p.y, p.x + 1) != 0 || getBoardData(p.z, p.y + 1, p.x) != 0 || getBoardData(p.z, p.y + 1, p.x + 1) != 0);
0378 }
0379 
0380 bool BoardLayout::allFilled(POSITION & p) const
0381 {
0382     return (getBoardData(p.z, p.y, p.x) != 0 && getBoardData(p.z, p.y, p.x + 1) != 0 && getBoardData(p.z, p.y + 1, p.x) != 0 && getBoardData(p.z, p.y + 1, p.x + 1) != 0);
0383 }
0384 
0385 void BoardLayout::insertTile(POSITION & p)
0386 {
0387     setBoardData(p.z, p.y, p.x, '1');
0388     setBoardData(p.z, p.y, p.x + 1, '2');
0389     setBoardData(p.z, p.y + 1, p.x + 1, '3');
0390     setBoardData(p.z, p.y + 1, p.x, '4');
0391 }
0392 
0393 UCHAR BoardLayout::getBoardData(short z, short y, short x) const
0394 {
0395     return m_board.at((z * m_width * m_height) + (y * m_width) + x);
0396 }
0397 
0398 void BoardLayout::setBoardData(short z, short y, short x, UCHAR value)
0399 {
0400     m_board[(z * m_width * m_height) + (y * m_width) + x] = value;
0401 }