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 }