Commit ac21130b authored by Luigi Toscano's avatar Luigi Toscano

Merge remote-tracking branch 'origin/master' into frameworks

parents ab569431 71bafbc7
......@@ -20,6 +20,9 @@ set(ksudoku_SRCS
main.cpp
generator/sudokuboard.cpp
generator/state.cpp
generator/dlxsolver.cpp
generator/cagegenerator.cpp
generator/mathdokugenerator.cpp
)
kde4_add_app_icon(ksudoku_SRCS "${KDE4_ICON_INSTALL_DIR}/oxygen/*/apps/ksudoku.png")
......@@ -66,6 +69,7 @@ add_subdirectory(themes)
########### install files ###############
install(PROGRAMS gui/org.kde.ksudoku.desktop DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES gui/org.kde.ksudoku.appdata.xml DESTINATION ${SHARE_INSTALL_PREFIX}/metainfo)
install(FILES gui/ksudokuui.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/ksudoku)
install(FILES ksudokurc DESTINATION ${KDE_INSTALL_CONFDIR})
......
This diff is collapsed.
/****************************************************************************
* Copyright 2015 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 CAGEGENERATOR_H
#define CAGEGENERATOR_H
#include <QObject>
#include <QVector>
#include "globals.h"
#include "sudokuboard.h"
enum Direction {ALONE = 0, N = 1, E = 2, S = 4, W = 8, TAKEN = 15};
class SKGraph;
class DLXSolver;
/**
* This class and its methods do all the work of generating a Mathdoku or
* Killer Sudoku puzzle, starting from a solved set of cell-values in a Sudoku
* grid. It lays down a pattern of irregular shaped cages, of different sizes,
* which together cover the grid. Cages of size 1 have only one possible
* solution, so they act as givens or clues. Cages of larger size are given an
* operator (+*-/) and a target value. In the solution, the values in each cage
* must combine together, using the operator, to equal the target value. Finally
* the puzzle, represented by the targets, the operators and the single cells,
* must have a unique solution. The DLX solver tests this. If there is no unique
* solution, the puzzle must be rejected and the user of this class will need to
* try again.
*
* In Killer Sudoku, the only operator is +, there are there are square boxes
* (as well as rows and columns) that must satisfy Sudoku rules and a cage
* cannot contain the same digit more than once.
*
* In Mathdoku (aka KenKen TM), all four operators can occur, a digit can occur
* more than once in a cage and Sudoku rules apply only to rows and columns. The
* latter means that a Mathdoku puzzle can have any size from 3x3 up to 9x9.
* Division and subtraction operators are a special case. They can only appear
* in cages of size 2. This is because the order in which you do divisions or
* subtractions, in a cage of size 3 or more, can affect the result. 6 - (4 - 1)
* = 3, but (6 - 4) - 1 = 1.
*
* @short A generator for Mathdoku and Killer Sudoku puzzles
*/
class CageGenerator : public QObject
{
Q_OBJECT
public:
CageGenerator (const BoardContents & solution);
virtual ~CageGenerator();
/**
* Fill the puzzle area with Mathdoku or Killer Sudoku cages. The graph
* parameter indicates the size and type of puzzle. The other parameters
* affect its difficulty. The cages are stored in the graph object, where
* they can be used by other objects (e.g. to display the cages).
*
* @param graph An SKGraph object representing the size, geometric
* layout and rules of the particular kind of puzzle.
* @param solutionMoves A pointer that returns an ordered list of cells
* found by the solver when it reached a solution.
* @param maxSize The maximum number of cells a cage can have.
* @param maxValue The maximum total value a cage's cells can have.
* @param hideOperators Whether operators are to be hidden in a Mathdoku
* puzzle. In a Killer Sudoku the operators are all +
* and are always hidden.
* @param maxCombos The maximum number of possible solutions any cage
* can have.
*
* @return The number of cages generated, or 0 = too many
* failures to make an acceptable cage, or -1 = no
* unique solution to the puzzle using the cages
* generated (the caller may need to try again).
*/
int makeCages (SKGraph * graph, QList<int> * solutionMoves,
int maxSize, int maxValue,
bool hideOperators, int maxCombos);
/**
* Using just the puzzle-graph and its cages, solve a Mathdoku or Killer
* Sudoku puzzle and check that it has only one solution. This method can
* be used with a manually entered puzzle or one loaded from a saved file,
* to obtain solution values and a move-sequence for hints, as well as
* checking that the puzzle and its data are valid.
*
* @param graph An SKGraph object representing the size, geometric
* layout and rules of the particular kind of puzzle.
* @param solution The solution returned if a unique solution exists.
* @param solutionMoves A pointer that returns an ordered list of cells
* found by the solver when it reached a solution.
* @param hideOperators Whether operators are to be hidden in a Mathdoku
* puzzle. In a Killer Sudoku the operators are all +
* and are always hidden.
*
* @return 0 = there is no solution,
* 1 = there is a unique solution,
* >1 = there is more than one solution.
*/
int checkPuzzle (SKGraph * graph, BoardContents & solution,
QList<int> * solutionMoves, bool hideOperators);
private:
SKGraph * mGraph; // The geometry of the puzzle.
DLXSolver * mDLXSolver; // A solver for generated puzzles.
BoardContents mSolution;
int mOrder; // The height and width of the grid.
int mBoardArea; // The number of cells in the grid.
bool mKillerSudoku; // Killer Sudoku or Mathdoku rules?
bool mHiddenOperators; // Operators in cages are displayed?
// Working-data used in the cage-generation algorithm.
QList<int> mUnusedCells; // Cells not yet assigned to cages.
QList<int> mNeighbourFlags; // The assigned neighbours cells have.
int mSingles; // The number of 1-cell cages (clues).
int mMinSingles; // The minimum number required.
int mMaxSingles; // The maximum number required.
int mMaxCombos; // The maximum combos a cage can have.
// mPossibilities is a list of possible combinations and values all the
// cages might have. It is used when setting up the DLX matrix for the
// solver and again when decoding the solver's result into a solution grid.
//
// The Index list indicates where the combos for each cage begin and end.
// It is used to find the first combo for each cage and the beginning of
// the next cage's combos. The difference between these two gives the number
// of possible values that might solve the cage. Divide that by the size of
// the cage to get the number of combos for that cage. One or more of these
// combos are correct ones, which the solver must find. The last entry in
// the index is equal to the total size of mPossibilities.
QList<int> * mPossibilities;
QList<int> * mPossibilitiesIndex;
// PRIVATE METHODS.
// Form a group of cells that makes up a cage of a chosen size (or less).
QVector<int> makeOneCage (int seedCell, int requiredSize);
// Choose an operator for the cage and calculate the cage's value.
void setCageTarget (QVector<int> cage, CageOperator & cageOperator,
int & cageValue);
// Check whether a generated cage is within parameter requirements.
bool cageIsOK (const QVector<int> cage, CageOperator cageOperator,
int cageValue);
// Set all possible values for the cells of a cage (used by the solver).
void setAllPossibilities (const QVector<int> cage, int nDigits,
CageOperator cageOperator, int cageValue);
// Set all possible values for one operator in a cage (used by the solver).
void setPossibilities (const QVector<int> cage, CageOperator cageOperator,
int cageValue);
// Set all possible values for a cage that has a multiply or add operator.
void setPossibleAddsOrMultiplies (const QVector<int> cage,
CageOperator cageOperator, int cageValue);
// Check if a cage contains duplicate digits (not allowed in Killer Sudoku).
bool hasDuplicates (int nDigits, int digits[]);
// Check if a combo of digits in a cage satisfies Sudoku rules (a Mathdoku
// cage can contain a digit more than once, but not in the same row/column).
bool isSelfConsistent (const QVector<int> cage, int nDigits, int digits[]);
// Initialise the cage generator for a particular size and type of puzzle.
void init (SKGraph * graph, bool hiddenOperators);
};
#endif // CAGEGENERATOR_H
This diff is collapsed.
This diff is collapsed.
/****************************************************************************
* Copyright 2015 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 "mathdokugenerator.h"
#include "skgraph.h"
#include "cagegenerator.h"
#include <QDebug>
MathdokuGenerator::MathdokuGenerator (SKGraph * graph)
:
mGraph (graph)
{
}
bool MathdokuGenerator::generateMathdokuTypes (BoardContents & puzzle,
BoardContents & solution,
QList<int> * solutionMoves,
Difficulty difficultyRequired)
{
// Cage sizes must be no more than the number of cells in a column or row.
int maxSize = qMin ((2 + difficultyRequired), mGraph->order());
int maxVal = 1000;
bool hideOps = false;
// int maxCombos = 120;
int maxCombos = 2000;
int maxTries = 20;
CageGenerator cageGen (solution);
int numTries = 0;
int numMultis = 0;
int n = 0;
while ((n <= 0) && (numTries < maxTries)) {
numTries++;
n = cageGen.makeCages (mGraph, solutionMoves,
maxSize, maxVal, hideOps, maxCombos);
if (n < 0) {
numMultis++;
}
}
if (numTries >= maxTries) {
qDebug() << "makeCages() FAILED after" << numTries << "tries"
<< numMultis << "multi-solutions";
return false; // Try another set of Sudoku cell-values.
}
qDebug() << "makeCages() required" << numTries << "tries"
<< numMultis << "multi-solutions";;
puzzle = mGraph->emptyBoard();
for (int n = 0; n < mGraph->cageCount(); n++) {
if (mGraph->cage(n).count() == 1) {
int index = mGraph->cage(n).at(0);
puzzle[index] = solution.at(index);
}
}
return true;
}
int MathdokuGenerator::solveMathdokuTypes (BoardContents & solution,
QList<int> * solutionMoves)
{
bool hideOps = false;
int result = 0;
CageGenerator cageGen (solution);
result = cageGen.checkPuzzle (mGraph, solution, solutionMoves, hideOps);
return result;
}
#include "mathdokugenerator.moc"
/****************************************************************************
* Copyright 2015 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 MATHDOKUGENERATOR_H
#define MATHDOKUGENERATOR_H
#include "globals.h"
#include <QVector>
class SKGraph;
/**
* @class MathdokuGenerator
* @short Generator for Killer Sudoku and Mathdoku puzzles.
*
* Generates a Killer Sudoku or Mathdoku puzzle from a Latin Square that
* satisfies Sudoku-type constraints. It acts as a controller for makeCages().
*/
class MathdokuGenerator
{
public:
MathdokuGenerator (SKGraph * graph);
/**
* Generate a Mathdoku or Killer Sudoku puzzle.
*
* @param puzzle The generated puzzle (returned).
* @param solution The values that must go into the solution.
* @param solutionMoves A pointer that returns an ordered list of cells
* found by the solver when it reached a solution.
* @param difficultyRequired The requested level of difficulty.
*
* @return True if puzzle-generation succeeded, false
* if too many tries were required.
*/
bool generateMathdokuTypes (BoardContents & puzzle,
BoardContents & solution,
QList<int> * solutionMoves,
Difficulty difficultyRequired);
/**
* Solve a Mathdoku or Killer Sudoku and check how many solutions there are.
* The solver requires only the puzzle-graph, which contains all the cages.
*
* @param solution The values returned as the solution.
* @param solutionMoves A pointer that returns an ordered list of cells
* found by the solver when it reached a solution.
*
* @return 0 = there is no solution,
* 1 = there is a unique solution,
* >1 = there is more than one solution.
*/
int solveMathdokuTypes (BoardContents & solution,
QList<int> * solutionMoves);
private:
SKGraph * mGraph; // The layout, rules and geometry of the puzzle.
};
#endif // MATHDOKUGENERATOR_H
/****************************************************************************
* Copyright 2011 Ian Wadham <iandw.au@gmail.com> *
* Copyright 2006 David Bau <david bau @ gmail com> Original algorithms *
* Copyright 2015 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 *
......@@ -20,6 +21,8 @@
#include "sudokuboard.h"
#include "state.h"
#include "mathdokugenerator.h"
#include <QDebug> // IDW test.
#include <KLocalizedString>
#include <KMessageBox>
......@@ -71,16 +74,57 @@ void SudokuBoard::setSeed()
}
}
void SudokuBoard::generatePuzzle (BoardContents & puzzle,
BoardContents & solution,
Difficulty difficultyRequired,
Symmetry symmetry)
bool SudokuBoard::generatePuzzle (BoardContents & puzzle,
BoardContents & solution,
Difficulty difficultyRequired,
Symmetry symmetry)
{
dbe "Entered generatePuzzle(): difficulty %d, symmetry %d\n",
difficultyRequired, symmetry);
QTime t;
t.start();
setSeed();
SudokuType puzzleType = m_graph->specificType();
if ((puzzleType == Mathdoku) || (puzzleType == KillerSudoku)) {
// Generate variants of Mathdoku (aka KenKen TM) or Killer Sudoku types.
int maxTries = 10;
int numTries = 0;
bool success = false;
while (true) {
MathdokuGenerator mg (m_graph);
// Find numbers to satisfy Sudoku rules: they will be the solution.
solution = fillBoard();
// Generate a Mathdoku or Killer Sudoku puzzle having this solution.
numTries++;
success = mg.generateMathdokuTypes (puzzle, solution,
&m_KSudokuMoves, difficultyRequired);
if (success) {
return true;
}
else if (numTries >= maxTries) {
QWidget owner;
if (KMessageBox::questionYesNo (&owner,
i18n("Attempts to generate a puzzle failed after "
"about 200 tries. Try again?"),
i18n("Mathdoku or Killer Sudoku Puzzle"))
== KMessageBox::No) {
return false; // Go back to the Welcome screen.
}
numTries = 0; // Try again.
}
}
}
else {
// Generate variants of Sudoku (2D) and Roxdoku (3D) types.
return generateSudokuRoxdokuTypes (puzzle, solution,
difficultyRequired, symmetry);
}
}
bool SudokuBoard::generateSudokuRoxdokuTypes (BoardContents & puzzle,
BoardContents & solution,
Difficulty difficultyRequired,
Symmetry symmetry)
{
const int maxTries = 20;
int count = 0;
float bestRating = 0.0;
......@@ -91,6 +135,8 @@ void SudokuBoard::generatePuzzle (BoardContents & puzzle,
BoardContents currPuzzle;
BoardContents currSolution;
QTime t;
t.start();
if (m_graph->sizeZ() > 1) {
symmetry = NONE; // Symmetry not implemented in 3-D.
}
......@@ -144,6 +190,9 @@ void SudokuBoard::generatePuzzle (BoardContents & puzzle,
puzzle = currPuzzle;
}
// Express the rating to 1 decimal place in whatever locale we have.
QString ratingStr = ki18n("%1").subs(bestRating, 0, 'f', 1).toString();
// Check and explain the Sudoku/Roxdoku puzzle-generator's results.
if ((d < difficultyRequired) && (count >= maxTries)) {
// Exit after max attempts?
QWidget owner;
......@@ -155,9 +204,9 @@ void SudokuBoard::generatePuzzle (BoardContents & puzzle,
"\n"
"If you accept the puzzle, it may help to change to "
"No Symmetry or some low symmetry type, then use "
"Game->New and try generating another puzzle.")
.arg(maxTries).arg(bestDifficulty)
.arg(bestRating, 0, 'f', 1).arg(difficultyRequired),
"Game->New and try generating another puzzle.",
maxTries, bestDifficulty,
ratingStr, difficultyRequired),
i18n("Difficulty Level"),
KGuiItem(i18n("&Try Again")), KGuiItem(i18n("&Accept")));
if (ans == KMessageBox::Yes) {
......@@ -170,19 +219,20 @@ void SudokuBoard::generatePuzzle (BoardContents & puzzle,
QWidget owner;
int ans = 0;
if (m_accum.nGuesses == 0) {
const QString rating = QString("%1").arg(bestRating, 0, 'f', 1);
ans = KMessageBox::questionYesNo (&owner,
i18n("It will be possible to solve the generated puzzle "
"by logic alone. No guessing will be required.\n"
"\n"
"The internal difficulty rating is %1. There are "
"%2 clues at the start and %3 moves to go.", rating, bestNClues, (m_stats.nCells - bestNClues)),
"%2 clues at the start and %3 moves to go.",
ratingStr, bestNClues,
(m_stats.nCells - bestNClues)),
i18n("Difficulty Level"),
KGuiItem(i18n("&OK")), KGuiItem(i18n("&Retry")));
}
else {
const QString average = QString::fromLatin1("%1").arg(((float) bestNGuesses) / 5.0, 0, 'f', 1);
const QString rating = QString::fromLatin1("%1").arg(bestRating, 0, 'f', 1);
QString avGuessStr = ki18n("%1").subs(((float) bestNGuesses) /
5.0, 0, 'f', 1).toString(); // Format as for ratingStr.
ans = KMessageBox::questionYesNo (&owner,
i18n("Solving the generated puzzle will require an "
"average of %1 guesses or branch points and if you "
......@@ -190,7 +240,9 @@ void SudokuBoard::generatePuzzle (BoardContents & puzzle,
"first guess should come after %2 moves.\n"
"\n"
"The internal difficulty rating is %3, there are "
"%4 clues at the start and %5 moves to go.", average, bestFirstGuessAt, rating, (m_stats.nCells - bestNClues)),
"%4 clues at the start and %5 moves to go.",
avGuessStr, bestFirstGuessAt, ratingStr,
bestNClues, (m_stats.nCells - bestNClues)),
i18n("Difficulty Level"),
KGuiItem(i18n("&OK")), KGuiItem(i18n("&Retry")));
}
......@@ -214,6 +266,7 @@ void SudokuBoard::generatePuzzle (BoardContents & puzzle,
dbo "SOLUTION\n");
print (solution);
}
return true;
}
Difficulty SudokuBoard::calculateRating (const BoardContents & puzzle,
......
/****************************************************************************
* Copyright 2011 Ian Wadham <iandw.au@gmail.com> *
* Copyright 2006 David Bau <david bau @ gmail com> Original algorithms *
* Copyright 2015 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 *
......@@ -39,6 +40,13 @@ enum GuessingMode {Random, NotRandom};
class SKGraph;
class State;
// TODO - SudokuBoard, MathdokuGenerator, CageGenerator and DLXSolver could be
// factored better. At the moment, MathdokuGenerator needs SudokuBoard's
// fillBoard() method to create a square that satisfies Sudoku rules for
// Killer Sudoku or Mathdoku puzzles. But fillBoard() depends on large
// parts of SudokuBoard's solver logic... so we have two solver objects
// co-existing for now, but this happens only for a second or so.
/**
* @class SudokuBoard sudokuboard.h
* @short Generalized data-structures and methods for handling Sudoku puzzles.
......@@ -90,7 +98,7 @@ class State;
* Classic Sudoku in several sizes and variants, Samurai Sudoku with five
* overlapping grids and the three-dimensional Roxdoku in several sizes.
*
* Each group (row, column, blocki or plane) contains N cells in which the
* Each group (row, column, block or plane) contains N cells in which the
* numbers 1 to N must appear exactly once. N can be 4, 9, 16 or 25, but not
* all types of puzzle support all four sizes.
*
......@@ -129,8 +137,12 @@ public:
* @param difficulty The required level of difficulty (as defined in file
* globals.h).
* @param symmetry The required symmetry of layout of the clues.
*
* @return Normally true, but false if the user wishes to go
* back to the Welcome screen (e.g. to change reqs.)
* after too many attempts to generate a puzzle.
*/
void generatePuzzle (BoardContents & puzzle,
bool generatePuzzle (BoardContents & puzzle,
BoardContents & solution,
Difficulty difficulty,
Symmetry symmetry);
......@@ -195,6 +207,16 @@ public:
BoardContents & solveBoard (const BoardContents & boardValues,
GuessingMode gMode = Random);
/**
* Fill the board with randomly chosen valid values, thus generating a
* solution from which a puzzle can be created (virtual). It is made
* public so that it can be used to fill a Mathdoku or Killer Sudoku
* board with numbers that satisfy Sudoku constraints.
*
* @return The filled board-vector.
*/
virtual BoardContents & fillBoard();
/**
* Initialize or re-initialize the random number generator.
*/
......@@ -241,14 +263,6 @@ protected:
*/
virtual void clear (BoardContents & boardValues);
/**
* Fill the board with randomly chosen valid values, thus generating a
* solution from which a puzzle can be created (virtual).
*
* @return The filled board-vector.
*/
virtual BoardContents & fillBoard();
/*
* Fill a vector of integers with values from 1 up to the size of the
* vector, then shuffle the integers into a random order.
......@@ -258,6 +272,11 @@ protected:
void randomSequence (QVector<int> & sequence);
private:
bool generateSudokuRoxdokuTypes (BoardContents & puzzle,
BoardContents & solution,
Difficulty difficulty,
Symmetry symmetry);
SKGraph * m_graph;
int m_vacant;
int m_unusable;
......
/****************************************************************************
* Copyright 2011 Ian Wadham <iandw.au@gmail.com> *
* Copyright 2015 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 *
......@@ -25,7 +26,7 @@
#define UNUSABLE -1
enum SudokuType {Plain, XSudoku, Jigsaw, Samurai, TinySamurai, Roxdoku, Aztec,
EndSudokuTypes};
Mathdoku, KillerSudoku, EndSudokuTypes};
enum Difficulty {VeryEasy = 0, Easy = 1, Medium = 2, Hard = 3, Diabolical = 4,