|
View previous topic :: View next topic |
Author |
Message |
| eswrite
| Joined: 02 Sep 2005 | Posts: 4 | : | | Items |
|
Posted: Fri Sep 02, 2005 5:06 pm Post subject: Brute force grid/puzzle generator |
|
|
I am fairly new to sudoku puzzles and thought it would be cool to write my own puzzle generator. So far, I have code that generates a 9x9 grid that follows the row, column and sub-grid requirement to include all numbers between 1-9. I call it a brute force generator, because I basically start assigning numbers at random, on a row by row basis, using only those numbers that conform to sudoku rules. Sometimes I have to "rewind" a row or two (my tests show no more than 3 row rewinds) when things get stuck, but still program performance is pretty good. I will include the code below (sorry, somewhat long). My next step is to generate the puzzle by hiding numbers, but I have no idea how to do this so that:
a) I have a solvable puzzle.
b) I can choose which number to remove for various levels of difficulty.
C++ code (updated), compilable with Borland C++ 5.5
Code: |
//=============================================================================
// Brute force Sudoku generator. Generates pseudo-random numbers on a row-by-row
// basis, checking that number placement conforms with Sudoku row, column and
// sub-grid rules.
//
// Copyright(c) Eduardo Suastegui, 2005
//=============================================================================
#include<iostream>
#include<fstream>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
#ifndef _RWSTD_NO_NAMESPACE
using namespace std;
#endif
//============================
// Generate a random value between 1 and max_value
int getRandValue(int max_value)
{
int rnd_value;
rnd_value = int((double(std::rand()) / RAND_MAX) * max_value + 1);
// printf("%d ", rnd_value);
return rnd_value;
}
//============================
// Get the grid value at the specified location
int getGridValue(int row, int col, int* grid)
{
return grid[row * 9 + col];
}
//============================
// Check whether a value already exists along a colum
bool isInCol(int row, int col, int value, int* grid)
{
bool inCol = false;
for (int r=0; r<row & !inCol; r++)
inCol = (getGridValue(r, col, grid) == value) ? true : false;
for (int r=row+1; r<9 & !inCol; r++)
inCol = (getGridValue(r, col, grid) == value) ? true : false;
return inCol;
}
//============================
// Check whether a value already exists along a row
bool isInRow(int row, int col, int value, int* grid)
{
bool inRow = false;
for (int c=0; c<col & !inRow; c++)
inRow = (getGridValue(row, c, grid) == value) ? true : false;
for (int c=col+1; c<9 & !inRow; c++)
inRow = (getGridValue(row, c, grid) == value) ? true : false;
return inRow;
}
//============================
// Check whether a value already exists in a sub-grid
bool isInSub(int row, int col, int value, int* grid)
{
#ifndef _RWSTD_NO_NAMESPACE
using namespace std;
#endif
bool inSub = false;
int srow, scol;
// Determine grid row,column starting position
srow = (row / 3) * 3;
scol = (col / 3) * 3;
//cout << "(" << srow << "," << scol << ")";
for (int r=srow; r<=srow+2 && !inSub; r++)
for (int c=scol; c<=scol+2 && !inSub; c++)
inSub = (getGridValue(r, c, grid) == value && r != row && c != col) ? true : false;
return inSub;
}
//============================
// Set a random value at the specified grid location
bool setGridValue(int row, int col, int* grid)
{
bool done = false;
int vbag[9] = {1,2,3,4,5,6,7,8,9}; // bag of random values
int len = 9;
int pick;
int value;
while (!done)
{
pick = len > 0 ? random(len) : 0;
value = vbag[pick];
done = (!isInRow(row, col, value, grid) &&
!isInCol(row, col, value, grid) &&
!isInSub(row, col, value, grid)) || len == 0;
if (!done)
{
if (len == 0)
break;
// Take duplicate out of bag by removing it from the array:
for (int i = pick; i < len-1; i++)
vbag[i] = vbag[i+1]; // shift left
--len; // reduce bag size
}
}
grid[row * 9 + col] = value;
//cout << len << "@" << pick << "=" << value << " ";
return done;
}
//============================
// Print the Grid, using the Simple Sudoku (ss) format:
// Example:
// *-----------*
// |.6.|1.4|.5.|
// |..8|3.5|6..|
// |2..|...|..1|
// |---+---+---|
// |8..|4.7|..6|
// |..6|...|3..|
// |7..|9.1|..4|
// |---+---+---|
// |5..|...|..2|
// |..7|2.6|9..|
// |.4.|5.8|.7.|
// *-----------*
void printGrid(ostream& out, int* grid)
{
int row, col;
int value;
out << " *-----------*" << endl;
for (row = 0; row < 9; row++)
{
out << " ";
for (col = 0; col < 9; col++)
{
if ((col % 3) == 0)
out << "|";
value = getGridValue(row, col, grid);
if (value == 0)
out << ".";
else
out << value;
}
out << "|" << endl;
if (row == 2 || row == 5)
out << " |---+---+---|" << endl;
}
out << " *-----------*" << endl;
}
//============================
// Check a row
bool checkRow(int row, int* grid)
{
bool rowOk = true;
int value;
for (int c = 8; c >= 0 && rowOk; c--)
{
value = getGridValue(row, c, grid);
rowOk = !isInRow(row, c, value, grid) && !isInCol(row, c, value, grid) && !isInSub(row, c, value, grid);
}
return rowOk;
}
// Clear the rows in the specified row range
void clearRows(int row1, int row2, int* grid)
{
int s_row = row1 > 0 ? row1 : 0;
int e_row = row2 < 9 ? row2 : 8;
for (int row = s_row; row <= e_row; row++)
for (int col = 0; col < 9; col++)
grid[row * 9 + col] = 0;
}
//============================
// Check the grid
bool checkGrid(int *grid)
{
int row, col;
int value;
bool gridOk = true;
cout << "Grid check..." << endl;
cout << " S = sub-grid violation" << endl;
cout << " R = Row violation" << endl;
cout << " C = Column violation" << endl;
cout << "+-+-+-+-+-+-+-+-+-+" << endl;
for (row = 0; row < 9; row++)
{
for (col = 0; col < 9; col++)
{
cout << "|";
value = getGridValue(row, col, grid);
if (isInSub(row, col, value, grid))
{
cout << "S";
gridOk = false;
}
else if (isInRow(row, col, value, grid))
{
cout << "R";
gridOk = false;
}
else if (isInCol(row, col, value, grid))
{
cout << "C";
gridOk = false;
}
else
cout << value;
}
cout << "|" << endl;
cout << "+-+-+-+-+-+-+-+-+-+" << endl;
}
return gridOk;
}
//============================
// Make a puzzle from the full grid by leaving num_squares revealed
// and hiding everything else. Revealed squares are chosen at random
int* makePuzzle(int* grid, int num_squares)
{
int row, col, value, pick;
int* gpuz = (int *) malloc(9*9 * sizeof(int));
int pbag[81];
memset(gpuz, 0x00, 9*9 * sizeof(int));
for (int n = 0; n < 81; ++n)
pbag[n] = n;
for (int n = 0; n < num_squares; ++n)
{
pick = getRandValue(81-n) - 1;
row = pbag[pick] / 9;
col = pbag[pick] % 9;
//cout << pbag[pick] << "=" << row << "," << col << endl;
gpuz[row * 9 + col] = getGridValue(row, col, grid);
for (int i = pick; i < 81-n-1; ++i)
pbag[i] = pbag[i+1]; // shift left
}
cout << "Puzzle generated" << endl;
printGrid(cout, gpuz);
return gpuz;
}
void main(int argc, char **argv)
{
int* grid;
int col, row;
int drow = 1;
int rowgen = 0;
char cval[16];
string paramStrIn;
string oFileStr = "mksudoku.ss";
int num_squares = 30;
// Read application parameters:
for (int a = 1; a < argc; ++a)
{
paramStrIn = argv[a];
if (paramStrIn[0] == '-' && paramStrIn.length() > 2)
{
switch (paramStrIn[1])
{
case 'f':
oFileStr = paramStrIn.substr(2, paramStrIn.length()-2);
break;
case 'n':
num_squares = atoi(paramStrIn.substr(2, paramStrIn.length()-2).c_str());
break;
default:
cout << "Invalid parameter: " << paramStrIn.substr(0,2) << endl;
cout << " Valid syntax: mksudoku -f[file_name] -n[num_squares]" << endl;
}
}
}
// Allocate grid:
grid = (int *) malloc(9*9 * sizeof(int));
memset(grid, 0x00, 9*9 * sizeof(int));
// Initialize randomizer:
std::srand(static_cast<unsigned>(std::time(0)));
// Fill in grid:
for (row = 0; row < 9; row++)
{
for (col = 0; col < 9; col++)
{
setGridValue(row, col, grid);
}
if (row > 0 && !checkRow(row, grid))
{
if (rowgen > 100)
{
// Unwind drow rows to re-generate random grid entries:
cout << row << "X";
cout << endl;
printGrid(cout, grid);
clearRows(row-drow, row, grid);
++drow;
row = row - drow >= -1 ? row - drow : -1;
rowgen = 0;
}
else
{
++rowgen;
cout << row << "G";
--row;
}
}
else
{
rowgen = 0;
cout << row << ".";
}
cout << " ";
}
cout << endl;
// Output the grid and check it:
printGrid(cout, grid);
if (!checkGrid(grid))
cout << "Grid is faulty. Please re-generate.\n";
else
{
int* gpuz = makePuzzle(grid, num_squares);
// Output puzzle and solution to file:
fstream out(oFileStr.c_str(), ios_base::out );
printGrid(out, gpuz);
cout << "Puzzle written to: " << oFileStr << endl;
free(gpuz);
}
// Free grid array:
free(grid);
return;
}
|
_________________ Test Everything; Hold On to the Good |
|
Back to top |
|
|
| eswrite
| Joined: 02 Sep 2005 | Posts: 4 | : | | Items |
|
Posted: Fri Sep 02, 2005 8:00 pm Post subject: |
|
|
Okay, I've gotten a little farther now, with the updated code shown above. Now, the code generates a puzzle, with the "revealed" squares chosen purely at random and written to a file in a format that SimpleSudoku (see www.angusj.com/sudoku) can accept. When I load the output file into SimpleSudoku, for 30-40 revealed squares, SimpleSudoku tells me the puzzle is invalid because it has multiple solutions. This probably has to do with the squares my program chose to reveal at random, or perhaps with the original sudoku grid (solution), I can't tell. Here's a sample run with 30 revealed squares, for which SimpleSudoku tells me there are 264(!) solutions. Anyone care to give this newbie a little insight?
Code: | C:\Downloads\sudoku>mksudoku
0. 1G 1G 1G 1. 2. 3. 4. 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G 5G
5G 5G 5. 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6
G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6G 6. 7G 7G 7G 7G 7G 7G 7G 7G 7G
7. 8.
*-----------*
|475|963|218|
|861|724|539|
|932|581|674|
|---+---+---|
|786|495|123|
|329|618|457|
|514|237|896|
|---+---+---|
|243|176|985|
|698|352|741|
|157|849|362|
*-----------*
Grid check...
S = sub-grid violation
R = Row violation
C = Column violation
+-+-+-+-+-+-+-+-+-+
|4|7|5|9|6|3|2|1|8|
+-+-+-+-+-+-+-+-+-+
|8|6|1|7|2|4|5|3|9|
+-+-+-+-+-+-+-+-+-+
|9|3|2|5|8|1|6|7|4|
+-+-+-+-+-+-+-+-+-+
|7|8|6|4|9|5|1|2|3|
+-+-+-+-+-+-+-+-+-+
|3|2|9|6|1|8|4|5|7|
+-+-+-+-+-+-+-+-+-+
|5|1|4|2|3|7|8|9|6|
+-+-+-+-+-+-+-+-+-+
|2|4|3|1|7|6|9|8|5|
+-+-+-+-+-+-+-+-+-+
|6|9|8|3|5|2|7|4|1|
+-+-+-+-+-+-+-+-+-+
|1|5|7|8|4|9|3|6|2|
+-+-+-+-+-+-+-+-+-+
Puzzle generated
*-----------*
|47.|...|...|
|.6.|..4|.3.|
|..2|..1|674|
|---+---+---|
|786|495|...|
|.2.|..8|...|
|5.4|2..|8..|
|---+---+---|
|243|.76|..5|
|...|...|...|
|..7|...|.6.|
*-----------*
Puzzle written to: mksudoku.ss |
_________________ Test Everything; Hold On to the Good |
|
Back to top |
|
|
| eswrite
| Joined: 02 Sep 2005 | Posts: 4 | : | | Items |
|
Posted: Wed Sep 07, 2005 3:55 pm Post subject: |
|
|
From what I have been able to learn here and elsewhere (in spite of zero answers to my post), is that the program can't just hide squares at random. It needs a solver to guarantee a unique solution every time another square is removed. Don't know whether I'll try this. For now, I think I'll just use Simple Sudoku--what a great little program--although I'm not sure it generates puzzles from a randomly selected solution; rather, it seems to pick puzzles from a library of solutions? _________________ Test Everything; Hold On to the Good |
|
Back to top |
|
|
| angusj Site Admin
| Joined: 18 Jun 2005 | Posts: 406 | : | | Items |
|
Posted: Wed Sep 07, 2005 11:30 pm Post subject: |
|
|
eswrite wrote: | From what I have been able to learn here and elsewhere (in spite of zero answers to my post), is that the program can't just hide squares at random. |
Correct. You need to test that the resulting puzzle is valid.
eswrite wrote: | It needs a solver to guarantee a unique solution every time another square is removed. |
That's certainly one very good way to do it .
eswrite wrote: | For now, I think I'll just use Simple Sudoku--what a great little program-- |
Thanks
eswrite wrote: | although I'm not sure it generates puzzles from a randomly selected solution; rather, it seems to pick puzzles from a library of solutions? |
Simple Sudoku definitely does generate a random puzzle every time. |
|
Back to top |
|
|
| mystman23
| Joined: 28 Aug 2005 | Posts: 5 | : | | Items |
|
Posted: Sat Sep 10, 2005 2:37 pm Post subject: Not working |
|
|
Does it matter if you use Dev C++ instead of Borland because it is not working. I tryed all the code from you first post and it says there is an error at line 104. It says "'random' undeclared (first use in function)". Do you know what is wrong? |
|
Back to top |
|
|
| eswrite
| Joined: 02 Sep 2005 | Posts: 4 | : | | Items |
|
Posted: Wed Sep 21, 2005 7:43 pm Post subject: |
|
|
mystman23: you have probably figured it out by now, but just in case, different libraries include the random (or rand) function in different include files. Identify which include file contains it in Dev C++, include it, and you should be good to go. Alternatively, you can substitute the call to random with a call to getRandValue(9)-1. _________________ Test Everything; Hold On to the Good |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
Igloo Theme Version 1.0 :: Created By: Andrew Charron
|