Commit ec4d81a9 authored by Ian Wadham's avatar Ian Wadham

Integrate the old KSudoku and the new generator/solver more closely.

Both now use the same data structures for puzzle and solution contents.  The SKGraph class is now the central data-structure used by views, generator, solver, save file, load file and manual puzzle entry.  It represents the type, size, shape and rules of a Sudoku puzzle, either in 2D or 3D, and has been streamlined and simplified. The PlainSudokuBoard, RoxdokuBoard and SamuraiBoard classes that inherited SudokuBoard (the generator/solver) have been deleted. SudokuBoard now works entirely by using SKGraph.

svn path=/trunk/KDE/kdegames/ksudoku/; revision=1274982
parent ea727a30
......@@ -9,7 +9,7 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}/gui/views
# ${CMAKE_CURRENT_BINARY_DIR}/gui/export
# ${CMAKE_CURRENT_BINARY_DIR}/gui/export/draw
# ${CMAKE_CURRENT_BINARY_DIR}/logic
${CMAKE_CURRENT_BINARY_DIR}/logic
${CMAKE_CURRENT_SOURCE_DIR}/generator
)
......@@ -20,9 +20,6 @@ endif(OPENGL_SUPPORT)
set(ksudoku_SRCS
main.cpp
generator/sudokuboard.cpp
generator/plainsudokuboard.cpp
generator/roxdokuboard.cpp
generator/samuraiboard.cpp
generator/state.cpp
)
......
########### next target ###############
set(mysudoku_SRCS
main.cpp
sudoku.cpp
sudokuboard.cpp
plainsudokuboard.cpp
samuraiboard.cpp
roxdokuboard.cpp
state.cpp
)
# kde4_add_app_icon(kgoldrunner_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/hi*-app-kgoldrunner.png")
kde4_add_executable(mysudoku ${mysudoku_SRCS})
target_link_libraries(mysudoku ${KDE4_KDEUI_LIBS} kdegames)
install(TARGETS mysudoku ${INSTALL_TARGETS_DEFAULT_ARGS} )
########### install files ###############
# install( PROGRAMS KGoldrunner.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
# install( FILES kgoldrunnerui.rc DESTINATION ${DATA_INSTALL_DIR}/kgoldrunner )
# kde4_install_icons( ${ICON_INSTALL_DIR} )
/****************************************************************************
* Copyright 2011 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************/
#include "debug.h"
#include "plainsudokuboard.h"
#include <stdio.h>
PlainSudokuBoard::PlainSudokuBoard (QObject * parent,
SudokuType sudokuType, int blockSize)
:
SudokuBoard (parent, sudokuType, blockSize)
{
dbgLevel = 1;
// Set where to fill the first block in PlainSudokuBoard::fillBoard().
// Central block for blockSize 3 or 5: below and right of centre for 2 or 4.
m_fillStartRow = blockSize * (blockSize / 2);
m_fillStartCol = m_fillStartRow;
}
void PlainSudokuBoard::setUpBoard()
{
m_boardSize = m_order;
m_boardArea = m_gridArea; // Area of the whole board.
// Create an empty board of the required size.
clear (m_currentValues);
// Make a list of groups that must satisfy Sudoku rules. These are rows,
// columns and blocks with m_order values in each. Note that the
// XSudokuBoard derived class starts m_nGroups at 2, rather than 0, to
// allow for the two diagonals and the RoxdokuBoard derived class (3D
// puzzle) calculates its own particular value of m_nGroups.
m_nGroups = m_nGroups + 3 * m_order;
m_groupSize = m_order;
m_groupList.fill (0, m_nGroups * m_groupSize);
qint32 index = 0;
index = makeRowColIndex (0, 0, index);
index = makeBlockIndex (index);
dbo1 "The index reached %d.\n", index);
// Make the solver faster (see SudokuBoard::updateValueRequirements()).
indexCellsToGroups();
dbo1 "Block %dx%d = %d, grid %dx%d = %d, board %dx%d = %d\n",
m_blockSize, m_blockSize, m_blockSize * m_blockSize,
m_order, m_order, m_gridArea,
m_boardSize, m_boardSize, m_boardArea);
dbo1 "There are %d groups of %d cells each.\n", m_nGroups, m_groupSize);
dbo2 "Groups are:\n");
for (int g = 0; g < m_nGroups; g++) {
for (int n = 0; n < m_groupSize; n++) {
dbo2 " %3d", m_groupList.at (g * m_groupSize + n));
}
dbo2 "\n");
}
}
void PlainSudokuBoard::clear (BoardContents & boardValues)
{
// Create an empty board of the required size.
boardValues.fill (0, m_boardArea);
}
BoardContents & PlainSudokuBoard::fillBoard()
{
// Solve the empty board, thus filling it with values at random. These
// values can be the starting point for generating a puzzle and also the
// final solution of that puzzle.
QVector<int> sequence (m_order);
randomSequence (sequence);
clear (m_currentValues);
// Fill a central block with values 1 to m_order in random sequence. This
// reduces the solver time considerably, especially if blockSize is 4 or 5.
int row, col;
for (int n = 0; n < m_order; n++) {
row = m_fillStartRow + (n / m_blockSize);
col = m_fillStartCol + (n % m_blockSize);
m_currentValues [row * m_order + col] = sequence.at (n) + 1;
}
solveBoard (m_currentValues);
dbo "BOARD FILLED\n");
return m_currentValues;
}
qint32 PlainSudokuBoard::makeRowColIndex (int i, int j, qint32 index)
{
dbo1 "Make row/col index on grid at row %d col %d, index %d.\n",
i, j, index);
qint32 offset = index;
int topLeft = i * m_boardSize + j;
int cell = topLeft;
// Make an index of the rows in this grid.
for (int row = 0; row < m_order; row++) {
for (int col = 0; col < m_order; col++) {
m_groupList [offset] = cell;
// dbo2 "Index row %d, col %d, value %d at offset %d.\n",
// row, col, cell, offset);
cell++;
offset++;
}
cell = cell + m_boardSize - m_order;
}
// Make an index of the columns in this grid.
cell = topLeft++;
for (int col = 0; col < m_order; col++) {
for (int row = 0; row < m_order; row++) {
m_groupList [offset] = cell;
// dbo2 "Index col %d, row %d, value %d at offset %d.\n",
// col, row, cell, offset);
cell = cell + m_boardSize;
offset++;
}
cell = topLeft++;
}
return offset;
}
qint32 PlainSudokuBoard::makeBlockIndex (qint32 index)
{
dbo1 "Make block index on whole board, index %d.\n", index);
qint32 offset = index;
// Index the blocks in the board from left to right, then top to bottom.
// Note: The blocks must be square, but an index like this could be
// constructed, in principle, for irregular blocks, as in Jigsaw Sudoku.
for (int row = 0; row < m_boardSize; row = row + m_blockSize) {
for (int col = 0; col < m_boardSize; col = col + m_blockSize) {
int topLeft = row * m_boardSize + col;
offset = indexSquareBlock (offset, topLeft);
}
}
return offset;
}
XSudokuBoard::XSudokuBoard (QObject * parent,
SudokuType sudokuType, int blockSize)
:
PlainSudokuBoard (parent, sudokuType, blockSize)
{
m_nGroups = 2; // Allow two more groups for the diagonals.
}
qint32 XSudokuBoard::makeBlockIndex (qint32 index)
{
// Make a normal Sudoku block index.
qint32 offset = PlainSudokuBoard::makeBlockIndex (index);
// Add cells on the diagonals to the group-indexes.
dbo1 "XSudoku: Index the cells on the diagonals, index %d.\n", index);
int cell_1 = 0;
int cell_2 = m_order - 1;
offset = offset + m_order; // 2 * m_order cells get added to the list.
for (int n = 0; n < m_order; n++) {
m_groupList [offset - m_order] = cell_1;
m_groupList [offset] = cell_2;
cell_1 = cell_1 + m_order + 1;
cell_2 = cell_2 + m_order - 1;
offset++;
}
return offset;
}
JigsawBoard::JigsawBoard (QObject * parent,
SudokuType sudokuType, int blockSize)
:
PlainSudokuBoard (parent, sudokuType, blockSize)
{
}
qint32 JigsawBoard::makeBlockIndex (qint32 index)
{
dbo1 "Make Jigsaw block index on whole board, index %d\n", index);
// Nine blocks. The central block is square, the outside eight have
// interlocking tabs and sockets (holes), like a jigsaw. The cell numbers
// run from left-to-right and then top-to-bottom.
const int blocks[] = { 0, 1, 2, 9, 10, 11, 18, 12, 20, // Tab 12, hole 19
3, 4, 5, 15, 13, 14, 21, 22, 23, // Tab 15, hole 12
6, 7, 8, 34, 16, 17, 24, 25, 26, // Tab 34, hole 15
27, 28, 29, 36, 37, 38, 45, 19, 47, // Tab 19, hole 46
30, 31, 32, 39, 40, 41, 48, 49, 50, // Central square
33, 61, 35, 42, 43, 44, 51, 52, 53, // Tab 61, hole 34
54, 55, 56, 63, 64, 46, 72, 73, 74, // Tab 46, hole 65
57, 58, 59, 66, 67, 65, 75, 76, 77, // Tab 65, hole 68
60, 68, 62, 69, 70, 71, 78, 79, 80 // Tab 68, hole 61
};
qint32 nCells = m_order * m_order;
// Copy the blocks into the block index.
for (int n = 0; n < nCells; n++) {
m_groupList [index + n] = blocks [n];
}
return (index + nCells);
}
AztecBoard::AztecBoard (QObject * parent,
SudokuType sudokuType, int blockSize)
:
PlainSudokuBoard (parent, sudokuType, blockSize)
{
}
qint32 AztecBoard::makeBlockIndex (qint32 index)
{
dbo1 "Make Aztec block index on whole board, index %d\n", index);
// Nine blocks. The central block is square, the outside eight have
// interlocking tabs and sockets (holes), like a jigsaw. The cell numbers
// run from left-to-right and then top-to-bottom.
const int blocks[] = { 0, 1, 9, 10, 11, 19, 20, 21, 29, // Top left
2, 3, 4, 5, 6, 12, 13, 14, 22, // Top middle
7, 8, 15, 16, 17, 23, 24, 25, 33, // Top right
18, 27, 28, 36, 37, 38, 45, 46, 54, // Middle left
30, 31, 32, 39, 40, 41, 48, 49, 50, // Central square
26, 34, 35, 42, 43, 44, 52, 53, 62, // Middle right
47, 55, 56, 57, 63, 64, 65, 72, 73, // Bottom left
58, 66, 67, 68, 74, 75, 76, 77, 78, // Bottom middle
51, 59, 60, 61, 69, 70, 71, 79, 80 // Bottom right
};
qint32 nCells = m_order * m_order;
// Copy the blocks into the block index.
for (int n = 0; n < nCells; n++) {
m_groupList [index + n] = blocks [n];
}
return (index + nCells);
}
#include "plainsudokuboard.moc"
/****************************************************************************
* Copyright 2011 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************/
#ifndef PLAINSUDOKUBOARD_H
#define PLAINSUDOKUBOARD_H
#include "sudokuboard.h"
/**
* @class PlainSudokuBoard plainsudokuboard.h
* @short Data-structures and methods for handling plain Sudoku puzzles.
*
* A plain or classic Sudoku puzzle has a single square grid containing smaller
* blocks of n x n cells. The overall grid also has n x n rows and n x n
* columns and the rows, columns and blocks must be filled with numbers in
* the range 1 to n x n. In the classic form, n = 3 and numbers 1 to 9 must
* be used in the solution. The grid has 9 rows, 9 columns and 9 blocks. The
* PlainSudokuBoard class can also handle 2 x 2, 4 x 4 and 5 x 5.
*/
class PlainSudokuBoard : public SudokuBoard
{
Q_OBJECT
public:
/**
* Constructs a new PlainSudokuBoard object with a required type and size.
* This will become a puzzle of the classic Sudoku type (here called a plain
* Sudoku), but possibly with block size other than 3x3.
*
* @param parent A pointer to the object that owns this object and
* will delete it automatically.
* @param sudokuType The type of Sudoku board required, which should
* always be SudokuType::Plain.
* @param blockSize The size of blocks required (value 2 to 5). The
* board will have blocks, rows and columns with
* blockSize squared cells and the numbers to be
* filled in will range from 1 to blockSize squared
* (e.g. a classic Sudoku has blockSize = 3).
* @see globals.h
*/
PlainSudokuBoard (QObject * parent, SudokuType sudokuType, int blockSize);
/**
* Sets up an empty board with the required number of cells and makes
* indices to the required groups of cells: rows, columns and blocks.
*
* If blockSize = 3, the board will contain 9x9 = 81 cells and there will
* be 9 rows, 9 columns and 9 blocks of 3x3 cells, to be filled in with
* values 1 to 9 (i.e. a classic Sudoku). Thus there will be 27 groups of
* 9 cells each. See the SudokuBoard class description for an overview.
*/
virtual void setUpBoard();
/**
* Clear a board-vector of the required type and size. All the cells in this
* type of Sudoku are set to zero (i.e. empty). There are no unused cells.
*/
virtual void clear (BoardContents & boardValues);
/**
* Fill the board with randomly chosen valid values, thus generating a
* solution from which a puzzle of the plain Sudoku type can be created.
*
* @return The filled board-vector.
*/
virtual BoardContents & fillBoard();
protected:
/**
* Make a list of cells in rows and columns of the board. In a plain
* Sudoku, the rows and columns indexed take up the entire board of
* blockSize squared rows and columns.
*
* @param i The row-number of the top left cell (i.e. 0).
* @param j The column-number of the top left cell (i.e. 0).
* @param index The current position in the list of groups.
* @return The updated position in the list of groups.
*/
virtual qint32 makeRowColIndex (int i, int j, qint32 index);
/**
* Make a list of cells in blocks of the board. In a plain Sudoku a block
* is a small square of cells of size blockSize * blockSize (e.g. 3x3).
*
* @param index The current position in the list of groups.
* @return The updated position in the list of groups.
*/
virtual qint32 makeBlockIndex (qint32 index);
};
/**
* @class XSudokuBoard plainsudokuboard.h
* @short Data-structures and methods for handling XSudoku puzzles.
*
* An XSudoku puzzle is exactly like a plain Sudoku puzzle, except that the two
* diagonals of the board also form groups that must be filled with the values
* 1 to blockSize * blockSize.
*/
class XSudokuBoard : public PlainSudokuBoard
{
Q_OBJECT
public:
/**
* Constructs a new XSudokuBoard object with a required type and size.
*
* @param parent A pointer to the object that owns this object and
* will delete it automatically.
* @param sudokuType The type of Sudoku board required, which should
* always be SudokuType::XSudoku.
* @param blockSize The size of blocks required (value 3 to 5). The
* board will have blocks, rows and columns with
* blockSize squared cells and the numbers to be
* filled in will range from 1 to blockSize squared.
* @see globals.h
*/
XSudokuBoard (QObject * parent, SudokuType sudokuType, int blockSize);
protected:
/**
* Make a list of cells in blocks of the board. This exactly the same as
* in a plain Sudoku, but with two blocks added, to represent the fact
* that the diagonals of the overall grid must also contain the numbers
* 1 to blockSize * blockSize (e.g. 1 to 9).
*
* @param index The current position in the list of groups.
* @return The updated position in the list of groups.
*/
virtual qint32 makeBlockIndex (qint32 index);
};
/**
* @class JigsawBoard plainsudokuboard.h
* @short Data-structures and methods for handling Jigsaw Sudoku puzzles.
*
* A Jigsaw Sudoku puzzle is exactly like a plain Sudoku puzzle, except that
* only the central block is square. The surrounding blocks have tabs and holes
* and interlock like jigsaw puzzle pieces. The block size is always 3.
*/
class JigsawBoard : public PlainSudokuBoard
{
Q_OBJECT
public:
/**
* Constructs a new JigsawBoard object with required type and size.
*
* @param parent A pointer to the object that owns this object and
* will delete it automatically.
* @param sudokuType The type of Sudoku board required, which should
* always be SudokuType::Jigsaw.
* @param blockSize The size of blocks required (must be 3). The board
* will have blocks, rows and columns with 3x3 = 9
* cells and the numbers to be filled in will range
* from 1 to 9.
* @see globals.h
*/
JigsawBoard (QObject * parent, SudokuType sudokuType, int blockSize);
protected:
/**
* Make a list of cells in blocks of the board. This differs completely
* from the plain Sudoku's makeBlockIndex() because of the irregular
* arrangement of the blocks.
*
* @param index The current position in the list of groups.
* @return The updated position in the list of groups.
*/
virtual qint32 makeBlockIndex (qint32 index);
};
/**
* @class AztecBoard plainsudokuboard.h
* @short Data-structures and methods for handling Aztec Sudoku puzzles.
*
* An Aztec Sudoku puzzle is exactly like a plain Sudoku puzzle, except that
* only the central block is square. The surrounding blocks interlock like
* jigsaw puzzle pieces, resembling an Aztec pyramid. The block size is 3.
*/
class AztecBoard : public PlainSudokuBoard
{
Q_OBJECT
public:
/**
* Constructs a new AztecBoard object with required type and size.
*
* @param parent A pointer to the object that owns this object and
* will delete it automatically.
* @param sudokuType The type of Sudoku board required, which should
* always be SudokuType::Aztec.
* @param blockSize The size of blocks required (must be 3). The board
* will have blocks, rows and columns with 3x3 = 9
* cells and the numbers to be filled in will range
* from 1 to 9.
* @see globals.h
*/
AztecBoard (QObject * parent, SudokuType sudokuType, int blockSize);
protected:
/**
* Make a list of cells in blocks of the board. This differs completely
* from the plain Sudoku's makeBlockIndex() because of the irregular
* arrangement of the blocks.
*
* @param index The current position in the list of groups.
* @return The updated position in the list of groups.
*/
virtual qint32 makeBlockIndex (qint32 index);
};
#endif // PLAINSUDOKUBOARD_H
/****************************************************************************
* Copyright 2011 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************/
#include "debug.h"
#include "roxdokuboard.h"
#include <stdio.h>
RoxdokuBoard::RoxdokuBoard (QObject * parent,
SudokuType sudokuType, int blockSize)
:
PlainSudokuBoard (parent, sudokuType, blockSize)
{
dbgLevel = 1;
// A Roxdoku puzzle should end up with 3 * blockSize groups: a set of groups
// for each dimension. In the 2D layout that corresponds to blockSize
// columns, blockSize square groups and blockSize pseudo-rows made up from
// corresponding rows of the square groups. On the plain Sudoku board,
// Roxdoku uses only the leftmost blockSize columns. The rest are marked
// as unused and do not go into any group-indexes.
m_nGroups = 3 * blockSize - 3 * blockSize * blockSize;
// Set where to fill the first block in PlainSudokuBoard::fillBoard().
// Central block at left for blockSize 3 or 5: below centre for size 4.
m_fillStartRow = blockSize * (blockSize / 2);
m_fillStartCol = 0;
}
void RoxdokuBoard::clear (BoardContents & boardValues)
{
dbo1 "Clear Roxdoku grid and mark unused.\n");
// Create an empty board of the required size.
boardValues.fill (0, m_boardArea);
// Roxdoku uses m_blockSize blocks in the first m_blockSize columns.
markUnusable (boardValues, 0, m_order, m_blockSize, m_order);
}
qint32 RoxdokuBoard::makeRowColIndex (int i, int j, qint32 index)
{
dbo1 "Make row/col index on grid at row %d col %d, index %d.\n",
i, j, index);
qint32 offset = index;
offset = 0;
int topLeft = i * m_boardSize + j;
int cell = topLeft;
// Make an index of the columns in this grid.
cell = topLeft++;
for (int col = 0; col < m_blockSize; col++) {
for (int row = 0; row < m_order; row++) {
m_groupList [offset] = cell;
dbo2 "Index col %d, row %d, value %d at offset %d.\n",
col, row, cell, offset);
cell = cell + m_boardSize;