/* * Copyright 2007-2012, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #include "SudokuGenerator.h" #include #include #include #include #include "ProgressWindow.h" #include "SudokuField.h" #include "SudokuSolver.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "SudokuGenerator" SudokuGenerator::SudokuGenerator() { } SudokuGenerator::~SudokuGenerator() { } bool SudokuGenerator::_HasOnlyOneSolution(SudokuField& field) { SudokuSolver solver(&field); solver.ComputeSolutions(); return solver.CountSolutions() == 1; } void SudokuGenerator::_Progress(BMessenger progress, const char* text, float percent) { BMessage update(kMsgProgressStatusUpdate); if (text) update.AddString("message", text); update.AddFloat("percent", percent); progress.SendMessage(&update); } void SudokuGenerator::Generate(SudokuField* target, uint32 fieldsLeft, BMessenger progress, volatile bool *quit) { // first step: generate a solved field - random brute force style SudokuField field(target->BlockSize()); uint32 inputCount = field.Size() * field.Size() / 3; _Progress(progress, B_TRANSLATE("Creating solvable field"), 5.f); while (!*quit) { field.Reset(); // generate input field uint32 validMask = 0; for (uint32 i = 0; i < inputCount; i++) { uint32 x; uint32 y; do { x = rand() % field.Size(); y = rand() % field.Size(); } while (!*quit && field.ValueAt(x, y) != 0); validMask = field.ValidMaskAt(x, y); if (validMask == 0) break; uint32 value; do { value = rand() % field.Size(); } while (!*quit && (validMask & (1UL << value)) == 0); field.SetValueAt(x, y, value + 1); } if (validMask == 0) continue; // try to solve it SudokuSolver solver(&field); solver.ComputeSolutions(); if (solver.CountSolutions() > 0) { // choose a random solution field.SetTo(solver.SolutionAt(rand() % solver.CountSolutions())); break; } } if (*quit) return; // next step: try to remove as many fields as possible (and wished) // that still have only a single solution int32 removeCount = field.Size() * field.Size() - fieldsLeft; bool tried[field.Size() * field.Size()]; int32 tries = field.Size() * field.Size() * 3 / 4; memset(tried, 0, sizeof(tried)); _Progress(progress, B_TRANSLATE("Searching for removable values"), 30.f); while (!*quit && removeCount > 0 && tries-- > 0) { SudokuField copy(field); uint32 x; uint32 y; do { x = rand() % field.Size(); y = rand() % field.Size(); } while (copy.ValueAt(x, y) == 0 || tried[x + y * field.Size()]); tried[x + y * field.Size()] = true; copy.SetValueAt(x, y, 0); if (_HasOnlyOneSolution(copy)) { _Progress(progress, NULL, 100.f - (70.f * removeCount / 70.f)); field.SetTo(©); removeCount--; } } if (*quit) return; if (tries <= 0) { puts("check all remove"); for (uint32 y = 0; y < field.Size(); y++) { for (uint32 x = 0; x < field.Size(); x++) { if (tried[x + y * field.Size()]) continue; SudokuField copy(field); copy.SetValueAt(x, y, 0); if (_HasOnlyOneSolution(copy)) { _Progress(progress, NULL, 100.f - (70.f * removeCount / 70.f)); field.SetTo(©); if (--removeCount <= 0 || *quit) break; } } if (removeCount <= 0 || *quit) break; } printf(" remove count = %" B_PRId32 "\n", removeCount); } // set the remaining values to be initial values for (uint32 y = 0; y < field.Size(); y++) { for (uint32 x = 0; x < field.Size(); x++) { if (field.ValueAt(x, y)) field.SetFlagsAt(x, y, kInitialValue); } } if (*quit) return; target->SetTo(&field); }