Warning, file /games/ksudoku/src/logic/skgraph.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /***************************************************************************
0002  *   Copyright 2005-2007 Francesco Rossi <redsh@email.it>                  *
0003  *   Copyright 2006-2007 Mick Kappenburg <ksudoku@kappendburg.net>         *
0004  *   Copyright 2006-2008 Johannes Bergmeier <johannes.bergmeier@gmx.net>   *
0005  *   Copyright 2012      Ian Wadham <iandw.au@gmail.com>                   *
0006  *   Copyright 2015      Ian Wadham <iandw.au@gmail.com>                   *
0007  *                                                                         *
0008  *   This program is free software; you can redistribute it and/or modify  *
0009  *   it under the terms of the GNU General Public License as published by  *
0010  *   the Free Software Foundation; either version 2 of the License, or     *
0011  *   (at your option) any later version.                                   *
0012  *                                                                         *
0013  *   This program is distributed in the hope that it will be useful,       *
0014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0016  *   GNU General Public License for more details.                          *
0017  *                                                                         *
0018  *   You should have received a copy of the GNU General Public License     *
0019  *   along with this program; if not, write to the                         *
0020  *   Free Software Foundation, Inc.,                                       *
0021  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0022  ***************************************************************************/
0023 
0024 #include "skgraph.h"
0025 
0026 #include <QMultiMap>
0027 
0028 SKGraph::SKGraph(int order, ksudoku::GameType type)
0029 {
0030     m_order = order;
0031     m_base  = 3;
0032     for (int n = 2; n <= 5; n++) {
0033         if (m_order == n * n) {
0034         m_base = n;
0035         }
0036     }
0037     m_type = type;
0038 }
0039 
0040 SKGraph::~SKGraph()
0041 {
0042     clearCages();
0043 }
0044 
0045 void SKGraph::initSudoku()
0046 {
0047     m_name = QStringLiteral("PlainSudoku");
0048     m_specificType = Plain;
0049 
0050     m_sizeX = m_order;
0051     m_sizeY = m_order;
0052     m_sizeZ = 1;
0053     m_emptyBoard.fill (UNUSABLE, size());
0054     initSudokuGroups(0, true);
0055     indexCellsToCliques();
0056 }
0057 
0058 void SKGraph::initSudokuGroups(int pos, bool withBlocks)
0059 {
0060     // initSudokuGroups() sets up rows and columns in a Sudoku grid of size
0061     // (m_order*m_order) cells. Its first parameter (usually 0) shows where
0062     // on the whole board the grid goes. This is relevant in Samurai and
0063     // related layouts. Its second attribute is true if square-block groups
0064     // are required or false if not (e.g. as in a Jigsaw or Mathdoku type).
0065 
0066     m_structures << SudokuGroups << pos << (withBlocks ? 1 : 0);
0067 
0068     QList<int> rowc, colc, blockc;
0069     for(int i = 0; i < m_order; ++i) {
0070         rowc.clear();
0071         colc.clear();
0072         blockc.clear();
0073         
0074         for(int j = 0; j < m_order; ++j)
0075         {
0076             rowc   << pos + j*m_sizeY + i;
0077             colc   << pos + i*m_sizeY + j;
0078             blockc << pos + ((i/m_base)*m_base + j%m_base) * m_sizeY                      + (i%m_base)*m_base + j/m_base;
0079         }
0080         addClique(rowc);
0081         addClique(colc);
0082         if (withBlocks) {
0083             addClique(blockc);
0084         }
0085     }
0086 }
0087 
0088 void SKGraph::initRoxdoku()
0089 {
0090     m_name = QStringLiteral("Roxdoku");
0091     m_specificType = Roxdoku;
0092 
0093     m_sizeX = m_base;
0094     m_sizeY = m_base;
0095     m_sizeZ = m_base;
0096     m_emptyBoard.fill (UNUSABLE, size());
0097     initRoxdokuGroups(0);
0098     indexCellsToCliques();
0099 }
0100 
0101 void SKGraph::initRoxdokuGroups(int pos)
0102 {
0103     // initRoxdokuGroups() sets up the intersecting planes in a
0104     // 3-D Roxdoku grid. Its only parameter shows where in the entire
0105     // three-dimensional layout the grid goes.
0106 
0107     m_structures << RoxdokuGroups << pos << 1;
0108 
0109     QList<int> xFace, yFace, zFace;
0110     int x = cellPosX(pos);
0111     int y = cellPosY(pos);
0112     int z = cellPosZ(pos);
0113 
0114     for (int i = 0; i < m_base; i++) {
0115         xFace.clear();
0116         yFace.clear();
0117         zFace.clear();
0118                 for (int j = 0; j < m_base; j++) {
0119                     for (int k = 0; k < m_base; k++) {
0120             // Intersecting faces at relative (0,0,0), (1,1,1), etc.
0121             xFace << cellIndex (x + i, y + j, z + k);
0122             yFace << cellIndex (x + k, y + i, z + j);
0123             zFace << cellIndex (x + j, y + k, z + i);
0124             }
0125         }
0126         addClique(xFace);
0127         addClique(yFace);
0128         addClique(zFace);
0129     }
0130 }
0131 
0132 void SKGraph::addCliqueStructure(const QList<int> &data) {
0133 
0134     m_structures << Clique << m_cliques.count() << 0;
0135 
0136     addClique(data);
0137 }
0138 
0139 void SKGraph::addClique(const QList<int> &data) {
0140     // Add to the cliques (groups) list.
0141     m_cliques << data;
0142     for (int n = 0; n < data.size(); n++) {
0143         // Set cells in groups VACANT: cells not in groups are UNUSABLE.
0144         m_emptyBoard [data.at(n)] = VACANT;
0145     }
0146 }
0147 
0148 void SKGraph::addCage(const QList<int> &cage, CageOperator cageOperator,
0149                       int cageValue)
0150 {
0151     // Add to the cages list.
0152     m_cages.append (new Cage);
0153     Cage * newCage        = m_cages.last();
0154     newCage->cage         = cage;
0155     newCage->cageOperator = cageOperator;
0156     newCage->cageValue    = cageValue;
0157 
0158     // Calculate cageTopLeft cell (used for displaying operator and value).
0159     int topY              = m_order;
0160     int leftX             = m_order;
0161     newCage->cageTopLeft  = 0;
0162     for (const int cell : cage) {
0163         if (cellPosY(cell) > topY) {
0164         continue;       // Below the best so far.
0165         }
0166         else if ((cellPosY(cell) == topY) && (cellPosX(cell) > leftX)) {
0167         continue;       // Same height as best but to the right.
0168         }
0169         newCage->cageTopLeft = cell;
0170         topY  = cellPosY(cell);
0171         leftX = cellPosX(cell);
0172     }
0173 }
0174 
0175 void SKGraph::dropCage(int cageNum)
0176 {
0177     if (cageNum >= m_cages.count()) {
0178         return;
0179     }
0180     delete m_cages.at (cageNum);
0181     m_cages.remove (cageNum);
0182 }
0183 
0184 void SKGraph::clearCages() {
0185     // Clear previous cages (if any).
0186     if (! m_cages.isEmpty()) {
0187         qDeleteAll (m_cages);
0188         m_cages.clear();
0189     }
0190 }
0191 
0192 void SKGraph::initCustom(const QString & name, SudokuType specificType,
0193                 int order, int sizeX, int sizeY, int sizeZ) {
0194     // The Serializer's deserializer methods will add groups (or cliques).
0195     m_name = name;
0196     m_specificType = specificType;
0197 
0198     m_order = order;
0199     m_sizeX = sizeX;
0200     m_sizeY = sizeY;
0201     m_sizeZ = sizeZ;
0202     m_emptyBoard.fill (UNUSABLE, size());
0203 }
0204 
0205 void SKGraph::endCustom()
0206 {
0207     indexCellsToCliques();
0208 }
0209 
0210 void SKGraph::indexCellsToCliques()
0211 {
0212     QMultiMap<int, int> cellsToCliques;
0213     int nCliques = cliqueCount();
0214     int nCells   = size();
0215     for (int g = 0; g < nCliques; g++) {
0216         QList<int> cellList = clique(g);
0217         for (int n = 0; n < m_order; n++) {
0218         cellsToCliques.insert (cellList.at (n), g);
0219         }
0220     }
0221 
0222     m_cellIndex.fill   (0, nCells + 1);
0223     m_cellCliques.fill (0, nCliques * m_order);
0224     int index = 0;
0225     for (int cell = 0; cell < nCells; cell++) {
0226         m_cellIndex [cell] = index;
0227         const QList<int> cliqueList = cellsToCliques.values (cell);
0228         for (int g : cliqueList) {
0229         m_cellCliques [index] = g;
0230         index++;
0231         }
0232     }
0233     m_cellIndex [nCells] = index;
0234 }
0235 
0236 const QList<int> SKGraph::cliqueList(int cell) const
0237 {
0238     // NOTE: We could have used QMultiMap<int, int>::values(int cell) to
0239     // access this index, but the index's main usage is on an inner loop
0240     // of the generator/solver and execution time is a concern there.
0241 
0242     QList<int> cells;
0243     int start = m_cellIndex.at(cell);
0244     int end   = m_cellIndex.at(cell + 1);
0245     for (int n = start; n < end; n++) {
0246         cells << m_cellCliques.at(n);
0247     }
0248     return cells;
0249 }