Guest User

Untitled

a guest
Jun 20th, 2018
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.98 KB | None | 0 0
  1. #include <algorithm>
  2. #include <array>
  3. #include <chrono>
  4. #include <iostream>
  5. #include <numeric>
  6. #include <string>
  7. #include <string_view>
  8. #include <vector>
  9.  
  10. #include "SudokuSolver.h"
  11.  
  12. int main()
  13. {
  14. std::string testSudoku1 =
  15. {
  16. "080090030030000069902063158020804590851907046394605870563040987200000015010050020" //MEDIUM SUDOKU
  17. };
  18. std::string testSudoku2 =
  19. {
  20. "001700806070000215000020000307040008000080000800060709000010000298000060706005300" //EASY SUDOKU
  21. };
  22. std::string testSudoku3 =
  23. {
  24. "075001002000000009090027040000094300000000000003810000030760010900000000600400580" //MEDIUM SUDOKU
  25. };
  26. std::string testSudoku4 =
  27. {
  28. "506000700010300000700050800000000020000070608000102040800090006030004000065000000" //EXTREME
  29. };
  30. std::string testSudoku5 =
  31. {
  32. "900784500700000930000001000080000006079030480300000070000200000065000004007468003" //EXTREME
  33. };
  34. std::string testSudoku6 =
  35. {
  36. "907000003000807000005003700040902050090060080010408020004200800000306000300000206" //EXTREME
  37. };
  38. std::string testSudoku7 =
  39. {
  40. "927000003000807000005003700040902050090060080010408020004200800000306000300000206" //INVALID BOARD
  41. };
  42. std::string testSudoku8 =
  43. {
  44. "0000000000000000" //4x4 EMPTY SUDOKU
  45. };
  46.  
  47. std::string testSudoku9 =
  48. {
  49. "000000010002000034000051000000006500070300080003000000000080000580000900690000000" //HYPERSUDOKU SUDOKU
  50. };
  51. std::vector<std::vector<short>> areasHyper(4, std::vector<short>(9)); //HYPERSUDOKU AREAS
  52. areasHyper[0] = { 10, 11, 12, 19, 20, 21, 28, 29, 30 };
  53. areasHyper[1] = { 14, 15, 16, 23, 24, 25, 32, 33, 34 };
  54. areasHyper[2] = { 46, 47, 48, 55, 56, 57, 64, 65, 66 };
  55. areasHyper[3] = { 50, 51, 52, 59, 60, 61, 68, 69, 70 };
  56.  
  57. std::string testSudoku10 =
  58. {
  59. "300000004002060100010908020005000600020000010009000800080304060004010900500000007" //NONOMINO SUDOKU
  60. };
  61. std::vector<std::vector<short>> areasN(9, std::vector<short>(9)); //NONOMINO AREAS, DISABLE BOXES WHEN SOLVING
  62. areasN[0] = { 0, 1, 2, 9, 10, 11, 18, 27, 28 };
  63. areasN[1] = { 3, 12, 13, 14, 23, 24, 25, 34, 35 };
  64. areasN[2] = { 4, 5, 6, 7, 8, 15, 16, 17, 26 };
  65. areasN[3] = { 19, 20, 21, 22, 29, 36, 37, 38, 39 };
  66. areasN[4] = { 30, 31, 32, 33, 40, 47, 48, 49, 50 };
  67. areasN[5] = { 41, 42, 43, 44, 51, 58, 59, 60, 61 };
  68. areasN[6] = { 45, 46, 55, 56, 57, 66, 67, 68, 77 };
  69. areasN[7] = { 54, 63, 64, 65, 72, 73, 74, 75, 76 };
  70. areasN[8] = { 52, 53, 62, 69, 70, 71, 78, 79, 80 };
  71.  
  72. std::string testSudoku11 =
  73. {
  74. "400805200000000000080070000000208907000000004105300000000000010000000000001007006" //X SUDOKU
  75. };
  76.  
  77. std::vector<std::vector<short>> areasX(2, std::vector<short>(9)); //X AREAS
  78.  
  79. areasX[0] = { 0, 10, 20, 30, 40, 50, 60, 70, 80 };
  80. areasX[1] = { 8, 16, 24, 32, 40, 48, 56, 64, 72 };
  81.  
  82. /*std::string sudoku; //TO GET A SUDOKU FROM INPUT
  83.  
  84. std::cout << "For empty cells type 0, without spaces" << std::endl;
  85. std::cout << "Input row 1 : ";
  86. std::cin >> sudoku;
  87.  
  88. int nRows = sudoku.length();
  89.  
  90. for (int i = 1; i < nRows; i++)
  91. {
  92. std::string row;
  93.  
  94. std::cout << "Input row " << i + 1 << " : ";
  95. std::cin >> row;
  96. sudoku += row;
  97. }
  98.  
  99. SudokuSolver solver(sudoku); */
  100.  
  101. SudokuSolver solver(testSudoku4); //SOLVE A TEST SUDOKU
  102.  
  103. int status = solver.solve();
  104. if (status == 1)
  105. {
  106. std::cout << "SOLVED" << std::endl;
  107. }
  108. else if (status == 0)
  109. {
  110. std::cout << "ERROR ON THE BOARD" << std::endl;
  111. }
  112.  
  113. std::cout << std::endl << solver << std::endl;
  114.  
  115. /*int nSolves = 1000; BENCHMARKING CODE
  116.  
  117. auto t0 = std::chrono::high_resolution_clock::now();
  118.  
  119. for (int i = 0; i < nSolves; i++)
  120. {
  121. solver.solve();
  122. }
  123.  
  124. auto t1 = std::chrono::high_resolution_clock::now();
  125.  
  126. auto time = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
  127.  
  128. std::cout << "Solving took " << time << " microsecs" << std::endl;
  129. std::cout << "Average solving time : " << (time) / nSolves << " microsecs" << std::endl;*/
  130.  
  131. return 0;
  132. }
  133.  
  134. enum class SudokuProgress : short
  135. {
  136. PROGRESS,
  137. NO_PROGRESS,
  138. CONFLICT
  139. };
  140.  
  141.  
  142. class Cell
  143. {
  144. private:
  145. std::vector<short> candidates;
  146.  
  147. short num;
  148. public:
  149. Cell(short maxCand) : candidates(maxCand), num(0)
  150. {
  151. std::iota(candidates.begin(), candidates.end(), 1);
  152. }
  153.  
  154. Cell(short n, short maxCand) : num(n) {}
  155.  
  156. const auto& getCandidates() const //CALL ONLY IF NOT SOLVED
  157. {
  158. return candidates;
  159. }
  160.  
  161. void resetCands()
  162. {
  163. candidates.clear();
  164. }
  165.  
  166. void setNum(int n)
  167. {
  168. num = n;
  169. }
  170.  
  171. short getNum() const
  172. {
  173. return num;
  174. }
  175.  
  176. short candidatesCount() const //CALL ONLY IF NOT SOLVED
  177. {
  178. return candidates.size();
  179. }
  180.  
  181. bool empty() const
  182. {
  183. return num == -1;
  184. }
  185.  
  186. SudokuProgress removeCandidate(short n)
  187. {
  188. if (!solved())
  189. {
  190. if (auto it = std::find(candidates.begin(), candidates.end(), n); it != candidates.end())
  191. {
  192. std::iter_swap(it, candidates.end() - 1);
  193. candidates.pop_back();
  194.  
  195. if (candidates.size() == 1)
  196. {
  197. num = candidates[0];
  198. }
  199.  
  200. return SudokuProgress::PROGRESS;
  201. }
  202. }
  203. else if (num == n)
  204. {
  205. num = -1;
  206. return SudokuProgress::CONFLICT;
  207. }
  208.  
  209. return SudokuProgress::NO_PROGRESS;
  210. }
  211.  
  212. bool solved() const
  213. {
  214. return num > 0;
  215. }
  216.  
  217. friend std::ostream& operator<<(std::ostream& os, const Cell& cell)
  218. {
  219. os << cell.getNum();
  220. return os;
  221. }
  222. };
  223.  
  224. #include "Cell.h"
  225.  
  226. typedef std::vector<Cell>::iterator cellIt;
  227. typedef short cellIndex;
  228.  
  229. typedef short ContID;
  230. typedef std::vector<short> uniqueContainer;
  231.  
  232. struct uniqueContainers : std::vector<uniqueContainer>
  233. {
  234. const ContID id;
  235.  
  236. uniqueContainers(const int& nConts, const int& contSize, const ContID& i) : std::vector<uniqueContainer>(nConts, uniqueContainer(contSize)), id(i) {}
  237.  
  238. uniqueContainers(const int& nConts, const int& contSize) : uniqueContainers(nConts, contSize, -1) {}
  239. };
  240.  
  241. class SudokuBoard : std::vector<Cell>
  242. {
  243. private:
  244. static std::vector<uniqueContainers> uniqueConts; //UNIQUE CONTAINERS(ROWS; COLUMNS AND BOXES)
  245. static short side;
  246.  
  247. static std::vector<short> contIndices;
  248.  
  249. short solvedCells;
  250.  
  251. static void initUniqueConts(bool box = true) //INITIALIZES DEFAULT UNIQUE CONTAINERS
  252. {
  253. uniqueConts.clear();
  254.  
  255. uniqueConts.emplace_back(side, side, 0); //ROWS
  256. uniqueConts.emplace_back(side, side, 1); //COLUMNS
  257. if (box)
  258. {
  259. uniqueConts.emplace_back(side, side, 2); //BOXES
  260. }
  261.  
  262. uniqueContainers& rows = uniqueConts[0];
  263. uniqueContainers& columns = uniqueConts[1];
  264.  
  265. for (int i = 0; i < side; i++)
  266. {
  267. for (int j = 0; j < side; j++)
  268. {
  269. rows[i][j] = side * i + j;
  270. columns[j][i] = side * i + j;
  271. }
  272. }
  273.  
  274. if (box)
  275. {
  276. uniqueContainers& boxes = uniqueConts[2];
  277. int boxSize = sqrt(side);
  278.  
  279. for (int i = 0; i < side; i++)
  280. {
  281. for (int j = 0; j < boxSize; j++)
  282. {
  283. const uniqueContainer& row = rows[(i / boxSize) * boxSize + j];
  284.  
  285. int rowOffset = i % boxSize * boxSize;
  286.  
  287. std::copy(row.begin() + rowOffset, row.begin() + (rowOffset + boxSize), boxes[i].begin() + j * boxSize);
  288. }
  289. }
  290. }
  291. }
  292.  
  293. static void addUniqueConts(const uniqueContainers& unique)
  294. {
  295. uniqueConts.emplace_back(unique.size(), unique[0].size(), uniqueConts.back().id + 1);
  296.  
  297. std::copy(unique.begin(), unique.end(), uniqueConts.back().begin());
  298. }
  299. public:
  300. SudokuBoard() {}
  301.  
  302. SudokuBoard(const SudokuBoard& other)
  303. {
  304. solvedCells = other.solvedCells;
  305. this->reserve(side*side);
  306.  
  307. for (auto& cell : other)
  308. {
  309. if (cell.solved())
  310. {
  311. this->emplace_back(cell.getNum(), side);
  312. }
  313. else
  314. {
  315. this->push_back(cell);
  316. }
  317. }
  318. }
  319.  
  320. SudokuBoard(SudokuBoard&& other) : std::vector<Cell>(std::move(other)), solvedCells(other.solvedCells) {}
  321.  
  322. void initCells(std::string_view boardStr) //CALL ONLY AFTER INITIALIZING UNIQUE CONTAINERS
  323. {
  324. side = sqrt(boardStr.length());
  325. contIndices.resize(boardStr.length()*uniqueConts.size());
  326.  
  327. std::fill(contIndices.begin(), contIndices.end(), -1);
  328.  
  329. for (int i = 0; i < boardStr.length(); i++)
  330. {
  331. short n = boardStr[i] - '0';
  332.  
  333. for (const uniqueContainers& uniqueCont : uniqueConts)
  334. {
  335. for (int j = 0; j < uniqueCont.size(); j++)
  336. {
  337. if (std::find(uniqueCont[j].begin(), uniqueCont[j].end(), i) != uniqueCont[j].end())
  338. {
  339. contIndices[i + uniqueCont.id * boardStr.length()] = j;
  340. break;
  341. }
  342. }
  343. }
  344.  
  345. if (n == 0)
  346. {
  347. this->emplace_back(side);
  348. }
  349. else
  350. {
  351. this->emplace_back(n, side);
  352. solvedCells++;
  353. }
  354. }
  355. }
  356.  
  357. short& getContIndex(const cellIndex& cellIndex, const ContID& uniqueContIndex) const
  358. {
  359. return contIndices[cellIndex + uniqueContIndex * this->size()];
  360. }
  361.  
  362. bool solved() const
  363. {
  364. return solvedCells == this->size();
  365. }
  366.  
  367. friend std::ostream& operator<<(std::ostream& os, const SudokuBoard& board)
  368. {
  369. for (int i = 0; i < side; i++)
  370. {
  371. for (int j = 0; j < side; j++)
  372. {
  373. if (board[j + board.side * i].solved())
  374. {
  375. os << board[j + side * i];
  376. }
  377. else
  378. {
  379. os << '0';
  380. }
  381.  
  382. os << " ";
  383. }
  384.  
  385. if (i < side - 1)
  386. os << std::endl;
  387. }
  388.  
  389. return os;
  390. }
  391.  
  392. friend class SudokuSolver;
  393. };
  394.  
  395.  
  396. std::vector<short> SudokuBoard::contIndices;
  397. std::vector<uniqueContainers> SudokuBoard::uniqueConts;
  398. short SudokuBoard::side;
  399.  
  400. #include "SudokuBoard.h"
  401.  
  402. class SudokuSolver
  403. {
  404. private:
  405. SudokuBoard startBoard;
  406.  
  407. int totalBoards;
  408. bool uniqueSol;
  409.  
  410. std::vector<SudokuBoard> solutions;
  411.  
  412. SudokuProgress removeCandidate(SudokuBoard& board, const int &eraser, const uniqueContainer& cont, std::vector<cellIndex>& nextCheck) //ERASER MUST BE A SOLVED CELL
  413. {
  414. SudokuProgress status = SudokuProgress::NO_PROGRESS;
  415.  
  416. for (const cellIndex& cell : cont)
  417. {
  418. if (cell != eraser)
  419. {
  420. SudokuProgress progress = board[cell].removeCandidate(board[eraser].getNum());
  421.  
  422. if (progress == SudokuProgress::PROGRESS) //removeCandidate(int) INSIDE CELL; NOT RECURSIVE
  423. {
  424. if (board[cell].solved()) //CHECK FOR SOLVED CELLS
  425. {
  426. nextCheck.push_back(cell);
  427. board.solvedCells++;
  428.  
  429. status = SudokuProgress::PROGRESS;
  430. }
  431. }
  432. else if (progress == SudokuProgress::CONFLICT) //INVALID BOARD
  433. {
  434. return SudokuProgress::CONFLICT;
  435. }
  436. }
  437. }
  438.  
  439. return status;
  440. }
  441.  
  442. SudokuProgress removeConflicts(SudokuBoard& board, std::vector<cellIndex>& toCheck) //REMOVE ILLEGAL CANDIDATES FROM CELLS
  443. {
  444. SudokuProgress status = SudokuProgress::NO_PROGRESS;
  445.  
  446. std::vector<cellIndex> check(std::move(toCheck));
  447. toCheck.reserve(check.size());
  448.  
  449. for (const uniqueContainers& uniqueConts : board.uniqueConts)
  450. {
  451. for (const cellIndex& cell : check)
  452. {
  453. int contIndex = board.getContIndex(cell, uniqueConts.id);
  454.  
  455. if (contIndex != -1)
  456. {
  457. SudokuProgress solvedCount = removeCandidate(board, cell, uniqueConts[contIndex], toCheck);
  458.  
  459. if (solvedCount == SudokuProgress::PROGRESS)
  460. {
  461. status = SudokuProgress::PROGRESS;
  462. }
  463. else if (solvedCount == SudokuProgress::CONFLICT)
  464. return SudokuProgress::CONFLICT;
  465. }
  466. }
  467. }
  468.  
  469. return status;
  470. }
  471.  
  472. bool solve(SudokuBoard board, std::vector<cellIndex> toCheck)
  473. {
  474. bool solved = false;
  475.  
  476. while (true)
  477. {
  478. SudokuProgress status = SudokuProgress::NO_PROGRESS;
  479.  
  480. do
  481. {
  482.  
  483. status = removeConflicts(board, toCheck);
  484.  
  485. } while (status == SudokuProgress::PROGRESS);
  486.  
  487. if (status == SudokuProgress::CONFLICT)
  488. {
  489. break;
  490. }
  491.  
  492. if (board.solved())
  493. {
  494. solutions.emplace_back(std::move(board));
  495.  
  496. return true;
  497. }
  498. else
  499. {
  500. cellIt leastCands = board.begin();
  501.  
  502. for (auto cell = leastCands + 1; cell != board.end(); ++cell)
  503. {
  504. if (!cell->solved())
  505. {
  506. if (cell->candidatesCount() < leastCands->candidatesCount() || leastCands->solved())
  507. {
  508. leastCands = cell;
  509. }
  510. }
  511. }
  512.  
  513. toCheck.push_back(leastCands - board.begin()); //toCheck IS GUARANTEED TO BE EMPTY BEFORE THIS LINE
  514.  
  515. board.solvedCells++;
  516.  
  517. auto& cands = leastCands->getCandidates();
  518.  
  519. for (auto cand = cands.begin(); cand != cands.end() - 1; ++cand)
  520. {
  521. totalBoards++;
  522.  
  523. leastCands->setNum(*cand);
  524.  
  525. if (solve(board, toCheck))
  526. {
  527. solved = true;
  528.  
  529. if (uniqueSol)
  530. return true;
  531. }
  532. }
  533.  
  534. leastCands->setNum(cands.back());
  535. }
  536. }
  537.  
  538. return solved;
  539. }
  540.  
  541. public:
  542. SudokuSolver(std::string_view boardStr) : totalBoards(0)
  543. {
  544. SudokuBoard::side = sqrt(boardStr.length());
  545. SudokuBoard::initUniqueConts();
  546.  
  547. startBoard.initCells(boardStr);
  548. }
  549.  
  550. SudokuSolver(std::string_view boardStr, const std::vector<uniqueContainers>& areas, bool boxes = true) : totalBoards(0)
  551. {
  552. SudokuBoard::side = sqrt(boardStr.length());
  553. SudokuBoard::initUniqueConts(boxes);
  554. for (const auto& area : areas)
  555. {
  556. SudokuBoard::addUniqueConts(area);
  557. }
  558.  
  559. startBoard.initCells(boardStr);
  560. }
  561.  
  562. int solve(bool uniqueSolution = false)
  563. {
  564. uniqueSol = uniqueSolution;
  565. std::vector<cellIndex> toCheck;
  566.  
  567. for (auto cell = startBoard.begin(); cell != startBoard.end(); ++cell)
  568. {
  569. if (cell->solved())
  570. {
  571. toCheck.push_back(cell - startBoard.begin());
  572. }
  573. }
  574.  
  575. totalBoards++;
  576. return solve(startBoard, std::move(toCheck));
  577. }
  578.  
  579. friend std::ostream& operator<<(std::ostream& os, const SudokuSolver& solver)
  580. {
  581. os << "TESTED " << solver.totalBoards << " BOARDS" << std::endl;
  582.  
  583. if (solver.uniqueSol)
  584. {
  585. os << "FIRST SOLUTION FOUND" << std::endl;
  586. }
  587. else
  588. {
  589. os << solver.solutions.size() << " SOLUTIONS FOUND" << std::endl;
  590. }
  591.  
  592. for (const auto& sol : solver.solutions)
  593. os << sol << std::endl << std::endl;
  594.  
  595. os << "INITIAL BOARD " << std::endl << solver.startBoard << std::endl;
  596.  
  597. return os;
  598. }
  599. };
Add Comment
Please, Sign In to add comment