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] = &marker;
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