X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/ad599e2b668fb8e62f166457254796fc84443d48..997065cf756f7f031d3ab0e7cf7a6a8d80d143bb:/solo.c diff --git a/solo.c b/solo.c index b36eb66..a298d15 100644 --- a/solo.c +++ b/solo.c @@ -146,19 +146,19 @@ enum { #define MAX_2SUMS 5 #define MAX_3SUMS 8 #define MAX_4SUMS 12 -unsigned int sum_bits2[18][MAX_2SUMS]; -unsigned int sum_bits3[25][MAX_3SUMS]; -unsigned int sum_bits4[31][MAX_4SUMS]; +unsigned long sum_bits2[18][MAX_2SUMS]; +unsigned long sum_bits3[25][MAX_3SUMS]; +unsigned long sum_bits4[31][MAX_4SUMS]; -static int find_sum_bits(unsigned int *array, int idx, int value_left, +static int find_sum_bits(unsigned long *array, int idx, int value_left, int addends_left, int min_addend, - unsigned int bitmask_so_far) + unsigned long bitmask_so_far) { int i; assert(addends_left >= 2); for (i = min_addend; i < value_left; i++) { - unsigned int new_bitmask = bitmask_so_far | (1 << i); + unsigned long new_bitmask = bitmask_so_far | (1L << i); assert(bitmask_so_far != new_bitmask); if (addends_left == 2) { @@ -167,7 +167,7 @@ static int find_sum_bits(unsigned int *array, int idx, int value_left, break; if (j > 9) continue; - array[idx++] = new_bitmask | (1 << j); + array[idx++] = new_bitmask | (1L << j); } else idx = find_sum_bits(array, idx, value_left - i, addends_left - 1, i + 1, @@ -1448,7 +1448,7 @@ static int solver_killer_sums(struct solver_usage *usage, int b, int cr = usage->cr; int i, ret, max_sums; int nsquares = cages->nr_squares[b]; - unsigned int *sumbits, possible_addends; + unsigned long *sumbits, possible_addends; if (clue == 0) { assert(nsquares == 0); @@ -1514,18 +1514,18 @@ static int solver_killer_sums(struct solver_usage *usage, int b, possible_addends = 0; for (i = 0; i < max_sums; i++) { int j; - unsigned int bits = sumbits[i]; + unsigned long bits = sumbits[i]; if (bits == 0) break; for (j = 0; j < nsquares; j++) { int n; - unsigned int square_bits = bits; + unsigned long square_bits = bits; int x = cages->blocks[b][j]; for (n = 1; n <= cr; n++) if (!cube2(x, n)) - square_bits &= ~(1 << n); + square_bits &= ~(1L << n); if (square_bits == 0) { break; } @@ -2933,8 +2933,8 @@ static int gridgen(int cr, struct block_structure *blocks, /* * Check whether a grid contains a valid complete puzzle. */ -static int check_valid(int cr, struct block_structure *blocks, int xtype, - digit *grid) +static int check_valid(int cr, struct block_structure *blocks, + struct block_structure *kblocks, int xtype, digit *grid) { unsigned char *used; int x, y, i, j, n; @@ -2988,6 +2988,25 @@ static int check_valid(int cr, struct block_structure *blocks, int xtype, } /* + * Check that each Killer cage, if any, contains at most one of + * everything. + */ + if (kblocks) { + for (i = 0; i < kblocks->nr_blocks; i++) { + memset(used, FALSE, cr); + for (j = 0; j < kblocks->nr_squares[i]; j++) + if (grid[kblocks->blocks[i][j]] > 0 && + grid[kblocks->blocks[i][j]] <= cr) { + if (used[grid[kblocks->blocks[i][j]]-1]) { + sfree(used); + return FALSE; + } + used[grid[kblocks->blocks[i][j]]-1] = TRUE; + } + } + } + + /* * Check that each diagonal contains precisely one of everything. */ if (xtype) { @@ -3528,7 +3547,7 @@ static char *new_game_desc(game_params *params, random_state *rs, if (!gridgen(cr, blocks, kblocks, params->xtype, grid, rs, area*area)) continue; - assert(check_valid(cr, blocks, params->xtype, grid)); + assert(check_valid(cr, blocks, kblocks, params->xtype, grid)); /* * Save the solved grid in aux. @@ -4409,7 +4428,7 @@ struct game_drawstate { unsigned char *pencil; unsigned char *hl; /* This is scratch space used within a single call to game_redraw. */ - int *entered_items; + int nregions, *entered_items; }; static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, @@ -4553,8 +4572,8 @@ static game_state *execute_move(game_state *from, char *move) * We've made a real change to the grid. Check to see * if the game has been completed. */ - if (!ret->completed && check_valid(cr, ret->blocks, ret->xtype, - ret->grid)) { + if (!ret->completed && check_valid(cr, ret->blocks, ret->kblocks, + ret->xtype, ret->grid)) { ret->completed = TRUE; } } @@ -4643,7 +4662,15 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state) memset(ds->pencil, 0, cr*cr*cr); ds->hl = snewn(cr*cr, unsigned char); memset(ds->hl, 0, cr*cr); - ds->entered_items = snewn(cr*cr, int); + /* + * ds->entered_items needs one row of cr entries per entity in + * which digits may not be duplicated. That's one for each row, + * each column, each block, each diagonal, and each Killer cage. + */ + ds->nregions = cr*3 + 2; + if (state->kblocks) + ds->nregions += state->kblocks->nr_blocks; + ds->entered_items = snewn(cr * ds->nregions, int); ds->tilesize = 0; /* not decided yet */ return ds; } @@ -4723,10 +4750,37 @@ static void draw_number(drawing *dr, game_drawstate *ds, game_state *state, if (state->kblocks) { int t = GRIDEXTRA * 3; - int kl = cx - 1, kt = cy - 1, kr = cx + cw, kb = cy + ch; + int kcx, kcy, kcw, kch; + int kl, kt, kr, kb; int has_left = 0, has_right = 0, has_top = 0, has_bottom = 0; /* + * In non-jigsaw mode, the Killer cages are placed at a + * fixed offset from the outer edge of the cell dividing + * lines, so that they look right whether those lines are + * thick or thin. In jigsaw mode, however, doing this will + * sometimes cause the cage outlines in adjacent squares to + * fail to match up with each other, so we must offset a + * fixed amount from the _centre_ of the cell dividing + * lines. + */ + if (state->blocks->r == 1) { + kcx = tx; + kcy = ty; + kcw = tw; + kch = th; + } else { + kcx = cx; + kcy = cy; + kcw = cw; + kch = ch; + } + kl = kcx - 1; + kt = kcy - 1; + kr = kcx + kcw; + kb = kcy + kch; + + /* * First, draw the lines dividing this area from neighbouring * different areas. */ @@ -4759,20 +4813,20 @@ static void draw_number(drawing *dr, game_drawstate *ds, game_state *state, if (x+1 < cr && y > 0 && !has_right && !has_top && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y-1)*cr+x+1]) { - draw_line(dr, cx + cw - t, kt + t, cx + cw, kt + t, col_killer); - draw_line(dr, cx + cw - t, kt, cx + cw - t, kt + t, col_killer); + draw_line(dr, kcx + kcw - t, kt + t, kcx + kcw, kt + t, col_killer); + draw_line(dr, kcx + kcw - t, kt, kcx + kcw - t, kt + t, col_killer); } if (x > 0 && y+1 < cr && !has_left && !has_bottom && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x-1]) { - draw_line(dr, kl, cy + ch - t, kl + t, cy + ch - t, col_killer); - draw_line(dr, kl + t, cy + ch - t, kl + t, cy + ch, col_killer); + draw_line(dr, kl, kcy + kch - t, kl + t, kcy + kch - t, col_killer); + draw_line(dr, kl + t, kcy + kch - t, kl + t, kcy + kch, col_killer); } if (x+1 < cr && y+1 < cr && !has_right && !has_bottom && state->kblocks->whichblock[y*cr+x] != state->kblocks->whichblock[(y+1)*cr+x+1]) { - draw_line(dr, cx + cw - t, cy + ch - t, cx + cw - t, cy + ch, col_killer); - draw_line(dr, cx + cw - t, cy + ch - t, cx + cw, cy + ch - t, col_killer); + draw_line(dr, kcx + kcw - t, kcy + kch - t, kcx + kcw - t, kcy + kch, col_killer); + draw_line(dr, kcx + kcw - t, kcy + kch - t, kcx + kcw, kcy + kch - t, col_killer); } } @@ -4943,21 +4997,36 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, * This array is used to keep track of rows, columns and boxes * which contain a number more than once. */ - for (x = 0; x < cr * cr; x++) + for (x = 0; x < cr * ds->nregions; x++) ds->entered_items[x] = 0; for (x = 0; x < cr; x++) for (y = 0; y < cr; y++) { digit d = state->grid[y*cr+x]; if (d) { - int box = state->blocks->whichblock[y*cr+x]; - ds->entered_items[x*cr+d-1] |= ((ds->entered_items[x*cr+d-1] & 1) << 1) | 1; - ds->entered_items[y*cr+d-1] |= ((ds->entered_items[y*cr+d-1] & 4) << 1) | 4; - ds->entered_items[box*cr+d-1] |= ((ds->entered_items[box*cr+d-1] & 16) << 1) | 16; + int box, kbox; + + /* Rows */ + ds->entered_items[x*cr+d-1]++; + + /* Columns */ + ds->entered_items[(y+cr)*cr+d-1]++; + + /* Blocks */ + box = state->blocks->whichblock[y*cr+x]; + ds->entered_items[(box+2*cr)*cr+d-1]++; + + /* Diagonals */ if (ds->xtype) { if (ondiag0(y*cr+x)) - ds->entered_items[d-1] |= ((ds->entered_items[d-1] & 64) << 1) | 64; + ds->entered_items[(3*cr)*cr+d-1]++; if (ondiag1(y*cr+x)) - ds->entered_items[cr+d-1] |= ((ds->entered_items[cr+d-1] & 64) << 1) | 64; + ds->entered_items[(3*cr+1)*cr+d-1]++; + } + + /* Killer cages */ + if (state->kblocks) { + kbox = state->kblocks->whichblock[y*cr+x]; + ds->entered_items[(kbox+3*cr+2)*cr+d-1]++; } } } @@ -4981,11 +5050,17 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate, /* Mark obvious errors (ie, numbers which occur more than once * in a single row, column, or box). */ - if (d && ((ds->entered_items[x*cr+d-1] & 2) || - (ds->entered_items[y*cr+d-1] & 8) || - (ds->entered_items[state->blocks->whichblock[y*cr+x]*cr+d-1] & 32) || - (ds->xtype && ((ondiag0(y*cr+x) && (ds->entered_items[d-1] & 128)) || - (ondiag1(y*cr+x) && (ds->entered_items[cr+d-1] & 128)))))) + if (d && (ds->entered_items[x*cr+d-1] > 1 || + ds->entered_items[(y+cr)*cr+d-1] > 1 || + ds->entered_items[(state->blocks->whichblock[y*cr+x] + +2*cr)*cr+d-1] > 1 || + (ds->xtype && ((ondiag0(y*cr+x) && + ds->entered_items[(3*cr)*cr+d-1] > 1) || + (ondiag1(y*cr+x) && + ds->entered_items[(3*cr+1)*cr+d-1]>1)))|| + (state->kblocks && + ds->entered_items[(state->kblocks->whichblock[y*cr+x] + +3*cr+2)*cr+d-1] > 1))) highlight |= 16; if (d && state->kblocks) {