166 lines
5.7 KiB
C++
166 lines
5.7 KiB
C++
#include "sudoku.h"
|
|
#include <nd-wfc/wfc.hpp>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <chrono>
|
|
#include <algorithm>
|
|
#include <iomanip>
|
|
|
|
// Helper function to load puzzles from a file (one puzzle per line)
|
|
std::vector<Sudoku> loadPuzzlesFromFile(const std::string& filename) {
|
|
std::vector<Sudoku> puzzles;
|
|
std::ifstream file(filename);
|
|
|
|
if (!file.is_open()) {
|
|
std::cerr << "Failed to open file: " << filename << std::endl;
|
|
return puzzles;
|
|
}
|
|
|
|
std::string line;
|
|
while (std::getline(file, line)) {
|
|
// Remove whitespace
|
|
line.erase(std::remove_if(line.begin(), line.end(),
|
|
[](char c) { return std::isspace(c); }), line.end());
|
|
|
|
if (line.empty()) continue;
|
|
|
|
Sudoku sudoku;
|
|
if (sudoku.loadFromString(line)) {
|
|
puzzles.push_back(std::move(sudoku));
|
|
}
|
|
}
|
|
|
|
return puzzles;
|
|
}
|
|
|
|
// Helper function to print a puzzle in a nice format
|
|
void printPuzzle(const Sudoku& sudoku, const std::string& title = "") {
|
|
if (!title.empty()) {
|
|
std::cout << title << std::endl;
|
|
std::cout << std::string(title.length(), '=') << std::endl;
|
|
}
|
|
|
|
for (int row = 0; row < 9; ++row) {
|
|
for (int col = 0; col < 9; ++col) {
|
|
uint8_t value = sudoku.get(row, col);
|
|
std::cout << (value == 0 ? '.' : static_cast<char>('0' + value));
|
|
if (col < 8) std::cout << " ";
|
|
if (col == 2 || col == 5) std::cout << "| ";
|
|
}
|
|
std::cout << std::endl;
|
|
if (row == 2 || row == 5) {
|
|
std::cout << "------+-------+------" << std::endl;
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
// Helper function to count filled cells in a puzzle
|
|
int countFilledCells(const Sudoku& sudoku) {
|
|
int count = 0;
|
|
for (int i = 0; i < 81; ++i) {
|
|
if (sudoku.get(i / 9, i % 9) != 0) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Analyze a single failing puzzle in detail
|
|
void analyzeFailingPuzzle(const Sudoku& originalPuzzle, int puzzleIndex) {
|
|
std::cout << "\n" << std::string(60, '=') << std::endl;
|
|
std::cout << "ANALYZING FAILING PUZZLE #" << puzzleIndex << std::endl;
|
|
std::cout << std::string(60, '=') << std::endl;
|
|
|
|
// Show original puzzle
|
|
printPuzzle(originalPuzzle, "Original Puzzle");
|
|
|
|
// Show statistics
|
|
int filledCells = countFilledCells(originalPuzzle);
|
|
std::cout << "Statistics:" << std::endl;
|
|
std::cout << " Filled cells: " << filledCells << "/81 (" << std::fixed << std::setprecision(1)
|
|
<< (filledCells * 100.0 / 81) << "%)" << std::endl;
|
|
std::cout << " Empty cells: " << (81 - filledCells) << std::endl;
|
|
std::cout << " Is valid: " << (originalPuzzle.isValid() ? "Yes" : "No") << std::endl;
|
|
std::cout << " Is solved: " << (originalPuzzle.isSolved() ? "Yes" : "No") << std::endl;
|
|
std::cout << std::endl;
|
|
|
|
// Try different solving approaches
|
|
|
|
// Test 1: Try solving with different configurations
|
|
std::cout << "Testing different solving approaches:" << std::endl;
|
|
|
|
// Test with verbose output to see what happens
|
|
Sudoku testPuzzle = originalPuzzle;
|
|
std::cout << "\nTest 1: Solving with verbose output..." << std::endl;
|
|
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
SudokuSolver::Run(testPuzzle, true); // Enable verbose output
|
|
auto end = std::chrono::high_resolution_clock::now();
|
|
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
|
|
|
|
bool solved = testPuzzle.isSolved();
|
|
std::cout << "Result: " << (solved ? "SOLVED" : "FAILED") << std::endl;
|
|
std::cout << "Time taken: " << duration.count() << "ms" << std::endl;
|
|
|
|
if (solved) {
|
|
printPuzzle(testPuzzle, "Solved Puzzle");
|
|
}
|
|
|
|
// Test 2: Multiple attempts
|
|
if (!solved) {
|
|
std::cout << "\nTest 2: Multiple solving attempts..." << std::endl;
|
|
|
|
int attempts = 3;
|
|
for (int attempt = 1; attempt <= attempts; ++attempt) {
|
|
Sudoku attemptPuzzle = originalPuzzle;
|
|
std::cout << "Attempt " << attempt << ": ";
|
|
|
|
auto attemptStart = std::chrono::high_resolution_clock::now();
|
|
SudokuSolver::Run(attemptPuzzle, false); // No verbose output
|
|
auto attemptEnd = std::chrono::high_resolution_clock::now();
|
|
auto attemptDuration = std::chrono::duration_cast<std::chrono::milliseconds>(attemptEnd - attemptStart);
|
|
|
|
bool attemptSolved = attemptPuzzle.isSolved();
|
|
std::cout << (attemptSolved ? "SOLVED" : "FAILED")
|
|
<< " (" << attemptDuration.count() << "ms)" << std::endl;
|
|
|
|
if (attemptSolved) {
|
|
std::cout << "SUCCESS on attempt " << attempt << "!" << std::endl;
|
|
printPuzzle(attemptPuzzle, "Successfully Solved Puzzle");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
const std::string failingPuzzlesFile = "/home/connor/repos/nd-wfc/demos/sudoku/data/Sudoku_failing.txt";
|
|
|
|
std::cout << "Loading failing puzzles from: " << failingPuzzlesFile << std::endl;
|
|
|
|
// Load failing puzzles
|
|
auto failingPuzzles = loadPuzzlesFromFile(failingPuzzlesFile);
|
|
|
|
if (failingPuzzles.empty()) {
|
|
std::cout << "No failing puzzles found!" << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
std::cout << "Found " << failingPuzzles.size() << " failing puzzles" << std::endl;
|
|
|
|
// Analyze each failing puzzle
|
|
for (size_t i = 0; i < failingPuzzles.size(); ++i) {
|
|
analyzeFailingPuzzle(failingPuzzles[i], i + 1);
|
|
}
|
|
|
|
std::cout << "\n" << std::string(60, '=') << std::endl;
|
|
std::cout << "ANALYSIS COMPLETE" << std::endl;
|
|
std::cout << "Total failing puzzles analyzed: " << failingPuzzles.size() << std::endl;
|
|
std::cout << std::string(60, '=') << std::endl;
|
|
|
|
return 0;
|
|
}
|