1/* 2 Project: Sudoku 3 Sudoku.m 4 5 Copyright (C) 2007-2011 The Free Software Foundation, Inc 6 7 Author: Marko Riedel 8 9 This application is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public 11 License as published by the Free Software Foundation; either 12 version 3 of the License, or (at your option) any later version. 13 14 This application is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 Library General Public License for more details. 18 19 You should have received a copy of the GNU General Public 20 License along with this library; if not, write to the Free 21 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22*/ 23 24#import "Sudoku.h" 25#import "DigitSource.h" 26 27#ifdef __MINGW__ 28#define srand48 srand 29#define lrand48 rand 30#endif 31 32@implementation Sudoku 33 34- (int)computescore:(fieldptr)fp 35{ 36 int d, res = 0; 37 38 for(d=0; d<9; d++){ 39 if(fp->nbdigits[d] != NULL){ 40 res++; 41 } 42 } 43 44 fp->score = res; 45 return res; 46} 47 48#define RETR(_x, _y) \ 49(data[_x][_y].puzzle==-1 ? data[_x][_y].guess : data[_x][_y].puzzle) 50 51- (int)retrX:(int)x Y:(int)y 52{ 53 return 54 (data[x][y].puzzle==-1 ? data[x][y].guess : data[x][y].puzzle); 55} 56 57- (BOOL)completed 58{ 59 int x, y; 60 61 for(x=0; x<9; x++){ 62 for(y=0; y<9; y++){ 63 if(RETR(x, y)==-1){ 64 return NO; 65 } 66 } 67 } 68 69 return YES; 70} 71 72- init 73{ 74 int x, y, pos; 75 76 allclues = 0; 77 for(x=0; x<9; x++) 78 { 79 for(y=0; y<9; y++) 80 { 81 data[x][y].value = -1; 82 data[x][y].guess = -1; 83 data[x][y].puzzle = -1; 84 } 85 } 86 87 // adjaceny 88 for(x=0; x<9; x++){ 89 for(y=0; y<9; y++){ 90 int n = 0; 91 int zx, zy; 92 93 for(pos=0; pos<9; pos++){ 94 if(pos!=y){ 95 data[x][y].adj[n].nx = x; 96 data[x][y].adj[n].ny = pos; 97 98 n++; 99 } 100 } 101 102 for(pos=0; pos<9; pos++){ 103 if(pos!=x){ 104 data[x][y].adj[n].nx = pos; 105 data[x][y].adj[n].ny = y; 106 107 n++; 108 } 109 } 110 111 zx = x/3; 112 zy = y/3; 113 for(pos=0; pos<9; pos++){ 114 int zzx = 3*zx + pos/3, zzy = 3*zy + pos%3; 115 if(zzx!=x && zzy!=y){ 116 data[x][y].adj[n].nx = zzx; 117 data[x][y].adj[n].ny = zzy; 118 119 n++; 120 } 121 } 122 123 assert(n == NBCOUNT); 124 } 125 } 126 127 return self; 128} 129 130- (int)valueX:(int)x Y:(int)y 131{ 132 return data[x][y].value; 133} 134 135- (int)puzzleX:(int)x Y:(int)y 136{ 137 return data[x][y].puzzle; 138} 139 140- (int)guessX:(int)x Y:(int)y 141{ 142 return data[x][y].guess; 143} 144 145- (field)fieldX:(int)x Y:(int)y 146{ 147 return data[x][y]; 148} 149 150- (fieldptr)fieldptrX:(int)x Y:(int)y 151{ 152 return &(data[x][y]); 153} 154 155- (seqstruct)seq:(int)pos 156{ 157 return seq[pos]; 158} 159 160 161- (cluestruct)clue:(int)pos 162{ 163 return clues[pos]; 164} 165 166 167- (NSString *)stateToString:(FIELD_TYPE)what 168{ 169 NSString *res = @""; 170 171 int x, y; 172 173 for(y=0; y<9; y++){ 174 for(x=0; x<9; x++){ 175 int outval; 176 if(what==FIELD_VALUE){ 177 outval = data[x][y].value; 178 } 179 else if(what==FIELD_PUZZLE){ 180 outval = data[x][y].puzzle; 181 } 182 else if(what==FIELD_GUESS){ 183 outval = data[x][y].guess; 184 } 185 else{ 186 [self computescore:&(data[x][y])]; 187 outval = data[x][y].score; 188 } 189 190 res = 191 [res stringByAppendingFormat: 192 (x>0 ? @" %c" : @"%c"), 193 (outval==-1 ? '.' : 194 ((what==FIELD_SCORE ? '0' : '1') + outval))]; 195 } 196 res = [res stringByAppendingString:@"\n"]; 197 } 198 199 return res; 200} 201 202- stateFromLineEnumerator:(NSEnumerator *)en what:(FIELD_TYPE)what 203{ 204 int x, y; 205 206 for(y=0; y<9; y++){ 207 NSString *line = [en nextObject]; 208 NSArray *fields = 209 [line componentsSeparatedByString:@" "]; 210 NSEnumerator *fieldenum = [fields objectEnumerator]; 211 212 for(x=0; x<9; x++){ 213 NSString *field = [fieldenum nextObject]; 214 NSScanner *scn = 215 [NSScanner scannerWithString:field]; 216 217 int inval = -1; 218 if([field isEqualToString:@"."]==NO){ 219 [scn scanInt:&inval]; 220 } 221 222 if(what==FIELD_VALUE){ 223 data[x][y].value = inval-1; 224 } 225 else if(what==FIELD_PUZZLE){ 226 data[x][y].puzzle = 227 (inval==-1 ? -1 : inval-1); 228 } 229 else if(what==FIELD_GUESS){ 230 data[x][y].guess = 231 (inval==-1 ? -1 : inval-1); 232 } 233 else{ 234 data[x][y].score = inval; 235 } 236 } 237 } 238 239 // skip separator 240 // NSLog(@"sep <%@>", [en nextObject]); 241 [en nextObject]; 242 return self; 243} 244 245#define RAND_DIST 35 246 247- (BOOL)selectClues 248{ 249 int pos, x, y, randpl = 0; 250 251 BOOL initial[9][9]; 252 int present[9]; 253 254 for(x=0; x<9; x++){ 255 for(y=0; y<9; y++){ 256 initial[x][y] = NO; 257 } 258 } 259 260 while(randpl<allclues){ 261 int lx = lrand48()%9, ly = lrand48()%9; 262 263 if(initial[lx][ly]==NO){ 264 initial[lx][ly] = YES; 265 randpl++; 266 } 267 } 268 269 if(allclues<=RAND_DIST){ 270 for(x=0; x<9; x++){ 271 int count = 0; 272 for(pos=0; pos<9; pos++){ 273 if(initial[x][pos]==YES){ 274 count++; 275 } 276 } 277 278 if(count>=8){ 279 return NO; 280 } 281 } 282 283 for(y=0; y<9; y++){ 284 int count = 0; 285 for(pos=0; pos<9; pos++){ 286 if(initial[pos][y]==YES){ 287 count++; 288 } 289 } 290 291 if(count>=8){ 292 return NO; 293 } 294 } 295 296 297 for(x=0; x<3; x++){ 298 for(y=0; y<3; y++){ 299 int e, f, count = 0; 300 301 for(e=0; e<3; e++){ 302 for(f=0; f<3; f++){ 303 if(initial[3*x+e][3*y+f]==YES){ 304 count++; 305 } 306 } 307 } 308 309 if(count>=8){ 310 return NO; 311 } 312 } 313 } 314 } 315 316 randpl=0; 317 while(randpl<allclues){ 318 for(x=0; x<9; x++){ 319 for(y=0; y<9; y++){ 320 if(initial[x][y]==YES){ 321 clues[randpl].x = x; 322 clues[randpl].y = y; 323 clues[randpl].value = data[x][y].value; 324 randpl++; 325 } 326 } 327 } 328 } 329 330 for(pos=0; pos<9; pos++){ 331 present[pos] = 0; 332 } 333 334 335 for(randpl=0; randpl<allclues; randpl++){ 336 present[clues[randpl].value]++; 337 } 338 339 for(pos=0; pos<9; pos++){ 340 if(!(present[pos])){ 341 return NO; 342 } 343 } 344 345 return YES; 346} 347 348 349- (BOOL)find 350{ 351 int x, y, pos; 352 const char *marker = "ClueMarker"; 353 NSAutoreleasePool *pool; 354 NSMutableSet *set; 355 356 success = NO; 357 placed = allclues; 358 359 for(x=0; x<9; x++){ 360 for(y=0; y<9; y++){ 361 data[x][y].x = x; 362 data[x][y].y = y; 363 364 data[x][y].value = -1; 365 // data[x][y].puzzle = -1; 366 // data[x][y].guess = -1; 367 368 for(pos=0; pos<9; pos++){ 369 data[x][y].nbdigits[pos] = NULL; 370 } 371 } 372 } 373 374 for(pos=0; pos<9*9; pos++){ 375 seq[pos].x = -1; 376 seq[pos].y = -1; 377 seq[pos].checked = 0; 378 } 379 380 for(pos=0; pos<allclues; pos++){ 381 int nb, d; 382 383 x = clues[pos].x; y = clues[pos].y; 384 385 seq[pos].x = x; seq[pos].y = y; 386 seq[pos].checked = 0; 387 388 d = clues[pos].value; 389 data[x][y].value = d; 390 for(nb=0; nb<NBCOUNT; nb++){ 391 int 392 nbx = data[x][y].adj[nb].nx, 393 nby = data[x][y].adj[nb].ny; 394 395 data[nbx][nby].nbdigits[d] = ▮ 396 } 397 } 398 399 pool = [NSAutoreleasePool new]; 400 set = [NSMutableSet setWithCapacity:128]; 401 402 NS_DURING 403 404 [self doFind:set]; 405 406 NS_HANDLER 407 408 if([[localException name] isEqualToString:EX_COMPLETE]==YES){ 409 success = YES; 410 } 411 412 NS_ENDHANDLER 413 414 RELEASE(pool); 415 416 for(x=0; x<9; x++){ 417 for(y=0; y<9; y++){ 418 [self computescore:&(data[x][y])]; 419 } 420 } 421 422 return success; 423} 424 425int comparefieldptrs(const void *a, const void *b) 426{ 427 fieldptr 428 ap = *(fieldptr *)a, 429 bp = *(fieldptr *)b; 430 431 if(ap->score < bp->score){ 432 return +1; 433 } 434 else if(ap->score > bp->score){ 435 return -1; 436 } 437 438 return 0; 439} 440 441- doFind:(NSMutableSet *)seen 442{ 443 NSString *dataStr = [self stateToString:FIELD_VALUE]; 444 int rest; 445 fieldptr buf[9*9]; 446 fieldptr *bPtr; 447 int x, y; 448 int score; 449 int range; 450 int mx; 451 int pos, d; 452 453 if(placed==9*9){ 454 // NSLog(@"--\n%@", dataStr); 455 // NSLog(@"--\n%@", scoreStr); 456 457 [NSException raise:EX_COMPLETE format:EX_COMPLETE_FMT]; 458 return self; // not reached 459 } 460 461 if([seen containsObject:dataStr]==YES){ 462 [NSException raise:EX_LOOP format:EX_LOOP_FMT]; 463 return self; // not reached 464 } 465 [seen addObject:dataStr]; 466 467 rest = 9*9-placed; 468 469 bPtr = buf; 470 471 472 for(x=0; x<9; x++){ 473 for(y=0; y<9; y++){ 474 fieldptr loc = &(data[x][y]); 475 476 [self computescore:loc]; 477 if(data[x][y].value == -1){ 478 *bPtr++ = loc; 479 } 480 } 481 } 482 qsort(buf, rest, sizeof(fieldptr), comparefieldptrs); 483 // NSLog(@"--\n%@", dataStr); 484 // NSLog(@"--\n%@", scoreStr); 485 486 score = buf[0]->score; 487 range = 1; 488 mx = 0; 489 490 while(range<rest && buf[range]->score==score){ 491 range++; 492 } 493 494 if(range>1){ // permute 495 for(mx=range; mx>1; mx--){ 496 int ind = lrand48() % mx; 497 498 fieldptr tmp; 499 500 tmp = buf[ind]; 501 buf[ind] = buf[mx-1]; 502 buf[mx-1] = tmp; 503 } 504 } 505 506 for(pos=0; pos<rest; pos++){ 507 fieldptr fp = buf[pos]; 508 int x = fp->x, y = fp->y; 509 510 for(d=0; d<9; d++){ 511 if(fp->nbdigits[d]==NULL){ 512 int nb; 513 514 seq[placed].x = x; 515 seq[placed].y = y; 516 seq[placed].checked++; 517 518 placed++; 519 data[x][y].value = d; 520 521 for(nb=0; nb<NBCOUNT; nb++){ 522 int nbx = data[x][y].adj[nb].nx, 523 nby = data[x][y].adj[nb].ny; 524 if(data[nbx][nby].nbdigits[d] == NULL){ 525 data[nbx][nby].nbdigits[d] = buf; 526 } 527 } 528 529 [self doFind:seen]; 530 531 for(nb=0; nb<NBCOUNT; nb++){ 532 int nbx = data[x][y].adj[nb].nx, 533 nby = data[x][y].adj[nb].ny; 534 if(data[nbx][nby].nbdigits[d] == buf){ 535 data[nbx][nby].nbdigits[d] = NULL; 536 } 537 } 538 539 data[x][y].value = -1; 540 placed--; 541 } 542 } 543 } 544 545 546 // [seen removeObject:dataStr]; 547 return self; 548} 549 550- (int)setClues:(int)count 551{ 552 int prev = allclues; 553 allclues = count; 554 return prev; 555} 556 557- (int)clues 558{ 559 return allclues; 560} 561 562- reset 563{ 564 int x, y; 565 566 for(x=0; x<9; x++){ 567 for(y=0; y<9; y++){ 568 data[x][y].guess = -1; 569 } 570 } 571 572 return self; 573} 574 575- loadSolution 576{ 577 int x, y; 578 579 for(x=0; x<9; x++){ 580 for(y=0; y<9; y++){ 581 if(data[x][y].puzzle==-1){ 582 data[x][y].guess = data[x][y].value; 583 } 584 } 585 } 586 587 return self; 588} 589 590- (NSString *)checkSequence 591{ 592 char buf[9*9+1]; 593 int pos; 594 595 for(pos=0; pos<9*9; pos++){ 596 buf[pos] = '0' + seq[pos].checked; 597 } 598 buf[pos] = 0; 599 600 return [NSString stringWithCString:buf]; 601} 602 603- cluesToPuzzle 604{ 605 int pos; 606 607 for(pos=0; pos<allclues; pos++){ 608 int x = clues[pos].x, y = clues[pos].y, 609 val = clues[pos].value; 610 611 data[x][y].puzzle = val; 612 } 613 614 return self; 615} 616 617- guessToClues 618{ 619 int pos = 0; 620 621 int x, y; 622 623 for(x=0; x<9; x++){ 624 for(y=0; y<9; y++){ 625 int val = data[x][y].guess; 626 627 if(val!=-1){ 628 clues[pos].x = x; 629 clues[pos].y = y; 630 clues[pos].value = val; 631 632 data[x][y].guess = -1; 633 data[x][y].puzzle = val; 634 635 pos++; 636 } 637 } 638 } 639 640 allclues = pos; 641 642 return self; 643} 644 645- copyStateFromSource:(Sudoku *)src 646{ 647 int x, y; 648 int pos; 649 650 for(x=0; x<9; x++){ 651 for(y=0; y<9; y++){ 652 data[x][y] = [src fieldX:x Y:y]; 653 } 654 } 655 656 for(pos=0; pos<9*9; pos++){ 657 seq[pos] = [src seq:pos]; 658 } 659 660 allclues = [src clues]; 661 for(pos=0; pos<9*9; pos++){ 662 clues[pos] = [src clue:pos]; 663 } 664 665 return self; 666} 667 668@end 669