void generateUniqueBoard(sudokuGrid *game) { int i, j; int positions[GRID_SIZE]; int targetValue, position; //start by generating a random board resetBoard(game); generateRandomBoard(game); //populate and shuffle positions array - these are targets for cell removal j = 0; for (i = 0; i <= GRID_SIZE; i++) { positions[j] = i; j++; } shuffleArr(positions,GRID_SIZE); //clear cells until we no longer have a unique solution j = 0; while (j < (GRID_SIZE - GRID_SIZE/PRINCIPAL_NUM)) { if (solutionCount(game,2) > 1) { //restore the last cell that we removed to ensure that we still have a unique solution commitMove(game,position,targetValue); } targetValue = game->values[positions[j]]; position = positions[j]; commitMove(game,position,BLANK_VALUE); j++; } //restore the last cell that we removed to ensure that we still have a unique solution commitMove(game,position,targetValue); }
int solutionCount(sudokuGrid *game,int solutionsCuttoff) { //this function destroys the board in the process of counting the number of solutions// int position, trialValue, count; //if the board is full up, we are done if (isBoardFull(game)) { count = 1; } else { count = 0; position = getEmptyCell(game); trialValue = MIN_VALUE; while ((count < solutionsCuttoff) && (trialValue <= MAX_VALUE)) { if (isLegalMove(game,position,trialValue)) { commitMove(game,position,trialValue); if (solutionCount(game,1)) { //game with trial value in this position is solvable count++; } //reset to a blank value to try another solution commitMove(game,position,BLANK_VALUE); } trialValue++; } } return count; }
int AbstractGrid::solutionCount() { MoveList possibleNextMoves; // TODO: put following in external function foreach (AbstractCell *cell, m_cells) { if (!cell->hasBeenMoved()) { Directions dirs = cell->cables(); Move move; if (dirs == None) { // no cables move = Move(cell->index(), Move::None); possibleNextMoves.append(move); } else if (dirs == (Up | Down) || dirs == (Left | Right)) { // cables forming a line move = Move(cell->index(), Move::None); possibleNextMoves.append(move); move = Move(cell->index(), Move::Left); possibleNextMoves.append(move); } else { // other kind of cables move = Move(cell->index(), Move::None); possibleNextMoves.append(move); move = Move(cell->index(), Move::Left); possibleNextMoves.append(move); move = Move(cell->index(), Move::Right); possibleNextMoves.append(move); move = Move(cell->index(), Move::Inverted); possibleNextMoves.append(move); } break; } } // all cells have been moved if (possibleNextMoves.isEmpty()) { return isPossibleSolution() ? 1 : 0; } // else int solutionsFound = 0; foreach (const Move &nextMove, possibleNextMoves) { int index = nextMove.index(); switch (nextMove.move()) { case Move::None: m_cells[index]->emptyMove(); break; case Move::Right: m_cells[index]->rotateClockwise(); break; case Move::Left: m_cells[index]->rotateCounterclockwise(); break; case Move::Inverted: m_cells[index]->invert(); break; } if (movesDoneArePossible()) { solutionsFound += solutionCount(); // recursive call } m_cells[index]->reset(); // undo move }
void AbstractGrid::initializeGrid(uint width, uint height, Wrapping wrapping) { if ((width * height) != (m_width * m_height)) { qDeleteAll(m_cells); m_cells.clear(); for (uint index = 0; index < width*height; ++index) { m_cells.append(newCell(index)); } } m_width = width; m_height = height; m_isWrapped = wrapping; createGrid(); while(hasUnneededCables() || solutionCount() != 1) { // the grid is invalid: create a new one createGrid(); } m_minimumMoves = 0; const int shuffleLimit = cellCount() * minCellRatio; QList<int> notShuffledCells; for (int i = 0; i < cellCount(); ++i) notShuffledCells.append(i); // select a random cell that is not yet shuffled // rotate such that initial and final states are not same // repeat above two steps until minimum moves equal to shuffle limit while(m_minimumMoves < shuffleLimit) { // selecting a random index int index = qrand() % notShuffledCells.count(); int cellNo = notShuffledCells[index]; // removing the selected index so that it must not be used again notShuffledCells.removeAt(index); AbstractCell *cell = m_cells[cellNo]; Directions dir = cell->cables(); // excludes None(Empty cell) if (dir == None) { continue; } // if straight line rotate once // cant rotate twice(it will be back on its initial state) else if ((dir == (Up | Down)) || (dir == (Left | Right))) { m_minimumMoves += 1; cell->rotateClockwise(); } // for every other case rotate 1..3 times else { int rotation = qrand() % 3 + 1; // 1..3 // cant rotate twice when m_minimumMoves == shuffleLimit - 1 if (m_minimumMoves == shuffleLimit - 1 && rotation == 2){ rotation = (qrand() % 2)? 1 : 3; // 1 or 3 } m_minimumMoves += (rotation == 3) ? 1 : rotation; while(rotation--) { cell->rotateClockwise(); } } } updateConnections(); }