213d2495 |
1 | /* |
2 | * separate.c: Implementation of `Block Puzzle', a Japanese-only |
3 | * Nikoli puzzle seen at |
4 | * http://www.nikoli.co.jp/ja/puzzles/block_puzzle/ |
5 | * |
6 | * It's difficult to be absolutely sure of the rules since online |
7 | * Japanese translators are so bad, but looking at the sample |
8 | * puzzle it seems fairly clear that the rules of this one are |
9 | * very simple. You have an mxn grid in which every square |
10 | * contains a letter, there are k distinct letters with k dividing |
11 | * mn, and every letter occurs the same number of times; your aim |
12 | * is to find a partition of the grid into disjoint k-ominoes such |
13 | * that each k-omino contains exactly one of each letter. |
14 | * |
15 | * (It may be that Nikoli always have m,n,k equal to one another. |
16 | * However, I don't see that that's critical to the puzzle; k|mn |
17 | * is the only really important constraint, and even that could |
18 | * probably be dispensed with if some squares were marked as |
19 | * unused.) |
20 | */ |
21 | |
22 | /* |
23 | * Current status: only the solver/generator is yet written, and |
24 | * although working in principle it's _very_ slow. It generates |
25 | * 5x5n5 or 6x6n4 readily enough, 6x6n6 with a bit of effort, and |
26 | * 7x7n7 only with a serious strain. I haven't dared try it higher |
27 | * than that yet. |
28 | * |
29 | * One idea to speed it up is to implement more of the solver. |
30 | * Ideas I've so far had include: |
31 | * |
32 | * - Generalise the deduction currently expressed as `an |
33 | * undersized chain with only one direction to extend must take |
34 | * it'. More generally, the deduction should say `if all the |
35 | * possible k-ominoes containing a given chain also contain |
36 | * square x, then mark square x as part of that k-omino'. |
37 | * + For example, consider this case: |
38 | * |
39 | * a ? b This represents the top left of a board; the letters |
40 | * ? ? ? a,b,c do not represent the letters used in the puzzle, |
41 | * c ? ? but indicate that those three squares are known to be |
42 | * of different ominoes. Now if k >= 4, we can immediately |
43 | * deduce that the square midway between b and c belongs to the |
44 | * same omino as a, because there is no way we can make a 4-or- |
45 | * more-omino containing a which does not also contain that square. |
46 | * (Most easily seen by imagining cutting that square out of the |
47 | * grid; then, clearly, the omino containing a has only two |
48 | * squares to expand into, and needs at least three.) |
49 | * |
50 | * The key difficulty with this mode of reasoning is |
51 | * identifying such squares. I can't immediately think of a |
52 | * simple algorithm for finding them on a wholesale basis. |
53 | * |
54 | * - Bfs out from a chain looking for the letters it lacks. For |
55 | * example, in this situation (top three rows of a 7x7n7 grid): |
56 | * |
57 | * +-----------+-+ |
58 | * |E-A-F-B-C D|D| |
59 | * +------- || |
60 | * |E-C-G-D G|G E| |
61 | * +-+--- | |
62 | * |E|E G A B F A| |
63 | * |
64 | * In this situation we can be sure that the top left chain |
65 | * E-A-F-B-C does extend rightwards to the D, because there is |
66 | * no other D within reach of that chain. Note also that the |
67 | * bfs can skip squares which are known to belong to other |
68 | * ominoes than this one. |
69 | * |
70 | * (This deduction, I fear, should only be used in an |
71 | * emergency, because it relies on _all_ squares within range |
72 | * of the bfs having particular values and so using it during |
73 | * incremental generation rather nails down a lot of the grid.) |
74 | * |
75 | * It's conceivable that another thing we could do would be to |
76 | * increase the flexibility in the grid generator: instead of |
77 | * nailing down the _value_ of any square depended on, merely nail |
78 | * down its equivalence to other squares. Unfortunately this turns |
79 | * the letter-selection phase of generation into a general graph |
80 | * colouring problem (we must draw a graph with equivalence |
81 | * classes of squares as the vertices, and an edge between any two |
82 | * vertices representing equivalence classes which contain squares |
83 | * that share an omino, and then k-colour the result) and hence |
84 | * requires recursion, which bodes ill for something we're doing |
85 | * that many times per generation. |
86 | * |
87 | * I suppose a simple thing I could try would be tuning the retry |
88 | * count, just in case it's set too high or too low for efficient |
89 | * generation. |
90 | */ |
91 | |
92 | #include <stdio.h> |
93 | #include <stdlib.h> |
94 | #include <string.h> |
95 | #include <assert.h> |
96 | #include <ctype.h> |
97 | #include <math.h> |
98 | |
99 | #include "puzzles.h" |
100 | |
101 | enum { |
102 | COL_BACKGROUND, |
103 | NCOLOURS |
104 | }; |
105 | |
106 | struct game_params { |
107 | int w, h, k; |
108 | }; |
109 | |
110 | struct game_state { |
111 | int FIXME; |
112 | }; |
113 | |
114 | static game_params *default_params(void) |
115 | { |
116 | game_params *ret = snew(game_params); |
117 | |
118 | ret->w = ret->h = ret->k = 5; /* FIXME: a bit bigger? */ |
119 | |
120 | return ret; |
121 | } |
122 | |
123 | static int game_fetch_preset(int i, char **name, game_params **params) |
124 | { |
125 | return FALSE; |
126 | } |
127 | |
128 | static void free_params(game_params *params) |
129 | { |
130 | sfree(params); |
131 | } |
132 | |
133 | static game_params *dup_params(game_params *params) |
134 | { |
135 | game_params *ret = snew(game_params); |
136 | *ret = *params; /* structure copy */ |
137 | return ret; |
138 | } |
139 | |
140 | static void decode_params(game_params *params, char const *string) |
141 | { |
142 | params->w = params->h = params->k = atoi(string); |
143 | while (*string && isdigit((unsigned char)*string)) string++; |
144 | if (*string == 'x') { |
145 | string++; |
146 | params->h = atoi(string); |
147 | while (*string && isdigit((unsigned char)*string)) string++; |
148 | } |
149 | if (*string == 'n') { |
150 | string++; |
151 | params->k = atoi(string); |
152 | while (*string && isdigit((unsigned char)*string)) string++; |
153 | } |
154 | } |
155 | |
156 | static char *encode_params(game_params *params, int full) |
157 | { |
158 | char buf[256]; |
159 | sprintf(buf, "%dx%dn%d", params->w, params->h, params->k); |
160 | return dupstr(buf); |
161 | } |
162 | |
163 | static config_item *game_configure(game_params *params) |
164 | { |
165 | return NULL; |
166 | } |
167 | |
168 | static game_params *custom_params(config_item *cfg) |
169 | { |
170 | return NULL; |
171 | } |
172 | |
173 | static char *validate_params(game_params *params, int full) |
174 | { |
175 | return NULL; |
176 | } |
177 | |
178 | /* ---------------------------------------------------------------------- |
179 | * Solver and generator. |
180 | */ |
181 | |
182 | struct solver_scratch { |
183 | int w, h, k; |
184 | |
185 | /* |
186 | * Tracks connectedness between squares. |
187 | */ |
188 | int *dsf; |
189 | |
190 | /* |
191 | * size[dsf_canonify(dsf, yx)] tracks the size of the |
192 | * connected component containing yx. |
193 | */ |
194 | int *size; |
195 | |
196 | /* |
197 | * contents[dsf_canonify(dsf, yx)*k+i] tracks whether or not |
198 | * the connected component containing yx includes letter i. If |
199 | * the value is -1, it doesn't; otherwise its value is the |
200 | * index in the main grid of the square which contributes that |
201 | * letter to the component. |
202 | */ |
203 | int *contents; |
204 | |
205 | /* |
206 | * disconnect[dsf_canonify(dsf, yx1)*w*h + dsf_canonify(dsf, yx2)] |
207 | * tracks whether or not the connected components containing |
208 | * yx1 and yx2 are known to be distinct. |
209 | */ |
210 | unsigned char *disconnect; |
211 | |
212 | /* |
213 | * Temporary space used only inside particular solver loops. |
214 | */ |
215 | int *tmp; |
216 | }; |
217 | |
218 | struct solver_scratch *solver_scratch_new(int w, int h, int k) |
219 | { |
220 | int wh = w*h; |
221 | struct solver_scratch *sc = snew(struct solver_scratch); |
222 | |
223 | sc->w = w; |
224 | sc->h = h; |
225 | sc->k = k; |
226 | |
227 | sc->dsf = snew_dsf(wh); |
228 | sc->size = snewn(wh, int); |
229 | sc->contents = snewn(wh * k, int); |
230 | sc->disconnect = snewn(wh*wh, unsigned char); |
231 | sc->tmp = snewn(wh, int); |
232 | |
233 | return sc; |
234 | } |
235 | |
236 | void solver_scratch_free(struct solver_scratch *sc) |
237 | { |
238 | sfree(sc->dsf); |
239 | sfree(sc->size); |
240 | sfree(sc->contents); |
241 | sfree(sc->disconnect); |
242 | sfree(sc->tmp); |
243 | sfree(sc); |
244 | } |
245 | |
246 | void solver_connect(struct solver_scratch *sc, int yx1, int yx2) |
247 | { |
248 | int w = sc->w, h = sc->h, k = sc->k; |
249 | int wh = w*h; |
250 | int i, yxnew; |
251 | |
252 | yx1 = dsf_canonify(sc->dsf, yx1); |
253 | yx2 = dsf_canonify(sc->dsf, yx2); |
254 | assert(yx1 != yx2); |
255 | |
256 | /* |
257 | * To connect two components together into a bigger one, we |
258 | * start by merging them in the dsf itself. |
259 | */ |
260 | dsf_merge(sc->dsf, yx1, yx2); |
261 | yxnew = dsf_canonify(sc->dsf, yx2); |
262 | |
263 | /* |
264 | * The size of the new component is the sum of the sizes of the |
265 | * old ones. |
266 | */ |
267 | sc->size[yxnew] = sc->size[yx1] + sc->size[yx2]; |
268 | |
269 | /* |
270 | * The contents bitmap of the new component is the union of the |
271 | * contents of the old ones. |
272 | * |
273 | * Given two numbers at most one of which is not -1, we can |
274 | * find the other one by adding the two and adding 1; this |
275 | * will yield -1 if both were -1 to begin with, otherwise the |
276 | * other. |
277 | * |
278 | * (A neater approach would be to take their bitwise AND, but |
279 | * this is unfortunately not well-defined standard C when done |
280 | * to signed integers.) |
281 | */ |
282 | for (i = 0; i < k; i++) { |
283 | assert(sc->contents[yx1*k+i] < 0 || sc->contents[yx2*k+i] < 0); |
284 | sc->contents[yxnew*k+i] = (sc->contents[yx1*k+i] + |
285 | sc->contents[yx2*k+i] + 1); |
286 | } |
287 | |
288 | /* |
289 | * We must combine the rows _and_ the columns in the disconnect |
290 | * matrix. |
291 | */ |
292 | for (i = 0; i < wh; i++) |
293 | sc->disconnect[yxnew*wh+i] = (sc->disconnect[yx1*wh+i] || |
294 | sc->disconnect[yx2*wh+i]); |
295 | for (i = 0; i < wh; i++) |
296 | sc->disconnect[i*wh+yxnew] = (sc->disconnect[i*wh+yx1] || |
297 | sc->disconnect[i*wh+yx2]); |
298 | } |
299 | |
300 | void solver_disconnect(struct solver_scratch *sc, int yx1, int yx2) |
301 | { |
302 | int w = sc->w, h = sc->h; |
303 | int wh = w*h; |
304 | |
305 | yx1 = dsf_canonify(sc->dsf, yx1); |
306 | yx2 = dsf_canonify(sc->dsf, yx2); |
307 | assert(yx1 != yx2); |
308 | assert(!sc->disconnect[yx1*wh+yx2]); |
309 | assert(!sc->disconnect[yx2*wh+yx1]); |
310 | |
311 | /* |
312 | * Mark the components as disconnected from each other in the |
313 | * disconnect matrix. |
314 | */ |
315 | sc->disconnect[yx1*wh+yx2] = sc->disconnect[yx2*wh+yx1] = 1; |
316 | } |
317 | |
318 | void solver_init(struct solver_scratch *sc) |
319 | { |
320 | int w = sc->w, h = sc->h; |
321 | int wh = w*h; |
322 | int i; |
323 | |
324 | /* |
325 | * Set up most of the scratch space. We don't set up the |
326 | * contents array, however, because this will change if we |
327 | * adjust the letter arrangement and re-run the solver. |
328 | */ |
329 | dsf_init(sc->dsf, wh); |
330 | for (i = 0; i < wh; i++) sc->size[i] = 1; |
331 | memset(sc->disconnect, 0, wh*wh); |
332 | } |
333 | |
334 | int solver_attempt(struct solver_scratch *sc, const unsigned char *grid, |
335 | unsigned char *gen_lock) |
336 | { |
337 | int w = sc->w, h = sc->h, k = sc->k; |
338 | int wh = w*h; |
339 | int i, x, y; |
340 | int done_something_overall = FALSE; |
341 | |
342 | /* |
343 | * Set up the contents array from the grid. |
344 | */ |
345 | for (i = 0; i < wh*k; i++) |
346 | sc->contents[i] = -1; |
347 | for (i = 0; i < wh; i++) |
348 | sc->contents[dsf_canonify(sc->dsf, i)*k+grid[i]] = i; |
349 | |
350 | while (1) { |
351 | int done_something = FALSE; |
352 | |
353 | /* |
354 | * Go over the grid looking for reasons to add to the |
355 | * disconnect matrix. We're after pairs of squares which: |
356 | * |
357 | * - are adjacent in the grid |
358 | * - belong to distinct dsf components |
359 | * - their components are not already marked as |
360 | * disconnected |
361 | * - their components share a letter in common. |
362 | */ |
363 | for (y = 0; y < h; y++) { |
364 | for (x = 0; x < w; x++) { |
365 | int dir; |
366 | for (dir = 0; dir < 2; dir++) { |
367 | int x2 = x + dir, y2 = y + 1 - dir; |
368 | int yx = y*w+x, yx2 = y2*w+x2; |
369 | |
370 | if (x2 >= w || y2 >= h) |
371 | continue; /* one square is outside the grid */ |
372 | |
373 | yx = dsf_canonify(sc->dsf, yx); |
374 | yx2 = dsf_canonify(sc->dsf, yx2); |
375 | if (yx == yx2) |
376 | continue; /* same dsf component */ |
377 | |
378 | if (sc->disconnect[yx*wh+yx2]) |
379 | continue; /* already known disconnected */ |
380 | |
381 | for (i = 0; i < k; i++) |
382 | if (sc->contents[yx*k+i] >= 0 && |
383 | sc->contents[yx2*k+i] >= 0) |
384 | break; |
385 | if (i == k) |
386 | continue; /* no letter in common */ |
387 | |
388 | /* |
389 | * We've found one. Mark yx and yx2 as |
390 | * disconnected from each other. |
391 | */ |
392 | #ifdef SOLVER_DIAGNOSTICS |
393 | printf("Disconnecting %d and %d (%c)\n", yx, yx2, 'A'+i); |
394 | #endif |
395 | solver_disconnect(sc, yx, yx2); |
396 | done_something = done_something_overall = TRUE; |
397 | |
398 | /* |
399 | * We have just made a deduction which hinges |
400 | * on two particular grid squares being the |
401 | * same. If we are feeding back to a generator |
402 | * loop, we must therefore mark those squares |
403 | * as fixed in the generator, so that future |
404 | * rearrangement of the grid will not break |
405 | * the information on which we have already |
406 | * based deductions. |
407 | */ |
408 | if (gen_lock) { |
409 | gen_lock[sc->contents[yx*k+i]] = 1; |
410 | gen_lock[sc->contents[yx2*k+i]] = 1; |
411 | } |
412 | } |
413 | } |
414 | } |
415 | |
416 | /* |
417 | * Now go over the grid looking for dsf components which |
418 | * are below maximum size and only have one way to extend, |
419 | * and extending them. |
420 | */ |
421 | for (i = 0; i < wh; i++) |
422 | sc->tmp[i] = -1; |
423 | for (y = 0; y < h; y++) { |
424 | for (x = 0; x < w; x++) { |
425 | int yx = dsf_canonify(sc->dsf, y*w+x); |
426 | int dir; |
427 | |
428 | if (sc->size[yx] == k) |
429 | continue; |
430 | |
431 | for (dir = 0; dir < 4; dir++) { |
432 | int x2 = x + (dir==0 ? -1 : dir==2 ? 1 : 0); |
433 | int y2 = y + (dir==1 ? -1 : dir==3 ? 1 : 0); |
434 | int yx2, yx2c; |
435 | |
436 | if (y2 < 0 || y2 >= h || x2 < 0 || x2 >= w) |
437 | continue; |
438 | yx2 = y2*w+x2; |
439 | yx2c = dsf_canonify(sc->dsf, yx2); |
440 | |
441 | if (yx2c != yx && !sc->disconnect[yx2c*wh+yx]) { |
442 | /* |
443 | * Component yx can be extended into square |
444 | * yx2. |
445 | */ |
446 | if (sc->tmp[yx] == -1) |
447 | sc->tmp[yx] = yx2; |
448 | else if (sc->tmp[yx] != yx2) |
449 | sc->tmp[yx] = -2; /* multiple choices found */ |
450 | } |
451 | } |
452 | } |
453 | } |
454 | for (i = 0; i < wh; i++) { |
455 | if (sc->tmp[i] >= 0) { |
456 | /* |
457 | * Make sure we haven't connected the two already |
458 | * during this loop (which could happen if for |
459 | * _both_ components this was the only way to |
460 | * extend them). |
461 | */ |
462 | if (dsf_canonify(sc->dsf, i) == |
463 | dsf_canonify(sc->dsf, sc->tmp[i])) |
464 | continue; |
465 | |
466 | #ifdef SOLVER_DIAGNOSTICS |
467 | printf("Connecting %d and %d\n", i, sc->tmp[i]); |
468 | #endif |
469 | solver_connect(sc, i, sc->tmp[i]); |
470 | done_something = done_something_overall = TRUE; |
471 | break; |
472 | } |
473 | } |
474 | |
475 | if (!done_something) |
476 | break; |
477 | } |
478 | |
479 | /* |
480 | * Return 0 if we haven't made any progress; 1 if we've done |
481 | * something but not solved it completely; 2 if we've solved |
482 | * it completely. |
483 | */ |
484 | for (i = 0; i < wh; i++) |
485 | if (sc->size[dsf_canonify(sc->dsf, i)] != k) |
486 | break; |
487 | if (i == wh) |
488 | return 2; |
489 | if (done_something_overall) |
490 | return 1; |
491 | return 0; |
492 | } |
493 | |
494 | unsigned char *generate(int w, int h, int k, random_state *rs) |
495 | { |
496 | int wh = w*h; |
497 | int n = wh/k; |
498 | struct solver_scratch *sc; |
499 | unsigned char *grid; |
500 | unsigned char *shuffled; |
501 | int i, j, m, retries; |
502 | int *permutation; |
503 | unsigned char *gen_lock; |
504 | extern int *divvy_rectangle(int w, int h, int k, random_state *rs); |
505 | |
506 | sc = solver_scratch_new(w, h, k); |
507 | grid = snewn(wh, unsigned char); |
508 | shuffled = snewn(k, unsigned char); |
509 | permutation = snewn(wh, int); |
510 | gen_lock = snewn(wh, unsigned char); |
511 | |
512 | do { |
513 | int *dsf = divvy_rectangle(w, h, k, rs); |
514 | |
515 | /* |
516 | * Go through the dsf and find the indices of all the |
517 | * squares involved in each omino, in a manner conducive |
518 | * to per-omino indexing. We set permutation[i*k+j] to be |
519 | * the index of the jth square (ordered arbitrarily) in |
520 | * omino i. |
521 | */ |
522 | for (i = j = 0; i < wh; i++) |
523 | if (dsf_canonify(dsf, i) == i) { |
524 | sc->tmp[i] = j; |
525 | /* |
526 | * During this loop and the following one, we use |
527 | * the last element of each row of permutation[] |
528 | * as a counter of the number of indices so far |
529 | * placed in it. When we place the final index of |
530 | * an omino, that counter is overwritten, but that |
531 | * doesn't matter because we'll never use it |
532 | * again. Of course this depends critically on |
533 | * divvy_rectangle() having returned correct |
534 | * results, or else chaos would ensue. |
535 | */ |
536 | permutation[j*k+k-1] = 0; |
537 | j++; |
538 | } |
539 | for (i = 0; i < wh; i++) { |
540 | j = sc->tmp[dsf_canonify(dsf, i)]; |
541 | m = permutation[j*k+k-1]++; |
542 | permutation[j*k+m] = i; |
543 | } |
544 | |
545 | /* |
546 | * Track which squares' letters we have already depended |
547 | * on for deductions. This is gradually updated by |
548 | * solver_attempt(). |
549 | */ |
550 | memset(gen_lock, 0, wh); |
551 | |
552 | /* |
553 | * Now repeatedly fill the grid with letters, and attempt |
554 | * to solve it. If the solver makes progress but does not |
555 | * fail completely, then gen_lock will have been updated |
556 | * and we try again. On a complete failure, though, we |
557 | * have no option but to give up and abandon this set of |
558 | * ominoes. |
559 | */ |
560 | solver_init(sc); |
561 | retries = k*k; |
562 | while (1) { |
563 | /* |
564 | * Fill the grid with letters. We can safely use |
565 | * sc->tmp to hold the set of letters required at each |
566 | * stage, since it's at least size k and is currently |
567 | * unused. |
568 | */ |
569 | for (i = 0; i < n; i++) { |
570 | /* |
571 | * First, determine the set of letters already |
572 | * placed in this omino by gen_lock. |
573 | */ |
574 | for (j = 0; j < k; j++) |
575 | sc->tmp[j] = j; |
576 | for (j = 0; j < k; j++) { |
577 | int index = permutation[i*k+j]; |
578 | int letter = grid[index]; |
579 | if (gen_lock[index]) |
580 | sc->tmp[letter] = -1; |
581 | } |
582 | /* |
583 | * Now collect together all the remaining letters |
584 | * and randomly shuffle them. |
585 | */ |
586 | for (j = m = 0; j < k; j++) |
587 | if (sc->tmp[j] >= 0) |
588 | sc->tmp[m++] = sc->tmp[j]; |
589 | shuffle(sc->tmp, m, sizeof(*sc->tmp), rs); |
590 | /* |
591 | * Finally, write the shuffled letters into the |
592 | * grid. |
593 | */ |
594 | for (j = 0; j < k; j++) { |
595 | int index = permutation[i*k+j]; |
596 | if (!gen_lock[index]) |
597 | grid[index] = sc->tmp[--m]; |
598 | } |
599 | assert(m == 0); |
600 | } |
601 | |
602 | /* |
603 | * Now we have a candidate grid. Attempt to progress |
604 | * the solution. |
605 | */ |
606 | m = solver_attempt(sc, grid, gen_lock); |
607 | if (m == 2 || /* success */ |
608 | (m == 0 && retries-- <= 0)) /* failure */ |
609 | break; |
610 | if (m == 1) |
611 | retries = k*k; /* reset this counter, and continue */ |
612 | } |
613 | |
614 | sfree(dsf); |
615 | } while (m == 0); |
616 | |
617 | sfree(gen_lock); |
618 | sfree(permutation); |
619 | sfree(shuffled); |
620 | solver_scratch_free(sc); |
621 | |
622 | return grid; |
623 | } |
624 | |
625 | /* ---------------------------------------------------------------------- |
626 | * End of solver/generator code. |
627 | */ |
628 | |
629 | static char *new_game_desc(game_params *params, random_state *rs, |
630 | char **aux, int interactive) |
631 | { |
632 | int w = params->w, h = params->h, wh = w*h, k = params->k; |
633 | unsigned char *grid; |
634 | char *desc; |
635 | int i; |
636 | |
637 | grid = generate(w, h, k, rs); |
638 | |
639 | desc = snewn(wh+1, char); |
640 | for (i = 0; i < wh; i++) |
641 | desc[i] = 'A' + grid[i]; |
642 | desc[wh] = '\0'; |
643 | |
644 | sfree(grid); |
645 | |
646 | return desc; |
647 | } |
648 | |
649 | static char *validate_desc(game_params *params, char *desc) |
650 | { |
651 | return NULL; |
652 | } |
653 | |
654 | static game_state *new_game(midend *me, game_params *params, char *desc) |
655 | { |
656 | game_state *state = snew(game_state); |
657 | |
658 | state->FIXME = 0; |
659 | |
660 | return state; |
661 | } |
662 | |
663 | static game_state *dup_game(game_state *state) |
664 | { |
665 | game_state *ret = snew(game_state); |
666 | |
667 | ret->FIXME = state->FIXME; |
668 | |
669 | return ret; |
670 | } |
671 | |
672 | static void free_game(game_state *state) |
673 | { |
674 | sfree(state); |
675 | } |
676 | |
677 | static char *solve_game(game_state *state, game_state *currstate, |
678 | char *aux, char **error) |
679 | { |
680 | return NULL; |
681 | } |
682 | |
fa3abef5 |
683 | static int game_can_format_as_text_now(game_params *params) |
684 | { |
685 | return TRUE; |
686 | } |
687 | |
213d2495 |
688 | static char *game_text_format(game_state *state) |
689 | { |
690 | return NULL; |
691 | } |
692 | |
693 | static game_ui *new_ui(game_state *state) |
694 | { |
695 | return NULL; |
696 | } |
697 | |
698 | static void free_ui(game_ui *ui) |
699 | { |
700 | } |
701 | |
702 | static char *encode_ui(game_ui *ui) |
703 | { |
704 | return NULL; |
705 | } |
706 | |
707 | static void decode_ui(game_ui *ui, char *encoding) |
708 | { |
709 | } |
710 | |
711 | static void game_changed_state(game_ui *ui, game_state *oldstate, |
712 | game_state *newstate) |
713 | { |
714 | } |
715 | |
716 | struct game_drawstate { |
717 | int tilesize; |
718 | int FIXME; |
719 | }; |
720 | |
721 | static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, |
722 | int x, int y, int button) |
723 | { |
724 | return NULL; |
725 | } |
726 | |
727 | static game_state *execute_move(game_state *state, char *move) |
728 | { |
729 | return NULL; |
730 | } |
731 | |
732 | /* ---------------------------------------------------------------------- |
733 | * Drawing routines. |
734 | */ |
735 | |
736 | static void game_compute_size(game_params *params, int tilesize, |
737 | int *x, int *y) |
738 | { |
739 | *x = *y = 10 * tilesize; /* FIXME */ |
740 | } |
741 | |
742 | static void game_set_size(drawing *dr, game_drawstate *ds, |
743 | game_params *params, int tilesize) |
744 | { |
745 | ds->tilesize = tilesize; |
746 | } |
747 | |
748 | static float *game_colours(frontend *fe, int *ncolours) |
749 | { |
750 | float *ret = snewn(3 * NCOLOURS, float); |
751 | |
752 | frontend_default_colour(fe, &ret[COL_BACKGROUND * 3]); |
753 | |
754 | *ncolours = NCOLOURS; |
755 | return ret; |
756 | } |
757 | |
758 | static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) |
759 | { |
760 | struct game_drawstate *ds = snew(struct game_drawstate); |
761 | |
762 | ds->tilesize = 0; |
763 | ds->FIXME = 0; |
764 | |
765 | return ds; |
766 | } |
767 | |
768 | static void game_free_drawstate(drawing *dr, game_drawstate *ds) |
769 | { |
770 | sfree(ds); |
771 | } |
772 | |
773 | static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, |
774 | game_state *state, int dir, game_ui *ui, |
775 | float animtime, float flashtime) |
776 | { |
777 | /* |
778 | * The initial contents of the window are not guaranteed and |
779 | * can vary with front ends. To be on the safe side, all games |
780 | * should start by drawing a big background-colour rectangle |
781 | * covering the whole window. |
782 | */ |
783 | draw_rect(dr, 0, 0, 10*ds->tilesize, 10*ds->tilesize, COL_BACKGROUND); |
784 | } |
785 | |
786 | static float game_anim_length(game_state *oldstate, game_state *newstate, |
787 | int dir, game_ui *ui) |
788 | { |
789 | return 0.0F; |
790 | } |
791 | |
792 | static float game_flash_length(game_state *oldstate, game_state *newstate, |
793 | int dir, game_ui *ui) |
794 | { |
795 | return 0.0F; |
796 | } |
797 | |
798 | static int game_timing_state(game_state *state, game_ui *ui) |
799 | { |
800 | return TRUE; |
801 | } |
802 | |
803 | static void game_print_size(game_params *params, float *x, float *y) |
804 | { |
805 | } |
806 | |
807 | static void game_print(drawing *dr, game_state *state, int tilesize) |
808 | { |
809 | } |
810 | |
811 | #ifdef COMBINED |
741706e7 |
812 | #define thegame separate |
213d2495 |
813 | #endif |
814 | |
815 | const struct game thegame = { |
741706e7 |
816 | "Separate", NULL, NULL, |
213d2495 |
817 | default_params, |
818 | game_fetch_preset, |
819 | decode_params, |
820 | encode_params, |
821 | free_params, |
822 | dup_params, |
823 | FALSE, game_configure, custom_params, |
824 | validate_params, |
825 | new_game_desc, |
826 | validate_desc, |
827 | new_game, |
828 | dup_game, |
829 | free_game, |
830 | FALSE, solve_game, |
fa3abef5 |
831 | FALSE, game_can_format_as_text_now, game_text_format, |
213d2495 |
832 | new_ui, |
833 | free_ui, |
834 | encode_ui, |
835 | decode_ui, |
836 | game_changed_state, |
837 | interpret_move, |
838 | execute_move, |
839 | 20 /* FIXME */, game_compute_size, game_set_size, |
840 | game_colours, |
841 | game_new_drawstate, |
842 | game_free_drawstate, |
843 | game_redraw, |
844 | game_anim_length, |
845 | game_flash_length, |
846 | FALSE, FALSE, game_print_size, game_print, |
847 | FALSE, /* wants_statusbar */ |
848 | FALSE, game_timing_state, |
849 | 0, /* flags */ |
850 | }; |