laydomino.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. /*
  2. * laydomino.c: code for performing a domino (2x1 tile) layout of
  3. * a given area of code.
  4. */
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <assert.h>
  8. #include "puzzles.h"
  9. /*
  10. * This function returns an array size w x h representing a grid:
  11. * each grid[i] = j, where j is the other end of a 2x1 domino.
  12. * If w*h is odd, one square will remain referring to itself.
  13. */
  14. int *domino_layout(int w, int h, random_state *rs)
  15. {
  16. int *grid, *grid2, *list;
  17. int wh = w*h;
  18. /*
  19. * Allocate space in which to lay the grid out.
  20. */
  21. grid = snewn(wh, int);
  22. grid2 = snewn(wh, int);
  23. list = snewn(2*wh, int);
  24. domino_layout_prealloc(w, h, rs, grid, grid2, list);
  25. sfree(grid2);
  26. sfree(list);
  27. return grid;
  28. }
  29. /*
  30. * As for domino_layout, but with preallocated buffers.
  31. * grid and grid2 should be size w*h, and list size 2*w*h.
  32. */
  33. void domino_layout_prealloc(int w, int h, random_state *rs,
  34. int *grid, int *grid2, int *list)
  35. {
  36. int i, j, k, m, wh = w*h, todo, done;
  37. /*
  38. * To begin with, set grid[i] = i for all i to indicate
  39. * that all squares are currently singletons. Later we'll
  40. * set grid[i] to be the index of the other end of the
  41. * domino on i.
  42. */
  43. for (i = 0; i < wh; i++)
  44. grid[i] = i;
  45. /*
  46. * Now prepare a list of the possible domino locations. There
  47. * are w*(h-1) possible vertical locations, and (w-1)*h
  48. * horizontal ones, for a total of 2*wh - h - w.
  49. *
  50. * I'm going to denote the vertical domino placement with
  51. * its top in square i as 2*i, and the horizontal one with
  52. * its left half in square i as 2*i+1.
  53. */
  54. k = 0;
  55. for (j = 0; j < h-1; j++)
  56. for (i = 0; i < w; i++)
  57. list[k++] = 2 * (j*w+i); /* vertical positions */
  58. for (j = 0; j < h; j++)
  59. for (i = 0; i < w-1; i++)
  60. list[k++] = 2 * (j*w+i) + 1; /* horizontal positions */
  61. assert(k == 2*wh - h - w);
  62. /*
  63. * Shuffle the list.
  64. */
  65. shuffle(list, k, sizeof(*list), rs);
  66. /*
  67. * Work down the shuffled list, placing a domino everywhere
  68. * we can.
  69. */
  70. for (i = 0; i < k; i++) {
  71. int horiz, xy, xy2;
  72. horiz = list[i] % 2;
  73. xy = list[i] / 2;
  74. xy2 = xy + (horiz ? 1 : w);
  75. if (grid[xy] == xy && grid[xy2] == xy2) {
  76. /*
  77. * We can place this domino. Do so.
  78. */
  79. grid[xy] = xy2;
  80. grid[xy2] = xy;
  81. }
  82. }
  83. #ifdef GENERATION_DIAGNOSTICS
  84. printf("generated initial layout\n");
  85. #endif
  86. /*
  87. * Now we've placed as many dominoes as we can immediately
  88. * manage. There will be squares remaining, but they'll be
  89. * singletons. So loop round and deal with the singletons
  90. * two by two.
  91. */
  92. while (1) {
  93. #ifdef GENERATION_DIAGNOSTICS
  94. for (j = 0; j < h; j++) {
  95. for (i = 0; i < w; i++) {
  96. int xy = j*w+i;
  97. int v = grid[xy];
  98. int c = (v == xy+1 ? '[' : v == xy-1 ? ']' :
  99. v == xy+w ? 'n' : v == xy-w ? 'U' : '.');
  100. putchar(c);
  101. }
  102. putchar('\n');
  103. }
  104. putchar('\n');
  105. #endif
  106. /*
  107. * Our strategy is:
  108. *
  109. * First find a singleton square.
  110. *
  111. * Then breadth-first search out from the starting
  112. * square. From that square (and any others we reach on
  113. * the way), examine all four neighbours of the square.
  114. * If one is an end of a domino, we move to the _other_
  115. * end of that domino before looking at neighbours
  116. * again. When we encounter another singleton on this
  117. * search, stop.
  118. *
  119. * This will give us a path of adjacent squares such
  120. * that all but the two ends are covered in dominoes.
  121. * So we can now shuffle every domino on the path up by
  122. * one.
  123. *
  124. * (Chessboard colours are mathematically important
  125. * here: we always end up pairing each singleton with a
  126. * singleton of the other colour. However, we never
  127. * have to track this manually, since it's
  128. * automatically taken care of by the fact that we
  129. * always make an even number of orthogonal moves.)
  130. */
  131. k = 0;
  132. for (j = 0; j < wh; j++) {
  133. if (grid[j] == j) {
  134. k++;
  135. i = j; /* start BFS here. */
  136. }
  137. }
  138. if (k == (wh % 2))
  139. break; /* if area is even, we have no more singletons;
  140. if area is odd, we have one singleton.
  141. either way, we're done. */
  142. #ifdef GENERATION_DIAGNOSTICS
  143. printf("starting b.f.s. at singleton %d\n", i);
  144. #endif
  145. /*
  146. * Set grid2 to -1 everywhere. It will hold our
  147. * distance-from-start values, and also our
  148. * backtracking data, during the b.f.s.
  149. */
  150. for (j = 0; j < wh; j++)
  151. grid2[j] = -1;
  152. grid2[i] = 0; /* starting square has distance zero */
  153. /*
  154. * Start our to-do list of squares. It'll live in
  155. * `list'; since the b.f.s can cover every square at
  156. * most once there is no need for it to be circular.
  157. * We'll just have two counters tracking the end of the
  158. * list and the squares we've already dealt with.
  159. */
  160. done = 0;
  161. todo = 1;
  162. list[0] = i;
  163. /*
  164. * Now begin the b.f.s. loop.
  165. */
  166. while (done < todo) {
  167. int d[4], nd, x, y;
  168. i = list[done++];
  169. #ifdef GENERATION_DIAGNOSTICS
  170. printf("b.f.s. iteration from %d\n", i);
  171. #endif
  172. x = i % w;
  173. y = i / w;
  174. nd = 0;
  175. if (x > 0)
  176. d[nd++] = i - 1;
  177. if (x+1 < w)
  178. d[nd++] = i + 1;
  179. if (y > 0)
  180. d[nd++] = i - w;
  181. if (y+1 < h)
  182. d[nd++] = i + w;
  183. /*
  184. * To avoid directional bias, process the
  185. * neighbours of this square in a random order.
  186. */
  187. shuffle(d, nd, sizeof(*d), rs);
  188. for (j = 0; j < nd; j++) {
  189. k = d[j];
  190. if (grid[k] == k) {
  191. #ifdef GENERATION_DIAGNOSTICS
  192. printf("found neighbouring singleton %d\n", k);
  193. #endif
  194. grid2[k] = i;
  195. break; /* found a target singleton! */
  196. }
  197. /*
  198. * We're moving through a domino here, so we
  199. * have two entries in grid2 to fill with
  200. * useful data. In grid[k] - the square
  201. * adjacent to where we came from - I'm going
  202. * to put the address _of_ the square we came
  203. * from. In the other end of the domino - the
  204. * square from which we will continue the
  205. * search - I'm going to put the distance.
  206. */
  207. m = grid[k];
  208. if (grid2[m] < 0 || grid2[m] > grid2[i]+1) {
  209. #ifdef GENERATION_DIAGNOSTICS
  210. printf("found neighbouring domino %d/%d\n", k, m);
  211. #endif
  212. grid2[m] = grid2[i]+1;
  213. grid2[k] = i;
  214. /*
  215. * And since we've now visited a new
  216. * domino, add m to the to-do list.
  217. */
  218. assert(todo < wh);
  219. list[todo++] = m;
  220. }
  221. }
  222. if (j < nd) {
  223. i = k;
  224. #ifdef GENERATION_DIAGNOSTICS
  225. printf("terminating b.f.s. loop, i = %d\n", i);
  226. #endif
  227. break;
  228. }
  229. i = -1; /* just in case the loop terminates */
  230. }
  231. /*
  232. * We expect this b.f.s. to have found us a target
  233. * square.
  234. */
  235. assert(i >= 0);
  236. /*
  237. * Now we can follow the trail back to our starting
  238. * singleton, re-laying dominoes as we go.
  239. */
  240. while (1) {
  241. j = grid2[i];
  242. assert(j >= 0 && j < wh);
  243. k = grid[j];
  244. grid[i] = j;
  245. grid[j] = i;
  246. #ifdef GENERATION_DIAGNOSTICS
  247. printf("filling in domino %d/%d (next %d)\n", i, j, k);
  248. #endif
  249. if (j == k)
  250. break; /* we've reached the other singleton */
  251. i = k;
  252. }
  253. #ifdef GENERATION_DIAGNOSTICS
  254. printf("fixup path completed\n");
  255. #endif
  256. }
  257. }
  258. /* vim: set shiftwidth=4 :set textwidth=80: */