static char *new_game_desc(game_params *params, random_state *rs, char **aux, int interactive) { int nballs = params->minballs, i; char *grid, *ret; unsigned char *bmp; if (params->maxballs > params->minballs) nballs += random_upto(rs, params->maxballs - params->minballs + 1); grid = snewn(params->w*params->h, char); memset(grid, 0, params->w * params->h * sizeof(char)); bmp = snewn(nballs*2 + 2, unsigned char); memset(bmp, 0, (nballs*2 + 2) * sizeof(unsigned char)); bmp[0] = params->w; bmp[1] = params->h; for (i = 0; i < nballs; i++) { int x, y; do { x = random_upto(rs, params->w); y = random_upto(rs, params->h); } while (grid[y*params->w + x]); grid[y*params->w + x] = 1; bmp[(i+1)*2 + 0] = x; bmp[(i+1)*2 + 1] = y; #ifdef ANDROID if (android_cancelled()) { sfree(grid); sfree(bmp); return NULL; } #endif } sfree(grid); obfuscate_bitmap(bmp, (nballs*2 + 2) * 8, FALSE); ret = bin2hex(bmp, nballs*2 + 2); sfree(bmp); return ret; }
static char *new_game_desc(game_params *params, random_state *rs, char **aux, int interactive) { const int w = params->w; const int h = params->h; const int sz = w * h; int *board = snewn(sz, int); int *randomize = snewn(sz, int); char *game_description = snewn(sz + 1, char); int i; for (i = 0; i < sz; ++i) { board[i] = EMPTY; randomize[i] = i; } make_board(board, w, h, rs); #ifdef ANDROID if (android_cancelled()) { sfree(randomize); sfree(board); return NULL; } #endif g_board = board; qsort(randomize, sz, sizeof (int), compare); minimize_clue_set(board, w, h, randomize); for (i = 0; i < sz; ++i) { assert(board[i] >= 0); assert(board[i] < 10); game_description[i] = board[i] + '0'; } game_description[sz] = '\0'; /* solver(board, w, h, aux); print_board(board, w, h); */ sfree(randomize); sfree(board); return game_description; }
/* generate a random valid board; uses validate_board. */ static void make_board(int *board, int w, int h, random_state *rs) { int *dsf; const unsigned int sz = w * h; /* w=h=2 is a special case which requires a number > max(w, h) */ /* TODO prove that this is the case ONLY for w=h=2. */ const int maxsize = min(max(max(w, h), 3), 9); /* Note that if 1 in {w, h} then it's impossible to have a region * of size > w*h, so the special case only affects w=h=2. */ int nboards = 0; int i; assert(w >= 1); assert(h >= 1); assert(board); dsf = snew_dsf(sz); /* implicit dsf_init */ /* I abuse the board variable: when generating the puzzle, it * contains a shuffled list of numbers {0, ..., nsq-1}. */ for (i = 0; i < (int)sz; ++i) board[i] = i; while (1) { int change; ++nboards; shuffle(board, sz, sizeof (int), rs); /* while the board can in principle be fixed */ do { #ifdef ANDROID if (android_cancelled()) { sfree(dsf); return; } #endif change = FALSE; for (i = 0; i < (int)sz; ++i) { int a = SENTINEL; int b = SENTINEL; int c = SENTINEL; const int aa = dsf_canonify(dsf, board[i]); int cc = sz; int j; for (j = 0; j < 4; ++j) { const int x = (board[i] % w) + dx[j]; const int y = (board[i] / w) + dy[j]; int bb; if (x < 0 || x >= w || y < 0 || y >= h) continue; bb = dsf_canonify(dsf, w*y + x); if (aa == bb) continue; else if (dsf_size(dsf, aa) == dsf_size(dsf, bb)) { a = aa; b = bb; c = cc; } else if (cc == sz) c = cc = bb; } if (a != SENTINEL) { a = dsf_canonify(dsf, a); assert(a != dsf_canonify(dsf, b)); if (c != sz) assert(a != dsf_canonify(dsf, c)); dsf_merge(dsf, a, c == sz? b: c); /* if repair impossible; make a new board */ if (dsf_size(dsf, a) > maxsize) goto retry; change = TRUE; } } } while (change); for (i = 0; i < (int)sz; ++i) board[i] = dsf_size(dsf, i); sfree(dsf); printv("returning board number %d\n", nboards); return; retry: dsf_init(dsf, sz); } assert(FALSE); /* unreachable */ }
static char *new_game_desc(game_params *params, random_state *rs, char **aux, int interactive) { int n = params->n, w = n+2, h = n+1, wh = w*h; int *grid, *grid2, *list; int i, j, k, len; char *ret; /* * Allocate space in which to lay the grid out. */ grid = snewn(wh, int); grid2 = snewn(wh, int); list = snewn(2*wh, int); /* * I haven't been able to think of any particularly clever * techniques for generating instances of Dominosa with a * unique solution. Many of the deductions used in this puzzle * are based on information involving half the grid at a time * (`of all the 6s, exactly one is next to a 3'), so a strategy * of partially solving the grid and then perturbing the place * where the solver got stuck seems particularly likely to * accidentally destroy the information which the solver had * used in getting that far. (Contrast with, say, Mines, in * which most deductions are local so this is an excellent * strategy.) * * Therefore I resort to the basest of brute force methods: * generate a random grid, see if it's solvable, throw it away * and try again if not. My only concession to sophistication * and cleverness is to at least _try_ not to generate obvious * 2x2 ambiguous sections (see comment below in the domino- * flipping section). * * During tests performed on 2005-07-15, I found that the brute * force approach without that tweak had to throw away about 87 * grids on average (at the default n=6) before finding a * unique one, or a staggering 379 at n=9; good job the * generator and solver are fast! When I added the * ambiguous-section avoidance, those numbers came down to 19 * and 26 respectively, which is a lot more sensible. */ do { domino_layout_prealloc(w, h, rs, grid, grid2, list); /* * Now we have a complete layout covering the whole * rectangle with dominoes. So shuffle the actual domino * values and fill the rectangle with numbers. */ k = 0; for (i = 0; i <= params->n; i++) for (j = 0; j <= i; j++) { list[k++] = i; list[k++] = j; } shuffle(list, k/2, 2*sizeof(*list), rs); j = 0; for (i = 0; i < wh; i++) if (grid[i] > i) { /* Optionally flip the domino round. */ int flip = -1; if (params->unique) { int t1, t2; /* * If we're after a unique solution, we can do * something here to improve the chances. If * we're placing a domino so that it forms a * 2x2 rectangle with one we've already placed, * and if that domino and this one share a * number, we can try not to put them so that * the identical numbers are diagonally * separated, because that automatically causes * non-uniqueness: * * +---+ +-+-+ * |2 3| |2|3| * +---+ -> | | | * |4 2| |4|2| * +---+ +-+-+ */ t1 = i; t2 = grid[i]; if (t2 == t1 + w) { /* this domino is vertical */ if (t1 % w > 0 &&/* and not on the left hand edge */ grid[t1-1] == t2-1 &&/* alongside one to left */ (grid2[t1-1] == list[j] || /* and has a number */ grid2[t1-1] == list[j+1] || /* in common */ grid2[t2-1] == list[j] || grid2[t2-1] == list[j+1])) { if (grid2[t1-1] == list[j] || grid2[t2-1] == list[j+1]) flip = 0; else flip = 1; } } else { /* this domino is horizontal */ if (t1 / w > 0 &&/* and not on the top edge */ grid[t1-w] == t2-w &&/* alongside one above */ (grid2[t1-w] == list[j] || /* and has a number */ grid2[t1-w] == list[j+1] || /* in common */ grid2[t2-w] == list[j] || grid2[t2-w] == list[j+1])) { if (grid2[t1-w] == list[j] || grid2[t2-w] == list[j+1]) flip = 0; else flip = 1; } } } if (flip < 0) flip = random_upto(rs, 2); grid2[i] = list[j + flip]; grid2[grid[i]] = list[j + 1 - flip]; j += 2; } assert(j == k); #ifdef ANDROID if (android_cancelled()) { sfree(list); sfree(grid2); sfree(grid); return NULL; } #endif } while (params->unique && solver(w, h, n, grid2, NULL) > 1); #ifdef GENERATION_DIAGNOSTICS for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { putchar('0' + grid2[j*w+i]); } putchar('\n'); } putchar('\n'); #endif /* * Encode the resulting game state. * * Our encoding is a string of digits. Any number greater than * 9 is represented by a decimal integer within square * brackets. We know there are n+2 of every number (it's paired * with each number from 0 to n inclusive, and one of those is * itself so that adds another occurrence), so we can work out * the string length in advance. */ /* * To work out the total length of the decimal encodings of all * the numbers from 0 to n inclusive: * - every number has a units digit; total is n+1. * - all numbers above 9 have a tens digit; total is max(n+1-10,0). * - all numbers above 99 have a hundreds digit; total is max(n+1-100,0). * - and so on. */ len = n+1; for (i = 10; i <= n; i *= 10) len += max(n + 1 - i, 0); /* Now add two square brackets for each number above 9. */ len += 2 * max(n + 1 - 10, 0); /* And multiply by n+2 for the repeated occurrences of each number. */ len *= n+2; /* * Now actually encode the string. */ ret = snewn(len+1, char); j = 0; for (i = 0; i < wh; i++) { k = grid2[i]; if (k < 10) ret[j++] = '0' + k; else j += sprintf(ret+j, "[%d]", k); assert(j <= len); } assert(j == len); ret[j] = '\0'; /* * Encode the solved state as an aux_info. */ { char *auxinfo = snewn(wh+1, char); for (i = 0; i < wh; i++) { int v = grid[i]; auxinfo[i] = (v == i+1 ? 'L' : v == i-1 ? 'R' : v == i+w ? 'T' : v == i-w ? 'B' : '.'); } auxinfo[wh] = '\0'; *aux = auxinfo; } sfree(list); sfree(grid2); sfree(grid); return ret; }