Sudoku Puzzle Solver with Dual Display
Solves all legal, single-solution Sudoku puzzles using multiple solver algorithms, chain techniques and recursive descent/backtrack.
Components and supplies
Matrix Keypad 4x4
Arduino Mega 2560
Generic Acrylic enclosure
IL19341 2.8" TFT display with micro-SD slot
9V 1A Switching Wall Power Supply
Project description
Code
Sudoku_Puzzles.h
c_cpp
Demo puzzles
1 2//#define Test_Only // define to truncate test/demo list 3 4 5/* 6 * Puzzles are loaded into both the puzzle grid and the solution grid 7 * Entries of numbers 1-9 are loaded into initial puzzle grid and solution grid 8 * Entries of numbers 11-19 are loaded ONLY into the solution grid (used to check puzzle algorithms) 9 * Remember to true up Puzzles Number total to match 10 * Each puzzle is one-dimensional (0-80) and loaded into each row and column by Test_Transfer routine 11 * PROGMEM is used to conserve global variable space (non-program-storage space) 12 * Some basic information on solving techiques is included as a comment after the puzzle definitions 13 */ 14 15 16const byte Easy [81 ] PROGMEM = // easy - solvable ***** 17{ 3,12,17,19,4,11,8,15,16, 18 16,5,4,7,12,8,9,13,11, 19 1,8,19,15,16,3,7,14,2, 20 12,1,16,18,19,14,5,7,3, 21 17,14,5,11,13,16,2,18,19, 22 9,3,8,12,15,17,11,6,14, 23 4,19,1,6,18,15,13,2,7, 24 18,16,2,3,17,9,4,1,15, 25 15,17,3,14,1,12,16,19,8}; 26 27const byte HR_1 [81 ] PROGMEM = // test hidden rectangle and has a WXYZ wing clear too 28{ 3,9,4,6,8,2,1,5,7, 29 6,1,8,7,3,5,4,2,9, 30 5,2,7,4,19,11,3,6,8, 31 1,5,3,19,4,7,12,18,6, 32 4,7,12,5,6,8,19,3,1, 33 8,6,19,11,12,13,7,4,5, 34 7,3,6,8,11,4,15,19,2, 35 9,4,5,12,17,16,18,11,3, 36 2,8,1,13,15,19,16,17,4}; 37 38const byte HR_2 [81 ] PROGMEM = // test hidden rectangle also 39{ 16,9,11,4,5,13,2,18,17, 40 3,15,2,7,8,11,16,9,14, 41 7,14,18,2,9,16,15,1,13, 42 9,3,5,1,2,8,7,4,6, 43 18,7,14,6,13,9,11,12,15, 44 12,11,16,5,14,7,18,13,9, 45 1,2,9,3,7,5,4,6,8, 46 5,6,3,8,1,4,9,17,12, 47 4,8,7,9,6,2,3,5,1}; 48 49const byte WXYZ [81 ] PROGMEM = // WXYZ Wing test 50{ 7,1,19,18,4,13,15,12,16, 51 5,6,14,2,11,7,18,3,19, 52 8,2,13,16,15,9,7,14,11, 53 6,5,7,4,13,11,2,9,18, 54 2,14,8,19,6,15,3,11,7, 55 19,3,1,7,18,2,14,6,15, 56 14,19,6,3,7,18,11,15,12, 57 11,7,2,5,19,4,6,8,3, 58 3,18,5,11,2,6,19,7,4}; 59 60const byte Forbidding_1 [81 ] PROGMEM = // test forbidding chain with group node 61{ 12,6,18,15,1,3,7,14,9, 62 5,4,11,19,17,8,13,6,2, 63 13,7,9,6,2,14,8,15,11, 64 11,8,13,2,4,15,9,7,16, 65 17,19,4,11,13,16,5,2,8, 66 16,2,15,17,8,9,14,1,13, 67 18,13,17,14,16,2,11,9,5, 68 4,5,12,13,19,11,16,8,7, 69 19,11,6,8,5,17,12,3,14}; 70/* 71const byte Forbidding_2 [81 ] PROGMEM = // test forbidding chain with group node (not loaded now) 72{ 17,5,2,4,3,1,9,6,18, 73 18,3,6,17,19,12,14,1,5, 74 1,9,14,18,16,15,13,2,17, 75 2,11,17,13,14,19,15,18,6, 76 9,4,18,16,15,17,12,13,11, 77 5,6,13,11,12,18,7,4,9, 78 3,17,9,12,18,16,11,15,14, 79 6,12,5,19,1,14,18,17,13, 80 14,8,11,5,17,13,6,9,12}; 81*/ 82 83const byte XC1 [81 ] PROGMEM = // Tests of X Chain 84{ 2,7,11,18,6,13,5,4,19, 85 19,5,14,1,2,7,13,8,16, 86 13,16,18,4,15,19,2,7,11, 87 18,11,19,13,4,6,7,5,2, 88 16,2,7,5,19,8,4,1,13, 89 5,14,13,7,1,2,9,16,8, 90 1,3,6,2,7,4,8,9,5, 91 7,8,5,19,13,1,16,2,4, 92 14,19,2,16,18,15,1,13,7}; 93 94const byte XC2 [81 ] PROGMEM = // Tests of X Chain 95{ 3,17,4,5,2,11,19,8,16, 96 11,18,6,14,9,13,15,17,12, 97 19,5,12,18,7,16,3,11,14, 98 15,14,17,6,8,9,11,2,3, 99 12,19,11,7,3,4,16,15,18, 100 18,6,3,1,5,2,7,14,19, 101 14,1,15,9,6,18,12,13,17, 102 17,12,9,13,4,15,18,6,11, 103 6,13,8,2,1,7,14,19,5}; 104 105const byte XC3 [ 81] PROGMEM = // Tests of X Chain 106{ 12,14,16,3,5,1,7,8,19, 107 8,5,7,6,2,9,3,4,1, 108 1,19,13,8,7,4,16,15,2, 109 5,13,9,1,6,2,8,17,4, 110 6,8,1,15,4,17,2,19,13, 111 14,17,12,19,13,8,1,6,15, 112 7,1,8,14,19,13,15,2,16, 113 19,12,15,17,1,16,14,13,8, 114 13,6,14,12,8,15,19,1,7}; 115 116const byte UR_3a [81 ] PROGMEM = // check UR3 117{ 9,12,4,15,7,16,18,1,13, 118 1,3,7,12,18,9,6,14,15, 119 6,15,8,14,13,1,12,9,7, 120 5,1,9,17,12,13,4,6,8, 121 2,7,3,16,14,18,1,5,9, 122 4,8,6,9,1,5,7,3,2, 123 17,16,2,13,5,14,19,18,1, 124 13,19,1,8,16,12,15,17,14, 125 18,4,5,1,9,17,13,2,6}; 126 127const byte Medium_1[81 ]PROGMEM = // medium - solvable 128{ 19,1,16,2,13,5,17,14,8, 129 17,2,4,9,8,16,11,13,5, 130 13,5,8,7,14,1,19,2,16, 131 6,19,5,3,17,2,14,8,1, 132 2,18,7,1,16,14,3,15,9, 133 14,13,1,5,19,8,2,6,17, 134 11,14,2,6,15,7,8,19,13, 135 8,16,13,14,1,19,5,17,2, 136 15,17,9,18,2,3,16,1,14}; 137 138const byte Medium_2 [81 ]PROGMEM = // medium 2 139{ 13,19,15,16,4,7,12,1,18, 140 17,2,18,5,11,13,19,14,6, 141 14,16,11,9,12,18,17,13,5, 142 12,15,3,11,19,14,18,6,17, 143 19,17,4,8,16,12,11,15,13, 144 11,18,6,13,17,15,4,9,2, 145 16,4,12,17,3,19,15,18,11, 146 8,11,19,12,15,6,13,7,14, 147 5,13,17,4,18,1,16,12,19}; 148 149const byte Difficult_1 [81 ]PROGMEM = // difficult 150{ 18,17,13,5,4,1,16,19,12, 151 14,15,6,12,7,19,11,13,8, 152 12,9,11,16,13,8,15,17,14, 153 5,6,18,14,2,13,19,1,17, 154 17,11,2,19,15,16,4,18,13, 155 13,14,19,11,18,7,12,16,15, 156 11,8,14,3,16,5,17,12,9, 157 19,12,17,18,1,14,13,15,6, 158 16,3,5,17,19,12,18,14,11}; 159 160const byte UR [81 ]PROGMEM = // Invokes Unique Rectangle 1 161{ 16,18,15,1,13,12,14,7,19, 162 7,3,14,15,9,18,11,16,12, 163 2,11,19,17,6,4,15,3,18, 164 19,12,6,18,17,1,13,4,15, 165 18,15,1,3,14,19,17,2,6, 166 14,17,13,2,15,16,8,19,11, 167 15,6,8,4,12,17,9,11,13, 168 3,14,12,9,11,15,16,18,17, 169 1,19,17,16,18,13,12,15,14}; 170 171const byte UR2V [81 ]PROGMEM = // Invokes Unique Rectangle 2, 2nd one 172{ 11,7,9,13,14,12,15,18,6, // Vertical x.x 173 3,18,5,6,19,1,4,17,12, // ... 174 6,14,12,18,15,17,13,11,19, // Box x.x 175 5,6,4,17,13,8,19,2,11, 176 2,9,8,5,1,4,6,3,7, 177 7,1,3,2,16,19,8,5,4, 178 14,13,17,19,12,15,11,6,8, 179 8,5,11,4,17,16,2,19,3, 180 19,12,6,11,18,13,17,4,15}; 181 182 183const byte UR2H [81 ]PROGMEM = // Invokes Unique Rectangle 2 184{ 4,6,9,5,18,13,2,7,1, // Horizontal x.........x 185 1,8,5,7,6,2,4,3,9, // Box x.........x 186 7,2,3,9,11,14,8,5,6, 187 6,4,7,8,5,9,3,1,2, 188 3,1,8,4,2,7,6,9,5, 189 9,5,2,11,13,6,7,8,4, 190 12,17,4,13,9,11,5,6,18, 191 18,13,1,6,14,5,9,12,17, 192 5,9,6,12,7,18,1,14,13}; 193 194 195const byte UR5 [81 ] PROGMEM = // test case for UR type 5 196{ 13,9,11,8,14,16,2,5,17, 197 8,17,15,2,19,13,16,1,14, 198 14,6,12,7,11,15,13,8,19, 199 9,8,4,5,2,1,7,6,3, 200 7,2,6,4,3,8,5,9,1, 201 5,1,3,19,16,7,4,2,8, 202 12,3,19,16,8,4,1,7,5, 203 1,5,8,3,7,12,19,4,16, 204 16,14,17,1,5,19,8,3,12}; 205 206const byte Expert [81 ] PROGMEM = 207{ 12,19,14,11,16,15,18,13,17, 208 16,11,7,8,3,14,9,15,12, 209 13,18,5,19,17,2,6,4,11, 210 15,13,2,6,18,11,14,7,19, 211 17,4,11,12,15,19,13,8,16, 212 18,6,19,17,14,3,2,11,15, 213 19,2,8,4,11,17,5,16,13, 214 14,17,13,15,9,6,1,12,18, 215 11,15,16,13,12,18,17,19,14}; 216 217const byte FC [81 ]PROGMEM = // Requires a forcing chain 218{ 4,11,15,2,7,13,6,19,18, 219 7,9,8,1,5,6,2,3,4, 220 16,2,13,8,4,19,15,11,7, 221 2,3,7,4,6,8,9,5,1, 222 8,4,9,5,3,1,7,2,6, 223 5,6,1,7,9,2,8,4,3, 224 13,8,2,16,1,5,4,7,9, 225 11,7,16,19,2,4,3,18,15, 226 19,15,4,13,8,7,11,16,2}; 227 228const byte XWing [81 ] PROGMEM = // Test x-wing and xy-wing algorithms 229{ 1,18,17,14,12,13,5,6,9, 230 4,9,2,17,5,6,1,13,8, 231 13,5,6,1,18,9,2,4,17, 232 15,13,9,6,4,17,8,12,1, 233 17,6,4,12,1,18,19,15,13, 234 2,1,8,19,3,5,6,17,4, 235 18,4,13,5,19,12,17,1,6, 236 9,17,5,13,6,1,4,18,2, 237 6,2,1,18,17,14,13,19,5}; 238 239const byte Swordfish_1 [81 ] PROGMEM = // Test Swordfish algorithm 240{ 1,9,5,3,6,7,2,4,8, 241 12,7,8,11,5,14,3,6,9, 242 3,14,6,12,9,8,1,5,7, 243 16,12,3,7,8,11,5,9,14, 244 7,11,9,14,12,5,18,13,6, 245 5,8,4,9,13,6,7,1,12, 246 8,3,2,5,4,9,6,7,1, 247 9,16,7,18,1,3,14,2,5, 248 14,5,1,16,7,2,9,18,13}; 249 250const byte Jellyfish_1 [81 ] PROGMEM = // Test Jellyfish algorithm 251{ 14,6,17,3,11,9,12,8,15, 252 11,15,3,17,18,12,6,14,19, 253 2,19,18,14,6,15,17,13,1, 254 16,8,12,1,15,3,14,9,17, 255 19,11,15,18,7,14,13,12,16, 256 13,7,14,2,19,6,11,5,18, 257 7,12,11,19,3,18,15,16,4, 258 15,14,9,16,12,17,8,11,13, 259 18,3,16,5,14,1,19,7,12}; 260 261const byte Diabolical_1 [81 ] PROGMEM = // Uses many algorithms incl. two forcing chains 262{ 1,18,16,13,14,15,12,7,19, 263 19,3,2,18,7,11,14,16,15, 264 5,4,7,16,2,9,18,13,1, 265 17,5,1,19,13,14,16,12,18, 266 3,16,19,2,18,7,15,11,4, 267 14,12,18,15,11,16,3,9,17, 268 2,11,13,4,9,18,7,5,6, 269 16,17,14,11,5,13,9,8,12, 270 18,9,15,17,16,12,11,14,3}; 271 272const byte Diabolical_2 [81 ] PROGMEM = // test case for UR type 5, other goodies, including XYZ Wing 273{ 3,14,18,15,11,17,19,6,12, 274 15,6,11,4,12,19,8,7,13, 275 19,17,12,16,3,8,4,15,1, 276 12,8,16,17,4,11,13,9,15, 277 14,5,19,2,8,3,17,1,16, 278 17,1,13,19,5,16,12,4,18, 279 1,12,4,3,7,15,16,18,19, 280 18,9,7,11,16,2,15,3,14, 281 16,3,15,18,19,14,11,12,7}; 282 283 284const byte Diabolical_3 [81 ] PROGMEM = // Was recursive until chains got to it, does show XYZ Wing too! 285{ 18,17,11,5,16,12,14,3,9, 286 16,3,9,18,4,1,2,5,7, 287 14,12,5,17,3,9,6,8,11, 288 15,9,14,3,2,17,11,6,18, 289 12,11,8,9,5,6,7,4,13, 290 17,16,13,14,1,8,9,2,15, 291 19,4,6,1,18,5,3,17,12, 292 13,8,7,12,9,14,5,1,16, 293 11,5,12,16,17,13,18,19,14}; 294 295const byte Diabolical_4 [81 ] PROGMEM = // was used to fix UR 5 error 296{ 4,13,8,15,9,16,11,2,17, 297 15,7,6,12,14,11,8,13,19, 298 2,19,11,7,18,3,14,16,15, 299 17,15,9,1,16,12,13,18,4, 300 16,1,14,19,3,18,17,5,12, 301 3,18,12,14,17,5,6,19,11, 302 18,14,15,3,11,9,12,17,6, 303 11,12,3,16,15,17,9,4,18, 304 19,6,17,18,2,14,5,11,3}; 305 306 307const byte Diabolical_5 [81] PROGMEM = //Does have XYZ but still stumped and requires recursive search! 308{ 11,5,16,13,14,8,17,19,2, 309 18,19,17,6,12,11,13,15,14, 310 13,4,2,5,9,17,1,18,16, 311 14,11,13,8,17,16,19,12,5, 312 16,17,9,12,1,15,8,14,13, 313 2,18,15,19,13,4,16,11,17, 314 17,13,1,14,5,9,2,6,18, 315 15,12,18,11,16,3,14,17,19, 316 9,16,14,7,18,12,15,3,11}; 317 318 319const byte RP_1 [81 ] PROGMEM = // Tests Remote_Pair 320{ 1,7,8,6,14,9,13,5,12, 321 9,3,4,1,5,12,6,18,7, 322 2,5,6,7,18,3,14,1,19, 323 7,9,3,5,6,18,12,4,1, 324 6,4,1,12,3,7,5,9,18, 325 8,2,5,9,1,4,7,3,6, 326 5,6,7,3,19,1,18,12,14, 327 4,1,12,18,7,5,19,6,13, 328 3,8,19,4,12,6,1,7,5}; 329 330const byte RP_2 [81 ] PROGMEM = // Tests Remote_Pair 331{ 7,9,8,4,5,2,3,1,6, 332 6,14,3,7,8,1,15,9,2, 333 15,1,2,19,3,16,8,7,14, 334 3,7,11,2,6,5,19,4,8, 335 8,2,19,1,4,3,7,6,15, 336 14,6,15,8,9,7,11,2,3, 337 9,8,16,15,1,4,2,3,7, 338 1,13,7,16,2,8,14,5,19, 339 2,15,14,13,7,19,16,8,1}; 340 341const byte MD_1 [81 ] PROGMEM = // Medusa 3-D tests 1 342{ 17,9,3,8,2,4,5,6,11, 343 14,8,5,6,13,11,19,17,2, 344 2,11,6,19,7,5,14,13,8, 345 3,2,1,7,6,9,8,4,5, 346 19,16,14,2,5,8,3,11,17, 347 5,7,8,11,4,13,2,9,6, 348 8,5,19,14,1,6,7,2,3, 349 11,14,7,13,8,2,6,5,19, 350 16,13,2,5,19,7,1,8,14}; 351 352const byte MD_1_B [81 ] PROGMEM = // Medusa 3-D tests 1, Sudoku coach 353{ 3,12,5,6,4,9,7,11,8, 354 7,4,11,8,2,15,13,9,16, 355 8,16,9,11,13,7,12,4,15, 356 1,3,4,5,7,2,6,8,9, 357 16,15,17,9,8,4,11,12,3, 358 9,8,2,13,6,11,5,7,4, 359 2,11,16,4,9,13,8,15,7, 360 15,9,8,7,1,16,4,3,2, 361 4,17,13,2,15,8,9,16,1}; 362 363 364const byte MD_1_37 [81 ] PROGMEM = // Medusa 3-D tests 1 with 37 eliminations! 365{ 16,17,12,9,11,8,4,3,15, 366 19,15,4,7,13,2,6,8,11, 367 13,8,1,16,5,4,19,17,2, 368 17,14,5,18,16,3,1,2,9, 369 11,19,16,5,2,17,3,14,8, 370 12,13,18,14,9,11,5,6,17, 371 15,16,13,12,7,9,8,1,14, 372 18,1,7,13,14,5,12,19,6, 373 4,12,19,1,18,6,17,5,13}; 374 375const byte MD_2 [81 ] PROGMEM = // Medusa 3-D Tests 2 376{ 3,18,16,11,5,2,14,19,17, 377 2,5,17,3,14,19,16,1,18, 378 19,11,4,6,18,7,5,2,3, 379 16,9,3,2,17,11,8,14,5, 380 5,7,11,18,16,14,12,3,19, 381 4,12,8,19,3,5,17,6,11, 382 11,16,5,4,19,8,3,17,12, 383 17,3,12,5,11,6,19,8,4, 384 8,4,19,17,2,3,11,5,6}; 385 386const byte MD_2_B [81 ] PROGMEM = // Medusa 3-D Tests 2, Series B 387{ 6,4,13,7,19,15,8,12,11, 388 12,7,11,18,6,4,15,13,19, 389 18,15,9,1,12,3,4,7,6, 390 5,11,7,4,13,18,19,6,2, 391 3,6,18,12,11,19,7,15,14, 392 14,9,2,15,7,6,1,18,13, 393 7,13,14,6,18,1,2,9,15, 394 9,2,15,13,4,7,6,1,18, 395 11,18,6,9,15,2,13,14,7}; 396 397 398const byte MD_3 [81 ] PROGMEM = // Medusa 3-D Tests Rule 3 399{ 2,9,16,15,17,14,8,3,11, 400 14,11,18,16,2,13,9,7,15, 401 15,13,17,1,18,9,4,16,2, 402 8,4,5,7,6,1,2,9,3, 403 6,12,11,13,19,18,5,4,7, 404 13,17,9,12,4,5,16,11,8, 405 9,18,3,4,15,7,11,12,16, 406 11,6,14,18,3,12,7,15,9, 407 17,5,12,19,11,16,3,8,4}; 408 409const byte MD_3_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 3, Series B 410{ 9,17,1,18,3,12,16,5,4, 411 8,5,12,16,14,11,17,3,19, 412 4,13,16,5,17,19,8,11,12, 413 15,11,7,14,12,6,13,9,8, 414 2,9,8,3,1,5,14,16,7, 415 6,4,13,7,19,18,1,2,15, 416 7,12,4,19,16,13,15,8,11, 417 11,6,15,12,18,14,19,7,13, 418 3,8,9,11,5,7,2,14,6}; 419 420const byte MD_4 [81 ] PROGMEM = // Medusa 3-D Tests Rule 4 421{ 1,19,12,18,5,6,17,14,3, 422 15,4,3,12,9,17,11,18,16, 423 8,17,16,11,4,3,15,19,2, 424 17,3,14,5,6,18,2,1,19, 425 9,5,18,4,2,1,16,3,7, 426 16,2,1,17,3,19,18,15,14, 427 3,1,7,9,8,12,14,16,5, 428 12,16,15,3,1,14,9,7,18, 429 14,18,19,6,7,15,3,12,1}; 430 431const byte MD_4_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 4, Series B 432{ 2,19,5,18,1,16,14,17,13, 433 11,4,18,13,9,7,16,5,12, 434 16,3,17,14,2,5,19,8,11, 435 8,17,4,16,5,11,13,12,19, 436 9,1,13,2,8,4,17,6,5, 437 5,16,12,9,7,13,8,11,4, 438 4,5,6,1,3,18,12,9,17, 439 13,12,11,7,6,19,5,4,18, 440 17,18,19,5,4,12,1,13,6}; 441 442const byte MD_5 [81 ] PROGMEM = // Medusa 3-D Tests Rule 5 443{ 9,2,3,4,18,7,16,1,5, 444 8,7,6,11,5,13,9,2,4, 445 5,14,11,2,19,16,18,3,17, 446 7,6,9,13,2,15,1,4,18, 447 4,3,2,18,16,11,17,5,9, 448 1,8,5,19,17,4,2,6,13, 449 13,9,8,16,4,2,15,7,1, 450 2,11,7,15,3,19,4,8,6, 451 16,15,14,7,11,8,13,9,2}; 452 453const byte MD_5_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 5, Series B 454{ 16,5,3,11,19,7,18,4,12, 455 9,14,11,6,18,2,7,3,5, 456 12,18,7,3,5,14,16,11,19, 457 7,1,12,14,16,3,5,19,8, 458 14,6,5,19,12,18,11,7,13, 459 8,13,9,5,7,11,14,2,16, 460 11,9,8,7,3,16,2,5,14, 461 5,12,14,8,11,9,13,16,7, 462 13,7,16,2,14,5,9,8,11}; 463 464const byte MD_6 [81 ] PROGMEM = // Medusa 3-D Tests Rule 6 465{ 9,8,6,7,2,1,3,4,5, 466 3,11,4,9,5,6,18,12,7, 467 15,12,7,18,3,14,9,6,11, 468 18,7,3,12,6,5,14,11,9, 469 6,9,12,14,1,7,15,18,3, 470 1,14,15,3,9,18,2,7,6, 471 14,15,18,6,7,9,11,3,12, 472 12,6,9,1,4,3,7,15,18, 473 7,3,1,5,8,2,6,9,4}; 474 475const byte MD_6_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 6, Series B 476{ 13,7,2,6,15,4,19,8,11, 477 16,11,15,13,8,9,4,7,12, 478 4,8,9,1,17,12,13,6,5, 479 15,19,1,8,14,13,6,12,7, 480 8,16,14,12,11,17,15,19,3, 481 2,13,7,19,16,15,8,11,14, 482 7,12,16,15,13,8,1,14,19, 483 11,14,3,7,9,16,12,15,8, 484 19,5,8,4,12,1,7,13,16}; 485 486const byte MD_3456 [81 ] PROGMEM = // Medusa 3-D Tests Rules 3,4,5,6 487{ 9,15,8,13,2,11,14,7,6, 488 6,12,14,19,15,7,1,18,13, 489 1,7,13,18,16,14,15,2,19, 490 17,18,5,4,13,16,12,9,1, 491 3,9,1,7,8,2,16,14,5, 492 4,6,12,11,19,5,8,3,17, 493 18,4,17,16,11,13,19,5,12, 494 5,13,6,12,14,19,17,1,18, 495 2,1,9,5,7,18,3,16,4}; 496 497const byte Workout [81 ] PROGMEM = // Uses many different solvers 498{ 11,18,16,14,12,5,17,19,3, 499 9,13,2,1,18,17,16,14,5, 500 7,4,15,16,13,19,11,8,12, 501 5,11,3,18,17,4,12,16,9, 502 16,9,18,13,15,12,14,17,1, 503 12,17,4,19,16,1,5,13,18, 504 8,16,11,5,14,13,9,2,17, 505 13,12,19,17,11,16,18,15,14, 506 14,15,17,2,9,18,3,11,16}; 507 508const byte Recursive_1 [ 81] PROGMEM = // Famous Arto Inkala puzzle 1 - one of the most difficult puzzles 509{ 1,16,12,18,15,7,14,9,13, 510 15,3,14,11,2,19,16,17,8, 511 17,18,9,6,14,13,5,12,11, 512 14,17,5,3,11,12,9,18,16, 513 19,1,13,15,8,16,17,14,2, 514 6,12,18,17,19,4,11,13,15, 515 3,15,16,14,17,18,12,1,19, 516 12,4,11,19,13,15,18,16,7, 517 18,19,7,12,16,11,3,15,14}; 518 519const byte Recursive_2 [81 ] PROGMEM = // 2nd Famous Arto Inkala puzzle - one of the most difficult puzzles 520{ 11,14,5,3,12,17,16,19,18, 521 8,13,19,16,15,14,11,2,17, 522 16,7,12,19,1,18,5,14,13, 523 4,19,16,11,18,5,3,17,12, 524 12,1,18,14,7,13,19,15,6, 525 17,15,3,2,19,16,14,8,11, 526 13,6,17,5,14,12,18,11,9, 527 19,18,4,17,16,11,12,3,15, 528 15,12,11,18,13,9,7,16,14}; 529 530// Matching pointers to each input grid and the name of the puzzle to display 531 532 533const char Puzzle_Names_1[] PROGMEM = "Easy"; 534const char Puzzle_Names_2[] PROGMEM = "Medium"; 535const char Puzzle_Names_3[] PROGMEM = "Difficult"; 536const char Puzzle_Names_4[] PROGMEM = "Expert"; 537const char Puzzle_Names_5[] PROGMEM = "Jellyfish"; 538const char Puzzle_Names_6[] PROGMEM = "Unique Rectangle 1"; 539const char Puzzle_Names_7[] PROGMEM = "Unique Rectangle 2"; 540const char Puzzle_Names_8[] PROGMEM = "Unique Rectangle 3"; 541const char Puzzle_Names_9[] PROGMEM = "Unique Rectangle 4"; 542const char Puzzle_Names_10[] PROGMEM = "Hidden Rectangle 1"; 543const char Puzzle_Names_11[] PROGMEM = "Hidden Rectangle 2"; 544const char Puzzle_Names_12[] PROGMEM = "Remote Pair 1"; 545const char Puzzle_Names_13[] PROGMEM = "Remote Pair 2"; 546const char Puzzle_Names_14[] PROGMEM = "Diabolical 1"; 547const char Puzzle_Names_15[] PROGMEM = "Diabolical 2"; 548const char Puzzle_Names_16[] PROGMEM = "Diabolical 3"; 549const char Puzzle_Names_17[] PROGMEM = "Medusa 1"; 550const char Puzzle_Names_18[] PROGMEM = "Medusa 2"; 551const char Puzzle_Names_19[] PROGMEM = "Medusa 3"; 552const char Puzzle_Names_20[] PROGMEM = "Medusa 4"; 553const char Puzzle_Names_21[] PROGMEM = "Medusa 5"; 554const char Puzzle_Names_22[] PROGMEM = "Medusa 6"; 555const char Puzzle_Names_22a[] PROGMEM = "Workout!"; 556const char Puzzle_Names_23[] PROGMEM = "Long Recursive 1"; 557const char Puzzle_Names_24[] PROGMEM = "Long Recursive 2"; 558 559 560#ifdef Test_Only // used in conjuntion with Test configurations in puzzle solver to auto run one puzzle on startup 561 562const byte* Puzzles_List [] = {Easy}; 563const char *const Puzzle_Names_Table[] PROGMEM = {Puzzle_Names_1}; 564#else 565const char *const Puzzle_Names_Table[] PROGMEM = {Puzzle_Names_1, Puzzle_Names_2, Puzzle_Names_3, Puzzle_Names_4, Puzzle_Names_5, 566 Puzzle_Names_6,Puzzle_Names_7, Puzzle_Names_8, Puzzle_Names_9, Puzzle_Names_10, 567 Puzzle_Names_11,Puzzle_Names_12,Puzzle_Names_13,Puzzle_Names_14,Puzzle_Names_15, 568 Puzzle_Names_16, Puzzle_Names_17, Puzzle_Names_18, Puzzle_Names_19,Puzzle_Names_20, 569 Puzzle_Names_21,Puzzle_Names_22,Puzzle_Names_22a,Puzzle_Names_23,Puzzle_Names_24 570 }; 571 572 573const byte* Puzzles_List [] = { Easy, 574 Medium_1, 575 Difficult_1, 576 Expert, 577 Jellyfish_1, 578 UR, 579 UR2H, 580 UR_3a, 581 UR5, 582 HR_1, 583 HR_2, 584 RP_1, 585 RP_2, 586 Diabolical_3, 587 Diabolical_4, 588 Diabolical_5, 589 MD_1 , 590 MD_1_B, 591 MD_2 , 592 MD_3 , 593 MD_5_B, 594 MD_6, 595 Workout, 596 Recursive_1, 597 Recursive_2}; 598#endif 599// end 600/* 601 * Solving Sudoku Puzzles 602Fill in all blank cells making sure that each row, column 603and 3 by 3 box contains the numbers 1 to 9. 604The Basics: 605Firstly, it's impossible to get very far without carefully 606maintaining a list of 'possible values' or candidates for each 607blank cell. Doing this by hand is laborious and prone to error, 608and often detracts from the fun of solving these puzzles. 609Fortunately, programs like this one will do this for you, 610while leaving you with the fun of applying logic to solve each 611puzzle. 612If you don't have a program to help - systematically analyse at 613each blank cell. Start with the assumption it can have any digit 614(or value) between 1 and 9, and then remove all values which 615have already been assigned to other cells in its respective row, 616column and 3x3 box. This leaves each blank cell with a list of 617candidates. 618Singles: 619Any cells which have only one candidate 620can safely be assigned that value. 621It is very important whenever a value is 622assigned to a cell, that this value is also 623excluded as a candidate from all other 624blank cells sharing the same row, 625column and box. (Programs like Simple 626Sudoku will do this laborious step 627automatically for you too.) 628Hidden Singles: 629Very frequently, there is only one 630candidate for a given row, column or box, 631but it is hidden among other candidates. 632In the example on the right, the 633candidate 6 is only found in the middle 634right cell of the 3x3 box. Since every box 635must have a 6, this cell must be that 6. 636Beyond the Basics: 637While the two steps above are the only ones which will directly 638assign a cell value, they will only solve the simplest puzzles. 639That's fortunate, otherwise Sudoku wouldn't be as popular as it 640is today. The following steps (in increasing complexity) will 641reduce the number of candidates in blank cells so, sooner or 642later, a 'single' candidate or 'hidden single' candidate will 643appear. 644 645Beyond the Basics 646 647Locked Candidates 1: (Pointing) 648Sometimes a candidate within a box is restricted to one row or 649column. Since one of these cells must contain that specific 650candidate, the candidate can safely be excluded from the 651remaining cells in that row or column outside of the box. 652In the example below, the right box only has candidate 2's in its 653bottom row. Since, one of those cells must be a 2, no cells in 654that row outside that box can be a 2. Therefore 2 can be 655excluded as a candidate from the highlighted cells. 656 657Locked Candidates 2: (Claiming) 658Sometimes a candidate within a row or 659column is restricted to one box. Since 660one of these cells must contain that 661specific candidate, the candidate can 662safely be excluded from the remaining 663cells in the box. 664 665Naked Pairs: 666If two cells in a group contain an identical pair of candidates and 667only those two candidates, then no other cells in that group could be those values. 668These 2 candidates can be excluded from other cells in the group. 669 670Advanced Steps: 671 672Naked Triples & Naked Quads: 673The same principle that applies to Naked 674Pairs applies to Naked Triples & Naked 675Quads. 676 677A Naked Triple occurs when three cells in 678a group contain no candidates other that 679the same three candidates. The cells 680which make up a Naked Triple don't have 681to contain every candidate of the triple. If 682these candidates are found in other cells 683in the group they can be excluded. 684 685A Naked Quad occurs when four cells in a 686group contain no candidates other that 687the same four candidates. 688 689Hidden Pairs: 690If two cells in a group contain a pair of 691candidates (hidden amongst other 692candidates) that are not found in any 693other cells in that group, then other 694candidates in those two cells can be 695excluded safely. 696 697Hidden Triples: 698If three candidates are restricted to three cells in a given group, 699then all other candidates in those three cells can be excluded. 700Hidden triples are generally extremely hard to spot but 701fortunately they're rarely required to solve a puzzle. 702Hidden Quads: 703If four candidates are restricted to four cells in a given group, 704then all other candidates in those four cells can be excluded. 705Hidden Quads are very rare, which is fortunate since they're 706almost impossible to spot even when you know they're there. 707For the Addict: 708The following techniques are no more difficult than those above 709but requires observation as to how specific candidates relate to 710each other (in particular patterns) beyond any given row, 711column or box. 712 713X-Wing: 714For every Sudoku, a value can exist only once in each row, 715column and box. If a value has only 2 possible locations in a 716given row (ie it has a candidate in only 2 cells in that row), then 717it must be assigned to one of these 2 cells. Given a particular 718puzzle that has two rows where a given candidate 'C' is 719restricted to exactly the same two columns (and no more than 2 720columns), and since: 7211) candidate C must be assigned once in each of these two 722rows, and 7232) no column can contain more than one of candidate C 724then candidate C must be assigned exactly once in each of 725these two columns within these two rows. Therefore, it's not 726possible for any other cells in these two columns to contain 727candidate C. This same logic applies when a puzzle that has two 728columns where candidate C is restricted to exactly the same two 729rows. 730 731Swordfish: 732The Swordfish pattern is a variation on the "X-Wing" pattern above. 733Formal definition: 734Given a particular puzzle that has three rows where a given 735candidate 'C' is restricted to the same three columns, and since 7361. candidate C must be assigned once in each of these three rows 7372. no column can contain more than one of candidate C 738then candidate C must be assigned exactly once in each of 739these three columns within these three rows. Therefore, it's not 740possible for any other cells in these three columns to contain 741candidate C. 742This same logic applies when a puzzle that has three columns 743where candidate C is restricted to exactly the same three rows. 744The X-Wing and Swordfish techniques can be further 745generalised to include rows containing candidates restricted to 746four cells in the same four columns (called a Jellyfish). 747 748XY-Wing (also called Y Wing): 749Not to be confused with X-Wing (see above). 750Formal definition: 751Given 3 cells where - 7521. all cells have exactly 2 candidates 7532. they share the same 3 candidates in the form - xy, yz, xz 7543. one cell (the Y 'stem' with canidates xy) shares a group 755with the other 2 cells (Y 'branches' with candidates xz & yz) 756then any other cell which shares a group with both 'branch' cells 757can have excluded the 'z' candidate that is common to these 'branch' cells. 758Proof: If a cell sharing a group with both branch cells is 759assigned the 'z' candidate, then neither branch can be assigned 760the 'z' candidate. Consequently, one branch would be the 'x' and 761the other the 'y' leaving the 'stem' without a candidate, an 762invalid state. 763Note: If all 3 cells in a xy-wing pattern shared the same unit/house, 764then they would be a 'naked triple'. 765 766XYZ-Wing: 767An XYZ-Wing is like an XY-Wing in that it is a group of three cells, one sharing a unit with the other two. 768E.g., base is {1,4,5} and second cell in same box is {1,4}, need to find 3rd cell {1,5} outside box but still seen by base 769However, the first cell in an XYZ-Wing has three candidates while the other two, called the wings, have two. 770Each of the wings must share one candidate with the first cell, (that's part of sharing a unit) but of different values. 771If the two second candidates in the wings are both the same, and are also the same as the extra candidate in the first cell, 772then if any fourth cell shares that candidate and a unit with all three, that candidate can be eliminated. 773It will be in the base's box and seen by base and both 'pincers'. 774 775WXYZ-Wing: 776A WXYZ-Wing is like an XY-Wing and an XYZ-Wing, except it is a group of four cells, one sharing a unit with the other three. 777The first cell in a WXYZ-Wing has three or four candidates while the other three, called the wings, have two each. 778Each of the wings must share one candidate with the first cell, (that's part of sharing a unit) but each wing must share a different value. 779If the second candidates in the wings are all the same, and are also the same as the left-over candidate of the first cell if the first cell 780has four candidates, then if any fifth cell shares that candidate and a unit with all the others, that candidate can be eliminated. 781(E.g.) Cells in one box, Base {5,8,9}, {1,9}, {1,5} and one cell outside box that 'sees' the base {1,8}. If any other cell in the box has '1' and sees the outside 782of the box cell too, eliminate the candidate 1 in that cell. 783 784Unique Rectangle Type 1: 785A "Unique Rectangle" (UR) consists of four cells that occupy exactly two rows, two columns, and two boxes. 786All four cells have the same two candidates left (in real sudokus not all cells have to hold all of the UR candidates). 787A UR Type 1 exists, if one of the four cells of a possible UR has additional candidates. 788If those candidates were eliminated, the UR would exist, causing two solutions. 789It is therefore absolutely necessary, that one of the additional candidates has to be true. 790That means, that the UR candidates can be eliminated from the cell with the additional candidates. 791 792Unique Rectangle Type 2: 793If in a possible UR two non diagonal cells have only one extra candidate, and that candidate is the same in both cells, 794all candidates seeing both extra candidates can be eliminated. In other words, you have two corners as in Type 1, and 795two more that have those same two candidates plus an third, matching in both. That candidate can be eliminated from any cell that is 796in the same house/unit as cell 3 and as cell 4 (the three-candidate corners) even those those cells may not be in same house as each other 797 798Unique Rectangle Type 3: 799This is a combination of UR with Naked/Locked Subsets. We look for two non diagonal cells in a possible UR that have extra candidates. 800Since one of those candidates has to be set to avoid the UR, we can treat both cells as one virtual cell containing only 801the extra candidates and try to build a Naked Subset (all additional cells have to see both UR cells with extra candidates). 802If such a UR/Naked Subset is found, all subset candidates can be eliminated from all cells outside the subset (but in the same house). 803So, one extra candidate seen in non-diagnal cells and one that is seen by both. 804 805Unique Rectangle Type 4: 806We look for additional candidates in two non diagonal cells again, but this time 807we ignore those extra candidates and concentrate on the UR candidates themselves: 808If one of the UR candidates is not possible anymore in any other cell of a house 809that contains both cells with the extra candidates, the other UR candidate can be eliminated from those UR cells. 810So search the house(s) that contain both non-diagonal cells (from the Type 2) and can't do the elimination if UR candidate cell is found 811 812Unique Rectangle Type 5: 813UR Type 5 is a variation of UR Type 2, but now the additional candidate can be in diagonal cells as well. 814Suppose we have a possible UR with one extra candidate in either two diagonal cells or in three cells. 815All occurences of that candidate can be eliminated in all cells that see all UR cells containing the additional candidate. 816The logic behind that move is the same as in a UR Type 2. In other words, in the corners we have one 2-candidate bivalue and at least two others, 817and maybe three, have three candidates with the 3rd matching in all. That candidate can be removed from any cell 'seen' by all three non-bivalue cells. 818 819Hidden Rectangle: 820Hidden Rectangles are very versatile, because they can be used in possible URs that contain up to 821three cells with arbitrary additional candidates (the UR is hidden under a clutter of additional 822candidates - not to be confused with Almost Unique Rectangles). 823We need a possible UR with two or three cells containing additional candidates (with only one cell the 824UR Type 1 should be used). Now take one UR cell without additional candidates and check the row and 825the column containing the opposite corner of the UR. If one UR candidate is nowhere outside the UR in 826those two houses, the other UR candidate can be eliminated from the opposite corner. 827 828Links and Inferences for chains 829The basis of all chains are two types of basic implications, normally called "links" or "inferences" 830 831Weak Links 832If two entities are weakly linked, they cannot be true at the same time. That means: If one of them is true, 833then the other has to be false (but both can be false as well, so if one is false, nothing can be concluded). 834In simple chains the entities are normally simply candidates, that share a house or a cell. 835 836Strong Links 837If two entities are strongly linked, they cannot be false at the same time. 838That means: If one of them is false, then the other has to be true (both true is only possible in very advanced types of links). 839If we use candidates, we would need exactly two candidates sharing a cell, or exactly two instances of the same candidate sharing a house (a conjugate pair in coloring terms). 840 841Those two link types are the basis for every type of chains, so the difference between them has to be understood completely. To sum it up: 842 843Weak Link: If a is true then b is false 844Strong Link: If a is false then b is true 845 846XY-Chain: 847This method can be used when a great number of cells contain only two possible candidates. 848in choosing one of the candidates in a starting cell, one forms a chain of choices 849which results in the suppression of a candidate in a given cell. 850If the other candidate is chosen in the starting cell and one ends with the suppression 851of the same candidate, it can be excluded safely. 852Pick your starting cell, and make a small u shape (a little smiley curve) underneath the first pencilmark. 853From there, look around, but instead of crossing out pencilmarks (otherwise it gets too messy!), 854when you find a value that it forces somewhere else, put the same u shape underneath the forced value. 855Ignore any that the first choice eliminated you might need them later! 856Carry on doing this until you cant make any more u forces. 857Now choose the second value in your original cell and this time put a little n symbol (a downturned mouth) above the second pencilmark. 858Like before, look at the implications and forces that this makes, continuing on until you cant find any more. 859If theres a forcing chain, at some point youll find a pencilmark with both a u and n on the same mark 860(in which case theyll almost join up!). This its a sure sign that whichever candidate you picked for the first cell, 861youve found the right value for the second cell. 862 863X-Chain: 864X-Chains are chains that use one digit only. X-Chains of length 4 are sometimes called Turbot Fishes. 865A X-Chain is a single-digit solving technique which uses a chain consisting of links that alternate between strong links and weak links, 866with the starting and ending link being strong. In other words, the chain involves an even number of cells having the target digit as a candidate. 867The target digit can be eliminated from any cell that is seen by both ends of the X-Chain. 868The important thing with X-Chains is, that they have to start and end with a strong link. This ensures that one of the endpoints actually contains 869the chain digit. That digit can be removed from any cell that sees both ends of the chain. (EG -- even number of nodes, elimination cell must 'see' both ends) 870 871Forbidding Chain -- Really another version of X-Chain, but implemented some handling of group nodes and that opens up more strong links for chains. Begin and 872end on weak link using single digit chain and alternating links. If you reach the head again, eliminate that candidate. 873 874Remote Pair Chain: 875Remote Pair is the simplest chaining technique. It considers only bivalue cells that contain the same two candidates. 876Since the cells are bivalue, a strong link exists within every cell between the two candidates. The links between the cells 877can therefore be weak (the cells have to see each other). To eliminate something, the chain has to be at least four cells long. 878The Remote Pair ensures, that any cell within the chain has the opposite value of the cell before it. Any cell outside the Remote Pair 879that sees two cells with different values cannot have one of the Remote Pair digits set. 880 881Medusa 3D: 8823D Medusa extends single's chains into a third dimension. Medusa 3-D extends the search is up through the bi-value cells which contain two different numbers. 883You can think of the different candidate numbers as existing in a third dimension lifting up from the page with 1 at the bottom and 9 at the top. 884In other words, its a chain that can jump not only between cells on strong links but inside a bivalue cell as well. Start the chain on a cell and one candidate in that cell and chain with alternating 'colors'. Create all possible chains and then 885analyze the chain using six rules. 886 887Rule 1 - Twice in a Cell - If two candidates in a cell have the same colour - all of that colour can be removed - and the opposite colour are all solutions. All that color in the ENTIRE puzzle grid! 888Rule 2 - Twice in a Unit - Similiar to 1, but looking for two coloured occurrences of X in the same unit (row, column or box) as opposed the two of the colour in the same cell. 889Rule 3 - Two Different Colors in a Cell with more than 2 candidates - We know that either ALL the blue candidates will be true, or ALL the green ones. If there are any another candidates in any cell with two colours, they cannot be solutions 890Rule 4 - Two Colors "Elsewhere" - where there are candidates that can see both colours they can be removed. By 'see' we mean any candidates that are the same candidate as members of the blue/green links. (e.g., a '6' sees both a green and blue '6'). It can be removed. 891Rule 5 - Two Colors in Unit and Cell - If an uncoloured candidate can see a coloured candidate elsewhere (it shares a unit) AND an opposite coloured candidate in its own cell, it can be removed. 892Rule 6 - Cell Emptied by Color - If all the candidates in an uncolored cell can 'see' their counterparts all colored the same, ALL of that color can be removed (e.g., same as 1, in the entire grid) 893 894*/ 895
Sudoku solver algorithms and code
c_cpp
Program that builds the actual solver
1/* 2 3 Sudoku Puzzle Solver 4 A Sudoku (i.e. the puzzle) is a partially completed grid. 5 A grid has 9 rows, 9 columns and 9 boxes, each having 9 cells (81 total). 6 7The program first tries to solve the input puzzle using solution algorithms including: 8 91. Naked Single 102. Hidden Single 113. Pointing and Claiming (Locked Candidates) 124. Naked Pair,Triple,Quad 135. Hidden Pair,Triple,Quad 146. X-Wing 157. Y-Wing (aka XY-Wing) 168. XYZ-Wing 179. WXYZ-Wing 1810. Swordfish 1911. Jellyfish 2012. Unique Rectangle (Type 1) 2113. Unique Rectangle (Type 2) 2214. Unique Rectangle (Type 3) 2315. Unique Rectangle (Type 4) 2416. Unique Rectangle (Type 5) 2517. Hidden Rectangle 26 27 28 29These solution algorithms use a classic "candidate-based" approach using an initial build of all candidate entries 30for each cell that is not initially filled by the input grid. Candidates are the same as "pencil marks" or 31"marks" in a manual/paper Sudoku puzzle 32 33If the algorithm-based solutions fail (and they do for certain input puzzles!), the program switches first to "Forcing Chain" algorithms including XY-Chain, X-Chain, Medusa 3_D, Forbidding Chain 34(that adds group nodes to X-Chain to enhance strong links) and Remote Pair and finally to a "Recursive Descent/Backtrack" solution search that is optimized using only the remaining candidates for 35each attempt made to fill the remaining empty cells. That should work for any legal input puzzle. 36 37Development started in February 2021 and Forcing Chain was added in mid-June. 38Unique Rectangle added in July 2021 39Additional chain solutions (remote pair, X Chain) added in August 40 41Version 1.0 -- 7-7-21 First 'complete' version with all algorithms in place 427-9-21 Add new puzzle 437-15-21 Added Unique Rectangle Types 1,2,4 (see Sudoku_Puzzles) and remote pair chain 448-18-21 Added X-Chain and two new recursive demos 458-25-21 Added XYZ-Wing 469-7-21 Added Hidden Rectangle 479-28-21 Add Forbidding Chain which is a Type 1 Nice Loop and an extension of X Chain 4810-1-21 Fix bug that occassionally set up wrong Solution Grid 4910-27-21 Added Medusa 3D, beginning to add its elimination rules 5012-27-21 Added "Workout" puzzle 5103-29-22 Continue clock code 5204-14-22 Date, time and temperature added 5305-10-22 Small updates to idle screen 54 55 56 57Acknowledging the very critical help from online Sudoku resources for descriptions, explanations, puzzles and quick development of test-bed solutions, including: 58HoDoKu 59SudokuSolutions 60SudokuOfTheDay 61Sudokuonline.io 62Sudoku Snake 63SudokuWiki.org 64 65*/ 66 67// #define Test_Mode1 // if defined, loads test puzzles and runs 68 // #define Test_Mode2 // if defined, preloads a demo puzzle in input mode 69// #define Test_Mode3 // check against pre-input solution (usually on if 1 is on, goes with Test_Mode1) 70 #define Test_Show // if not defined, turns off some of the Show_XXX routines used for debugging 71 72#include <Adafruit_GFX.h> // graphics library 73#include <Adafruit_ILI9341.h> // color display 74#include <SdFat.h> // SD card & FAT filesystem library 75#include <Adafruit_SPIFlash.h> // SPI / QSPI flash library 76#include <Adafruit_ImageReader.h> // Image-reading functions 77#include "RTClib.h" // real time clock library 78#include "Sudoku_Puzzles.h" // test and demo puzzles 79#include <Keypad.h> // keypad library 80 81// Initialize for Adafruit DS3231 RTC real time clock 82RTC_DS3231 rtc; 83 84// Color display 1 85#define TFT1_DC 9 // color display control pins 86#define TFT1_CS 10 87#define SD1_CS 4 // SD card select pin 88// Comment out the next line to load from SPI/QSPI flash instead of SD card: 89 90// on Mega use SPI Hardware CLK = 52, MISO = 50, MOSI = 51, CS = 10, DC = 9 (for last two, whatever is declared ast TFT1_DC and TFT1_CS) 91Adafruit_ILI9341 tft1 = Adafruit_ILI9341(TFT1_CS, TFT1_DC); 92 93// Color display 2 94#define TFT2_DC 11 // color display control pins 95#define TFT2_CS 12 96#define SD2_CS 7 // SD card select pin 97 98// on Mega use SPI Hardware CLK = 52, MISO = 50, MOSI = 51, CS = 10, DC = 9 (for last two, whatever is declared ast TFT2_DC and TFT2_CS) 99Adafruit_ILI9341 tft2 = Adafruit_ILI9341(TFT2_CS, TFT2_DC); 100 101 102// Comment out the next line to load from SPI/QSPI flash instead of SD card: 103#define USE_SD_CARD 104 105#if defined(USE_SD_CARD) 106 SdFat SD; // SD card filesystem 107 Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys 108#else 109 110 Adafruit_SPIFlash flash(&flashTransport); 111 FatFileSystem filesys; 112 Adafruit_ImageReader reader(filesys); // Image-reader, pass in flash filesys 113#endif 114 115Adafruit_Image img; // An image loaded into RAM 116 117// Keypad 118// * AdaFruit PID3844 4x4 Matrix Keypad (need only if you use the Adafruit key library rather than keypad.h) 119// define pins here - this keypad is pinout (with keys facing you) 120// * 0 # D (keys facing you) 121// nc C1 C2 C3 C4 R1 R2 R3 R4 nc (nc = no connection) 122#define R1 32 // row connections 1-4 123#define R2 33 124#define R3 34 125#define R4 35 126#define C1 38 // column connections 1-4 127#define C2 39 128#define C3 40 129#define C4 41 130 131const byte KROWS = 4; //four rows 132const byte KCOLS = 4; //four columns 133 134char Kkeys[KROWS][KCOLS] = { // key map for key.h library 135 {'1','2','3','A'}, 136 {'4','5','6','B'}, 137 {'7','8','9','C'}, 138 {'*','0','#', 'D'}}; 139 140byte KrowPins[KROWS] = {32,33,34,35}; //connect to the row pinouts of the keypad 141byte KcolPins[KCOLS] = {38,39,40,41}; //connect to the column pinouts of the keypad 142 143Keypad K_Keypad = Keypad( makeKeymap(Kkeys), KrowPins, KcolPins, KROWS, KCOLS ); 144 145/* 146 * 0 through 9 (48-57) are numbers 147 * * 42 is Backspace 148 * # 35 is Forward line (cycling) 149 * A 65 is Reset/Load Puzzle 150 * B 66 is Run Puzzle 151 * C 67 is is Configure 152 * D 68 is Demo (Run next internal puzzle) 153 */ 154// Current use of Keypad Note Main Mode/Entry Mode definitions below 155#define K_Fnumber 48 // lowest number 0 156#define K_Lnumber 57 // highest 9 157#define K_Backspace 42 // * backspace during entry 158#define K_Forward 35 // # Goes forward one space during entry 159#define K_Input 65 // (A) Go to Entry Mode/Reset (wipe) entered puzzle in input mode 160#define K_Solve 66 // (B) Solve Loaded Puzzle/Return from Input Mode to Execute Puzzle 161#define K_Check 67 // (C) Configuration Changes/Check puzzle validity 162#define K_Demo 68 // (D) demo next puzzle/Play Sudoku 163int K_Char = 0; // returned decimal value of key press 164int K_Demo_Current = 0; // for each press of D increment 165const int K_DC_Interval = 750; // cursor flash interval during puzzle input 166unsigned long DC_Timer; // used for non blocking timer 167bool Show_SD_Image; // used to see if SD demo images working 168const int SD_Robots = 3; // number of rrx images to use 169const int Delay_After_Solve = 10000; // delay after solving to clear the scroll screen 170unsigned long Sleep_Timer; // used for non blocking timer 171unsigned long Sleep_Interval = 300000; // interval with no input to go back to sleep demo 172 173 174// debugging and display tools // named after Digital Equipment Corporation's DDT (dynamic debugging technique) 175 176void DDTv(String st, int vt, bool CRR) { // Print descriptor and value 177 Serial.print(st); 178 if (CRR) {Serial.println(vt);} else {Serial.print(vt);}} // if Carriage return request <NL> then do so 179 180void DDTvl(String st, unsigned long vt, bool CRR) { // Print descriptor and value 181 Serial.print(st); 182 if (CRR) {Serial.println(vt);} else {Serial.print(vt);}} // if Carriage return request <NL> then do so 183 184void DDTn(int st, bool CRR){ // print a number 185 if (CRR) {Serial.println(st);} else {Serial.print(st);}} 186 187void DDTs(String st, bool CRR){ // print a string 188 if (CRR) {Serial.println(st);} else {Serial.print(st);}} 189 190void DDTsp(bool CRR){if (CRR) {Serial.println(" ");} else {Serial.print(" ");} } // print one space (to separate values, etc. 191 192const bool NL = true; // new line after Show_NRC and DDTx 193const bool SL = false; // don't, i.e., no <newline> 194 195char Dstring[ ] = "1 2 3 1 2 3 1 2 3 "; 196char Bstring[ ] = " "; 197const byte DHoffset [] = {0,3,6, 10,13,16, 20,23,26}; 198const byte DVoffset [9] = {6,13,20,27,34,41,48,55,62}; 199const byte HStart = 10; 200 // constants for TFT Color display 201const byte Ver1 = 2; //x axis of lh vertical line 202const byte Ver2 = 79; //x axis of 2nd vertical line 203const byte Ver3 = 159; //x axis of 3rd vertical line (don't care about rh, 4th) 204const byte Hor1 = 2; //y axis of first (top) horizontal line 205const byte Hor2 = 79; //y axis of 2nd horizontal line 206const byte Hor3 = 159; //y axis of 3rd horizontal line 207const byte Hor4 = 240; //y axis of 4th horizontal line (bottom of puzzle grid) 208const byte Dis1 =10; //x displacement of first digit from nearest line 209const byte Dis2 = 33; //x displacement of 2nd digit from nearest line 210const byte Dis3 = 55; //x displacement of 3rd digit from nearest line 211const byte BoxOffsets[3] = {Dis1,Dis2,Dis3}; // array of offsets left to right and top to bottom for 1st, 2nd and 3rd digit in box 212 213const byte Msg1Offset = 27; // y axis displacement from bottom of grid to one line message 214const byte Msg1TextSize = 4; // text sizes are 1-5 (small to largest) (if 5, its max of 8 and pw of 15 and offset 20) 215const byte Msg1TextMax = 10; // maximum characters for single line message 216const byte Msg1PW = 12; // character width in pixels (for centering adjustment) 217const byte Msg2Offset = 16; // y axis displacement from bottom of grid to two line message 218const byte Msg2TextSize = 3; 219const byte Msg2TextMax = 13; // maximum characters for double line message 220const byte Msg2PW = 9; // character width in pixels (for centering adjustment) 221const byte Msg3Offset = 6; // y axis displacement from bottom of grid to three line message 222const byte Msg3TextSize = 2; 223const byte Msg3TextMax = 20; // maximum characters for three line message 224const byte Msg3PW = 6; // character width in pixels (for centering adjustment) 225const int T_Msg_Delay = 3000; // after a scroll message - keep display for a bit 226const byte DigitTextSize = 2; // size of text for puzzle grid display 227const byte T_ScrollTextOffsetVert =2; // distance to scrolling messages from top 228const byte T_ScrollTextOffsetHor =8; // distance to scrolling messages from top 229const byte T_ScrollTextSize = 1; // very small for updates 230const byte T_Scrolling_Limit = 29; // maximum number of lines on display 231const int Scrolling_Text_Line_Delay = 100; // delay after each line printed on progress/move display 232const int Scrolling_Text_Page_Delay = 500; // delay before clearing current page 233const int Scroll_Display_Delay = 2000; // used to wait between successive displays 234const int Scroll_Error_Delay = 3000; // after error message before resetting progress display 235bool Scrolling_Text_Just_Cleared = false; // used to skip initial delay 236int T_Scrolling_Pointer = 0; // pointer for display 237const int Grid_Color = ILI9341_GREEN; // color for puzzle grid 238const int Display1_Color = ILI9341_YELLOW; // display portion of main (Puzzle) TFT display 239const int Display2_Color = ILI9341_WHITE; // display portion of main (Puzzle) TFT display 240const int Constant_Color = ILI9341_BLACK; // input digits 241const int Entry_Color = ILI9341_BLUE; // and placed numbers 242const int Msg1_Color = ILI9341_BLUE; // text color in display area - puzzle display 243const int Msg2_Color = ILI9341_BLACK; // text color in display area - progress display 244 245int Date_Time_Num; // used to build clock stuff 246int Date_Time_Num_Year; // 247int Date_Time_Num_Month; // 248int Date_Time_Num_Day; // 249int Date_Time_Num_Hour; // 250int Date_Time_Num_Minute; // 251int Date_Time_Num_Second; // not input but is displayed 252int Date_Time_Num_DOW; // day of the week 253bool Date_Time_Num_Valid; // used to check clock input 254int Old_Minute = 61; // persistent minute to control sleep screen refresh 255 256float fc = 0.0; // receives floating point temperature in celcius 257int ifc =0; // convert to integer and adjust c to f 258float fc_adjust = -8.0; // adjustment to temperature because its not calibrated 259const int Display_Temp_Cycle = 15; // Display temp every n seconds 260int Display_Temp_Counter = 0; // Seconds counter to decide temp vs time 261 262String daysOfTheWeek[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 263String monthsOfTheYear[12] = {"January", "February", "March", "April", "May", "June", "July","August", "September", "October", "November", "December"}; 264 265 266bool Show_Clear_Place = true; // Show Candidate clearing and placement 267bool Use_Algorithms = true; // use algorithms before reverting to recursive descend/backtrack 268bool Use_All_Demos = false; // normally, run just one demo on "D" command at main menu 269byte Demo_Counter = 0; // used as above 270 271byte Box_Coords [9][2] = {{0,0},{0,3},{0,6},{3,0},{3,3},{3,6},{6,0},{6,3},{6,6}}; //Row and Column coords of each of the 9 boxes 272const byte Row_Pointer = 0; // index to row within Box_Coords, CanCoord, RR_CC etc. 273const byte Column_Pointer = 1; // and index to column 274 275 byte Puzzle_Grid [9][9]; // Puzzle working solution and display grid 276#ifdef Test_Mode3 277byte Solution_Grid [9][9]; // and pre done solution (used for test) 278#endif 279 char Puzzle_Buffer [25]; // returned puzzle name 280 String Puzzle_Name; 281 int Puzzles_Number = sizeof(Puzzles_List)/2; // number of test/demo puzzles 282 byte Cell_Entry =0; 283 int Cell_Pointer = 0; 284 285 const int Minimum_Starting_Clues = 17; // literature says no unique solution puzzle has less than 17 clues 286 287int Occurence_Counter = 0; // persistent placement counter for algorithms 288int RR_CC[3][2]; // used to store coordinates of found instances (e.g. for pointing pair) 289int RR_CC_Ptr = 0; 290 291byte SWF [9][6]; // array for x-wing,swordfish & jellyfish algorithms (numbr of candidate bits, membership in x,sword and fish and [C1][C2][C3][C4] or [R1][R2][R3][R4] marks) 292const int SWF_Number_Found = 0; // index into SWF for how many of the marks are stored 293const int SWF_Member =1; // index into SWF for a flag for whether this row/column is part of the x,s or j fish 294const int SWF_Marks = 2; // where up to 4 columns or rows start to be stored 295int SWF_Membership = 0; // number of members (has to be 2,3, or 4 depending on x-wing, swordfish or jellyfish) 296int SWF_Start = 0; // and this is the starting c/r for the unit being checked 297int SWF_Mask = 0; // this is # of columns or rows across ALL columns/rows checked against initial 298 299int Digit_Array[10]; // quick place to unpack digits 0-9 300 301int Y_Wing_Z; // used to globally return Y wing candidate to clear 302bool No_Solution; // set true by routines like RD and Unique Rectangle that can detect no solution to end Solve loop 303String PTQ_String[3] = {{"Pair"}, {"Triple"}, {"Quad"}}; //used for displays to identify source of 'move' 304 305int Match_Counter = 0; // match counter for naked and hidden searches 306int BitMask = 0; // bitmask used for matching 307int BitMask_Result = 0; // found match 308byte CanCount[9]; // number of candidates in each analyzed cell 309int CanCand[9]; // contents for each analyzed cell 310bool CanMark[9]; // used to mark which cells match/don't match 311byte CanCoord [9][2]; // coordinate pointers back to main Candidates matrix 312int CC_Length = 0; // number of active candidates in analytical array (0-9) 313int CCC = 0; // calculated candidate counter 314 315String Row_Labels[] = {"A","B","C","D","E","F","G","H","I"}; // can change to 0-9 if you want 316 317// Hierarchy is Puzzle ==> Box (0-8), Rows, Columns and Cell 318// Boxes, Rows and Columns run 0-8 (not 1-9), and each has both a 'total filled' count in cell 0, and knock-in markers in cells 1-9 319// Where that cell (1-9) will be non-zero if that number exists in the row or column or box 320 321int Filled = 0; // how many cells are filled (will end with 81) 322int Last_Filled = 0; // total at start of pass 323int Last_Total_Candidates = 0; // same for candidates because you can have a pass where candidates change but nothing placed 324int Chain_Filled = 0; // number of filled cells when entering chain solutions 325int Chain_CCC = 0; // same 326int Passes = 0; // number of passes til solution 327unsigned long Solve_Timer; // time it took for the solution 328 329int Candidates[9][9] ; // candidate matrix is cell x 9 possible contents - each cell has 1-9 as markers for a candidate in 'bits' switched on 330 // e.g. if a candidate cell contains 2,6,9 then bits 2,6, and 9 are 'on' as candidates, starting from rh side (don't make this a byte -- too small!) 331byte Candidates_Count[9][9]; // contains the number of bits turned on in this cell, hence the number of candidates 332 333const int RDC_Max_Size = 81-Minimum_Starting_Clues; 334const byte RDC_CurrCan = 2; // re-use Row_Pointer and Column_Pointer from above (index 0 and 1).. this adds a 'current candidate marker' 335byte RDC_Matrix [RDC_Max_Size][3]; // Recursive descent matrix -- possible blank matrix, so r,c, and which candidate we are up to. This would fit a minimum-clue puzzle 336int RDC_Length = 0; // the number of blank cells we actually have at start of recursive descent/backtrack 337int RDC_Ptr = 0; // and current pointer - points to recursive level and CurrCan array, row and column 338int RDC_Calls = 0; // recursive calls counter 339 340byte FC_R [RDC_Max_Size]; // forcing chain -- pointer to the row of each 2-candidate cell (copied into from RDC Matrix after its loaded by Cand_Load2) 341byte FC_C [RDC_Max_Size]; // pointer to column 342byte FC_CN[RDC_Max_Size][2]; // contains the two candidates 343int FC_UD[RDC_Max_Size][2]; // contains the 'up' and/or 'down' marker -- as each chain is followed (Soduku Solver terminology) 344const byte FC_1 = 0; // index to first (or left-hand) candidate in FC_CN and FC_UD 345const byte FC_2 = 1; // 2nd or right hand 346const byte FC_Up = 0b00000001; // bit marker for an 'up' 347const byte FC_Down = 0b00000010; // bit marker for a 'down' 348const byte FC_UpandDown = FC_Up | FC_Down; 349const bool MD_Mark_Green = true; // flip flop on color for this cell 350const byte MD_Green = 0; // index to 'Green' -- each '1' bit marks a candidate position that is colored green (really same as FC_1 and 2) 351const byte MD_Yellow = 1; // index to 'Yellow' -- each '1' bit marks a candidate position that is colored yellow (for coloring algorithms) 352byte MD_Green_Candidates; // candidates colored - used for display/diagnostics 353byte MD_Yellow_Candidates; 354bool MD_Eliminated_Candidate; // don't use multiple Medusa rules if prior MD rules 'worked' because underlying data could be bad 355const byte FC_Flag = 99; // used as flag to bypass after use 356bool FC_List[RDC_Max_Size]; 357bool FC_Invoked = false; 358const int FC_Max_Heads = 25; // maximum number of direct successors each recursion can loop through (8 in my row + 8 in my box + 8 in my column) 359byte FC_Ptr; // incremental store into array 360const bool Strong_Link = true; 361const bool Weak_Link = false; 362byte XC_Link_Counter; // X Chain link counter and row, column of head of the chain 363byte XC_Head_R; 364byte XC_Head_C; 365unsigned int FC_Used_List[RDC_Max_Size]; // used by more complex 'used list' for forbidding chain and x-chain 366byte WXYZ_Didnt_Match; // candidate that DIDN'T match(i.e. 'x') out of {589} as base against {x,5}, {x,8} or {x,9} 367byte WXYZ_Match; // candidate that DID match (these are used by WXYZ_BitsMatchOne), so one of 5,8, or 9 368 369 370void setup(void) { 371 372 Serial.begin(9600); 373 ImageReturnCode stat; // Status from image-reading functions 374 tft1.begin(); // initialize color display is 240 wide (horizontal) by 320 long (vertical) 375 tft2.begin(); // initialize 2nd color display, also 240 wide (horizontal) by 320 long (vertical) 376 377 // The Adafruit_ImageReader constructor call (above, before setup()) 378 // accepts an uninitialized SdFat or FatFileSystem object. This MUST 379 // BE INITIALIZED before using any of the image reader functions! 380 Serial.print(F("Initializing filesystem...")); 381#if defined(USE_SD_CARD) // Note: the SD Card seems to fail the first time AFTER 382 // SD card is pretty straightforward, a single call... // an upload but works subsequently (next power up cycle) 383 Show_SD_Image = true; // assume its working 384 if(!SD.begin(SD1_CS, SD_SCK_MHZ(25))) { // ESP32 requires 25 MHz limit 385 DDTs("SD begin() failed",NL); 386 Show_SD_Image = false;} 387#else 388 // SPI or QSPI flash requires two steps, one to access the bare flash 389 // memory itself, then the second to access the filesystem within... 390 if(!flash.begin()) { 391 DDTs("flash begin() failed",NL)); 392 Show_SD_Image = false;} 393 if(!filesys.begin(&flash)) { 394 DDTs("filesys begin() failed",NL); 395 Show_SD_Image = false;} 396#endif 397 398if (! rtc.begin()) { // check that clock is there 399 T_Msg2("RTC", "Didn't Start"); // clock missing is an error 400 delay(T_Msg_Delay);} // 401 if (rtc.lostPower()) { // if power lost, notify 402 T_Msg2("RTC", "Lost Power!"); 403 delay(T_Msg_Delay); 404 T_Msg2("Replace","Battery?"); 405 delay(T_Msg_Delay);} 406 else 407 {DateTime now = rtc.now(); // else just reload from RTC 408 Date_Time_Num_Year = now.year(); 409 Date_Time_Num_Month = now.month(); 410 Date_Time_Num_Day = now.day(); 411 Date_Time_Num_Hour = now.hour(); 412 Date_Time_Num_Minute = now.minute(); 413 Date_Time_Num_Second = now.second(); 414 Date_Time_Num_DOW = now.dayOfTheWeek(); 415 T_Msg3("Clock Set", FDM_DT(Date_Time_Num_Year,Date_Time_Num_Month,Date_Time_Num_Day,Date_Time_Num_Hour,Date_Time_Num_Minute),daysOfTheWeek[Date_Time_Num_DOW]); 416 delay(T_Msg_Delay); 417 } 418 419 #ifndef Test_Mode1 420 Sleep_Demo(); // show start/sleep demo 421 #endif 422 423 T_Display_Setup(); // clear and create color display 424 Clear_Scroll_Display(); // and display of 'moves' (scrolling progress display( 425 426#ifdef Test_Mode1 // test mode loads test puzzle and runs 427 for (int i = 0; i < Puzzles_Number; i++){ 428 T_Display_Setup(); 429 Clear_Scroll_Display(); 430 Transfer_Test(i); // get the puzzle 431 DDTv("Puzzle Grid Fetched ",i+1,SL); 432 DDTs(" Named ",SL); 433 DDTs(Puzzle_Name,NL); 434 T_Msg3("Fetching",FDM("Puzzle ",i+1,""),Puzzle_Name); // offset by 1 for readability 435 T_Scroll_Message(FDM("Puzzle ",i+1," Loaded")); 436 T_Scroll_Message(Puzzle_Name); 437 Examine_Puzzle(); // analyze the puzzle 438 Puzzle_Check(); // check the puzzle 439 Solve_Puzzle(); // and solve it 440 Clear_Puzzle(); // clear puzzle grid 441 delay(Scroll_Error_Delay);} 442 while(true){;} 443#endif // Test_Mode 1 444 445 K_Return_To_Main(); 446 K_Char = 0; 447 Sleep_Timer = millis(); // set sleep timer 448 449} // end of Setup 450 451void loop(void) { 452 453 K_Read(); // read keypad 454 455 switch (K_Char){ 456 457 case K_Demo: // is it demo mode (D)? 458 K_Char = 0; // yes, clear it 459 if (Use_All_Demos) { // if doing all, load all 460 Clear_Scroll_Display(); 461 for (K_Demo_Current=0; K_Demo_Current< Puzzles_Number;K_Demo_Current++) {Run_Test();} 462 } else 463 { Clear_Scroll_Display(); 464 T_Scroll_Message(F("Select Puzzle to Run")); 465 T_Scroll_Message(F("Enter Two Digit Puzzle Number")); 466 T_Scroll_Message(FDM("From 01 to ",Puzzles_Number," ")); 467 T_Msg3("Enter Puzzle # ",FDM("1 to ",Puzzles_Number,""),"to run"); 468 while(K_Char == 0){K_Read();} 469 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 470 K_Demo_Current = K_Char - K_Fnumber; 471 K_Char = 0; } //clear 472 while(K_Char == 0){K_Read();} 473 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 474 K_Demo_Current = ((10*K_Demo_Current)+(K_Char - K_Fnumber));} // form 2 digit number 475 if (K_Demo_Current > 0 && K_Demo_Current <= Puzzles_Number){ 476 K_Demo_Current--; // correct 1-n to 0-(n-1) 477 Run_Test();} else 478 {T_Scroll_Message("Invalid Puzzle Number!"); 479 T_Msg3("Not a valid","Puzzle Number",FDM("",K_Demo_Current,"")); 480 delay(Scroll_Display_Delay);}} 481 K_Return_To_Main(); 482 Sleep_Timer = millis(); // reset sleep timer 483 break; 484 485 case K_Input: // look for puzzle entry (A) 486 K_Char = 0; 487 Clear_Puzzle(); 488 T_Display_Setup(); 489 T_Msg2("Load", "Puzzle"); // display state 490 K_Get_Puzzle(); // get puzzle input, loaded and ready 491 K_Return_To_Main(); // indicate we've returned 492 Sleep_Timer = millis(); // reset sleep timer 493 break; 494 495 case K_Solve: // solve current puzzle 496 K_Char = 0; 497 Examine_Puzzle(); // make sure it is legal 498 DDTv("Filled is ",Filled,NL); 499 if (Filled > 0){ // but only if not blank 500 if (Puzzle_Check()){ 501 Clear_Scroll_Display(); // new progress display 502 Solve_Puzzle(); 503 delay(Delay_After_Solve); 504 K_R_T_M_Scroll_Only();} else // check the puzzle 505 { T_Msg3("Puzzle Doesn't","Check Out","Try Again");}} 506 Sleep_Timer = millis(); // reset sleep timer 507 break; 508 509 case K_Check: // Configuration 510 K_Char = 0; 511 K_Configure(); 512 K_Return_To_Main(); 513 Sleep_Timer = millis(); // reset sleep timer 514 break; 515 516 case K_Backspace: // used to go into sleep mode 517 K_Char = 0; 518 Sleep_Demo(); // show start/sleep demo 519 K_Return_To_Main(); 520 Sleep_Timer = millis(); // reset sleep timer 521 break; 522 523 default: 524 K_Char = 0; 525 break; 526 } // end of switch K_Char 527 528 if (millis() - Sleep_Timer > Sleep_Interval) { 529 Sleep_Demo(); // show start/sleep demo 530 K_Return_To_Main(); 531 Sleep_Timer = millis();} 532 533 } // end of "loop" 534 535void Examine_Puzzle(){ // set up analytical contents 536 537 Filled = 0; // reset counters 538 int Cell_Entry = 0; 539 540 for (int r = 0; r < 9; r++){ 541 for (int c = 0; c < 9; c++){ // if its a non-zero cell 542 Cell_Entry = Puzzle_Grid[r][c]; 543 if (Cell_Entry > 0){ 544 Filled++; // another cell is filled 545 T_Number(r,c,Cell_Entry,Constant_Color); // and enter initial cell on color display 546 } // end of looking at the specific cell 547 } // end of looking at the column in row 548 } // end of looking at this row 549 CCC = 0; // candidates count initialized 550 Build_Candidates(); // build the candidates matrix 551 552 Show_Grid(); // show initial puzzle grid 553 554} // end of Examine Puzzle 555 556bool Puzzle_Check(){ // check puzzle for initial integrity 557int Cell_Entry; // Note: can't check to see if it has a solution 558 559 if (Filled < Minimum_Starting_Clues){ 560 DDTv("Puzzle has too few initial clues ",Filled,NL); 561 T_Msg3("Puzzle Error",FDM("Only ",Filled," Clues"),"Try Again"); 562 T_Scroll_Message(F("Too few cells filled at start")); 563 T_Scroll_Message(F("Single solution needs >=17 clues")); 564 return false;} 565 for (int r = 0; r <9; r++){ // check loaded puzzle for integrity 566 for (int c = 0; c <9; c++){ 567 Cell_Entry = Puzzle_Grid[r][c]; 568 if (Cell_Entry > 0){ 569 if (!RD_Check(Cell_Entry, r,c)){ 570 DDTs("Input Puzzle has an error at ",SL); 571 Show_NRC(Cell_Entry,r,c,NL); 572 Show_Grid(); // and final puzzle grid 573 T_Msg3("Puzzle","Conflict", FDM_NRC(Cell_Entry,r,c)); 574 T_Scroll_Message("Cell Entry"); 575 T_Scroll_Message("Conflict Error"); 576 T_Scroll_Message("Entry Invalid"); 577 return false;}} 578 else { 579 if (Candidates_Count[r][c] ==0){ // if its an unfilled cell, it has to have candidates 580 DDTs("Input Puzzle has an error at ",SL); 581 Show_NRC(Cell_Entry,r,c,NL); 582 DDTv("Candidates for cell = ",Candidates_Count[r][c],NL); 583 T_Msg3("Puzzle","Candidate Error", FDM_NRC(Cell_Entry,r,c)); 584 T_Scroll_Message("Cell with no candidates error"); 585 return false;}} 586 }} 587 return true; 588} // end of Puzzle_Check 589 590void Solve_Puzzle(){ 591 592 String Pass_String; // differentiate one from multiple 593 String Time_String; 594 595 DDTv(F("Starting Algorithm Solution with "),Filled,SL); 596 DDTv(F(" Cells Filled and Starting Candidate Count of "),CCC,NL); 597 T_Msg3("Solving",FDM(" with ",Filled," Cells"),FDM(" and ",CCC," Candidates")); 598 T_Scroll_Message(F("Starting with Algorithms")); 599 T_Scroll_Message(FDM("with ",Filled," cells filled")); 600 T_Scroll_Message(FDM(" and ",CCC," candidates")); 601 602 Passes = 0; // reset pass counter 603 No_Solution = false; // assume there IS a solution 604 FC_Invoked = false; // we haven't used forcing chain 605 Solve_Timer = millis(); // get starting timer 606 607 do { // stop when the puzzle is solved 608 Passes++; // increment pass counter 609 Last_Filled = Filled; // store filled BEFORE doing anything 610 Last_Total_Candidates = CCC; // both filled and candidates 611 //solving routines follow 612 if (Use_Algorithms){ // if not jumping to recursive routine 613 614 Naked_Singles(); 615 Hidden_Singles(); 616 Hidden_Pairs_Triples_Quads(); 617 Naked_Pairs_Triples_Quads(); 618 Pointing(); 619 Claiming(); 620 X_Wing(); 621 Y_Wing(); 622 XYZ_Wing(); 623 WXYZ_Wing(); 624 Swordfish(); 625 Jellyfish(); 626 Unique_Rectangle(); 627 Hidden_Rectangle(); 628 629 } // end of normal algorithms 630 631 if (Filled == 81) { // solved when all cells filled! 632 DDTsp(NL); 633 DDTv("Done! ",Filled,SL); 634 DDTv(" Cells Filled in ", Passes,SL ); 635 DDTs(" Passes",NL); 636 Show_Grid(); // and final puzzle grid 637 T_Scroll_Message("Solved the Puzzle!!"); 638 Solve_Timer = (millis()-Solve_Timer)/1000; 639 if (Passes > 1) {Pass_String = " Passes";} else {Pass_String = " Pass";} 640 if (Solve_Timer > 1) {Time_String = " Seconds";} else {Time_String = " Second";} 641 T_Scroll_Message(FDM("Completed in ",Passes,Pass_String)); 642 T_Scroll_Message(FDM(" and it took ",Solve_Timer,Time_String)); 643 T_Msg3("SOLVED!",FDM("in ",Passes,Pass_String), FDM("taking ",Solve_Timer,Time_String)); 644 return;} 645 646 if (Filled == Last_Filled && CCC == Last_Total_Candidates) { // initial check for no progress 647 if (!FC_Invoked){ // have we tried the chain solutions? 648 DDTs(F("Trying Chain Solutions!"),NL); // console and progress panel msgs 649 T_Scroll_Message(F("Trying Chain Solutions")); 650 T_Msg3("Trying","Chain","Solutions"); 651 // try the 'easy' chain solutions first 652 DDTs(F("XY-Chain..."),NL); // console and progress panel msgs 653 T_Scroll_Message(F("XY-Chain...")); 654 XY_Chain(); // call XY Chain Forcing Chain 655 DDTs(F("X-Chain..."),NL); // console and progress panel msgs 656 T_Scroll_Message(F("X-Chain...")); 657 X_Chain(); 658 659 if (Filled == Last_Filled && CCC == Last_Total_Candidates){ // second layer of 'less easy' slower chains solutions 660 661 DDTs(F("Medusa 3-D ..."),NL); // console and progress panel msgs 662 T_Scroll_Message(F("Medusa 3-D ...")); 663 Medusa_3D(); 664 DDTs(F("Remote Pair..."),NL); // console and progress panel msgs 665 T_Scroll_Message(F("Remote Pair...")); 666 Remote_Pair(); // call remote pair chain algorithm 667 DDTs(F("Forbidding Chain..."),NL); // console and progress panel msgs 668 T_Scroll_Message(F("Forbidding Chain...")); 669 Forbidding_Chain();} 670 671 FC_Invoked = true;} // set flag that we've tried it 672 673 if (Filled == Last_Filled && CCC == Last_Total_Candidates) // if STILL no progress, then fall through to recursive search 674 675 { DDTv("Algorithms Stumped at ",Filled,NL); // if didn't find anything new, only recursive search/backtrack is left to do! 676 DDTs("Too close for missiles -- switching to guns!",NL); 677 T_Msg3(FDM("Stumped at ",Filled," Filled"),"2 Close for Missiles","Switch to Guns!"); 678 T_Scroll_Message(FDM("Stumped at ",Filled," Cells Filled")); 679 T_Scroll_Message("Too Close for Missiles"); 680 T_Scroll_Message("Switching to Guns!"); 681 delay(2000); 682 if (!RD()){ DDTs("No Solution",NL); 683 No_Solution = true; // end it 684 DDTs("Recursive Search Failed",NL); 685 T_Msg3("Recursive","Search","Failed!"); 686 T_Scroll_Message("Recursive Search Failed"); 687 if (Filled == 80){T_Scroll_Message("Multiple Puzzle Solutions"); 688 T_Scroll_Message("Not a Legal Puzzle");} 689 else {T_Scroll_Message("Puzzle Has No Solution"); } 690 if (Filled == 80) {T_Msg2("Non-Unique","Solution!");} else{T_Msg2("No","Solution!");} } 691 else { 692 DDTs("Recursive Descent/Backtrack Solution Succeeded!",NL); 693 DDTv("Recursive Solution Required ",RDC_Calls,SL); 694 DDTs(" Tries",SL); 695 Solve_Timer = (millis()-Solve_Timer)/1000; 696 DDTv(" and ",Solve_Timer,SL); 697 DDTs(" seconds",NL); 698 T_Msg3("Success!",FDM("Required ",RDC_Calls," tries"),FDM("and ",Solve_Timer," Seconds")); 699 T_Scroll_Message("Recursive Search Succeeded!"); 700 T_Scroll_Message(FDM("Required ",RDC_Calls," tries")); 701 T_Scroll_Message(FDM(" and it took ",Solve_Timer," Seconds")); 702 return;} 703 } else { T_Msg3("Chain","Algorithms","Worked!"); // if AFTER FC and/or RP we've made progress, reset flag 704 FC_Invoked = false;} // and reenter algorithm loop 705 } // end of initial "no progress" 706 707 if (CCC <0) {DDTv("Less than no candidates left",CCC,NL); 708 T_Msg1("Error!"); 709 T_Scroll_Message("Error!"); 710 T_Scroll_Message("Negative candidates left!!"); 711 Display_Debug(); 712 while(1){;}} 713 714 if (Filled > 81) {DDTv("Overfilled at",Filled,NL); 715 T_Msg2("Error!","Overfilled"); 716 Display_Debug(); 717 while(1){;}} 718 } while (Filled < 81 && !No_Solution); // end of "While Not Filled" 719} // end of Solve Puzzle 720 721void Fill_The_Cell(int n, int r,int c, String x){ // update the grid and database 722 int b = In_Box(r,c); 723 DDTs (x,SL); 724 DDTv(" is placing ",n,SL); 725 DDTs(" in ",SL); 726 Show_RC(r,c,NL); 727 if (Show_Clear_Place) {T_Scroll_Message(FDM_FTC(n,r,c,x));} // show and scroll 728 729 if (Puzzle_Grid[r][c] !=0 ){DDTv("DUPLICATE PLACE!",Puzzle_Grid[r][c],SL); 730 Show_NRC(n,r,c,NL); 731 T_Msg2("Duplicate","Placement"); 732 T_Scroll_Message(F("Duplicate Placement")); 733 T_Scroll_Message(FDM_FTC(n,r,c,"Error")); 734 Display_Debug(); 735 while(1){;}} 736 737#ifdef Test_Mode3 // if defined in input grid then 738 if (n != Solution_Grid[r][c]){ // for debugging 739 DDTv("Making mistake putting ",n,SL); // tests whether n placed in cell matches solution 740 DDTs(" in ",SL); 741 Show_RC(r,c,SL); 742 DDTv(" - should be ",Solution_Grid[r][c],NL); 743 T_Msg3("Mistake","Versus","Solution"); 744 T_Scroll_Message(F("Mistake Against Solution")); 745 T_Scroll_Message(FDM_FTC(n,r,c,"Error")); 746 Display_Debug(); 747 while(1){;}} 748#endif 749 750 Puzzle_Grid[r][c] = n; 751 Filled++; // increment how many cells are filled (will end with 81) 752 Placement_Removes_Candidates(n,r,c); // and clear candidates matrix 753 Candidates[r][c] = 0; // ensure cleared for this cell 754 if (Candidates_Count[r][c] >0) { // clear counts for cell and decrement calculated 755 CCC = CCC - Candidates_Count[r][c]; 756 Candidates_Count[r][c] = 0; } 757 T_Number(r,c,n,Entry_Color); //update display 758} // end of Fill The Cell 759 760void Build_Candidates(){ 761 for (int r = 0; r < 9;r++) { // clear the candidate matrix 762 for (int c = 0; c < 9; c++){ // and counts first 763 Candidates[r][c] = 0; 764 Candidates_Count[r][c] =0; }} 765 for (int r = 0; r < 9;r++) { // build the candidate matrix 766 for (int c = 0; c < 9; c++){ // if it is a candidate, mark it 767 for (int n = 1; n <= 9; n++){ // check all nine possibles 768 if (Can_Go(n, r,c)) {Candidate_On(n,r,c);} }} // set and increase count for # of candidates 769 } // end of building candidate count and content 770 771} // end of build candidates 772 773bool Can_Go(int check_number, int r, int c){ 774 if (Puzzle_Grid[r][c] != 0) return false; // if cell already has completed entry, can't go here 775 for (int cc= 0; cc < 9; cc++) {if (Puzzle_Grid[r][cc] == check_number){return false;}} // check all columns for this row, and 776 for (int rr= 0; rr < 9; rr++) {if (Puzzle_Grid[rr][c] == check_number){return false;}} // and all rows for this column, and 777 for (int rr =Box_Coords[In_Box(r,c)][Row_Pointer]; rr < Box_Coords[In_Box(r,c)][Row_Pointer]+3; rr++){ // all cells in the box 778 for (int cc = Box_Coords[In_Box(r,c)][Column_Pointer]; cc< Box_Coords[In_Box(r,c)][Column_Pointer]+3; cc++){ 779 if (Puzzle_Grid[rr][cc] == check_number){return false;}}} 780 return true; // neither, so it can go here 781} // end of can go 782 783void Naked_Singles(){ 784for (int r = 0; r < 9; r++){ // check for naked singles - only one candidate for a cell 785 for (int c = 0; c< 9; c++){ // for these, one candidate only and count is 1 786 if ((Candidates_Count[r][c]) == 1){ // if any cell has a candidate count of 1 787 Fill_The_Cell(Find_Next_Candidate(r,c,1),r,c,"Naked Single");}}}} // if it does, fill it 788 789void Hidden_Singles(){ // check for hidden singles - multipe candidates potentially in cell 790 for (int r = 0; r < 9; r++){ // but this is the only 'n'(i.e., only occurence of candidate) in this row or column or box 791 for (int c = 0; c< 9; c++){ 792 for (int n = 1; n<=9; n++){ // check each number 793 794 if (If_Candidate_On(n,r,c)){ // that is present as a candidate 795 Occurence_Counter = 0; // assume its the only one 796 for (int cc = 0; cc <9; cc++){ // check each column in the row 797 if (If_Candidate_On(n,r,cc)){Occurence_Counter++;}} // if we found this candidate in the row, its not unique 798 if ( Occurence_Counter == 1){ 799 Fill_The_Cell(n,r,c,"Hidden Single");}} // and if it is unique, place it 800 801 if (If_Candidate_On(n,r,c)){ // that is present as a candidate 802 Occurence_Counter = 0; // assume its the only one 803 for (int rr = 0; rr <9; rr++){ // check each row in the column 804 if (If_Candidate_On(n,rr,c)){Occurence_Counter++;}} // if we found this candidate in the row, its not unique 805 if ( Occurence_Counter == 1) {Fill_The_Cell(n,r,c,"Hidden Single");}} // and if it is unique, place it 806 807 if (If_Candidate_On(n,r,c)){ // that is present as a candidate 808 Occurence_Counter = 1; // assume its the only one 809 for (int srr = Box_Coords[In_Box(r,c)][Row_Pointer]; srr <= (Box_Coords[In_Box(r,c)][Row_Pointer])+2; srr++){ //walk the box 810 for (int scc = Box_Coords[In_Box(r,c)][Column_Pointer]; scc <= (Box_Coords[In_Box(r,c)][Column_Pointer])+2; scc++){ 811 if (!((srr == r) && (scc==c))) { if (If_Candidate_On(n,srr,scc)){Occurence_Counter++;}}}} // check each cell but not the source cell 812 if ( Occurence_Counter == 1) {Fill_The_Cell(n,r,c,"Hidden Single");}} // and if it is unique, place it 813 814 }}} // end of row, column number iteration 815} // end of Hidden Singles 816 817/* 818 * Locked Candidates Type 1 (Pointing): If in a block all candidates of a certain digit are confined to a row or column, that digit cannot appear outside of that block in that row or column. 819 * Locked Candidates Type 2 (Claiming): Works exactly the other way round: If in a row (or column) all candidates of a certain digit are confined to one block, that candidate that be eliminated from all other cells in that block. 820 */ 821 822void Pointing(){ 823 int BitMask_R ; // row instance counter, and 824 int BitMask_C; // column one too 825 for (int b = 0; b < 9; b++){ // check all the boxes in the puzzle 826 for (int n = 1; n <=9; n++){ // and all candidate numbers in each box 827 BitMask_R = 0; 828 BitMask_C = 0; // clear instance counters 829 for (int srr = Box_Coords[b][Row_Pointer]; srr <= (Box_Coords[b][Row_Pointer])+2; srr++){ //walk the box 830 for (int scc = Box_Coords[b][Column_Pointer]; scc <= (Box_Coords[b][Column_Pointer])+2; scc++){ 831 if (If_Candidate_On(n,srr,scc)){bitSet(BitMask_R,srr); // for every 'n' found, mark the row in the row bitmask 832 bitSet(BitMask_C,scc);}}} // and same for column 833 if (BitsCount(BitMask_R) == 1){Clear_Row_Outside_Box(n,BitsWhich(BitMask_R),b,"Pointing Lock");} // if found only in one row , clear row of candidate OUTSIDE box b 834 if (BitsCount(BitMask_C) == 1){Clear_Column_Outside_Box(n,BitsWhich(BitMask_C),b,"Pointing Lock");} // if found in only one column, clear it OUTSIDE of box 835 } // end of each number 836 } // end of each box 837}// end of Pointing 838 839 void Claiming(){ // note this is also sometimes called "Box/Line Intersection" 840 int BitMask_B; 841 for (int r = 0; r < 9; r++){ // check this row 842 for (int n = 1; n <=9; n++){ // for each number 843 BitMask_B = 0; 844 for (int c = 0; c < 9; c++){if (If_Candidate_On(n,r,c)){bitSet(BitMask_B,In_Box(r,c));}} // for every 'n' found, mark the bit corresponding to the box in the bitmask 845 if (BitsCount(BitMask_B) == 1){Clear_Box_Except_Row(n,r,BitsWhich(BitMask_B),"Claiming Lock");}}} // if all n in this row are in only one box , clear candidate inside box b, except for this row 846 for (int c = 0; c < 9; c++){ // check all the columns too 847 for (int n = 1; n <=9; n++){ // of that, you can eliminate that candidate from any other cells in the row or column that the candidate is aligned on. 848 BitMask_B = 0; 849 for (int r = 0; r < 9; r++){if (If_Candidate_On(n,r,c)){bitSet(BitMask_B,In_Box(r,c));}} // for every 'n' found, mark the box in the bitmask 850 if (BitsCount(BitMask_B) == 1){Clear_Box_Except_Column(n,c,BitsWhich(BitMask_B),"Claiming Lock");}}} // if all n in this row are in only one box , clear candidate inside box b, except for this row 851 852} // end of claiming 853 854void Naked_Pairs_Triples_Quads(){ // Naked triples are harder to spot. Sometimes, all three numbers in a naked triple appear in all three boxes 855 //of the triple. However, sometimes the numbers of the triple don't all appear in all three boxes 856String Clear_String = " "; // indicator string 857 858 // start of row pass 859 for (int r = 0; r<9; r++){ // step through each row 860 for (int n = 4; n >=2; n--){ 861 CC_Length = 0; 862 Clear_String = "Naked " + PTQ_String[n-2]; // construct string for display 863 for (int c = 0; c<9; c++){ 864 if ((Candidates_Count[r][c]<= n) && (Candidates[r][c] > 0)){ // only load if candidates under limit and non-zero (filled) cell 865 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 866 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 867 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 868 CanCoord[CC_Length][Column_Pointer] = c; 869 CC_Length++;}} // increment counter too 870 if (CC_Length >= n){ // search if we found sufficient number of cells to bother 871 BitMask_Result = NPTQ_Search(n,CC_Length); 872 if(BitMask_Result> 0){ // returned bitmask if found a match 873 for (int cc = 0; cc < 9; cc++){ 874 Clear_All_But_Marked(r,cc,BitMask_Result, Clear_String); } // clear candidates from remainder of row 875 }} }} 876 877 for (int c = 0; c<9; c++){ // step through each column 878 for (int n = 4; n >=2; n--){ 879 CC_Length = 0; 880 Clear_String = "Naked " + PTQ_String[n-2]; // construct string for display 881 for (int r = 0; r<9; r++){ 882 if ((Candidates_Count[r][c]<= n) && (Candidates[r][c] > 0)){ // only load if candidates under limit and non-zero (filled) cell 883 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 884 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 885 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 886 CanCoord[CC_Length][Column_Pointer] = c; 887 CC_Length++;}} // increment counter too 888 if (CC_Length >= n){ // search if we found sufficient number of cells to bother 889 BitMask_Result = NPTQ_Search(n,CC_Length); 890 if(BitMask_Result> 0){ // returned bitmask if found a match 891 for (int rr = 0; rr < 9; rr++){ 892 Clear_All_But_Marked(rr,c,BitMask_Result, Clear_String); } // clear candidates from remainder of column 893 }} }} 894 895 for (int b = 0; b <9; b++){ // start of box pass 896 for (int n = 4; n >=2; n--){ 897 CC_Length = 0; 898 Clear_String = "Naked " + PTQ_String[n-2]; // construct string for display 899 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 900 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 901 902 if ((Candidates_Count[srr][scc]<= n) && (Candidates[srr][scc] > 0)){ // only load if candidates under limit and non-zero (filled) cell 903 CanCount[CC_Length] = Candidates_Count[srr][scc]; // Load length of each cell in row 904 CanCand[CC_Length] = Candidates[srr][scc]; // load contents of each cell 905 CanCoord[CC_Length][Row_Pointer] = srr; // load cell coordinates too 906 CanCoord[CC_Length][Column_Pointer] = scc; 907 CC_Length++;}} // increment counter too 908 if (CC_Length >= n){ // search if we found sufficient number of cells to bother 909 BitMask_Result = NPTQ_Search(n,CC_Length); 910 if(BitMask_Result> 0){ // returned bitmask if found a match 911 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 912 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 913 Clear_All_But_Marked(srr,scc,BitMask_Result, Clear_String); }}} // clear candidates from remainder of cells in this box 914 }} }} 915} // end of Candidates Naked Pairs Triples Quads 916 917int NPTQ_Search(int snumber, int scounter){ // search array - called with number of cells (2,3,4) and number of entries in array 918 919 for (int start_ptr = 0; start_ptr < scounter; start_ptr++){ // check each cell against remaining cells 920 Match_Counter = 0; // number of matching cells 921 for (int i = 0; i < 9; i++){CanMark[i] = false;} // clear markers 922 BitMask = CanCand[start_ptr]; // initial bitmask of candidates 923 for (int check_ptr = start_ptr; check_ptr < scounter; check_ptr++){ // check starting pointer against all remaining pointers 924 if ((Number_of_Candidates(BitMask,CanCand[check_ptr])<= snumber) ) // if this would add too many candidates, mark false 925 {CanMark[check_ptr] = true; // We would not over-run bit count, so mark it good 926 BitMask = BitMask | CanCand[check_ptr]; // accumulate a new mask 927 Match_Counter++;} // at each accumulation, increment match counter 928 } // end of checking start pointer against all others 929 if (Match_Counter == snumber) { // if we have the right number, return the mask, else use 0 as a no match flag 930 return BitMask;} else {return 0;} 931 } // end of all start pointers in row, column or box 932 933 } // end of NPTQ Search 934 935void Hidden_Pairs_Triples_Quads(){ // Hidden pairs, triples and quads. A pair or more that can only be placed in n cells, where n is the number we are searching 936 937 for (int r = 0; r<9; r++){ // step through each row 938 CC_Length = 0; 939 for (int c = 0; c<9; c++){ 940 if (Candidates[r][c] > 0){ // load each non-zero (filled) cell 941 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 942 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 943 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 944 CanCoord[CC_Length][Column_Pointer] = c; 945 CC_Length++;}} // increment counter too 946 HPTQ_Process(); 947 } // end of all rows 948 //start of column pass 949 for (int c = 0; c<9; c++){ // step through each column 950 CC_Length = 0; 951 for (int r = 0; r<9; r++){ 952 if (Candidates[r][c] > 0){ // load each non-zero (filled) cell 953 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 954 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 955 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 956 CanCoord[CC_Length][Column_Pointer] = c; 957 CC_Length++;}} // increment counter too 958 HPTQ_Process(); 959 } // end of all columns 960 961 for (int b = 0; b <9; b++){ //start of box pass 962 CC_Length = 0; 963 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 964 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 965 if (Candidates[srr][scc] > 0){ // only load if candidates if non-zero (filled) cell 966 CanCount[CC_Length] = Candidates_Count[srr][scc]; // Load length of each cell in row 967 CanCand[CC_Length] = Candidates[srr][scc]; // load contents of each cell 968 CanCoord[CC_Length][Row_Pointer] = srr; // load cell coordinates too 969 CanCoord[CC_Length][Column_Pointer] = scc; 970 CC_Length++;}}} // increment counter too 971 HPTQ_Process(); 972 } // end of all boxes 973 974} // end of Candidates Hidden Pairs Triples Quads 975 976void HPTQ_Process(){ // process row, column or box for hidden pair, triple, quad 977 int BM[5]; // process pairs, triples and quads 978 String Clear_String = " "; // indicator string 979 980 Clear_String = "Hidden " + PTQ_String[0]; // construct string for display 981 for (int m1 = 0; m1 < CC_Length-1; m1++){ // process pairs 982 for (int m2 = m1+1; m2 < CC_Length; m2++){ 983 BitMask = CanCand[m1] & CanCand[m2]; // get initial mask 984 for (int m = 0; m < CC_Length; m++){ 985 if ((m != m1) && (m != m2)){ 986 BitMask = BitsOff(BitMask, CanCand[m]);}} // remove candidates present outside the pair 987 if (BitsCount(BitMask) == 2){ 988 for (int i = 0; i < CC_Length; i++){CanMark[i] = false;} // unmark all the markers 989 CanMark[m1] = true; // and mark these two cells to process 990 CanMark[m2] = true; 991 Clear_HPTQ(BitMask,Clear_String); // call the clearing mechanism 992 CanCand[m1] = Candidates[CanCoord[m1][Row_Pointer]][CanCoord[m1][Column_Pointer]]; // and update the analytical array for new contents of 993 CanCand[m2] = Candidates[CanCoord[m2][Row_Pointer]][CanCoord[m2][Column_Pointer]]; // these two cells 994 }}} // end of process pairs 995 996 Clear_String = "Hidden " + PTQ_String[1]; // construct string for display 997 for (int m1 = 0; m1 < CC_Length-2; m1++){ // process triplets 998 for (int m2 = m1+1; m2 < CC_Length-1; m2++){ // rolling through all possible triplets in the 999 for (int m3 = m2+1; m3 < CC_Length; m3++){ // CanCan analytical array (loaded above) 1000 BM[1] = CanCand[m1]; // load the three bit marker 1001 BM[2] = CanCand[m2]; 1002 BM[3] = CanCand[m3]; 1003 for (int m = 0; m < CC_Length; m++){ 1004 if ((m != m1) && (m != m2) && (m != m3)){ // remove all candidates present outside the trio 1005 for (int i = 1; i<=3; i++){BM[i] = BitsOff(BM[i],CanCand[m]);}}} 1006 BitMask = BM[1] | BM[2] | BM[3]; // create a combined bitmask 1007 for (int i = 1; i <=3; i++){if (BM[i] == 0) {BitMask = 0;}} // check that all three contributed to the mask 1008 if (BitsCount(BitMask) == 3){ 1009 for (int i = 0; i < CC_Length; i++){CanMark[i] = false;} // unmark all the markers 1010 CanMark[m1] = true; // and mark these three cells to process 1011 CanMark[m2] = true; 1012 CanMark[m3] = true; 1013 Clear_HPTQ(BitMask,Clear_String); // call the clearing mechanism 1014 CanCand[m1] = Candidates[CanCoord[m1][Row_Pointer]][CanCoord[m1][Column_Pointer]]; // and update the analytical array for new contents of 1015 CanCand[m2] = Candidates[CanCoord[m2][Row_Pointer]][CanCoord[m2][Column_Pointer]]; // these three cells 1016 CanCand[m3] = Candidates[CanCoord[m3][Row_Pointer]][CanCoord[m3][Column_Pointer]]; // these three cells 1017 }}}} // end of process triplets 1018 1019 Clear_String = "Hidden " + PTQ_String[2]; // construct string for display 1020 for (int m1 = 0; m1 < CC_Length-3; m1++){ // process quads 1021 for (int m2 = m1+1; m2 < CC_Length-2; m2++){ // rolling through all possible triplets in the 1022 for (int m3 = m2+1; m3 < CC_Length-1; m3++){ // CanCan analytical array (loaded above) 1023 for (int m4 = m3+1; m4 < CC_Length; m4++){ 1024 BM[1] = CanCand[m1]; // load the four bit markers 1025 BM[2] = CanCand[m2]; 1026 BM[3] = CanCand[m3]; 1027 BM[4] = CanCand[m4]; 1028 for (int m = 0; m < CC_Length; m++){ 1029 if ((m != m1) && (m != m2) && (m != m3)&& (m != m4)){ // remove all candidates present outside the four being checked 1030 for (int i = 1; i<=4; i++){BM[i] = BitsOff(BM[i],CanCand[m]);}}} 1031 BitMask = BM[1] | BM[2] | BM[3] | BM[4]; // create a combined bitmask 1032 for (int i = 1; i <=4; i++){if (BM[i] == 0) {BitMask = 0;}} // check that all of them contributed to the mask 1033 if (BitsCount(BitMask) == 4){ 1034 for (int i = 0; i < CC_Length; i++){CanMark[i] = false;} // unmark all the markers 1035 CanMark[m1] = true; // and mark these three cells to process 1036 CanMark[m2] = true; 1037 CanMark[m3] = true; 1038 CanMark[m4] = true; 1039 Clear_HPTQ(BitMask,Clear_String); // call the clearing mechanism 1040 CanCand[m1] = Candidates[CanCoord[m1][Row_Pointer]][CanCoord[m1][Column_Pointer]]; // and update the analytical array for new contents of 1041 CanCand[m2] = Candidates[CanCoord[m2][Row_Pointer]][CanCoord[m2][Column_Pointer]]; // these four cells 1042 CanCand[m3] = Candidates[CanCoord[m3][Row_Pointer]][CanCoord[m3][Column_Pointer]]; // because they need to match Puzzle_Grid 1043 CanCand[m4] = Candidates[CanCoord[m4][Row_Pointer]][CanCoord[m4][Column_Pointer]]; // and its been updated 1044 }}}}} // end of process quads 1045 1046} // end of HPTQ Process 1047 1048void Y_Wing(){ // routine to execute Y algorithm - 3 cells with 2 candidates each in 1049 // XY (Pivot), YZ (Pincer 1) and XZ (Pincer 2) pattern 1050 Cand_Load_2(); // load RD array with all two candidate r,c, and candidates (c1 x 10 + c2) 1051 1052 for (int i = 0; i < RDC_Length; i++){ // ask each cell to be the pivot 1053 for (int j = 0; j < RDC_Length; j++){ // and find to others 1054 for (int k = 0; k < RDC_Length; k++){ 1055 if (Y_Check(i,j,k)){Clear_Y(i,j,k);} // look for the XY XY, YZ, XZ matchup and clear if found 1056 }}} 1057 1058 } // end of Y_Wing 1059 1060bool Y_Check(int i, int j, int k){ // check for the appropriate pattern 1061 int CN1A, CN1B, CN2A, CN2B, CN3A, CN3B; // three candidates to check 1062 bool Match_Flag; 1063 int Y_Candidate; // matching candidate between Pivot and Pincer 1 1064 int Z_Candidate; // non-matching pivot / Pincer one in Pincer 1 1065 int X_Candidate; // non-matching candidate Pivot/ Pincer 1 that is in Pivot 1066 1067 if (i == j || i == k || j == k){return false;} // don't compare to yourself 1068 1069 if (!In_Same_Unit(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],RDC_Matrix[j][Row_Pointer],RDC_Matrix[j][Column_Pointer])){return false;} // check same unit pivot and pincer 1 1070 if (!In_Same_Unit(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],RDC_Matrix[k][Row_Pointer],RDC_Matrix[k][Column_Pointer])){return false;} // check same unit pivot and pincer 2 1071 if (In_Same_Unit(RDC_Matrix[j][Row_Pointer],RDC_Matrix[j][Column_Pointer],RDC_Matrix[k][Row_Pointer],RDC_Matrix[k][Column_Pointer])){return false;} //but pincer 1 and pincer 2 can't be same 1072 1073 CN1A = RDC_Matrix[i][RDC_CurrCan]/10; // load with the six candidates 1074 CN1B = RDC_Matrix[i][RDC_CurrCan]%10; 1075 CN2A = RDC_Matrix[j][RDC_CurrCan]/10; 1076 CN2B = RDC_Matrix[j][RDC_CurrCan]%10; 1077 CN3A = RDC_Matrix[k][RDC_CurrCan]/10; 1078 CN3B = RDC_Matrix[k][RDC_CurrCan]%10; 1079 1080 Match_Flag = false; // check pivot against pincer 1 1081 if ((CN1A == CN2A) && (CN1B != CN2B)) {Match_Flag = true; // flag that we have one of the legal matchs 1082 Y_Candidate = CN1A; //common between pivot and pincer 1083 X_Candidate = CN1B; //unique to pivot 1084 Z_Candidate = CN2B;} // unique to pincer 1085 if ((CN1B == CN2A) && (CN1A != CN2B)) {Match_Flag = true; 1086 Y_Candidate = CN1B; //common between pivot and pincer 1087 X_Candidate = CN1A; //unique to pivot 1088 Z_Candidate = CN2B;} // unique to pincer 1089 if ((CN1A == CN2B) && (CN1B != CN2A)) {Match_Flag = true; 1090 Y_Candidate = CN1A; //common between pivot and pincer 1091 X_Candidate = CN1B; //unique to pivot 1092 Z_Candidate = CN2A;} // unique to pincer 1093 if ((CN1B == CN2B) && (CN1A != CN2A)) {Match_Flag = true; 1094 Y_Candidate = CN1B; //common between pivot and pincer 1095 X_Candidate = CN1A; //unique to pivot 1096 Z_Candidate = CN2A;} // unique to pincer 1097 if (!Match_Flag) {return false;} 1098 1099 // We have pivot and one pincer, the XY and YZ, so we now need to find a 2nd pivot with XZ 1100 1101 if ((CN3A == X_Candidate && CN3B == Z_Candidate) || (CN3B == X_Candidate && CN3A == Z_Candidate)) { // if Pincer 2's candidates match X,Z then 1102 Y_Wing_Z = Z_Candidate; // we have found a match, load candidate to clear 1103 return true; } // and return 'success' 1104 1105 return false; // otherwise fall through to failure 1106 1107} // end of Y_Check 1108 1109void Clear_Y(int i,int j,int k){ // clear the candidate from cells that can 'see' both pincers 1110 int mr1, mr2, mr3, mc1, mc2, mc3; // 1111 mr1 = RDC_Matrix[i][Row_Pointer]; // for readability and speed, retrieve coordinates 1112 mr2 = RDC_Matrix[j][Row_Pointer]; // rather than do repeated, nested indexing 1113 mr3 = RDC_Matrix[k][Row_Pointer]; 1114 mc1 = RDC_Matrix[i][Column_Pointer]; 1115 mc2 = RDC_Matrix[j][Column_Pointer]; 1116 mc3 = RDC_Matrix[k][Column_Pointer]; 1117 1118 for (int r = 0; r <9; r++){ // check all puzzle cells 1119 for (int c = 0; c <9; c++){ // clear candidate seen by both pincers 1120 if (!(r == mr1 && c == mc1) && !(r == mr2 && c == mc2) && !(r == mr3 && c == mc3) && In_Same_Unit(r,c,mr2,mc2) && In_Same_Unit(r,c,mr3,mc3)){ 1121 Candidate_Off(Y_Wing_Z,r,c, "Y-Wing"); }}} 1122} // end of Clear_XY 1123 1124void XYZ_Wing(){ 1125 1126 byte C_Y_Search_Candidate; 1127 byte C_Z_Clear_Candidate; 1128 1129 for (int b = 0; b<9; b++){ // check each block 1130 for (int rr = Box_Coords[b][Row_Pointer]; rr <= (Box_Coords[b][Row_Pointer])+2; rr++){ //walk the box 1131 for (int cc = Box_Coords[b][Column_Pointer]; cc <= (Box_Coords[b][Column_Pointer])+2; cc++){ 1132 if (Candidates_Count[rr][cc] == 3){ // eg {1,4,5} 1133 for (int r = Box_Coords[b][Row_Pointer]; r <= (Box_Coords[b][Row_Pointer])+2; r++){ //walk the box again 1134 for (int c = Box_Coords[b][Column_Pointer]; c <= (Box_Coords[b][Column_Pointer])+2; c++){ // looking for a subset bivalue// 1135 if (Candidates_Count[r][c] == 2 && (BitsMatch(Candidates[r][c],Candidates[rr][cc]))) { // found one and it contains two of my 3 (could be {1,4} or {1,5} or {4,5}) 1136 C_Y_Search_Candidate = BitsWhich( (Candidates[r][c] ^ Candidates[rr][cc])); // need to find another bivalue with this candidate and one of the two 1137 for (int rrr = 0; rrr <9; rrr++){ // check all puzzle cells 1138 for (int ccc = 0; ccc <9; ccc++){ 1139 if ( (Candidates_Count[rrr][ccc] == 2)&& (In_Same_Unit(rr,cc,rrr,ccc)) && (If_Candidate_On(C_Y_Search_Candidate,rrr, ccc)) && (BitsMatch(Candidates[rrr][ccc],Candidates[rr][cc])) // same unit as base, has the search candidate and all candidates are in base 1140 && (In_Box(rrr,ccc) != b) ) // and its in a different box and a bivalue 1141 { 1142 BitMask = Candidates[rr][cc]; // start with the three candidates in base 1143 BitMask = BitMask & Candidates[r][c]; // prune candidate not found in first 'pincer' 1144 BitMask = BitMask & Candidates[rrr][ccc]; // prune candidate not found in second 'pincer' 1145 C_Z_Clear_Candidate = BitsWhich(BitMask); // last candidate standing is Z - the candidate common to all three 1146 XYZ_Clear( C_Z_Clear_Candidate,b,rr,cc,r,c,rrr,ccc); // look for candidates to clear 1147 } // end of found 3rd cell 1148 }} // end of puzzle walk to find 3rd cell 1149 } // end of found first bivalue 1150 }} // end of check remainder (none pivot) of box for bivalue 1151 } // end of found a potential pivot (3 candidates) 1152 }} // end of box walk 1153 } // end of all boxes 1154} // end of XYZ Wing 1155 1156void XYZ_Clear(int MyCandidate, int b, int rz, int cz, int rx, int cx, int ry, int cy){ 1157 for (int rr = Box_Coords[b][Row_Pointer]; rr <= (Box_Coords[b][Row_Pointer])+2; rr++){ //walk the box 1158 for (int cc = Box_Coords[b][Column_Pointer]; cc <= (Box_Coords[b][Column_Pointer])+2; cc++){ 1159 if (!Same_Cell(rr,cc,rz,cz) && !Same_Cell(rr,cc,rx,cx)){ // dont look at base (z) or 2nd in-box cell (x) 1160 if (In_Same_Unit(rr,cc,rz,cz) && (In_Same_Unit(rr,cc,rx,cx) && (In_Same_Unit(rr,cc,ry,cy)))){Candidate_Off(MyCandidate,rr,cc,"XYZ-Wing");} 1161 } 1162 }} // end 1163} // end of XYZ_Clear 1164 1165void WXYZ_Wing(){ // implements WXYZ algorithm 1166 1167 byte WXYZ_P1_CandidateDM; 1168 byte WXYZ_P1_CandidateM; 1169 byte WXYZ_P2_CandidateDM; 1170 byte WXYZ_P2_CandidateM; 1171 byte WXYZ_P3_Candidate; 1172 1173 1174 for (int b = 0; b<9; b++){ // check each box 1175 for (int rr = Box_Coords[b][Row_Pointer]; rr <= (Box_Coords[b][Row_Pointer])+2; rr++){ //walk the box 1176 for (int cc = Box_Coords[b][Column_Pointer]; cc <= (Box_Coords[b][Column_Pointer])+2; cc++){ 1177 if (Candidates_Count[rr][cc] == 3){ // eg {1,4,5} [Found base] 1178 BitMask = Candidates[rr][cc]; // and store the mask 1179 for (int r1 = Box_Coords[b][Row_Pointer]; r1 <= (Box_Coords[b][Row_Pointer])+2; r1++){ //walk the box again 1180 for (int c1 = Box_Coords[b][Column_Pointer]; c1 <= (Box_Coords[b][Column_Pointer])+2; c1++){ // looking for a subset bivalue 1181 if ( Candidates_Count[r1][c1] == 2 && (WXYZ_BitsMatchOne(Candidates[r1][c1],BitMask)) ) { // found one and it contains one of base's 3 [Found Wing 1] 1182 bitClear(BitMask,WXYZ_Match); // clear the 'used' 3-c candidate we matched on 1183 WXYZ_P1_CandidateDM = WXYZ_Didnt_Match; // and remember what the unique (non-match) candidate was in the first bivalue 1184 WXYZ_P1_CandidateM = WXYZ_Match; // and remember what the match candidate was too 1185 1186 for (int r2 = Box_Coords[b][Row_Pointer]; r2 <= (Box_Coords[b][Row_Pointer])+2; r2++){ //walk the box again 1187 for (int c2 = Box_Coords[b][Column_Pointer]; c2 <= (Box_Coords[b][Column_Pointer])+2; c2++){ // looking for a subset bivalue 1188 if ( (Candidates_Count[r2][c2] == 2) && (WXYZ_BitsMatchOne(Candidates[r2][c2],BitMask)) && !(Same_Cell(r1,c1,r2,c2)) ) { // [Found Wing 2] 1189 bitClear(BitMask,WXYZ_Match); // clear the 'used' 3-c candidate we matched on 1190 WXYZ_P2_CandidateDM = WXYZ_Didnt_Match; // and remember what the unique (non-match) candidate was in the first bivalue 1191 WXYZ_P2_CandidateM = WXYZ_Match; // and remember what the match candidate was too 1192 WXYZ_P3_Candidate = BitsWhich(BitMask); // the fourth candidate should be {WXYZ_P3_Candidate, WXYZ_P2_CandidateDM} 1193 1194 if ( (WXYZ_P2_CandidateDM == WXYZ_P1_CandidateDM) && (WXYZ_P2_CandidateM != WXYZ_P1_CandidateM) ){ // now have three of the four (those in the box) [Found Wing 3] 1195 bitSet(BitMask, WXYZ_P3_Candidate); 1196 bitSet(BitMask, WXYZ_P2_CandidateDM); 1197 for (int r3 = 0; r3 < 9; r3++){ // walk the puzzle looking for 1198 for (int c3 = 0; c3 < 9; c3++){ 1199 if ( (In_Box(r3,c3) != b) && (Candidates_Count[r3][c3] == 2) && (Candidates[r3][c3] == BitMask) && In_Same_Unit(r3,c3,rr,cc) ) 1200 { 1201 // DDTs("WXYZ Base cell is ",SL); 1202 // Show_RC(rr,cc,SL); 1203 // DDTs(" Wing 1 is ",SL); 1204 // Show_RC(r1,c1,SL); 1205 // DDTs(" Wing 2 is ",SL); 1206 // Show_RC(r2,c2,SL); 1207 // DDTs(" Wing3 (not in box) is ",SL); 1208 // Show_RC(r3,c3,SL); 1209 // DDTv(" Candidate to clear is ",WXYZ_P2_CandidateDM,NL); 1210 for (int r4 = Box_Coords[b][Row_Pointer]; r4 <= (Box_Coords[b][Row_Pointer])+2; r4++){ //walk the box 1211 for (int c4 = Box_Coords[b][Column_Pointer]; c4 <= (Box_Coords[b][Column_Pointer])+2; c4++){ 1212 if (In_Same_Unit(r4,c4,r3,c3) && !(Same_Cell(r4,c4,r1,c1)) && !(Same_Cell(c4,c4,r2,c2)) ) {Candidate_Off(WXYZ_P2_CandidateDM,r4,c4,"WXYZ-Wing");} }} 1213 } // end of found 3rd Wing, clearing candidate 1214 }} // end of walk puzzle looking for third Wing 1215 1216 } // end of we have all 3 (base plus two Wings) inside the box 1217 } // end of found Wing 1 1218 }} // end of walking the box looking for Wing 2 (r3,c2) 1219 1220 } // end of found first bivalue Wing 1221 }} // end of check remainder (none pivot) of box for bivalue 1222 } // end of found a potential pivot (3 candidates) 1223 }} // end of box walk 1224 } // end of all boxes 1225} // end of WXYZ Wing 1226 1227bool WXYZ_BitsMatchOne(int BitMask1,int BitMask2){ // returns true if there is match on one single bit of smaller against larger mask 1228 // and loads global stores of which matched, which didn't and a count (has to be one) 1229byte WXYZ_Match_Count = 0; // reset number of candidates we match on 1230 1231// DDTs("BMO called with ",SL); 1232// Show_PB(BitMask1); 1233 // Show_PB(BitMask2); 1234 for (int n = 0; n <=9; n++){ 1235 if (bitRead(BitMask1,n) > 0) { // if candidate is on in first mask, and 1236 if (bitRead(BitMask2,n) > 0){ // on in 2nd also, its match so 1237 WXYZ_Match_Count++; // increment match count 1238 WXYZ_Match = n;}} // and this is the candidate that matches so far 1239 if ((bitRead(BitMask1,n) > 0) && (bitRead(BitMask2,n) == 0)){WXYZ_Didnt_Match = n;}} 1240 if (WXYZ_Match_Count == 1){ 1241 // DDTs("BMO returns TRUE with ",SL); 1242 // DDTv(" Match is ",WXYZ_Match,SL); 1243 // DDTv(" and NOMatch is ",WXYZ_Didnt_Match,NL); 1244 return true;} else { 1245 // DDTs("BMO returns FALSE ",NL); 1246 return false;} // if there was a match and there's only ONE then return true 1247} // end of WXYZ_BitsMatchOne 1248 1249void Cand_Load_2(){ // load RDC with r,c, and candidates for all two candidate cells 1250 RDC_Length = 0; // used by RDC, UR and FC 1251 byte First_Candidate; 1252 byte Second_Candidate; 1253 for (int r = 0; r < 9; r++){ // set up RDC with all cells in puzzle having 2 candidates 1254 for (int c = 0; c < 9; c++){ 1255 if (Candidates_Count[r][c] == 2){ // if two candidates, store row, column 1256 RDC_Matrix[RDC_Length][Row_Pointer] = r; 1257 RDC_Matrix[RDC_Length][Column_Pointer] = c; 1258 First_Candidate = Find_Next_Candidate(r,c,1); 1259 Second_Candidate = Find_Next_Candidate(r,c,First_Candidate+1); 1260 RDC_Matrix[RDC_Length][RDC_CurrCan] = (First_Candidate * 10) + Second_Candidate; 1261 RDC_Length++; }}} // and increment pointer (So, both candidates "fit" into single byte (10*C1 + C2) 1262} // end of Cand_Load_2 1263 1264/* 1265 * X_Wing, Swordfish and JellyFish are all variants of the same approach: n candidates appearing at least partially in n rows or columns 1266 */ 1267 1268void X_Wing(){ // X_Wing algorithm 1269 for (int n = 1; n <=9; n++){ // looking at each number 1270 for (int r = 0; r < 9; r++){Cand_Limited_R(r, n,2);} // build array on each ROW 1271 for (int r = 0; r < 9-1; r++){ // examine the row array 1272 if ((SWF[r][SWF_Number_Found] > 0) && (SWF[r][SWF_Number_Found] <=2) ){ 1273 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1274 SWF_Mask = 0; // will be used to mark what the active columns are 1275 SWF_Start = SWF[r][SWF_Number_Found]; // this is how many unique columns are involved with this row 1276 SWF[r][SWF_Member] = true; // and this row always starts pre-marked for membership 1277 SWF_Membership = true; // and thats how many I have 1278 for (int j = 0; j < SWF[r][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[r][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1279 for (int rr = r+1; rr < 9; rr++){if (SWF_Match(r,rr,2)){SWF[rr][SWF_Member] = true; // if this row can be added without exceeding 2 columns, mark it 1280 SWF_Membership++;}} // and increment membership number 1281 1282 if ((SWF_Membership == 2) && (BitsCount(SWF_Mask)==2)) {ClearR_SWF(n,"X-Wing" );} // check that we have 2 columns and 2 rows 1283 1284 } // end of row not having zero of this 'r' 1285 } // end of row 1286 } // end for this candidate number 1287 1288 1289 for (int n = 1; n <=9; n++){ // looking at each number 1290 for (int c = 0; c < 9; c++){Cand_Limited_C(c, n,2);} // build array on each COLUMN 1291 // Show_SWF(n); 1292 for (int c = 0; c < 9-1; c++){ // examine the column array 1293 if ((SWF[c][SWF_Number_Found] > 0) && (SWF[c][SWF_Number_Found] <=2) ){ 1294 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1295 SWF_Mask = 0; // will be used to mark what the active rows are 1296 SWF_Start = SWF[c][SWF_Number_Found]; // this is how many unique rows are involved with this column 1297 SWF[c][SWF_Member] = true; // and this row always starts pre-marked for membership 1298 SWF_Membership = true; // and thats how many I have 1299 for (int j = 0; j < SWF[c][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[c][SWF_Marks+j]);} // mark where the starting column's rows are 'x'ed 1300 for (int cc = c+1; cc < 9; cc++){if (SWF_Match(c,cc,2)){SWF[cc][SWF_Member] = true; // if this row can be added without exceeding 2 columns, mark it 1301 SWF_Membership++;}} // and increment membership number 1302 1303 if ((SWF_Membership == 2) && (BitsCount(SWF_Mask)==2)) {ClearC_SWF(n, "X-Wing");} // check that we have 2 columns and two rows 1304 1305 } // end of column not having zero of this 'c' 1306 } // end of column 1307 } // end for this candidate number 1308} // end of X_Wing 1309 1310void Swordfish(){ // swordfish algorithm 1311 for (int n = 1; n <=9; n++){ // looking at each number 1312 for (int r = 0; r < 9; r++){Cand_Limited_R(r, n,3);} // build array on each ROW 1313 // Show_SWF(n); 1314 for (int r = 0; r < 9-2; r++){ // examine the row array 1315 if ((SWF[r][SWF_Number_Found] > 0) && (SWF[r][SWF_Number_Found] <=3) ){ 1316 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1317 SWF_Mask = 0; // will be used to mark what the active columns are 1318 SWF_Start = SWF[r][SWF_Number_Found]; // this is how many unique columns are involved with this row 1319 SWF[r][SWF_Member] = true; // and this row always starts pre-marked for membership 1320 SWF_Membership = true; // and thats how many I have 1321 for (int j = 0; j < SWF[r][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[r][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1322 for (int rr = r+1; rr < 9; rr++){if (SWF_Match(r,rr,3)){SWF[rr][SWF_Member] = true; // if this row can be added without exceeding 3 columns, mark it 1323 SWF_Membership++;}} // and increment membership number 1324 1325 if ((SWF_Membership == 3) && (BitsCount(SWF_Mask)==3)) {ClearR_SWF(n, "Swordfish");} // check that we have 3 row/columns and 3 columns/rows 1326 1327 } // end of row not having zero of this 'r' 1328 } // end of row 1329 } // end for this candidate number 1330 1331 1332 for (int n = 1; n <=9; n++){ // looking at each number 1333 for (int c = 0; c < 9; c++){Cand_Limited_C(c, n,3);} // build array on each COLUMN 1334 // Show_SWF(n); 1335 for (int c = 0; c < 9-2; c++){ // examine the column array 1336 if ((SWF[c][SWF_Number_Found] > 0) && (SWF[c][SWF_Number_Found] <=3) ){ 1337 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1338 SWF_Mask = 0; // will be used to mark what the active rows are 1339 SWF_Start = SWF[c][SWF_Number_Found]; // this is how many unique rows are involved with this column 1340 SWF[c][SWF_Member] = true; // and this row always starts pre-marked for membership 1341 SWF_Membership = true; // and thats how many I have 1342 for (int j = 0; j < SWF[c][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[c][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1343 for (int cc = c+1; cc < 9; cc++){if (SWF_Match(c,cc,3)){SWF[cc][SWF_Member] = true; // if this row can be added without exceeding 3 columns, mark it 1344 SWF_Membership++;}} // and increment membership number 1345 1346 if ((SWF_Membership == 3) && (BitsCount(SWF_Mask)==3)) {ClearC_SWF(n, "Swordfish");} // check that we have 3 row/columns and 3 columns/rows 1347 1348 } // end of column not having zero of this 'c' 1349 } // end of column 1350 } // end for this candidate number 1351} // end of Swordfish 1352 1353void Jellyfish(){ // Jellyfish algorithm, same as Swordfish but 4 rows or columns 1354 for (int n = 1; n <=9; n++){ // looking at each number 1355 for (int r = 0; r < 9; r++){Cand_Limited_R(r, n,4);} // build array on each ROW 1356 for (int r = 0; r < 9-3; r++){ // examine the row array 1357 if ((SWF[r][SWF_Number_Found] > 0) && (SWF[r][SWF_Number_Found] <=4) ){ 1358 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1359 SWF_Mask = 0; // will be used to mark what the active columns are 1360 SWF_Start = SWF[r][SWF_Number_Found]; // this is how many unique columns are involved with this row 1361 SWF[r][SWF_Member] = true; // and this row always starts pre-marked for membership 1362 SWF_Membership = true; // and thats how many I have 1363 for (int j = 0; j < SWF[r][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[r][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1364 for (int rr = r+1; rr < 9; rr++){if (SWF_Match(r,rr,4)){SWF[rr][SWF_Member] = true; // if this row can be added without exceeding 4 columns, mark it 1365 SWF_Membership++;}} // and increment membership number 1366 1367 if ((SWF_Membership == 4) && (BitsCount(SWF_Mask) ==4)) {ClearR_SWF(n, "Jellyfish");} // check that we have 4 row/columns and 4 columns/rows 1368 1369 } // end of row not having zero of this 'r' 1370 } // end of row 1371 } // end for this candidate number 1372 1373 1374 for (int n = 1; n <=9; n++){ // looking at each number 1375 for (int c = 0; c < 9; c++){Cand_Limited_C(c, n,4);} // build array on each COLUMN 1376 for (int c = 0; c < 9-3; c++){ // examine the column array 1377 if ((SWF[c][SWF_Number_Found] > 0) && (SWF[c][SWF_Number_Found] <=4) ){ 1378 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1379 SWF_Mask = 0; // will be used to mark what the active rows are 1380 SWF_Start = SWF[c][SWF_Number_Found]; // this is how many unique rows are involved with this column 1381 SWF[c][SWF_Member] = true; // and this row always starts pre-marked for membership 1382 SWF_Membership = true; // and thats how many I have 1383 for (int j = 0; j < SWF[c][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[c][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1384 for (int cc = c+1; cc < 9; cc++){if (SWF_Match(c,cc,4)){SWF[cc][SWF_Member] = true; // if this row can be added without exceeding 4 columns, mark it 1385 SWF_Membership++;}} // and increment membership number 1386 1387 if ((SWF_Membership == 4) && (BitsCount(SWF_Mask)==4)) {ClearC_SWF(n, "Jellyfish");} // check that we have 4 row/columns and 4 columns/rows 1388 1389 } // end of column not having zero of this 'c' 1390 } // end of column 1391 } // end for this candidate number 1392} // end of JellyFish 1393 1394void Cand_Limited_R(int r, int n, int My_Limit){ // check for a candidate appearing twice or thrice in row 1395 int counter = 0; 1396 SWF[r][SWF_Number_Found] = 0; 1397 for (int i = 0; i<My_Limit; i++){SWF[r][SWF_Marks+i] = 0;} 1398 for (int c = 0; c<9; c++){ 1399 if (If_Candidate_On(n,r, c)){ 1400 SWF[r][SWF_Number_Found]++; // number of rows found in 1401 if (SWF[r][SWF_Number_Found] <=My_Limit){SWF[r][SWF_Marks+counter] = c; // found in this column, up to limit (e.g., x-wing = 2, Swordfish=3, Jellyfish = 4) 1402 counter++; // and increment counter 1403 }}} 1404} // end of CandLimited_R 1405 1406void Cand_Limited_C(int c, int n,int My_Limit){ // check for a candidate appearing twice or thrice in column 1407 int counter = 0; 1408 SWF[c][SWF_Number_Found] = 0; 1409 for (int i = 0; i<My_Limit; i++){SWF[c][SWF_Marks+i] = 0;} 1410 for (int r = 0; r<9; r++){ 1411 if (If_Candidate_On(n,r, c)){ 1412 SWF[c][SWF_Number_Found]++; // number of columns found in 1413 if (SWF[c][SWF_Number_Found] <=My_Limit){SWF[c][SWF_Marks+counter] = r; // found in this row 1414 counter++; // and increment counter 1415 }}} 1416} // end of CandLimited_C 1417 1418bool SWF_Match(int Master,int Matcher, int My_Limit){ // look for matching position in marked cells 1419 int counter = 0; // so far no data 1420 BitMask = 0; // clear mask 1421 if (SWF[Matcher][SWF_Number_Found] == 0 || (Master==Matcher) || (SWF[Matcher][SWF_Number_Found] > My_Limit)){return false;} // if this is a blank row, or too many no sense in checking 1422 for (int j = 0; j < SWF[Matcher][SWF_Number_Found]; j++){ // run through columns for this row 1423 bitSet(BitMask, SWF[Matcher][SWF_Marks+j] ); // set a bit for each one found 1424 if (bitRead(SWF_Mask, SWF[Matcher][SWF_Marks+j]) == 0) {counter++;}} // if this is a 'new' column, add another to counter 1425 if (counter + SWF_Start <=My_Limit) { // if I'm still under the wire, return true 1426 SWF_Mask = SWF_Mask | BitMask; // keep tabs on # columns/rows over multiple matches 1427 return true;} 1428 return false; // if more than three, no good 1429 } // end of SWF_Match 1430 1431void XY_Chain(){ // XY Chain algorithm 1432 1433 Cand_Load_2(); // load all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2) 1434 for (int j = 0; j< RDC_Length; j++) { // try every cell in the that is in the 2 candidate list 1435 FC_Load(); // copy RD matrix to working force chain matrix 1436 FC_List_Clear(); // clear list of entries that have already been used as a 'successor' 1437 XY_Chain_R(j,FC_1, FC_Up); // follow all chains on first candidate 1438 1439 FC_List_Clear(); // reset list of entries that have already been used as a 'successor' 1440 XY_Chain_R(j,FC_2, FC_Down); // follow all chains on first candidate 1441 1442 for (int k = 0; k < RDC_Length; k++){ // check each entry in the chain list 1443 if (FC_UD[k][FC_1] == FC_UpandDown ) { // if there's both an 'up' mark and a 'down' mark, 1444 Candidate_Off(FC_CN[k][FC_1],FC_R[k],FC_C[k], "XY-Chain"); // clear the corresponding candidate -- got there through both sides of chain 1445 return;} // and return because all other search data is invalid (this entry is no longer part of 2-candidate list) 1446 else {if (FC_UD[k][FC_2] == FC_UpandDown ) { // and it saves time -- we may be back again if all easier algorithms fail 1447 Candidate_Off(FC_CN[k][FC_2],FC_R[k],FC_C[k], "XY-Chain"); 1448 return;}} 1449 } 1450 } // end of tried all 2 candidate cells 1451 return; // return after trying all 2-candidate entries as head-of-chain 1452 } // end of XY chain 1453 1454 void XY_Chain_R(int index, int LR, int My_Mark){ // follows chain and marks with up or down 1455 byte FC_Heads[FC_Max_Heads]; 1456 byte FC_LR[FC_Max_Heads]; 1457 byte FC_HP = 0; 1458 byte FC_LRN; 1459 1460 byte Next_Candidate = FC_CN[index][LR]; // get candidate 1 or 2 1461 if (LR == FC_1) {FC_LRN = FC_2;} else {FC_LRN = FC_1;} //flip it 1462 FC_UD[index][FC_LRN] = FC_UD[index][FC_LRN] | My_Mark; // mark the candidate 1463 1464 for (int i = 0; i< RDC_Length; i++){ 1465 if ((index !=i)&& (!FC_List[i]) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (Next_Candidate == FC_CN[i][FC_1] || Next_Candidate == FC_CN[i][FC_2]) ) // test to see if entry is a valid successor 1466 // as next link in chain 1467 { // increment head of chain pointer 1468 FC_Heads[FC_HP] = i; // store each possible successor 1469 FC_List[i] = true; // Mark FC_List for this entry so don't check successors multiple times 1470 if (Next_Candidate == FC_CN[i][FC_1]){FC_LR[FC_HP] = FC_2;} else {FC_LR[FC_HP] = FC_1;} // if match was on first candidate, use 2nd, elsewise use 1st 1471 1472 if (FC_HP < FC_Max_Heads-1) {FC_HP++;} 1473 } // end of "this IS a valid successor" 1474 } // end of trying all cells as a potential successor 1475 if (FC_HP == 0) {return; } // just return if no successors found 1476 1477 for (int i = 0; i < FC_HP; i++){ // if there ARE successors to this entry (next link in chain), 1478 XY_Chain_R(FC_Heads[i],FC_LR[i],My_Mark);} // follow onto each of them separately 1479 1480 } // end of XY_Chain_R 1481 1482void X_Chain(){ // single candidate forcing chain - X Chain 1483 for (int n = 1; n <= 9; n++){ // for each candidate n, ************** 1484 XC_Load(n); // find occurences 1485 for (int j = 0; j<FC_Ptr; j++){ // go through each cell with current n, 1486 if (If_Candidate_On(n,FC_R[j],FC_C[j])){ // make sure candidate wasn't cleared earlier 1487 XC_Link_Counter = 0; // reset link counter 1488 XC_Head_R = FC_R[j]; // unlike XY chain, we need to know where the head of the chain 1489 XC_Head_C = FC_C[j]; // was 1490 FC_Put_On_List(XC_Head_R,XC_Head_C); // don't double back on the head of the chain 1491 XC_Chain(j,n,Strong_Link); // jump off looking for strong links 1492 } // end of candidate is still in this cell 1493 } // end of all of single digit 1494 } // end of all 9 digits 1495} // end of X_Chain 1496 1497void XC_Chain(int index,int n, bool My_Mark){ // follows chain looking for strong and weak links 1498 1499 for (int i = 0; i< FC_Ptr; i++){ // look all through the list of cells with 'n' as candidate 1500 if ((index !=i)&& (!FC_On_List(FC_R[i],FC_C[i])) && (If_Candidate_On(n,FC_R[i],FC_C[i])) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (XC_Link(i,index,n,My_Mark)) ) // test to see if entry is a valid successor, either strong or weak 1501 // as next link in chain 1502 { 1503 // DDTv(" Candidate ",n,SL); 1504 // DDTsp(SL); 1505 // Show_RC(FC_R[index],FC_C[index],SL); 1506 // for (int xx = 1; xx <=XC_Link_Counter; xx++){DDTs(" ",SL);} 1507 // if (My_Mark) {DDTs(" Links Strong to ",SL);}else {DDTs(" Links weak to ",SL);} 1508 // Show_RC(FC_R[i],FC_C[i],NL); 1509 XC_Link_Counter++; // increment link counter 1510 FC_Put_On_List(FC_R[i],FC_C[i]); // add this cell to the 'used' list 1511 if (XC_Link_Counter == 3 || XC_Link_Counter == 5 || XC_Link_Counter == 7 || XC_Link_Counter == 9 || XC_Link_Counter == 11 || XC_Link_Counter == 13){ 1512 XC_Clear_Check(n,XC_Head_R,XC_Head_C,FC_R[i],FC_C[i]);} // clear candidate if we find appropriate entries (seen by both ends of chain) 1513 XC_Chain(i,n,(!My_Mark)); // follow onto each of them separately , alternating looking for strong and weak links 1514 XC_Link_Counter--; 1515 } // end of "this IS a valid successor" 1516 } // end of trying all cells as a potential successor 1517 return; // just return if no successors found 1518 } // end of XC Chain 1519 1520void XC_Clear_Check(int n, int r1, int c1, int r2, int c2){ //clear candidates from cells that see both end of chain 1521 byte r3; // target row and column 1522 byte c3; 1523 bool XC_Eligible; // eligible to be cleared 1524 1525 for (int i = 0; i< FC_Ptr; i++){ // we already know all cells with candidate n, so don't check others 1526 r3 = FC_R[i]; // retrieve row and column 1527 c3 = FC_C[i]; 1528 XC_Eligible = true; // assume we found one 1529 if ((r3 == r1 && c3 == c1) || (r3 == r2 && c3 == c2)) {XC_Eligible = false;} // but don't check the two endpoints of chain 1530 if (XC_Eligible){if ( !((In_Same_Unit(r1,c1,r3,c3)) && (In_Same_Unit(r2,c2,r3,c3)))){XC_Eligible = false;}} // not an endpoint 1531 if (XC_Eligible) {Candidate_Off(n,r3,c3, "X-Chain");} 1532 } 1533} // end of XC Clear Check 1534 1535bool XC_Link(int Dest_index,int Src_index, int n, bool My_Mark){ // validate as weak or strong link 1536 byte r1; // row and column and box of unit shared by both 1537 byte c1; // current (source) and successor (destination) 1538 byte r2; 1539 byte c2; 1540 byte b1; 1541 byte b2; 1542 byte counter = 0; // for strong links only current and successor should be in the shared unit 1543 1544 if (My_Mark == Weak_Link) {return true;} // we already tested 'in same unit' so either a strong or weak link works as a link 1545 // else we are looking for a strong link (See definitions in Sudoku_Puzzles.h) 1546 r1 = FC_R[Src_index]; // load rows and columns to simplify and reduce calculations for indexing 1547 c1 = FC_C[Src_index]; // have row and column of 'link from' and 'link to' 1548 r2 = FC_R[Dest_index]; 1549 c2 = FC_C[Dest_index]; 1550 b1 = In_Box(r1,c1); 1551 b2 = In_Box(r2,c2); 1552 1553 if (r1 == r2){ // are they in same row? 1554 counter = 0; // yes, reset counter of occurences 1555 for (int cc = 0; cc <9; cc++){ 1556 if (If_Candidate_On(n, r1, cc)){counter++;}} // count # times this candidate appears in row 1557 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 1558 1559 if (c1 == c2){ // are they in same column? 1560 counter = 0; // yes, reset counter of occurences 1561 for (int rr = 0; rr <9; rr++){ 1562 if (If_Candidate_On(n, rr, c1)){counter++;}} // count # times this candidate appears in column 1563 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 1564 1565 if (b1 == b2){ // are they in same box? 1566 counter = 0; // yes, reset counter of occurences 1567 for (int rr = Box_Coords[b1][Row_Pointer]; rr <= (Box_Coords[b1][Row_Pointer])+2; rr++){ //walk the box 1568 for (int cc = Box_Coords[b1][Column_Pointer]; cc <= (Box_Coords[b1][Column_Pointer])+2; cc++) {if (If_Candidate_On(n, rr, cc)) {counter++;}}} // count # times this candidate appears in the box 1569 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 1570 1571 return true; // passed all tests: everywhere link-from 'sees' link-to, there's only 2 of them 1572 1573 } // end of XC Link 1574 1575void XC_Load(int n){ // load all cells having candidates of 'n' into force chain array 1576 FC_Ptr = 0; // reset pointer 1577 for (int r = 0; r < 9; r++){ 1578 for (int c = 0; c < 9; c++){ 1579 if (If_Candidate_On(n,r,c)){ // for every cell having this candidate 1580 FC_R[FC_Ptr] = r; // store row and column indices 1581 FC_C[FC_Ptr] = c; 1582 FC_Ptr ++; }}} // and increment index 1583 1584} // end of XC Load 1585 1586void FC_Load(){ // converts RDC_Matrix to split candidates and 1587 for (int i = 0; i < RDC_Length; i++){ // only works for bi-value cells 1588 FC_R[i] = RDC_Matrix[i][Row_Pointer]; // store r and c 1589 FC_C[i] = RDC_Matrix[i][Column_Pointer]; 1590 FC_CN[i][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; 1591 FC_CN[i][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 1592 FC_UD[i][FC_1]=0; // and resets the up/down bit flags 1593 FC_UD[i][FC_2]=0;} 1594 1595} // end of FC Load 1596 1597void FC_List_Clear(){for (int k=0; k<RDC_Max_Size;k++){FC_List[k] = false;}} // clear list of successors to current cell 1598 1599void FC_Put_On_List(byte r, byte c){ // store entries on 'used' cell list to prevent doubling back and looping 1600 FC_Used_List[XC_Link_Counter] = (10*r)+c; // store number from 00 to 88 (A1 to I9) 1601} // end of FC_Put_On_List 1602 1603void FC_Put_Node_On_List(byte r0, byte c0, byte r1, byte c1){ // store 2nd cell in node as 'used' cell list as well as first one 1604 int T0; 1605 int High_Part = ( (1000*r1)+(100*c1) ); 1606 int Low_Part = ((10*r0)+c0); 1607 if (High_Part < Low_Part){ // arrange for 'bigger' number to be first 1608 T0 = Low_Part; 1609 Low_Part = High_Part; 1610 High_Part = T0;} 1611 FC_Used_List[XC_Link_Counter] = High_Part + Low_Part; // store number from 00 to 88 (A1 to I9) shift over by 10 (i.e., use 100's and 1000's) 1612} // end of FC_Put_On_List 1613 1614 1615bool FC_On_List(int r, int c){ // check to see if cell[r][c] has been registered in the used array 1616 int Search_First; 1617 int Search_Second; 1618 int Search_Hash = (10*r)+c; // first r,c, are stored as 10*r+c, the possible second is 1000*r+100*c 1619 for (int i = 0; i <= XC_Link_Counter; i++){ 1620 Search_First = (FC_Used_List[i])%100; // lower half of possible two row and column values 1621 Search_Second = (FC_Used_List[i])/100; 1622 if (Search_Hash == Search_First) {return true;} // if it matches either 'lower' or 'upper' value 1623 if ( (Search_Second > 0) && (Search_Hash == Search_Second) ) { return true;} 1624 } 1625 return false; 1626} // end of FC_On_List 1627 1628void Forbidding_Chain(){ // single candidate forcing chain - Forbidding Chain 1629 // Show_Candidates(); 1630 for (int n = 1; n <= 9; n++){ // for each candidate n, 1631 XC_Load(n); // find occurences 1632 for (int j = 0; j<FC_Ptr; j++){ // go through each cell with current n, 1633 if (If_Candidate_On(n,FC_R[j],FC_C[j])){ // make sure candidate wasn't cleared earlier 1634 XC_Link_Counter = 0; // reset link counter 1635 XC_Head_R = FC_R[j]; // unlike XY chain, we need to know where the head of the chain 1636 XC_Head_C = FC_C[j]; // was 1637 FC_Put_On_List(XC_Head_R,XC_Head_C); // register head so dont double back and loop 1638 FC_List[XC_Link_Counter] = Weak_Link; 1639 // DDTv("Starting candidate ",n,SL); 1640 // DDTs(" with head ***************",SL); 1641 // Show_RC(XC_Head_R,XC_Head_C,NL); 1642 FBC_Chain(j,n,Weak_Link); // jump off looking for weak links 1643 } // end of candidate is still in this cell 1644 } // end of all of single digit 1645 } // end of all 9 digits 1646} // end of Forbidding Chain 1647 1648void FBC_Chain(int index,int n, bool My_Mark){ // follows chain looking for strong and weak links, (X chain with group nodes) 1649 1650 for (int i = 0; i< FC_Ptr; i++){ // look all through the list of cells with 'n' as candidate 1651 if ((index !=i)&& (Same_Cell(FC_R[i],FC_C[i],XC_Head_R,XC_Head_C)) && (If_Candidate_On(n,FC_R[i],FC_C[i])) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (FBC_Link(i,index,n,My_Mark)) &&(XC_Link_Counter >=2 )&&(My_Mark == Weak_Link) ){ 1652// DDTs("Found Head of Chain ***************** **********************************",SL); 1653// Show_RC(FC_R[i],FC_C[i],NL); 1654 // for (int x = 1; x<= XC_Link_Counter; x++){ 1655 // if (FC_List[x]){DDTs(" Linked Strong to ",SL); } else {DDTs(" Linked Weak to ",SL); } 1656 // Show_RC_Node(x); 1657 // } 1658 // if (FC_List[0]){DDTs(" Linked Strong to ",SL); } else {DDTs(" Linked Weak to ",SL); } 1659 // Show_RC_Node(0); 1660 Candidate_Off(n,XC_Head_R,XC_Head_C, "Forbidding"); 1661 return; 1662 } 1663 1664 if ((index !=i)&& (!FC_On_List(FC_R[i],FC_C[i])) && (If_Candidate_On(n,FC_R[i],FC_C[i])) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (FBC_Link(i,index,n,My_Mark)) ) // test to see if entry is a valid successor, either strong or weak 1665 // as next link in chain 1666 { 1667 // DDTv(" Candidate ",n,SL); 1668 // DDTsp(SL); 1669 // Show_RC(FC_R[index],FC_C[index],SL); 1670 // for (int xx = 1; xx <=XC_Link_Counter; xx++){DDTs(" ",SL);} 1671 // if (My_Mark) {DDTs(" Links Strong to ",SL);}else {DDTs(" Links weak to ",SL);} 1672 // Show_RC(FC_R[i],FC_C[i],NL); 1673 XC_Link_Counter++; // increment link counter 1674 FC_Put_On_List(FC_R[i],FC_C[i]); // add this cell to the 'used' list 1675 FC_List[XC_Link_Counter] = My_Mark; // store currrent Strong_Link or Weak_Link for debugging/display 1676 FBC_Chain(i,n,(!My_Mark)); // follow onto each of them separately , alternating looking for strong and weak links 1677 XC_Link_Counter--; 1678 } // end of "this IS a valid successor" 1679 } // end of trying all cells as a potential successor 1680 return; //upon return, pass it along 1681 } // end of XC Chain 1682 1683bool FBC_Link(int Dest_index,int Src_index, int n, bool My_Mark){ // validate as weak or strong link 1684 byte r1; // row and column and box of unit shared by both 1685 byte c1; // current (source) and successor (destination) 1686 byte r2; byte c2; 1687 byte b1; byte b2; 1688 byte r3; byte c3; 1689 byte counter = 0; // for strong links only current and successor should be in the shared unit 1690 bool Link_is_Strong = true; // assume a strong link if found 1691 1692 if (My_Mark == Weak_Link) {return true;} // we already tested 'in same unit' so either a strong or weak link works as a link 1693 // else we are looking for a strong link (See definitions in Sudoku_Puzzles.h) 1694 r1 = FC_R[Src_index]; // load rows and columns to simplify and reduce calculations for indexing 1695 c1 = FC_C[Src_index]; // have row and column of 'link from' and 'link to' 1696 r2 = FC_R[Dest_index]; 1697 c2 = FC_C[Dest_index]; 1698 b1 = In_Box(r1,c1); // Source box 1699 b2 = In_Box(r2,c2); // Destination box 1700 byte scounter; byte dcounter; byte ncounter; 1701 byte NR1; byte NC1; byte NR2; byte NC2; 1702 byte r0 = ((FC_Used_List[XC_Link_Counter-1])%100)/10; // Predecessor row and column 1703 byte c0 = ((FC_Used_List[XC_Link_Counter-1])%100)%10; 1704 1705 1706 // DDTs("FB Link Checking ",SL); 1707// Show_RC(r1,c1,SL); 1708// DDTs(" and ",SL); 1709// Show_RC(r2,c2,SL); 1710// DDTv(" Box 1 is ",b1,SL); 1711// DDTv(" Box 2 is ",b2,SL); 1712// if (My_Mark == Strong_Link){DDTs(" Looking for Strong Link",NL);} else {DDTs(" Looking for Weak Link",NL);} 1713 1714 1715 if (b1 == b2){ // are they in same box? 1716 counter = 0; // yes, reset counter of occurences 1717 for (int rr = Box_Coords[b1][Row_Pointer]; rr <= (Box_Coords[b1][Row_Pointer])+2; rr++){ //walk the box 1718 for (int cc = Box_Coords[b1][Column_Pointer]; cc <= (Box_Coords[b1][Column_Pointer])+2; cc++) {if (If_Candidate_On(n, rr, cc)) {counter++;}}} // count # times this candidate appears in the box 1719 if (counter !=2) {Link_is_Strong = false;}} // if not 2 (link from and to) then its not a strong link 1720 1721 if ((r1 == r2) && (b1 != b2)){ // are they in same row and different boxes? 1722 counter = 0; // yes, reset counter of occurences 1723 for (int cc = 0; cc <9; cc++){ 1724 if (If_Candidate_On(n, r1, cc)){counter++;}} // count # times this candidate appears in row 1725 if (counter == 2){Link_is_Strong = true;} // simplest case, only two in the row 1726 else {Link_is_Strong = false;} 1727 if (!Link_is_Strong){ // if failed simple 'strong' test, 1728 Link_is_Strong = true; // reset to 'true' for beyond simple test (i.e., there's more than one in each of source and destination) 1729 ncounter = 0; 1730 scounter = 0; 1731 dcounter = 0; 1732 for (int cc = 0; cc <9; cc++){ 1733 if ( (In_Box(r1,cc) != b1) && (In_Box(r1,cc) != b2) && (If_Candidate_On(n, r1, cc)) ){ncounter++;} // count any n's outside of source and destination boxes and in this row 1734 if ( (In_Box(r1,cc) == b1) && (If_Candidate_On(n, r1, cc)) ){scounter++; // count the n's inside source box and row 1735 if (scounter == 1){NR1 = r1; 1736 NC1 = cc;} 1737 if (scounter == 2){NR2 = r1; 1738 NC2 = cc;}} 1739 if ( (In_Box(r1,cc) == b2) && (If_Candidate_On(n, r1, cc)) ){dcounter++;}} 1740 // count the n's inside destination box and row and test for <=2 scounter, 0 in row between and 1 for destination box's row 1741 if ( (ncounter > 0) || (scounter > 2) || (dcounter > 1) ) {Link_is_Strong = false;} // due to limitations, only two source cells in node, and one cell in destination 1742 if ( FC_On_List(NR1,NC1) && (FC_On_List(NR2,NC2)) ) {Link_is_Strong = false;} // if both potential cells in the node are 'used', can't do it 1743 if ( !(In_Same_Unit(NR1,NC1,r0,c0)) || !(In_Same_Unit(NR2,NC2,r0,c0)) ){Link_is_Strong = false;} 1744 if (Link_is_Strong){ 1745 // if (FC_On_List(NR1,NC1)) {DDTs("First cell is on the list",NL);} 1746 // if (FC_On_List(NR2,NC2)) {DDTs("Second cell is on the list",NL);} 1747 FC_Put_Node_On_List(NR1,NC1,NR2,NC2); 1748 // DDTs("Created Row Node ",SL); 1749 // Show_RC(NR1,NC1,SL); 1750 // DDTs(" and ",SL); 1751 // Show_RC(NR2,NC2,SL); 1752 // DDTv(" Stored as ",FC_Used_List[XC_Link_Counter],NL); 1753 } 1754 1755 } // end of failed simple test 1756 } // end this is a 'row' search 1757 1758 if ((c1 == c2) && (b1 != b2)){ // are they in same column and different boxes? 1759 counter = 0; // yes, reset counter of occurences 1760 for (int rr = 0; rr <9; rr++){ 1761 if (If_Candidate_On(n, rr, c1)){counter++;}} // count # times this candidate appears in row 1762 if (counter == 2){Link_is_Strong = true;} // simplest case, only two in the row 1763 else {Link_is_Strong = false;} 1764 if (!Link_is_Strong){ // if failed simple 'strong' test, 1765 Link_is_Strong = true; // reset to 'true' for beyond simple test (i.e., there's more than one in each of source and destination) 1766 ncounter = 0; 1767 scounter = 0; 1768 dcounter = 0; 1769 for (int rr = 0; rr <9; rr++){ 1770 if ( (In_Box(rr,c1) != b1) && (In_Box(rr,c1) != b2) && (If_Candidate_On(n, rr, c1)) ){ncounter++;} // count any n's outside of source and destination boxes and in this column 1771 if ( (In_Box(rr,c1) == b1) && (If_Candidate_On(n, rr, c1)) ){ 1772 scounter++; 1773 if (scounter == 1){NR1 = rr; 1774 NC1 = c1;} 1775 if (scounter == 2){NR2 = rr; 1776 NC2 = c1;}} // count the n's inside source box and column 1777 if ( (In_Box(rr,c1) == b2) && (If_Candidate_On(n, rr, c1)) ){dcounter++;}} // count the n's inside destination box and columnn and test for <=2 scounter, 0 in column between and 1 for destination box's column 1778 1779 if ( (ncounter > 0) || (scounter > 2) || (dcounter > 1)) {Link_is_Strong = false;} // due to limitations, only two source cells in node, and one cell in destination 1780 if ( FC_On_List(NR1,NC1) && (FC_On_List(NR2,NC2)) ) {Link_is_Strong = false;} // if both potential cells in the node are 'used', can't do it 1781 if ( !(In_Same_Unit(NR1,NC1,r0,c0)) || !(In_Same_Unit(NR2,NC2,r0,c0)) ){Link_is_Strong = false;} 1782 if (Link_is_Strong){ 1783 // if (FC_On_List(NR1,NC1)) {DDTs("First cell is on the list",NL);} 1784 // if (FC_On_List(NR2,NC2)) {DDTs("Second cell is on the list",NL);} 1785 FC_Put_Node_On_List(NR1,NC1,NR2,NC2); 1786 // DDTs("Created Column Node ",SL); 1787 // Show_RC(NR1,NC1,SL); 1788 // DDTs(" and ",SL); 1789 // Show_RC(NR2,NC2,SL); 1790 // DDTv(" Stored as ",FC_Used_List[XC_Link_Counter],NL); 1791 } 1792 } // end of failed simple test 1793 } // end this is a 'column' search 1794 1795 1796 if ((My_Mark == Strong_Link) && Link_is_Strong ) {return true;} // passed all tests: everywhere link-from 'sees' link-to, there's only 2 of them 1797 if ((My_Mark == Strong_Link) && !Link_is_Strong ) {return false;} 1798 if ((My_Mark == Weak_Link) && Link_is_Strong) {return false;} 1799 if ((My_Mark == Weak_Link) && !Link_is_Strong) {return true;} 1800 } // end of FBC Link 1801 //*************** 1802 // algorithm for Unique Rectangle, types 1,2,and 4 1803void Unique_Rectangle(){ // UR type one is three 2-candidates cells found and one implied, e.g. 1804 // {3,4} {3,4} {3,4} and {3,4,7} 1805 bool Rectangular; // conforms to all rules for UR 1806 bool UR_Eligible; // eligible by UR 2 to be cleared (Protects corner cells, etc.) 1807 const int UR_First = 0; // constant index to first candidate 1808 byte UR_Candidate; // third candidate 1809 byte UR_R; // row of 'open' corner of rectangle 1810 byte UR_C; // column of open corner 1811 byte UR3_R; // row and column of found locked candidate cell 1812 byte UR3_C; 1813 byte UR3_CC; // make sure there's only one found 1814 byte UR3_PB1; // first pattern breaker candidate 1815 byte UR3_PB2; // second pattern breaker candidate 1816 bool UR_Mark_1; 1817 bool UR_Mark_2; 1818 byte UR5_CC; // corner counter for UR type 5 1819 byte UR5_CC3; // three candidate counter for UR type 5 1820 int UR5_Row_Counter; //counts rows involved in UR type 5 1821 int UR5_Column_Counter; //counts columns involved in UR type 5 1822 byte UR5_Candidate; // extra candidate 1823 1824 Cand_Load_2(); // load all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2) 1825 for (int i=0; i <RDC_Length-1; i++){ // check each entry in the array ************** -1????? 1826 FC_Ptr = UR_First; // reset pointer 1827 UR_R = FC_Flag; // and flag row and column pointers 1828 UR_C = FC_Flag; 1829 FC_R[FC_Ptr] = RDC_Matrix[i][Row_Pointer]; // store r and c 1830 FC_C[FC_Ptr] = RDC_Matrix[i][Column_Pointer]; 1831 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; // store candidates 1832 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 1833 FC_Ptr++; // increment pointer 1834 for (int j = i+1; j < RDC_Length; j++){ // check rest of array now 1835 if ( (FC_CN[UR_First][FC_1] == RDC_Matrix[j][RDC_CurrCan]/10) && (FC_CN[ UR_First][FC_2] == RDC_Matrix[j][RDC_CurrCan]%10) ){ //if same candidates, store as well 1836 FC_R[FC_Ptr] = RDC_Matrix[j][Row_Pointer]; // store r and c 1837 FC_C[FC_Ptr] = RDC_Matrix[j][Column_Pointer]; 1838 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[j][RDC_CurrCan]/10; // store candidates 1839 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[j][RDC_CurrCan]%10; // note: not used except for Show_FC and diagnostics 1840 Rectangular = false; // assume it doesn't meet rectangular criteria 1841 if ((FC_R[FC_Ptr] == FC_R[UR_First]) &&(FC_C[FC_Ptr] != FC_C[UR_First])){ // if rows match and columns don't 1842 UR_C = FC_C[FC_Ptr]; // this will be the 'open' column 1843 Rectangular = true; } // and we have a rectangle 1844 if ((FC_C[FC_Ptr] == FC_C[UR_First]) &&(FC_R[FC_Ptr] != FC_R[UR_First])){ // if Columns match and row don't 1845 UR_R = FC_R[FC_Ptr]; // this will be the 'open' row 1846 Rectangular = true; } // and we have a rectangle 1847 if(Rectangular) {FC_Ptr++;} // if this is a valid corner, increment 1848 } // end of same candidates 1849 } // end of checking through end of array 1850 1851 if (FC_Ptr == 4){ // if we have four corners with same two candidates in two boxes, 1852 BitMask = 0; // clear bit mask to count boxes involved 1853 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,In_Box(FC_R[k],FC_C[k])); } // turn on a bit for each box involved 1854 if (BitsCount(BitMask) == 2){ // count them, need two and only two 1855 No_Solution = true; // this is not a valid puzzle due to Deadly Pattern 1856 DDTs("Deadly Pattern Anchored At ",SL); 1857 Show_RC(FC_R[UR_First], FC_C[UR_First],NL); 1858 Show_Grid(); // and final puzzle grid 1859 T_Msg3("Puzzle","Deadly", "Pattern"); 1860 T_Scroll_Message("Deadly Pattern"); 1861 T_Scroll_Message("Puzzle with"); 1862 T_Scroll_Message("Multiple Solutions"); 1863 return; }} // end of rectangular and 4 corners 1864 1865 if (FC_Ptr == 3){ // if we have three corners, 1866 BitMask = 0; // clear bit mask to count boxes involved 1867 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,In_Box(FC_R[k],FC_C[k])); } // turn on a bit for each box involved 1868 if (BitsCount(BitMask) == 2){ // count them, need two and only two 1869 Candidate_Off(FC_CN[UR_First][FC_1],UR_R, UR_C, "Uniq Rect #1"); // if so, candidate 1870 Candidate_Off(FC_CN[UR_First][FC_2],UR_R, UR_C, "Uniq Rect #1"); 1871 }} // end of rectangular AND 3 corners 1872 1873 // UR type two is two 2-candidates cells found and two more corners with same two candidates PLUS one other, matching candidate (e.g.) 1874 // {3,4} {3,4} and {3,4,7} {3,4,7} // note *********** later enhancement: could be {3,4} {3,4} and {3,4,7} {3,4,8} 1875 // and that also complicates non-diagonal 1876 if (FC_Ptr == 2){ // we found two only 1877 if (UR_C == FC_Flag){ // if row is known and column not, sweep columns within known row 1878 for (int c = 0; c < 9; c++){ // look at each column 1879 for (int j = 0; j <=1; j++){ 1880 if ( (c != FC_C[UR_First]) && (Candidates_Count[FC_R[j]][c] ==3) ){ // ignore the cell we are in & and any cell with not 3 candidates 1881 if ( ( If_Candidate_On(FC_CN[UR_First][FC_1],FC_R[j],c)) && ( If_Candidate_On(FC_CN[UR_First][FC_2],FC_R[j],c)) ){ //if this 3 candidate cell contains the two core candidates, 1882 FC_R[FC_Ptr] = FC_R[j]; // store r and c 1883 FC_C[FC_Ptr] = c; 1884 FC_Ptr++;}}} // if this is a valid corner, increment 1885 }} // end of column search with known row 1886 else 1887 { if (UR_R == FC_Flag){ // if row is known and column not, sweep columns within known row 1888 for (int r = 0; r < 9; r++){ // look at each column 1889 for (int j = 0; j <=1; j++){ 1890 if ( (r != FC_R[UR_First]) && (Candidates_Count[r][FC_C[j]] ==3) ){ // ignore the cell we are in & and any cell with not 3 candidates (may only need last test!) 1891 if ( ( If_Candidate_On(FC_CN[UR_First][FC_1],r,FC_C[j])) && ( If_Candidate_On(FC_CN[UR_First][FC_2],r,FC_C[j])) ){ //if this 3 candidate cell contains the two core candidates, 1892 FC_R[FC_Ptr] = r; // store r and c 1893 FC_C[FC_Ptr] = FC_C[j]; 1894 FC_Ptr++;}}} // if this is a valid corner, increment 1895 }}} // end of row search with known column 1896 Rectangular = true; // assume its going to be a match 1897 if (FC_Ptr != 4){Rectangular = false;} // we should have four corners now 1898 BitMask = 0; // clear bit mask to count boxes involved 1899 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,In_Box(FC_R[k],FC_C[k])); } // turn on a bit for each box involved 1900 if (BitsCount(BitMask) != 2){Rectangular = false;} // count them, need two and only two 1901 BitMask = 0; // clear bit mask to count boxes involved 1902 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,FC_R[k]); } // turn on a bit for each row involved 1903 if (BitsCount(BitMask) != 2){Rectangular = false;} // count them, need two and only two 1904 BitMask = 0; // clear bit mask to count boxes involved 1905 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,FC_C[k]); } // turn on a bit for each column involved 1906 if (BitsCount(BitMask) != 2){Rectangular = false;} // count them, need two and only two 1907 if (Candidates[FC_R[UR_First+2]][FC_C[UR_First+2]] != Candidates[FC_R[UR_First+3]][FC_C[UR_First+3]]){Rectangular = false;} // if the three candidates in two new cells not equal, not a match 1908 if (Rectangular){ // if still 'true' then its a good-to-go Unique Rectangle Type 2 1909 BitMask = Candidates[FC_R[UR_First+2]][FC_C[UR_First+2]] ^ Candidates[FC_R[UR_First]][FC_C[UR_First]]; // use XOR to clear out shared candidates and leave the unique one 1910 UR_Candidate = BitsWhich(BitMask); // get candidate number from bit position 1911 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 1912 for (int cc = 0; cc < 9; cc++){ 1913 UR_Eligible = true; // assume its not a protected entry 1914 for (int j = 0; j < 4; j++){if (rr == FC_R[j] && cc == FC_C[j]){UR_Eligible = false;}} // don't examine the four cells in the unique rectangle 1915 if (!In_Same_Unit(rr,cc,FC_R[2],FC_C[2]) || !In_Same_Unit(rr,cc,FC_R[3],FC_C[3])){UR_Eligible = false;} // must be in same unit (i.e."seeable") by both new corners 1916 if (UR_Eligible){Candidate_Off(UR_Candidate,rr, cc, "Uniq Rect #2");}}} // if eligible, clear it 1917 } // end of found a rectangular UR type 2 1918 // Look for a Unique Rectangle Type 4 1919 if (Rectangular){ // Look only if a valid rectangle came out of Type 2 1920 UR_Mark_1 = false; // assume search for this candidate in rest of house fails 1921 UR_Mark_2 = false; // and this one too 1922 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 1923 for (int cc = 0; cc < 9; cc++){ 1924 UR_Eligible = true; // assume its not a protected entry 1925 for (int j = 0; j < 4; j++){if (rr == FC_R[j] && cc == FC_C[j]){UR_Eligible = false;}} // protect the four corners again 1926 if (In_Same_Unit(rr,cc,FC_R[2],FC_C[2]) && In_Same_Unit(rr,cc,FC_R[3],FC_C[3]) && UR_Eligible ){ // same house as both non-diagonal corners? 1927 if (If_Candidate_On(FC_CN[UR_First][FC_1],rr,cc)){UR_Mark_1 = true;} // have found another instance of candidate 1 in same house 1928 if (If_Candidate_On(FC_CN[UR_First][FC_2],rr,cc)){UR_Mark_2 = true;} // have found another instance of candidate 2 in same house 1929 } // end of "this cell is in same unit as both 3-candidate non-diagnonal corners and isn't one of our existing corner-cells" 1930 }} // end of checking all cells for candidates exist in other cells in house 1931 1932 if (!UR_Mark_1){Candidate_Off(FC_CN[UR_First][FC_2],FC_R[2],FC_C[2], "Uniq Rect #4");} // if first candidate not found elsewhere in house, clear 2nd candidate 1933 if (!UR_Mark_1){Candidate_Off(FC_CN[UR_First][FC_2],FC_R[3],FC_C[3], "Uniq Rect #4");} // from both non-diagonal cells with extra candidates 1934 if (!UR_Mark_2){Candidate_Off(FC_CN[UR_First][FC_1],FC_R[2],FC_C[2], "Uniq Rect #4");} // if first candidate not found elsewhere in house, clear 2nd candidate 1935 if (!UR_Mark_2){Candidate_Off(FC_CN[UR_First][FC_1],FC_R[3],FC_C[3], "Uniq Rect #4");} // from both non-diagonal cells with extra candidates 1936 } 1937 1938 } // end of rectangle and 2 corners 1939 1940 } // end of checking each 'first' corner 1941 1942 // UR Type 5 1943 1944 Cand_Load_2(); // reload all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2 1945 for (int i = 0; i< RDC_Length; i++){ // use each 2-candidate cell as anchor 1946 FC_Ptr = UR_First; // reset pointer 1947 FC_R[FC_Ptr] = RDC_Matrix[i][Row_Pointer]; // store r and c 1948 FC_C[FC_Ptr] = RDC_Matrix[i][Column_Pointer]; 1949 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; // store candidates 1950 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 1951 FC_UD[FC_Ptr][FC_1] = FC_UpandDown; // mark candidates 1952 1953 FC_Ptr++; 1954 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 1955 for (int cc = 0; cc < 9; cc++){ 1956 if ( (rr != FC_R[UR_First]) || (cc != FC_C[UR_First]) ){ 1957 if ( ((Candidates_Count[rr][cc] == 2) || (Candidates_Count[rr][cc] == 3)) && (If_Candidate_On(FC_CN[UR_First][FC_1],rr,cc)) && (If_Candidate_On(FC_CN[UR_First][FC_2],rr,cc)) ){ // if 2 or 3 candidates and 2 are same as anchor 1958 FC_R[FC_Ptr] = rr; // store r and c 1959 FC_C[FC_Ptr] = cc; 1960 FC_UD[FC_Ptr][FC_1] = 0; // clear the Up/Down flag, it will be used later 1961 FC_Ptr++;}}}} 1962 if (FC_Ptr >= 4) { 1963 for (int j=UR_First; j< FC_Ptr; j++){ // look at every entry that could be a sub-anchor to the rectangle 1964 for (int k= UR_First; k< FC_Ptr; k++){ // examine every cell 1965 if (k != j){ // don't check against yourself 1966 if (FC_R[k] == FC_R[j]) {FC_UD[j][FC_1] = FC_UD[j][FC_1] | FC_Up;} // if I am same row, UD with flag 1967 if (FC_C[k] == FC_C[j]) {FC_UD[j][FC_1] = FC_UD[j][FC_1] | FC_Down;}}}} // if I am same column, mark entry with 2nd flag 1968 UR5_CC = 0; // reset 'corner counter' 1969 UR5_CC3 = 0; // count how many of the cells have three candidates 1970 UR5_Row_Counter = 0; // and counters of row and columns involved 1971 UR5_Column_Counter = 0; 1972 for (int j=UR_First; j< FC_Ptr; j++){ 1973 if (FC_UD[j][FC_1] == FC_UpandDown){ 1974 UR5_CC++; // count corners 1975 if (Candidates_Count[FC_R[j]][FC_C[j]] == 3) {UR5_CC3++;} // and count corners with extra candidate (has to be 2 or 3) 1976 bitSet(UR5_Row_Counter,FC_R[j]); // and rows and 1977 bitSet(UR5_Column_Counter,FC_C[j]);}} // columns 1978 Rectangular = true; // assume we have a valid UR 1979 if (UR5_CC != 4 || BitsCount(UR5_Row_Counter)!=2 || BitsCount(UR5_Column_Counter)!=2 ){ Rectangular = false;} // if doesnt have four corners comprised of 2 Rows & 2 Columns, its not a UR 5 1980 if (UR5_CC3 <2 || UR5_CC3 >3){ Rectangular = false;} // must have two or three corners with the three candidates 1981 if (UR5_CC3 == 2){ // if its only two corners, ensure they are diagonal 1982 RR_CC_Ptr = 0; // diagonal corner counter 1983 for (int j=UR_First; j< UR5_CC; j++){if (Candidates_Count[FC_R[j]][FC_C[j]] == 3){ // for both corners that have 3 candidates store r,c 1984 RR_CC[RR_CC_Ptr][Row_Pointer] = FC_R[j]; 1985 RR_CC[RR_CC_Ptr][Column_Pointer] = FC_C[j]; 1986 RR_CC_Ptr++;}} 1987 if (RR_CC[0][Row_Pointer] == RR_CC[1][Row_Pointer] || RR_CC[0][Column_Pointer] == RR_CC[1][Column_Pointer]) { 1988 Rectangular = false;} 1989 } 1990 BitMask = 0; 1991 bitSet(BitMask, FC_CN[UR_First][FC_1]); // set bits for both anchor candidates 1992 bitSet(BitMask, FC_CN[UR_First][FC_2]); 1993 for (int j=UR_First; j< FC_Ptr; j++){if (FC_UD[j][FC_1] == FC_UpandDown) {BitMask = BitMask | Candidates[FC_R[j]][FC_C[j]];}} // Logical OR against all the candidates in all the corner entries 1994 if (BitsCount(BitMask) != 3){Rectangular = false;} // Can only have the two anchors plus one extra candidate in all the cells 1995 bitClear(BitMask, FC_CN[UR_First][FC_1]); // clear both anchor candidates 1996 bitClear(BitMask, FC_CN[UR_First][FC_2]); 1997 UR5_Candidate = BitsWhich(BitMask); 1998 if (Rectangular) { 1999 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 2000 for (int cc = 0; cc < 9; cc++){ 2001 UR_Eligible = true; // assume its going to be eligible 2002 for (int i = 0; i < FC_Ptr; i++){ 2003 if ( (rr==FC_R[i]) && (cc==FC_C[i]) ){UR_Eligible = false;} // false if its one our corners 2004 if ( Candidates_Count[FC_R[i]][FC_C[i]]==3) { 2005 if (FC_UD[i][FC_1] == FC_UpandDown) { 2006 if (!In_Same_Unit(rr,cc,FC_R[i],FC_C[i])) { 2007 UR_Eligible = false;}}} // false if its not in the same house as all diagonal (2 or 3) 2008 } // end of checking cell against all in the FC array 2009 if(UR_Eligible){ 2010 // Show_Candidates(); 2011 // Show_FC(FC_Ptr); 2012 // DDTv("candidate ",UR5_Candidate,NL); 2013 Candidate_Off(UR5_Candidate,rr,cc,"Uniq Rect #5");}}} // clear it if it passed all tests 2014 } // end of clearing the extra candidate from all cells that can see ALL the diagonal cells that have that candidate IF it was 'rectangular' 2015 } // end of at least four cells 2016 2017 } // end of checking all candidates as anchor for UR 5 2018 2019 // UR 3 -- 2020 2021 Cand_Load_2(); // reload all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2 2022 for (int i=0; i <RDC_Length-1; i++){ // check each entry in the array ************** -1????? 2023 FC_Ptr = UR_First; // reset pointer 2024 UR_R = FC_Flag; // and flag row and column pointers 2025 UR_C = FC_Flag; 2026 FC_R[FC_Ptr] = RDC_Matrix[i][Row_Pointer]; // store r and c 2027 FC_C[FC_Ptr] = RDC_Matrix[i][Column_Pointer]; 2028 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; // store candidates 2029 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 2030 FC_Ptr++; // increment pointer 2031 for (int j = i+1; j < RDC_Length; j++){ // check rest of array now 2032 if ( (FC_CN[UR_First][FC_1] == RDC_Matrix[j][RDC_CurrCan]/10) && (FC_CN[ UR_First][FC_2] == RDC_Matrix[j][RDC_CurrCan]%10) ){ //if same candidates, store as well 2033 FC_R[FC_Ptr] = RDC_Matrix[j][Row_Pointer]; // store r and c 2034 FC_C[FC_Ptr] = RDC_Matrix[j][Column_Pointer]; 2035 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[j][RDC_CurrCan]/10; // store candidates 2036 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[j][RDC_CurrCan]%10; // note: not used except for Show_FC and diagnostics 2037 Rectangular = false; // assume it doesn't meet rectangular criteria 2038 if ((FC_R[FC_Ptr] == FC_R[UR_First]) &&(FC_C[FC_Ptr] != FC_C[UR_First])){ // if rows match and columns don't 2039 UR_C = FC_C[FC_Ptr]; // Mark that we have a 'row match' by setting 'multiple columns' 2040 Rectangular = true; } // and we have a rectangle 2041 if ((FC_C[FC_Ptr] == FC_C[UR_First]) &&(FC_R[FC_Ptr] != FC_R[UR_First])){ // if Columns match and row don't 2042 UR_R = FC_R[FC_Ptr]; // Mark that we have a 'column match' by setting 'multiple rows' 2043 Rectangular = true; } // and we have a rectangle 2044 if(Rectangular) {FC_Ptr++;} // if this is a valid corner, increment 2045 } // end of same candidates 2046 } // end of checking through end of array 2047 2048 if (Rectangular){ // if we have two bivalues with either same row or column, 2049 2050 if (UR_R == FC_Flag) { // if we matched on row, flag will still be set 2051 for (int r=0;r<9;r++){ // check all rows but 2052 if (r != FC_R[UR_First]){ // ignore the one the first two anchors are in 2053 if ( (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[r][FC_C[UR_First]])) && (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[r][FC_C[UR_First+1]])) ) { 2054 FC_R[FC_Ptr] = r; // store r and c of third corner 2055 FC_C[FC_Ptr] = FC_C[UR_First]; 2056 FC_Ptr++; // increment pointer 2057 FC_R[FC_Ptr] = r; // store r and c of fourth corner 2058 FC_C[FC_Ptr] = FC_C[UR_First+1]; 2059 FC_Ptr++;}}} // increment pointer 2060 } // end of we have a row match between first two corners (e.g., A1 and A6) 2061 2062 if (UR_C == FC_Flag) { // if we matched on column, flag will also be set 2063 for (int c=0;c<9;c++){ // check all rows but 2064 if (c != FC_C[UR_First]){ // ignore the one the first two anchors are in 2065 if ( (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[FC_R[UR_First]][c])) && (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[FC_R[UR_First+1]][c])) ) { 2066 FC_R[FC_Ptr] = FC_R[UR_First]; // store r and c of third corner 2067 FC_C[FC_Ptr] = c; 2068 FC_Ptr++; // increment pointer 2069 FC_R[FC_Ptr] = FC_R[UR_First+1]; // store r and c of fourth corner 2070 FC_C[FC_Ptr] = c; 2071 FC_Ptr++; }}} 2072 } // end of we have a row match between first two corners (e.g., A1 and A6) 2073 2074 if (FC_Ptr !=4){Rectangular == false;} // should have four corners, and 2075 BitMask = Candidates[FC_R[UR_First+2]][FC_C[UR_First+2]] | Candidates[FC_R[UR_First+3]][FC_C[UR_First+3]]; // set bitmask to be combined candidates of two non-bivalue cells 2076 bitClear(BitMask, FC_CN[UR_First][FC_1]); // clear both anchor candidates 2077 bitClear(BitMask, FC_CN[UR_First][FC_2]); // from the mask 2078 if (BitsCount(BitMask) != 2) {Rectangular = false;} // should have two, non-bivalue candidates 2079 } // end of rectangular (we have two bivalue corner anchors) 2080 if (Rectangular && (FC_Ptr == 4)){ 2081 UR3_CC = 0; // reset locked candidate counter 2082 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 2083 for (int cc = 0; cc < 9; cc++){ 2084 UR_Eligible = true; // assume we cant find a two cell match for BitMask above 2085 for (int k=0; k <4;k++){if (rr == FC_R[k] && cc == FC_C[k]){ UR_Eligible = false;}} // don't check against yourself 2086 if (UR_Eligible) {for (int k=2; k <4;k++){ if(!In_Same_Unit(rr,cc,FC_R[k],FC_C[k])){UR_Eligible = false;}}} // Must be in same house as the two non-bivalue cells 2087 if (UR_Eligible) {if ((Candidates[rr][cc] ^ BitMask)!=0){UR_Eligible = false;}} // finally check that we have the two non-bivalue extra candidates 2088 if (UR_Eligible) { 2089 UR3_R = rr; // made it so set row and colunn of locked bivalue "pattern breaker" candidates cell 2090 UR3_C = cc; 2091 UR3_CC++;} // increment counter to ensure we find only one 2092 if (UR3_CC == 1){ 2093 UR3_PB1 = Find_Next_Candidate(UR3_R,UR3_C,1); // store the two pattern breaker candidates 2094 UR3_PB2 = Find_Next_Candidate(UR3_R,UR3_C,UR3_PB1+1); 2095 } else {UR_Eligible = false;} 2096 }} // end rr,cc 2097 if (UR3_CC == 1){ // check that we have only one pattern breaker bivalue cell 2098 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle for possible clearing 2099 for (int cc = 0; cc < 9; cc++){ 2100 UR_Eligible = true; // assume it is an eligible cell to check 2101 for (int k=0; k <4;k++){if (rr == FC_R[k] && cc == FC_C[k]){UR_Eligible = false;}} // don't check against any of the anchors or 2102 if ( (rr == UR3_R) && (cc == UR3_C) ) {UR_Eligible = false;} // don't check against the locked candidate cell either 2103 if ((!In_Same_Unit(rr,cc,UR3_R,UR3_C)) || (!In_Same_Unit(rr,cc,FC_R[2], FC_C[2])) || (!In_Same_Unit(rr,cc,FC_R[3], FC_C[3]))) {UR_Eligible = false;} // must 'see' bivalue breaker and the two cells with breaker candidates 2104 if (UR_Eligible){Candidate_Off(UR3_PB1,rr,cc,"Uniq Rect #3"); // if still eligible (same house, not a corner, not locked candidate cell, potentially clear the pattern breaker candidates 2105 Candidate_Off(UR3_PB2,rr,cc,"Uniq Rect #3");}}} 2106 } 2107 2108 } // end of UR3 Rectangular 2109 } // end of UR Type 3 check all possible anchors 2110 2111} // end of Unique Rectangle types 1,2,4 &5 2112 2113void Hidden_Rectangle(){ // UR type 4 but with hidden candidates in two or three cells 2114 byte AR; // anchor row 2115 byte AC; // column 2116 byte DR; byte DC; byte D2R; byte D2C; byte D3R; byte D3C; // the other three corners 2117 byte Search_Candidate; // candidate to search for in its row and column outside the rectangle coordinates 2118 bool Candidate_Found; // results of that search 2119 byte HR_Bivalue_Counter; // need two or three corners with more than two candidates so count them (<=2) 2120 2121 Cand_Load_2(); // reload all bi-value candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2 2122 FC_Load(); // convert to easier to manipulate array 2123 // Show_Candidates(); 2124 // Show_FC(RDC_Length); 2125 for (int i=0; i <RDC_Length-1; i++){ // check each entry in the array 2126 AR = FC_R[i]; // anchor row 2127 AC = FC_C[i]; // and column 2128 BitMask = Candidates[AR][AC]; // the bits that must be on (our two candidates) 2129 // DDTsp(NL); 2130 // DDTs("Base Anchor ",SL); 2131 // Show_RC(AR,AC,SL); 2132 // Show_PB(Candidates[AR][AC]); 2133 HR_Bivalue_Counter = 1; // we have one corner with only two candidates to start with 2134 for (int rr = 0; rr < 9; rr++){ // search rest of puzzle 2135 for (int cc=0; cc<9;cc++){ // each row and column looking for potential match 2136 DR = rr; // load potential diagonal row and column 2137 DC = cc; 2138 // DDTs("Trying ",SL); 2139 // Show_RC(DR,DC,SL); 2140 // Show_PB(Candidates[DR][DC]); 2141 // Show_RC(DR,DC,NL); 2142 if (BitsMatch(BitMask,Candidates[DR][DC]) && (AR != DR) && (AC != DC)) { // if we have a diagonal and bits match, 2143 if (Candidates_Count[DR][DC] == 2){ HR_Bivalue_Counter++;} // if the diagonal has only two candidates, increment counter again 2144 // DDTsp(NL); 2145 // DDTs("Diagonal ",SL); 2146 // Show_RC(DR,DC,SL); 2147 // Show_PB(Candidates[DR][DC]); 2148 D2R = AR; // Next corner is AR, DC 2149 D2C = DC; 2150 D3R = DR; // and final corner is DR,AC 2151 D3C = AC; // have all four corners now 2152 // DDTsp(NL); 2153 // DDTs("3rd corner ",SL); 2154 // Show_RC(D2R,D2C,SL); 2155 // Show_PB(Candidates[D2R][D2C]); 2156 // DDTsp(NL); 2157 // DDTs("4rd corner ",SL); 2158 // Show_RC(D3R,D3C,SL); 2159 // Show_PB(Candidates[D3R][D3C]); 2160 if (Candidates_Count[D2R][D2C] == 2){ HR_Bivalue_Counter++;} // check candidate count for the two new corners 2161 if (Candidates_Count[D3R][D3C] == 2){ HR_Bivalue_Counter++;} 2162 if ( (BitsMatch(BitMask,Candidates[D2R][D2C])) && (BitsMatch(BitMask,Candidates[D3R][D3C])) && (HR_Bivalue_Counter <= 2) ){ // if we have our 2 anchor candidates in all 4 corners, and at least two of them have more than 2 candidates 2163 Search_Candidate = FC_CN[i][FC_1]; // check the first candidate 2164 Candidate_Found = false; // assume it won't be found 2165 for (int r = 0; r<9; r++){ // check all rows, fix column 2166 if ( (bitRead(Candidates[r][DC],Search_Candidate)) && (!Same_Cell(r,DC,AR,AC)) && (!Same_Cell(r,DC,DR,DC))&& (!Same_Cell(r,DC,D2R,D2C))&& (!Same_Cell(r,DC,D3R,D3C)) ){Candidate_Found = true;}} 2167 for (int c = 0; c<9; c++){ // check all columns, fix row 2168 if ( (bitRead(Candidates[DR][c],Search_Candidate)) && (!Same_Cell(DR,c,AR,AC)) && (!Same_Cell(DR,c,DR,DC))&& (!Same_Cell(DR,c,D2R,D2C))&& (!Same_Cell(DR,c,D3R,D3C)) ){Candidate_Found = true;}} 2169 if (!Candidate_Found) {Candidate_Off(FC_CN[i][FC_2],DR,DC,"Hidden Rect");} // if search for candidate in its row and column OUTSIDE of the four rectangle corners, clear OTHER candidate 2170 2171 Search_Candidate = FC_CN[i][FC_2]; // check the second candidate 2172 Candidate_Found = false; // assume it won't be found 2173 for (int r = 0; r<9; r++){ // check all rows, fix column 2174 if ( (bitRead(Candidates[r][DC],Search_Candidate)) && (!Same_Cell(r,DC,AR,AC)) && (!Same_Cell(r,DC,DR,DC))&& (!Same_Cell(r,DC,D2R,D2C))&& (!Same_Cell(r,DC,D3R,D3C)) ){Candidate_Found = true;}} 2175 for (int c = 0; c<9; c++){ // check all columns, fix row 2176 if ( (bitRead(Candidates[DR][c],Search_Candidate)) && (!Same_Cell(DR,c,AR,AC)) && (!Same_Cell(DR,c,DR,DC))&& (!Same_Cell(DR,c,D2R,D2C))&& (!Same_Cell(DR,c,D3R,D3C)) ){Candidate_Found = true;}} 2177 if (!Candidate_Found) {Candidate_Off(FC_CN[i][FC_1],DR,DC,"Hidden Rect");} // if search for candidate in its row and column OUTSIDE of the four rectangle corners, clear OTHER candidate 2178 } // end of found a hidden rectangle and clear 2179 } // end of found a valid diagonal 2180 }} // end of search for valid diagonal 2181 } // end of stepping through each cell in bivalue array 2182 2183} // end of Hidden Rectangle 2184 2185void Medusa_3D(){ // implements Medusa 3D "coloring" algorithm 2186 byte r; 2187 byte c; 2188 byte n; 2189 2190 Medusa_Build(); // set up the data for Medusa 2191 2192 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2193 r = FC_R[i]; 2194 c = FC_C[i]; 2195 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2196 MD_Clear(); 2197 n = RDC_CanNumber(r,c,Candnum); // find out what it is 2198 // DDTs("New Head of Chain ",SL); 2199 // Show_NRC(n,r,c,NL); 2200 Medusa_Chain(i,n,MD_Mark_Green); // and chain 2201 MD_Eliminated_Candidate = false; // haven't found anything yet 2202 MD_Rule_1(); // and run through the rules 2203 MD_Rule_2(); // possibly run next rule 2204 MD_Rule_3(); // possibly run next rule 2205 MD_Rule_4(); // and maybe run fourth rule 2206 MD_Rule_5(); // and maybe run penultimate rule 2207 MD_Rule_6(); // and maybe run last rule 2208 2209 } } 2210} // end of Medusa 3D 2211 //Rule 1 - Same Color Twice in a Cell 2212void MD_Rule_1(){ // first elimination rule: a cell with two of same color means eliminate ALL candidates with that color (everywhere!) 2213 byte r; byte c; // interim r and c's to reduce indexing 2214 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2215 r = FC_R[i]; 2216 c = FC_C[i]; 2217 if (MD_Colored_Green(i) >1){ // found cell with 2 or more 2218 MD_Clear_All_Green("Medusa 3_D #1"); // can clear ALL with that color (green or yellow) 2219 MD_Eliminated_Candidate = true;} 2220 if (MD_Colored_Yellow(i) > 1){ 2221 MD_Clear_All_Yellow("Medusa 3_D #1"); 2222 MD_Eliminated_Candidate = true;} 2223 } 2224} // end of MD_Rule_1 2225 //Rule 2 - Same Color Twice in a Unit 2226void MD_Rule_2(){ // 2nd elimination rule, like #1, except look for two of same color within a unit (same r,c,b) 2227 byte r; byte c; // interim r and c's to reduce indexing 2228 byte n; // colored candidate to look for in unit 2229 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2230 r = FC_R[i]; 2231 c = FC_C[i]; 2232 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2233 n = RDC_CanNumber(r,c,Candnum); 2234 if( MD_Candidate_Green(n,i)){ // if we find cell with that candidate colored green 2235 for (int j = 0; j < RDC_Length; j++){ // check all other cells 2236 if ( (j != i) && (In_Same_Unit(r,c,FC_R[j],FC_C[j])) && (MD_Candidate_Green(n,j)) ){ 2237 MD_Clear_All_Green("Medusa 3_D #2"); 2238 MD_Eliminated_Candidate = true;} 2239 }} // end of clear the green 2240 if( MD_Candidate_Yellow(n,i)){ // if we find cell with that candidate colored green 2241 for (int j = 0; j < RDC_Length; j++){ // check all other cells 2242 if ( (j != i) && (In_Same_Unit(r,c,FC_R[j],FC_C[j])) && (MD_Candidate_Yellow(n,j)) ){ 2243 MD_Clear_All_Yellow("Medusa 3_D #2"); 2244 MD_Eliminated_Candidate = true;} 2245 }} // end of clear the yellow 2246 } // all candidates 2247 } // all cells with candidates 2248} // end of MD_Rule_2 2249 //Rule 3 - Two colors in a cell 2250void MD_Rule_3(){ // 3rd elimination rule: 2 colors in a cell, can eliminate any UNCOLORED in that cell 2251 byte r; byte c; byte n; // interim r and c's to reduce indexing 2252 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2253 r = FC_R[i]; 2254 c = FC_C[i]; 2255 if ( (MD_Colored_Green(i) > 0) && (MD_Colored_Yellow(i) > 0) ){ // if this is a cell with two colors 2256 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2257 n = RDC_CanNumber(r,c,Candnum); 2258 if ( !MD_Colored_Already(n,i) ) { // clear every candidate in this cell that isn't colored 2259 Candidate_Off(n,r,c,"Medusa 3_D #3"); 2260 MD_Eliminated_Candidate = true;} 2261 } // all candidates within a cell 2262 } // found green & yellow in a cell 2263 } // all cells 2264 } // end of MD_Rule_3 2265 // Rule 4 - Two colors 'elsewhere' - (Sees Two Different Colors) 2266void MD_Rule_4(){ // 4th elimination rule, eliminate any candidate in any cell that is uncolored AND sees same n in same unit with two different colors 2267 byte r; byte c; byte n; // interim r and c's to reduce indexing 2268 byte GCnt ; byte YCnt; // count the greens and yellows in same unit 2269 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2270 r = FC_R[i]; 2271 c = FC_C[i]; 2272 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2273 n = RDC_CanNumber(r,c,Candnum); 2274 if ( !(MD_Colored_Already(n,i)) ){ // if this is in an uncolored candidate, 2275 GCnt = 0; // reset counters 2276 YCnt = 0; 2277 for (int j = 0; j < RDC_Length; j++){ // sweep through every cell with candidates 2278 if ( (MD_Candidate_Green(n,j))&& (In_Same_Unit(r,c, FC_R[j],FC_C[j])) ){GCnt++;} else // count how many in same unit are green 2279 {if ( (MD_Candidate_Yellow(n,j))&& (In_Same_Unit(r,c, FC_R[j],FC_C[j])) ){YCnt++;}} // count how many in same unit are yellow 2280 } // end count all colored that can see r,c,n 2281 if ( (GCnt > 0) && (YCnt > 0) ) { // eliminate appropriate candidate 2282 Candidate_Off(n,r,c,"Medusa 3_D #4"); 2283 MD_Eliminated_Candidate = true;} 2284 } // end check if uncolored this r,c,n 2285 }} 2286} // end of MD_Rule 4 2287 //Rule 5 - Two colors Unit + Cell (Unit-Cell Elimination) 2288void MD_Rule_5(){ // If an uncolored candidate can see a colored candidate of the same value elsewhere (it shares a unit) 2289 // AND an OPPOSITE colored candidate in its own cell, the uncolored candidate can be removed. 2290 byte r; byte c; byte r1; byte c1; byte n; byte n2; byte n3; // current row and column, search row and column, uncolored and same unit colored & same cell uncolored candidates 2291 byte GCnt ; byte YCnt; // count the greens and yellows in same unit 2292 bool MD5_Eliminate; // flag for controlling elimination of candidates 2293 2294 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2295 r = FC_R[i]; 2296 c = FC_C[i]; 2297 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2298 n = RDC_CanNumber(r,c,Candnum); 2299 MD5_Eliminate = false; // assume we WON'T find the right combination 2300 if ( !(MD_Colored_Already(n,i)) ){ // if this is in an uncolored candidate, 2301 // if (r== 2 && c== 4 && n== 8){ 2302 // DDTs("uncolored ",SL); 2303 // Show_NRC(n,r,c, SL); 2304 // Show_Candidates_Color();} 2305 GCnt = 0; // reset counters 2306 YCnt = 0; 2307 for (int Candnum2 = 1; Candnum2 <= Candidates_Count[r][c];Candnum2++){ // see if there is a green or yellow color in this cell 2308 n2 = RDC_CanNumber(r,c,Candnum2); // fetch the other candidates in the cell (its OK we see n again, it isn't colored and wont bump counter) 2309 // if (r== 2 && c== 4 && n== 8){ 2310 // DDTv(" checking n2 ",n2,NL);} 2311 if (MD_Candidate_Green(n2,i)) {GCnt++;} else {if (MD_Candidate_Yellow(n2,i)){YCnt++;}}} 2312 /// if (r== 2 && c== 4 && n== 8){ 2313 // DDTv(" Gcnt= ",GCnt,SL); 2314 // DDTv(" YCnt= ",YCnt,NL);} 2315 if (GCnt > 0){ // There IS a green colored candidate in same cell 2316 MD5_Eliminate = false; // assume we WON'T find a yellow cell meeting criteria 2317 for (int j = 0; j< RDC_Length;j++){ // for all active cells with candidates, 2318 r1 = FC_R[j]; 2319 c1 = FC_C[j]; 2320 if ( (MD_Candidate_Yellow(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD5_Eliminate = true;} // if we find the original uncolored candidate, in same unit and colored, we can eliminate 2321 } // end of checking all 2nd cells 2322 2323 } else // if not a green, look for yellow 2324 { 2325 if (YCnt > 0) { 2326 MD5_Eliminate = false; // assume we WON'T find a yellow cell meeting criteria 2327 for (int j = 0; j< RDC_Length;j++){ // for all active cells with candidates, 2328 r1 = FC_R[j]; 2329 c1 = FC_C[j]; 2330 if ( (MD_Candidate_Green(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD5_Eliminate = true;} // if we find the original uncolored candidate, in same unit AND colored, we can eliminate 2331 } // end of checking all 2nd cells 2332 }} 2333 if (MD5_Eliminate){ 2334 Candidate_Off(n,r,c,"Medusa 3_D #5"); // all criteria met, so eliminate the original, uncolored candidate 2335 MD_Eliminated_Candidate = true;} 2336 } // end found an uncolored candidate 2337 } // end check each candidate in the cell 2338 } // end check each cell with candidates 2339 2340} // end of MD Rule 5 2341 //Rule 6 - Cell Emptied by Color ((Emptyinging a Cell) 2342void MD_Rule_6(){ //If a particular uncolored Cell can "see" Cells that each contain one of the candidates 2343 // of the uncolored Cell and if all these candidates have the same color, 2344 //then this color can not be the solution. So, if all n's in an uncolored cell can 'see' green n's in same unit, 2345 // then green can be eliminated and vice-versa if they all see yellow n's 2346 byte r; byte c; byte r1; byte c1; byte n; byte n2; 2347 byte nc; // number of candidates in this cell 2348 bool MD6_C[10]; // true if this candidate is present in cell 2349 bool MD6_G[10]; // true if there is a 'green' color same n, same unit 2350 bool MD6_Y[10]; // corresponding yellow color 2351 bool MD6_All_Green; bool MD6_All_Yellow; // used to test for all found green or yellow outside uncolored cell 2352 // Show_Candidates_Color(); 2353 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2354 r = FC_R[i]; 2355 c = FC_C[i]; 2356 if (MD_Total_Colored(i) == 0){ // if this cell has no colored candidates 2357// DDTv(" Cell index ",i,SL); 2358// Show_RC(r,c,SL); 2359// Show_PB(Candidates[r][c]); 2360// DDTsp(NL); 2361 for (int k=0; k <=9; k++){ 2362 MD6_C[k] = false; 2363 MD6_G[k] = false; 2364 MD6_Y[k] = false; } // clear all analytical bits 2365 for (int k=1; k <= Candidates_Count[r][c]; k++){ 2366 n = RDC_CanNumber(r,c,k); // get the candidate number 2367 MD6_C[n] = true;} // and flip bit on in candidate list 2368 // DDTs(" Candidates in check cell are ",SL); 2369 // for (int kk = 0; kk<=9; kk++){if ( MD6_C[kk] ){DDTs("1",SL);} else {DDTs("0",SL);}} 2370 /// DDTsp(NL); 2371 for (int n=1; n <=9; n++){ // step through 2372 if (MD6_C[n]){ // if that candidate is present, 2373 for (int j = 0; j< RDC_Length;j++){ // for all active cells with candidates, 2374 r1 = FC_R[j]; 2375 c1 = FC_C[j]; 2376 if ( (MD_Candidate_Green(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD6_G[n] = true;} // if we find the same candidate, in same unit AND colored green, mark it 2377 if ( (MD_Candidate_Yellow(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD6_Y[n] = true;} // if we find the same candidate, in same unit AND colored yellow, mark it 2378 }}} // end of checking all 2nd cells 2379 MD6_All_Green = true; // assume we will find what we want 2380 MD6_All_Yellow = true; 2381 for (int n= 1; n <=9; n++){ //step through whole list 2382 if (MD6_C[n]){ // if candidate n is on the list (its one of the candidates in this cell), 2383 if ( (!MD6_G[n]) && (!MD6_Y[n]) ){ // scenario 1, could not find any color for this candidate 2384 MD6_All_Green = false; 2385 MD6_All_Yellow = false;} 2386 if ( (!MD6_G[n]) || (MD6_Y[n]) ){MD6_All_Green = false;} //scenario 2, if ANY candidate in the cell doesn't see green, its not true that they all see green 2387 if ( (!MD6_Y[n]) || (MD6_G[n]) ){MD6_All_Yellow = false;}}} //and if ANY candidate in the cell doesn't see yellow, its not true that they all see yellow 2388 2389 if (MD6_All_Green){ // if every uncolored candidate in cell can 'see' the same candidate in green, clear all Green 2390 MD_Clear_All_Green("Medusa 3_D #6"); 2391 MD_Eliminated_Candidate = true;} 2392 if (MD6_All_Yellow){ 2393 MD_Clear_All_Yellow("Medusa 3_D #6"); 2394 MD_Eliminated_Candidate = true;} // Same for yellow 2395 2396 } // end 'if no color candidates this cell 2397 } // end 'all active cells' 2398} // end of MD_Rule 6 2399 2400void MD_Clear_All_Green(String slabel){ // clear all green candidates in all cells 2401 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2402 int r = FC_R[i]; 2403 int c = FC_C[i]; 2404 byte n; 2405 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2406 n = RDC_CanNumber(r,c,Candnum); // find out what it is 2407 if (bitRead(FC_UD[i][MD_Green],n) > 0){Candidate_Off(n,r,c,slabel);} 2408}} 2409} // end of MD_Clear_All_Green 2410 2411void MD_Clear_All_Yellow(String slabel){ // clear all green candidates in all cells 2412 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2413 int r = FC_R[i]; 2414 int c = FC_C[i]; 2415 byte n; 2416 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2417 n = RDC_CanNumber(r,c,Candnum); 2418 if (bitRead(FC_UD[i][MD_Yellow],n) > 0){Candidate_Off(n,r,c,slabel);} 2419}} 2420} // end of MD_Clear_All_Yellow 2421 2422void Medusa_Chain(int Src_index, int n, bool My_Color){ 2423 byte Next_n; 2424 byte Src_r; byte Src_c; byte Dest_r; byte Dest_c; 2425 2426 MD_Color(Src_index,n,My_Color); // Mark this cell/candidate combo the right color (Green or Yellow) 2427 Src_r = FC_R[Src_index]; // and fetch its row and column 2428 Src_c = FC_C[Src_index]; 2429 2430 for (int Dest_index = 0; Dest_index < RDC_Length; Dest_index++){ // for each non bi-value cell with candidates, 2431 Dest_r = FC_R[Dest_index]; // fetch possible 'next link' r and c, including source cell 2432 Dest_c = FC_C[Dest_index]; 2433 for (int Dest_Candnum = 1; Dest_Candnum <= Candidates_Count[Dest_r][Dest_c];Dest_Candnum++){ // for each candidate in that cell 2434 Next_n = RDC_CanNumber(Dest_r,Dest_c,Dest_Candnum); // find out what the candidate is 2435 if (!MD_Colored_Already(Next_n,Dest_index)){ // ignore cell/candidates we've used and colored already 2436 if (MD_Strong_Link(Dest_index,Src_index,n,Next_n)){ 2437 // for (int k = 0; k <= XC_Link_Counter;k++){DDTsp(SL);} 2438 // if (My_Color == MD_Mark_Green){DDTs("Green ",SL);} else {DDTs("Yellow ",SL);} 2439 // Show_NRC(n,Src_r,Src_c,SL); 2440 // DDTs(" links to ",SL); 2441 // Show_NRC(Next_n,Dest_r,Dest_c,NL); 2442 XC_Link_Counter++; 2443 Medusa_Chain(Dest_index,Next_n,!My_Color); // follow the link 2444 XC_Link_Counter--;} // end of non-bi-value 2445 } // end of it is a fresh cell/candidate 2446 }} // end of check each cell and candidate 2447} // end of Medusa Chain 2448 2449bool MD_Strong_Link(int Dest_index,int Src_index, int Current_n, int Next_n){ // validate as a strong link 2450 2451 byte counter = 0; // for strong links only current and successor should be in the shared unit 2452 byte r1 = FC_R[Src_index]; // load rows and columns to simplify and reduce calculations for indexing 2453 byte c1 = FC_C[Src_index]; // have row and column of 'link from' and 'link to' 2454 byte r2 = FC_R[Dest_index]; 2455 byte c2 = FC_C[Dest_index]; 2456 byte b1 = In_Box(r1,c1); 2457 byte b2 = In_Box(r2,c2); 2458 2459 if (Src_index == Dest_index){ // if we are in the same cell, and 2460 if ( (Candidates_Count[r1][c1] == 2) && (MD_Total_Colored(Src_index)==1) ) { // we are in a bi-value cell and the 2nd candidate is uncolored 2461 return true;}} 2462 2463 if (Current_n !=Next_n) {return false;} // if not bi-value, 'from' and 'to' candidate must match 2464 if (!In_Same_Unit(r1,c1,r2,c2)) {return false;} // and they have to be in same unit ("see each other") 2465 2466 if (r1 == r2){ // are they in same row? 2467 counter = 0; // yes, reset counter of occurences 2468 for (int cc = 0; cc <9; cc++){ 2469 if (If_Candidate_On(Next_n, r1, cc)){counter++;}} // count # times this candidate appears in row 2470 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 2471 2472 if (c1 == c2){ // are they in same column? 2473 counter = 0; // yes, reset counter of occurences 2474 for (int rr = 0; rr <9; rr++){ 2475 if (If_Candidate_On(Next_n, rr, c1)){counter++;}} // count # times this candidate appears in column 2476 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 2477 2478 if (b1 == b2){ // are they in same box? 2479 counter = 0; // yes, reset counter of occurences 2480 for (int rr = Box_Coords[b1][Row_Pointer]; rr <= (Box_Coords[b1][Row_Pointer])+2; rr++){ //walk the box 2481 for (int cc = Box_Coords[b1][Column_Pointer]; cc <= (Box_Coords[b1][Column_Pointer])+2; cc++) {if (If_Candidate_On(Next_n, rr, cc)) {counter++;}}} // count # times this candidate appears in the box 2482 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 2483 2484 return true; // passed all tests: everywhere link-from 'sees' link-to, there's only 2 of them 2485 2486 } // end of MD Strong Link 2487 2488void Medusa_Build(){ // set up Medusa data 2489 Build_RD(); // load RDC first with r,c, and a 'current candidate' pointer 2490 for (int i = 0; i < RDC_Length; i++){ // also load FC 'simpler' row and column access (mimics RDC_Matrix row and column pointers) 2491 FC_R[i] = RDC_Matrix[i][Row_Pointer]; // store r and c 2492 FC_C[i] = RDC_Matrix[i][Column_Pointer]; 2493 FC_UD[i][MD_Green]=0; // and reset the flags, here denoting 'colored green' and 'colored yellow' 2494 FC_UD[i][MD_Yellow]=0;} 2495} // end of Medusa_Build 2496 2497void MD_Clear(){ 2498 for (int k=0; k<RDC_Max_Size;k++){ // clear out all colors (and incidentally, all 'used') 2499 FC_UD[k][MD_Green]=0; // and reset the flags, here denoting 'colored green' and 'colored yellow' 2500 FC_UD[k][MD_Yellow]=0;} 2501 MD_Green_Candidates = 0; 2502 MD_Yellow_Candidates = 0; 2503 XC_Link_Counter = 0; // reset link counter 2504 } // end of MD Clear 2505 2506void MD_Color(int i,int n, bool My_Color){ // set "Green" or "Yellow" color for this index 2507 if (My_Color == MD_Mark_Green) {bitSet(FC_UD[i][MD_Green],n); // set bit and return 2508 MD_Green_Candidates++;} // increment counter 2509 else 2510 {bitSet(FC_UD[i][MD_Yellow],n); 2511 MD_Yellow_Candidates++;} 2512} // end of MD_Color 2513 2514bool MD_Colored_Already(int n,int i){ // check to see if colored already (i.e., "used") 2515 if ( (bitRead(FC_UD[i][MD_Green],n) > 0) || (bitRead(FC_UD[i][MD_Yellow],n) > 0) ) {return true;} else {return false;}; // by checking both 'color' bits for this index 2516} // end of MD_Colored_Already 2517 2518int MD_Total_Colored(int i){ // count the total colored candidates in this cell 2519 byte Total_Bits = 0; 2520 Total_Bits = BitsCount(FC_UD[i][MD_Green]) + BitsCount(FC_UD[i][MD_Yellow]); // sum of green + yellow 2521 return Total_Bits; 2522}// end of MD_Total_Colored 2523 2524int MD_Colored_Green(int i){ // count the total Green candidates in this cell 2525 byte Total_Bits = 0; 2526 Total_Bits = BitsCount(FC_UD[i][MD_Green]); // green 2527 return Total_Bits; 2528}// end of MD_Total_Colored 2529 2530int MD_Colored_Yellow(int i){ // count the total Yellow candidates in this cell 2531 byte Total_Bits = 0; 2532 Total_Bits = BitsCount(FC_UD[i][MD_Yellow]); // yellow 2533 return Total_Bits; 2534}// end of MD_Total_Colored 2535 2536bool MD_Candidate_Green(int n,int i){ // check if specific candidate is colored green 2537 if ( bitRead(FC_UD[i][MD_Green],n) > 0 ) {return true;} else {return false;}} 2538 2539bool MD_Candidate_Yellow(int n,int i){ // check if specific candidate is colored yellow 2540 if ( bitRead(FC_UD[i][MD_Yellow],n) > 0 ) {return true;} else {return false;}} 2541 2542int MD_RC_i(int r, int c){ // convert from r,c, back to i index 2543 for (int i = 0; i < RDC_Length; i++) {if ( (FC_R[i] == r) && (FC_C[i] == c) ){return i;}} // found it 2544 return -1; 2545} // end of MD_RC_i 2546 2547void Show_Candidates_Color(){ 2548 int j; 2549 for (int r = 0; r < 9; r++){ 2550 for (int c = 0; c< 9; c++){ 2551 j = MD_RC_i(r,c); 2552 DDTsp(SL); 2553 Show_RC(r,c,SL); 2554 Show_PB_Color(Candidates[r][c],j); 2555 DDTsp(SL);} 2556 DDTsp(NL); } 2557} // end of show candidates with color 2558 2559void Show_PB_Color(int n, int j){ 2560 String digits[10] = {"0","1","2","3","4","5","6","7","8","9"}; 2561 DDTs(" {",SL); 2562 for (int i=1; i<=9;i++){if (bitRead(n,i)) { 2563 DDTs(digits[i],SL); 2564 if (j >= 0){ 2565 if ( bitRead(FC_UD[j][MD_Green],i) > 0 ) {DDTs("g",SL);} 2566 if ( bitRead(FC_UD[j][MD_Yellow],i) > 0 ) {DDTs("y",SL);}} 2567 } 2568 } 2569 DDTs("} ",SL); 2570} // end of showPB (Candidate print) 2571 2572 2573void Remote_Pair(){ // chain algorithm for remote pair 2574 /* 2575 * A remote pair is an even number of cells, all of which contain the same two candidates, 2576 * that share units in a chain pattern. Both of these candidates can be eliminated from any cell that shares a unit with both end-points of this chain. 2577 */ 2578 2579 const byte RP_First = 0; 2580 bool RP_Eligible; // eligible, i.e., not protected 2581 Cand_Load_2(); // load all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2) 2582 FC_List_Clear(); // clear non-repeating list 2583 for (int i=0; i <RDC_Length; i++){ // check each entry in the array 2584 FC_Ptr = 0; // reset pointer 2585 RP_Chain(i,RDC_Matrix[i][Row_Pointer], RDC_Matrix[i][Column_Pointer],(RDC_Matrix[i][RDC_CurrCan]/10) ,(RDC_Matrix[i][RDC_CurrCan]%10),FC_Up); // start the chain 2586 if (FC_Ptr >=4){ // chain must be four or more cells 2587 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 2588 for (int cc = 0; cc < 9; cc++){ 2589 RP_Eligible = true; // assume its not a protected entry 2590 for (int j = RP_First; j < FC_Ptr; j++){if (rr == FC_R[j] && cc == FC_C[j]){RP_Eligible = false;}} 2591 if (RP_Eligible && RP_Match(rr,cc)){ Candidate_Off(FC_CN[RP_First][FC_1],rr,cc, "Remote Pair"); // 2592 Candidate_Off(FC_CN[RP_First][FC_2],rr,cc, "Remote Pair"); }}} // end of all rows and columns 2593 //Any cell outside the Remote Pair that sees two cells with different values cannot have one of the Remote Pair digits set. 2594 } // if chain is 4 or more end at least four cells 2595 } // end of checking all two candidate entries 2596} //end of Remote Pair 2597 2598bool RP_Match(int r, int c){ // check for in same house as two of the opposite polarity chain members 2599 // DDTv("RP_Length is ",RP_Length,SL); 2600 // DDTv("FC ptr is ",FC_Ptr,NL); 2601 int RP_Ptr = 0; // build a list of all chain members in same house as this entry 2602 int RP_Ups = 0; // number of positive markers 2603 int RP_Downs = 0; // and downs 2604 for (int i = 0; i < FC_Ptr; i++){ // check each entry in chain 2605 if (In_Same_Unit(r,c,FC_R[i],FC_C[i])){FC_List[RP_Ptr] = i; // if we are in the same house, store pointer 2606 if (FC_UD[i][FC_1] == FC_Up) {RP_Ups++;} else {RP_Downs++;} // count the number of ups and downs 2607 RP_Ptr++; }} // and increment pointer 2608 if (RP_Ptr >= 2 && RP_Ups >=1 && RP_Downs >=1){return true;} // seen by at least two members of chain and both up and down 2609 return false; 2610} // end of RP Match 2611 2612 2613void RP_Chain(int MyIndex,int MyRow, int MyColumn, int MyCandidate1, int MyCandidate2, int MyMark){ 2614 byte NM; // next mark (flips) 2615 byte NR; // next row 2616 byte NC; // next column 2617 byte NC1; // first candidate of pair 2618 byte NC2; // second 2619 FC_R[FC_Ptr] = MyRow; 2620 FC_C[FC_Ptr] = MyColumn; 2621 FC_CN[FC_Ptr][FC_1] = MyCandidate1; 2622 FC_CN[FC_Ptr][FC_2] = MyCandidate2; 2623 FC_UD[FC_Ptr][FC_1] = MyMark; // store 'polarity' mark 2624 FC_UD[FC_Ptr][FC_2] = FC_Flag; // mark it so we don't find it again 2625 FC_List[MyIndex] = true; // 2626 FC_Ptr++; // increment pointer 2627 for (int j = 0; j < RDC_Length; j++){ 2628 NR = RDC_Matrix[j][Row_Pointer]; 2629 NC = RDC_Matrix[j][Column_Pointer]; 2630 NC1 = RDC_Matrix[j][RDC_CurrCan]/10; 2631 NC2 = RDC_Matrix[j][RDC_CurrCan]%10; 2632 if ( (!FC_List[j]) && (NC1 == MyCandidate1) && (NC2 == MyCandidate2) && (In_Same_Unit(MyRow,MyColumn, NR,NC)) ){ 2633 if (MyMark == FC_Up) {NM = FC_Down;} else {NM = FC_Up;} // flip the mark 2634 RP_Chain(j,NR,NC,NC1,NC2,NM);}} // and follow the chain 2635 return; // return when can't find next link 2636} // end of RPC 2637 2638bool RD(){ // recursive descent solution 2639 RDC_Ptr = 0; // and pointer 2640 Build_RD(); // build the recursive descent list 2641 T_Msg3("Trying Recursive","Descent and ","Backtrack Search"); 2642 DDTv(F("Starting Recursive Descent/Backtrack Solution with "),RDC_Length,SL); 2643 DDTv(" empty cells and ",CCC,SL); 2644 DDTs(" candidates",NL); 2645 T_Scroll_Message(F("Starting Recursive Descent/Backtrack")); 2646 T_Scroll_Message(FDM("with ",RDC_Length," empty cells")); 2647 T_Scroll_Message(FDM("and ",CCC," candidates")); 2648 return RD_Fill(RDC_Ptr); // pretty deceptively simple, eh? Stepping off a cliff! 2649} // end of Recursive Descent 2650 2651bool RD_Fill(int ptr){ // Main Decend/Backtrack routine 2652int Cell_Entry; // will be the candidate to try 2653bool SuccessFit = false; // true indicates we have found an entry for the cell that fits 2654int r = RDC_Matrix[ptr][Row_Pointer]; // just to make the code easier to read 2655int c = RDC_Matrix[ptr][Column_Pointer]; 2656 2657if (ptr >= RDC_Length) {return true;} // done? (i.e., pointing past last empty cell in list) 2658 2659 while (true){ // only two ways out, [1] and [2] below 2660 SuccessFit = false; 2661 while (!SuccessFit){ 2662 RDC_Matrix[ptr][RDC_CurrCan]++; // increment counter 2663 if (RDC_Matrix[ptr][RDC_CurrCan] > Candidates_Count[r][c]){ // are we out of candidates (ctr greater than count)? 2664 RDC_Matrix[ptr][RDC_CurrCan] = 0; //[1]we are out of candidates, reset this level's count 2665 return false;} // and return failure 2666 Cell_Entry = RDC_CanNumber(r,c,RDC_Matrix[ptr][RDC_CurrCan]); // return the 'nth' candidate from candidates array 2667 RDC_Calls++; // how many numbers have I tried? 2668 SuccessFit = RD_Check(Cell_Entry,r, c); } // try the number using current puzzle grid (not stored data) 2669 Puzzle_Grid[r][c]= Cell_Entry; // plug in the number because we succeeded 2670 T_Number(r,c,Cell_Entry,Entry_Color); // update display and call next level 2671 if (RD_Fill(ptr+1)) {return true;} //[2] if previous levels cleared, all OK, return true and pop up 2672 Puzzle_Grid[r][c]= 0; // unplug the number and try again 2673 T_Number(r,c,Cell_Entry,Grid_Color); // also clear failed entry from display 2674 } // end of "While True" infinite loop 2675 2676} // end of RD Fill 2677 //Recursive Descent/Backtrack routines start here 2678void Build_RD(){ // build the list of unsolved candidates 2679 2680 RDC_Calls = 0; //reset call counter 2681 RDC_Length = 0; // initialize the length of the array 2682 for (int r = 0; r < 9; r++){ 2683 for (int c = 0; c < 9; c++){ 2684 if (Puzzle_Grid[r][c] == 0) { // for every non-filled cell, 2685 RDC_Matrix[RDC_Length][Row_Pointer] = r; // store row and column indices 2686 RDC_Matrix[RDC_Length][Column_Pointer] = c; 2687 RDC_Matrix[RDC_Length][RDC_CurrCan] = 0; // and initialize where we are against the candidates -- current candidate being accessed 2688 RDC_Length++; }}} // and increment index 2689} // end of Build the RD matrix 2690 2691int RDC_CanNumber(int r,int c, int bposition){ // convert bit-packed candidates to packed array 2692 int ctr = 0; 2693 for (int n = 1; n <=9; n++){ // walk through 2694 if (bitRead(Candidates[r][c],n)){ // if a bit is on, store into array 2695 Digit_Array[++ctr] = n;}} // in sequence 2696 return Digit_Array[bposition]; // return the 'nth' candidate that is 'on' 2697} // end of Candidate bit selected by order number 2698 2699bool RD_Check(int MN, int MR, int MC){ // direct check for legality of placement in puzzle grid 2700 for (int c = 0; c<9; c++){if (Puzzle_Grid[MR][c] == MN && (c != MC)){return false;}}; // check my row 2701 for (int r = 0; r<9; r++){if (Puzzle_Grid[r][MC] == MN && (r != MR)){return false;}}; // check my column 2702 for (int r = Box_Coords[In_Box(MR,MC)][Row_Pointer]; r <= (Box_Coords[In_Box(MR,MC)][Row_Pointer])+2; r++){ //walk the box 2703 for (int c = Box_Coords[In_Box(MR,MC)][Column_Pointer]; c <= (Box_Coords[In_Box(MR,MC)][Column_Pointer])+2; c++){ 2704 if (Puzzle_Grid[r][c] == MN && (c != MC)&& (r != MR)) {return false;}}} // check each cell 2705 return true; 2706} // end of RD Check 2707 2708// Utility bit and candidate manipulation and test routines 2709 2710bool If_Candidate_On(int n,int r,int c){ // returns 'true' if bit 'n' is on in the cell (indicating 'yes' candidate) 2711 if (bitRead(Candidates[r][c],n) > 0) {return true;} else {return false;} 2712} // End of If_Candidate_On 2713 2714void Candidate_On(int n,int r,int c){ // turns the bit on for candidate number n 2715 if (!If_Candidate_On( n, r, c)){ 2716 bitSet(Candidates[r][c], n); // set bit and 2717 Candidates_Count[r][c]++; // increment count for this candidate 2718 CCC++;}} // increment calculated total candidate count too 2719 2720void Candidate_Off(int n,int r,int c, String xx){ // turns the bit off for candidate number n (clears candidate n from cell) 2721 if (Candidates_Count[r][c] !=0){ 2722 if (If_Candidate_On(n,r,c)){ 2723 bitClear(Candidates[r][c], n); 2724 Candidates_Count[r][c]--; // decrement candidate counter for this cell 2725 CCC--; // decrement calculated count too 2726 DDTs(xx,SL); 2727 DDTs(" cleared candidate ",SL); 2728 Show_NRC(n,r,c,NL); 2729 if (Show_Clear_Place) {T_Scroll_Message(FDM_CC(n,r,c,xx));} // show cleared candidate message if on 2730 #ifdef Test_Mode3 // if defined in input grid then 2731 if (n == Solution_Grid[r][c]){ //if about to clear a candidate that IS the solution for this cell 2732 DDTv("Error clearing candidate",n,SL); // tests whether n placed in cell matches solution 2733 DDTs(" in ",SL); 2734 Show_RC(r,c,NL); 2735 T_Msg3("Mistake","Versus","Solution"); 2736 T_Scroll_Message("Mistake Against Solution"); 2737 T_Scroll_Message(FDM_FTC(n,r,c,"Error")); 2738 Display_Debug(); 2739 while(1){;}} 2740#endif 2741 }}} // end of Candidates Bit Off 2742 2743void Placement_Removes_Candidates(int n,int r,int c){ // remove appropriate candidate bit from unit of number just placed 2744 for (int cc = 0; cc <9; cc++){ // walk the row column by column, ignoring current cell (leave that cell alone) 2745 if (cc != c){if (If_Candidate_On(n,r,cc)){Candidate_Off(n,r,cc," Placement");}}} 2746 for (int rr = 0; rr<9; rr++){ 2747 if (rr !=r ){if (If_Candidate_On(n,rr,c)){Candidate_Off(n,rr,c," Placement");}}} 2748 for (int srr = Box_Coords[In_Box(r,c)][Row_Pointer]; srr <= (Box_Coords[In_Box(r,c)][Row_Pointer])+2; srr++){ 2749 for (int scc = Box_Coords[In_Box(r,c)][Column_Pointer]; scc <= (Box_Coords[In_Box(r,c)][Column_Pointer])+2; scc++){ 2750 if ((srr != r) && (scc !=c)){ if (If_Candidate_On(n,srr,scc)) {Candidate_Off(n,srr,scc," Placement");}} // no other cell in this box either 2751 }} // end of box 'walk' 2752} // end of Remove Candidates 2753 2754int BitsOff(int Input_Mask,int Off_Mask){ // turn bits off in input mask that occur in OffMask 2755 for (int n = 0; n <=9; n++){ 2756 if (bitRead(Off_Mask,n)>0){bitClear(Input_Mask,n);}} 2757 return Input_Mask; 2758} // end of BitOff 2759 2760bool BitsMatch(int a, int b){ // returns true if all bits in 'a' are in 'b' (though 'b' may have others too) 2761 bool MyResult = true; // assume they do match 2762 for (int n = 0; n <=9; n++){ // set false if they don't 2763 if ((bitRead(a,n)>0) && (bitRead(b,n)<=0)){MyResult = false;}} 2764 return MyResult; 2765} 2766 2767int BitsCount(int Input_Mask){ // count the number of bits (candidates) in mask 2768 int MyResult = 0; // initialize 2769 for (int n = 0; n <=9; n++){ // count 'em 2770 if (bitRead(Input_Mask,n)>0){MyResult++;}} 2771 return MyResult; 2772} // End of Bitscount 2773 2774int BitsWhich(int Input_Mask){ // return the n that is represented in mask 2775 for (int n = 0; n <=9; n++){if (bitRead(Input_Mask,n)>0){return n;}} // find the first one 2776 return 0; 2777} // End of Bitscount 2778 2779int Number_of_Candidates(int a, int b){ // count number of candidates in combined ('ored') inputs 2780 int counter = 0; 2781 int BitCheck = a | b; // 'or' the two inputs and get a bit mask 2782 for (int n = 1; n <=9; n++){ // check all bits and count them 2783 if (bitRead(BitCheck,n)) {counter++;}} 2784 return counter; 2785} // end of number of candidates 2786 2787int Find_Next_Candidate(int r,int c, int start_bit){ // convert from next SINGLE bit to next single number 2788 for (int n = start_bit; n <=9; n++){ // walk thru the numbers from start point 2789 if (If_Candidate_On(n, r, c)){ // if a bit is on, return it 2790 return n;}} // return 2791} // end of Candidates Find Number 2792 2793bool In_Same_Unit(int r1,int c1, int r2,int c2){ // are two cells in the same Unit or House? 2794 if ((r1 == r2) || (c1 == c2)) {return true;} // same row or column 2795 if (In_Box(r1,c1) == In_Box(r2,c2)) {return true;} // same if in same box 2796 return false; // else they are not 2797} // end of In_Same_Unit 2798 2799int In_Box(int r, int c){ // determine box from row and column 2800 switch (r){ // returns box number from 0 to 8 2801 case 0: case 1: case 2: if (c<3) {return 0;} else if (c < 6) {return 1;} else return 2; 2802 break; 2803 case 3: case 4: case 5: if (c<3) {return 3;} else if (c < 6) {return 4;} else return 5; 2804 break; 2805 case 6: case 7: case 8: if (c<3) {return 6;} else if (c < 6) {return 7;} else return 8; 2806 break; } 2807 } // end of In_Box 2808 2809bool Same_Cell(int r1,int c1,int r2,int c2){ // return to facilitate ignoring 'this' cell 2810 if ((r1 == r2) && (c1 == c2)) {return true;} else {return false;} 2811} // end of same cell 2812 2813// Specialized 'clearing' routines based on specific criteria 2814 2815void Clear_Puzzle(){ // remove puzzle contents 2816 for (int r = 0; r <9; r++){ // clear prior puzzle 2817 for (int c = 0; c<9; c++){ 2818 Puzzle_Grid[r][c] = 0;}}} 2819 2820void Clear_HPTQ(int Input_BitMask, String xx){ // clear UNMASKED bits (remove as candidates) from marked CanMark entries 2821 for (int i = 0; i < CC_Length; i++){ // check each entry 2822 if (CanMark[i]){ // if WAS found in a match, then 2823 for (int n = 1; n <=9; n++){ // go through 2824 if (bitRead(Input_BitMask,n)== 0){ // if candidate is NOT present in mask, clear it in the candidate 2825 Candidate_Off(n,CanCoord[i][Row_Pointer],CanCoord[i][Column_Pointer],xx);}}}} // this will leave only the quad bits on and clear all bits no longer candidates 2826 } // end of Clear HPTQ 2827 2828void Clear_Row_Outside_Box(int n, int r, int b, String xx){ // clear row r of candidate n but NOT in this box 2829 for (int c = 0; c<9; c++){ // for every column in the row 2830 if (b != In_Box(r,c)){Candidate_Off(n,r,c,xx);}} // but only OUTSIDE this box 2831} // end of candidates clear row outside box 2832 2833void Clear_Column_Outside_Box(int n, int c, int b, String xx){ // clear column c of candidate n but NOT in this box 2834 for (int r = 0; r<9; r++){ // for every row in the column 2835 if (b != In_Box(r,c)){Candidate_Off(n,r,c,xx);}} // but only OUTSIDE this box 2836} // end of candidates clear column but only outside my box 2837 2838void Clear_Box_Except_Row(int n,int r,int b,String xx){ // clear the box of candidate, protecting row r 2839 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 2840 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 2841 if (srr != r){Candidate_Off(n,srr,scc,xx);}}} 2842} // end of Clear Box Except for Row 2843 2844void Clear_Box_Except_Column(int n,int c,int b,String xx) { // clear the box of candidate, protecting column c 2845 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 2846 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 2847 if (scc != c){Candidate_Off(n,srr,scc,xx);}}} 2848} // end of Clear Box Except for Column 2849 2850void Clear_All_But_Marked(int rr,int cc,int BitMask_Result, String xxx){ // for this unit, clear candidates in bit mask BUT NOT CANDIDATES MARKED in CanCoord, CanMark 2851 bool Clear_Flag = true; // assume its OK to clear 2852 int Current_Bit = 0; 2853 for (int i = 0; i< 9; i++){ 2854 if (CanMark[i]){ if ((rr == CanCoord[i][Row_Pointer]) && (cc == CanCoord[i][Column_Pointer])){Clear_Flag = false;}}} 2855 if (Clear_Flag){ 2856 for (int n = 1; n <=9; n++){ // climb through the numbers 2857 Current_Bit = bitRead(BitMask_Result,n); // read the bit 2858 if (Current_Bit > 0){Candidate_Off(n,rr, cc, xxx); 2859 }}} // if on in the mask, clear it 2860} // end of Clear_All_But_Marked 2861 2862void ClearR_SWF(int n, String xxx){ // clear candidate from the three columns but NOT in the three rows 2863 byte counter = 0; 2864 bool Protect_R[9] ; // rows to NOT clear 2865 bool Clear_C[9]; // columns to clear EXCEPT for protected rows 2866 for (int i = 0; i < 9; i++) { 2867 Protect_R[i] = false; 2868 Clear_C[i] = false;} 2869 for (int i = 0; i < 9; i++){if (SWF[i][SWF_Member]) { 2870 Protect_R[i] = true; // its one of the rows 2871 for (int j = 0; j < SWF[i][SWF_Number_Found]; j++){ // and mark the columns 2872 Clear_C[SWF[i][SWF_Marks+j]] = true; }}} 2873 for (int r = 0; r < 9; r++){ 2874 for (int c = 0; c < 9; c++){ 2875 if (Clear_C[c] && !Protect_R[r]) { 2876 Candidate_Off(n,r,c, xxx); }}} // clear candidate this column if NOT one of the rows 2877}// end of SWF ClearR 2878 2879void ClearC_SWF(int n, String xxx){ // clear candidate from the three rows but NOT in the three columns 2880 byte counter = 0; 2881 bool Protect_C[9] ; // columns to NOT clear 2882 bool Clear_R[9]; // rows to clear EXCEPT for protected columns 2883 for (int i = 0; i < 9; i++) { 2884 Protect_C[i] = false; 2885 Clear_R[i] = false;} 2886 for (int i = 0; i < 9; i++){if (SWF[i][SWF_Member]) { 2887 Protect_C[i] = true; // its one of the rows 2888 for (int j = 0; j < SWF[i][SWF_Number_Found]; j++){ // and mark the columns 2889 Clear_R[SWF[i][SWF_Marks+j]] = true; }}} 2890 for (int r = 0; r < 9; r++){ 2891 for (int c = 0; c < 9; c++){ 2892 if (Clear_R[r] && !Protect_C[c]) { 2893 Candidate_Off(n,r,c, xxx); }}} // clear candidate this column if NOT one of the rows 2894}// end of SWF ClearC 2895 2896 2897// What follows are a lot of "Show" routines mostly used for Serial terminal display and debugging 2898 2899void Display_Debug(){ 2900 Show_Grid(); 2901 Show_Candidates(); 2902} // end of Display_Debug 2903 2904void Show_Grid(){ // Show the puzzle grid 2905 DDTsp(NL); 2906 DDTsp(SL); 2907 for (int c = 1; c <=9;c++){DDTv(" ",c,SL);} 2908 DDTsp(NL); 2909 DDTs(" ",SL); 2910 for (int c = 0; c <=9;c++){DDTs("____",SL);} 2911 DDTsp(NL); 2912 for (int r = 0; r < 9; r++){ 2913 DDTs(Row_Labels[r],SL); 2914 for (int c = 0; c <9;c++){ 2915 DDTv(" ",Puzzle_Grid[r][c],SL); } 2916 DDTsp(NL); } 2917} // end of show grid 2918 2919void Show_NRC(int n, int r, int c, bool CRR){ // show Row and Column label and # and either New Line or Same Line 2920 Show_RC(r,c,SL); 2921 if (CRR) {DDTv(" ",n,NL);} else {DDTv(" ",n,SL);} 2922} // end of Show NRC 2923 2924void Show_RC(int r, int c, bool CRR){ // show only row and column label with no number content 2925 DDTs(Row_Labels[r],SL); 2926 DDTn(c+1,SL); 2927 if (CRR) {DDTs("",NL);} else {DDTs("",SL);} 2928} // end of Show NRC // "Show" routines normally not used 2929 2930void Show_Candidates(){ 2931 for (int r = 0; r < 9; r++){ 2932 for (int c = 0; c< 9; c++){ 2933 Show_RC(r,c,SL); 2934 Show_PB(Candidates[r][c]); 2935 DDTsp(SL);} 2936 DDTsp(NL); } 2937} // end of show candidates 2938 2939void Show_PB(int n){ 2940 String digits[10] = {"0","1","2","3","4","5","6","7","8","9"}; 2941 DDTs(" {",SL); 2942 for (int i=1; i<=9;i++){if (bitRead(n,i)) {DDTs(digits[i],SL);}} 2943 DDTs("} ",SL); 2944} // end of showPB (Candidate print) 2945 2946 2947#ifdef Test_Show 2948 2949void Show_Single_Candidate(int r,int c){ 2950 DDTs(Row_Labels[r],SL); 2951 DDTv("",c+1,SL); 2952 DDTv( " Count ", Candidates_Count[r][c],SL); 2953 DDTs(" Candidate(s): ",SL); 2954 for (int n = 1; n<=9; n++){ 2955 if (If_Candidate_On(n,r,c))DDTv(" ",n,SL);} 2956 DDTsp(NL); 2957} // end of show single candidate 2958 2959void Show_Y(int i,int j, int k){ // debugging display for XY algorithm 2960 DDTv("Y Anchor Data - length ",RDC_Length,NL); // show X Y Search data based on 3 anchors 2961 DDTv("Pivot ",i,SL); 2962 DDTsp(SL); 2963 Show_RC(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],SL); 2964 DDTv(" ",RDC_Matrix[i][RDC_CurrCan]/10,SL); 2965 DDTv(" ",RDC_Matrix[i][RDC_CurrCan]%10,NL); 2966 DDTv("Wing 1 ",j,SL); 2967 DDTsp(SL); 2968 Show_RC(RDC_Matrix[j][Row_Pointer],RDC_Matrix[j][Column_Pointer],SL); 2969 DDTv(" ",RDC_Matrix[j][RDC_CurrCan]/10,SL); 2970 DDTv(" ",RDC_Matrix[j][RDC_CurrCan]%10,NL); 2971 DDTv("Wing 2 ",k,SL); 2972 DDTsp(SL); 2973 Show_RC(RDC_Matrix[k][Row_Pointer],RDC_Matrix[k][Column_Pointer],SL); 2974 DDTv(" ",RDC_Matrix[k][RDC_CurrCan]/10,SL); 2975 DDTv(" ",RDC_Matrix[k][RDC_CurrCan]%10,NL); 2976 DDTv(" Candidate to Clear is ",Y_Wing_Z,NL); 2977} // end of Show XY 2978 2979void Show_SWF(int n){ // debugging display for swordfish 2980 DDTv(" SWF data for candidate ",n,NL); 2981 for (int i = 0; i < 9; i++){ 2982 if (SWF[i][SWF_Number_Found] > 0){ 2983 DDTs("Row ",SL); 2984 DDTs(Row_Labels[i],SL); 2985 DDTv(" Member ",SWF[i][SWF_Member],SL); 2986 DDTv(" occurs [",SWF[i][SWF_Number_Found],SL); 2987 DDTs("] ",SL); 2988 for (int j = 0; j < SWF[i][SWF_Number_Found]; j++){DDTv(" ",SWF[i][SWF_Marks+j]+1,SL);} 2989 DDTsp(NL);}} 2990} // end of Show SWF 2991 2992void Show_Search_Data(int counter, int pairthruquad, int rcs, int bitmask, bool smark){ 2993 DDTv("Search counter ",counter,SL); 2994 if (bitmask >0) Show_PB(bitmask); 2995 DDTv(" PQT",pairthruquad,SL); 2996 if (rcs <9){DDTv(" In Row/Column/box",rcs,SL);} else {DDTsp(NL);} 2997 for (int j = 0; j < counter; j++){ 2998 DDTv("Index ",j,SL); 2999 DDTv(" # candidates ",CanCount[j],SL); 3000 Show_PB(CanCand[j]); 3001 if (smark){ DDTv(" Mark ",CanMark[j],SL); 3002 DDTsp(NL);} 3003 Show_RC(CanCoord[j][Row_Pointer],CanCoord[j][Column_Pointer],NL); } 3004} // end of Show Search Data 3005 3006void Show_RDC(){ // show recursive descent data for debugging 3007 DDTv("Length is ",RDC_Length,NL); 3008 DDTsp(NL); 3009 for (int i = 0; i < RDC_Length; i++){ 3010 DDTv("Number ",i,SL); 3011 DDTv(" # of candidates ",Candidates_Count[RDC_Matrix[i][Row_Pointer]][RDC_Matrix[i][Column_Pointer]],SL); 3012 Show_RC(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],SL); 3013 Show_PB(Candidates[RDC_Matrix[i][Row_Pointer]][RDC_Matrix[i][Column_Pointer]]); 3014 DDTsp(NL); 3015 } 3016} // end of Show_RD 3017 3018void Show_X(int MC){ // debug x chain 3019 DDTv("Candidate is ",MC,SL); 3020 DDTv(" and Length is ",FC_Ptr,NL); 3021 for (int i = 0; i< FC_Ptr; i++){ 3022 Show_RC(FC_R[i],FC_C[i],SL); 3023 DDTv(" #candidates ", FC_CN[i][FC_1],SL); 3024 DDTsp(SL); 3025 Show_PB(Candidates[FC_R[i]][FC_C[i]]); 3026 DDTsp(NL); 3027 } 3028} // end of Show X 3029 3030 void Show_FC(int My_Length){ 3031 for (int i = 0; i <My_Length; i++){ 3032 Show_RC(FC_R[i],FC_C[i],SL); 3033 DDTv(" ",FC_CN[i][FC_1],SL); 3034 DDTsp(SL); 3035 Show_FCUD(i,FC_1,SL); 3036 DDTs(" / ",SL); 3037 DDTv(" ",FC_CN[i][FC_2],SL); 3038 DDTsp(SL); 3039 Show_FCUD(i,FC_2,NL); 3040 } 3041 } // end of Show_FC 3042 3043 void Show_FCUD(int index, int LR, bool CRR ){ 3044 if (FC_UD[index][LR] == (FC_UpandDown)){ DDTs("Up & Down",SL);} 3045 else { 3046 if (FC_UD[index][LR] == FC_Up){ DDTs("Up",SL);} 3047 if (FC_UD[index][LR] == FC_Down){ DDTs("Down",SL);} 3048 if (FC_UD[index][LR] == 0) DDTs("None",SL);} 3049 if(CRR){DDTsp(NL);} else {DDTsp(SL);} 3050 } // end of Show Forcing chain Up and Down 3051 3052void Show_RC_Node(byte XC_Index){ 3053 int RC1 = FC_Used_List[XC_Index] /100; // upper half 3054 int RC2 = FC_Used_List[XC_Index] %100; 3055 if (RC1 > 0){Show_RC(RC1/10,RC1%10,SL);} 3056 if (RC2 > 0){Show_RC(RC2/10,RC2%10,SL);} 3057 DDTsp(NL); 3058}// end of Show_RC_Node 3059 3060 3061#endif // Test_Show 3062 3063void T_Number(int r, int c, int n, int MyColor){ 3064 int MyHOffset = 0; // horizontal (x) offset to the Box 3065 int MyVOffset = 0; // vertical (y) offset to the Box 3066 int MyBoxH_Offset = 0; // intra-box digit (1,2,3) horizontal offset 3067 int MyBoxV_Offset = 0; // intra-box digit (1,2,3) vertical offset 3068 3069 switch (r){ // on row given set which vertical line we are 'below' 3070 case 0: case 1: case 2: MyVOffset = Ver1; 3071 break; 3072 case 3: case 4: case 5: MyVOffset = Ver2; 3073 break; 3074 case 6: case 7: case 8: MyVOffset = Ver3; 3075 break; 3076 default: 3077 DDTs("Puzzle Grid T Number Impossible Inputs",NL); 3078 break; 3079 } // end of Row vertical set 3080 switch (c){ // on column, pick the box (left hand 'line') we are in 3081 case 0: case 1: case 2: MyHOffset = Hor1; 3082 break; 3083 case 3: case 4: case 5: MyHOffset = Hor2; 3084 break; 3085 case 6: case 7: case 8: MyHOffset = Hor3; 3086 break; 3087 default: 3088 DDTs("Puzzle Grid T Number Impossible Inputs",NL); 3089 while(1){;} 3090 break; 3091 } // end of Column horizontal set 3092 3093 MyBoxV_Offset = BoxOffsets[r % 3]; // starting with 'box line' pick depth (row within box 1,2,3) 3094 MyBoxH_Offset = BoxOffsets[c % 3]; // starting with the box's left line, offset for digit 3095 tft1.setTextSize(DigitTextSize); // set grid text size 3096 tft1.setTextColor(Grid_Color); // set to background color (not selected color) 3097 for (int i = 0; i <=9; i++){ // kludge to ensure prior digit erased 3098 tft1.setCursor(MyHOffset+MyBoxH_Offset,MyVOffset+MyBoxV_Offset); // by printing ALL digits in background color 3099 tft1.print(i); } 3100 tft1.print('?'); // as well as the ? cursor 3101 tft1.setTextColor(MyColor); // set to MY color selected in call 3102 tft1.setCursor(MyHOffset+MyBoxH_Offset,MyVOffset+MyBoxV_Offset); // reset to proper position one last time 3103 if (n > 0 && n<10){ // if its zero, leave the square blank 3104 tft1.print(n);} else { // and finally print the number 3105 if (n == 10){ 3106 tft1.print('?');}} 3107 3108} // end of T_Number 3109 3110void T_Display_Setup(){ // reset to initial puzzle 3111 tft1.fillScreen(Grid_Color); // fill top 3112 tft1.drawRect(0, 0, 240, 240, ILI9341_RED); // draw grid and box separators 3113 tft1.drawRect(1, 1, 238, 238, ILI9341_RED); 3114 tft1.drawRect(2, 2, 236, 236, ILI9341_RED); 3115 tft1.drawFastVLine(159, 0, 239, ILI9341_RED); 3116 tft1.drawFastVLine(79, 0, 239, ILI9341_RED); 3117 tft1.drawFastHLine(0, 79, 239, ILI9341_RED); 3118 tft1.drawFastHLine(0, 159, 239, ILI9341_RED); 3119 tft1.fillRect(0,241,241,320,Display1_Color); // fill bottom display area 3120} // end of T_Display_Setup 3121 3122void T_MsgSetup1(int DC1, int DC2){ // initial display 3123 String Time_Text = " "; 3124 String Date_Text = " "; 3125 int Phour = 0; 3126 DateTime now = rtc.now(); // Reload from RTC 3127 Date_Time_Num_Year = now.year(); 3128 Date_Time_Num_Month = now.month(); 3129 Date_Time_Num_Day = now.day(); 3130 Date_Time_Num_Hour = now.hour(); 3131 Date_Time_Num_Minute = now.minute(); 3132 Date_Time_Num_Second = now.second(); 3133 Date_Time_Num_DOW = now.dayOfTheWeek(); 3134 if (Old_Minute != Date_Time_Num_Minute){ 3135 tft1.fillScreen(ILI9341_RED); 3136 tft2.fillScreen(ILI9341_BLUE); // fill background color 3137 fc = rtc.getTemperature(); // currently not used 3138 fc = ((fc * (9.0/5.0)) + 32.0 )+ fc_adjust; // convert c to f F = C * 9/5 + 32 and adjustment 3139 ifc = fc; // convert to integer - note will always round down 3140 Old_Minute = Date_Time_Num_Minute; 3141 } 3142 3143 tft1.setTextSize(3); // set text size (was 5) 3144 tft1.setTextColor(DC1); //set to text color 3145 tft2.setTextSize(3); // set text size 3146 tft2.setTextColor(DC2); //set to text color 3147 tft2.setCursor(5,140); 3148 Phour = Date_Time_Num_Hour; 3149 if (Phour >= 12){Phour = Phour-12;} 3150 if (Phour == 0){Phour = 12;} 3151 Time_Text = Time_Text + Phour; 3152 Time_Text = Time_Text + ":"; 3153 if (Date_Time_Num_Minute < 10) Time_Text = Time_Text + "0"; 3154 Time_Text = Time_Text + Date_Time_Num_Minute; 3155 tft1.setTextSize(4); 3156 tft1.setCursor(45,40); 3157 tft1.println(F("Sudoku")); 3158 tft1.setTextSize(4); 3159 tft1.setCursor(45,82); 3160 tft1.println(F("Solver")); 3161 tft1.setTextSize(5); 3162 tft1.setCursor(20,140); 3163 tft1.println(Time_Text); 3164 tft1.setTextSize(2); 3165 tft1.setCursor(35,220); 3166 tft1.println(F("Hold Any Key")); 3167 tft1.setCursor(35,245); 3168 tft1.println(F("for Main Menu")); 3169 tft2.setCursor(15,80); 3170 tft2.println(daysOfTheWeek[Date_Time_Num_DOW]); 3171 tft2.setCursor(0,140); 3172 Date_Text = Date_Text + monthsOfTheYear[Date_Time_Num_Month-1]; 3173 Date_Text = Date_Text + " "; 3174 Date_Text = Date_Text + Date_Time_Num_Day; 3175 tft2.println(Date_Text); 3176 Date_Text = ifc; 3177 Date_Text = Date_Text + "F"; 3178 tft2.setCursor(65,200); 3179 tft2.println(Date_Text); 3180 3181 } // end of T_MsgSetup1 3182 3183void T_Msg1(String xxx){ 3184 tft1.fillRect(0,241,241,320,Display1_Color); // clear bottom display area 3185 tft1.setCursor(((Msg1TextMax-xxx.length())*Msg1PW), Hor4+Msg1Offset); 3186 tft1.setTextSize(Msg1TextSize); // set text size and color 3187 tft1.setTextColor(Msg1_Color); 3188 tft1.println(xxx);} 3189 3190void T_Msg2(String xxx1, String xxx2){ 3191 tft1.fillRect(0,241,241,320,Display1_Color); 3192 tft1.setCursor(((Msg2TextMax-xxx1.length())*Msg2PW), Hor4+Msg2Offset); 3193 tft1.setTextSize(Msg2TextSize); 3194 tft1.setTextColor(Msg1_Color); 3195 tft1.println(xxx1); 3196 tft1.setCursor(((Msg2TextMax-xxx2.length())*Msg2PW), Hor4+(3*Msg2Offset)); 3197 tft1.println(xxx2);} 3198 3199void T_Msg3(String xxx1, String xxx2, String xxx3){ 3200 tft1.fillRect(0,241,241,320,Display1_Color); 3201 tft1.setCursor(((Msg3TextMax-xxx1.length())*Msg3PW), Hor4+Msg3Offset); 3202 tft1.setTextSize(Msg3TextSize); 3203 tft1.setTextColor(Msg1_Color); 3204 tft1.println(xxx1); 3205 tft1.setCursor(((Msg3TextMax-xxx2.length())*Msg3PW), Hor4+(Msg3Offset+25)); 3206 tft1.println(xxx2); 3207 tft1.setCursor(((Msg3TextMax-xxx3.length())*Msg3PW), Hor4+(Msg3Offset+50)); 3208 tft1.println(xxx3);} 3209 3210void T_Scroll_Message (String SMString){ // scrolls progress on 2nd display 3211 tft2.setTextSize(T_ScrollTextSize); 3212 tft2.setTextColor(Msg2_Color); 3213 if ((T_Scrolling_Pointer == 0) && (!Scrolling_Text_Just_Cleared)){ 3214 delay(Scrolling_Text_Page_Delay); 3215 tft2.fillRect(0,0,240,320,Display2_Color);} 3216 tft2.setCursor(T_ScrollTextOffsetHor, (T_Scrolling_Pointer*11)+T_ScrollTextOffsetVert); 3217 tft2.print (SMString); 3218 Scrolling_Text_Just_Cleared = false; // clear flag 3219 delay(Scrolling_Text_Line_Delay); 3220 T_Scrolling_Pointer = (T_Scrolling_Pointer+1)%T_Scrolling_Limit; // increment pointer in queue 3221} // end of M_Scrolling Message 3222 3223void Clear_Scroll_Display(){ 3224 tft2.fillRect(0,0,240,320,Display2_Color); // clear by filling 3225 T_Scrolling_Pointer = 0; // reset to first line 3226 Scrolling_Text_Just_Cleared = true; // and set flag (so we don't get a beginning delay) 3227} // end of CSD 3228 3229String FDM(String S1, int SMValue, String S2){ // form a display message 3230 String DestString = " "; // create string to receive text and number 3231 DestString = S1+SMValue+S2; // string, value, string 3232 return DestString;} 3233 3234String FDM_NRC(int n,int r,int c){ // form row, column, number string 3235 String DestString = " "; // create string to receive text and number 3236 DestString = DestString+ Row_Labels[r]+(c+1)+ " "+n; 3237 return DestString; 3238} // end of FDM_NRC 3239 3240String FDM_RC(int r,int c){ // form row, column, 3241 String DestString = " "; // create string to receive text and number 3242 DestString = DestString+ Row_Labels[r]+(c+1); 3243 return DestString; 3244} // end of FDM_NRC 3245 3246String FDM_FTC(int n, int r, int c, String x){ // display for Fill The Cell 3247 String DestString = x + " is placing "; // initialize string 3248 DestString = DestString + n; 3249 DestString = DestString + " in"; 3250 DestString = DestString + FDM_RC(r, c); 3251 return DestString; 3252} // end of FDM fill the cell 3253 3254String FDM_CC(int n, int r, int c, String x){ // display for Clearing Candidate 3255 String DestString = x + " clears candidate "; // initialize string 3256 DestString = DestString + n; 3257 DestString = DestString + " in"; 3258 DestString = DestString + FDM_RC(r, c); 3259 return DestString; 3260} // end of FDM clearing candidate 3261 3262String FDM_DT(int Yi, int Mi, int Di, int Hi, int Mii){ // display for date & time 3263 String DestString = ""; // initialize string 3264 DestString = DestString + Mi; 3265 DestString = DestString + "/"; 3266 DestString = DestString + Di; 3267 DestString = DestString + "/"; 3268 DestString = DestString + Yi; 3269 DestString = DestString + " "; 3270 DestString = DestString + Hi; 3271 DestString = DestString + ":"; 3272 if (Mii <10) {DestString = DestString + "0";} 3273 DestString = DestString + Mii; 3274 if (Hi >= 12){DestString = DestString + "pm";} else {DestString = DestString + "am";} 3275 return DestString; // return mm/dd/yyyy hh:mm 3276} // end of FDM Build Date Time string 3277 3278String FDM_DT_YR(int Yi){ // display for year only 3279 String DestString = ""; // initialize string 3280 DestString = DestString + Yi; 3281 return DestString; // return mm/dd/yyyy hh:mm 3282} // end of FDM Year Only 3283 3284String FDM_DT_MD(int Mi, int Di){ // display for month and date 3285 String DestString = ""; // initialize string 3286 DestString = DestString + Mi; 3287 DestString = DestString + "/"; 3288 DestString = DestString + Di; 3289 return DestString; // return mm/dd/yyyy hh:mm 3290} // end of FDM Month and Day 3291 3292String FDM_DT_HR(int Hi, int Mii,int Si){ // display for Time 3293 String DestString = ""; // initialize string 3294 if (Hi == 0) {DestString = DestString + "12";} else {DestString = DestString + Hi;} 3295 DestString = DestString + ":"; 3296 if (Mii <10) {DestString = DestString + "0";} 3297 DestString = DestString + Mii; 3298 DestString = DestString + ":"; 3299 if (Si <10) {DestString = DestString + "0";} 3300 if (Hi <12) {DestString = DestString + " am";} else {DestString = DestString + " pm";} 3301 DestString = DestString + Si; 3302 return DestString; 3303 } // end of FDM show time only 3304 3305// Keyboard routines 3306 3307void K_Read(){ 3308 char key = K_Keypad.getKey(); 3309 if (key != NO_KEY){ 3310 K_Char = key;} 3311 } // end of Key Read 3312 3313void K_Get_Puzzle(){ // get the numbers for the puzzle 3314 ImageReturnCode stat; // Status from image-reading functions 3315 bool Getting_Input = true; 3316 bool Display_Cursor = false; 3317 bool Display_Cursor_Last; 3318 DC_Timer = millis(); // timer between flashes of cell vs cursor 3319 int r = 0; // initial row and column 3320 int c = 0; 3321 byte Cell_Entry = 0; 3322 3323 T_Number(r,c,10,Entry_Color); // display timer 3324 K_Set_Message(); // set progress display 3325 3326 #ifdef Test_Mode2 // if preloading a puzzle, 3327 Clear_Puzzle(); 3328 T_Display_Setup(); // clear and set up color display 3329 Transfer_Test(K_Demo_Current); // increment and wrap 3330 for (r = 0; r < 9; r++){ 3331 for (c = 0; c < 9; c++){T_Number(r,c,Puzzle_Grid[r][c],Constant_Color);}} //update display 3332 Examine_Puzzle(); // analyze the puzzle 3333 Puzzle_Check(); // check the puzzle 3334 r = 0; 3335 c = 0; 3336 #endif // Test_Mode2 3337 3338 T_Msg2("Puzzle Entry","Mode"); 3339 delay(1000); 3340 T_Msg2("Enter",FDM_RC(r,c)); 3341 3342 while (Getting_Input){ 3343 3344 K_Read(); // get a keyboard input 3345 3346 Display_Cursor_Last = Display_Cursor; // save current value 3347 if ((millis() - DC_Timer) >= K_DC_Interval){ // if time to switch number vs. cursor 3348 DC_Timer = millis(); // reset timer and 3349 Display_Cursor = !Display_Cursor;} // reverse it 3350 if (Display_Cursor_Last != Display_Cursor){ // if it switched, 3351 if (Display_Cursor){T_Number(r,c,10,Entry_Color);} // show cursor, or 3352 else {T_Number(r,c,Puzzle_Grid[r][c],Constant_Color);}} // show current entry 3353 3354 switch (K_Char){ // dispatch on input 3355 3356 case K_Backspace: 3357 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // * = backspace 3358 c--; // check for backspace and decrement column 3359 if (c < 0){ r--; // if its a negative column, decrement row and 3360 c = 8; // and put it into last column 3361 if (r < 0) {r=8;}} // if negative row, go back to the end (I9) 3362 T_Msg2("Enter",FDM_RC(r,c)); 3363 K_Char = 0; // clear input character 3364 break; 3365 3366 case K_Forward: 3367 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // # = restore cell and go forward 3368 c = c+ (3-(c%3)); // to next Box 3369 if (c == 9){r++; // if past last column 3370 c=0; // reset column 3371 if (r == 9) {r=0; // if off the end, then 3372 c=0;}} 3373 T_Msg2("Enter",FDM_RC(r,c)); 3374 K_Char = 0; 3375 break; 3376 3377 case K_Input: // A = clear input and start over 3378 K_Char = 0; 3379 Clear_Puzzle(); // clear any puzzle input 3380 T_Display_Setup(); // clear and create color display 3381 T_Msg2("Puzzle", "Cleared"); 3382 r = 0; 3383 c = 0; 3384 T_Msg2("Enter",FDM_RC(r,c)); 3385 break; 3386 3387 case K_Solve: // B = Done here, return to main menu 3388 T_Msg2("Checking", "Puzzle"); 3389 K_Char = 0; 3390 Examine_Puzzle(); 3391 if (Filled == 0){T_Scroll_Message(F("Returning to Main Menu")); 3392 T_Msg3("Puzzle is Empty","Returning","To Main"); 3393 Getting_Input = false; 3394 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // get rid of cursor 3395 delay(Scroll_Display_Delay); 3396 break;} 3397 if (Puzzle_Check()){T_Msg3("Puzzle Appears OK","Returning","To Main Menu"); 3398 if (!Show_SD_Image){ 3399 T_Scroll_Message(F("Puzzle Appears Good")); 3400 T_Scroll_Message(F("Returning to Main Menu")); 3401 T_Scroll_Message(F("Press B there to Solve"));} 3402 Getting_Input = false; 3403 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // get rid of cursor 3404 delay(Scroll_Display_Delay); } 3405 else {T_Msg3("Puzzle Error","Edit It","Or A to Reset/Wipe"); 3406 T_Scroll_Message(F("Puzzle Error - Edit It or A to Reset")); 3407 delay(Scroll_Error_Delay);} 3408 break; 3409 3410 case K_Check: // C = restore cell and go UP one line 3411 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // D = restore cell and down one line 3412 r--; // increment row and 3413 c=0; // reset column 3414 if (r < 0) {r = 8;} // if off the start, go back to end 3415 T_Msg2("Enter",FDM_RC(r,c)); 3416 K_Char = 0; 3417 break; 3418 3419 case K_Demo: // is it go down a row)? 3420 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // D = restore cell and down one line 3421 r++; // increment row and 3422 c=0; // reset column 3423 if (r == 9) {r = 0;} // if off the end, go back to beginning 3424 T_Msg2("Enter",FDM_RC(r,c)); 3425 K_Char = 0; 3426 break; 3427 3428 default: 3429 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3430 Cell_Entry = K_Char - K_Fnumber; 3431 K_Char = 0; //clear 3432 if (RD_Check(Cell_Entry,r,c)|| Cell_Entry == 0){ // zero cells and legal entries get a pass 3433 Puzzle_Grid[r][c] = Cell_Entry; // store it 3434 T_Number(r,c,Cell_Entry,Constant_Color); //update display 3435 if (Cell_Entry > 0) {T_Msg2(FDM("Entered ",Cell_Entry," in"),FDM_RC(r,c));} else 3436 {T_Msg2("Skipped",FDM_RC(r,c));} 3437 c++; // increment one cell 3438 if (c == 9){r++; // if past last column 3439 c=0; // reset column 3440 if (r == 9) {r=0; // if off the end, then 3441 c=0;}} 3442 } else { 3443 T_Msg3("Puzzle Entry","Error or Conflict", FDM_NRC(Cell_Entry,r,c)); 3444 Clear_Scroll_Display(); 3445 T_Scroll_Message(F("Puzzle Entry Error")); 3446 T_Scroll_Message(FDM("Can't Put ",Cell_Entry," there")); 3447 T_Scroll_Message(F("Please Edit or A to Start Over")); 3448 delay(Scroll_Error_Delay); 3449 K_Set_Message();} 3450 } 3451 break; 3452 } // end of switch on K_Char 3453 } // done with input 3454} // end get puzzle 3455 3456void K_Set_Message(){ 3457 Clear_Scroll_Display(); 3458 if (Show_SD_Image){reader.drawBMP("/rr5.bmp", tft2, 0, 0); } // use image if acccessible 3459 else 3460 { T_Scroll_Message(F("Ready for Puzzle Input")); // constant strings stored in PROGMEM 3461 T_Scroll_Message(F("A - Clear Puzzle and Restart Input")); // using F Macro 3462 T_Scroll_Message(F("B - Check Puzzle and Return")); 3463 T_Scroll_Message(F(" to Command Level")); 3464 T_Scroll_Message(F("C - Go Up One Row")); 3465 T_Scroll_Message(F("D - Go Down One Row")); 3466 T_Scroll_Message(F("* - Back Space Cursor")); 3467 T_Scroll_Message(F("# - Forward Cursor to Next Box")); 3468 T_Scroll_Message(F("1-9 - Cell Entry")); 3469 T_Scroll_Message(F("0 - Blank/Skipped Cell")); 3470 } // end of K Set Message 3471} 3472 3473void K_Configure(){ 3474 ImageReturnCode stat; // Status from image-reading functions 3475 T_Msg2("Setup ","Mode"); 3476 Clear_Scroll_Display(); 3477 if (Show_SD_Image){reader.drawBMP("/rr6.bmp", tft2, 0, 0);} // use image if acccessible 3478 else 3479 {T_Scroll_Message(F("Configuration Mode")); 3480 T_Scroll_Message(F("A - Toggle Use Algorithms")); 3481 T_Scroll_Message(F("B - Toggle Show Clears & Placement")); 3482 T_Scroll_Message(F("C - Exit Setup")); 3483 T_Scroll_Message(F("D - Toggle Single/Multiple Demos")); 3484 T_Scroll_Message(F("# - Set Date and Time"));} 3485 3486 bool Getting_Input = true; 3487 3488 while (Getting_Input){ 3489 3490 K_Read(); // get a keyboard input 3491 delay(10); 3492 3493 switch (K_Char){ // dispatch on input 3494 3495 case K_Input: // A = Toggle Use/Don't Use algorithms before descend/backtrack 3496 Use_Algorithms = !Use_Algorithms; // toggle 3497 if (Use_Algorithms){ 3498 T_Msg2("Algorithms","On"); 3499 } else 3500 {T_Msg2("Algorithms","Off"); 3501 } 3502 break; 3503 3504 case K_Solve: // B = Toggle showing progress screens 3505 Show_Clear_Place = !Show_Clear_Place; // toggle 3506 if (Show_Clear_Place){ 3507 T_Msg3("Progress","Display","On"); 3508 } else 3509 {T_Msg3("Progress","Display","Off"); 3510 } 3511 break; 3512 3513 case K_Check: // C = Exit Configuration 3514 T_Msg3("Exiting","From", "Setup"); 3515 K_Char = 0; 3516 Getting_Input = false; 3517 delay(Scroll_Display_Delay); 3518 break; 3519 3520 case K_Demo: 3521 Use_All_Demos = !Use_All_Demos; // toggle 3522 if (Use_All_Demos){ 3523 T_Msg3("Demo All",FDM(" ",Puzzles_Number," "),"Puzzles"); 3524 } else 3525 {T_Msg3("Demo Single","Puzzle","Only"); 3526 } 3527 break; 3528 3529 case K_Forward: // # enter date and time 3530 K_Char = 0; // clear the # that got us here 3531 T_Msg3("Enter","Year","YYYY"); 3532 Date_Time_Num = 0; 3533 for (int i = 1; i<=4; i++){ // form 6 digit number 3534 while(K_Char == 0){K_Read();} 3535 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3536 Date_Time_Num = (Date_Time_Num *10) + (K_Char - K_Fnumber); 3537 K_Char = 0; }} //clear 3538 Date_Time_Num_Year = Date_Time_Num; // remember year 3539 T_Msg3("Enter","Month & Day","MMDD"); 3540 K_Char = 0; // clear any input 3541 Date_Time_Num = 0; 3542 for (int i = 1; i<=4; i++){ // form 6 digit number 3543 while(K_Char == 0){K_Read();} 3544 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3545 Date_Time_Num = (Date_Time_Num *10) + (K_Char - K_Fnumber); 3546 K_Char = 0; }} //clear 3547 Date_Time_Num_Month = Date_Time_Num/100; // remember month, and 3548 Date_Time_Num_Day = Date_Time_Num%100; // day 3549 T_Msg3("Enter 24hr","Time","HHMM"); 3550 Date_Time_Num = 0; 3551 K_Char = 0; // clear any input 3552 for (int i = 1; i<=4; i++){ // form 6 digit number 3553 while(K_Char == 0){K_Read();} 3554 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3555 Date_Time_Num = (Date_Time_Num *10) + (K_Char - K_Fnumber); 3556 K_Char = 0; }} //clear 3557 Date_Time_Num_Hour = Date_Time_Num/100; // remember hour and 3558 Date_Time_Num_Minute = Date_Time_Num%100; // minute 3559 3560 Date_Time_Num_Valid = true; // assume its all valid 3561 if ( (Date_Time_Num_Year < 2021 || Date_Time_Num_Year > 2099) || (Date_Time_Num_Month == 0 || Date_Time_Num_Month > 12) || (Date_Time_Num_Day == 0 || Date_Time_Num_Day >31) || 3562 (Date_Time_Num_Hour >= 24) || (Date_Time_Num_Minute > 59) ) Date_Time_Num_Valid = false; 3563 if ( Date_Time_Num_Month == 2 && Date_Time_Num_Day > 29) {Date_Time_Num_Valid = false; } 3564 if ( (Date_Time_Num_Month == 4 || Date_Time_Num_Month == 6 || Date_Time_Num_Month == 9 || Date_Time_Num_Month == 11) && (Date_Time_Num_Day > 30) ){Date_Time_Num_Valid = false; } 3565 if (Date_Time_Num_Valid) { 3566 T_Msg3("Date & Time","Are Set To", FDM_DT(Date_Time_Num_Year,Date_Time_Num_Month,Date_Time_Num_Day,Date_Time_Num_Hour,Date_Time_Num_Minute)); 3567 rtc.adjust(DateTime(Date_Time_Num_Year,Date_Time_Num_Month,Date_Time_Num_Day,Date_Time_Num_Hour,Date_Time_Num_Minute, 0)); // set clock, zeroed out seconds 3568 } else 3569 {T_Msg2("Invalid","Entry");} 3570 K_Char = 0; // clear any input 3571 break; 3572 3573 case K_Backspace: 3574 default: 3575 break; 3576 3577 } // end of switch on K_Char 3578 K_Char =0; 3579 } // done with input 3580 return; 3581}// end of K_Configure 3582 3583void K_Return_To_Main(){ // message displayed when returning to top menu 3584 T_Msg3("Back to","Main Menu","Ready"); 3585 K_R_T_M_Scroll_Only(); 3586} // end of Return to Main 3587 3588void K_R_T_M_Scroll_Only() { 3589 ImageReturnCode stat; // Status from image-reading functions 3590 3591 if (Show_SD_Image){reader.drawBMP("/rr4.bmp", tft2, 0, 0);} // use image if acccessible 3592 else 3593 {Clear_Scroll_Display(); // otherwise use new progress display 3594 T_Scroll_Message(F("Ready for Command")); 3595 T_Scroll_Message(F("A - Load Puzzle")); 3596 T_Scroll_Message(F("B - Solve Puzzle You Loaded")); 3597 T_Scroll_Message(F("C - Configure Setup")); 3598 T_Scroll_Message(F("D - Demo Built in Puzzle")); 3599 T_Scroll_Message(F("* - Sleep Mode"));}} 3600 3601void Transfer_Test(int Current_Puzzle ){ //load appropriate puzzle from Sudoku.Puzzles.h 3602 strcpy_P(Puzzle_Buffer, (char *)pgm_read_word(&(Puzzle_Names_Table[Current_Puzzle]))); // Necessary casts and dereferencing, copy from PROGMEM. 3603 Puzzle_Name = String(Puzzle_Buffer); // and convert buffer to string 3604 Cell_Pointer = int(Puzzles_List[Current_Puzzle]); // get pointer to input puzzle grid 3605 for (int r = 0; r <9; r++){ // for all rows and columns 3606 for (int c = 0; c<9; c++){ // transfer the 81 bytes 3607 Cell_Entry = pgm_read_byte_near(Cell_Pointer); 3608 if (Cell_Entry > 19 || Cell_Entry < 1 ){ 3609 DDTs("Test/Demo Puzzle Bad Entry",NL); 3610 Show_NRC(Cell_Entry,r,c,NL); 3611 T_Scroll_Message(F("Test/Demo Puzzle Bad Entry")); 3612 T_Msg3("Demo/Test","Puzzle","Error"); 3613 Display_Debug(); 3614 while(1){;}} 3615 if (Cell_Entry < 10) {Puzzle_Grid[r][c] = Cell_Entry;} else {Puzzle_Grid[r][c] = 0;} // transfer all non-solution entries 3616#ifdef Test_Mode3 3617 if (Cell_Entry < 10){Solution_Grid[r][c] = Cell_Entry;} // solution gets all filled squares 3618 else {Solution_Grid[r][c] = Cell_Entry-10;} 3619#endif 3620 Cell_Pointer++;}} // plus cells coded +10 to indicate its solution only 3621 3622 } // end of transfer test 3623 3624void Run_Test() { // run demo puzzle/test puzzle 3625 Clear_Puzzle(); // puzzle 3626 Clear_Scroll_Display(); // new progress display 3627 T_Display_Setup(); // clear and set up color display 3628 Transfer_Test(K_Demo_Current); // increment and wrap 3629 T_Msg3("Fetching",FDM("Puzzle ",K_Demo_Current+1,""),Puzzle_Name); 3630 T_Scroll_Message("Running Demonstration Puzzle"); 3631 T_Scroll_Message(FDM(Puzzle_Name+" #",K_Demo_Current+1," Loaded")); 3632 DDTs(FDM(Puzzle_Name+" #",K_Demo_Current+1," Loaded"),NL); 3633 Examine_Puzzle(); // analyze the puzzle 3634 Solve_Puzzle(); // and solve it 3635 delay(Scroll_Display_Delay); 3636} // end of Run Test 3637 3638void Sleep_Demo(){ // startup / sleep demo 3639 3640 int Demo_Iteration = 0; // in case alt beginning - loops through positions 3641 int Robot_Iteration = 0; 3642 bool Screen_One = true; 3643 bool Need_Background = true; 3644 ImageReturnCode stat; // Status from image-reading functions 3645 3646 K_Char = 0; // flush any keyboard character 3647 do { // Keyboard will load a character 3648 K_Read(); // any input (any key) 3649 if (Show_SD_Image){ // if SD card loaded working 3650 switch (Robot_Iteration){ 3651 case 0: 3652 if (Screen_One) {reader.drawBMP("/rr1.bmp", tft1, 0, 0); } else {reader.drawBMP("/rr1.bmp", tft2, 0, 0);} 3653 break; 3654 case 1: 3655 if (Screen_One) {reader.drawBMP("/rr2.bmp", tft1, 0, 0);} else {reader.drawBMP("/rr2.bmp", tft2, 0, 0);} 3656 break; 3657 case 2: 3658 if (Screen_One) {reader.drawBMP("/rr3.bmp", tft1, 0, 0);} else {reader.drawBMP("/rr3.bmp", tft2, 0, 0);} 3659 break;} 3660 Robot_Iteration = (Robot_Iteration+1)%SD_Robots; 3661 Screen_One = !Screen_One; } 3662 else 3663 {if (Need_Background){ 3664 tft1.fillScreen(ILI9341_RED); 3665 tft2.fillScreen(ILI9341_BLUE); 3666 Need_Background = false;} // fill background color 3667 T_MsgSetup1(ILI9341_BLUE,ILI9341_RED); // when not loaded or working 3668 Demo_Iteration = (Demo_Iteration + 1)%4; // four rotations 3669 delay(250);} 3670 } while (K_Char == 0); // end of display loop 3671 tft1.setRotation(0); // restore any "odd" orientation 3672 tft2.setRotation(0); 3673 T_Display_Setup(); // clear and create color display 3674 K_Char = 0; // flush any keyboard character 3675}// end of sleep demo 3676
Sudoku_Puzzles.h
c_cpp
Demo puzzles
1 2//#define Test_Only // define to truncate test/demo list 3 4 5/* 6 * Puzzles are loaded into both the puzzle grid and the solution grid 7 * Entries of numbers 1-9 are loaded into initial puzzle grid and solution grid 8 * Entries of numbers 11-19 are loaded ONLY into the solution grid (used to check puzzle algorithms) 9 * Remember to true up Puzzles Number total to match 10 * Each puzzle is one-dimensional (0-80) and loaded into each row and column by Test_Transfer routine 11 * PROGMEM is used to conserve global variable space (non-program-storage space) 12 * Some basic information on solving techiques is included as a comment after the puzzle definitions 13 */ 14 15 16const byte Easy [81 ] PROGMEM = // easy - solvable ***** 17{ 3,12,17,19,4,11,8,15,16, 18 16,5,4,7,12,8,9,13,11, 19 1,8,19,15,16,3,7,14,2, 20 12,1,16,18,19,14,5,7,3, 21 17,14,5,11,13,16,2,18,19, 22 9,3,8,12,15,17,11,6,14, 23 4,19,1,6,18,15,13,2,7, 24 18,16,2,3,17,9,4,1,15, 25 15,17,3,14,1,12,16,19,8}; 26 27const byte HR_1 [81 ] PROGMEM = // test hidden rectangle and has a WXYZ wing clear too 28{ 3,9,4,6,8,2,1,5,7, 29 6,1,8,7,3,5,4,2,9, 30 5,2,7,4,19,11,3,6,8, 31 1,5,3,19,4,7,12,18,6, 32 4,7,12,5,6,8,19,3,1, 33 8,6,19,11,12,13,7,4,5, 34 7,3,6,8,11,4,15,19,2, 35 9,4,5,12,17,16,18,11,3, 36 2,8,1,13,15,19,16,17,4}; 37 38const byte HR_2 [81 ] PROGMEM = // test hidden rectangle also 39{ 16,9,11,4,5,13,2,18,17, 40 3,15,2,7,8,11,16,9,14, 41 7,14,18,2,9,16,15,1,13, 42 9,3,5,1,2,8,7,4,6, 43 18,7,14,6,13,9,11,12,15, 44 12,11,16,5,14,7,18,13,9, 45 1,2,9,3,7,5,4,6,8, 46 5,6,3,8,1,4,9,17,12, 47 4,8,7,9,6,2,3,5,1}; 48 49const byte WXYZ [81 ] PROGMEM = // WXYZ Wing test 50{ 7,1,19,18,4,13,15,12,16, 51 5,6,14,2,11,7,18,3,19, 52 8,2,13,16,15,9,7,14,11, 53 6,5,7,4,13,11,2,9,18, 54 2,14,8,19,6,15,3,11,7, 55 19,3,1,7,18,2,14,6,15, 56 14,19,6,3,7,18,11,15,12, 57 11,7,2,5,19,4,6,8,3, 58 3,18,5,11,2,6,19,7,4}; 59 60const byte Forbidding_1 [81 ] PROGMEM = // test forbidding chain with group node 61{ 12,6,18,15,1,3,7,14,9, 62 5,4,11,19,17,8,13,6,2, 63 13,7,9,6,2,14,8,15,11, 64 11,8,13,2,4,15,9,7,16, 65 17,19,4,11,13,16,5,2,8, 66 16,2,15,17,8,9,14,1,13, 67 18,13,17,14,16,2,11,9,5, 68 4,5,12,13,19,11,16,8,7, 69 19,11,6,8,5,17,12,3,14}; 70/* 71const byte Forbidding_2 [81 ] PROGMEM = // test forbidding chain with group node (not loaded now) 72{ 17,5,2,4,3,1,9,6,18, 73 18,3,6,17,19,12,14,1,5, 74 1,9,14,18,16,15,13,2,17, 75 2,11,17,13,14,19,15,18,6, 76 9,4,18,16,15,17,12,13,11, 77 5,6,13,11,12,18,7,4,9, 78 3,17,9,12,18,16,11,15,14, 79 6,12,5,19,1,14,18,17,13, 80 14,8,11,5,17,13,6,9,12}; 81*/ 82 83const byte XC1 [81 ] PROGMEM = // Tests of X Chain 84{ 2,7,11,18,6,13,5,4,19, 85 19,5,14,1,2,7,13,8,16, 86 13,16,18,4,15,19,2,7,11, 87 18,11,19,13,4,6,7,5,2, 88 16,2,7,5,19,8,4,1,13, 89 5,14,13,7,1,2,9,16,8, 90 1,3,6,2,7,4,8,9,5, 91 7,8,5,19,13,1,16,2,4, 92 14,19,2,16,18,15,1,13,7}; 93 94const byte XC2 [81 ] PROGMEM = // Tests of X Chain 95{ 3,17,4,5,2,11,19,8,16, 96 11,18,6,14,9,13,15,17,12, 97 19,5,12,18,7,16,3,11,14, 98 15,14,17,6,8,9,11,2,3, 99 12,19,11,7,3,4,16,15,18, 100 18,6,3,1,5,2,7,14,19, 101 14,1,15,9,6,18,12,13,17, 102 17,12,9,13,4,15,18,6,11, 103 6,13,8,2,1,7,14,19,5}; 104 105const byte XC3 [ 81] PROGMEM = // Tests of X Chain 106{ 12,14,16,3,5,1,7,8,19, 107 8,5,7,6,2,9,3,4,1, 108 1,19,13,8,7,4,16,15,2, 109 5,13,9,1,6,2,8,17,4, 110 6,8,1,15,4,17,2,19,13, 111 14,17,12,19,13,8,1,6,15, 112 7,1,8,14,19,13,15,2,16, 113 19,12,15,17,1,16,14,13,8, 114 13,6,14,12,8,15,19,1,7}; 115 116const byte UR_3a [81 ] PROGMEM = // check UR3 117{ 9,12,4,15,7,16,18,1,13, 118 1,3,7,12,18,9,6,14,15, 119 6,15,8,14,13,1,12,9,7, 120 5,1,9,17,12,13,4,6,8, 121 2,7,3,16,14,18,1,5,9, 122 4,8,6,9,1,5,7,3,2, 123 17,16,2,13,5,14,19,18,1, 124 13,19,1,8,16,12,15,17,14, 125 18,4,5,1,9,17,13,2,6}; 126 127const byte Medium_1[81 ]PROGMEM = // medium - solvable 128{ 19,1,16,2,13,5,17,14,8, 129 17,2,4,9,8,16,11,13,5, 130 13,5,8,7,14,1,19,2,16, 131 6,19,5,3,17,2,14,8,1, 132 2,18,7,1,16,14,3,15,9, 133 14,13,1,5,19,8,2,6,17, 134 11,14,2,6,15,7,8,19,13, 135 8,16,13,14,1,19,5,17,2, 136 15,17,9,18,2,3,16,1,14}; 137 138const byte Medium_2 [81 ]PROGMEM = // medium 2 139{ 13,19,15,16,4,7,12,1,18, 140 17,2,18,5,11,13,19,14,6, 141 14,16,11,9,12,18,17,13,5, 142 12,15,3,11,19,14,18,6,17, 143 19,17,4,8,16,12,11,15,13, 144 11,18,6,13,17,15,4,9,2, 145 16,4,12,17,3,19,15,18,11, 146 8,11,19,12,15,6,13,7,14, 147 5,13,17,4,18,1,16,12,19}; 148 149const byte Difficult_1 [81 ]PROGMEM = // difficult 150{ 18,17,13,5,4,1,16,19,12, 151 14,15,6,12,7,19,11,13,8, 152 12,9,11,16,13,8,15,17,14, 153 5,6,18,14,2,13,19,1,17, 154 17,11,2,19,15,16,4,18,13, 155 13,14,19,11,18,7,12,16,15, 156 11,8,14,3,16,5,17,12,9, 157 19,12,17,18,1,14,13,15,6, 158 16,3,5,17,19,12,18,14,11}; 159 160const byte UR [81 ]PROGMEM = // Invokes Unique Rectangle 1 161{ 16,18,15,1,13,12,14,7,19, 162 7,3,14,15,9,18,11,16,12, 163 2,11,19,17,6,4,15,3,18, 164 19,12,6,18,17,1,13,4,15, 165 18,15,1,3,14,19,17,2,6, 166 14,17,13,2,15,16,8,19,11, 167 15,6,8,4,12,17,9,11,13, 168 3,14,12,9,11,15,16,18,17, 169 1,19,17,16,18,13,12,15,14}; 170 171const byte UR2V [81 ]PROGMEM = // Invokes Unique Rectangle 2, 2nd one 172{ 11,7,9,13,14,12,15,18,6, // Vertical x.x 173 3,18,5,6,19,1,4,17,12, // ... 174 6,14,12,18,15,17,13,11,19, // Box x.x 175 5,6,4,17,13,8,19,2,11, 176 2,9,8,5,1,4,6,3,7, 177 7,1,3,2,16,19,8,5,4, 178 14,13,17,19,12,15,11,6,8, 179 8,5,11,4,17,16,2,19,3, 180 19,12,6,11,18,13,17,4,15}; 181 182 183const byte UR2H [81 ]PROGMEM = // Invokes Unique Rectangle 2 184{ 4,6,9,5,18,13,2,7,1, // Horizontal x.........x 185 1,8,5,7,6,2,4,3,9, // Box x.........x 186 7,2,3,9,11,14,8,5,6, 187 6,4,7,8,5,9,3,1,2, 188 3,1,8,4,2,7,6,9,5, 189 9,5,2,11,13,6,7,8,4, 190 12,17,4,13,9,11,5,6,18, 191 18,13,1,6,14,5,9,12,17, 192 5,9,6,12,7,18,1,14,13}; 193 194 195const byte UR5 [81 ] PROGMEM = // test case for UR type 5 196{ 13,9,11,8,14,16,2,5,17, 197 8,17,15,2,19,13,16,1,14, 198 14,6,12,7,11,15,13,8,19, 199 9,8,4,5,2,1,7,6,3, 200 7,2,6,4,3,8,5,9,1, 201 5,1,3,19,16,7,4,2,8, 202 12,3,19,16,8,4,1,7,5, 203 1,5,8,3,7,12,19,4,16, 204 16,14,17,1,5,19,8,3,12}; 205 206const byte Expert [81 ] PROGMEM = 207{ 12,19,14,11,16,15,18,13,17, 208 16,11,7,8,3,14,9,15,12, 209 13,18,5,19,17,2,6,4,11, 210 15,13,2,6,18,11,14,7,19, 211 17,4,11,12,15,19,13,8,16, 212 18,6,19,17,14,3,2,11,15, 213 19,2,8,4,11,17,5,16,13, 214 14,17,13,15,9,6,1,12,18, 215 11,15,16,13,12,18,17,19,14}; 216 217const byte FC [81 ]PROGMEM = // Requires a forcing chain 218{ 4,11,15,2,7,13,6,19,18, 219 7,9,8,1,5,6,2,3,4, 220 16,2,13,8,4,19,15,11,7, 221 2,3,7,4,6,8,9,5,1, 222 8,4,9,5,3,1,7,2,6, 223 5,6,1,7,9,2,8,4,3, 224 13,8,2,16,1,5,4,7,9, 225 11,7,16,19,2,4,3,18,15, 226 19,15,4,13,8,7,11,16,2}; 227 228const byte XWing [81 ] PROGMEM = // Test x-wing and xy-wing algorithms 229{ 1,18,17,14,12,13,5,6,9, 230 4,9,2,17,5,6,1,13,8, 231 13,5,6,1,18,9,2,4,17, 232 15,13,9,6,4,17,8,12,1, 233 17,6,4,12,1,18,19,15,13, 234 2,1,8,19,3,5,6,17,4, 235 18,4,13,5,19,12,17,1,6, 236 9,17,5,13,6,1,4,18,2, 237 6,2,1,18,17,14,13,19,5}; 238 239const byte Swordfish_1 [81 ] PROGMEM = // Test Swordfish algorithm 240{ 1,9,5,3,6,7,2,4,8, 241 12,7,8,11,5,14,3,6,9, 242 3,14,6,12,9,8,1,5,7, 243 16,12,3,7,8,11,5,9,14, 244 7,11,9,14,12,5,18,13,6, 245 5,8,4,9,13,6,7,1,12, 246 8,3,2,5,4,9,6,7,1, 247 9,16,7,18,1,3,14,2,5, 248 14,5,1,16,7,2,9,18,13}; 249 250const byte Jellyfish_1 [81 ] PROGMEM = // Test Jellyfish algorithm 251{ 14,6,17,3,11,9,12,8,15, 252 11,15,3,17,18,12,6,14,19, 253 2,19,18,14,6,15,17,13,1, 254 16,8,12,1,15,3,14,9,17, 255 19,11,15,18,7,14,13,12,16, 256 13,7,14,2,19,6,11,5,18, 257 7,12,11,19,3,18,15,16,4, 258 15,14,9,16,12,17,8,11,13, 259 18,3,16,5,14,1,19,7,12}; 260 261const byte Diabolical_1 [81 ] PROGMEM = // Uses many algorithms incl. two forcing chains 262{ 1,18,16,13,14,15,12,7,19, 263 19,3,2,18,7,11,14,16,15, 264 5,4,7,16,2,9,18,13,1, 265 17,5,1,19,13,14,16,12,18, 266 3,16,19,2,18,7,15,11,4, 267 14,12,18,15,11,16,3,9,17, 268 2,11,13,4,9,18,7,5,6, 269 16,17,14,11,5,13,9,8,12, 270 18,9,15,17,16,12,11,14,3}; 271 272const byte Diabolical_2 [81 ] PROGMEM = // test case for UR type 5, other goodies, including XYZ Wing 273{ 3,14,18,15,11,17,19,6,12, 274 15,6,11,4,12,19,8,7,13, 275 19,17,12,16,3,8,4,15,1, 276 12,8,16,17,4,11,13,9,15, 277 14,5,19,2,8,3,17,1,16, 278 17,1,13,19,5,16,12,4,18, 279 1,12,4,3,7,15,16,18,19, 280 18,9,7,11,16,2,15,3,14, 281 16,3,15,18,19,14,11,12,7}; 282 283 284const byte Diabolical_3 [81 ] PROGMEM = // Was recursive until chains got to it, does show XYZ Wing too! 285{ 18,17,11,5,16,12,14,3,9, 286 16,3,9,18,4,1,2,5,7, 287 14,12,5,17,3,9,6,8,11, 288 15,9,14,3,2,17,11,6,18, 289 12,11,8,9,5,6,7,4,13, 290 17,16,13,14,1,8,9,2,15, 291 19,4,6,1,18,5,3,17,12, 292 13,8,7,12,9,14,5,1,16, 293 11,5,12,16,17,13,18,19,14}; 294 295const byte Diabolical_4 [81 ] PROGMEM = // was used to fix UR 5 error 296{ 4,13,8,15,9,16,11,2,17, 297 15,7,6,12,14,11,8,13,19, 298 2,19,11,7,18,3,14,16,15, 299 17,15,9,1,16,12,13,18,4, 300 16,1,14,19,3,18,17,5,12, 301 3,18,12,14,17,5,6,19,11, 302 18,14,15,3,11,9,12,17,6, 303 11,12,3,16,15,17,9,4,18, 304 19,6,17,18,2,14,5,11,3}; 305 306 307const byte Diabolical_5 [81] PROGMEM = //Does have XYZ but still stumped and requires recursive search! 308{ 11,5,16,13,14,8,17,19,2, 309 18,19,17,6,12,11,13,15,14, 310 13,4,2,5,9,17,1,18,16, 311 14,11,13,8,17,16,19,12,5, 312 16,17,9,12,1,15,8,14,13, 313 2,18,15,19,13,4,16,11,17, 314 17,13,1,14,5,9,2,6,18, 315 15,12,18,11,16,3,14,17,19, 316 9,16,14,7,18,12,15,3,11}; 317 318 319const byte RP_1 [81 ] PROGMEM = // Tests Remote_Pair 320{ 1,7,8,6,14,9,13,5,12, 321 9,3,4,1,5,12,6,18,7, 322 2,5,6,7,18,3,14,1,19, 323 7,9,3,5,6,18,12,4,1, 324 6,4,1,12,3,7,5,9,18, 325 8,2,5,9,1,4,7,3,6, 326 5,6,7,3,19,1,18,12,14, 327 4,1,12,18,7,5,19,6,13, 328 3,8,19,4,12,6,1,7,5}; 329 330const byte RP_2 [81 ] PROGMEM = // Tests Remote_Pair 331{ 7,9,8,4,5,2,3,1,6, 332 6,14,3,7,8,1,15,9,2, 333 15,1,2,19,3,16,8,7,14, 334 3,7,11,2,6,5,19,4,8, 335 8,2,19,1,4,3,7,6,15, 336 14,6,15,8,9,7,11,2,3, 337 9,8,16,15,1,4,2,3,7, 338 1,13,7,16,2,8,14,5,19, 339 2,15,14,13,7,19,16,8,1}; 340 341const byte MD_1 [81 ] PROGMEM = // Medusa 3-D tests 1 342{ 17,9,3,8,2,4,5,6,11, 343 14,8,5,6,13,11,19,17,2, 344 2,11,6,19,7,5,14,13,8, 345 3,2,1,7,6,9,8,4,5, 346 19,16,14,2,5,8,3,11,17, 347 5,7,8,11,4,13,2,9,6, 348 8,5,19,14,1,6,7,2,3, 349 11,14,7,13,8,2,6,5,19, 350 16,13,2,5,19,7,1,8,14}; 351 352const byte MD_1_B [81 ] PROGMEM = // Medusa 3-D tests 1, Sudoku coach 353{ 3,12,5,6,4,9,7,11,8, 354 7,4,11,8,2,15,13,9,16, 355 8,16,9,11,13,7,12,4,15, 356 1,3,4,5,7,2,6,8,9, 357 16,15,17,9,8,4,11,12,3, 358 9,8,2,13,6,11,5,7,4, 359 2,11,16,4,9,13,8,15,7, 360 15,9,8,7,1,16,4,3,2, 361 4,17,13,2,15,8,9,16,1}; 362 363 364const byte MD_1_37 [81 ] PROGMEM = // Medusa 3-D tests 1 with 37 eliminations! 365{ 16,17,12,9,11,8,4,3,15, 366 19,15,4,7,13,2,6,8,11, 367 13,8,1,16,5,4,19,17,2, 368 17,14,5,18,16,3,1,2,9, 369 11,19,16,5,2,17,3,14,8, 370 12,13,18,14,9,11,5,6,17, 371 15,16,13,12,7,9,8,1,14, 372 18,1,7,13,14,5,12,19,6, 373 4,12,19,1,18,6,17,5,13}; 374 375const byte MD_2 [81 ] PROGMEM = // Medusa 3-D Tests 2 376{ 3,18,16,11,5,2,14,19,17, 377 2,5,17,3,14,19,16,1,18, 378 19,11,4,6,18,7,5,2,3, 379 16,9,3,2,17,11,8,14,5, 380 5,7,11,18,16,14,12,3,19, 381 4,12,8,19,3,5,17,6,11, 382 11,16,5,4,19,8,3,17,12, 383 17,3,12,5,11,6,19,8,4, 384 8,4,19,17,2,3,11,5,6}; 385 386const byte MD_2_B [81 ] PROGMEM = // Medusa 3-D Tests 2, Series B 387{ 6,4,13,7,19,15,8,12,11, 388 12,7,11,18,6,4,15,13,19, 389 18,15,9,1,12,3,4,7,6, 390 5,11,7,4,13,18,19,6,2, 391 3,6,18,12,11,19,7,15,14, 392 14,9,2,15,7,6,1,18,13, 393 7,13,14,6,18,1,2,9,15, 394 9,2,15,13,4,7,6,1,18, 395 11,18,6,9,15,2,13,14,7}; 396 397 398const byte MD_3 [81 ] PROGMEM = // Medusa 3-D Tests Rule 3 399{ 2,9,16,15,17,14,8,3,11, 400 14,11,18,16,2,13,9,7,15, 401 15,13,17,1,18,9,4,16,2, 402 8,4,5,7,6,1,2,9,3, 403 6,12,11,13,19,18,5,4,7, 404 13,17,9,12,4,5,16,11,8, 405 9,18,3,4,15,7,11,12,16, 406 11,6,14,18,3,12,7,15,9, 407 17,5,12,19,11,16,3,8,4}; 408 409const byte MD_3_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 3, Series B 410{ 9,17,1,18,3,12,16,5,4, 411 8,5,12,16,14,11,17,3,19, 412 4,13,16,5,17,19,8,11,12, 413 15,11,7,14,12,6,13,9,8, 414 2,9,8,3,1,5,14,16,7, 415 6,4,13,7,19,18,1,2,15, 416 7,12,4,19,16,13,15,8,11, 417 11,6,15,12,18,14,19,7,13, 418 3,8,9,11,5,7,2,14,6}; 419 420const byte MD_4 [81 ] PROGMEM = // Medusa 3-D Tests Rule 4 421{ 1,19,12,18,5,6,17,14,3, 422 15,4,3,12,9,17,11,18,16, 423 8,17,16,11,4,3,15,19,2, 424 17,3,14,5,6,18,2,1,19, 425 9,5,18,4,2,1,16,3,7, 426 16,2,1,17,3,19,18,15,14, 427 3,1,7,9,8,12,14,16,5, 428 12,16,15,3,1,14,9,7,18, 429 14,18,19,6,7,15,3,12,1}; 430 431const byte MD_4_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 4, Series B 432{ 2,19,5,18,1,16,14,17,13, 433 11,4,18,13,9,7,16,5,12, 434 16,3,17,14,2,5,19,8,11, 435 8,17,4,16,5,11,13,12,19, 436 9,1,13,2,8,4,17,6,5, 437 5,16,12,9,7,13,8,11,4, 438 4,5,6,1,3,18,12,9,17, 439 13,12,11,7,6,19,5,4,18, 440 17,18,19,5,4,12,1,13,6}; 441 442const byte MD_5 [81 ] PROGMEM = // Medusa 3-D Tests Rule 5 443{ 9,2,3,4,18,7,16,1,5, 444 8,7,6,11,5,13,9,2,4, 445 5,14,11,2,19,16,18,3,17, 446 7,6,9,13,2,15,1,4,18, 447 4,3,2,18,16,11,17,5,9, 448 1,8,5,19,17,4,2,6,13, 449 13,9,8,16,4,2,15,7,1, 450 2,11,7,15,3,19,4,8,6, 451 16,15,14,7,11,8,13,9,2}; 452 453const byte MD_5_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 5, Series B 454{ 16,5,3,11,19,7,18,4,12, 455 9,14,11,6,18,2,7,3,5, 456 12,18,7,3,5,14,16,11,19, 457 7,1,12,14,16,3,5,19,8, 458 14,6,5,19,12,18,11,7,13, 459 8,13,9,5,7,11,14,2,16, 460 11,9,8,7,3,16,2,5,14, 461 5,12,14,8,11,9,13,16,7, 462 13,7,16,2,14,5,9,8,11}; 463 464const byte MD_6 [81 ] PROGMEM = // Medusa 3-D Tests Rule 6 465{ 9,8,6,7,2,1,3,4,5, 466 3,11,4,9,5,6,18,12,7, 467 15,12,7,18,3,14,9,6,11, 468 18,7,3,12,6,5,14,11,9, 469 6,9,12,14,1,7,15,18,3, 470 1,14,15,3,9,18,2,7,6, 471 14,15,18,6,7,9,11,3,12, 472 12,6,9,1,4,3,7,15,18, 473 7,3,1,5,8,2,6,9,4}; 474 475const byte MD_6_B [81 ] PROGMEM = // Medusa 3-D Tests Rule 6, Series B 476{ 13,7,2,6,15,4,19,8,11, 477 16,11,15,13,8,9,4,7,12, 478 4,8,9,1,17,12,13,6,5, 479 15,19,1,8,14,13,6,12,7, 480 8,16,14,12,11,17,15,19,3, 481 2,13,7,19,16,15,8,11,14, 482 7,12,16,15,13,8,1,14,19, 483 11,14,3,7,9,16,12,15,8, 484 19,5,8,4,12,1,7,13,16}; 485 486const byte MD_3456 [81 ] PROGMEM = // Medusa 3-D Tests Rules 3,4,5,6 487{ 9,15,8,13,2,11,14,7,6, 488 6,12,14,19,15,7,1,18,13, 489 1,7,13,18,16,14,15,2,19, 490 17,18,5,4,13,16,12,9,1, 491 3,9,1,7,8,2,16,14,5, 492 4,6,12,11,19,5,8,3,17, 493 18,4,17,16,11,13,19,5,12, 494 5,13,6,12,14,19,17,1,18, 495 2,1,9,5,7,18,3,16,4}; 496 497const byte Workout [81 ] PROGMEM = // Uses many different solvers 498{ 11,18,16,14,12,5,17,19,3, 499 9,13,2,1,18,17,16,14,5, 500 7,4,15,16,13,19,11,8,12, 501 5,11,3,18,17,4,12,16,9, 502 16,9,18,13,15,12,14,17,1, 503 12,17,4,19,16,1,5,13,18, 504 8,16,11,5,14,13,9,2,17, 505 13,12,19,17,11,16,18,15,14, 506 14,15,17,2,9,18,3,11,16}; 507 508const byte Recursive_1 [ 81] PROGMEM = // Famous Arto Inkala puzzle 1 - one of the most difficult puzzles 509{ 1,16,12,18,15,7,14,9,13, 510 15,3,14,11,2,19,16,17,8, 511 17,18,9,6,14,13,5,12,11, 512 14,17,5,3,11,12,9,18,16, 513 19,1,13,15,8,16,17,14,2, 514 6,12,18,17,19,4,11,13,15, 515 3,15,16,14,17,18,12,1,19, 516 12,4,11,19,13,15,18,16,7, 517 18,19,7,12,16,11,3,15,14}; 518 519const byte Recursive_2 [81 ] PROGMEM = // 2nd Famous Arto Inkala puzzle - one of the most difficult puzzles 520{ 11,14,5,3,12,17,16,19,18, 521 8,13,19,16,15,14,11,2,17, 522 16,7,12,19,1,18,5,14,13, 523 4,19,16,11,18,5,3,17,12, 524 12,1,18,14,7,13,19,15,6, 525 17,15,3,2,19,16,14,8,11, 526 13,6,17,5,14,12,18,11,9, 527 19,18,4,17,16,11,12,3,15, 528 15,12,11,18,13,9,7,16,14}; 529 530// Matching pointers to each input grid and the name of the puzzle to display 531 532 533const char Puzzle_Names_1[] PROGMEM = "Easy"; 534const char Puzzle_Names_2[] PROGMEM = "Medium"; 535const char Puzzle_Names_3[] PROGMEM = "Difficult"; 536const char Puzzle_Names_4[] PROGMEM = "Expert"; 537const char Puzzle_Names_5[] PROGMEM = "Jellyfish"; 538const char Puzzle_Names_6[] PROGMEM = "Unique Rectangle 1"; 539const char Puzzle_Names_7[] PROGMEM = "Unique Rectangle 2"; 540const char Puzzle_Names_8[] PROGMEM = "Unique Rectangle 3"; 541const char Puzzle_Names_9[] PROGMEM = "Unique Rectangle 4"; 542const char Puzzle_Names_10[] PROGMEM = "Hidden Rectangle 1"; 543const char Puzzle_Names_11[] PROGMEM = "Hidden Rectangle 2"; 544const char Puzzle_Names_12[] PROGMEM = "Remote Pair 1"; 545const char Puzzle_Names_13[] PROGMEM = "Remote Pair 2"; 546const char Puzzle_Names_14[] PROGMEM = "Diabolical 1"; 547const char Puzzle_Names_15[] PROGMEM = "Diabolical 2"; 548const char Puzzle_Names_16[] PROGMEM = "Diabolical 3"; 549const char Puzzle_Names_17[] PROGMEM = "Medusa 1"; 550const char Puzzle_Names_18[] PROGMEM = "Medusa 2"; 551const char Puzzle_Names_19[] PROGMEM = "Medusa 3"; 552const char Puzzle_Names_20[] PROGMEM = "Medusa 4"; 553const char Puzzle_Names_21[] PROGMEM = "Medusa 5"; 554const char Puzzle_Names_22[] PROGMEM = "Medusa 6"; 555const char Puzzle_Names_22a[] PROGMEM = "Workout!"; 556const char Puzzle_Names_23[] PROGMEM = "Long Recursive 1"; 557const char Puzzle_Names_24[] PROGMEM = "Long Recursive 2"; 558 559 560#ifdef Test_Only // used in conjuntion with Test configurations in puzzle solver to auto run one puzzle on startup 561 562const byte* Puzzles_List [] = {Easy}; 563const char *const Puzzle_Names_Table[] PROGMEM = {Puzzle_Names_1}; 564#else 565const char *const Puzzle_Names_Table[] PROGMEM = {Puzzle_Names_1, Puzzle_Names_2, Puzzle_Names_3, Puzzle_Names_4, Puzzle_Names_5, 566 Puzzle_Names_6,Puzzle_Names_7, Puzzle_Names_8, Puzzle_Names_9, Puzzle_Names_10, 567 Puzzle_Names_11,Puzzle_Names_12,Puzzle_Names_13,Puzzle_Names_14,Puzzle_Names_15, 568 Puzzle_Names_16, Puzzle_Names_17, Puzzle_Names_18, Puzzle_Names_19,Puzzle_Names_20, 569 Puzzle_Names_21,Puzzle_Names_22,Puzzle_Names_22a,Puzzle_Names_23,Puzzle_Names_24 570 }; 571 572 573const byte* Puzzles_List [] = { Easy, 574 Medium_1, 575 Difficult_1, 576 Expert, 577 Jellyfish_1, 578 UR, 579 UR2H, 580 UR_3a, 581 UR5, 582 HR_1, 583 HR_2, 584 RP_1, 585 RP_2, 586 Diabolical_3, 587 Diabolical_4, 588 Diabolical_5, 589 MD_1 , 590 MD_1_B, 591 MD_2 , 592 MD_3 , 593 MD_5_B, 594 MD_6, 595 Workout, 596 Recursive_1, 597 Recursive_2}; 598#endif 599// end 600/* 601 * Solving Sudoku Puzzles 602Fill in all blank cells making sure that each row, column 603and 3 by 3 box contains the numbers 1 to 9. 604The Basics: 605Firstly, it's impossible to get very far without carefully 606maintaining a list of 'possible values' or candidates for each 607blank cell. Doing this by hand is laborious and prone to error, 608and often detracts from the fun of solving these puzzles. 609Fortunately, programs like this one will do this for you, 610while leaving you with the fun of applying logic to solve each 611puzzle. 612If you don't have a program to help - systematically analyse at 613each blank cell. Start with the assumption it can have any digit 614(or value) between 1 and 9, and then remove all values which 615have already been assigned to other cells in its respective row, 616column and 3x3 box. This leaves each blank cell with a list of 617candidates. 618Singles: 619Any cells which have only one candidate 620can safely be assigned that value. 621It is very important whenever a value is 622assigned to a cell, that this value is also 623excluded as a candidate from all other 624blank cells sharing the same row, 625column and box. (Programs like Simple 626Sudoku will do this laborious step 627automatically for you too.) 628Hidden Singles: 629Very frequently, there is only one 630candidate for a given row, column or box, 631but it is hidden among other candidates. 632In the example on the right, the 633candidate 6 is only found in the middle 634right cell of the 3x3 box. Since every box 635must have a 6, this cell must be that 6. 636Beyond the Basics: 637While the two steps above are the only ones which will directly 638assign a cell value, they will only solve the simplest puzzles. 639That's fortunate, otherwise Sudoku wouldn't be as popular as it 640is today. The following steps (in increasing complexity) will 641reduce the number of candidates in blank cells so, sooner or 642later, a 'single' candidate or 'hidden single' candidate will 643appear. 644 645Beyond the Basics 646 647Locked Candidates 1: (Pointing) 648Sometimes a candidate within a box is restricted to one row or 649column. Since one of these cells must contain that specific 650candidate, the candidate can safely be excluded from the 651remaining cells in that row or column outside of the box. 652In the example below, the right box only has candidate 2's in its 653bottom row. Since, one of those cells must be a 2, no cells in 654that row outside that box can be a 2. Therefore 2 can be 655excluded as a candidate from the highlighted cells. 656 657Locked Candidates 2: (Claiming) 658Sometimes a candidate within a row or 659column is restricted to one box. Since 660one of these cells must contain that 661specific candidate, the candidate can 662safely be excluded from the remaining 663cells in the box. 664 665Naked Pairs: 666If two cells in a group contain an identical pair of candidates and 667only those two candidates, then no other cells in that group could be those values. 668These 2 candidates can be excluded from other cells in the group. 669 670Advanced Steps: 671 672Naked Triples & Naked Quads: 673The same principle that applies to Naked 674Pairs applies to Naked Triples & Naked 675Quads. 676 677A Naked Triple occurs when three cells in 678a group contain no candidates other that 679the same three candidates. The cells 680which make up a Naked Triple don't have 681to contain every candidate of the triple. If 682these candidates are found in other cells 683in the group they can be excluded. 684 685A Naked Quad occurs when four cells in a 686group contain no candidates other that 687the same four candidates. 688 689Hidden Pairs: 690If two cells in a group contain a pair of 691candidates (hidden amongst other 692candidates) that are not found in any 693other cells in that group, then other 694candidates in those two cells can be 695excluded safely. 696 697Hidden Triples: 698If three candidates are restricted to three cells in a given group, 699then all other candidates in those three cells can be excluded. 700Hidden triples are generally extremely hard to spot but 701fortunately they're rarely required to solve a puzzle. 702Hidden Quads: 703If four candidates are restricted to four cells in a given group, 704then all other candidates in those four cells can be excluded. 705Hidden Quads are very rare, which is fortunate since they're 706almost impossible to spot even when you know they're there. 707For the Addict: 708The following techniques are no more difficult than those above 709but requires observation as to how specific candidates relate to 710each other (in particular patterns) beyond any given row, 711column or box. 712 713X-Wing: 714For every Sudoku, a value can exist only once in each row, 715column and box. If a value has only 2 possible locations in a 716given row (ie it has a candidate in only 2 cells in that row), then 717it must be assigned to one of these 2 cells. Given a particular 718puzzle that has two rows where a given candidate 'C' is 719restricted to exactly the same two columns (and no more than 2 720columns), and since: 7211) candidate C must be assigned once in each of these two 722rows, and 7232) no column can contain more than one of candidate C 724then candidate C must be assigned exactly once in each of 725these two columns within these two rows. Therefore, it's not 726possible for any other cells in these two columns to contain 727candidate C. This same logic applies when a puzzle that has two 728columns where candidate C is restricted to exactly the same two 729rows. 730 731Swordfish: 732The Swordfish pattern is a variation on the "X-Wing" pattern above. 733Formal definition: 734Given a particular puzzle that has three rows where a given 735candidate 'C' is restricted to the same three columns, and since 7361. candidate C must be assigned once in each of these three rows 7372. no column can contain more than one of candidate C 738then candidate C must be assigned exactly once in each of 739these three columns within these three rows. Therefore, it's not 740possible for any other cells in these three columns to contain 741candidate C. 742This same logic applies when a puzzle that has three columns 743where candidate C is restricted to exactly the same three rows. 744The X-Wing and Swordfish techniques can be further 745generalised to include rows containing candidates restricted to 746four cells in the same four columns (called a Jellyfish). 747 748XY-Wing (also called Y Wing): 749Not to be confused with X-Wing (see above). 750Formal definition: 751Given 3 cells where - 7521. all cells have exactly 2 candidates 7532. they share the same 3 candidates in the form - xy, yz, xz 7543. one cell (the Y 'stem' with canidates xy) shares a group 755with the other 2 cells (Y 'branches' with candidates xz & yz) 756then any other cell which shares a group with both 'branch' cells 757can have excluded the 'z' candidate that is common to these 'branch' cells. 758Proof: If a cell sharing a group with both branch cells is 759assigned the 'z' candidate, then neither branch can be assigned 760the 'z' candidate. Consequently, one branch would be the 'x' and 761the other the 'y' leaving the 'stem' without a candidate, an 762invalid state. 763Note: If all 3 cells in a xy-wing pattern shared the same unit/house, 764then they would be a 'naked triple'. 765 766XYZ-Wing: 767An XYZ-Wing is like an XY-Wing in that it is a group of three cells, one sharing a unit with the other two. 768E.g., base is {1,4,5} and second cell in same box is {1,4}, need to find 3rd cell {1,5} outside box but still seen by base 769However, the first cell in an XYZ-Wing has three candidates while the other two, called the wings, have two. 770Each of the wings must share one candidate with the first cell, (that's part of sharing a unit) but of different values. 771If the two second candidates in the wings are both the same, and are also the same as the extra candidate in the first cell, 772then if any fourth cell shares that candidate and a unit with all three, that candidate can be eliminated. 773It will be in the base's box and seen by base and both 'pincers'. 774 775WXYZ-Wing: 776A WXYZ-Wing is like an XY-Wing and an XYZ-Wing, except it is a group of four cells, one sharing a unit with the other three. 777The first cell in a WXYZ-Wing has three or four candidates while the other three, called the wings, have two each. 778Each of the wings must share one candidate with the first cell, (that's part of sharing a unit) but each wing must share a different value. 779If the second candidates in the wings are all the same, and are also the same as the left-over candidate of the first cell if the first cell 780has four candidates, then if any fifth cell shares that candidate and a unit with all the others, that candidate can be eliminated. 781(E.g.) Cells in one box, Base {5,8,9}, {1,9}, {1,5} and one cell outside box that 'sees' the base {1,8}. If any other cell in the box has '1' and sees the outside 782of the box cell too, eliminate the candidate 1 in that cell. 783 784Unique Rectangle Type 1: 785A "Unique Rectangle" (UR) consists of four cells that occupy exactly two rows, two columns, and two boxes. 786All four cells have the same two candidates left (in real sudokus not all cells have to hold all of the UR candidates). 787A UR Type 1 exists, if one of the four cells of a possible UR has additional candidates. 788If those candidates were eliminated, the UR would exist, causing two solutions. 789It is therefore absolutely necessary, that one of the additional candidates has to be true. 790That means, that the UR candidates can be eliminated from the cell with the additional candidates. 791 792Unique Rectangle Type 2: 793If in a possible UR two non diagonal cells have only one extra candidate, and that candidate is the same in both cells, 794all candidates seeing both extra candidates can be eliminated. In other words, you have two corners as in Type 1, and 795two more that have those same two candidates plus an third, matching in both. That candidate can be eliminated from any cell that is 796in the same house/unit as cell 3 and as cell 4 (the three-candidate corners) even those those cells may not be in same house as each other 797 798Unique Rectangle Type 3: 799This is a combination of UR with Naked/Locked Subsets. We look for two non diagonal cells in a possible UR that have extra candidates. 800Since one of those candidates has to be set to avoid the UR, we can treat both cells as one virtual cell containing only 801the extra candidates and try to build a Naked Subset (all additional cells have to see both UR cells with extra candidates). 802If such a UR/Naked Subset is found, all subset candidates can be eliminated from all cells outside the subset (but in the same house). 803So, one extra candidate seen in non-diagnal cells and one that is seen by both. 804 805Unique Rectangle Type 4: 806We look for additional candidates in two non diagonal cells again, but this time 807we ignore those extra candidates and concentrate on the UR candidates themselves: 808If one of the UR candidates is not possible anymore in any other cell of a house 809that contains both cells with the extra candidates, the other UR candidate can be eliminated from those UR cells. 810So search the house(s) that contain both non-diagonal cells (from the Type 2) and can't do the elimination if UR candidate cell is found 811 812Unique Rectangle Type 5: 813UR Type 5 is a variation of UR Type 2, but now the additional candidate can be in diagonal cells as well. 814Suppose we have a possible UR with one extra candidate in either two diagonal cells or in three cells. 815All occurences of that candidate can be eliminated in all cells that see all UR cells containing the additional candidate. 816The logic behind that move is the same as in a UR Type 2. In other words, in the corners we have one 2-candidate bivalue and at least two others, 817and maybe three, have three candidates with the 3rd matching in all. That candidate can be removed from any cell 'seen' by all three non-bivalue cells. 818 819Hidden Rectangle: 820Hidden Rectangles are very versatile, because they can be used in possible URs that contain up to 821three cells with arbitrary additional candidates (the UR is hidden under a clutter of additional 822candidates - not to be confused with Almost Unique Rectangles). 823We need a possible UR with two or three cells containing additional candidates (with only one cell the 824UR Type 1 should be used). Now take one UR cell without additional candidates and check the row and 825the column containing the opposite corner of the UR. If one UR candidate is nowhere outside the UR in 826those two houses, the other UR candidate can be eliminated from the opposite corner. 827 828Links and Inferences for chains 829The basis of all chains are two types of basic implications, normally called "links" or "inferences" 830 831Weak Links 832If two entities are weakly linked, they cannot be true at the same time. That means: If one of them is true, 833then the other has to be false (but both can be false as well, so if one is false, nothing can be concluded). 834In simple chains the entities are normally simply candidates, that share a house or a cell. 835 836Strong Links 837If two entities are strongly linked, they cannot be false at the same time. 838That means: If one of them is false, then the other has to be true (both true is only possible in very advanced types of links). 839If we use candidates, we would need exactly two candidates sharing a cell, or exactly two instances of the same candidate sharing a house (a conjugate pair in coloring terms). 840 841Those two link types are the basis for every type of chains, so the difference between them has to be understood completely. To sum it up: 842 843Weak Link: If a is true then b is false 844Strong Link: If a is false then b is true 845 846XY-Chain: 847This method can be used when a great number of cells contain only two possible candidates. 848in choosing one of the candidates in a starting cell, one forms a chain of choices 849which results in the suppression of a candidate in a given cell. 850If the other candidate is chosen in the starting cell and one ends with the suppression 851of the same candidate, it can be excluded safely. 852Pick your starting cell, and make a small u shape (a little smiley curve) underneath the first pencilmark. 853From there, look around, but instead of crossing out pencilmarks (otherwise it gets too messy!), 854when you find a value that it forces somewhere else, put the same u shape underneath the forced value. 855Ignore any that the first choice eliminated you might need them later! 856Carry on doing this until you cant make any more u forces. 857Now choose the second value in your original cell and this time put a little n symbol (a downturned mouth) above the second pencilmark. 858Like before, look at the implications and forces that this makes, continuing on until you cant find any more. 859If theres a forcing chain, at some point youll find a pencilmark with both a u and n on the same mark 860(in which case theyll almost join up!). This its a sure sign that whichever candidate you picked for the first cell, 861youve found the right value for the second cell. 862 863X-Chain: 864X-Chains are chains that use one digit only. X-Chains of length 4 are sometimes called Turbot Fishes. 865A X-Chain is a single-digit solving technique which uses a chain consisting of links that alternate between strong links and weak links, 866with the starting and ending link being strong. In other words, the chain involves an even number of cells having the target digit as a candidate. 867The target digit can be eliminated from any cell that is seen by both ends of the X-Chain. 868The important thing with X-Chains is, that they have to start and end with a strong link. This ensures that one of the endpoints actually contains 869the chain digit. That digit can be removed from any cell that sees both ends of the chain. (EG -- even number of nodes, elimination cell must 'see' both ends) 870 871Forbidding Chain -- Really another version of X-Chain, but implemented some handling of group nodes and that opens up more strong links for chains. Begin and 872end on weak link using single digit chain and alternating links. If you reach the head again, eliminate that candidate. 873 874Remote Pair Chain: 875Remote Pair is the simplest chaining technique. It considers only bivalue cells that contain the same two candidates. 876Since the cells are bivalue, a strong link exists within every cell between the two candidates. The links between the cells 877can therefore be weak (the cells have to see each other). To eliminate something, the chain has to be at least four cells long. 878The Remote Pair ensures, that any cell within the chain has the opposite value of the cell before it. Any cell outside the Remote Pair 879that sees two cells with different values cannot have one of the Remote Pair digits set. 880 881Medusa 3D: 8823D Medusa extends single's chains into a third dimension. Medusa 3-D extends the search is up through the bi-value cells which contain two different numbers. 883You can think of the different candidate numbers as existing in a third dimension lifting up from the page with 1 at the bottom and 9 at the top. 884In other words, its a chain that can jump not only between cells on strong links but inside a bivalue cell as well. Start the chain on a cell and one candidate in that cell and chain with alternating 'colors'. Create all possible chains and then 885analyze the chain using six rules. 886 887Rule 1 - Twice in a Cell - If two candidates in a cell have the same colour - all of that colour can be removed - and the opposite colour are all solutions. All that color in the ENTIRE puzzle grid! 888Rule 2 - Twice in a Unit - Similiar to 1, but looking for two coloured occurrences of X in the same unit (row, column or box) as opposed the two of the colour in the same cell. 889Rule 3 - Two Different Colors in a Cell with more than 2 candidates - We know that either ALL the blue candidates will be true, or ALL the green ones. If there are any another candidates in any cell with two colours, they cannot be solutions 890Rule 4 - Two Colors "Elsewhere" - where there are candidates that can see both colours they can be removed. By 'see' we mean any candidates that are the same candidate as members of the blue/green links. (e.g., a '6' sees both a green and blue '6'). It can be removed. 891Rule 5 - Two Colors in Unit and Cell - If an uncoloured candidate can see a coloured candidate elsewhere (it shares a unit) AND an opposite coloured candidate in its own cell, it can be removed. 892Rule 6 - Cell Emptied by Color - If all the candidates in an uncolored cell can 'see' their counterparts all colored the same, ALL of that color can be removed (e.g., same as 1, in the entire grid) 893 894*/ 895
Sudoku solver algorithms and code
c_cpp
Program that builds the actual solver
1/* 2 3 Sudoku Puzzle Solver 4 A Sudoku (i.e. the puzzle) is a partially completed grid. 5 A grid has 9 rows, 9 columns and 9 boxes, each having 9 cells (81 total). 6 7The program first tries to solve the input puzzle using solution algorithms including: 8 91. Naked Single 102. Hidden Single 113. Pointing and Claiming (Locked Candidates) 124. Naked Pair,Triple,Quad 135. Hidden Pair,Triple,Quad 146. X-Wing 157. Y-Wing (aka XY-Wing) 168. XYZ-Wing 179. WXYZ-Wing 1810. Swordfish 1911. Jellyfish 2012. Unique Rectangle (Type 1) 2113. Unique Rectangle (Type 2) 2214. Unique Rectangle (Type 3) 2315. Unique Rectangle (Type 4) 2416. Unique Rectangle (Type 5) 2517. Hidden Rectangle 26 27 28 29These solution algorithms use a classic "candidate-based" approach using an initial build of all candidate entries 30for each cell that is not initially filled by the input grid. Candidates are the same as "pencil marks" or 31"marks" in a manual/paper Sudoku puzzle 32 33If the algorithm-based solutions fail (and they do for certain input puzzles!), the program switches first to "Forcing Chain" algorithms including XY-Chain, X-Chain, Medusa 3_D, Forbidding Chain 34(that adds group nodes to X-Chain to enhance strong links) and Remote Pair and finally to a "Recursive Descent/Backtrack" solution search that is optimized using only the remaining candidates for 35each attempt made to fill the remaining empty cells. That should work for any legal input puzzle. 36 37Development started in February 2021 and Forcing Chain was added in mid-June. 38Unique Rectangle added in July 2021 39Additional chain solutions (remote pair, X Chain) added in August 40 41Version 1.0 -- 7-7-21 First 'complete' version with all algorithms in place 427-9-21 Add new puzzle 437-15-21 Added Unique Rectangle Types 1,2,4 (see Sudoku_Puzzles) and remote pair chain 448-18-21 Added X-Chain and two new recursive demos 458-25-21 Added XYZ-Wing 469-7-21 Added Hidden Rectangle 479-28-21 Add Forbidding Chain which is a Type 1 Nice Loop and an extension of X Chain 4810-1-21 Fix bug that occassionally set up wrong Solution Grid 4910-27-21 Added Medusa 3D, beginning to add its elimination rules 5012-27-21 Added "Workout" puzzle 5103-29-22 Continue clock code 5204-14-22 Date, time and temperature added 5305-10-22 Small updates to idle screen 54 55 56 57Acknowledging the very critical help from online Sudoku resources for descriptions, explanations, puzzles and quick development of test-bed solutions, including: 58HoDoKu 59SudokuSolutions 60SudokuOfTheDay 61Sudokuonline.io 62Sudoku Snake 63SudokuWiki.org 64 65*/ 66 67// #define Test_Mode1 // if defined, loads test puzzles and runs 68 // #define Test_Mode2 // if defined, preloads a demo puzzle in input mode 69// #define Test_Mode3 // check against pre-input solution (usually on if 1 is on, goes with Test_Mode1) 70 #define Test_Show // if not defined, turns off some of the Show_XXX routines used for debugging 71 72#include <Adafruit_GFX.h> // graphics library 73#include <Adafruit_ILI9341.h> // color display 74#include <SdFat.h> // SD card & FAT filesystem library 75#include <Adafruit_SPIFlash.h> // SPI / QSPI flash library 76#include <Adafruit_ImageReader.h> // Image-reading functions 77#include "RTClib.h" // real time clock library 78#include "Sudoku_Puzzles.h" // test and demo puzzles 79#include <Keypad.h> // keypad library 80 81// Initialize for Adafruit DS3231 RTC real time clock 82RTC_DS3231 rtc; 83 84// Color display 1 85#define TFT1_DC 9 // color display control pins 86#define TFT1_CS 10 87#define SD1_CS 4 // SD card select pin 88// Comment out the next line to load from SPI/QSPI flash instead of SD card: 89 90// on Mega use SPI Hardware CLK = 52, MISO = 50, MOSI = 51, CS = 10, DC = 9 (for last two, whatever is declared ast TFT1_DC and TFT1_CS) 91Adafruit_ILI9341 tft1 = Adafruit_ILI9341(TFT1_CS, TFT1_DC); 92 93// Color display 2 94#define TFT2_DC 11 // color display control pins 95#define TFT2_CS 12 96#define SD2_CS 7 // SD card select pin 97 98// on Mega use SPI Hardware CLK = 52, MISO = 50, MOSI = 51, CS = 10, DC = 9 (for last two, whatever is declared ast TFT2_DC and TFT2_CS) 99Adafruit_ILI9341 tft2 = Adafruit_ILI9341(TFT2_CS, TFT2_DC); 100 101 102// Comment out the next line to load from SPI/QSPI flash instead of SD card: 103#define USE_SD_CARD 104 105#if defined(USE_SD_CARD) 106 SdFat SD; // SD card filesystem 107 Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys 108#else 109 110 Adafruit_SPIFlash flash(&flashTransport); 111 FatFileSystem filesys; 112 Adafruit_ImageReader reader(filesys); // Image-reader, pass in flash filesys 113#endif 114 115Adafruit_Image img; // An image loaded into RAM 116 117// Keypad 118// * AdaFruit PID3844 4x4 Matrix Keypad (need only if you use the Adafruit key library rather than keypad.h) 119// define pins here - this keypad is pinout (with keys facing you) 120// * 0 # D (keys facing you) 121// nc C1 C2 C3 C4 R1 R2 R3 R4 nc (nc = no connection) 122#define R1 32 // row connections 1-4 123#define R2 33 124#define R3 34 125#define R4 35 126#define C1 38 // column connections 1-4 127#define C2 39 128#define C3 40 129#define C4 41 130 131const byte KROWS = 4; //four rows 132const byte KCOLS = 4; //four columns 133 134char Kkeys[KROWS][KCOLS] = { // key map for key.h library 135 {'1','2','3','A'}, 136 {'4','5','6','B'}, 137 {'7','8','9','C'}, 138 {'*','0','#', 'D'}}; 139 140byte KrowPins[KROWS] = {32,33,34,35}; //connect to the row pinouts of the keypad 141byte KcolPins[KCOLS] = {38,39,40,41}; //connect to the column pinouts of the keypad 142 143Keypad K_Keypad = Keypad( makeKeymap(Kkeys), KrowPins, KcolPins, KROWS, KCOLS ); 144 145/* 146 * 0 through 9 (48-57) are numbers 147 * * 42 is Backspace 148 * # 35 is Forward line (cycling) 149 * A 65 is Reset/Load Puzzle 150 * B 66 is Run Puzzle 151 * C 67 is is Configure 152 * D 68 is Demo (Run next internal puzzle) 153 */ 154// Current use of Keypad Note Main Mode/Entry Mode definitions below 155#define K_Fnumber 48 // lowest number 0 156#define K_Lnumber 57 // highest 9 157#define K_Backspace 42 // * backspace during entry 158#define K_Forward 35 // # Goes forward one space during entry 159#define K_Input 65 // (A) Go to Entry Mode/Reset (wipe) entered puzzle in input mode 160#define K_Solve 66 // (B) Solve Loaded Puzzle/Return from Input Mode to Execute Puzzle 161#define K_Check 67 // (C) Configuration Changes/Check puzzle validity 162#define K_Demo 68 // (D) demo next puzzle/Play Sudoku 163int K_Char = 0; // returned decimal value of key press 164int K_Demo_Current = 0; // for each press of D increment 165const int K_DC_Interval = 750; // cursor flash interval during puzzle input 166unsigned long DC_Timer; // used for non blocking timer 167bool Show_SD_Image; // used to see if SD demo images working 168const int SD_Robots = 3; // number of rrx images to use 169const int Delay_After_Solve = 10000; // delay after solving to clear the scroll screen 170unsigned long Sleep_Timer; // used for non blocking timer 171unsigned long Sleep_Interval = 300000; // interval with no input to go back to sleep demo 172 173 174// debugging and display tools // named after Digital Equipment Corporation's DDT (dynamic debugging technique) 175 176void DDTv(String st, int vt, bool CRR) { // Print descriptor and value 177 Serial.print(st); 178 if (CRR) {Serial.println(vt);} else {Serial.print(vt);}} // if Carriage return request <NL> then do so 179 180void DDTvl(String st, unsigned long vt, bool CRR) { // Print descriptor and value 181 Serial.print(st); 182 if (CRR) {Serial.println(vt);} else {Serial.print(vt);}} // if Carriage return request <NL> then do so 183 184void DDTn(int st, bool CRR){ // print a number 185 if (CRR) {Serial.println(st);} else {Serial.print(st);}} 186 187void DDTs(String st, bool CRR){ // print a string 188 if (CRR) {Serial.println(st);} else {Serial.print(st);}} 189 190void DDTsp(bool CRR){if (CRR) {Serial.println(" ");} else {Serial.print(" ");} } // print one space (to separate values, etc. 191 192const bool NL = true; // new line after Show_NRC and DDTx 193const bool SL = false; // don't, i.e., no <newline> 194 195char Dstring[ ] = "1 2 3 1 2 3 1 2 3 "; 196char Bstring[ ] = " "; 197const byte DHoffset [] = {0,3,6, 10,13,16, 20,23,26}; 198const byte DVoffset [9] = {6,13,20,27,34,41,48,55,62}; 199const byte HStart = 10; 200 // constants for TFT Color display 201const byte Ver1 = 2; //x axis of lh vertical line 202const byte Ver2 = 79; //x axis of 2nd vertical line 203const byte Ver3 = 159; //x axis of 3rd vertical line (don't care about rh, 4th) 204const byte Hor1 = 2; //y axis of first (top) horizontal line 205const byte Hor2 = 79; //y axis of 2nd horizontal line 206const byte Hor3 = 159; //y axis of 3rd horizontal line 207const byte Hor4 = 240; //y axis of 4th horizontal line (bottom of puzzle grid) 208const byte Dis1 =10; //x displacement of first digit from nearest line 209const byte Dis2 = 33; //x displacement of 2nd digit from nearest line 210const byte Dis3 = 55; //x displacement of 3rd digit from nearest line 211const byte BoxOffsets[3] = {Dis1,Dis2,Dis3}; // array of offsets left to right and top to bottom for 1st, 2nd and 3rd digit in box 212 213const byte Msg1Offset = 27; // y axis displacement from bottom of grid to one line message 214const byte Msg1TextSize = 4; // text sizes are 1-5 (small to largest) (if 5, its max of 8 and pw of 15 and offset 20) 215const byte Msg1TextMax = 10; // maximum characters for single line message 216const byte Msg1PW = 12; // character width in pixels (for centering adjustment) 217const byte Msg2Offset = 16; // y axis displacement from bottom of grid to two line message 218const byte Msg2TextSize = 3; 219const byte Msg2TextMax = 13; // maximum characters for double line message 220const byte Msg2PW = 9; // character width in pixels (for centering adjustment) 221const byte Msg3Offset = 6; // y axis displacement from bottom of grid to three line message 222const byte Msg3TextSize = 2; 223const byte Msg3TextMax = 20; // maximum characters for three line message 224const byte Msg3PW = 6; // character width in pixels (for centering adjustment) 225const int T_Msg_Delay = 3000; // after a scroll message - keep display for a bit 226const byte DigitTextSize = 2; // size of text for puzzle grid display 227const byte T_ScrollTextOffsetVert =2; // distance to scrolling messages from top 228const byte T_ScrollTextOffsetHor =8; // distance to scrolling messages from top 229const byte T_ScrollTextSize = 1; // very small for updates 230const byte T_Scrolling_Limit = 29; // maximum number of lines on display 231const int Scrolling_Text_Line_Delay = 100; // delay after each line printed on progress/move display 232const int Scrolling_Text_Page_Delay = 500; // delay before clearing current page 233const int Scroll_Display_Delay = 2000; // used to wait between successive displays 234const int Scroll_Error_Delay = 3000; // after error message before resetting progress display 235bool Scrolling_Text_Just_Cleared = false; // used to skip initial delay 236int T_Scrolling_Pointer = 0; // pointer for display 237const int Grid_Color = ILI9341_GREEN; // color for puzzle grid 238const int Display1_Color = ILI9341_YELLOW; // display portion of main (Puzzle) TFT display 239const int Display2_Color = ILI9341_WHITE; // display portion of main (Puzzle) TFT display 240const int Constant_Color = ILI9341_BLACK; // input digits 241const int Entry_Color = ILI9341_BLUE; // and placed numbers 242const int Msg1_Color = ILI9341_BLUE; // text color in display area - puzzle display 243const int Msg2_Color = ILI9341_BLACK; // text color in display area - progress display 244 245int Date_Time_Num; // used to build clock stuff 246int Date_Time_Num_Year; // 247int Date_Time_Num_Month; // 248int Date_Time_Num_Day; // 249int Date_Time_Num_Hour; // 250int Date_Time_Num_Minute; // 251int Date_Time_Num_Second; // not input but is displayed 252int Date_Time_Num_DOW; // day of the week 253bool Date_Time_Num_Valid; // used to check clock input 254int Old_Minute = 61; // persistent minute to control sleep screen refresh 255 256float fc = 0.0; // receives floating point temperature in celcius 257int ifc =0; // convert to integer and adjust c to f 258float fc_adjust = -8.0; // adjustment to temperature because its not calibrated 259const int Display_Temp_Cycle = 15; // Display temp every n seconds 260int Display_Temp_Counter = 0; // Seconds counter to decide temp vs time 261 262String daysOfTheWeek[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 263String monthsOfTheYear[12] = {"January", "February", "March", "April", "May", "June", "July","August", "September", "October", "November", "December"}; 264 265 266bool Show_Clear_Place = true; // Show Candidate clearing and placement 267bool Use_Algorithms = true; // use algorithms before reverting to recursive descend/backtrack 268bool Use_All_Demos = false; // normally, run just one demo on "D" command at main menu 269byte Demo_Counter = 0; // used as above 270 271byte Box_Coords [9][2] = {{0,0},{0,3},{0,6},{3,0},{3,3},{3,6},{6,0},{6,3},{6,6}}; //Row and Column coords of each of the 9 boxes 272const byte Row_Pointer = 0; // index to row within Box_Coords, CanCoord, RR_CC etc. 273const byte Column_Pointer = 1; // and index to column 274 275 byte Puzzle_Grid [9][9]; // Puzzle working solution and display grid 276#ifdef Test_Mode3 277byte Solution_Grid [9][9]; // and pre done solution (used for test) 278#endif 279 char Puzzle_Buffer [25]; // returned puzzle name 280 String Puzzle_Name; 281 int Puzzles_Number = sizeof(Puzzles_List)/2; // number of test/demo puzzles 282 byte Cell_Entry =0; 283 int Cell_Pointer = 0; 284 285 const int Minimum_Starting_Clues = 17; // literature says no unique solution puzzle has less than 17 clues 286 287int Occurence_Counter = 0; // persistent placement counter for algorithms 288int RR_CC[3][2]; // used to store coordinates of found instances (e.g. for pointing pair) 289int RR_CC_Ptr = 0; 290 291byte SWF [9][6]; // array for x-wing,swordfish & jellyfish algorithms (numbr of candidate bits, membership in x,sword and fish and [C1][C2][C3][C4] or [R1][R2][R3][R4] marks) 292const int SWF_Number_Found = 0; // index into SWF for how many of the marks are stored 293const int SWF_Member =1; // index into SWF for a flag for whether this row/column is part of the x,s or j fish 294const int SWF_Marks = 2; // where up to 4 columns or rows start to be stored 295int SWF_Membership = 0; // number of members (has to be 2,3, or 4 depending on x-wing, swordfish or jellyfish) 296int SWF_Start = 0; // and this is the starting c/r for the unit being checked 297int SWF_Mask = 0; // this is # of columns or rows across ALL columns/rows checked against initial 298 299int Digit_Array[10]; // quick place to unpack digits 0-9 300 301int Y_Wing_Z; // used to globally return Y wing candidate to clear 302bool No_Solution; // set true by routines like RD and Unique Rectangle that can detect no solution to end Solve loop 303String PTQ_String[3] = {{"Pair"}, {"Triple"}, {"Quad"}}; //used for displays to identify source of 'move' 304 305int Match_Counter = 0; // match counter for naked and hidden searches 306int BitMask = 0; // bitmask used for matching 307int BitMask_Result = 0; // found match 308byte CanCount[9]; // number of candidates in each analyzed cell 309int CanCand[9]; // contents for each analyzed cell 310bool CanMark[9]; // used to mark which cells match/don't match 311byte CanCoord [9][2]; // coordinate pointers back to main Candidates matrix 312int CC_Length = 0; // number of active candidates in analytical array (0-9) 313int CCC = 0; // calculated candidate counter 314 315String Row_Labels[] = {"A","B","C","D","E","F","G","H","I"}; // can change to 0-9 if you want 316 317// Hierarchy is Puzzle ==> Box (0-8), Rows, Columns and Cell 318// Boxes, Rows and Columns run 0-8 (not 1-9), and each has both a 'total filled' count in cell 0, and knock-in markers in cells 1-9 319// Where that cell (1-9) will be non-zero if that number exists in the row or column or box 320 321int Filled = 0; // how many cells are filled (will end with 81) 322int Last_Filled = 0; // total at start of pass 323int Last_Total_Candidates = 0; // same for candidates because you can have a pass where candidates change but nothing placed 324int Chain_Filled = 0; // number of filled cells when entering chain solutions 325int Chain_CCC = 0; // same 326int Passes = 0; // number of passes til solution 327unsigned long Solve_Timer; // time it took for the solution 328 329int Candidates[9][9] ; // candidate matrix is cell x 9 possible contents - each cell has 1-9 as markers for a candidate in 'bits' switched on 330 // e.g. if a candidate cell contains 2,6,9 then bits 2,6, and 9 are 'on' as candidates, starting from rh side (don't make this a byte -- too small!) 331byte Candidates_Count[9][9]; // contains the number of bits turned on in this cell, hence the number of candidates 332 333const int RDC_Max_Size = 81-Minimum_Starting_Clues; 334const byte RDC_CurrCan = 2; // re-use Row_Pointer and Column_Pointer from above (index 0 and 1).. this adds a 'current candidate marker' 335byte RDC_Matrix [RDC_Max_Size][3]; // Recursive descent matrix -- possible blank matrix, so r,c, and which candidate we are up to. This would fit a minimum-clue puzzle 336int RDC_Length = 0; // the number of blank cells we actually have at start of recursive descent/backtrack 337int RDC_Ptr = 0; // and current pointer - points to recursive level and CurrCan array, row and column 338int RDC_Calls = 0; // recursive calls counter 339 340byte FC_R [RDC_Max_Size]; // forcing chain -- pointer to the row of each 2-candidate cell (copied into from RDC Matrix after its loaded by Cand_Load2) 341byte FC_C [RDC_Max_Size]; // pointer to column 342byte FC_CN[RDC_Max_Size][2]; // contains the two candidates 343int FC_UD[RDC_Max_Size][2]; // contains the 'up' and/or 'down' marker -- as each chain is followed (Soduku Solver terminology) 344const byte FC_1 = 0; // index to first (or left-hand) candidate in FC_CN and FC_UD 345const byte FC_2 = 1; // 2nd or right hand 346const byte FC_Up = 0b00000001; // bit marker for an 'up' 347const byte FC_Down = 0b00000010; // bit marker for a 'down' 348const byte FC_UpandDown = FC_Up | FC_Down; 349const bool MD_Mark_Green = true; // flip flop on color for this cell 350const byte MD_Green = 0; // index to 'Green' -- each '1' bit marks a candidate position that is colored green (really same as FC_1 and 2) 351const byte MD_Yellow = 1; // index to 'Yellow' -- each '1' bit marks a candidate position that is colored yellow (for coloring algorithms) 352byte MD_Green_Candidates; // candidates colored - used for display/diagnostics 353byte MD_Yellow_Candidates; 354bool MD_Eliminated_Candidate; // don't use multiple Medusa rules if prior MD rules 'worked' because underlying data could be bad 355const byte FC_Flag = 99; // used as flag to bypass after use 356bool FC_List[RDC_Max_Size]; 357bool FC_Invoked = false; 358const int FC_Max_Heads = 25; // maximum number of direct successors each recursion can loop through (8 in my row + 8 in my box + 8 in my column) 359byte FC_Ptr; // incremental store into array 360const bool Strong_Link = true; 361const bool Weak_Link = false; 362byte XC_Link_Counter; // X Chain link counter and row, column of head of the chain 363byte XC_Head_R; 364byte XC_Head_C; 365unsigned int FC_Used_List[RDC_Max_Size]; // used by more complex 'used list' for forbidding chain and x-chain 366byte WXYZ_Didnt_Match; // candidate that DIDN'T match(i.e. 'x') out of {589} as base against {x,5}, {x,8} or {x,9} 367byte WXYZ_Match; // candidate that DID match (these are used by WXYZ_BitsMatchOne), so one of 5,8, or 9 368 369 370void setup(void) { 371 372 Serial.begin(9600); 373 ImageReturnCode stat; // Status from image-reading functions 374 tft1.begin(); // initialize color display is 240 wide (horizontal) by 320 long (vertical) 375 tft2.begin(); // initialize 2nd color display, also 240 wide (horizontal) by 320 long (vertical) 376 377 // The Adafruit_ImageReader constructor call (above, before setup()) 378 // accepts an uninitialized SdFat or FatFileSystem object. This MUST 379 // BE INITIALIZED before using any of the image reader functions! 380 Serial.print(F("Initializing filesystem...")); 381#if defined(USE_SD_CARD) // Note: the SD Card seems to fail the first time AFTER 382 // SD card is pretty straightforward, a single call... // an upload but works subsequently (next power up cycle) 383 Show_SD_Image = true; // assume its working 384 if(!SD.begin(SD1_CS, SD_SCK_MHZ(25))) { // ESP32 requires 25 MHz limit 385 DDTs("SD begin() failed",NL); 386 Show_SD_Image = false;} 387#else 388 // SPI or QSPI flash requires two steps, one to access the bare flash 389 // memory itself, then the second to access the filesystem within... 390 if(!flash.begin()) { 391 DDTs("flash begin() failed",NL)); 392 Show_SD_Image = false;} 393 if(!filesys.begin(&flash)) { 394 DDTs("filesys begin() failed",NL); 395 Show_SD_Image = false;} 396#endif 397 398if (! rtc.begin()) { // check that clock is there 399 T_Msg2("RTC", "Didn't Start"); // clock missing is an error 400 delay(T_Msg_Delay);} // 401 if (rtc.lostPower()) { // if power lost, notify 402 T_Msg2("RTC", "Lost Power!"); 403 delay(T_Msg_Delay); 404 T_Msg2("Replace","Battery?"); 405 delay(T_Msg_Delay);} 406 else 407 {DateTime now = rtc.now(); // else just reload from RTC 408 Date_Time_Num_Year = now.year(); 409 Date_Time_Num_Month = now.month(); 410 Date_Time_Num_Day = now.day(); 411 Date_Time_Num_Hour = now.hour(); 412 Date_Time_Num_Minute = now.minute(); 413 Date_Time_Num_Second = now.second(); 414 Date_Time_Num_DOW = now.dayOfTheWeek(); 415 T_Msg3("Clock Set", FDM_DT(Date_Time_Num_Year,Date_Time_Num_Month,Date_Time_Num_Day,Date_Time_Num_Hour,Date_Time_Num_Minute),daysOfTheWeek[Date_Time_Num_DOW]); 416 delay(T_Msg_Delay); 417 } 418 419 #ifndef Test_Mode1 420 Sleep_Demo(); // show start/sleep demo 421 #endif 422 423 T_Display_Setup(); // clear and create color display 424 Clear_Scroll_Display(); // and display of 'moves' (scrolling progress display( 425 426#ifdef Test_Mode1 // test mode loads test puzzle and runs 427 for (int i = 0; i < Puzzles_Number; i++){ 428 T_Display_Setup(); 429 Clear_Scroll_Display(); 430 Transfer_Test(i); // get the puzzle 431 DDTv("Puzzle Grid Fetched ",i+1,SL); 432 DDTs(" Named ",SL); 433 DDTs(Puzzle_Name,NL); 434 T_Msg3("Fetching",FDM("Puzzle ",i+1,""),Puzzle_Name); // offset by 1 for readability 435 T_Scroll_Message(FDM("Puzzle ",i+1," Loaded")); 436 T_Scroll_Message(Puzzle_Name); 437 Examine_Puzzle(); // analyze the puzzle 438 Puzzle_Check(); // check the puzzle 439 Solve_Puzzle(); // and solve it 440 Clear_Puzzle(); // clear puzzle grid 441 delay(Scroll_Error_Delay);} 442 while(true){;} 443#endif // Test_Mode 1 444 445 K_Return_To_Main(); 446 K_Char = 0; 447 Sleep_Timer = millis(); // set sleep timer 448 449} // end of Setup 450 451void loop(void) { 452 453 K_Read(); // read keypad 454 455 switch (K_Char){ 456 457 case K_Demo: // is it demo mode (D)? 458 K_Char = 0; // yes, clear it 459 if (Use_All_Demos) { // if doing all, load all 460 Clear_Scroll_Display(); 461 for (K_Demo_Current=0; K_Demo_Current< Puzzles_Number;K_Demo_Current++) {Run_Test();} 462 } else 463 { Clear_Scroll_Display(); 464 T_Scroll_Message(F("Select Puzzle to Run")); 465 T_Scroll_Message(F("Enter Two Digit Puzzle Number")); 466 T_Scroll_Message(FDM("From 01 to ",Puzzles_Number," ")); 467 T_Msg3("Enter Puzzle # ",FDM("1 to ",Puzzles_Number,""),"to run"); 468 while(K_Char == 0){K_Read();} 469 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 470 K_Demo_Current = K_Char - K_Fnumber; 471 K_Char = 0; } //clear 472 while(K_Char == 0){K_Read();} 473 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 474 K_Demo_Current = ((10*K_Demo_Current)+(K_Char - K_Fnumber));} // form 2 digit number 475 if (K_Demo_Current > 0 && K_Demo_Current <= Puzzles_Number){ 476 K_Demo_Current--; // correct 1-n to 0-(n-1) 477 Run_Test();} else 478 {T_Scroll_Message("Invalid Puzzle Number!"); 479 T_Msg3("Not a valid","Puzzle Number",FDM("",K_Demo_Current,"")); 480 delay(Scroll_Display_Delay);}} 481 K_Return_To_Main(); 482 Sleep_Timer = millis(); // reset sleep timer 483 break; 484 485 case K_Input: // look for puzzle entry (A) 486 K_Char = 0; 487 Clear_Puzzle(); 488 T_Display_Setup(); 489 T_Msg2("Load", "Puzzle"); // display state 490 K_Get_Puzzle(); // get puzzle input, loaded and ready 491 K_Return_To_Main(); // indicate we've returned 492 Sleep_Timer = millis(); // reset sleep timer 493 break; 494 495 case K_Solve: // solve current puzzle 496 K_Char = 0; 497 Examine_Puzzle(); // make sure it is legal 498 DDTv("Filled is ",Filled,NL); 499 if (Filled > 0){ // but only if not blank 500 if (Puzzle_Check()){ 501 Clear_Scroll_Display(); // new progress display 502 Solve_Puzzle(); 503 delay(Delay_After_Solve); 504 K_R_T_M_Scroll_Only();} else // check the puzzle 505 { T_Msg3("Puzzle Doesn't","Check Out","Try Again");}} 506 Sleep_Timer = millis(); // reset sleep timer 507 break; 508 509 case K_Check: // Configuration 510 K_Char = 0; 511 K_Configure(); 512 K_Return_To_Main(); 513 Sleep_Timer = millis(); // reset sleep timer 514 break; 515 516 case K_Backspace: // used to go into sleep mode 517 K_Char = 0; 518 Sleep_Demo(); // show start/sleep demo 519 K_Return_To_Main(); 520 Sleep_Timer = millis(); // reset sleep timer 521 break; 522 523 default: 524 K_Char = 0; 525 break; 526 } // end of switch K_Char 527 528 if (millis() - Sleep_Timer > Sleep_Interval) { 529 Sleep_Demo(); // show start/sleep demo 530 K_Return_To_Main(); 531 Sleep_Timer = millis();} 532 533 } // end of "loop" 534 535void Examine_Puzzle(){ // set up analytical contents 536 537 Filled = 0; // reset counters 538 int Cell_Entry = 0; 539 540 for (int r = 0; r < 9; r++){ 541 for (int c = 0; c < 9; c++){ // if its a non-zero cell 542 Cell_Entry = Puzzle_Grid[r][c]; 543 if (Cell_Entry > 0){ 544 Filled++; // another cell is filled 545 T_Number(r,c,Cell_Entry,Constant_Color); // and enter initial cell on color display 546 } // end of looking at the specific cell 547 } // end of looking at the column in row 548 } // end of looking at this row 549 CCC = 0; // candidates count initialized 550 Build_Candidates(); // build the candidates matrix 551 552 Show_Grid(); // show initial puzzle grid 553 554} // end of Examine Puzzle 555 556bool Puzzle_Check(){ // check puzzle for initial integrity 557int Cell_Entry; // Note: can't check to see if it has a solution 558 559 if (Filled < Minimum_Starting_Clues){ 560 DDTv("Puzzle has too few initial clues ",Filled,NL); 561 T_Msg3("Puzzle Error",FDM("Only ",Filled," Clues"),"Try Again"); 562 T_Scroll_Message(F("Too few cells filled at start")); 563 T_Scroll_Message(F("Single solution needs >=17 clues")); 564 return false;} 565 for (int r = 0; r <9; r++){ // check loaded puzzle for integrity 566 for (int c = 0; c <9; c++){ 567 Cell_Entry = Puzzle_Grid[r][c]; 568 if (Cell_Entry > 0){ 569 if (!RD_Check(Cell_Entry, r,c)){ 570 DDTs("Input Puzzle has an error at ",SL); 571 Show_NRC(Cell_Entry,r,c,NL); 572 Show_Grid(); // and final puzzle grid 573 T_Msg3("Puzzle","Conflict", FDM_NRC(Cell_Entry,r,c)); 574 T_Scroll_Message("Cell Entry"); 575 T_Scroll_Message("Conflict Error"); 576 T_Scroll_Message("Entry Invalid"); 577 return false;}} 578 else { 579 if (Candidates_Count[r][c] ==0){ // if its an unfilled cell, it has to have candidates 580 DDTs("Input Puzzle has an error at ",SL); 581 Show_NRC(Cell_Entry,r,c,NL); 582 DDTv("Candidates for cell = ",Candidates_Count[r][c],NL); 583 T_Msg3("Puzzle","Candidate Error", FDM_NRC(Cell_Entry,r,c)); 584 T_Scroll_Message("Cell with no candidates error"); 585 return false;}} 586 }} 587 return true; 588} // end of Puzzle_Check 589 590void Solve_Puzzle(){ 591 592 String Pass_String; // differentiate one from multiple 593 String Time_String; 594 595 DDTv(F("Starting Algorithm Solution with "),Filled,SL); 596 DDTv(F(" Cells Filled and Starting Candidate Count of "),CCC,NL); 597 T_Msg3("Solving",FDM(" with ",Filled," Cells"),FDM(" and ",CCC," Candidates")); 598 T_Scroll_Message(F("Starting with Algorithms")); 599 T_Scroll_Message(FDM("with ",Filled," cells filled")); 600 T_Scroll_Message(FDM(" and ",CCC," candidates")); 601 602 Passes = 0; // reset pass counter 603 No_Solution = false; // assume there IS a solution 604 FC_Invoked = false; // we haven't used forcing chain 605 Solve_Timer = millis(); // get starting timer 606 607 do { // stop when the puzzle is solved 608 Passes++; // increment pass counter 609 Last_Filled = Filled; // store filled BEFORE doing anything 610 Last_Total_Candidates = CCC; // both filled and candidates 611 //solving routines follow 612 if (Use_Algorithms){ // if not jumping to recursive routine 613 614 Naked_Singles(); 615 Hidden_Singles(); 616 Hidden_Pairs_Triples_Quads(); 617 Naked_Pairs_Triples_Quads(); 618 Pointing(); 619 Claiming(); 620 X_Wing(); 621 Y_Wing(); 622 XYZ_Wing(); 623 WXYZ_Wing(); 624 Swordfish(); 625 Jellyfish(); 626 Unique_Rectangle(); 627 Hidden_Rectangle(); 628 629 } // end of normal algorithms 630 631 if (Filled == 81) { // solved when all cells filled! 632 DDTsp(NL); 633 DDTv("Done! ",Filled,SL); 634 DDTv(" Cells Filled in ", Passes,SL ); 635 DDTs(" Passes",NL); 636 Show_Grid(); // and final puzzle grid 637 T_Scroll_Message("Solved the Puzzle!!"); 638 Solve_Timer = (millis()-Solve_Timer)/1000; 639 if (Passes > 1) {Pass_String = " Passes";} else {Pass_String = " Pass";} 640 if (Solve_Timer > 1) {Time_String = " Seconds";} else {Time_String = " Second";} 641 T_Scroll_Message(FDM("Completed in ",Passes,Pass_String)); 642 T_Scroll_Message(FDM(" and it took ",Solve_Timer,Time_String)); 643 T_Msg3("SOLVED!",FDM("in ",Passes,Pass_String), FDM("taking ",Solve_Timer,Time_String)); 644 return;} 645 646 if (Filled == Last_Filled && CCC == Last_Total_Candidates) { // initial check for no progress 647 if (!FC_Invoked){ // have we tried the chain solutions? 648 DDTs(F("Trying Chain Solutions!"),NL); // console and progress panel msgs 649 T_Scroll_Message(F("Trying Chain Solutions")); 650 T_Msg3("Trying","Chain","Solutions"); 651 // try the 'easy' chain solutions first 652 DDTs(F("XY-Chain..."),NL); // console and progress panel msgs 653 T_Scroll_Message(F("XY-Chain...")); 654 XY_Chain(); // call XY Chain Forcing Chain 655 DDTs(F("X-Chain..."),NL); // console and progress panel msgs 656 T_Scroll_Message(F("X-Chain...")); 657 X_Chain(); 658 659 if (Filled == Last_Filled && CCC == Last_Total_Candidates){ // second layer of 'less easy' slower chains solutions 660 661 DDTs(F("Medusa 3-D ..."),NL); // console and progress panel msgs 662 T_Scroll_Message(F("Medusa 3-D ...")); 663 Medusa_3D(); 664 DDTs(F("Remote Pair..."),NL); // console and progress panel msgs 665 T_Scroll_Message(F("Remote Pair...")); 666 Remote_Pair(); // call remote pair chain algorithm 667 DDTs(F("Forbidding Chain..."),NL); // console and progress panel msgs 668 T_Scroll_Message(F("Forbidding Chain...")); 669 Forbidding_Chain();} 670 671 FC_Invoked = true;} // set flag that we've tried it 672 673 if (Filled == Last_Filled && CCC == Last_Total_Candidates) // if STILL no progress, then fall through to recursive search 674 675 { DDTv("Algorithms Stumped at ",Filled,NL); // if didn't find anything new, only recursive search/backtrack is left to do! 676 DDTs("Too close for missiles -- switching to guns!",NL); 677 T_Msg3(FDM("Stumped at ",Filled," Filled"),"2 Close for Missiles","Switch to Guns!"); 678 T_Scroll_Message(FDM("Stumped at ",Filled," Cells Filled")); 679 T_Scroll_Message("Too Close for Missiles"); 680 T_Scroll_Message("Switching to Guns!"); 681 delay(2000); 682 if (!RD()){ DDTs("No Solution",NL); 683 No_Solution = true; // end it 684 DDTs("Recursive Search Failed",NL); 685 T_Msg3("Recursive","Search","Failed!"); 686 T_Scroll_Message("Recursive Search Failed"); 687 if (Filled == 80){T_Scroll_Message("Multiple Puzzle Solutions"); 688 T_Scroll_Message("Not a Legal Puzzle");} 689 else {T_Scroll_Message("Puzzle Has No Solution"); } 690 if (Filled == 80) {T_Msg2("Non-Unique","Solution!");} else{T_Msg2("No","Solution!");} } 691 else { 692 DDTs("Recursive Descent/Backtrack Solution Succeeded!",NL); 693 DDTv("Recursive Solution Required ",RDC_Calls,SL); 694 DDTs(" Tries",SL); 695 Solve_Timer = (millis()-Solve_Timer)/1000; 696 DDTv(" and ",Solve_Timer,SL); 697 DDTs(" seconds",NL); 698 T_Msg3("Success!",FDM("Required ",RDC_Calls," tries"),FDM("and ",Solve_Timer," Seconds")); 699 T_Scroll_Message("Recursive Search Succeeded!"); 700 T_Scroll_Message(FDM("Required ",RDC_Calls," tries")); 701 T_Scroll_Message(FDM(" and it took ",Solve_Timer," Seconds")); 702 return;} 703 } else { T_Msg3("Chain","Algorithms","Worked!"); // if AFTER FC and/or RP we've made progress, reset flag 704 FC_Invoked = false;} // and reenter algorithm loop 705 } // end of initial "no progress" 706 707 if (CCC <0) {DDTv("Less than no candidates left",CCC,NL); 708 T_Msg1("Error!"); 709 T_Scroll_Message("Error!"); 710 T_Scroll_Message("Negative candidates left!!"); 711 Display_Debug(); 712 while(1){;}} 713 714 if (Filled > 81) {DDTv("Overfilled at",Filled,NL); 715 T_Msg2("Error!","Overfilled"); 716 Display_Debug(); 717 while(1){;}} 718 } while (Filled < 81 && !No_Solution); // end of "While Not Filled" 719} // end of Solve Puzzle 720 721void Fill_The_Cell(int n, int r,int c, String x){ // update the grid and database 722 int b = In_Box(r,c); 723 DDTs (x,SL); 724 DDTv(" is placing ",n,SL); 725 DDTs(" in ",SL); 726 Show_RC(r,c,NL); 727 if (Show_Clear_Place) {T_Scroll_Message(FDM_FTC(n,r,c,x));} // show and scroll 728 729 if (Puzzle_Grid[r][c] !=0 ){DDTv("DUPLICATE PLACE!",Puzzle_Grid[r][c],SL); 730 Show_NRC(n,r,c,NL); 731 T_Msg2("Duplicate","Placement"); 732 T_Scroll_Message(F("Duplicate Placement")); 733 T_Scroll_Message(FDM_FTC(n,r,c,"Error")); 734 Display_Debug(); 735 while(1){;}} 736 737#ifdef Test_Mode3 // if defined in input grid then 738 if (n != Solution_Grid[r][c]){ // for debugging 739 DDTv("Making mistake putting ",n,SL); // tests whether n placed in cell matches solution 740 DDTs(" in ",SL); 741 Show_RC(r,c,SL); 742 DDTv(" - should be ",Solution_Grid[r][c],NL); 743 T_Msg3("Mistake","Versus","Solution"); 744 T_Scroll_Message(F("Mistake Against Solution")); 745 T_Scroll_Message(FDM_FTC(n,r,c,"Error")); 746 Display_Debug(); 747 while(1){;}} 748#endif 749 750 Puzzle_Grid[r][c] = n; 751 Filled++; // increment how many cells are filled (will end with 81) 752 Placement_Removes_Candidates(n,r,c); // and clear candidates matrix 753 Candidates[r][c] = 0; // ensure cleared for this cell 754 if (Candidates_Count[r][c] >0) { // clear counts for cell and decrement calculated 755 CCC = CCC - Candidates_Count[r][c]; 756 Candidates_Count[r][c] = 0; } 757 T_Number(r,c,n,Entry_Color); //update display 758} // end of Fill The Cell 759 760void Build_Candidates(){ 761 for (int r = 0; r < 9;r++) { // clear the candidate matrix 762 for (int c = 0; c < 9; c++){ // and counts first 763 Candidates[r][c] = 0; 764 Candidates_Count[r][c] =0; }} 765 for (int r = 0; r < 9;r++) { // build the candidate matrix 766 for (int c = 0; c < 9; c++){ // if it is a candidate, mark it 767 for (int n = 1; n <= 9; n++){ // check all nine possibles 768 if (Can_Go(n, r,c)) {Candidate_On(n,r,c);} }} // set and increase count for # of candidates 769 } // end of building candidate count and content 770 771} // end of build candidates 772 773bool Can_Go(int check_number, int r, int c){ 774 if (Puzzle_Grid[r][c] != 0) return false; // if cell already has completed entry, can't go here 775 for (int cc= 0; cc < 9; cc++) {if (Puzzle_Grid[r][cc] == check_number){return false;}} // check all columns for this row, and 776 for (int rr= 0; rr < 9; rr++) {if (Puzzle_Grid[rr][c] == check_number){return false;}} // and all rows for this column, and 777 for (int rr =Box_Coords[In_Box(r,c)][Row_Pointer]; rr < Box_Coords[In_Box(r,c)][Row_Pointer]+3; rr++){ // all cells in the box 778 for (int cc = Box_Coords[In_Box(r,c)][Column_Pointer]; cc< Box_Coords[In_Box(r,c)][Column_Pointer]+3; cc++){ 779 if (Puzzle_Grid[rr][cc] == check_number){return false;}}} 780 return true; // neither, so it can go here 781} // end of can go 782 783void Naked_Singles(){ 784for (int r = 0; r < 9; r++){ // check for naked singles - only one candidate for a cell 785 for (int c = 0; c< 9; c++){ // for these, one candidate only and count is 1 786 if ((Candidates_Count[r][c]) == 1){ // if any cell has a candidate count of 1 787 Fill_The_Cell(Find_Next_Candidate(r,c,1),r,c,"Naked Single");}}}} // if it does, fill it 788 789void Hidden_Singles(){ // check for hidden singles - multipe candidates potentially in cell 790 for (int r = 0; r < 9; r++){ // but this is the only 'n'(i.e., only occurence of candidate) in this row or column or box 791 for (int c = 0; c< 9; c++){ 792 for (int n = 1; n<=9; n++){ // check each number 793 794 if (If_Candidate_On(n,r,c)){ // that is present as a candidate 795 Occurence_Counter = 0; // assume its the only one 796 for (int cc = 0; cc <9; cc++){ // check each column in the row 797 if (If_Candidate_On(n,r,cc)){Occurence_Counter++;}} // if we found this candidate in the row, its not unique 798 if ( Occurence_Counter == 1){ 799 Fill_The_Cell(n,r,c,"Hidden Single");}} // and if it is unique, place it 800 801 if (If_Candidate_On(n,r,c)){ // that is present as a candidate 802 Occurence_Counter = 0; // assume its the only one 803 for (int rr = 0; rr <9; rr++){ // check each row in the column 804 if (If_Candidate_On(n,rr,c)){Occurence_Counter++;}} // if we found this candidate in the row, its not unique 805 if ( Occurence_Counter == 1) {Fill_The_Cell(n,r,c,"Hidden Single");}} // and if it is unique, place it 806 807 if (If_Candidate_On(n,r,c)){ // that is present as a candidate 808 Occurence_Counter = 1; // assume its the only one 809 for (int srr = Box_Coords[In_Box(r,c)][Row_Pointer]; srr <= (Box_Coords[In_Box(r,c)][Row_Pointer])+2; srr++){ //walk the box 810 for (int scc = Box_Coords[In_Box(r,c)][Column_Pointer]; scc <= (Box_Coords[In_Box(r,c)][Column_Pointer])+2; scc++){ 811 if (!((srr == r) && (scc==c))) { if (If_Candidate_On(n,srr,scc)){Occurence_Counter++;}}}} // check each cell but not the source cell 812 if ( Occurence_Counter == 1) {Fill_The_Cell(n,r,c,"Hidden Single");}} // and if it is unique, place it 813 814 }}} // end of row, column number iteration 815} // end of Hidden Singles 816 817/* 818 * Locked Candidates Type 1 (Pointing): If in a block all candidates of a certain digit are confined to a row or column, that digit cannot appear outside of that block in that row or column. 819 * Locked Candidates Type 2 (Claiming): Works exactly the other way round: If in a row (or column) all candidates of a certain digit are confined to one block, that candidate that be eliminated from all other cells in that block. 820 */ 821 822void Pointing(){ 823 int BitMask_R ; // row instance counter, and 824 int BitMask_C; // column one too 825 for (int b = 0; b < 9; b++){ // check all the boxes in the puzzle 826 for (int n = 1; n <=9; n++){ // and all candidate numbers in each box 827 BitMask_R = 0; 828 BitMask_C = 0; // clear instance counters 829 for (int srr = Box_Coords[b][Row_Pointer]; srr <= (Box_Coords[b][Row_Pointer])+2; srr++){ //walk the box 830 for (int scc = Box_Coords[b][Column_Pointer]; scc <= (Box_Coords[b][Column_Pointer])+2; scc++){ 831 if (If_Candidate_On(n,srr,scc)){bitSet(BitMask_R,srr); // for every 'n' found, mark the row in the row bitmask 832 bitSet(BitMask_C,scc);}}} // and same for column 833 if (BitsCount(BitMask_R) == 1){Clear_Row_Outside_Box(n,BitsWhich(BitMask_R),b,"Pointing Lock");} // if found only in one row , clear row of candidate OUTSIDE box b 834 if (BitsCount(BitMask_C) == 1){Clear_Column_Outside_Box(n,BitsWhich(BitMask_C),b,"Pointing Lock");} // if found in only one column, clear it OUTSIDE of box 835 } // end of each number 836 } // end of each box 837}// end of Pointing 838 839 void Claiming(){ // note this is also sometimes called "Box/Line Intersection" 840 int BitMask_B; 841 for (int r = 0; r < 9; r++){ // check this row 842 for (int n = 1; n <=9; n++){ // for each number 843 BitMask_B = 0; 844 for (int c = 0; c < 9; c++){if (If_Candidate_On(n,r,c)){bitSet(BitMask_B,In_Box(r,c));}} // for every 'n' found, mark the bit corresponding to the box in the bitmask 845 if (BitsCount(BitMask_B) == 1){Clear_Box_Except_Row(n,r,BitsWhich(BitMask_B),"Claiming Lock");}}} // if all n in this row are in only one box , clear candidate inside box b, except for this row 846 for (int c = 0; c < 9; c++){ // check all the columns too 847 for (int n = 1; n <=9; n++){ // of that, you can eliminate that candidate from any other cells in the row or column that the candidate is aligned on. 848 BitMask_B = 0; 849 for (int r = 0; r < 9; r++){if (If_Candidate_On(n,r,c)){bitSet(BitMask_B,In_Box(r,c));}} // for every 'n' found, mark the box in the bitmask 850 if (BitsCount(BitMask_B) == 1){Clear_Box_Except_Column(n,c,BitsWhich(BitMask_B),"Claiming Lock");}}} // if all n in this row are in only one box , clear candidate inside box b, except for this row 851 852} // end of claiming 853 854void Naked_Pairs_Triples_Quads(){ // Naked triples are harder to spot. Sometimes, all three numbers in a naked triple appear in all three boxes 855 //of the triple. However, sometimes the numbers of the triple don't all appear in all three boxes 856String Clear_String = " "; // indicator string 857 858 // start of row pass 859 for (int r = 0; r<9; r++){ // step through each row 860 for (int n = 4; n >=2; n--){ 861 CC_Length = 0; 862 Clear_String = "Naked " + PTQ_String[n-2]; // construct string for display 863 for (int c = 0; c<9; c++){ 864 if ((Candidates_Count[r][c]<= n) && (Candidates[r][c] > 0)){ // only load if candidates under limit and non-zero (filled) cell 865 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 866 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 867 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 868 CanCoord[CC_Length][Column_Pointer] = c; 869 CC_Length++;}} // increment counter too 870 if (CC_Length >= n){ // search if we found sufficient number of cells to bother 871 BitMask_Result = NPTQ_Search(n,CC_Length); 872 if(BitMask_Result> 0){ // returned bitmask if found a match 873 for (int cc = 0; cc < 9; cc++){ 874 Clear_All_But_Marked(r,cc,BitMask_Result, Clear_String); } // clear candidates from remainder of row 875 }} }} 876 877 for (int c = 0; c<9; c++){ // step through each column 878 for (int n = 4; n >=2; n--){ 879 CC_Length = 0; 880 Clear_String = "Naked " + PTQ_String[n-2]; // construct string for display 881 for (int r = 0; r<9; r++){ 882 if ((Candidates_Count[r][c]<= n) && (Candidates[r][c] > 0)){ // only load if candidates under limit and non-zero (filled) cell 883 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 884 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 885 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 886 CanCoord[CC_Length][Column_Pointer] = c; 887 CC_Length++;}} // increment counter too 888 if (CC_Length >= n){ // search if we found sufficient number of cells to bother 889 BitMask_Result = NPTQ_Search(n,CC_Length); 890 if(BitMask_Result> 0){ // returned bitmask if found a match 891 for (int rr = 0; rr < 9; rr++){ 892 Clear_All_But_Marked(rr,c,BitMask_Result, Clear_String); } // clear candidates from remainder of column 893 }} }} 894 895 for (int b = 0; b <9; b++){ // start of box pass 896 for (int n = 4; n >=2; n--){ 897 CC_Length = 0; 898 Clear_String = "Naked " + PTQ_String[n-2]; // construct string for display 899 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 900 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 901 902 if ((Candidates_Count[srr][scc]<= n) && (Candidates[srr][scc] > 0)){ // only load if candidates under limit and non-zero (filled) cell 903 CanCount[CC_Length] = Candidates_Count[srr][scc]; // Load length of each cell in row 904 CanCand[CC_Length] = Candidates[srr][scc]; // load contents of each cell 905 CanCoord[CC_Length][Row_Pointer] = srr; // load cell coordinates too 906 CanCoord[CC_Length][Column_Pointer] = scc; 907 CC_Length++;}} // increment counter too 908 if (CC_Length >= n){ // search if we found sufficient number of cells to bother 909 BitMask_Result = NPTQ_Search(n,CC_Length); 910 if(BitMask_Result> 0){ // returned bitmask if found a match 911 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 912 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 913 Clear_All_But_Marked(srr,scc,BitMask_Result, Clear_String); }}} // clear candidates from remainder of cells in this box 914 }} }} 915} // end of Candidates Naked Pairs Triples Quads 916 917int NPTQ_Search(int snumber, int scounter){ // search array - called with number of cells (2,3,4) and number of entries in array 918 919 for (int start_ptr = 0; start_ptr < scounter; start_ptr++){ // check each cell against remaining cells 920 Match_Counter = 0; // number of matching cells 921 for (int i = 0; i < 9; i++){CanMark[i] = false;} // clear markers 922 BitMask = CanCand[start_ptr]; // initial bitmask of candidates 923 for (int check_ptr = start_ptr; check_ptr < scounter; check_ptr++){ // check starting pointer against all remaining pointers 924 if ((Number_of_Candidates(BitMask,CanCand[check_ptr])<= snumber) ) // if this would add too many candidates, mark false 925 {CanMark[check_ptr] = true; // We would not over-run bit count, so mark it good 926 BitMask = BitMask | CanCand[check_ptr]; // accumulate a new mask 927 Match_Counter++;} // at each accumulation, increment match counter 928 } // end of checking start pointer against all others 929 if (Match_Counter == snumber) { // if we have the right number, return the mask, else use 0 as a no match flag 930 return BitMask;} else {return 0;} 931 } // end of all start pointers in row, column or box 932 933 } // end of NPTQ Search 934 935void Hidden_Pairs_Triples_Quads(){ // Hidden pairs, triples and quads. A pair or more that can only be placed in n cells, where n is the number we are searching 936 937 for (int r = 0; r<9; r++){ // step through each row 938 CC_Length = 0; 939 for (int c = 0; c<9; c++){ 940 if (Candidates[r][c] > 0){ // load each non-zero (filled) cell 941 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 942 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 943 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 944 CanCoord[CC_Length][Column_Pointer] = c; 945 CC_Length++;}} // increment counter too 946 HPTQ_Process(); 947 } // end of all rows 948 //start of column pass 949 for (int c = 0; c<9; c++){ // step through each column 950 CC_Length = 0; 951 for (int r = 0; r<9; r++){ 952 if (Candidates[r][c] > 0){ // load each non-zero (filled) cell 953 CanCount[CC_Length] = Candidates_Count[r][c]; // Load length of each cell in row 954 CanCand[CC_Length] = Candidates[r][c]; // load contents of each cell 955 CanCoord[CC_Length][Row_Pointer] = r; // load cell coordinates too 956 CanCoord[CC_Length][Column_Pointer] = c; 957 CC_Length++;}} // increment counter too 958 HPTQ_Process(); 959 } // end of all columns 960 961 for (int b = 0; b <9; b++){ //start of box pass 962 CC_Length = 0; 963 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 964 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 965 if (Candidates[srr][scc] > 0){ // only load if candidates if non-zero (filled) cell 966 CanCount[CC_Length] = Candidates_Count[srr][scc]; // Load length of each cell in row 967 CanCand[CC_Length] = Candidates[srr][scc]; // load contents of each cell 968 CanCoord[CC_Length][Row_Pointer] = srr; // load cell coordinates too 969 CanCoord[CC_Length][Column_Pointer] = scc; 970 CC_Length++;}}} // increment counter too 971 HPTQ_Process(); 972 } // end of all boxes 973 974} // end of Candidates Hidden Pairs Triples Quads 975 976void HPTQ_Process(){ // process row, column or box for hidden pair, triple, quad 977 int BM[5]; // process pairs, triples and quads 978 String Clear_String = " "; // indicator string 979 980 Clear_String = "Hidden " + PTQ_String[0]; // construct string for display 981 for (int m1 = 0; m1 < CC_Length-1; m1++){ // process pairs 982 for (int m2 = m1+1; m2 < CC_Length; m2++){ 983 BitMask = CanCand[m1] & CanCand[m2]; // get initial mask 984 for (int m = 0; m < CC_Length; m++){ 985 if ((m != m1) && (m != m2)){ 986 BitMask = BitsOff(BitMask, CanCand[m]);}} // remove candidates present outside the pair 987 if (BitsCount(BitMask) == 2){ 988 for (int i = 0; i < CC_Length; i++){CanMark[i] = false;} // unmark all the markers 989 CanMark[m1] = true; // and mark these two cells to process 990 CanMark[m2] = true; 991 Clear_HPTQ(BitMask,Clear_String); // call the clearing mechanism 992 CanCand[m1] = Candidates[CanCoord[m1][Row_Pointer]][CanCoord[m1][Column_Pointer]]; // and update the analytical array for new contents of 993 CanCand[m2] = Candidates[CanCoord[m2][Row_Pointer]][CanCoord[m2][Column_Pointer]]; // these two cells 994 }}} // end of process pairs 995 996 Clear_String = "Hidden " + PTQ_String[1]; // construct string for display 997 for (int m1 = 0; m1 < CC_Length-2; m1++){ // process triplets 998 for (int m2 = m1+1; m2 < CC_Length-1; m2++){ // rolling through all possible triplets in the 999 for (int m3 = m2+1; m3 < CC_Length; m3++){ // CanCan analytical array (loaded above) 1000 BM[1] = CanCand[m1]; // load the three bit marker 1001 BM[2] = CanCand[m2]; 1002 BM[3] = CanCand[m3]; 1003 for (int m = 0; m < CC_Length; m++){ 1004 if ((m != m1) && (m != m2) && (m != m3)){ // remove all candidates present outside the trio 1005 for (int i = 1; i<=3; i++){BM[i] = BitsOff(BM[i],CanCand[m]);}}} 1006 BitMask = BM[1] | BM[2] | BM[3]; // create a combined bitmask 1007 for (int i = 1; i <=3; i++){if (BM[i] == 0) {BitMask = 0;}} // check that all three contributed to the mask 1008 if (BitsCount(BitMask) == 3){ 1009 for (int i = 0; i < CC_Length; i++){CanMark[i] = false;} // unmark all the markers 1010 CanMark[m1] = true; // and mark these three cells to process 1011 CanMark[m2] = true; 1012 CanMark[m3] = true; 1013 Clear_HPTQ(BitMask,Clear_String); // call the clearing mechanism 1014 CanCand[m1] = Candidates[CanCoord[m1][Row_Pointer]][CanCoord[m1][Column_Pointer]]; // and update the analytical array for new contents of 1015 CanCand[m2] = Candidates[CanCoord[m2][Row_Pointer]][CanCoord[m2][Column_Pointer]]; // these three cells 1016 CanCand[m3] = Candidates[CanCoord[m3][Row_Pointer]][CanCoord[m3][Column_Pointer]]; // these three cells 1017 }}}} // end of process triplets 1018 1019 Clear_String = "Hidden " + PTQ_String[2]; // construct string for display 1020 for (int m1 = 0; m1 < CC_Length-3; m1++){ // process quads 1021 for (int m2 = m1+1; m2 < CC_Length-2; m2++){ // rolling through all possible triplets in the 1022 for (int m3 = m2+1; m3 < CC_Length-1; m3++){ // CanCan analytical array (loaded above) 1023 for (int m4 = m3+1; m4 < CC_Length; m4++){ 1024 BM[1] = CanCand[m1]; // load the four bit markers 1025 BM[2] = CanCand[m2]; 1026 BM[3] = CanCand[m3]; 1027 BM[4] = CanCand[m4]; 1028 for (int m = 0; m < CC_Length; m++){ 1029 if ((m != m1) && (m != m2) && (m != m3)&& (m != m4)){ // remove all candidates present outside the four being checked 1030 for (int i = 1; i<=4; i++){BM[i] = BitsOff(BM[i],CanCand[m]);}}} 1031 BitMask = BM[1] | BM[2] | BM[3] | BM[4]; // create a combined bitmask 1032 for (int i = 1; i <=4; i++){if (BM[i] == 0) {BitMask = 0;}} // check that all of them contributed to the mask 1033 if (BitsCount(BitMask) == 4){ 1034 for (int i = 0; i < CC_Length; i++){CanMark[i] = false;} // unmark all the markers 1035 CanMark[m1] = true; // and mark these three cells to process 1036 CanMark[m2] = true; 1037 CanMark[m3] = true; 1038 CanMark[m4] = true; 1039 Clear_HPTQ(BitMask,Clear_String); // call the clearing mechanism 1040 CanCand[m1] = Candidates[CanCoord[m1][Row_Pointer]][CanCoord[m1][Column_Pointer]]; // and update the analytical array for new contents of 1041 CanCand[m2] = Candidates[CanCoord[m2][Row_Pointer]][CanCoord[m2][Column_Pointer]]; // these four cells 1042 CanCand[m3] = Candidates[CanCoord[m3][Row_Pointer]][CanCoord[m3][Column_Pointer]]; // because they need to match Puzzle_Grid 1043 CanCand[m4] = Candidates[CanCoord[m4][Row_Pointer]][CanCoord[m4][Column_Pointer]]; // and its been updated 1044 }}}}} // end of process quads 1045 1046} // end of HPTQ Process 1047 1048void Y_Wing(){ // routine to execute Y algorithm - 3 cells with 2 candidates each in 1049 // XY (Pivot), YZ (Pincer 1) and XZ (Pincer 2) pattern 1050 Cand_Load_2(); // load RD array with all two candidate r,c, and candidates (c1 x 10 + c2) 1051 1052 for (int i = 0; i < RDC_Length; i++){ // ask each cell to be the pivot 1053 for (int j = 0; j < RDC_Length; j++){ // and find to others 1054 for (int k = 0; k < RDC_Length; k++){ 1055 if (Y_Check(i,j,k)){Clear_Y(i,j,k);} // look for the XY XY, YZ, XZ matchup and clear if found 1056 }}} 1057 1058 } // end of Y_Wing 1059 1060bool Y_Check(int i, int j, int k){ // check for the appropriate pattern 1061 int CN1A, CN1B, CN2A, CN2B, CN3A, CN3B; // three candidates to check 1062 bool Match_Flag; 1063 int Y_Candidate; // matching candidate between Pivot and Pincer 1 1064 int Z_Candidate; // non-matching pivot / Pincer one in Pincer 1 1065 int X_Candidate; // non-matching candidate Pivot/ Pincer 1 that is in Pivot 1066 1067 if (i == j || i == k || j == k){return false;} // don't compare to yourself 1068 1069 if (!In_Same_Unit(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],RDC_Matrix[j][Row_Pointer],RDC_Matrix[j][Column_Pointer])){return false;} // check same unit pivot and pincer 1 1070 if (!In_Same_Unit(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],RDC_Matrix[k][Row_Pointer],RDC_Matrix[k][Column_Pointer])){return false;} // check same unit pivot and pincer 2 1071 if (In_Same_Unit(RDC_Matrix[j][Row_Pointer],RDC_Matrix[j][Column_Pointer],RDC_Matrix[k][Row_Pointer],RDC_Matrix[k][Column_Pointer])){return false;} //but pincer 1 and pincer 2 can't be same 1072 1073 CN1A = RDC_Matrix[i][RDC_CurrCan]/10; // load with the six candidates 1074 CN1B = RDC_Matrix[i][RDC_CurrCan]%10; 1075 CN2A = RDC_Matrix[j][RDC_CurrCan]/10; 1076 CN2B = RDC_Matrix[j][RDC_CurrCan]%10; 1077 CN3A = RDC_Matrix[k][RDC_CurrCan]/10; 1078 CN3B = RDC_Matrix[k][RDC_CurrCan]%10; 1079 1080 Match_Flag = false; // check pivot against pincer 1 1081 if ((CN1A == CN2A) && (CN1B != CN2B)) {Match_Flag = true; // flag that we have one of the legal matchs 1082 Y_Candidate = CN1A; //common between pivot and pincer 1083 X_Candidate = CN1B; //unique to pivot 1084 Z_Candidate = CN2B;} // unique to pincer 1085 if ((CN1B == CN2A) && (CN1A != CN2B)) {Match_Flag = true; 1086 Y_Candidate = CN1B; //common between pivot and pincer 1087 X_Candidate = CN1A; //unique to pivot 1088 Z_Candidate = CN2B;} // unique to pincer 1089 if ((CN1A == CN2B) && (CN1B != CN2A)) {Match_Flag = true; 1090 Y_Candidate = CN1A; //common between pivot and pincer 1091 X_Candidate = CN1B; //unique to pivot 1092 Z_Candidate = CN2A;} // unique to pincer 1093 if ((CN1B == CN2B) && (CN1A != CN2A)) {Match_Flag = true; 1094 Y_Candidate = CN1B; //common between pivot and pincer 1095 X_Candidate = CN1A; //unique to pivot 1096 Z_Candidate = CN2A;} // unique to pincer 1097 if (!Match_Flag) {return false;} 1098 1099 // We have pivot and one pincer, the XY and YZ, so we now need to find a 2nd pivot with XZ 1100 1101 if ((CN3A == X_Candidate && CN3B == Z_Candidate) || (CN3B == X_Candidate && CN3A == Z_Candidate)) { // if Pincer 2's candidates match X,Z then 1102 Y_Wing_Z = Z_Candidate; // we have found a match, load candidate to clear 1103 return true; } // and return 'success' 1104 1105 return false; // otherwise fall through to failure 1106 1107} // end of Y_Check 1108 1109void Clear_Y(int i,int j,int k){ // clear the candidate from cells that can 'see' both pincers 1110 int mr1, mr2, mr3, mc1, mc2, mc3; // 1111 mr1 = RDC_Matrix[i][Row_Pointer]; // for readability and speed, retrieve coordinates 1112 mr2 = RDC_Matrix[j][Row_Pointer]; // rather than do repeated, nested indexing 1113 mr3 = RDC_Matrix[k][Row_Pointer]; 1114 mc1 = RDC_Matrix[i][Column_Pointer]; 1115 mc2 = RDC_Matrix[j][Column_Pointer]; 1116 mc3 = RDC_Matrix[k][Column_Pointer]; 1117 1118 for (int r = 0; r <9; r++){ // check all puzzle cells 1119 for (int c = 0; c <9; c++){ // clear candidate seen by both pincers 1120 if (!(r == mr1 && c == mc1) && !(r == mr2 && c == mc2) && !(r == mr3 && c == mc3) && In_Same_Unit(r,c,mr2,mc2) && In_Same_Unit(r,c,mr3,mc3)){ 1121 Candidate_Off(Y_Wing_Z,r,c, "Y-Wing"); }}} 1122} // end of Clear_XY 1123 1124void XYZ_Wing(){ 1125 1126 byte C_Y_Search_Candidate; 1127 byte C_Z_Clear_Candidate; 1128 1129 for (int b = 0; b<9; b++){ // check each block 1130 for (int rr = Box_Coords[b][Row_Pointer]; rr <= (Box_Coords[b][Row_Pointer])+2; rr++){ //walk the box 1131 for (int cc = Box_Coords[b][Column_Pointer]; cc <= (Box_Coords[b][Column_Pointer])+2; cc++){ 1132 if (Candidates_Count[rr][cc] == 3){ // eg {1,4,5} 1133 for (int r = Box_Coords[b][Row_Pointer]; r <= (Box_Coords[b][Row_Pointer])+2; r++){ //walk the box again 1134 for (int c = Box_Coords[b][Column_Pointer]; c <= (Box_Coords[b][Column_Pointer])+2; c++){ // looking for a subset bivalue// 1135 if (Candidates_Count[r][c] == 2 && (BitsMatch(Candidates[r][c],Candidates[rr][cc]))) { // found one and it contains two of my 3 (could be {1,4} or {1,5} or {4,5}) 1136 C_Y_Search_Candidate = BitsWhich( (Candidates[r][c] ^ Candidates[rr][cc])); // need to find another bivalue with this candidate and one of the two 1137 for (int rrr = 0; rrr <9; rrr++){ // check all puzzle cells 1138 for (int ccc = 0; ccc <9; ccc++){ 1139 if ( (Candidates_Count[rrr][ccc] == 2)&& (In_Same_Unit(rr,cc,rrr,ccc)) && (If_Candidate_On(C_Y_Search_Candidate,rrr, ccc)) && (BitsMatch(Candidates[rrr][ccc],Candidates[rr][cc])) // same unit as base, has the search candidate and all candidates are in base 1140 && (In_Box(rrr,ccc) != b) ) // and its in a different box and a bivalue 1141 { 1142 BitMask = Candidates[rr][cc]; // start with the three candidates in base 1143 BitMask = BitMask & Candidates[r][c]; // prune candidate not found in first 'pincer' 1144 BitMask = BitMask & Candidates[rrr][ccc]; // prune candidate not found in second 'pincer' 1145 C_Z_Clear_Candidate = BitsWhich(BitMask); // last candidate standing is Z - the candidate common to all three 1146 XYZ_Clear( C_Z_Clear_Candidate,b,rr,cc,r,c,rrr,ccc); // look for candidates to clear 1147 } // end of found 3rd cell 1148 }} // end of puzzle walk to find 3rd cell 1149 } // end of found first bivalue 1150 }} // end of check remainder (none pivot) of box for bivalue 1151 } // end of found a potential pivot (3 candidates) 1152 }} // end of box walk 1153 } // end of all boxes 1154} // end of XYZ Wing 1155 1156void XYZ_Clear(int MyCandidate, int b, int rz, int cz, int rx, int cx, int ry, int cy){ 1157 for (int rr = Box_Coords[b][Row_Pointer]; rr <= (Box_Coords[b][Row_Pointer])+2; rr++){ //walk the box 1158 for (int cc = Box_Coords[b][Column_Pointer]; cc <= (Box_Coords[b][Column_Pointer])+2; cc++){ 1159 if (!Same_Cell(rr,cc,rz,cz) && !Same_Cell(rr,cc,rx,cx)){ // dont look at base (z) or 2nd in-box cell (x) 1160 if (In_Same_Unit(rr,cc,rz,cz) && (In_Same_Unit(rr,cc,rx,cx) && (In_Same_Unit(rr,cc,ry,cy)))){Candidate_Off(MyCandidate,rr,cc,"XYZ-Wing");} 1161 } 1162 }} // end 1163} // end of XYZ_Clear 1164 1165void WXYZ_Wing(){ // implements WXYZ algorithm 1166 1167 byte WXYZ_P1_CandidateDM; 1168 byte WXYZ_P1_CandidateM; 1169 byte WXYZ_P2_CandidateDM; 1170 byte WXYZ_P2_CandidateM; 1171 byte WXYZ_P3_Candidate; 1172 1173 1174 for (int b = 0; b<9; b++){ // check each box 1175 for (int rr = Box_Coords[b][Row_Pointer]; rr <= (Box_Coords[b][Row_Pointer])+2; rr++){ //walk the box 1176 for (int cc = Box_Coords[b][Column_Pointer]; cc <= (Box_Coords[b][Column_Pointer])+2; cc++){ 1177 if (Candidates_Count[rr][cc] == 3){ // eg {1,4,5} [Found base] 1178 BitMask = Candidates[rr][cc]; // and store the mask 1179 for (int r1 = Box_Coords[b][Row_Pointer]; r1 <= (Box_Coords[b][Row_Pointer])+2; r1++){ //walk the box again 1180 for (int c1 = Box_Coords[b][Column_Pointer]; c1 <= (Box_Coords[b][Column_Pointer])+2; c1++){ // looking for a subset bivalue 1181 if ( Candidates_Count[r1][c1] == 2 && (WXYZ_BitsMatchOne(Candidates[r1][c1],BitMask)) ) { // found one and it contains one of base's 3 [Found Wing 1] 1182 bitClear(BitMask,WXYZ_Match); // clear the 'used' 3-c candidate we matched on 1183 WXYZ_P1_CandidateDM = WXYZ_Didnt_Match; // and remember what the unique (non-match) candidate was in the first bivalue 1184 WXYZ_P1_CandidateM = WXYZ_Match; // and remember what the match candidate was too 1185 1186 for (int r2 = Box_Coords[b][Row_Pointer]; r2 <= (Box_Coords[b][Row_Pointer])+2; r2++){ //walk the box again 1187 for (int c2 = Box_Coords[b][Column_Pointer]; c2 <= (Box_Coords[b][Column_Pointer])+2; c2++){ // looking for a subset bivalue 1188 if ( (Candidates_Count[r2][c2] == 2) && (WXYZ_BitsMatchOne(Candidates[r2][c2],BitMask)) && !(Same_Cell(r1,c1,r2,c2)) ) { // [Found Wing 2] 1189 bitClear(BitMask,WXYZ_Match); // clear the 'used' 3-c candidate we matched on 1190 WXYZ_P2_CandidateDM = WXYZ_Didnt_Match; // and remember what the unique (non-match) candidate was in the first bivalue 1191 WXYZ_P2_CandidateM = WXYZ_Match; // and remember what the match candidate was too 1192 WXYZ_P3_Candidate = BitsWhich(BitMask); // the fourth candidate should be {WXYZ_P3_Candidate, WXYZ_P2_CandidateDM} 1193 1194 if ( (WXYZ_P2_CandidateDM == WXYZ_P1_CandidateDM) && (WXYZ_P2_CandidateM != WXYZ_P1_CandidateM) ){ // now have three of the four (those in the box) [Found Wing 3] 1195 bitSet(BitMask, WXYZ_P3_Candidate); 1196 bitSet(BitMask, WXYZ_P2_CandidateDM); 1197 for (int r3 = 0; r3 < 9; r3++){ // walk the puzzle looking for 1198 for (int c3 = 0; c3 < 9; c3++){ 1199 if ( (In_Box(r3,c3) != b) && (Candidates_Count[r3][c3] == 2) && (Candidates[r3][c3] == BitMask) && In_Same_Unit(r3,c3,rr,cc) ) 1200 { 1201 // DDTs("WXYZ Base cell is ",SL); 1202 // Show_RC(rr,cc,SL); 1203 // DDTs(" Wing 1 is ",SL); 1204 // Show_RC(r1,c1,SL); 1205 // DDTs(" Wing 2 is ",SL); 1206 // Show_RC(r2,c2,SL); 1207 // DDTs(" Wing3 (not in box) is ",SL); 1208 // Show_RC(r3,c3,SL); 1209 // DDTv(" Candidate to clear is ",WXYZ_P2_CandidateDM,NL); 1210 for (int r4 = Box_Coords[b][Row_Pointer]; r4 <= (Box_Coords[b][Row_Pointer])+2; r4++){ //walk the box 1211 for (int c4 = Box_Coords[b][Column_Pointer]; c4 <= (Box_Coords[b][Column_Pointer])+2; c4++){ 1212 if (In_Same_Unit(r4,c4,r3,c3) && !(Same_Cell(r4,c4,r1,c1)) && !(Same_Cell(c4,c4,r2,c2)) ) {Candidate_Off(WXYZ_P2_CandidateDM,r4,c4,"WXYZ-Wing");} }} 1213 } // end of found 3rd Wing, clearing candidate 1214 }} // end of walk puzzle looking for third Wing 1215 1216 } // end of we have all 3 (base plus two Wings) inside the box 1217 } // end of found Wing 1 1218 }} // end of walking the box looking for Wing 2 (r3,c2) 1219 1220 } // end of found first bivalue Wing 1221 }} // end of check remainder (none pivot) of box for bivalue 1222 } // end of found a potential pivot (3 candidates) 1223 }} // end of box walk 1224 } // end of all boxes 1225} // end of WXYZ Wing 1226 1227bool WXYZ_BitsMatchOne(int BitMask1,int BitMask2){ // returns true if there is match on one single bit of smaller against larger mask 1228 // and loads global stores of which matched, which didn't and a count (has to be one) 1229byte WXYZ_Match_Count = 0; // reset number of candidates we match on 1230 1231// DDTs("BMO called with ",SL); 1232// Show_PB(BitMask1); 1233 // Show_PB(BitMask2); 1234 for (int n = 0; n <=9; n++){ 1235 if (bitRead(BitMask1,n) > 0) { // if candidate is on in first mask, and 1236 if (bitRead(BitMask2,n) > 0){ // on in 2nd also, its match so 1237 WXYZ_Match_Count++; // increment match count 1238 WXYZ_Match = n;}} // and this is the candidate that matches so far 1239 if ((bitRead(BitMask1,n) > 0) && (bitRead(BitMask2,n) == 0)){WXYZ_Didnt_Match = n;}} 1240 if (WXYZ_Match_Count == 1){ 1241 // DDTs("BMO returns TRUE with ",SL); 1242 // DDTv(" Match is ",WXYZ_Match,SL); 1243 // DDTv(" and NOMatch is ",WXYZ_Didnt_Match,NL); 1244 return true;} else { 1245 // DDTs("BMO returns FALSE ",NL); 1246 return false;} // if there was a match and there's only ONE then return true 1247} // end of WXYZ_BitsMatchOne 1248 1249void Cand_Load_2(){ // load RDC with r,c, and candidates for all two candidate cells 1250 RDC_Length = 0; // used by RDC, UR and FC 1251 byte First_Candidate; 1252 byte Second_Candidate; 1253 for (int r = 0; r < 9; r++){ // set up RDC with all cells in puzzle having 2 candidates 1254 for (int c = 0; c < 9; c++){ 1255 if (Candidates_Count[r][c] == 2){ // if two candidates, store row, column 1256 RDC_Matrix[RDC_Length][Row_Pointer] = r; 1257 RDC_Matrix[RDC_Length][Column_Pointer] = c; 1258 First_Candidate = Find_Next_Candidate(r,c,1); 1259 Second_Candidate = Find_Next_Candidate(r,c,First_Candidate+1); 1260 RDC_Matrix[RDC_Length][RDC_CurrCan] = (First_Candidate * 10) + Second_Candidate; 1261 RDC_Length++; }}} // and increment pointer (So, both candidates "fit" into single byte (10*C1 + C2) 1262} // end of Cand_Load_2 1263 1264/* 1265 * X_Wing, Swordfish and JellyFish are all variants of the same approach: n candidates appearing at least partially in n rows or columns 1266 */ 1267 1268void X_Wing(){ // X_Wing algorithm 1269 for (int n = 1; n <=9; n++){ // looking at each number 1270 for (int r = 0; r < 9; r++){Cand_Limited_R(r, n,2);} // build array on each ROW 1271 for (int r = 0; r < 9-1; r++){ // examine the row array 1272 if ((SWF[r][SWF_Number_Found] > 0) && (SWF[r][SWF_Number_Found] <=2) ){ 1273 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1274 SWF_Mask = 0; // will be used to mark what the active columns are 1275 SWF_Start = SWF[r][SWF_Number_Found]; // this is how many unique columns are involved with this row 1276 SWF[r][SWF_Member] = true; // and this row always starts pre-marked for membership 1277 SWF_Membership = true; // and thats how many I have 1278 for (int j = 0; j < SWF[r][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[r][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1279 for (int rr = r+1; rr < 9; rr++){if (SWF_Match(r,rr,2)){SWF[rr][SWF_Member] = true; // if this row can be added without exceeding 2 columns, mark it 1280 SWF_Membership++;}} // and increment membership number 1281 1282 if ((SWF_Membership == 2) && (BitsCount(SWF_Mask)==2)) {ClearR_SWF(n,"X-Wing" );} // check that we have 2 columns and 2 rows 1283 1284 } // end of row not having zero of this 'r' 1285 } // end of row 1286 } // end for this candidate number 1287 1288 1289 for (int n = 1; n <=9; n++){ // looking at each number 1290 for (int c = 0; c < 9; c++){Cand_Limited_C(c, n,2);} // build array on each COLUMN 1291 // Show_SWF(n); 1292 for (int c = 0; c < 9-1; c++){ // examine the column array 1293 if ((SWF[c][SWF_Number_Found] > 0) && (SWF[c][SWF_Number_Found] <=2) ){ 1294 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1295 SWF_Mask = 0; // will be used to mark what the active rows are 1296 SWF_Start = SWF[c][SWF_Number_Found]; // this is how many unique rows are involved with this column 1297 SWF[c][SWF_Member] = true; // and this row always starts pre-marked for membership 1298 SWF_Membership = true; // and thats how many I have 1299 for (int j = 0; j < SWF[c][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[c][SWF_Marks+j]);} // mark where the starting column's rows are 'x'ed 1300 for (int cc = c+1; cc < 9; cc++){if (SWF_Match(c,cc,2)){SWF[cc][SWF_Member] = true; // if this row can be added without exceeding 2 columns, mark it 1301 SWF_Membership++;}} // and increment membership number 1302 1303 if ((SWF_Membership == 2) && (BitsCount(SWF_Mask)==2)) {ClearC_SWF(n, "X-Wing");} // check that we have 2 columns and two rows 1304 1305 } // end of column not having zero of this 'c' 1306 } // end of column 1307 } // end for this candidate number 1308} // end of X_Wing 1309 1310void Swordfish(){ // swordfish algorithm 1311 for (int n = 1; n <=9; n++){ // looking at each number 1312 for (int r = 0; r < 9; r++){Cand_Limited_R(r, n,3);} // build array on each ROW 1313 // Show_SWF(n); 1314 for (int r = 0; r < 9-2; r++){ // examine the row array 1315 if ((SWF[r][SWF_Number_Found] > 0) && (SWF[r][SWF_Number_Found] <=3) ){ 1316 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1317 SWF_Mask = 0; // will be used to mark what the active columns are 1318 SWF_Start = SWF[r][SWF_Number_Found]; // this is how many unique columns are involved with this row 1319 SWF[r][SWF_Member] = true; // and this row always starts pre-marked for membership 1320 SWF_Membership = true; // and thats how many I have 1321 for (int j = 0; j < SWF[r][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[r][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1322 for (int rr = r+1; rr < 9; rr++){if (SWF_Match(r,rr,3)){SWF[rr][SWF_Member] = true; // if this row can be added without exceeding 3 columns, mark it 1323 SWF_Membership++;}} // and increment membership number 1324 1325 if ((SWF_Membership == 3) && (BitsCount(SWF_Mask)==3)) {ClearR_SWF(n, "Swordfish");} // check that we have 3 row/columns and 3 columns/rows 1326 1327 } // end of row not having zero of this 'r' 1328 } // end of row 1329 } // end for this candidate number 1330 1331 1332 for (int n = 1; n <=9; n++){ // looking at each number 1333 for (int c = 0; c < 9; c++){Cand_Limited_C(c, n,3);} // build array on each COLUMN 1334 // Show_SWF(n); 1335 for (int c = 0; c < 9-2; c++){ // examine the column array 1336 if ((SWF[c][SWF_Number_Found] > 0) && (SWF[c][SWF_Number_Found] <=3) ){ 1337 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1338 SWF_Mask = 0; // will be used to mark what the active rows are 1339 SWF_Start = SWF[c][SWF_Number_Found]; // this is how many unique rows are involved with this column 1340 SWF[c][SWF_Member] = true; // and this row always starts pre-marked for membership 1341 SWF_Membership = true; // and thats how many I have 1342 for (int j = 0; j < SWF[c][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[c][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1343 for (int cc = c+1; cc < 9; cc++){if (SWF_Match(c,cc,3)){SWF[cc][SWF_Member] = true; // if this row can be added without exceeding 3 columns, mark it 1344 SWF_Membership++;}} // and increment membership number 1345 1346 if ((SWF_Membership == 3) && (BitsCount(SWF_Mask)==3)) {ClearC_SWF(n, "Swordfish");} // check that we have 3 row/columns and 3 columns/rows 1347 1348 } // end of column not having zero of this 'c' 1349 } // end of column 1350 } // end for this candidate number 1351} // end of Swordfish 1352 1353void Jellyfish(){ // Jellyfish algorithm, same as Swordfish but 4 rows or columns 1354 for (int n = 1; n <=9; n++){ // looking at each number 1355 for (int r = 0; r < 9; r++){Cand_Limited_R(r, n,4);} // build array on each ROW 1356 for (int r = 0; r < 9-3; r++){ // examine the row array 1357 if ((SWF[r][SWF_Number_Found] > 0) && (SWF[r][SWF_Number_Found] <=4) ){ 1358 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1359 SWF_Mask = 0; // will be used to mark what the active columns are 1360 SWF_Start = SWF[r][SWF_Number_Found]; // this is how many unique columns are involved with this row 1361 SWF[r][SWF_Member] = true; // and this row always starts pre-marked for membership 1362 SWF_Membership = true; // and thats how many I have 1363 for (int j = 0; j < SWF[r][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[r][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1364 for (int rr = r+1; rr < 9; rr++){if (SWF_Match(r,rr,4)){SWF[rr][SWF_Member] = true; // if this row can be added without exceeding 4 columns, mark it 1365 SWF_Membership++;}} // and increment membership number 1366 1367 if ((SWF_Membership == 4) && (BitsCount(SWF_Mask) ==4)) {ClearR_SWF(n, "Jellyfish");} // check that we have 4 row/columns and 4 columns/rows 1368 1369 } // end of row not having zero of this 'r' 1370 } // end of row 1371 } // end for this candidate number 1372 1373 1374 for (int n = 1; n <=9; n++){ // looking at each number 1375 for (int c = 0; c < 9; c++){Cand_Limited_C(c, n,4);} // build array on each COLUMN 1376 for (int c = 0; c < 9-3; c++){ // examine the column array 1377 if ((SWF[c][SWF_Number_Found] > 0) && (SWF[c][SWF_Number_Found] <=4) ){ 1378 for (int i = 0; i <9; i++){SWF[i][SWF_Member] = false;} 1379 SWF_Mask = 0; // will be used to mark what the active rows are 1380 SWF_Start = SWF[c][SWF_Number_Found]; // this is how many unique rows are involved with this column 1381 SWF[c][SWF_Member] = true; // and this row always starts pre-marked for membership 1382 SWF_Membership = true; // and thats how many I have 1383 for (int j = 0; j < SWF[c][SWF_Number_Found]; j++){bitSet(SWF_Mask,SWF[c][SWF_Marks+j]);} // mark where the starting row's columns are 'x'ed 1384 for (int cc = c+1; cc < 9; cc++){if (SWF_Match(c,cc,4)){SWF[cc][SWF_Member] = true; // if this row can be added without exceeding 4 columns, mark it 1385 SWF_Membership++;}} // and increment membership number 1386 1387 if ((SWF_Membership == 4) && (BitsCount(SWF_Mask)==4)) {ClearC_SWF(n, "Jellyfish");} // check that we have 4 row/columns and 4 columns/rows 1388 1389 } // end of column not having zero of this 'c' 1390 } // end of column 1391 } // end for this candidate number 1392} // end of JellyFish 1393 1394void Cand_Limited_R(int r, int n, int My_Limit){ // check for a candidate appearing twice or thrice in row 1395 int counter = 0; 1396 SWF[r][SWF_Number_Found] = 0; 1397 for (int i = 0; i<My_Limit; i++){SWF[r][SWF_Marks+i] = 0;} 1398 for (int c = 0; c<9; c++){ 1399 if (If_Candidate_On(n,r, c)){ 1400 SWF[r][SWF_Number_Found]++; // number of rows found in 1401 if (SWF[r][SWF_Number_Found] <=My_Limit){SWF[r][SWF_Marks+counter] = c; // found in this column, up to limit (e.g., x-wing = 2, Swordfish=3, Jellyfish = 4) 1402 counter++; // and increment counter 1403 }}} 1404} // end of CandLimited_R 1405 1406void Cand_Limited_C(int c, int n,int My_Limit){ // check for a candidate appearing twice or thrice in column 1407 int counter = 0; 1408 SWF[c][SWF_Number_Found] = 0; 1409 for (int i = 0; i<My_Limit; i++){SWF[c][SWF_Marks+i] = 0;} 1410 for (int r = 0; r<9; r++){ 1411 if (If_Candidate_On(n,r, c)){ 1412 SWF[c][SWF_Number_Found]++; // number of columns found in 1413 if (SWF[c][SWF_Number_Found] <=My_Limit){SWF[c][SWF_Marks+counter] = r; // found in this row 1414 counter++; // and increment counter 1415 }}} 1416} // end of CandLimited_C 1417 1418bool SWF_Match(int Master,int Matcher, int My_Limit){ // look for matching position in marked cells 1419 int counter = 0; // so far no data 1420 BitMask = 0; // clear mask 1421 if (SWF[Matcher][SWF_Number_Found] == 0 || (Master==Matcher) || (SWF[Matcher][SWF_Number_Found] > My_Limit)){return false;} // if this is a blank row, or too many no sense in checking 1422 for (int j = 0; j < SWF[Matcher][SWF_Number_Found]; j++){ // run through columns for this row 1423 bitSet(BitMask, SWF[Matcher][SWF_Marks+j] ); // set a bit for each one found 1424 if (bitRead(SWF_Mask, SWF[Matcher][SWF_Marks+j]) == 0) {counter++;}} // if this is a 'new' column, add another to counter 1425 if (counter + SWF_Start <=My_Limit) { // if I'm still under the wire, return true 1426 SWF_Mask = SWF_Mask | BitMask; // keep tabs on # columns/rows over multiple matches 1427 return true;} 1428 return false; // if more than three, no good 1429 } // end of SWF_Match 1430 1431void XY_Chain(){ // XY Chain algorithm 1432 1433 Cand_Load_2(); // load all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2) 1434 for (int j = 0; j< RDC_Length; j++) { // try every cell in the that is in the 2 candidate list 1435 FC_Load(); // copy RD matrix to working force chain matrix 1436 FC_List_Clear(); // clear list of entries that have already been used as a 'successor' 1437 XY_Chain_R(j,FC_1, FC_Up); // follow all chains on first candidate 1438 1439 FC_List_Clear(); // reset list of entries that have already been used as a 'successor' 1440 XY_Chain_R(j,FC_2, FC_Down); // follow all chains on first candidate 1441 1442 for (int k = 0; k < RDC_Length; k++){ // check each entry in the chain list 1443 if (FC_UD[k][FC_1] == FC_UpandDown ) { // if there's both an 'up' mark and a 'down' mark, 1444 Candidate_Off(FC_CN[k][FC_1],FC_R[k],FC_C[k], "XY-Chain"); // clear the corresponding candidate -- got there through both sides of chain 1445 return;} // and return because all other search data is invalid (this entry is no longer part of 2-candidate list) 1446 else {if (FC_UD[k][FC_2] == FC_UpandDown ) { // and it saves time -- we may be back again if all easier algorithms fail 1447 Candidate_Off(FC_CN[k][FC_2],FC_R[k],FC_C[k], "XY-Chain"); 1448 return;}} 1449 } 1450 } // end of tried all 2 candidate cells 1451 return; // return after trying all 2-candidate entries as head-of-chain 1452 } // end of XY chain 1453 1454 void XY_Chain_R(int index, int LR, int My_Mark){ // follows chain and marks with up or down 1455 byte FC_Heads[FC_Max_Heads]; 1456 byte FC_LR[FC_Max_Heads]; 1457 byte FC_HP = 0; 1458 byte FC_LRN; 1459 1460 byte Next_Candidate = FC_CN[index][LR]; // get candidate 1 or 2 1461 if (LR == FC_1) {FC_LRN = FC_2;} else {FC_LRN = FC_1;} //flip it 1462 FC_UD[index][FC_LRN] = FC_UD[index][FC_LRN] | My_Mark; // mark the candidate 1463 1464 for (int i = 0; i< RDC_Length; i++){ 1465 if ((index !=i)&& (!FC_List[i]) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (Next_Candidate == FC_CN[i][FC_1] || Next_Candidate == FC_CN[i][FC_2]) ) // test to see if entry is a valid successor 1466 // as next link in chain 1467 { // increment head of chain pointer 1468 FC_Heads[FC_HP] = i; // store each possible successor 1469 FC_List[i] = true; // Mark FC_List for this entry so don't check successors multiple times 1470 if (Next_Candidate == FC_CN[i][FC_1]){FC_LR[FC_HP] = FC_2;} else {FC_LR[FC_HP] = FC_1;} // if match was on first candidate, use 2nd, elsewise use 1st 1471 1472 if (FC_HP < FC_Max_Heads-1) {FC_HP++;} 1473 } // end of "this IS a valid successor" 1474 } // end of trying all cells as a potential successor 1475 if (FC_HP == 0) {return; } // just return if no successors found 1476 1477 for (int i = 0; i < FC_HP; i++){ // if there ARE successors to this entry (next link in chain), 1478 XY_Chain_R(FC_Heads[i],FC_LR[i],My_Mark);} // follow onto each of them separately 1479 1480 } // end of XY_Chain_R 1481 1482void X_Chain(){ // single candidate forcing chain - X Chain 1483 for (int n = 1; n <= 9; n++){ // for each candidate n, ************** 1484 XC_Load(n); // find occurences 1485 for (int j = 0; j<FC_Ptr; j++){ // go through each cell with current n, 1486 if (If_Candidate_On(n,FC_R[j],FC_C[j])){ // make sure candidate wasn't cleared earlier 1487 XC_Link_Counter = 0; // reset link counter 1488 XC_Head_R = FC_R[j]; // unlike XY chain, we need to know where the head of the chain 1489 XC_Head_C = FC_C[j]; // was 1490 FC_Put_On_List(XC_Head_R,XC_Head_C); // don't double back on the head of the chain 1491 XC_Chain(j,n,Strong_Link); // jump off looking for strong links 1492 } // end of candidate is still in this cell 1493 } // end of all of single digit 1494 } // end of all 9 digits 1495} // end of X_Chain 1496 1497void XC_Chain(int index,int n, bool My_Mark){ // follows chain looking for strong and weak links 1498 1499 for (int i = 0; i< FC_Ptr; i++){ // look all through the list of cells with 'n' as candidate 1500 if ((index !=i)&& (!FC_On_List(FC_R[i],FC_C[i])) && (If_Candidate_On(n,FC_R[i],FC_C[i])) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (XC_Link(i,index,n,My_Mark)) ) // test to see if entry is a valid successor, either strong or weak 1501 // as next link in chain 1502 { 1503 // DDTv(" Candidate ",n,SL); 1504 // DDTsp(SL); 1505 // Show_RC(FC_R[index],FC_C[index],SL); 1506 // for (int xx = 1; xx <=XC_Link_Counter; xx++){DDTs(" ",SL);} 1507 // if (My_Mark) {DDTs(" Links Strong to ",SL);}else {DDTs(" Links weak to ",SL);} 1508 // Show_RC(FC_R[i],FC_C[i],NL); 1509 XC_Link_Counter++; // increment link counter 1510 FC_Put_On_List(FC_R[i],FC_C[i]); // add this cell to the 'used' list 1511 if (XC_Link_Counter == 3 || XC_Link_Counter == 5 || XC_Link_Counter == 7 || XC_Link_Counter == 9 || XC_Link_Counter == 11 || XC_Link_Counter == 13){ 1512 XC_Clear_Check(n,XC_Head_R,XC_Head_C,FC_R[i],FC_C[i]);} // clear candidate if we find appropriate entries (seen by both ends of chain) 1513 XC_Chain(i,n,(!My_Mark)); // follow onto each of them separately , alternating looking for strong and weak links 1514 XC_Link_Counter--; 1515 } // end of "this IS a valid successor" 1516 } // end of trying all cells as a potential successor 1517 return; // just return if no successors found 1518 } // end of XC Chain 1519 1520void XC_Clear_Check(int n, int r1, int c1, int r2, int c2){ //clear candidates from cells that see both end of chain 1521 byte r3; // target row and column 1522 byte c3; 1523 bool XC_Eligible; // eligible to be cleared 1524 1525 for (int i = 0; i< FC_Ptr; i++){ // we already know all cells with candidate n, so don't check others 1526 r3 = FC_R[i]; // retrieve row and column 1527 c3 = FC_C[i]; 1528 XC_Eligible = true; // assume we found one 1529 if ((r3 == r1 && c3 == c1) || (r3 == r2 && c3 == c2)) {XC_Eligible = false;} // but don't check the two endpoints of chain 1530 if (XC_Eligible){if ( !((In_Same_Unit(r1,c1,r3,c3)) && (In_Same_Unit(r2,c2,r3,c3)))){XC_Eligible = false;}} // not an endpoint 1531 if (XC_Eligible) {Candidate_Off(n,r3,c3, "X-Chain");} 1532 } 1533} // end of XC Clear Check 1534 1535bool XC_Link(int Dest_index,int Src_index, int n, bool My_Mark){ // validate as weak or strong link 1536 byte r1; // row and column and box of unit shared by both 1537 byte c1; // current (source) and successor (destination) 1538 byte r2; 1539 byte c2; 1540 byte b1; 1541 byte b2; 1542 byte counter = 0; // for strong links only current and successor should be in the shared unit 1543 1544 if (My_Mark == Weak_Link) {return true;} // we already tested 'in same unit' so either a strong or weak link works as a link 1545 // else we are looking for a strong link (See definitions in Sudoku_Puzzles.h) 1546 r1 = FC_R[Src_index]; // load rows and columns to simplify and reduce calculations for indexing 1547 c1 = FC_C[Src_index]; // have row and column of 'link from' and 'link to' 1548 r2 = FC_R[Dest_index]; 1549 c2 = FC_C[Dest_index]; 1550 b1 = In_Box(r1,c1); 1551 b2 = In_Box(r2,c2); 1552 1553 if (r1 == r2){ // are they in same row? 1554 counter = 0; // yes, reset counter of occurences 1555 for (int cc = 0; cc <9; cc++){ 1556 if (If_Candidate_On(n, r1, cc)){counter++;}} // count # times this candidate appears in row 1557 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 1558 1559 if (c1 == c2){ // are they in same column? 1560 counter = 0; // yes, reset counter of occurences 1561 for (int rr = 0; rr <9; rr++){ 1562 if (If_Candidate_On(n, rr, c1)){counter++;}} // count # times this candidate appears in column 1563 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 1564 1565 if (b1 == b2){ // are they in same box? 1566 counter = 0; // yes, reset counter of occurences 1567 for (int rr = Box_Coords[b1][Row_Pointer]; rr <= (Box_Coords[b1][Row_Pointer])+2; rr++){ //walk the box 1568 for (int cc = Box_Coords[b1][Column_Pointer]; cc <= (Box_Coords[b1][Column_Pointer])+2; cc++) {if (If_Candidate_On(n, rr, cc)) {counter++;}}} // count # times this candidate appears in the box 1569 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 1570 1571 return true; // passed all tests: everywhere link-from 'sees' link-to, there's only 2 of them 1572 1573 } // end of XC Link 1574 1575void XC_Load(int n){ // load all cells having candidates of 'n' into force chain array 1576 FC_Ptr = 0; // reset pointer 1577 for (int r = 0; r < 9; r++){ 1578 for (int c = 0; c < 9; c++){ 1579 if (If_Candidate_On(n,r,c)){ // for every cell having this candidate 1580 FC_R[FC_Ptr] = r; // store row and column indices 1581 FC_C[FC_Ptr] = c; 1582 FC_Ptr ++; }}} // and increment index 1583 1584} // end of XC Load 1585 1586void FC_Load(){ // converts RDC_Matrix to split candidates and 1587 for (int i = 0; i < RDC_Length; i++){ // only works for bi-value cells 1588 FC_R[i] = RDC_Matrix[i][Row_Pointer]; // store r and c 1589 FC_C[i] = RDC_Matrix[i][Column_Pointer]; 1590 FC_CN[i][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; 1591 FC_CN[i][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 1592 FC_UD[i][FC_1]=0; // and resets the up/down bit flags 1593 FC_UD[i][FC_2]=0;} 1594 1595} // end of FC Load 1596 1597void FC_List_Clear(){for (int k=0; k<RDC_Max_Size;k++){FC_List[k] = false;}} // clear list of successors to current cell 1598 1599void FC_Put_On_List(byte r, byte c){ // store entries on 'used' cell list to prevent doubling back and looping 1600 FC_Used_List[XC_Link_Counter] = (10*r)+c; // store number from 00 to 88 (A1 to I9) 1601} // end of FC_Put_On_List 1602 1603void FC_Put_Node_On_List(byte r0, byte c0, byte r1, byte c1){ // store 2nd cell in node as 'used' cell list as well as first one 1604 int T0; 1605 int High_Part = ( (1000*r1)+(100*c1) ); 1606 int Low_Part = ((10*r0)+c0); 1607 if (High_Part < Low_Part){ // arrange for 'bigger' number to be first 1608 T0 = Low_Part; 1609 Low_Part = High_Part; 1610 High_Part = T0;} 1611 FC_Used_List[XC_Link_Counter] = High_Part + Low_Part; // store number from 00 to 88 (A1 to I9) shift over by 10 (i.e., use 100's and 1000's) 1612} // end of FC_Put_On_List 1613 1614 1615bool FC_On_List(int r, int c){ // check to see if cell[r][c] has been registered in the used array 1616 int Search_First; 1617 int Search_Second; 1618 int Search_Hash = (10*r)+c; // first r,c, are stored as 10*r+c, the possible second is 1000*r+100*c 1619 for (int i = 0; i <= XC_Link_Counter; i++){ 1620 Search_First = (FC_Used_List[i])%100; // lower half of possible two row and column values 1621 Search_Second = (FC_Used_List[i])/100; 1622 if (Search_Hash == Search_First) {return true;} // if it matches either 'lower' or 'upper' value 1623 if ( (Search_Second > 0) && (Search_Hash == Search_Second) ) { return true;} 1624 } 1625 return false; 1626} // end of FC_On_List 1627 1628void Forbidding_Chain(){ // single candidate forcing chain - Forbidding Chain 1629 // Show_Candidates(); 1630 for (int n = 1; n <= 9; n++){ // for each candidate n, 1631 XC_Load(n); // find occurences 1632 for (int j = 0; j<FC_Ptr; j++){ // go through each cell with current n, 1633 if (If_Candidate_On(n,FC_R[j],FC_C[j])){ // make sure candidate wasn't cleared earlier 1634 XC_Link_Counter = 0; // reset link counter 1635 XC_Head_R = FC_R[j]; // unlike XY chain, we need to know where the head of the chain 1636 XC_Head_C = FC_C[j]; // was 1637 FC_Put_On_List(XC_Head_R,XC_Head_C); // register head so dont double back and loop 1638 FC_List[XC_Link_Counter] = Weak_Link; 1639 // DDTv("Starting candidate ",n,SL); 1640 // DDTs(" with head ***************",SL); 1641 // Show_RC(XC_Head_R,XC_Head_C,NL); 1642 FBC_Chain(j,n,Weak_Link); // jump off looking for weak links 1643 } // end of candidate is still in this cell 1644 } // end of all of single digit 1645 } // end of all 9 digits 1646} // end of Forbidding Chain 1647 1648void FBC_Chain(int index,int n, bool My_Mark){ // follows chain looking for strong and weak links, (X chain with group nodes) 1649 1650 for (int i = 0; i< FC_Ptr; i++){ // look all through the list of cells with 'n' as candidate 1651 if ((index !=i)&& (Same_Cell(FC_R[i],FC_C[i],XC_Head_R,XC_Head_C)) && (If_Candidate_On(n,FC_R[i],FC_C[i])) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (FBC_Link(i,index,n,My_Mark)) &&(XC_Link_Counter >=2 )&&(My_Mark == Weak_Link) ){ 1652// DDTs("Found Head of Chain ***************** **********************************",SL); 1653// Show_RC(FC_R[i],FC_C[i],NL); 1654 // for (int x = 1; x<= XC_Link_Counter; x++){ 1655 // if (FC_List[x]){DDTs(" Linked Strong to ",SL); } else {DDTs(" Linked Weak to ",SL); } 1656 // Show_RC_Node(x); 1657 // } 1658 // if (FC_List[0]){DDTs(" Linked Strong to ",SL); } else {DDTs(" Linked Weak to ",SL); } 1659 // Show_RC_Node(0); 1660 Candidate_Off(n,XC_Head_R,XC_Head_C, "Forbidding"); 1661 return; 1662 } 1663 1664 if ((index !=i)&& (!FC_On_List(FC_R[i],FC_C[i])) && (If_Candidate_On(n,FC_R[i],FC_C[i])) && (In_Same_Unit(FC_R[index],FC_C[index],FC_R[i],FC_C[i])) && (FBC_Link(i,index,n,My_Mark)) ) // test to see if entry is a valid successor, either strong or weak 1665 // as next link in chain 1666 { 1667 // DDTv(" Candidate ",n,SL); 1668 // DDTsp(SL); 1669 // Show_RC(FC_R[index],FC_C[index],SL); 1670 // for (int xx = 1; xx <=XC_Link_Counter; xx++){DDTs(" ",SL);} 1671 // if (My_Mark) {DDTs(" Links Strong to ",SL);}else {DDTs(" Links weak to ",SL);} 1672 // Show_RC(FC_R[i],FC_C[i],NL); 1673 XC_Link_Counter++; // increment link counter 1674 FC_Put_On_List(FC_R[i],FC_C[i]); // add this cell to the 'used' list 1675 FC_List[XC_Link_Counter] = My_Mark; // store currrent Strong_Link or Weak_Link for debugging/display 1676 FBC_Chain(i,n,(!My_Mark)); // follow onto each of them separately , alternating looking for strong and weak links 1677 XC_Link_Counter--; 1678 } // end of "this IS a valid successor" 1679 } // end of trying all cells as a potential successor 1680 return; //upon return, pass it along 1681 } // end of XC Chain 1682 1683bool FBC_Link(int Dest_index,int Src_index, int n, bool My_Mark){ // validate as weak or strong link 1684 byte r1; // row and column and box of unit shared by both 1685 byte c1; // current (source) and successor (destination) 1686 byte r2; byte c2; 1687 byte b1; byte b2; 1688 byte r3; byte c3; 1689 byte counter = 0; // for strong links only current and successor should be in the shared unit 1690 bool Link_is_Strong = true; // assume a strong link if found 1691 1692 if (My_Mark == Weak_Link) {return true;} // we already tested 'in same unit' so either a strong or weak link works as a link 1693 // else we are looking for a strong link (See definitions in Sudoku_Puzzles.h) 1694 r1 = FC_R[Src_index]; // load rows and columns to simplify and reduce calculations for indexing 1695 c1 = FC_C[Src_index]; // have row and column of 'link from' and 'link to' 1696 r2 = FC_R[Dest_index]; 1697 c2 = FC_C[Dest_index]; 1698 b1 = In_Box(r1,c1); // Source box 1699 b2 = In_Box(r2,c2); // Destination box 1700 byte scounter; byte dcounter; byte ncounter; 1701 byte NR1; byte NC1; byte NR2; byte NC2; 1702 byte r0 = ((FC_Used_List[XC_Link_Counter-1])%100)/10; // Predecessor row and column 1703 byte c0 = ((FC_Used_List[XC_Link_Counter-1])%100)%10; 1704 1705 1706 // DDTs("FB Link Checking ",SL); 1707// Show_RC(r1,c1,SL); 1708// DDTs(" and ",SL); 1709// Show_RC(r2,c2,SL); 1710// DDTv(" Box 1 is ",b1,SL); 1711// DDTv(" Box 2 is ",b2,SL); 1712// if (My_Mark == Strong_Link){DDTs(" Looking for Strong Link",NL);} else {DDTs(" Looking for Weak Link",NL);} 1713 1714 1715 if (b1 == b2){ // are they in same box? 1716 counter = 0; // yes, reset counter of occurences 1717 for (int rr = Box_Coords[b1][Row_Pointer]; rr <= (Box_Coords[b1][Row_Pointer])+2; rr++){ //walk the box 1718 for (int cc = Box_Coords[b1][Column_Pointer]; cc <= (Box_Coords[b1][Column_Pointer])+2; cc++) {if (If_Candidate_On(n, rr, cc)) {counter++;}}} // count # times this candidate appears in the box 1719 if (counter !=2) {Link_is_Strong = false;}} // if not 2 (link from and to) then its not a strong link 1720 1721 if ((r1 == r2) && (b1 != b2)){ // are they in same row and different boxes? 1722 counter = 0; // yes, reset counter of occurences 1723 for (int cc = 0; cc <9; cc++){ 1724 if (If_Candidate_On(n, r1, cc)){counter++;}} // count # times this candidate appears in row 1725 if (counter == 2){Link_is_Strong = true;} // simplest case, only two in the row 1726 else {Link_is_Strong = false;} 1727 if (!Link_is_Strong){ // if failed simple 'strong' test, 1728 Link_is_Strong = true; // reset to 'true' for beyond simple test (i.e., there's more than one in each of source and destination) 1729 ncounter = 0; 1730 scounter = 0; 1731 dcounter = 0; 1732 for (int cc = 0; cc <9; cc++){ 1733 if ( (In_Box(r1,cc) != b1) && (In_Box(r1,cc) != b2) && (If_Candidate_On(n, r1, cc)) ){ncounter++;} // count any n's outside of source and destination boxes and in this row 1734 if ( (In_Box(r1,cc) == b1) && (If_Candidate_On(n, r1, cc)) ){scounter++; // count the n's inside source box and row 1735 if (scounter == 1){NR1 = r1; 1736 NC1 = cc;} 1737 if (scounter == 2){NR2 = r1; 1738 NC2 = cc;}} 1739 if ( (In_Box(r1,cc) == b2) && (If_Candidate_On(n, r1, cc)) ){dcounter++;}} 1740 // count the n's inside destination box and row and test for <=2 scounter, 0 in row between and 1 for destination box's row 1741 if ( (ncounter > 0) || (scounter > 2) || (dcounter > 1) ) {Link_is_Strong = false;} // due to limitations, only two source cells in node, and one cell in destination 1742 if ( FC_On_List(NR1,NC1) && (FC_On_List(NR2,NC2)) ) {Link_is_Strong = false;} // if both potential cells in the node are 'used', can't do it 1743 if ( !(In_Same_Unit(NR1,NC1,r0,c0)) || !(In_Same_Unit(NR2,NC2,r0,c0)) ){Link_is_Strong = false;} 1744 if (Link_is_Strong){ 1745 // if (FC_On_List(NR1,NC1)) {DDTs("First cell is on the list",NL);} 1746 // if (FC_On_List(NR2,NC2)) {DDTs("Second cell is on the list",NL);} 1747 FC_Put_Node_On_List(NR1,NC1,NR2,NC2); 1748 // DDTs("Created Row Node ",SL); 1749 // Show_RC(NR1,NC1,SL); 1750 // DDTs(" and ",SL); 1751 // Show_RC(NR2,NC2,SL); 1752 // DDTv(" Stored as ",FC_Used_List[XC_Link_Counter],NL); 1753 } 1754 1755 } // end of failed simple test 1756 } // end this is a 'row' search 1757 1758 if ((c1 == c2) && (b1 != b2)){ // are they in same column and different boxes? 1759 counter = 0; // yes, reset counter of occurences 1760 for (int rr = 0; rr <9; rr++){ 1761 if (If_Candidate_On(n, rr, c1)){counter++;}} // count # times this candidate appears in row 1762 if (counter == 2){Link_is_Strong = true;} // simplest case, only two in the row 1763 else {Link_is_Strong = false;} 1764 if (!Link_is_Strong){ // if failed simple 'strong' test, 1765 Link_is_Strong = true; // reset to 'true' for beyond simple test (i.e., there's more than one in each of source and destination) 1766 ncounter = 0; 1767 scounter = 0; 1768 dcounter = 0; 1769 for (int rr = 0; rr <9; rr++){ 1770 if ( (In_Box(rr,c1) != b1) && (In_Box(rr,c1) != b2) && (If_Candidate_On(n, rr, c1)) ){ncounter++;} // count any n's outside of source and destination boxes and in this column 1771 if ( (In_Box(rr,c1) == b1) && (If_Candidate_On(n, rr, c1)) ){ 1772 scounter++; 1773 if (scounter == 1){NR1 = rr; 1774 NC1 = c1;} 1775 if (scounter == 2){NR2 = rr; 1776 NC2 = c1;}} // count the n's inside source box and column 1777 if ( (In_Box(rr,c1) == b2) && (If_Candidate_On(n, rr, c1)) ){dcounter++;}} // count the n's inside destination box and columnn and test for <=2 scounter, 0 in column between and 1 for destination box's column 1778 1779 if ( (ncounter > 0) || (scounter > 2) || (dcounter > 1)) {Link_is_Strong = false;} // due to limitations, only two source cells in node, and one cell in destination 1780 if ( FC_On_List(NR1,NC1) && (FC_On_List(NR2,NC2)) ) {Link_is_Strong = false;} // if both potential cells in the node are 'used', can't do it 1781 if ( !(In_Same_Unit(NR1,NC1,r0,c0)) || !(In_Same_Unit(NR2,NC2,r0,c0)) ){Link_is_Strong = false;} 1782 if (Link_is_Strong){ 1783 // if (FC_On_List(NR1,NC1)) {DDTs("First cell is on the list",NL);} 1784 // if (FC_On_List(NR2,NC2)) {DDTs("Second cell is on the list",NL);} 1785 FC_Put_Node_On_List(NR1,NC1,NR2,NC2); 1786 // DDTs("Created Column Node ",SL); 1787 // Show_RC(NR1,NC1,SL); 1788 // DDTs(" and ",SL); 1789 // Show_RC(NR2,NC2,SL); 1790 // DDTv(" Stored as ",FC_Used_List[XC_Link_Counter],NL); 1791 } 1792 } // end of failed simple test 1793 } // end this is a 'column' search 1794 1795 1796 if ((My_Mark == Strong_Link) && Link_is_Strong ) {return true;} // passed all tests: everywhere link-from 'sees' link-to, there's only 2 of them 1797 if ((My_Mark == Strong_Link) && !Link_is_Strong ) {return false;} 1798 if ((My_Mark == Weak_Link) && Link_is_Strong) {return false;} 1799 if ((My_Mark == Weak_Link) && !Link_is_Strong) {return true;} 1800 } // end of FBC Link 1801 //*************** 1802 // algorithm for Unique Rectangle, types 1,2,and 4 1803void Unique_Rectangle(){ // UR type one is three 2-candidates cells found and one implied, e.g. 1804 // {3,4} {3,4} {3,4} and {3,4,7} 1805 bool Rectangular; // conforms to all rules for UR 1806 bool UR_Eligible; // eligible by UR 2 to be cleared (Protects corner cells, etc.) 1807 const int UR_First = 0; // constant index to first candidate 1808 byte UR_Candidate; // third candidate 1809 byte UR_R; // row of 'open' corner of rectangle 1810 byte UR_C; // column of open corner 1811 byte UR3_R; // row and column of found locked candidate cell 1812 byte UR3_C; 1813 byte UR3_CC; // make sure there's only one found 1814 byte UR3_PB1; // first pattern breaker candidate 1815 byte UR3_PB2; // second pattern breaker candidate 1816 bool UR_Mark_1; 1817 bool UR_Mark_2; 1818 byte UR5_CC; // corner counter for UR type 5 1819 byte UR5_CC3; // three candidate counter for UR type 5 1820 int UR5_Row_Counter; //counts rows involved in UR type 5 1821 int UR5_Column_Counter; //counts columns involved in UR type 5 1822 byte UR5_Candidate; // extra candidate 1823 1824 Cand_Load_2(); // load all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2) 1825 for (int i=0; i <RDC_Length-1; i++){ // check each entry in the array ************** -1????? 1826 FC_Ptr = UR_First; // reset pointer 1827 UR_R = FC_Flag; // and flag row and column pointers 1828 UR_C = FC_Flag; 1829 FC_R[FC_Ptr] = RDC_Matrix[i][Row_Pointer]; // store r and c 1830 FC_C[FC_Ptr] = RDC_Matrix[i][Column_Pointer]; 1831 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; // store candidates 1832 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 1833 FC_Ptr++; // increment pointer 1834 for (int j = i+1; j < RDC_Length; j++){ // check rest of array now 1835 if ( (FC_CN[UR_First][FC_1] == RDC_Matrix[j][RDC_CurrCan]/10) && (FC_CN[ UR_First][FC_2] == RDC_Matrix[j][RDC_CurrCan]%10) ){ //if same candidates, store as well 1836 FC_R[FC_Ptr] = RDC_Matrix[j][Row_Pointer]; // store r and c 1837 FC_C[FC_Ptr] = RDC_Matrix[j][Column_Pointer]; 1838 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[j][RDC_CurrCan]/10; // store candidates 1839 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[j][RDC_CurrCan]%10; // note: not used except for Show_FC and diagnostics 1840 Rectangular = false; // assume it doesn't meet rectangular criteria 1841 if ((FC_R[FC_Ptr] == FC_R[UR_First]) &&(FC_C[FC_Ptr] != FC_C[UR_First])){ // if rows match and columns don't 1842 UR_C = FC_C[FC_Ptr]; // this will be the 'open' column 1843 Rectangular = true; } // and we have a rectangle 1844 if ((FC_C[FC_Ptr] == FC_C[UR_First]) &&(FC_R[FC_Ptr] != FC_R[UR_First])){ // if Columns match and row don't 1845 UR_R = FC_R[FC_Ptr]; // this will be the 'open' row 1846 Rectangular = true; } // and we have a rectangle 1847 if(Rectangular) {FC_Ptr++;} // if this is a valid corner, increment 1848 } // end of same candidates 1849 } // end of checking through end of array 1850 1851 if (FC_Ptr == 4){ // if we have four corners with same two candidates in two boxes, 1852 BitMask = 0; // clear bit mask to count boxes involved 1853 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,In_Box(FC_R[k],FC_C[k])); } // turn on a bit for each box involved 1854 if (BitsCount(BitMask) == 2){ // count them, need two and only two 1855 No_Solution = true; // this is not a valid puzzle due to Deadly Pattern 1856 DDTs("Deadly Pattern Anchored At ",SL); 1857 Show_RC(FC_R[UR_First], FC_C[UR_First],NL); 1858 Show_Grid(); // and final puzzle grid 1859 T_Msg3("Puzzle","Deadly", "Pattern"); 1860 T_Scroll_Message("Deadly Pattern"); 1861 T_Scroll_Message("Puzzle with"); 1862 T_Scroll_Message("Multiple Solutions"); 1863 return; }} // end of rectangular and 4 corners 1864 1865 if (FC_Ptr == 3){ // if we have three corners, 1866 BitMask = 0; // clear bit mask to count boxes involved 1867 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,In_Box(FC_R[k],FC_C[k])); } // turn on a bit for each box involved 1868 if (BitsCount(BitMask) == 2){ // count them, need two and only two 1869 Candidate_Off(FC_CN[UR_First][FC_1],UR_R, UR_C, "Uniq Rect #1"); // if so, candidate 1870 Candidate_Off(FC_CN[UR_First][FC_2],UR_R, UR_C, "Uniq Rect #1"); 1871 }} // end of rectangular AND 3 corners 1872 1873 // UR type two is two 2-candidates cells found and two more corners with same two candidates PLUS one other, matching candidate (e.g.) 1874 // {3,4} {3,4} and {3,4,7} {3,4,7} // note *********** later enhancement: could be {3,4} {3,4} and {3,4,7} {3,4,8} 1875 // and that also complicates non-diagonal 1876 if (FC_Ptr == 2){ // we found two only 1877 if (UR_C == FC_Flag){ // if row is known and column not, sweep columns within known row 1878 for (int c = 0; c < 9; c++){ // look at each column 1879 for (int j = 0; j <=1; j++){ 1880 if ( (c != FC_C[UR_First]) && (Candidates_Count[FC_R[j]][c] ==3) ){ // ignore the cell we are in & and any cell with not 3 candidates 1881 if ( ( If_Candidate_On(FC_CN[UR_First][FC_1],FC_R[j],c)) && ( If_Candidate_On(FC_CN[UR_First][FC_2],FC_R[j],c)) ){ //if this 3 candidate cell contains the two core candidates, 1882 FC_R[FC_Ptr] = FC_R[j]; // store r and c 1883 FC_C[FC_Ptr] = c; 1884 FC_Ptr++;}}} // if this is a valid corner, increment 1885 }} // end of column search with known row 1886 else 1887 { if (UR_R == FC_Flag){ // if row is known and column not, sweep columns within known row 1888 for (int r = 0; r < 9; r++){ // look at each column 1889 for (int j = 0; j <=1; j++){ 1890 if ( (r != FC_R[UR_First]) && (Candidates_Count[r][FC_C[j]] ==3) ){ // ignore the cell we are in & and any cell with not 3 candidates (may only need last test!) 1891 if ( ( If_Candidate_On(FC_CN[UR_First][FC_1],r,FC_C[j])) && ( If_Candidate_On(FC_CN[UR_First][FC_2],r,FC_C[j])) ){ //if this 3 candidate cell contains the two core candidates, 1892 FC_R[FC_Ptr] = r; // store r and c 1893 FC_C[FC_Ptr] = FC_C[j]; 1894 FC_Ptr++;}}} // if this is a valid corner, increment 1895 }}} // end of row search with known column 1896 Rectangular = true; // assume its going to be a match 1897 if (FC_Ptr != 4){Rectangular = false;} // we should have four corners now 1898 BitMask = 0; // clear bit mask to count boxes involved 1899 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,In_Box(FC_R[k],FC_C[k])); } // turn on a bit for each box involved 1900 if (BitsCount(BitMask) != 2){Rectangular = false;} // count them, need two and only two 1901 BitMask = 0; // clear bit mask to count boxes involved 1902 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,FC_R[k]); } // turn on a bit for each row involved 1903 if (BitsCount(BitMask) != 2){Rectangular = false;} // count them, need two and only two 1904 BitMask = 0; // clear bit mask to count boxes involved 1905 for (int k = 0; k < FC_Ptr; k++){bitSet(BitMask,FC_C[k]); } // turn on a bit for each column involved 1906 if (BitsCount(BitMask) != 2){Rectangular = false;} // count them, need two and only two 1907 if (Candidates[FC_R[UR_First+2]][FC_C[UR_First+2]] != Candidates[FC_R[UR_First+3]][FC_C[UR_First+3]]){Rectangular = false;} // if the three candidates in two new cells not equal, not a match 1908 if (Rectangular){ // if still 'true' then its a good-to-go Unique Rectangle Type 2 1909 BitMask = Candidates[FC_R[UR_First+2]][FC_C[UR_First+2]] ^ Candidates[FC_R[UR_First]][FC_C[UR_First]]; // use XOR to clear out shared candidates and leave the unique one 1910 UR_Candidate = BitsWhich(BitMask); // get candidate number from bit position 1911 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 1912 for (int cc = 0; cc < 9; cc++){ 1913 UR_Eligible = true; // assume its not a protected entry 1914 for (int j = 0; j < 4; j++){if (rr == FC_R[j] && cc == FC_C[j]){UR_Eligible = false;}} // don't examine the four cells in the unique rectangle 1915 if (!In_Same_Unit(rr,cc,FC_R[2],FC_C[2]) || !In_Same_Unit(rr,cc,FC_R[3],FC_C[3])){UR_Eligible = false;} // must be in same unit (i.e."seeable") by both new corners 1916 if (UR_Eligible){Candidate_Off(UR_Candidate,rr, cc, "Uniq Rect #2");}}} // if eligible, clear it 1917 } // end of found a rectangular UR type 2 1918 // Look for a Unique Rectangle Type 4 1919 if (Rectangular){ // Look only if a valid rectangle came out of Type 2 1920 UR_Mark_1 = false; // assume search for this candidate in rest of house fails 1921 UR_Mark_2 = false; // and this one too 1922 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 1923 for (int cc = 0; cc < 9; cc++){ 1924 UR_Eligible = true; // assume its not a protected entry 1925 for (int j = 0; j < 4; j++){if (rr == FC_R[j] && cc == FC_C[j]){UR_Eligible = false;}} // protect the four corners again 1926 if (In_Same_Unit(rr,cc,FC_R[2],FC_C[2]) && In_Same_Unit(rr,cc,FC_R[3],FC_C[3]) && UR_Eligible ){ // same house as both non-diagonal corners? 1927 if (If_Candidate_On(FC_CN[UR_First][FC_1],rr,cc)){UR_Mark_1 = true;} // have found another instance of candidate 1 in same house 1928 if (If_Candidate_On(FC_CN[UR_First][FC_2],rr,cc)){UR_Mark_2 = true;} // have found another instance of candidate 2 in same house 1929 } // end of "this cell is in same unit as both 3-candidate non-diagnonal corners and isn't one of our existing corner-cells" 1930 }} // end of checking all cells for candidates exist in other cells in house 1931 1932 if (!UR_Mark_1){Candidate_Off(FC_CN[UR_First][FC_2],FC_R[2],FC_C[2], "Uniq Rect #4");} // if first candidate not found elsewhere in house, clear 2nd candidate 1933 if (!UR_Mark_1){Candidate_Off(FC_CN[UR_First][FC_2],FC_R[3],FC_C[3], "Uniq Rect #4");} // from both non-diagonal cells with extra candidates 1934 if (!UR_Mark_2){Candidate_Off(FC_CN[UR_First][FC_1],FC_R[2],FC_C[2], "Uniq Rect #4");} // if first candidate not found elsewhere in house, clear 2nd candidate 1935 if (!UR_Mark_2){Candidate_Off(FC_CN[UR_First][FC_1],FC_R[3],FC_C[3], "Uniq Rect #4");} // from both non-diagonal cells with extra candidates 1936 } 1937 1938 } // end of rectangle and 2 corners 1939 1940 } // end of checking each 'first' corner 1941 1942 // UR Type 5 1943 1944 Cand_Load_2(); // reload all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2 1945 for (int i = 0; i< RDC_Length; i++){ // use each 2-candidate cell as anchor 1946 FC_Ptr = UR_First; // reset pointer 1947 FC_R[FC_Ptr] = RDC_Matrix[i][Row_Pointer]; // store r and c 1948 FC_C[FC_Ptr] = RDC_Matrix[i][Column_Pointer]; 1949 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; // store candidates 1950 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 1951 FC_UD[FC_Ptr][FC_1] = FC_UpandDown; // mark candidates 1952 1953 FC_Ptr++; 1954 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 1955 for (int cc = 0; cc < 9; cc++){ 1956 if ( (rr != FC_R[UR_First]) || (cc != FC_C[UR_First]) ){ 1957 if ( ((Candidates_Count[rr][cc] == 2) || (Candidates_Count[rr][cc] == 3)) && (If_Candidate_On(FC_CN[UR_First][FC_1],rr,cc)) && (If_Candidate_On(FC_CN[UR_First][FC_2],rr,cc)) ){ // if 2 or 3 candidates and 2 are same as anchor 1958 FC_R[FC_Ptr] = rr; // store r and c 1959 FC_C[FC_Ptr] = cc; 1960 FC_UD[FC_Ptr][FC_1] = 0; // clear the Up/Down flag, it will be used later 1961 FC_Ptr++;}}}} 1962 if (FC_Ptr >= 4) { 1963 for (int j=UR_First; j< FC_Ptr; j++){ // look at every entry that could be a sub-anchor to the rectangle 1964 for (int k= UR_First; k< FC_Ptr; k++){ // examine every cell 1965 if (k != j){ // don't check against yourself 1966 if (FC_R[k] == FC_R[j]) {FC_UD[j][FC_1] = FC_UD[j][FC_1] | FC_Up;} // if I am same row, UD with flag 1967 if (FC_C[k] == FC_C[j]) {FC_UD[j][FC_1] = FC_UD[j][FC_1] | FC_Down;}}}} // if I am same column, mark entry with 2nd flag 1968 UR5_CC = 0; // reset 'corner counter' 1969 UR5_CC3 = 0; // count how many of the cells have three candidates 1970 UR5_Row_Counter = 0; // and counters of row and columns involved 1971 UR5_Column_Counter = 0; 1972 for (int j=UR_First; j< FC_Ptr; j++){ 1973 if (FC_UD[j][FC_1] == FC_UpandDown){ 1974 UR5_CC++; // count corners 1975 if (Candidates_Count[FC_R[j]][FC_C[j]] == 3) {UR5_CC3++;} // and count corners with extra candidate (has to be 2 or 3) 1976 bitSet(UR5_Row_Counter,FC_R[j]); // and rows and 1977 bitSet(UR5_Column_Counter,FC_C[j]);}} // columns 1978 Rectangular = true; // assume we have a valid UR 1979 if (UR5_CC != 4 || BitsCount(UR5_Row_Counter)!=2 || BitsCount(UR5_Column_Counter)!=2 ){ Rectangular = false;} // if doesnt have four corners comprised of 2 Rows & 2 Columns, its not a UR 5 1980 if (UR5_CC3 <2 || UR5_CC3 >3){ Rectangular = false;} // must have two or three corners with the three candidates 1981 if (UR5_CC3 == 2){ // if its only two corners, ensure they are diagonal 1982 RR_CC_Ptr = 0; // diagonal corner counter 1983 for (int j=UR_First; j< UR5_CC; j++){if (Candidates_Count[FC_R[j]][FC_C[j]] == 3){ // for both corners that have 3 candidates store r,c 1984 RR_CC[RR_CC_Ptr][Row_Pointer] = FC_R[j]; 1985 RR_CC[RR_CC_Ptr][Column_Pointer] = FC_C[j]; 1986 RR_CC_Ptr++;}} 1987 if (RR_CC[0][Row_Pointer] == RR_CC[1][Row_Pointer] || RR_CC[0][Column_Pointer] == RR_CC[1][Column_Pointer]) { 1988 Rectangular = false;} 1989 } 1990 BitMask = 0; 1991 bitSet(BitMask, FC_CN[UR_First][FC_1]); // set bits for both anchor candidates 1992 bitSet(BitMask, FC_CN[UR_First][FC_2]); 1993 for (int j=UR_First; j< FC_Ptr; j++){if (FC_UD[j][FC_1] == FC_UpandDown) {BitMask = BitMask | Candidates[FC_R[j]][FC_C[j]];}} // Logical OR against all the candidates in all the corner entries 1994 if (BitsCount(BitMask) != 3){Rectangular = false;} // Can only have the two anchors plus one extra candidate in all the cells 1995 bitClear(BitMask, FC_CN[UR_First][FC_1]); // clear both anchor candidates 1996 bitClear(BitMask, FC_CN[UR_First][FC_2]); 1997 UR5_Candidate = BitsWhich(BitMask); 1998 if (Rectangular) { 1999 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 2000 for (int cc = 0; cc < 9; cc++){ 2001 UR_Eligible = true; // assume its going to be eligible 2002 for (int i = 0; i < FC_Ptr; i++){ 2003 if ( (rr==FC_R[i]) && (cc==FC_C[i]) ){UR_Eligible = false;} // false if its one our corners 2004 if ( Candidates_Count[FC_R[i]][FC_C[i]]==3) { 2005 if (FC_UD[i][FC_1] == FC_UpandDown) { 2006 if (!In_Same_Unit(rr,cc,FC_R[i],FC_C[i])) { 2007 UR_Eligible = false;}}} // false if its not in the same house as all diagonal (2 or 3) 2008 } // end of checking cell against all in the FC array 2009 if(UR_Eligible){ 2010 // Show_Candidates(); 2011 // Show_FC(FC_Ptr); 2012 // DDTv("candidate ",UR5_Candidate,NL); 2013 Candidate_Off(UR5_Candidate,rr,cc,"Uniq Rect #5");}}} // clear it if it passed all tests 2014 } // end of clearing the extra candidate from all cells that can see ALL the diagonal cells that have that candidate IF it was 'rectangular' 2015 } // end of at least four cells 2016 2017 } // end of checking all candidates as anchor for UR 5 2018 2019 // UR 3 -- 2020 2021 Cand_Load_2(); // reload all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2 2022 for (int i=0; i <RDC_Length-1; i++){ // check each entry in the array ************** -1????? 2023 FC_Ptr = UR_First; // reset pointer 2024 UR_R = FC_Flag; // and flag row and column pointers 2025 UR_C = FC_Flag; 2026 FC_R[FC_Ptr] = RDC_Matrix[i][Row_Pointer]; // store r and c 2027 FC_C[FC_Ptr] = RDC_Matrix[i][Column_Pointer]; 2028 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[i][RDC_CurrCan]/10; // store candidates 2029 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[i][RDC_CurrCan]%10; 2030 FC_Ptr++; // increment pointer 2031 for (int j = i+1; j < RDC_Length; j++){ // check rest of array now 2032 if ( (FC_CN[UR_First][FC_1] == RDC_Matrix[j][RDC_CurrCan]/10) && (FC_CN[ UR_First][FC_2] == RDC_Matrix[j][RDC_CurrCan]%10) ){ //if same candidates, store as well 2033 FC_R[FC_Ptr] = RDC_Matrix[j][Row_Pointer]; // store r and c 2034 FC_C[FC_Ptr] = RDC_Matrix[j][Column_Pointer]; 2035 FC_CN[FC_Ptr][FC_1] = RDC_Matrix[j][RDC_CurrCan]/10; // store candidates 2036 FC_CN[FC_Ptr][FC_2] = RDC_Matrix[j][RDC_CurrCan]%10; // note: not used except for Show_FC and diagnostics 2037 Rectangular = false; // assume it doesn't meet rectangular criteria 2038 if ((FC_R[FC_Ptr] == FC_R[UR_First]) &&(FC_C[FC_Ptr] != FC_C[UR_First])){ // if rows match and columns don't 2039 UR_C = FC_C[FC_Ptr]; // Mark that we have a 'row match' by setting 'multiple columns' 2040 Rectangular = true; } // and we have a rectangle 2041 if ((FC_C[FC_Ptr] == FC_C[UR_First]) &&(FC_R[FC_Ptr] != FC_R[UR_First])){ // if Columns match and row don't 2042 UR_R = FC_R[FC_Ptr]; // Mark that we have a 'column match' by setting 'multiple rows' 2043 Rectangular = true; } // and we have a rectangle 2044 if(Rectangular) {FC_Ptr++;} // if this is a valid corner, increment 2045 } // end of same candidates 2046 } // end of checking through end of array 2047 2048 if (Rectangular){ // if we have two bivalues with either same row or column, 2049 2050 if (UR_R == FC_Flag) { // if we matched on row, flag will still be set 2051 for (int r=0;r<9;r++){ // check all rows but 2052 if (r != FC_R[UR_First]){ // ignore the one the first two anchors are in 2053 if ( (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[r][FC_C[UR_First]])) && (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[r][FC_C[UR_First+1]])) ) { 2054 FC_R[FC_Ptr] = r; // store r and c of third corner 2055 FC_C[FC_Ptr] = FC_C[UR_First]; 2056 FC_Ptr++; // increment pointer 2057 FC_R[FC_Ptr] = r; // store r and c of fourth corner 2058 FC_C[FC_Ptr] = FC_C[UR_First+1]; 2059 FC_Ptr++;}}} // increment pointer 2060 } // end of we have a row match between first two corners (e.g., A1 and A6) 2061 2062 if (UR_C == FC_Flag) { // if we matched on column, flag will also be set 2063 for (int c=0;c<9;c++){ // check all rows but 2064 if (c != FC_C[UR_First]){ // ignore the one the first two anchors are in 2065 if ( (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[FC_R[UR_First]][c])) && (BitsMatch(Candidates[FC_R[UR_First]][FC_C[UR_First]],Candidates[FC_R[UR_First+1]][c])) ) { 2066 FC_R[FC_Ptr] = FC_R[UR_First]; // store r and c of third corner 2067 FC_C[FC_Ptr] = c; 2068 FC_Ptr++; // increment pointer 2069 FC_R[FC_Ptr] = FC_R[UR_First+1]; // store r and c of fourth corner 2070 FC_C[FC_Ptr] = c; 2071 FC_Ptr++; }}} 2072 } // end of we have a row match between first two corners (e.g., A1 and A6) 2073 2074 if (FC_Ptr !=4){Rectangular == false;} // should have four corners, and 2075 BitMask = Candidates[FC_R[UR_First+2]][FC_C[UR_First+2]] | Candidates[FC_R[UR_First+3]][FC_C[UR_First+3]]; // set bitmask to be combined candidates of two non-bivalue cells 2076 bitClear(BitMask, FC_CN[UR_First][FC_1]); // clear both anchor candidates 2077 bitClear(BitMask, FC_CN[UR_First][FC_2]); // from the mask 2078 if (BitsCount(BitMask) != 2) {Rectangular = false;} // should have two, non-bivalue candidates 2079 } // end of rectangular (we have two bivalue corner anchors) 2080 if (Rectangular && (FC_Ptr == 4)){ 2081 UR3_CC = 0; // reset locked candidate counter 2082 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 2083 for (int cc = 0; cc < 9; cc++){ 2084 UR_Eligible = true; // assume we cant find a two cell match for BitMask above 2085 for (int k=0; k <4;k++){if (rr == FC_R[k] && cc == FC_C[k]){ UR_Eligible = false;}} // don't check against yourself 2086 if (UR_Eligible) {for (int k=2; k <4;k++){ if(!In_Same_Unit(rr,cc,FC_R[k],FC_C[k])){UR_Eligible = false;}}} // Must be in same house as the two non-bivalue cells 2087 if (UR_Eligible) {if ((Candidates[rr][cc] ^ BitMask)!=0){UR_Eligible = false;}} // finally check that we have the two non-bivalue extra candidates 2088 if (UR_Eligible) { 2089 UR3_R = rr; // made it so set row and colunn of locked bivalue "pattern breaker" candidates cell 2090 UR3_C = cc; 2091 UR3_CC++;} // increment counter to ensure we find only one 2092 if (UR3_CC == 1){ 2093 UR3_PB1 = Find_Next_Candidate(UR3_R,UR3_C,1); // store the two pattern breaker candidates 2094 UR3_PB2 = Find_Next_Candidate(UR3_R,UR3_C,UR3_PB1+1); 2095 } else {UR_Eligible = false;} 2096 }} // end rr,cc 2097 if (UR3_CC == 1){ // check that we have only one pattern breaker bivalue cell 2098 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle for possible clearing 2099 for (int cc = 0; cc < 9; cc++){ 2100 UR_Eligible = true; // assume it is an eligible cell to check 2101 for (int k=0; k <4;k++){if (rr == FC_R[k] && cc == FC_C[k]){UR_Eligible = false;}} // don't check against any of the anchors or 2102 if ( (rr == UR3_R) && (cc == UR3_C) ) {UR_Eligible = false;} // don't check against the locked candidate cell either 2103 if ((!In_Same_Unit(rr,cc,UR3_R,UR3_C)) || (!In_Same_Unit(rr,cc,FC_R[2], FC_C[2])) || (!In_Same_Unit(rr,cc,FC_R[3], FC_C[3]))) {UR_Eligible = false;} // must 'see' bivalue breaker and the two cells with breaker candidates 2104 if (UR_Eligible){Candidate_Off(UR3_PB1,rr,cc,"Uniq Rect #3"); // if still eligible (same house, not a corner, not locked candidate cell, potentially clear the pattern breaker candidates 2105 Candidate_Off(UR3_PB2,rr,cc,"Uniq Rect #3");}}} 2106 } 2107 2108 } // end of UR3 Rectangular 2109 } // end of UR Type 3 check all possible anchors 2110 2111} // end of Unique Rectangle types 1,2,4 &5 2112 2113void Hidden_Rectangle(){ // UR type 4 but with hidden candidates in two or three cells 2114 byte AR; // anchor row 2115 byte AC; // column 2116 byte DR; byte DC; byte D2R; byte D2C; byte D3R; byte D3C; // the other three corners 2117 byte Search_Candidate; // candidate to search for in its row and column outside the rectangle coordinates 2118 bool Candidate_Found; // results of that search 2119 byte HR_Bivalue_Counter; // need two or three corners with more than two candidates so count them (<=2) 2120 2121 Cand_Load_2(); // reload all bi-value candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2 2122 FC_Load(); // convert to easier to manipulate array 2123 // Show_Candidates(); 2124 // Show_FC(RDC_Length); 2125 for (int i=0; i <RDC_Length-1; i++){ // check each entry in the array 2126 AR = FC_R[i]; // anchor row 2127 AC = FC_C[i]; // and column 2128 BitMask = Candidates[AR][AC]; // the bits that must be on (our two candidates) 2129 // DDTsp(NL); 2130 // DDTs("Base Anchor ",SL); 2131 // Show_RC(AR,AC,SL); 2132 // Show_PB(Candidates[AR][AC]); 2133 HR_Bivalue_Counter = 1; // we have one corner with only two candidates to start with 2134 for (int rr = 0; rr < 9; rr++){ // search rest of puzzle 2135 for (int cc=0; cc<9;cc++){ // each row and column looking for potential match 2136 DR = rr; // load potential diagonal row and column 2137 DC = cc; 2138 // DDTs("Trying ",SL); 2139 // Show_RC(DR,DC,SL); 2140 // Show_PB(Candidates[DR][DC]); 2141 // Show_RC(DR,DC,NL); 2142 if (BitsMatch(BitMask,Candidates[DR][DC]) && (AR != DR) && (AC != DC)) { // if we have a diagonal and bits match, 2143 if (Candidates_Count[DR][DC] == 2){ HR_Bivalue_Counter++;} // if the diagonal has only two candidates, increment counter again 2144 // DDTsp(NL); 2145 // DDTs("Diagonal ",SL); 2146 // Show_RC(DR,DC,SL); 2147 // Show_PB(Candidates[DR][DC]); 2148 D2R = AR; // Next corner is AR, DC 2149 D2C = DC; 2150 D3R = DR; // and final corner is DR,AC 2151 D3C = AC; // have all four corners now 2152 // DDTsp(NL); 2153 // DDTs("3rd corner ",SL); 2154 // Show_RC(D2R,D2C,SL); 2155 // Show_PB(Candidates[D2R][D2C]); 2156 // DDTsp(NL); 2157 // DDTs("4rd corner ",SL); 2158 // Show_RC(D3R,D3C,SL); 2159 // Show_PB(Candidates[D3R][D3C]); 2160 if (Candidates_Count[D2R][D2C] == 2){ HR_Bivalue_Counter++;} // check candidate count for the two new corners 2161 if (Candidates_Count[D3R][D3C] == 2){ HR_Bivalue_Counter++;} 2162 if ( (BitsMatch(BitMask,Candidates[D2R][D2C])) && (BitsMatch(BitMask,Candidates[D3R][D3C])) && (HR_Bivalue_Counter <= 2) ){ // if we have our 2 anchor candidates in all 4 corners, and at least two of them have more than 2 candidates 2163 Search_Candidate = FC_CN[i][FC_1]; // check the first candidate 2164 Candidate_Found = false; // assume it won't be found 2165 for (int r = 0; r<9; r++){ // check all rows, fix column 2166 if ( (bitRead(Candidates[r][DC],Search_Candidate)) && (!Same_Cell(r,DC,AR,AC)) && (!Same_Cell(r,DC,DR,DC))&& (!Same_Cell(r,DC,D2R,D2C))&& (!Same_Cell(r,DC,D3R,D3C)) ){Candidate_Found = true;}} 2167 for (int c = 0; c<9; c++){ // check all columns, fix row 2168 if ( (bitRead(Candidates[DR][c],Search_Candidate)) && (!Same_Cell(DR,c,AR,AC)) && (!Same_Cell(DR,c,DR,DC))&& (!Same_Cell(DR,c,D2R,D2C))&& (!Same_Cell(DR,c,D3R,D3C)) ){Candidate_Found = true;}} 2169 if (!Candidate_Found) {Candidate_Off(FC_CN[i][FC_2],DR,DC,"Hidden Rect");} // if search for candidate in its row and column OUTSIDE of the four rectangle corners, clear OTHER candidate 2170 2171 Search_Candidate = FC_CN[i][FC_2]; // check the second candidate 2172 Candidate_Found = false; // assume it won't be found 2173 for (int r = 0; r<9; r++){ // check all rows, fix column 2174 if ( (bitRead(Candidates[r][DC],Search_Candidate)) && (!Same_Cell(r,DC,AR,AC)) && (!Same_Cell(r,DC,DR,DC))&& (!Same_Cell(r,DC,D2R,D2C))&& (!Same_Cell(r,DC,D3R,D3C)) ){Candidate_Found = true;}} 2175 for (int c = 0; c<9; c++){ // check all columns, fix row 2176 if ( (bitRead(Candidates[DR][c],Search_Candidate)) && (!Same_Cell(DR,c,AR,AC)) && (!Same_Cell(DR,c,DR,DC))&& (!Same_Cell(DR,c,D2R,D2C))&& (!Same_Cell(DR,c,D3R,D3C)) ){Candidate_Found = true;}} 2177 if (!Candidate_Found) {Candidate_Off(FC_CN[i][FC_1],DR,DC,"Hidden Rect");} // if search for candidate in its row and column OUTSIDE of the four rectangle corners, clear OTHER candidate 2178 } // end of found a hidden rectangle and clear 2179 } // end of found a valid diagonal 2180 }} // end of search for valid diagonal 2181 } // end of stepping through each cell in bivalue array 2182 2183} // end of Hidden Rectangle 2184 2185void Medusa_3D(){ // implements Medusa 3D "coloring" algorithm 2186 byte r; 2187 byte c; 2188 byte n; 2189 2190 Medusa_Build(); // set up the data for Medusa 2191 2192 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2193 r = FC_R[i]; 2194 c = FC_C[i]; 2195 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2196 MD_Clear(); 2197 n = RDC_CanNumber(r,c,Candnum); // find out what it is 2198 // DDTs("New Head of Chain ",SL); 2199 // Show_NRC(n,r,c,NL); 2200 Medusa_Chain(i,n,MD_Mark_Green); // and chain 2201 MD_Eliminated_Candidate = false; // haven't found anything yet 2202 MD_Rule_1(); // and run through the rules 2203 MD_Rule_2(); // possibly run next rule 2204 MD_Rule_3(); // possibly run next rule 2205 MD_Rule_4(); // and maybe run fourth rule 2206 MD_Rule_5(); // and maybe run penultimate rule 2207 MD_Rule_6(); // and maybe run last rule 2208 2209 } } 2210} // end of Medusa 3D 2211 //Rule 1 - Same Color Twice in a Cell 2212void MD_Rule_1(){ // first elimination rule: a cell with two of same color means eliminate ALL candidates with that color (everywhere!) 2213 byte r; byte c; // interim r and c's to reduce indexing 2214 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2215 r = FC_R[i]; 2216 c = FC_C[i]; 2217 if (MD_Colored_Green(i) >1){ // found cell with 2 or more 2218 MD_Clear_All_Green("Medusa 3_D #1"); // can clear ALL with that color (green or yellow) 2219 MD_Eliminated_Candidate = true;} 2220 if (MD_Colored_Yellow(i) > 1){ 2221 MD_Clear_All_Yellow("Medusa 3_D #1"); 2222 MD_Eliminated_Candidate = true;} 2223 } 2224} // end of MD_Rule_1 2225 //Rule 2 - Same Color Twice in a Unit 2226void MD_Rule_2(){ // 2nd elimination rule, like #1, except look for two of same color within a unit (same r,c,b) 2227 byte r; byte c; // interim r and c's to reduce indexing 2228 byte n; // colored candidate to look for in unit 2229 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2230 r = FC_R[i]; 2231 c = FC_C[i]; 2232 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2233 n = RDC_CanNumber(r,c,Candnum); 2234 if( MD_Candidate_Green(n,i)){ // if we find cell with that candidate colored green 2235 for (int j = 0; j < RDC_Length; j++){ // check all other cells 2236 if ( (j != i) && (In_Same_Unit(r,c,FC_R[j],FC_C[j])) && (MD_Candidate_Green(n,j)) ){ 2237 MD_Clear_All_Green("Medusa 3_D #2"); 2238 MD_Eliminated_Candidate = true;} 2239 }} // end of clear the green 2240 if( MD_Candidate_Yellow(n,i)){ // if we find cell with that candidate colored green 2241 for (int j = 0; j < RDC_Length; j++){ // check all other cells 2242 if ( (j != i) && (In_Same_Unit(r,c,FC_R[j],FC_C[j])) && (MD_Candidate_Yellow(n,j)) ){ 2243 MD_Clear_All_Yellow("Medusa 3_D #2"); 2244 MD_Eliminated_Candidate = true;} 2245 }} // end of clear the yellow 2246 } // all candidates 2247 } // all cells with candidates 2248} // end of MD_Rule_2 2249 //Rule 3 - Two colors in a cell 2250void MD_Rule_3(){ // 3rd elimination rule: 2 colors in a cell, can eliminate any UNCOLORED in that cell 2251 byte r; byte c; byte n; // interim r and c's to reduce indexing 2252 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2253 r = FC_R[i]; 2254 c = FC_C[i]; 2255 if ( (MD_Colored_Green(i) > 0) && (MD_Colored_Yellow(i) > 0) ){ // if this is a cell with two colors 2256 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2257 n = RDC_CanNumber(r,c,Candnum); 2258 if ( !MD_Colored_Already(n,i) ) { // clear every candidate in this cell that isn't colored 2259 Candidate_Off(n,r,c,"Medusa 3_D #3"); 2260 MD_Eliminated_Candidate = true;} 2261 } // all candidates within a cell 2262 } // found green & yellow in a cell 2263 } // all cells 2264 } // end of MD_Rule_3 2265 // Rule 4 - Two colors 'elsewhere' - (Sees Two Different Colors) 2266void MD_Rule_4(){ // 4th elimination rule, eliminate any candidate in any cell that is uncolored AND sees same n in same unit with two different colors 2267 byte r; byte c; byte n; // interim r and c's to reduce indexing 2268 byte GCnt ; byte YCnt; // count the greens and yellows in same unit 2269 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2270 r = FC_R[i]; 2271 c = FC_C[i]; 2272 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2273 n = RDC_CanNumber(r,c,Candnum); 2274 if ( !(MD_Colored_Already(n,i)) ){ // if this is in an uncolored candidate, 2275 GCnt = 0; // reset counters 2276 YCnt = 0; 2277 for (int j = 0; j < RDC_Length; j++){ // sweep through every cell with candidates 2278 if ( (MD_Candidate_Green(n,j))&& (In_Same_Unit(r,c, FC_R[j],FC_C[j])) ){GCnt++;} else // count how many in same unit are green 2279 {if ( (MD_Candidate_Yellow(n,j))&& (In_Same_Unit(r,c, FC_R[j],FC_C[j])) ){YCnt++;}} // count how many in same unit are yellow 2280 } // end count all colored that can see r,c,n 2281 if ( (GCnt > 0) && (YCnt > 0) ) { // eliminate appropriate candidate 2282 Candidate_Off(n,r,c,"Medusa 3_D #4"); 2283 MD_Eliminated_Candidate = true;} 2284 } // end check if uncolored this r,c,n 2285 }} 2286} // end of MD_Rule 4 2287 //Rule 5 - Two colors Unit + Cell (Unit-Cell Elimination) 2288void MD_Rule_5(){ // If an uncolored candidate can see a colored candidate of the same value elsewhere (it shares a unit) 2289 // AND an OPPOSITE colored candidate in its own cell, the uncolored candidate can be removed. 2290 byte r; byte c; byte r1; byte c1; byte n; byte n2; byte n3; // current row and column, search row and column, uncolored and same unit colored & same cell uncolored candidates 2291 byte GCnt ; byte YCnt; // count the greens and yellows in same unit 2292 bool MD5_Eliminate; // flag for controlling elimination of candidates 2293 2294 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2295 r = FC_R[i]; 2296 c = FC_C[i]; 2297 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2298 n = RDC_CanNumber(r,c,Candnum); 2299 MD5_Eliminate = false; // assume we WON'T find the right combination 2300 if ( !(MD_Colored_Already(n,i)) ){ // if this is in an uncolored candidate, 2301 // if (r== 2 && c== 4 && n== 8){ 2302 // DDTs("uncolored ",SL); 2303 // Show_NRC(n,r,c, SL); 2304 // Show_Candidates_Color();} 2305 GCnt = 0; // reset counters 2306 YCnt = 0; 2307 for (int Candnum2 = 1; Candnum2 <= Candidates_Count[r][c];Candnum2++){ // see if there is a green or yellow color in this cell 2308 n2 = RDC_CanNumber(r,c,Candnum2); // fetch the other candidates in the cell (its OK we see n again, it isn't colored and wont bump counter) 2309 // if (r== 2 && c== 4 && n== 8){ 2310 // DDTv(" checking n2 ",n2,NL);} 2311 if (MD_Candidate_Green(n2,i)) {GCnt++;} else {if (MD_Candidate_Yellow(n2,i)){YCnt++;}}} 2312 /// if (r== 2 && c== 4 && n== 8){ 2313 // DDTv(" Gcnt= ",GCnt,SL); 2314 // DDTv(" YCnt= ",YCnt,NL);} 2315 if (GCnt > 0){ // There IS a green colored candidate in same cell 2316 MD5_Eliminate = false; // assume we WON'T find a yellow cell meeting criteria 2317 for (int j = 0; j< RDC_Length;j++){ // for all active cells with candidates, 2318 r1 = FC_R[j]; 2319 c1 = FC_C[j]; 2320 if ( (MD_Candidate_Yellow(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD5_Eliminate = true;} // if we find the original uncolored candidate, in same unit and colored, we can eliminate 2321 } // end of checking all 2nd cells 2322 2323 } else // if not a green, look for yellow 2324 { 2325 if (YCnt > 0) { 2326 MD5_Eliminate = false; // assume we WON'T find a yellow cell meeting criteria 2327 for (int j = 0; j< RDC_Length;j++){ // for all active cells with candidates, 2328 r1 = FC_R[j]; 2329 c1 = FC_C[j]; 2330 if ( (MD_Candidate_Green(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD5_Eliminate = true;} // if we find the original uncolored candidate, in same unit AND colored, we can eliminate 2331 } // end of checking all 2nd cells 2332 }} 2333 if (MD5_Eliminate){ 2334 Candidate_Off(n,r,c,"Medusa 3_D #5"); // all criteria met, so eliminate the original, uncolored candidate 2335 MD_Eliminated_Candidate = true;} 2336 } // end found an uncolored candidate 2337 } // end check each candidate in the cell 2338 } // end check each cell with candidates 2339 2340} // end of MD Rule 5 2341 //Rule 6 - Cell Emptied by Color ((Emptyinging a Cell) 2342void MD_Rule_6(){ //If a particular uncolored Cell can "see" Cells that each contain one of the candidates 2343 // of the uncolored Cell and if all these candidates have the same color, 2344 //then this color can not be the solution. So, if all n's in an uncolored cell can 'see' green n's in same unit, 2345 // then green can be eliminated and vice-versa if they all see yellow n's 2346 byte r; byte c; byte r1; byte c1; byte n; byte n2; 2347 byte nc; // number of candidates in this cell 2348 bool MD6_C[10]; // true if this candidate is present in cell 2349 bool MD6_G[10]; // true if there is a 'green' color same n, same unit 2350 bool MD6_Y[10]; // corresponding yellow color 2351 bool MD6_All_Green; bool MD6_All_Yellow; // used to test for all found green or yellow outside uncolored cell 2352 // Show_Candidates_Color(); 2353 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2354 r = FC_R[i]; 2355 c = FC_C[i]; 2356 if (MD_Total_Colored(i) == 0){ // if this cell has no colored candidates 2357// DDTv(" Cell index ",i,SL); 2358// Show_RC(r,c,SL); 2359// Show_PB(Candidates[r][c]); 2360// DDTsp(NL); 2361 for (int k=0; k <=9; k++){ 2362 MD6_C[k] = false; 2363 MD6_G[k] = false; 2364 MD6_Y[k] = false; } // clear all analytical bits 2365 for (int k=1; k <= Candidates_Count[r][c]; k++){ 2366 n = RDC_CanNumber(r,c,k); // get the candidate number 2367 MD6_C[n] = true;} // and flip bit on in candidate list 2368 // DDTs(" Candidates in check cell are ",SL); 2369 // for (int kk = 0; kk<=9; kk++){if ( MD6_C[kk] ){DDTs("1",SL);} else {DDTs("0",SL);}} 2370 /// DDTsp(NL); 2371 for (int n=1; n <=9; n++){ // step through 2372 if (MD6_C[n]){ // if that candidate is present, 2373 for (int j = 0; j< RDC_Length;j++){ // for all active cells with candidates, 2374 r1 = FC_R[j]; 2375 c1 = FC_C[j]; 2376 if ( (MD_Candidate_Green(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD6_G[n] = true;} // if we find the same candidate, in same unit AND colored green, mark it 2377 if ( (MD_Candidate_Yellow(n,j)) && (In_Same_Unit(r,c,r1,c1)) && (!Same_Cell(r,c,r1,c1)) ) {MD6_Y[n] = true;} // if we find the same candidate, in same unit AND colored yellow, mark it 2378 }}} // end of checking all 2nd cells 2379 MD6_All_Green = true; // assume we will find what we want 2380 MD6_All_Yellow = true; 2381 for (int n= 1; n <=9; n++){ //step through whole list 2382 if (MD6_C[n]){ // if candidate n is on the list (its one of the candidates in this cell), 2383 if ( (!MD6_G[n]) && (!MD6_Y[n]) ){ // scenario 1, could not find any color for this candidate 2384 MD6_All_Green = false; 2385 MD6_All_Yellow = false;} 2386 if ( (!MD6_G[n]) || (MD6_Y[n]) ){MD6_All_Green = false;} //scenario 2, if ANY candidate in the cell doesn't see green, its not true that they all see green 2387 if ( (!MD6_Y[n]) || (MD6_G[n]) ){MD6_All_Yellow = false;}}} //and if ANY candidate in the cell doesn't see yellow, its not true that they all see yellow 2388 2389 if (MD6_All_Green){ // if every uncolored candidate in cell can 'see' the same candidate in green, clear all Green 2390 MD_Clear_All_Green("Medusa 3_D #6"); 2391 MD_Eliminated_Candidate = true;} 2392 if (MD6_All_Yellow){ 2393 MD_Clear_All_Yellow("Medusa 3_D #6"); 2394 MD_Eliminated_Candidate = true;} // Same for yellow 2395 2396 } // end 'if no color candidates this cell 2397 } // end 'all active cells' 2398} // end of MD_Rule 6 2399 2400void MD_Clear_All_Green(String slabel){ // clear all green candidates in all cells 2401 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2402 int r = FC_R[i]; 2403 int c = FC_C[i]; 2404 byte n; 2405 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2406 n = RDC_CanNumber(r,c,Candnum); // find out what it is 2407 if (bitRead(FC_UD[i][MD_Green],n) > 0){Candidate_Off(n,r,c,slabel);} 2408}} 2409} // end of MD_Clear_All_Green 2410 2411void MD_Clear_All_Yellow(String slabel){ // clear all green candidates in all cells 2412 for (int i = 0; i < RDC_Length; i++){ // for each cell with candidates, 2413 int r = FC_R[i]; 2414 int c = FC_C[i]; 2415 byte n; 2416 for (int Candnum = 1; Candnum <= Candidates_Count[r][c];Candnum++){ // for each candidate in the cell, 2417 n = RDC_CanNumber(r,c,Candnum); 2418 if (bitRead(FC_UD[i][MD_Yellow],n) > 0){Candidate_Off(n,r,c,slabel);} 2419}} 2420} // end of MD_Clear_All_Yellow 2421 2422void Medusa_Chain(int Src_index, int n, bool My_Color){ 2423 byte Next_n; 2424 byte Src_r; byte Src_c; byte Dest_r; byte Dest_c; 2425 2426 MD_Color(Src_index,n,My_Color); // Mark this cell/candidate combo the right color (Green or Yellow) 2427 Src_r = FC_R[Src_index]; // and fetch its row and column 2428 Src_c = FC_C[Src_index]; 2429 2430 for (int Dest_index = 0; Dest_index < RDC_Length; Dest_index++){ // for each non bi-value cell with candidates, 2431 Dest_r = FC_R[Dest_index]; // fetch possible 'next link' r and c, including source cell 2432 Dest_c = FC_C[Dest_index]; 2433 for (int Dest_Candnum = 1; Dest_Candnum <= Candidates_Count[Dest_r][Dest_c];Dest_Candnum++){ // for each candidate in that cell 2434 Next_n = RDC_CanNumber(Dest_r,Dest_c,Dest_Candnum); // find out what the candidate is 2435 if (!MD_Colored_Already(Next_n,Dest_index)){ // ignore cell/candidates we've used and colored already 2436 if (MD_Strong_Link(Dest_index,Src_index,n,Next_n)){ 2437 // for (int k = 0; k <= XC_Link_Counter;k++){DDTsp(SL);} 2438 // if (My_Color == MD_Mark_Green){DDTs("Green ",SL);} else {DDTs("Yellow ",SL);} 2439 // Show_NRC(n,Src_r,Src_c,SL); 2440 // DDTs(" links to ",SL); 2441 // Show_NRC(Next_n,Dest_r,Dest_c,NL); 2442 XC_Link_Counter++; 2443 Medusa_Chain(Dest_index,Next_n,!My_Color); // follow the link 2444 XC_Link_Counter--;} // end of non-bi-value 2445 } // end of it is a fresh cell/candidate 2446 }} // end of check each cell and candidate 2447} // end of Medusa Chain 2448 2449bool MD_Strong_Link(int Dest_index,int Src_index, int Current_n, int Next_n){ // validate as a strong link 2450 2451 byte counter = 0; // for strong links only current and successor should be in the shared unit 2452 byte r1 = FC_R[Src_index]; // load rows and columns to simplify and reduce calculations for indexing 2453 byte c1 = FC_C[Src_index]; // have row and column of 'link from' and 'link to' 2454 byte r2 = FC_R[Dest_index]; 2455 byte c2 = FC_C[Dest_index]; 2456 byte b1 = In_Box(r1,c1); 2457 byte b2 = In_Box(r2,c2); 2458 2459 if (Src_index == Dest_index){ // if we are in the same cell, and 2460 if ( (Candidates_Count[r1][c1] == 2) && (MD_Total_Colored(Src_index)==1) ) { // we are in a bi-value cell and the 2nd candidate is uncolored 2461 return true;}} 2462 2463 if (Current_n !=Next_n) {return false;} // if not bi-value, 'from' and 'to' candidate must match 2464 if (!In_Same_Unit(r1,c1,r2,c2)) {return false;} // and they have to be in same unit ("see each other") 2465 2466 if (r1 == r2){ // are they in same row? 2467 counter = 0; // yes, reset counter of occurences 2468 for (int cc = 0; cc <9; cc++){ 2469 if (If_Candidate_On(Next_n, r1, cc)){counter++;}} // count # times this candidate appears in row 2470 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 2471 2472 if (c1 == c2){ // are they in same column? 2473 counter = 0; // yes, reset counter of occurences 2474 for (int rr = 0; rr <9; rr++){ 2475 if (If_Candidate_On(Next_n, rr, c1)){counter++;}} // count # times this candidate appears in column 2476 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 2477 2478 if (b1 == b2){ // are they in same box? 2479 counter = 0; // yes, reset counter of occurences 2480 for (int rr = Box_Coords[b1][Row_Pointer]; rr <= (Box_Coords[b1][Row_Pointer])+2; rr++){ //walk the box 2481 for (int cc = Box_Coords[b1][Column_Pointer]; cc <= (Box_Coords[b1][Column_Pointer])+2; cc++) {if (If_Candidate_On(Next_n, rr, cc)) {counter++;}}} // count # times this candidate appears in the box 2482 if (counter !=2) {return false;}} // if not 2 (link from and to) then its not a strong link 2483 2484 return true; // passed all tests: everywhere link-from 'sees' link-to, there's only 2 of them 2485 2486 } // end of MD Strong Link 2487 2488void Medusa_Build(){ // set up Medusa data 2489 Build_RD(); // load RDC first with r,c, and a 'current candidate' pointer 2490 for (int i = 0; i < RDC_Length; i++){ // also load FC 'simpler' row and column access (mimics RDC_Matrix row and column pointers) 2491 FC_R[i] = RDC_Matrix[i][Row_Pointer]; // store r and c 2492 FC_C[i] = RDC_Matrix[i][Column_Pointer]; 2493 FC_UD[i][MD_Green]=0; // and reset the flags, here denoting 'colored green' and 'colored yellow' 2494 FC_UD[i][MD_Yellow]=0;} 2495} // end of Medusa_Build 2496 2497void MD_Clear(){ 2498 for (int k=0; k<RDC_Max_Size;k++){ // clear out all colors (and incidentally, all 'used') 2499 FC_UD[k][MD_Green]=0; // and reset the flags, here denoting 'colored green' and 'colored yellow' 2500 FC_UD[k][MD_Yellow]=0;} 2501 MD_Green_Candidates = 0; 2502 MD_Yellow_Candidates = 0; 2503 XC_Link_Counter = 0; // reset link counter 2504 } // end of MD Clear 2505 2506void MD_Color(int i,int n, bool My_Color){ // set "Green" or "Yellow" color for this index 2507 if (My_Color == MD_Mark_Green) {bitSet(FC_UD[i][MD_Green],n); // set bit and return 2508 MD_Green_Candidates++;} // increment counter 2509 else 2510 {bitSet(FC_UD[i][MD_Yellow],n); 2511 MD_Yellow_Candidates++;} 2512} // end of MD_Color 2513 2514bool MD_Colored_Already(int n,int i){ // check to see if colored already (i.e., "used") 2515 if ( (bitRead(FC_UD[i][MD_Green],n) > 0) || (bitRead(FC_UD[i][MD_Yellow],n) > 0) ) {return true;} else {return false;}; // by checking both 'color' bits for this index 2516} // end of MD_Colored_Already 2517 2518int MD_Total_Colored(int i){ // count the total colored candidates in this cell 2519 byte Total_Bits = 0; 2520 Total_Bits = BitsCount(FC_UD[i][MD_Green]) + BitsCount(FC_UD[i][MD_Yellow]); // sum of green + yellow 2521 return Total_Bits; 2522}// end of MD_Total_Colored 2523 2524int MD_Colored_Green(int i){ // count the total Green candidates in this cell 2525 byte Total_Bits = 0; 2526 Total_Bits = BitsCount(FC_UD[i][MD_Green]); // green 2527 return Total_Bits; 2528}// end of MD_Total_Colored 2529 2530int MD_Colored_Yellow(int i){ // count the total Yellow candidates in this cell 2531 byte Total_Bits = 0; 2532 Total_Bits = BitsCount(FC_UD[i][MD_Yellow]); // yellow 2533 return Total_Bits; 2534}// end of MD_Total_Colored 2535 2536bool MD_Candidate_Green(int n,int i){ // check if specific candidate is colored green 2537 if ( bitRead(FC_UD[i][MD_Green],n) > 0 ) {return true;} else {return false;}} 2538 2539bool MD_Candidate_Yellow(int n,int i){ // check if specific candidate is colored yellow 2540 if ( bitRead(FC_UD[i][MD_Yellow],n) > 0 ) {return true;} else {return false;}} 2541 2542int MD_RC_i(int r, int c){ // convert from r,c, back to i index 2543 for (int i = 0; i < RDC_Length; i++) {if ( (FC_R[i] == r) && (FC_C[i] == c) ){return i;}} // found it 2544 return -1; 2545} // end of MD_RC_i 2546 2547void Show_Candidates_Color(){ 2548 int j; 2549 for (int r = 0; r < 9; r++){ 2550 for (int c = 0; c< 9; c++){ 2551 j = MD_RC_i(r,c); 2552 DDTsp(SL); 2553 Show_RC(r,c,SL); 2554 Show_PB_Color(Candidates[r][c],j); 2555 DDTsp(SL);} 2556 DDTsp(NL); } 2557} // end of show candidates with color 2558 2559void Show_PB_Color(int n, int j){ 2560 String digits[10] = {"0","1","2","3","4","5","6","7","8","9"}; 2561 DDTs(" {",SL); 2562 for (int i=1; i<=9;i++){if (bitRead(n,i)) { 2563 DDTs(digits[i],SL); 2564 if (j >= 0){ 2565 if ( bitRead(FC_UD[j][MD_Green],i) > 0 ) {DDTs("g",SL);} 2566 if ( bitRead(FC_UD[j][MD_Yellow],i) > 0 ) {DDTs("y",SL);}} 2567 } 2568 } 2569 DDTs("} ",SL); 2570} // end of showPB (Candidate print) 2571 2572 2573void Remote_Pair(){ // chain algorithm for remote pair 2574 /* 2575 * A remote pair is an even number of cells, all of which contain the same two candidates, 2576 * that share units in a chain pattern. Both of these candidates can be eliminated from any cell that shares a unit with both end-points of this chain. 2577 */ 2578 2579 const byte RP_First = 0; 2580 bool RP_Eligible; // eligible, i.e., not protected 2581 Cand_Load_2(); // load all 2 candidate cells, r,c and both candidates "fitted" into single byte (C1 + 10*C2) 2582 FC_List_Clear(); // clear non-repeating list 2583 for (int i=0; i <RDC_Length; i++){ // check each entry in the array 2584 FC_Ptr = 0; // reset pointer 2585 RP_Chain(i,RDC_Matrix[i][Row_Pointer], RDC_Matrix[i][Column_Pointer],(RDC_Matrix[i][RDC_CurrCan]/10) ,(RDC_Matrix[i][RDC_CurrCan]%10),FC_Up); // start the chain 2586 if (FC_Ptr >=4){ // chain must be four or more cells 2587 for (int rr = 0; rr < 9; rr++){ // check all cells in puzzle 2588 for (int cc = 0; cc < 9; cc++){ 2589 RP_Eligible = true; // assume its not a protected entry 2590 for (int j = RP_First; j < FC_Ptr; j++){if (rr == FC_R[j] && cc == FC_C[j]){RP_Eligible = false;}} 2591 if (RP_Eligible && RP_Match(rr,cc)){ Candidate_Off(FC_CN[RP_First][FC_1],rr,cc, "Remote Pair"); // 2592 Candidate_Off(FC_CN[RP_First][FC_2],rr,cc, "Remote Pair"); }}} // end of all rows and columns 2593 //Any cell outside the Remote Pair that sees two cells with different values cannot have one of the Remote Pair digits set. 2594 } // if chain is 4 or more end at least four cells 2595 } // end of checking all two candidate entries 2596} //end of Remote Pair 2597 2598bool RP_Match(int r, int c){ // check for in same house as two of the opposite polarity chain members 2599 // DDTv("RP_Length is ",RP_Length,SL); 2600 // DDTv("FC ptr is ",FC_Ptr,NL); 2601 int RP_Ptr = 0; // build a list of all chain members in same house as this entry 2602 int RP_Ups = 0; // number of positive markers 2603 int RP_Downs = 0; // and downs 2604 for (int i = 0; i < FC_Ptr; i++){ // check each entry in chain 2605 if (In_Same_Unit(r,c,FC_R[i],FC_C[i])){FC_List[RP_Ptr] = i; // if we are in the same house, store pointer 2606 if (FC_UD[i][FC_1] == FC_Up) {RP_Ups++;} else {RP_Downs++;} // count the number of ups and downs 2607 RP_Ptr++; }} // and increment pointer 2608 if (RP_Ptr >= 2 && RP_Ups >=1 && RP_Downs >=1){return true;} // seen by at least two members of chain and both up and down 2609 return false; 2610} // end of RP Match 2611 2612 2613void RP_Chain(int MyIndex,int MyRow, int MyColumn, int MyCandidate1, int MyCandidate2, int MyMark){ 2614 byte NM; // next mark (flips) 2615 byte NR; // next row 2616 byte NC; // next column 2617 byte NC1; // first candidate of pair 2618 byte NC2; // second 2619 FC_R[FC_Ptr] = MyRow; 2620 FC_C[FC_Ptr] = MyColumn; 2621 FC_CN[FC_Ptr][FC_1] = MyCandidate1; 2622 FC_CN[FC_Ptr][FC_2] = MyCandidate2; 2623 FC_UD[FC_Ptr][FC_1] = MyMark; // store 'polarity' mark 2624 FC_UD[FC_Ptr][FC_2] = FC_Flag; // mark it so we don't find it again 2625 FC_List[MyIndex] = true; // 2626 FC_Ptr++; // increment pointer 2627 for (int j = 0; j < RDC_Length; j++){ 2628 NR = RDC_Matrix[j][Row_Pointer]; 2629 NC = RDC_Matrix[j][Column_Pointer]; 2630 NC1 = RDC_Matrix[j][RDC_CurrCan]/10; 2631 NC2 = RDC_Matrix[j][RDC_CurrCan]%10; 2632 if ( (!FC_List[j]) && (NC1 == MyCandidate1) && (NC2 == MyCandidate2) && (In_Same_Unit(MyRow,MyColumn, NR,NC)) ){ 2633 if (MyMark == FC_Up) {NM = FC_Down;} else {NM = FC_Up;} // flip the mark 2634 RP_Chain(j,NR,NC,NC1,NC2,NM);}} // and follow the chain 2635 return; // return when can't find next link 2636} // end of RPC 2637 2638bool RD(){ // recursive descent solution 2639 RDC_Ptr = 0; // and pointer 2640 Build_RD(); // build the recursive descent list 2641 T_Msg3("Trying Recursive","Descent and ","Backtrack Search"); 2642 DDTv(F("Starting Recursive Descent/Backtrack Solution with "),RDC_Length,SL); 2643 DDTv(" empty cells and ",CCC,SL); 2644 DDTs(" candidates",NL); 2645 T_Scroll_Message(F("Starting Recursive Descent/Backtrack")); 2646 T_Scroll_Message(FDM("with ",RDC_Length," empty cells")); 2647 T_Scroll_Message(FDM("and ",CCC," candidates")); 2648 return RD_Fill(RDC_Ptr); // pretty deceptively simple, eh? Stepping off a cliff! 2649} // end of Recursive Descent 2650 2651bool RD_Fill(int ptr){ // Main Decend/Backtrack routine 2652int Cell_Entry; // will be the candidate to try 2653bool SuccessFit = false; // true indicates we have found an entry for the cell that fits 2654int r = RDC_Matrix[ptr][Row_Pointer]; // just to make the code easier to read 2655int c = RDC_Matrix[ptr][Column_Pointer]; 2656 2657if (ptr >= RDC_Length) {return true;} // done? (i.e., pointing past last empty cell in list) 2658 2659 while (true){ // only two ways out, [1] and [2] below 2660 SuccessFit = false; 2661 while (!SuccessFit){ 2662 RDC_Matrix[ptr][RDC_CurrCan]++; // increment counter 2663 if (RDC_Matrix[ptr][RDC_CurrCan] > Candidates_Count[r][c]){ // are we out of candidates (ctr greater than count)? 2664 RDC_Matrix[ptr][RDC_CurrCan] = 0; //[1]we are out of candidates, reset this level's count 2665 return false;} // and return failure 2666 Cell_Entry = RDC_CanNumber(r,c,RDC_Matrix[ptr][RDC_CurrCan]); // return the 'nth' candidate from candidates array 2667 RDC_Calls++; // how many numbers have I tried? 2668 SuccessFit = RD_Check(Cell_Entry,r, c); } // try the number using current puzzle grid (not stored data) 2669 Puzzle_Grid[r][c]= Cell_Entry; // plug in the number because we succeeded 2670 T_Number(r,c,Cell_Entry,Entry_Color); // update display and call next level 2671 if (RD_Fill(ptr+1)) {return true;} //[2] if previous levels cleared, all OK, return true and pop up 2672 Puzzle_Grid[r][c]= 0; // unplug the number and try again 2673 T_Number(r,c,Cell_Entry,Grid_Color); // also clear failed entry from display 2674 } // end of "While True" infinite loop 2675 2676} // end of RD Fill 2677 //Recursive Descent/Backtrack routines start here 2678void Build_RD(){ // build the list of unsolved candidates 2679 2680 RDC_Calls = 0; //reset call counter 2681 RDC_Length = 0; // initialize the length of the array 2682 for (int r = 0; r < 9; r++){ 2683 for (int c = 0; c < 9; c++){ 2684 if (Puzzle_Grid[r][c] == 0) { // for every non-filled cell, 2685 RDC_Matrix[RDC_Length][Row_Pointer] = r; // store row and column indices 2686 RDC_Matrix[RDC_Length][Column_Pointer] = c; 2687 RDC_Matrix[RDC_Length][RDC_CurrCan] = 0; // and initialize where we are against the candidates -- current candidate being accessed 2688 RDC_Length++; }}} // and increment index 2689} // end of Build the RD matrix 2690 2691int RDC_CanNumber(int r,int c, int bposition){ // convert bit-packed candidates to packed array 2692 int ctr = 0; 2693 for (int n = 1; n <=9; n++){ // walk through 2694 if (bitRead(Candidates[r][c],n)){ // if a bit is on, store into array 2695 Digit_Array[++ctr] = n;}} // in sequence 2696 return Digit_Array[bposition]; // return the 'nth' candidate that is 'on' 2697} // end of Candidate bit selected by order number 2698 2699bool RD_Check(int MN, int MR, int MC){ // direct check for legality of placement in puzzle grid 2700 for (int c = 0; c<9; c++){if (Puzzle_Grid[MR][c] == MN && (c != MC)){return false;}}; // check my row 2701 for (int r = 0; r<9; r++){if (Puzzle_Grid[r][MC] == MN && (r != MR)){return false;}}; // check my column 2702 for (int r = Box_Coords[In_Box(MR,MC)][Row_Pointer]; r <= (Box_Coords[In_Box(MR,MC)][Row_Pointer])+2; r++){ //walk the box 2703 for (int c = Box_Coords[In_Box(MR,MC)][Column_Pointer]; c <= (Box_Coords[In_Box(MR,MC)][Column_Pointer])+2; c++){ 2704 if (Puzzle_Grid[r][c] == MN && (c != MC)&& (r != MR)) {return false;}}} // check each cell 2705 return true; 2706} // end of RD Check 2707 2708// Utility bit and candidate manipulation and test routines 2709 2710bool If_Candidate_On(int n,int r,int c){ // returns 'true' if bit 'n' is on in the cell (indicating 'yes' candidate) 2711 if (bitRead(Candidates[r][c],n) > 0) {return true;} else {return false;} 2712} // End of If_Candidate_On 2713 2714void Candidate_On(int n,int r,int c){ // turns the bit on for candidate number n 2715 if (!If_Candidate_On( n, r, c)){ 2716 bitSet(Candidates[r][c], n); // set bit and 2717 Candidates_Count[r][c]++; // increment count for this candidate 2718 CCC++;}} // increment calculated total candidate count too 2719 2720void Candidate_Off(int n,int r,int c, String xx){ // turns the bit off for candidate number n (clears candidate n from cell) 2721 if (Candidates_Count[r][c] !=0){ 2722 if (If_Candidate_On(n,r,c)){ 2723 bitClear(Candidates[r][c], n); 2724 Candidates_Count[r][c]--; // decrement candidate counter for this cell 2725 CCC--; // decrement calculated count too 2726 DDTs(xx,SL); 2727 DDTs(" cleared candidate ",SL); 2728 Show_NRC(n,r,c,NL); 2729 if (Show_Clear_Place) {T_Scroll_Message(FDM_CC(n,r,c,xx));} // show cleared candidate message if on 2730 #ifdef Test_Mode3 // if defined in input grid then 2731 if (n == Solution_Grid[r][c]){ //if about to clear a candidate that IS the solution for this cell 2732 DDTv("Error clearing candidate",n,SL); // tests whether n placed in cell matches solution 2733 DDTs(" in ",SL); 2734 Show_RC(r,c,NL); 2735 T_Msg3("Mistake","Versus","Solution"); 2736 T_Scroll_Message("Mistake Against Solution"); 2737 T_Scroll_Message(FDM_FTC(n,r,c,"Error")); 2738 Display_Debug(); 2739 while(1){;}} 2740#endif 2741 }}} // end of Candidates Bit Off 2742 2743void Placement_Removes_Candidates(int n,int r,int c){ // remove appropriate candidate bit from unit of number just placed 2744 for (int cc = 0; cc <9; cc++){ // walk the row column by column, ignoring current cell (leave that cell alone) 2745 if (cc != c){if (If_Candidate_On(n,r,cc)){Candidate_Off(n,r,cc," Placement");}}} 2746 for (int rr = 0; rr<9; rr++){ 2747 if (rr !=r ){if (If_Candidate_On(n,rr,c)){Candidate_Off(n,rr,c," Placement");}}} 2748 for (int srr = Box_Coords[In_Box(r,c)][Row_Pointer]; srr <= (Box_Coords[In_Box(r,c)][Row_Pointer])+2; srr++){ 2749 for (int scc = Box_Coords[In_Box(r,c)][Column_Pointer]; scc <= (Box_Coords[In_Box(r,c)][Column_Pointer])+2; scc++){ 2750 if ((srr != r) && (scc !=c)){ if (If_Candidate_On(n,srr,scc)) {Candidate_Off(n,srr,scc," Placement");}} // no other cell in this box either 2751 }} // end of box 'walk' 2752} // end of Remove Candidates 2753 2754int BitsOff(int Input_Mask,int Off_Mask){ // turn bits off in input mask that occur in OffMask 2755 for (int n = 0; n <=9; n++){ 2756 if (bitRead(Off_Mask,n)>0){bitClear(Input_Mask,n);}} 2757 return Input_Mask; 2758} // end of BitOff 2759 2760bool BitsMatch(int a, int b){ // returns true if all bits in 'a' are in 'b' (though 'b' may have others too) 2761 bool MyResult = true; // assume they do match 2762 for (int n = 0; n <=9; n++){ // set false if they don't 2763 if ((bitRead(a,n)>0) && (bitRead(b,n)<=0)){MyResult = false;}} 2764 return MyResult; 2765} 2766 2767int BitsCount(int Input_Mask){ // count the number of bits (candidates) in mask 2768 int MyResult = 0; // initialize 2769 for (int n = 0; n <=9; n++){ // count 'em 2770 if (bitRead(Input_Mask,n)>0){MyResult++;}} 2771 return MyResult; 2772} // End of Bitscount 2773 2774int BitsWhich(int Input_Mask){ // return the n that is represented in mask 2775 for (int n = 0; n <=9; n++){if (bitRead(Input_Mask,n)>0){return n;}} // find the first one 2776 return 0; 2777} // End of Bitscount 2778 2779int Number_of_Candidates(int a, int b){ // count number of candidates in combined ('ored') inputs 2780 int counter = 0; 2781 int BitCheck = a | b; // 'or' the two inputs and get a bit mask 2782 for (int n = 1; n <=9; n++){ // check all bits and count them 2783 if (bitRead(BitCheck,n)) {counter++;}} 2784 return counter; 2785} // end of number of candidates 2786 2787int Find_Next_Candidate(int r,int c, int start_bit){ // convert from next SINGLE bit to next single number 2788 for (int n = start_bit; n <=9; n++){ // walk thru the numbers from start point 2789 if (If_Candidate_On(n, r, c)){ // if a bit is on, return it 2790 return n;}} // return 2791} // end of Candidates Find Number 2792 2793bool In_Same_Unit(int r1,int c1, int r2,int c2){ // are two cells in the same Unit or House? 2794 if ((r1 == r2) || (c1 == c2)) {return true;} // same row or column 2795 if (In_Box(r1,c1) == In_Box(r2,c2)) {return true;} // same if in same box 2796 return false; // else they are not 2797} // end of In_Same_Unit 2798 2799int In_Box(int r, int c){ // determine box from row and column 2800 switch (r){ // returns box number from 0 to 8 2801 case 0: case 1: case 2: if (c<3) {return 0;} else if (c < 6) {return 1;} else return 2; 2802 break; 2803 case 3: case 4: case 5: if (c<3) {return 3;} else if (c < 6) {return 4;} else return 5; 2804 break; 2805 case 6: case 7: case 8: if (c<3) {return 6;} else if (c < 6) {return 7;} else return 8; 2806 break; } 2807 } // end of In_Box 2808 2809bool Same_Cell(int r1,int c1,int r2,int c2){ // return to facilitate ignoring 'this' cell 2810 if ((r1 == r2) && (c1 == c2)) {return true;} else {return false;} 2811} // end of same cell 2812 2813// Specialized 'clearing' routines based on specific criteria 2814 2815void Clear_Puzzle(){ // remove puzzle contents 2816 for (int r = 0; r <9; r++){ // clear prior puzzle 2817 for (int c = 0; c<9; c++){ 2818 Puzzle_Grid[r][c] = 0;}}} 2819 2820void Clear_HPTQ(int Input_BitMask, String xx){ // clear UNMASKED bits (remove as candidates) from marked CanMark entries 2821 for (int i = 0; i < CC_Length; i++){ // check each entry 2822 if (CanMark[i]){ // if WAS found in a match, then 2823 for (int n = 1; n <=9; n++){ // go through 2824 if (bitRead(Input_BitMask,n)== 0){ // if candidate is NOT present in mask, clear it in the candidate 2825 Candidate_Off(n,CanCoord[i][Row_Pointer],CanCoord[i][Column_Pointer],xx);}}}} // this will leave only the quad bits on and clear all bits no longer candidates 2826 } // end of Clear HPTQ 2827 2828void Clear_Row_Outside_Box(int n, int r, int b, String xx){ // clear row r of candidate n but NOT in this box 2829 for (int c = 0; c<9; c++){ // for every column in the row 2830 if (b != In_Box(r,c)){Candidate_Off(n,r,c,xx);}} // but only OUTSIDE this box 2831} // end of candidates clear row outside box 2832 2833void Clear_Column_Outside_Box(int n, int c, int b, String xx){ // clear column c of candidate n but NOT in this box 2834 for (int r = 0; r<9; r++){ // for every row in the column 2835 if (b != In_Box(r,c)){Candidate_Off(n,r,c,xx);}} // but only OUTSIDE this box 2836} // end of candidates clear column but only outside my box 2837 2838void Clear_Box_Except_Row(int n,int r,int b,String xx){ // clear the box of candidate, protecting row r 2839 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 2840 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 2841 if (srr != r){Candidate_Off(n,srr,scc,xx);}}} 2842} // end of Clear Box Except for Row 2843 2844void Clear_Box_Except_Column(int n,int c,int b,String xx) { // clear the box of candidate, protecting column c 2845 for (int srr = Box_Coords[b][Row_Pointer]; srr <= Box_Coords[b][Row_Pointer]+2; srr++){ 2846 for (int scc = Box_Coords[b][Column_Pointer]; scc <= Box_Coords[b][Column_Pointer]+2; scc++){ 2847 if (scc != c){Candidate_Off(n,srr,scc,xx);}}} 2848} // end of Clear Box Except for Column 2849 2850void Clear_All_But_Marked(int rr,int cc,int BitMask_Result, String xxx){ // for this unit, clear candidates in bit mask BUT NOT CANDIDATES MARKED in CanCoord, CanMark 2851 bool Clear_Flag = true; // assume its OK to clear 2852 int Current_Bit = 0; 2853 for (int i = 0; i< 9; i++){ 2854 if (CanMark[i]){ if ((rr == CanCoord[i][Row_Pointer]) && (cc == CanCoord[i][Column_Pointer])){Clear_Flag = false;}}} 2855 if (Clear_Flag){ 2856 for (int n = 1; n <=9; n++){ // climb through the numbers 2857 Current_Bit = bitRead(BitMask_Result,n); // read the bit 2858 if (Current_Bit > 0){Candidate_Off(n,rr, cc, xxx); 2859 }}} // if on in the mask, clear it 2860} // end of Clear_All_But_Marked 2861 2862void ClearR_SWF(int n, String xxx){ // clear candidate from the three columns but NOT in the three rows 2863 byte counter = 0; 2864 bool Protect_R[9] ; // rows to NOT clear 2865 bool Clear_C[9]; // columns to clear EXCEPT for protected rows 2866 for (int i = 0; i < 9; i++) { 2867 Protect_R[i] = false; 2868 Clear_C[i] = false;} 2869 for (int i = 0; i < 9; i++){if (SWF[i][SWF_Member]) { 2870 Protect_R[i] = true; // its one of the rows 2871 for (int j = 0; j < SWF[i][SWF_Number_Found]; j++){ // and mark the columns 2872 Clear_C[SWF[i][SWF_Marks+j]] = true; }}} 2873 for (int r = 0; r < 9; r++){ 2874 for (int c = 0; c < 9; c++){ 2875 if (Clear_C[c] && !Protect_R[r]) { 2876 Candidate_Off(n,r,c, xxx); }}} // clear candidate this column if NOT one of the rows 2877}// end of SWF ClearR 2878 2879void ClearC_SWF(int n, String xxx){ // clear candidate from the three rows but NOT in the three columns 2880 byte counter = 0; 2881 bool Protect_C[9] ; // columns to NOT clear 2882 bool Clear_R[9]; // rows to clear EXCEPT for protected columns 2883 for (int i = 0; i < 9; i++) { 2884 Protect_C[i] = false; 2885 Clear_R[i] = false;} 2886 for (int i = 0; i < 9; i++){if (SWF[i][SWF_Member]) { 2887 Protect_C[i] = true; // its one of the rows 2888 for (int j = 0; j < SWF[i][SWF_Number_Found]; j++){ // and mark the columns 2889 Clear_R[SWF[i][SWF_Marks+j]] = true; }}} 2890 for (int r = 0; r < 9; r++){ 2891 for (int c = 0; c < 9; c++){ 2892 if (Clear_R[r] && !Protect_C[c]) { 2893 Candidate_Off(n,r,c, xxx); }}} // clear candidate this column if NOT one of the rows 2894}// end of SWF ClearC 2895 2896 2897// What follows are a lot of "Show" routines mostly used for Serial terminal display and debugging 2898 2899void Display_Debug(){ 2900 Show_Grid(); 2901 Show_Candidates(); 2902} // end of Display_Debug 2903 2904void Show_Grid(){ // Show the puzzle grid 2905 DDTsp(NL); 2906 DDTsp(SL); 2907 for (int c = 1; c <=9;c++){DDTv(" ",c,SL);} 2908 DDTsp(NL); 2909 DDTs(" ",SL); 2910 for (int c = 0; c <=9;c++){DDTs("____",SL);} 2911 DDTsp(NL); 2912 for (int r = 0; r < 9; r++){ 2913 DDTs(Row_Labels[r],SL); 2914 for (int c = 0; c <9;c++){ 2915 DDTv(" ",Puzzle_Grid[r][c],SL); } 2916 DDTsp(NL); } 2917} // end of show grid 2918 2919void Show_NRC(int n, int r, int c, bool CRR){ // show Row and Column label and # and either New Line or Same Line 2920 Show_RC(r,c,SL); 2921 if (CRR) {DDTv(" ",n,NL);} else {DDTv(" ",n,SL);} 2922} // end of Show NRC 2923 2924void Show_RC(int r, int c, bool CRR){ // show only row and column label with no number content 2925 DDTs(Row_Labels[r],SL); 2926 DDTn(c+1,SL); 2927 if (CRR) {DDTs("",NL);} else {DDTs("",SL);} 2928} // end of Show NRC // "Show" routines normally not used 2929 2930void Show_Candidates(){ 2931 for (int r = 0; r < 9; r++){ 2932 for (int c = 0; c< 9; c++){ 2933 Show_RC(r,c,SL); 2934 Show_PB(Candidates[r][c]); 2935 DDTsp(SL);} 2936 DDTsp(NL); } 2937} // end of show candidates 2938 2939void Show_PB(int n){ 2940 String digits[10] = {"0","1","2","3","4","5","6","7","8","9"}; 2941 DDTs(" {",SL); 2942 for (int i=1; i<=9;i++){if (bitRead(n,i)) {DDTs(digits[i],SL);}} 2943 DDTs("} ",SL); 2944} // end of showPB (Candidate print) 2945 2946 2947#ifdef Test_Show 2948 2949void Show_Single_Candidate(int r,int c){ 2950 DDTs(Row_Labels[r],SL); 2951 DDTv("",c+1,SL); 2952 DDTv( " Count ", Candidates_Count[r][c],SL); 2953 DDTs(" Candidate(s): ",SL); 2954 for (int n = 1; n<=9; n++){ 2955 if (If_Candidate_On(n,r,c))DDTv(" ",n,SL);} 2956 DDTsp(NL); 2957} // end of show single candidate 2958 2959void Show_Y(int i,int j, int k){ // debugging display for XY algorithm 2960 DDTv("Y Anchor Data - length ",RDC_Length,NL); // show X Y Search data based on 3 anchors 2961 DDTv("Pivot ",i,SL); 2962 DDTsp(SL); 2963 Show_RC(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],SL); 2964 DDTv(" ",RDC_Matrix[i][RDC_CurrCan]/10,SL); 2965 DDTv(" ",RDC_Matrix[i][RDC_CurrCan]%10,NL); 2966 DDTv("Wing 1 ",j,SL); 2967 DDTsp(SL); 2968 Show_RC(RDC_Matrix[j][Row_Pointer],RDC_Matrix[j][Column_Pointer],SL); 2969 DDTv(" ",RDC_Matrix[j][RDC_CurrCan]/10,SL); 2970 DDTv(" ",RDC_Matrix[j][RDC_CurrCan]%10,NL); 2971 DDTv("Wing 2 ",k,SL); 2972 DDTsp(SL); 2973 Show_RC(RDC_Matrix[k][Row_Pointer],RDC_Matrix[k][Column_Pointer],SL); 2974 DDTv(" ",RDC_Matrix[k][RDC_CurrCan]/10,SL); 2975 DDTv(" ",RDC_Matrix[k][RDC_CurrCan]%10,NL); 2976 DDTv(" Candidate to Clear is ",Y_Wing_Z,NL); 2977} // end of Show XY 2978 2979void Show_SWF(int n){ // debugging display for swordfish 2980 DDTv(" SWF data for candidate ",n,NL); 2981 for (int i = 0; i < 9; i++){ 2982 if (SWF[i][SWF_Number_Found] > 0){ 2983 DDTs("Row ",SL); 2984 DDTs(Row_Labels[i],SL); 2985 DDTv(" Member ",SWF[i][SWF_Member],SL); 2986 DDTv(" occurs [",SWF[i][SWF_Number_Found],SL); 2987 DDTs("] ",SL); 2988 for (int j = 0; j < SWF[i][SWF_Number_Found]; j++){DDTv(" ",SWF[i][SWF_Marks+j]+1,SL);} 2989 DDTsp(NL);}} 2990} // end of Show SWF 2991 2992void Show_Search_Data(int counter, int pairthruquad, int rcs, int bitmask, bool smark){ 2993 DDTv("Search counter ",counter,SL); 2994 if (bitmask >0) Show_PB(bitmask); 2995 DDTv(" PQT",pairthruquad,SL); 2996 if (rcs <9){DDTv(" In Row/Column/box",rcs,SL);} else {DDTsp(NL);} 2997 for (int j = 0; j < counter; j++){ 2998 DDTv("Index ",j,SL); 2999 DDTv(" # candidates ",CanCount[j],SL); 3000 Show_PB(CanCand[j]); 3001 if (smark){ DDTv(" Mark ",CanMark[j],SL); 3002 DDTsp(NL);} 3003 Show_RC(CanCoord[j][Row_Pointer],CanCoord[j][Column_Pointer],NL); } 3004} // end of Show Search Data 3005 3006void Show_RDC(){ // show recursive descent data for debugging 3007 DDTv("Length is ",RDC_Length,NL); 3008 DDTsp(NL); 3009 for (int i = 0; i < RDC_Length; i++){ 3010 DDTv("Number ",i,SL); 3011 DDTv(" # of candidates ",Candidates_Count[RDC_Matrix[i][Row_Pointer]][RDC_Matrix[i][Column_Pointer]],SL); 3012 Show_RC(RDC_Matrix[i][Row_Pointer],RDC_Matrix[i][Column_Pointer],SL); 3013 Show_PB(Candidates[RDC_Matrix[i][Row_Pointer]][RDC_Matrix[i][Column_Pointer]]); 3014 DDTsp(NL); 3015 } 3016} // end of Show_RD 3017 3018void Show_X(int MC){ // debug x chain 3019 DDTv("Candidate is ",MC,SL); 3020 DDTv(" and Length is ",FC_Ptr,NL); 3021 for (int i = 0; i< FC_Ptr; i++){ 3022 Show_RC(FC_R[i],FC_C[i],SL); 3023 DDTv(" #candidates ", FC_CN[i][FC_1],SL); 3024 DDTsp(SL); 3025 Show_PB(Candidates[FC_R[i]][FC_C[i]]); 3026 DDTsp(NL); 3027 } 3028} // end of Show X 3029 3030 void Show_FC(int My_Length){ 3031 for (int i = 0; i <My_Length; i++){ 3032 Show_RC(FC_R[i],FC_C[i],SL); 3033 DDTv(" ",FC_CN[i][FC_1],SL); 3034 DDTsp(SL); 3035 Show_FCUD(i,FC_1,SL); 3036 DDTs(" / ",SL); 3037 DDTv(" ",FC_CN[i][FC_2],SL); 3038 DDTsp(SL); 3039 Show_FCUD(i,FC_2,NL); 3040 } 3041 } // end of Show_FC 3042 3043 void Show_FCUD(int index, int LR, bool CRR ){ 3044 if (FC_UD[index][LR] == (FC_UpandDown)){ DDTs("Up & Down",SL);} 3045 else { 3046 if (FC_UD[index][LR] == FC_Up){ DDTs("Up",SL);} 3047 if (FC_UD[index][LR] == FC_Down){ DDTs("Down",SL);} 3048 if (FC_UD[index][LR] == 0) DDTs("None",SL);} 3049 if(CRR){DDTsp(NL);} else {DDTsp(SL);} 3050 } // end of Show Forcing chain Up and Down 3051 3052void Show_RC_Node(byte XC_Index){ 3053 int RC1 = FC_Used_List[XC_Index] /100; // upper half 3054 int RC2 = FC_Used_List[XC_Index] %100; 3055 if (RC1 > 0){Show_RC(RC1/10,RC1%10,SL);} 3056 if (RC2 > 0){Show_RC(RC2/10,RC2%10,SL);} 3057 DDTsp(NL); 3058}// end of Show_RC_Node 3059 3060 3061#endif // Test_Show 3062 3063void T_Number(int r, int c, int n, int MyColor){ 3064 int MyHOffset = 0; // horizontal (x) offset to the Box 3065 int MyVOffset = 0; // vertical (y) offset to the Box 3066 int MyBoxH_Offset = 0; // intra-box digit (1,2,3) horizontal offset 3067 int MyBoxV_Offset = 0; // intra-box digit (1,2,3) vertical offset 3068 3069 switch (r){ // on row given set which vertical line we are 'below' 3070 case 0: case 1: case 2: MyVOffset = Ver1; 3071 break; 3072 case 3: case 4: case 5: MyVOffset = Ver2; 3073 break; 3074 case 6: case 7: case 8: MyVOffset = Ver3; 3075 break; 3076 default: 3077 DDTs("Puzzle Grid T Number Impossible Inputs",NL); 3078 break; 3079 } // end of Row vertical set 3080 switch (c){ // on column, pick the box (left hand 'line') we are in 3081 case 0: case 1: case 2: MyHOffset = Hor1; 3082 break; 3083 case 3: case 4: case 5: MyHOffset = Hor2; 3084 break; 3085 case 6: case 7: case 8: MyHOffset = Hor3; 3086 break; 3087 default: 3088 DDTs("Puzzle Grid T Number Impossible Inputs",NL); 3089 while(1){;} 3090 break; 3091 } // end of Column horizontal set 3092 3093 MyBoxV_Offset = BoxOffsets[r % 3]; // starting with 'box line' pick depth (row within box 1,2,3) 3094 MyBoxH_Offset = BoxOffsets[c % 3]; // starting with the box's left line, offset for digit 3095 tft1.setTextSize(DigitTextSize); // set grid text size 3096 tft1.setTextColor(Grid_Color); // set to background color (not selected color) 3097 for (int i = 0; i <=9; i++){ // kludge to ensure prior digit erased 3098 tft1.setCursor(MyHOffset+MyBoxH_Offset,MyVOffset+MyBoxV_Offset); // by printing ALL digits in background color 3099 tft1.print(i); } 3100 tft1.print('?'); // as well as the ? cursor 3101 tft1.setTextColor(MyColor); // set to MY color selected in call 3102 tft1.setCursor(MyHOffset+MyBoxH_Offset,MyVOffset+MyBoxV_Offset); // reset to proper position one last time 3103 if (n > 0 && n<10){ // if its zero, leave the square blank 3104 tft1.print(n);} else { // and finally print the number 3105 if (n == 10){ 3106 tft1.print('?');}} 3107 3108} // end of T_Number 3109 3110void T_Display_Setup(){ // reset to initial puzzle 3111 tft1.fillScreen(Grid_Color); // fill top 3112 tft1.drawRect(0, 0, 240, 240, ILI9341_RED); // draw grid and box separators 3113 tft1.drawRect(1, 1, 238, 238, ILI9341_RED); 3114 tft1.drawRect(2, 2, 236, 236, ILI9341_RED); 3115 tft1.drawFastVLine(159, 0, 239, ILI9341_RED); 3116 tft1.drawFastVLine(79, 0, 239, ILI9341_RED); 3117 tft1.drawFastHLine(0, 79, 239, ILI9341_RED); 3118 tft1.drawFastHLine(0, 159, 239, ILI9341_RED); 3119 tft1.fillRect(0,241,241,320,Display1_Color); // fill bottom display area 3120} // end of T_Display_Setup 3121 3122void T_MsgSetup1(int DC1, int DC2){ // initial display 3123 String Time_Text = " "; 3124 String Date_Text = " "; 3125 int Phour = 0; 3126 DateTime now = rtc.now(); // Reload from RTC 3127 Date_Time_Num_Year = now.year(); 3128 Date_Time_Num_Month = now.month(); 3129 Date_Time_Num_Day = now.day(); 3130 Date_Time_Num_Hour = now.hour(); 3131 Date_Time_Num_Minute = now.minute(); 3132 Date_Time_Num_Second = now.second(); 3133 Date_Time_Num_DOW = now.dayOfTheWeek(); 3134 if (Old_Minute != Date_Time_Num_Minute){ 3135 tft1.fillScreen(ILI9341_RED); 3136 tft2.fillScreen(ILI9341_BLUE); // fill background color 3137 fc = rtc.getTemperature(); // currently not used 3138 fc = ((fc * (9.0/5.0)) + 32.0 )+ fc_adjust; // convert c to f F = C * 9/5 + 32 and adjustment 3139 ifc = fc; // convert to integer - note will always round down 3140 Old_Minute = Date_Time_Num_Minute; 3141 } 3142 3143 tft1.setTextSize(3); // set text size (was 5) 3144 tft1.setTextColor(DC1); //set to text color 3145 tft2.setTextSize(3); // set text size 3146 tft2.setTextColor(DC2); //set to text color 3147 tft2.setCursor(5,140); 3148 Phour = Date_Time_Num_Hour; 3149 if (Phour >= 12){Phour = Phour-12;} 3150 if (Phour == 0){Phour = 12;} 3151 Time_Text = Time_Text + Phour; 3152 Time_Text = Time_Text + ":"; 3153 if (Date_Time_Num_Minute < 10) Time_Text = Time_Text + "0"; 3154 Time_Text = Time_Text + Date_Time_Num_Minute; 3155 tft1.setTextSize(4); 3156 tft1.setCursor(45,40); 3157 tft1.println(F("Sudoku")); 3158 tft1.setTextSize(4); 3159 tft1.setCursor(45,82); 3160 tft1.println(F("Solver")); 3161 tft1.setTextSize(5); 3162 tft1.setCursor(20,140); 3163 tft1.println(Time_Text); 3164 tft1.setTextSize(2); 3165 tft1.setCursor(35,220); 3166 tft1.println(F("Hold Any Key")); 3167 tft1.setCursor(35,245); 3168 tft1.println(F("for Main Menu")); 3169 tft2.setCursor(15,80); 3170 tft2.println(daysOfTheWeek[Date_Time_Num_DOW]); 3171 tft2.setCursor(0,140); 3172 Date_Text = Date_Text + monthsOfTheYear[Date_Time_Num_Month-1]; 3173 Date_Text = Date_Text + " "; 3174 Date_Text = Date_Text + Date_Time_Num_Day; 3175 tft2.println(Date_Text); 3176 Date_Text = ifc; 3177 Date_Text = Date_Text + "F"; 3178 tft2.setCursor(65,200); 3179 tft2.println(Date_Text); 3180 3181 } // end of T_MsgSetup1 3182 3183void T_Msg1(String xxx){ 3184 tft1.fillRect(0,241,241,320,Display1_Color); // clear bottom display area 3185 tft1.setCursor(((Msg1TextMax-xxx.length())*Msg1PW), Hor4+Msg1Offset); 3186 tft1.setTextSize(Msg1TextSize); // set text size and color 3187 tft1.setTextColor(Msg1_Color); 3188 tft1.println(xxx);} 3189 3190void T_Msg2(String xxx1, String xxx2){ 3191 tft1.fillRect(0,241,241,320,Display1_Color); 3192 tft1.setCursor(((Msg2TextMax-xxx1.length())*Msg2PW), Hor4+Msg2Offset); 3193 tft1.setTextSize(Msg2TextSize); 3194 tft1.setTextColor(Msg1_Color); 3195 tft1.println(xxx1); 3196 tft1.setCursor(((Msg2TextMax-xxx2.length())*Msg2PW), Hor4+(3*Msg2Offset)); 3197 tft1.println(xxx2);} 3198 3199void T_Msg3(String xxx1, String xxx2, String xxx3){ 3200 tft1.fillRect(0,241,241,320,Display1_Color); 3201 tft1.setCursor(((Msg3TextMax-xxx1.length())*Msg3PW), Hor4+Msg3Offset); 3202 tft1.setTextSize(Msg3TextSize); 3203 tft1.setTextColor(Msg1_Color); 3204 tft1.println(xxx1); 3205 tft1.setCursor(((Msg3TextMax-xxx2.length())*Msg3PW), Hor4+(Msg3Offset+25)); 3206 tft1.println(xxx2); 3207 tft1.setCursor(((Msg3TextMax-xxx3.length())*Msg3PW), Hor4+(Msg3Offset+50)); 3208 tft1.println(xxx3);} 3209 3210void T_Scroll_Message (String SMString){ // scrolls progress on 2nd display 3211 tft2.setTextSize(T_ScrollTextSize); 3212 tft2.setTextColor(Msg2_Color); 3213 if ((T_Scrolling_Pointer == 0) && (!Scrolling_Text_Just_Cleared)){ 3214 delay(Scrolling_Text_Page_Delay); 3215 tft2.fillRect(0,0,240,320,Display2_Color);} 3216 tft2.setCursor(T_ScrollTextOffsetHor, (T_Scrolling_Pointer*11)+T_ScrollTextOffsetVert); 3217 tft2.print (SMString); 3218 Scrolling_Text_Just_Cleared = false; // clear flag 3219 delay(Scrolling_Text_Line_Delay); 3220 T_Scrolling_Pointer = (T_Scrolling_Pointer+1)%T_Scrolling_Limit; // increment pointer in queue 3221} // end of M_Scrolling Message 3222 3223void Clear_Scroll_Display(){ 3224 tft2.fillRect(0,0,240,320,Display2_Color); // clear by filling 3225 T_Scrolling_Pointer = 0; // reset to first line 3226 Scrolling_Text_Just_Cleared = true; // and set flag (so we don't get a beginning delay) 3227} // end of CSD 3228 3229String FDM(String S1, int SMValue, String S2){ // form a display message 3230 String DestString = " "; // create string to receive text and number 3231 DestString = S1+SMValue+S2; // string, value, string 3232 return DestString;} 3233 3234String FDM_NRC(int n,int r,int c){ // form row, column, number string 3235 String DestString = " "; // create string to receive text and number 3236 DestString = DestString+ Row_Labels[r]+(c+1)+ " "+n; 3237 return DestString; 3238} // end of FDM_NRC 3239 3240String FDM_RC(int r,int c){ // form row, column, 3241 String DestString = " "; // create string to receive text and number 3242 DestString = DestString+ Row_Labels[r]+(c+1); 3243 return DestString; 3244} // end of FDM_NRC 3245 3246String FDM_FTC(int n, int r, int c, String x){ // display for Fill The Cell 3247 String DestString = x + " is placing "; // initialize string 3248 DestString = DestString + n; 3249 DestString = DestString + " in"; 3250 DestString = DestString + FDM_RC(r, c); 3251 return DestString; 3252} // end of FDM fill the cell 3253 3254String FDM_CC(int n, int r, int c, String x){ // display for Clearing Candidate 3255 String DestString = x + " clears candidate "; // initialize string 3256 DestString = DestString + n; 3257 DestString = DestString + " in"; 3258 DestString = DestString + FDM_RC(r, c); 3259 return DestString; 3260} // end of FDM clearing candidate 3261 3262String FDM_DT(int Yi, int Mi, int Di, int Hi, int Mii){ // display for date & time 3263 String DestString = ""; // initialize string 3264 DestString = DestString + Mi; 3265 DestString = DestString + "/"; 3266 DestString = DestString + Di; 3267 DestString = DestString + "/"; 3268 DestString = DestString + Yi; 3269 DestString = DestString + " "; 3270 DestString = DestString + Hi; 3271 DestString = DestString + ":"; 3272 if (Mii <10) {DestString = DestString + "0";} 3273 DestString = DestString + Mii; 3274 if (Hi >= 12){DestString = DestString + "pm";} else {DestString = DestString + "am";} 3275 return DestString; // return mm/dd/yyyy hh:mm 3276} // end of FDM Build Date Time string 3277 3278String FDM_DT_YR(int Yi){ // display for year only 3279 String DestString = ""; // initialize string 3280 DestString = DestString + Yi; 3281 return DestString; // return mm/dd/yyyy hh:mm 3282} // end of FDM Year Only 3283 3284String FDM_DT_MD(int Mi, int Di){ // display for month and date 3285 String DestString = ""; // initialize string 3286 DestString = DestString + Mi; 3287 DestString = DestString + "/"; 3288 DestString = DestString + Di; 3289 return DestString; // return mm/dd/yyyy hh:mm 3290} // end of FDM Month and Day 3291 3292String FDM_DT_HR(int Hi, int Mii,int Si){ // display for Time 3293 String DestString = ""; // initialize string 3294 if (Hi == 0) {DestString = DestString + "12";} else {DestString = DestString + Hi;} 3295 DestString = DestString + ":"; 3296 if (Mii <10) {DestString = DestString + "0";} 3297 DestString = DestString + Mii; 3298 DestString = DestString + ":"; 3299 if (Si <10) {DestString = DestString + "0";} 3300 if (Hi <12) {DestString = DestString + " am";} else {DestString = DestString + " pm";} 3301 DestString = DestString + Si; 3302 return DestString; 3303 } // end of FDM show time only 3304 3305// Keyboard routines 3306 3307void K_Read(){ 3308 char key = K_Keypad.getKey(); 3309 if (key != NO_KEY){ 3310 K_Char = key;} 3311 } // end of Key Read 3312 3313void K_Get_Puzzle(){ // get the numbers for the puzzle 3314 ImageReturnCode stat; // Status from image-reading functions 3315 bool Getting_Input = true; 3316 bool Display_Cursor = false; 3317 bool Display_Cursor_Last; 3318 DC_Timer = millis(); // timer between flashes of cell vs cursor 3319 int r = 0; // initial row and column 3320 int c = 0; 3321 byte Cell_Entry = 0; 3322 3323 T_Number(r,c,10,Entry_Color); // display timer 3324 K_Set_Message(); // set progress display 3325 3326 #ifdef Test_Mode2 // if preloading a puzzle, 3327 Clear_Puzzle(); 3328 T_Display_Setup(); // clear and set up color display 3329 Transfer_Test(K_Demo_Current); // increment and wrap 3330 for (r = 0; r < 9; r++){ 3331 for (c = 0; c < 9; c++){T_Number(r,c,Puzzle_Grid[r][c],Constant_Color);}} //update display 3332 Examine_Puzzle(); // analyze the puzzle 3333 Puzzle_Check(); // check the puzzle 3334 r = 0; 3335 c = 0; 3336 #endif // Test_Mode2 3337 3338 T_Msg2("Puzzle Entry","Mode"); 3339 delay(1000); 3340 T_Msg2("Enter",FDM_RC(r,c)); 3341 3342 while (Getting_Input){ 3343 3344 K_Read(); // get a keyboard input 3345 3346 Display_Cursor_Last = Display_Cursor; // save current value 3347 if ((millis() - DC_Timer) >= K_DC_Interval){ // if time to switch number vs. cursor 3348 DC_Timer = millis(); // reset timer and 3349 Display_Cursor = !Display_Cursor;} // reverse it 3350 if (Display_Cursor_Last != Display_Cursor){ // if it switched, 3351 if (Display_Cursor){T_Number(r,c,10,Entry_Color);} // show cursor, or 3352 else {T_Number(r,c,Puzzle_Grid[r][c],Constant_Color);}} // show current entry 3353 3354 switch (K_Char){ // dispatch on input 3355 3356 case K_Backspace: 3357 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // * = backspace 3358 c--; // check for backspace and decrement column 3359 if (c < 0){ r--; // if its a negative column, decrement row and 3360 c = 8; // and put it into last column 3361 if (r < 0) {r=8;}} // if negative row, go back to the end (I9) 3362 T_Msg2("Enter",FDM_RC(r,c)); 3363 K_Char = 0; // clear input character 3364 break; 3365 3366 case K_Forward: 3367 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // # = restore cell and go forward 3368 c = c+ (3-(c%3)); // to next Box 3369 if (c == 9){r++; // if past last column 3370 c=0; // reset column 3371 if (r == 9) {r=0; // if off the end, then 3372 c=0;}} 3373 T_Msg2("Enter",FDM_RC(r,c)); 3374 K_Char = 0; 3375 break; 3376 3377 case K_Input: // A = clear input and start over 3378 K_Char = 0; 3379 Clear_Puzzle(); // clear any puzzle input 3380 T_Display_Setup(); // clear and create color display 3381 T_Msg2("Puzzle", "Cleared"); 3382 r = 0; 3383 c = 0; 3384 T_Msg2("Enter",FDM_RC(r,c)); 3385 break; 3386 3387 case K_Solve: // B = Done here, return to main menu 3388 T_Msg2("Checking", "Puzzle"); 3389 K_Char = 0; 3390 Examine_Puzzle(); 3391 if (Filled == 0){T_Scroll_Message(F("Returning to Main Menu")); 3392 T_Msg3("Puzzle is Empty","Returning","To Main"); 3393 Getting_Input = false; 3394 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // get rid of cursor 3395 delay(Scroll_Display_Delay); 3396 break;} 3397 if (Puzzle_Check()){T_Msg3("Puzzle Appears OK","Returning","To Main Menu"); 3398 if (!Show_SD_Image){ 3399 T_Scroll_Message(F("Puzzle Appears Good")); 3400 T_Scroll_Message(F("Returning to Main Menu")); 3401 T_Scroll_Message(F("Press B there to Solve"));} 3402 Getting_Input = false; 3403 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // get rid of cursor 3404 delay(Scroll_Display_Delay); } 3405 else {T_Msg3("Puzzle Error","Edit It","Or A to Reset/Wipe"); 3406 T_Scroll_Message(F("Puzzle Error - Edit It or A to Reset")); 3407 delay(Scroll_Error_Delay);} 3408 break; 3409 3410 case K_Check: // C = restore cell and go UP one line 3411 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // D = restore cell and down one line 3412 r--; // increment row and 3413 c=0; // reset column 3414 if (r < 0) {r = 8;} // if off the start, go back to end 3415 T_Msg2("Enter",FDM_RC(r,c)); 3416 K_Char = 0; 3417 break; 3418 3419 case K_Demo: // is it go down a row)? 3420 T_Number(r,c,Puzzle_Grid[r][c],Constant_Color); // D = restore cell and down one line 3421 r++; // increment row and 3422 c=0; // reset column 3423 if (r == 9) {r = 0;} // if off the end, go back to beginning 3424 T_Msg2("Enter",FDM_RC(r,c)); 3425 K_Char = 0; 3426 break; 3427 3428 default: 3429 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3430 Cell_Entry = K_Char - K_Fnumber; 3431 K_Char = 0; //clear 3432 if (RD_Check(Cell_Entry,r,c)|| Cell_Entry == 0){ // zero cells and legal entries get a pass 3433 Puzzle_Grid[r][c] = Cell_Entry; // store it 3434 T_Number(r,c,Cell_Entry,Constant_Color); //update display 3435 if (Cell_Entry > 0) {T_Msg2(FDM("Entered ",Cell_Entry," in"),FDM_RC(r,c));} else 3436 {T_Msg2("Skipped",FDM_RC(r,c));} 3437 c++; // increment one cell 3438 if (c == 9){r++; // if past last column 3439 c=0; // reset column 3440 if (r == 9) {r=0; // if off the end, then 3441 c=0;}} 3442 } else { 3443 T_Msg3("Puzzle Entry","Error or Conflict", FDM_NRC(Cell_Entry,r,c)); 3444 Clear_Scroll_Display(); 3445 T_Scroll_Message(F("Puzzle Entry Error")); 3446 T_Scroll_Message(FDM("Can't Put ",Cell_Entry," there")); 3447 T_Scroll_Message(F("Please Edit or A to Start Over")); 3448 delay(Scroll_Error_Delay); 3449 K_Set_Message();} 3450 } 3451 break; 3452 } // end of switch on K_Char 3453 } // done with input 3454} // end get puzzle 3455 3456void K_Set_Message(){ 3457 Clear_Scroll_Display(); 3458 if (Show_SD_Image){reader.drawBMP("/rr5.bmp", tft2, 0, 0); } // use image if acccessible 3459 else 3460 { T_Scroll_Message(F("Ready for Puzzle Input")); // constant strings stored in PROGMEM 3461 T_Scroll_Message(F("A - Clear Puzzle and Restart Input")); // using F Macro 3462 T_Scroll_Message(F("B - Check Puzzle and Return")); 3463 T_Scroll_Message(F(" to Command Level")); 3464 T_Scroll_Message(F("C - Go Up One Row")); 3465 T_Scroll_Message(F("D - Go Down One Row")); 3466 T_Scroll_Message(F("* - Back Space Cursor")); 3467 T_Scroll_Message(F("# - Forward Cursor to Next Box")); 3468 T_Scroll_Message(F("1-9 - Cell Entry")); 3469 T_Scroll_Message(F("0 - Blank/Skipped Cell")); 3470 } // end of K Set Message 3471} 3472 3473void K_Configure(){ 3474 ImageReturnCode stat; // Status from image-reading functions 3475 T_Msg2("Setup ","Mode"); 3476 Clear_Scroll_Display(); 3477 if (Show_SD_Image){reader.drawBMP("/rr6.bmp", tft2, 0, 0);} // use image if acccessible 3478 else 3479 {T_Scroll_Message(F("Configuration Mode")); 3480 T_Scroll_Message(F("A - Toggle Use Algorithms")); 3481 T_Scroll_Message(F("B - Toggle Show Clears & Placement")); 3482 T_Scroll_Message(F("C - Exit Setup")); 3483 T_Scroll_Message(F("D - Toggle Single/Multiple Demos")); 3484 T_Scroll_Message(F("# - Set Date and Time"));} 3485 3486 bool Getting_Input = true; 3487 3488 while (Getting_Input){ 3489 3490 K_Read(); // get a keyboard input 3491 delay(10); 3492 3493 switch (K_Char){ // dispatch on input 3494 3495 case K_Input: // A = Toggle Use/Don't Use algorithms before descend/backtrack 3496 Use_Algorithms = !Use_Algorithms; // toggle 3497 if (Use_Algorithms){ 3498 T_Msg2("Algorithms","On"); 3499 } else 3500 {T_Msg2("Algorithms","Off"); 3501 } 3502 break; 3503 3504 case K_Solve: // B = Toggle showing progress screens 3505 Show_Clear_Place = !Show_Clear_Place; // toggle 3506 if (Show_Clear_Place){ 3507 T_Msg3("Progress","Display","On"); 3508 } else 3509 {T_Msg3("Progress","Display","Off"); 3510 } 3511 break; 3512 3513 case K_Check: // C = Exit Configuration 3514 T_Msg3("Exiting","From", "Setup"); 3515 K_Char = 0; 3516 Getting_Input = false; 3517 delay(Scroll_Display_Delay); 3518 break; 3519 3520 case K_Demo: 3521 Use_All_Demos = !Use_All_Demos; // toggle 3522 if (Use_All_Demos){ 3523 T_Msg3("Demo All",FDM(" ",Puzzles_Number," "),"Puzzles"); 3524 } else 3525 {T_Msg3("Demo Single","Puzzle","Only"); 3526 } 3527 break; 3528 3529 case K_Forward: // # enter date and time 3530 K_Char = 0; // clear the # that got us here 3531 T_Msg3("Enter","Year","YYYY"); 3532 Date_Time_Num = 0; 3533 for (int i = 1; i<=4; i++){ // form 6 digit number 3534 while(K_Char == 0){K_Read();} 3535 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3536 Date_Time_Num = (Date_Time_Num *10) + (K_Char - K_Fnumber); 3537 K_Char = 0; }} //clear 3538 Date_Time_Num_Year = Date_Time_Num; // remember year 3539 T_Msg3("Enter","Month & Day","MMDD"); 3540 K_Char = 0; // clear any input 3541 Date_Time_Num = 0; 3542 for (int i = 1; i<=4; i++){ // form 6 digit number 3543 while(K_Char == 0){K_Read();} 3544 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3545 Date_Time_Num = (Date_Time_Num *10) + (K_Char - K_Fnumber); 3546 K_Char = 0; }} //clear 3547 Date_Time_Num_Month = Date_Time_Num/100; // remember month, and 3548 Date_Time_Num_Day = Date_Time_Num%100; // day 3549 T_Msg3("Enter 24hr","Time","HHMM"); 3550 Date_Time_Num = 0; 3551 K_Char = 0; // clear any input 3552 for (int i = 1; i<=4; i++){ // form 6 digit number 3553 while(K_Char == 0){K_Read();} 3554 if (K_Char >= K_Fnumber && K_Char <= K_Lnumber){ // if its a digit 3555 Date_Time_Num = (Date_Time_Num *10) + (K_Char - K_Fnumber); 3556 K_Char = 0; }} //clear 3557 Date_Time_Num_Hour = Date_Time_Num/100; // remember hour and 3558 Date_Time_Num_Minute = Date_Time_Num%100; // minute 3559 3560 Date_Time_Num_Valid = true; // assume its all valid 3561 if ( (Date_Time_Num_Year < 2021 || Date_Time_Num_Year > 2099) || (Date_Time_Num_Month == 0 || Date_Time_Num_Month > 12) || (Date_Time_Num_Day == 0 || Date_Time_Num_Day >31) || 3562 (Date_Time_Num_Hour >= 24) || (Date_Time_Num_Minute > 59) ) Date_Time_Num_Valid = false; 3563 if ( Date_Time_Num_Month == 2 && Date_Time_Num_Day > 29) {Date_Time_Num_Valid = false; } 3564 if ( (Date_Time_Num_Month == 4 || Date_Time_Num_Month == 6 || Date_Time_Num_Month == 9 || Date_Time_Num_Month == 11) && (Date_Time_Num_Day > 30) ){Date_Time_Num_Valid = false; } 3565 if (Date_Time_Num_Valid) { 3566 T_Msg3("Date & Time","Are Set To", FDM_DT(Date_Time_Num_Year,Date_Time_Num_Month,Date_Time_Num_Day,Date_Time_Num_Hour,Date_Time_Num_Minute)); 3567 rtc.adjust(DateTime(Date_Time_Num_Year,Date_Time_Num_Month,Date_Time_Num_Day,Date_Time_Num_Hour,Date_Time_Num_Minute, 0)); // set clock, zeroed out seconds 3568 } else 3569 {T_Msg2("Invalid","Entry");} 3570 K_Char = 0; // clear any input 3571 break; 3572 3573 case K_Backspace: 3574 default: 3575 break; 3576 3577 } // end of switch on K_Char 3578 K_Char =0; 3579 } // done with input 3580 return; 3581}// end of K_Configure 3582 3583void K_Return_To_Main(){ // message displayed when returning to top menu 3584 T_Msg3("Back to","Main Menu","Ready"); 3585 K_R_T_M_Scroll_Only(); 3586} // end of Return to Main 3587 3588void K_R_T_M_Scroll_Only() { 3589 ImageReturnCode stat; // Status from image-reading functions 3590 3591 if (Show_SD_Image){reader.drawBMP("/rr4.bmp", tft2, 0, 0);} // use image if acccessible 3592 else 3593 {Clear_Scroll_Display(); // otherwise use new progress display 3594 T_Scroll_Message(F("Ready for Command")); 3595 T_Scroll_Message(F("A - Load Puzzle")); 3596 T_Scroll_Message(F("B - Solve Puzzle You Loaded")); 3597 T_Scroll_Message(F("C - Configure Setup")); 3598 T_Scroll_Message(F("D - Demo Built in Puzzle")); 3599 T_Scroll_Message(F("* - Sleep Mode"));}} 3600 3601void Transfer_Test(int Current_Puzzle ){ //load appropriate puzzle from Sudoku.Puzzles.h 3602 strcpy_P(Puzzle_Buffer, (char *)pgm_read_word(&(Puzzle_Names_Table[Current_Puzzle]))); // Necessary casts and dereferencing, copy from PROGMEM. 3603 Puzzle_Name = String(Puzzle_Buffer); // and convert buffer to string 3604 Cell_Pointer = int(Puzzles_List[Current_Puzzle]); // get pointer to input puzzle grid 3605 for (int r = 0; r <9; r++){ // for all rows and columns 3606 for (int c = 0; c<9; c++){ // transfer the 81 bytes 3607 Cell_Entry = pgm_read_byte_near(Cell_Pointer); 3608 if (Cell_Entry > 19 || Cell_Entry < 1 ){ 3609 DDTs("Test/Demo Puzzle Bad Entry",NL); 3610 Show_NRC(Cell_Entry,r,c,NL); 3611 T_Scroll_Message(F("Test/Demo Puzzle Bad Entry")); 3612 T_Msg3("Demo/Test","Puzzle","Error"); 3613 Display_Debug(); 3614 while(1){;}} 3615 if (Cell_Entry < 10) {Puzzle_Grid[r][c] = Cell_Entry;} else {Puzzle_Grid[r][c] = 0;} // transfer all non-solution entries 3616#ifdef Test_Mode3 3617 if (Cell_Entry < 10){Solution_Grid[r][c] = Cell_Entry;} // solution gets all filled squares 3618 else {Solution_Grid[r][c] = Cell_Entry-10;} 3619#endif 3620 Cell_Pointer++;}} // plus cells coded +10 to indicate its solution only 3621 3622 } // end of transfer test 3623 3624void Run_Test() { // run demo puzzle/test puzzle 3625 Clear_Puzzle(); // puzzle 3626 Clear_Scroll_Display(); // new progress display 3627 T_Display_Setup(); // clear and set up color display 3628 Transfer_Test(K_Demo_Current); // increment and wrap 3629 T_Msg3("Fetching",FDM("Puzzle ",K_Demo_Current+1,""),Puzzle_Name); 3630 T_Scroll_Message("Running Demonstration Puzzle"); 3631 T_Scroll_Message(FDM(Puzzle_Name+" #",K_Demo_Current+1," Loaded")); 3632 DDTs(FDM(Puzzle_Name+" #",K_Demo_Current+1," Loaded"),NL); 3633 Examine_Puzzle(); // analyze the puzzle 3634 Solve_Puzzle(); // and solve it 3635 delay(Scroll_Display_Delay); 3636} // end of Run Test 3637 3638void Sleep_Demo(){ // startup / sleep demo 3639 3640 int Demo_Iteration = 0; // in case alt beginning - loops through positions 3641 int Robot_Iteration = 0; 3642 bool Screen_One = true; 3643 bool Need_Background = true; 3644 ImageReturnCode stat; // Status from image-reading functions 3645 3646 K_Char = 0; // flush any keyboard character 3647 do { // Keyboard will load a character 3648 K_Read(); // any input (any key) 3649 if (Show_SD_Image){ // if SD card loaded working 3650 switch (Robot_Iteration){ 3651 case 0: 3652 if (Screen_One) {reader.drawBMP("/rr1.bmp", tft1, 0, 0); } else {reader.drawBMP("/rr1.bmp", tft2, 0, 0);} 3653 break; 3654 case 1: 3655 if (Screen_One) {reader.drawBMP("/rr2.bmp", tft1, 0, 0);} else {reader.drawBMP("/rr2.bmp", tft2, 0, 0);} 3656 break; 3657 case 2: 3658 if (Screen_One) {reader.drawBMP("/rr3.bmp", tft1, 0, 0);} else {reader.drawBMP("/rr3.bmp", tft2, 0, 0);} 3659 break;} 3660 Robot_Iteration = (Robot_Iteration+1)%SD_Robots; 3661 Screen_One = !Screen_One; } 3662 else 3663 {if (Need_Background){ 3664 tft1.fillScreen(ILI9341_RED); 3665 tft2.fillScreen(ILI9341_BLUE); 3666 Need_Background = false;} // fill background color 3667 T_MsgSetup1(ILI9341_BLUE,ILI9341_RED); // when not loaded or working 3668 Demo_Iteration = (Demo_Iteration + 1)%4; // four rotations 3669 delay(250);} 3670 } while (K_Char == 0); // end of display loop 3671 tft1.setRotation(0); // restore any "odd" orientation 3672 tft2.setRotation(0); 3673 T_Display_Setup(); // clear and create color display 3674 K_Char = 0; // flush any keyboard character 3675}// end of sleep demo 3676
Downloadable files
Sudoku Solver Components and Connection
Describes connections in lieu of Fritzing diagram
Sudoku Solver Components and Connection
Sudoku Solver Code
Should be loaded in software section but I keep getting errors so here it is
Sudoku Solver Code
Sudoku solver companion (.h) file
contains puzzles for demo and test
Sudoku solver companion (.h) file
Sudoku solver companion (.h) file
contains puzzles for demo and test
Sudoku solver companion (.h) file
Sudoku Solver Code
Should be loaded in software section but I keep getting errors so here it is
Sudoku Solver Code
Sudoku Solver Components and Connection
Describes connections in lieu of Fritzing diagram
Sudoku Solver Components and Connection
Sudoku solver list of implemented solver algorithms
Describes the solvers that are implemented in the program
Sudoku solver list of implemented solver algorithms
Comments
Only logged in users can leave comments