Commit my work so far on a generator for Nikoli's `Block Puzzle'. It
[sgt/puzzles] / unfinished / divvy.c
CommitLineData
11d273f7 1/*
2 * Library code to divide up a rectangle into a number of equally
3 * sized ominoes, in a random fashion.
4 *
5 * Could use this for generating solved grids of
6 * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/
7 * or for generating the playfield for Jigsaw Sudoku.
8 */
9
9d36cbd7 10/*
11 * Possible improvements which might cut the fail rate:
12 *
13 * - instead of picking one omino to extend in an iteration, try
14 * them all in succession (in a randomised order)
15 *
16 * - (for real rigour) instead of bfsing over ominoes, bfs over
17 * the space of possible _removed squares_. That way we aren't
18 * limited to randomly choosing a single square to remove from
19 * an omino and failing if that particular square doesn't
20 * happen to work.
21 *
4d31c526 22 * However, I don't currently think it's necessary to do either of
23 * these, because the failure rate is already low enough to be
24 * easily tolerable, under all circumstances I've been able to
25 * think of.
9d36cbd7 26 */
27
11d273f7 28#include <assert.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <stddef.h>
32
33#include "puzzles.h"
34
35/*
36 * Subroutine which implements a function used in computing both
37 * whether a square can safely be added to an omino, and whether
38 * it can safely be removed.
39 *
40 * We enumerate the eight squares 8-adjacent to this one, in
41 * cyclic order. We go round that loop and count the number of
42 * times we find a square owned by the target omino next to one
43 * not owned by it. We then return success iff that count is 2.
44 *
45 * When adding a square to an omino, this is precisely the
46 * criterion which tells us that adding the square won't leave a
47 * hole in the middle of the omino. (There's no explicit
48 * requirement in the statement of our problem that the ominoes be
49 * simply connected, but we do know they must be all of equal size
50 * and so it's clear that we must avoid leaving holes, since a
51 * hole would necessarily be smaller than the maximum omino size.)
52 *
53 * When removing a square from an omino, the _same_ criterion
54 * tells us that removing the square won't disconnect the omino.
55 */
56static int addremcommon(int w, int h, int x, int y, int *own, int val)
57{
58 int neighbours[8];
59 int dir, count;
60
61 for (dir = 0; dir < 8; dir++) {
62 int dx = ((dir & 3) == 2 ? 0 : dir > 2 && dir < 6 ? +1 : -1);
63 int dy = ((dir & 3) == 0 ? 0 : dir < 4 ? -1 : +1);
64 int sx = x+dx, sy = y+dy;
65
66 if (sx < 0 || sx >= w || sy < 0 || sy >= h)
67 neighbours[dir] = -1; /* outside the grid */
68 else
69 neighbours[dir] = own[sy*w+sx];
70 }
71
72 /*
73 * To begin with, check 4-adjacency.
74 */
75 if (neighbours[0] != val && neighbours[2] != val &&
76 neighbours[4] != val && neighbours[6] != val)
77 return FALSE;
78
79 count = 0;
80
81 for (dir = 0; dir < 8; dir++) {
82 int next = (dir + 1) & 7;
83 int gotthis = (neighbours[dir] == val);
84 int gotnext = (neighbours[next] == val);
85
86 if (gotthis != gotnext)
87 count++;
88 }
89
90 return (count == 2);
91}
92
93/*
94 * w and h are the dimensions of the rectangle.
95 *
96 * k is the size of the required ominoes. (So k must divide w*h,
97 * of course.)
98 *
99 * The returned result is a w*h-sized dsf.
100 *
101 * In both of the above suggested use cases, the user would
102 * probably want w==h==k, but that isn't a requirement.
103 */
9d36cbd7 104static int *divvy_internal(int w, int h, int k, random_state *rs)
11d273f7 105{
106 int *order, *queue, *tmp, *own, *sizes, *addable, *removable, *retdsf;
107 int wh = w*h;
108 int i, j, n, x, y, qhead, qtail;
109
110 n = wh / k;
111 assert(wh == k*n);
112
113 order = snewn(wh, int);
114 tmp = snewn(wh, int);
115 own = snewn(wh, int);
116 sizes = snewn(n, int);
117 queue = snewn(n, int);
118 addable = snewn(wh*4, int);
119 removable = snewn(wh, int);
120
121 /*
122 * Permute the grid squares into a random order, which will be
123 * used for iterating over the grid whenever we need to search
124 * for something. This prevents directional bias and arranges
125 * for the answer to be non-deterministic.
126 */
127 for (i = 0; i < wh; i++)
128 order[i] = i;
129 shuffle(order, wh, sizeof(*order), rs);
130
131 /*
132 * Begin by choosing a starting square at random for each
133 * omino.
134 */
135 for (i = 0; i < wh; i++) {
136 own[i] = -1;
137 }
138 for (i = 0; i < n; i++) {
139 own[order[i]] = i;
140 sizes[i] = 1;
141 }
142
143 /*
144 * Now repeatedly pick a random omino which isn't already at
145 * the target size, and find a way to expand it by one. This
146 * may involve stealing a square from another omino, in which
147 * case we then re-expand that omino, forming a chain of
148 * square-stealing which terminates in an as yet unclaimed
149 * square. Hence every successful iteration around this loop
150 * causes the number of unclaimed squares to drop by one, and
151 * so the process is bounded in duration.
152 */
153 while (1) {
154
155#ifdef DIVVY_DIAGNOSTICS
156 {
157 int x, y;
158 printf("Top of loop. Current grid:\n");
159 for (y = 0; y < h; y++) {
160 for (x = 0; x < w; x++)
161 printf("%3d", own[y*w+x]);
162 printf("\n");
163 }
164 }
165#endif
166
167 /*
168 * Go over the grid and figure out which squares can
169 * safely be added to, or removed from, each omino. We
170 * don't take account of other ominoes in this process, so
171 * we will often end up knowing that a square can be
172 * poached from one omino by another.
173 *
174 * For each square, there may be up to four ominoes to
175 * which it can be added (those to which it is
176 * 4-adjacent).
177 */
178 for (y = 0; y < h; y++) {
179 for (x = 0; x < w; x++) {
180 int yx = y*w+x;
181 int curr = own[yx];
182 int dir;
183
184 if (curr < 0) {
9d36cbd7 185 removable[yx] = FALSE; /* can't remove if not owned! */
186 } else if (sizes[curr] == 1) {
187 removable[yx] = TRUE; /* can always remove a singleton */
11d273f7 188 } else {
189 /*
190 * See if this square can be removed from its
191 * omino without disconnecting it.
192 */
193 removable[yx] = addremcommon(w, h, x, y, own, curr);
194 }
195
196 for (dir = 0; dir < 4; dir++) {
197 int dx = (dir == 0 ? -1 : dir == 1 ? +1 : 0);
198 int dy = (dir == 2 ? -1 : dir == 3 ? +1 : 0);
199 int sx = x + dx, sy = y + dy;
200 int syx = sy*w+sx;
201
202 addable[yx*4+dir] = -1;
203
204 if (sx < 0 || sx >= w || sy < 0 || sy >= h)
205 continue; /* no omino here! */
206 if (own[syx] < 0)
207 continue; /* also no omino here */
208 if (own[syx] == own[yx])
209 continue; /* we already got one */
210 if (!addremcommon(w, h, x, y, own, own[syx]))
211 continue; /* would non-simply connect the omino */
212
213 addable[yx*4+dir] = own[syx];
214 }
215 }
216 }
217
218 for (i = j = 0; i < n; i++)
219 if (sizes[i] < k)
220 tmp[j++] = i;
221 if (j == 0)
222 break; /* all ominoes are complete! */
223 j = tmp[random_upto(rs, j)];
f40d37bd 224#ifdef DIVVY_DIAGNOSTICS
225 printf("Trying to extend %d\n", j);
226#endif
11d273f7 227
228 /*
229 * So we're trying to expand omino j. We breadth-first
230 * search out from j across the space of ominoes.
231 *
232 * For bfs purposes, we use two elements of tmp per omino:
233 * tmp[2*i+0] tells us which omino we got to i from, and
234 * tmp[2*i+1] numbers the grid square that omino stole
235 * from us.
236 *
237 * This requires that wh (the size of tmp) is at least 2n,
238 * i.e. k is at least 2. There would have been nothing to
239 * stop a user calling this function with k=1, but if they
240 * did then we wouldn't have got to _here_ in the code -
241 * we would have noticed above that all ominoes were
242 * already at their target sizes, and terminated :-)
243 */
244 assert(wh >= 2*n);
245 for (i = 0; i < n; i++)
246 tmp[2*i] = tmp[2*i+1] = -1;
247 qhead = qtail = 0;
248 queue[qtail++] = j;
249 tmp[2*j] = tmp[2*j+1] = -2; /* special value: `starting point' */
250
251 while (qhead < qtail) {
252 int tmpsq;
253
254 j = queue[qhead];
255
256 /*
257 * We wish to expand omino j. However, we might have
258 * got here by omino j having a square stolen from it,
259 * so first of all we must temporarily mark that
260 * square as not belonging to j, so that our adjacency
261 * calculations don't assume j _does_ belong to us.
262 */
263 tmpsq = tmp[2*j+1];
264 if (tmpsq >= 0) {
265 assert(own[tmpsq] == j);
9d36cbd7 266 own[tmpsq] = -3;
11d273f7 267 }
268
269 /*
270 * OK. Now begin by seeing if we can find any
271 * unclaimed square into which we can expand omino j.
272 * If we find one, the entire bfs terminates.
273 */
274 for (i = 0; i < wh; i++) {
275 int dir;
276
9d36cbd7 277 if (own[order[i]] != -1)
11d273f7 278 continue; /* this square is claimed */
9d36cbd7 279
280 /*
281 * Special case: if our current omino was size 1
282 * and then had a square stolen from it, it's now
283 * size zero, which means it's valid to `expand'
284 * it into _any_ unclaimed square.
285 */
286 if (sizes[j] == 1 && tmpsq >= 0)
287 break; /* got one */
288
289 /*
290 * Failing that, we must do the full test for
291 * addability.
292 */
11d273f7 293 for (dir = 0; dir < 4; dir++)
294 if (addable[order[i]*4+dir] == j) {
295 /*
296 * We know this square is addable to this
297 * omino with the grid in the state it had
298 * at the top of the loop. However, we
299 * must now check that it's _still_
300 * addable to this omino when the omino is
301 * missing a square. To do this it's only
302 * necessary to re-check addremcommon.
f40d37bd 303 */
11d273f7 304 if (!addremcommon(w, h, order[i]%w, order[i]/w,
305 own, j))
306 continue;
307 break;
308 }
309 if (dir == 4)
310 continue; /* we can't add this square to j */
9d36cbd7 311
11d273f7 312 break; /* got one! */
313 }
314 if (i < wh) {
315 i = order[i];
316
317 /*
9d36cbd7 318 * Restore the temporarily removed square _before_
319 * we start shifting ownerships about.
320 */
321 if (tmpsq >= 0)
322 own[tmpsq] = j;
323
324 /*
11d273f7 325 * We are done. We can add square i to omino j,
326 * and then backtrack along the trail in tmp
327 * moving squares between ominoes, ending up
328 * expanding our starting omino by one.
329 */
f40d37bd 330#ifdef DIVVY_DIAGNOSTICS
331 printf("(%d,%d)", i%w, i/w);
332#endif
11d273f7 333 while (1) {
334 own[i] = j;
f40d37bd 335#ifdef DIVVY_DIAGNOSTICS
336 printf(" -> %d", j);
337#endif
11d273f7 338 if (tmp[2*j] == -2)
339 break;
340 i = tmp[2*j+1];
341 j = tmp[2*j];
f40d37bd 342#ifdef DIVVY_DIAGNOSTICS
343 printf("; (%d,%d)", i%w, i/w);
344#endif
11d273f7 345 }
f40d37bd 346#ifdef DIVVY_DIAGNOSTICS
347 printf("\n");
348#endif
11d273f7 349
350 /*
351 * Increment the size of the starting omino.
352 */
353 sizes[j]++;
354
355 /*
356 * Terminate the bfs loop.
357 */
358 break;
359 }
360
361 /*
362 * If we get here, we haven't been able to expand
363 * omino j into an unclaimed square. So now we begin
364 * to investigate expanding it into squares which are
365 * claimed by ominoes the bfs has not yet visited.
366 */
367 for (i = 0; i < wh; i++) {
368 int dir, nj;
369
370 nj = own[order[i]];
371 if (nj < 0 || tmp[2*nj] != -1)
372 continue; /* unclaimed, or owned by wrong omino */
373 if (!removable[order[i]])
374 continue; /* its omino won't let it go */
375
376 for (dir = 0; dir < 4; dir++)
377 if (addable[order[i]*4+dir] == j) {
378 /*
379 * As above, re-check addremcommon.
380 */
381 if (!addremcommon(w, h, order[i]%w, order[i]/w,
382 own, j))
383 continue;
384
385 /*
386 * We have found a square we can use to
387 * expand omino j, at the expense of the
388 * as-yet unvisited omino nj. So add this
389 * to the bfs queue.
390 */
391 assert(qtail < n);
392 queue[qtail++] = nj;
393 tmp[2*nj] = j;
394 tmp[2*nj+1] = order[i];
395
396 /*
397 * Now terminate the loop over dir, to
398 * ensure we don't accidentally add the
399 * same omino twice to the queue.
400 */
401 break;
402 }
403 }
404
405 /*
406 * Restore the temporarily removed square.
407 */
408 if (tmpsq >= 0)
409 own[tmpsq] = j;
410
411 /*
412 * Advance the queue head.
413 */
414 qhead++;
415 }
416
417 if (qhead == qtail) {
418 /*
419 * We have finished the bfs and not found any way to
420 * expand omino j. Panic, and return failure.
421 *
422 * FIXME: or should we loop over all ominoes before we
423 * give up?
424 */
f40d37bd 425#ifdef DIVVY_DIAGNOSTICS
426 printf("FAIL!\n");
427#endif
11d273f7 428 retdsf = NULL;
429 goto cleanup;
430 }
431 }
432
f40d37bd 433#ifdef DIVVY_DIAGNOSTICS
434 {
435 int x, y;
436 printf("SUCCESS! Final grid:\n");
437 for (y = 0; y < h; y++) {
438 for (x = 0; x < w; x++)
439 printf("%3d", own[y*w+x]);
440 printf("\n");
441 }
442 }
443#endif
444
11d273f7 445 /*
446 * Construct the output dsf.
447 */
448 for (i = 0; i < wh; i++) {
449 assert(own[i] >= 0 && own[i] < n);
450 tmp[own[i]] = i;
451 }
452 retdsf = snew_dsf(wh);
453 for (i = 0; i < wh; i++) {
454 dsf_merge(retdsf, i, tmp[own[i]]);
455 }
456
457 /*
458 * Construct the output dsf a different way, to verify that
459 * the ominoes really are k-ominoes and we haven't
460 * accidentally split one into two disconnected pieces.
461 */
462 dsf_init(tmp, wh);
463 for (y = 0; y < h; y++)
464 for (x = 0; x+1 < w; x++)
465 if (own[y*w+x] == own[y*w+(x+1)])
466 dsf_merge(tmp, y*w+x, y*w+(x+1));
467 for (x = 0; x < w; x++)
468 for (y = 0; y+1 < h; y++)
469 if (own[y*w+x] == own[(y+1)*w+x])
470 dsf_merge(tmp, y*w+x, (y+1)*w+x);
471 for (i = 0; i < wh; i++) {
472 j = dsf_canonify(retdsf, i);
473 assert(dsf_canonify(tmp, j) == dsf_canonify(tmp, i));
474 }
475
476 cleanup:
477
478 /*
479 * Free our temporary working space.
480 */
481 sfree(order);
482 sfree(tmp);
483 sfree(own);
484 sfree(sizes);
485 sfree(queue);
486 sfree(addable);
487 sfree(removable);
488
489 /*
490 * And we're done.
491 */
492 return retdsf;
493}
494
495#ifdef TESTMODE
9d36cbd7 496static int fail_counter = 0;
497#endif
498
499int *divvy_rectangle(int w, int h, int k, random_state *rs)
500{
501 int *ret;
502
503 do {
504 ret = divvy_internal(w, h, k, rs);
505
506#ifdef TESTMODE
507 if (!ret)
508 fail_counter++;
509#endif
510
511 } while (!ret);
512
513 return ret;
514}
515
516#ifdef TESTMODE
11d273f7 517
518/*
519 * gcc -g -O0 -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
520 *
521 * or to debug
522 *
523 * gcc -g -O0 -DDIVVY_DIAGNOSTICS -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
524 */
525
526int main(int argc, char **argv)
527{
528 int *dsf;
9d36cbd7 529 int i;
11d273f7 530 int w = 9, h = 4, k = 6, tries = 100;
531 random_state *rs;
532
533 rs = random_new("123456", 6);
534
535 if (argc > 1)
536 w = atoi(argv[1]);
537 if (argc > 2)
538 h = atoi(argv[2]);
539 if (argc > 3)
540 k = atoi(argv[3]);
541 if (argc > 4)
542 tries = atoi(argv[4]);
543
11d273f7 544 for (i = 0; i < tries; i++) {
9d36cbd7 545 int x, y;
11d273f7 546
9d36cbd7 547 dsf = divvy_rectangle(w, h, k, rs);
548 assert(dsf);
549
550 for (y = 0; y <= 2*h; y++) {
551 for (x = 0; x <= 2*w; x++) {
552 int miny = y/2 - 1, maxy = y/2;
553 int minx = x/2 - 1, maxx = x/2;
554 int classes[4], tx, ty;
555 for (ty = 0; ty < 2; ty++)
556 for (tx = 0; tx < 2; tx++) {
557 int cx = minx+tx, cy = miny+ty;
558 if (cx < 0 || cx >= w || cy < 0 || cy >= h)
559 classes[ty*2+tx] = -1;
11d273f7 560 else
9d36cbd7 561 classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
11d273f7 562 }
9d36cbd7 563 switch (y%2 * 2 + x%2) {
564 case 0: /* corner */
565 /*
566 * Cases for the corner:
567 *
568 * - if all four surrounding squares belong
569 * to the same omino, we print a space.
570 *
571 * - if the top two are the same and the
572 * bottom two are the same, we print a
573 * horizontal line.
574 *
575 * - if the left two are the same and the
576 * right two are the same, we print a
577 * vertical line.
578 *
579 * - otherwise, we print a cross.
580 */
581 if (classes[0] == classes[1] &&
582 classes[1] == classes[2] &&
583 classes[2] == classes[3])
584 printf(" ");
585 else if (classes[0] == classes[1] &&
586 classes[2] == classes[3])
587 printf("-");
588 else if (classes[0] == classes[2] &&
589 classes[1] == classes[3])
590 printf("|");
591 else
592 printf("+");
593 break;
594 case 1: /* horiz edge */
595 if (classes[1] == classes[3])
596 printf(" ");
597 else
598 printf("--");
599 break;
600 case 2: /* vert edge */
601 if (classes[2] == classes[3])
602 printf(" ");
603 else
604 printf("|");
605 break;
606 case 3: /* square centre */
607 printf(" ");
608 break;
11d273f7 609 }
11d273f7 610 }
611 printf("\n");
11d273f7 612 }
9d36cbd7 613 printf("\n");
614 sfree(dsf);
11d273f7 615 }
616
9d36cbd7 617 printf("%d retries needed for %d successes\n", fail_counter, tries);
11d273f7 618
619 return 0;
620}
621
622#endif