+ if (state->completed)
+ return FALSE;
+ return TRUE;
+}
+
+static void game_print_size(game_params *params, float *x, float *y)
+{
+ int pw, ph;
+
+ /*
+ * I'll use 9mm squares by default. They should be quite big
+ * for this game, because players will want to jot down no end
+ * of pencil marks in the squares.
+ */
+ game_compute_size(params, 900, &pw, &ph);
+ *x = pw / 100.0F;
+ *y = ph / 100.0F;
+}
+
+/*
+ * Subfunction to draw the thick lines between cells. In order to do
+ * this using the line-drawing rather than rectangle-drawing API (so
+ * as to get line thicknesses to scale correctly) and yet have
+ * correctly mitred joins between lines, we must do this by tracing
+ * the boundary of each sub-block and drawing it in one go as a
+ * single polygon.
+ *
+ * This subfunction is also reused with thinner dotted lines to
+ * outline the Killer cages, this time offsetting the outline toward
+ * the interior of the affected squares.
+ */
+static void outline_block_structure(drawing *dr, game_drawstate *ds,
+ game_state *state,
+ struct block_structure *blocks,
+ int ink, int inset)
+{
+ int cr = state->cr;
+ int *coords;
+ int bi, i, n;
+ int x, y, dx, dy, sx, sy, sdx, sdy;
+
+ /*
+ * Maximum perimeter of a k-omino is 2k+2. (Proof: start
+ * with k unconnected squares, with total perimeter 4k.
+ * Now repeatedly join two disconnected components
+ * together into a larger one; every time you do so you
+ * remove at least two unit edges, and you require k-1 of
+ * these operations to create a single connected piece, so
+ * you must have at most 4k-2(k-1) = 2k+2 unit edges left
+ * afterwards.)
+ */
+ coords = snewn(4*cr+4, int); /* 2k+2 points, 2 coords per point */
+
+ /*
+ * Iterate over all the blocks.
+ */
+ for (bi = 0; bi < blocks->nr_blocks; bi++) {
+ if (blocks->nr_squares[bi] == 0)
+ continue;
+
+ /*
+ * For each block, find a starting square within it
+ * which has a boundary at the left.
+ */
+ for (i = 0; i < cr; i++) {
+ int j = blocks->blocks[bi][i];
+ if (j % cr == 0 || blocks->whichblock[j-1] != bi)
+ break;
+ }
+ assert(i < cr); /* every block must have _some_ leftmost square */
+ x = blocks->blocks[bi][i] % cr;
+ y = blocks->blocks[bi][i] / cr;
+ dx = -1;
+ dy = 0;
+
+ /*
+ * Now begin tracing round the perimeter. At all
+ * times, (x,y) describes some square within the
+ * block, and (x+dx,y+dy) is some adjacent square
+ * outside it; so the edge between those two squares
+ * is always an edge of the block.
+ */
+ sx = x, sy = y, sdx = dx, sdy = dy; /* save starting position */
+ n = 0;
+ do {
+ int cx, cy, tx, ty, nin;
+
+ /*
+ * Advance to the next edge, by looking at the two
+ * squares beyond it. If they're both outside the block,
+ * we turn right (by leaving x,y the same and rotating
+ * dx,dy clockwise); if they're both inside, we turn
+ * left (by rotating dx,dy anticlockwise and contriving
+ * to leave x+dx,y+dy unchanged); if one of each, we go
+ * straight on (and may enforce by assertion that
+ * they're one of each the _right_ way round).
+ */
+ nin = 0;
+ tx = x - dy + dx;
+ ty = y + dx + dy;
+ nin += (tx >= 0 && tx < cr && ty >= 0 && ty < cr &&
+ blocks->whichblock[ty*cr+tx] == bi);
+ tx = x - dy;
+ ty = y + dx;
+ nin += (tx >= 0 && tx < cr && ty >= 0 && ty < cr &&
+ blocks->whichblock[ty*cr+tx] == bi);
+ if (nin == 0) {
+ /*
+ * Turn right.
+ */
+ int tmp;
+ tmp = dx;
+ dx = -dy;
+ dy = tmp;
+ } else if (nin == 2) {
+ /*
+ * Turn left.
+ */
+ int tmp;
+
+ x += dx;
+ y += dy;
+
+ tmp = dx;
+ dx = dy;
+ dy = -tmp;
+
+ x -= dx;
+ y -= dy;
+ } else {
+ /*
+ * Go straight on.
+ */
+ x -= dy;
+ y += dx;
+ }
+
+ /*
+ * Now enforce by assertion that we ended up
+ * somewhere sensible.
+ */
+ assert(x >= 0 && x < cr && y >= 0 && y < cr &&
+ blocks->whichblock[y*cr+x] == bi);
+ assert(x+dx < 0 || x+dx >= cr || y+dy < 0 || y+dy >= cr ||
+ blocks->whichblock[(y+dy)*cr+(x+dx)] != bi);
+
+ /*
+ * Record the point we just went past at one end of the
+ * edge. To do this, we translate (x,y) down and right
+ * by half a unit (so they're describing a point in the
+ * _centre_ of the square) and then translate back again
+ * in a manner rotated by dy and dx.
+ */
+ assert(n < 2*cr+2);
+ cx = ((2*x+1) + dy + dx) / 2;
+ cy = ((2*y+1) - dx + dy) / 2;
+ coords[2*n+0] = BORDER + cx * TILE_SIZE;
+ coords[2*n+1] = BORDER + cy * TILE_SIZE;
+ coords[2*n+0] -= dx * inset;
+ coords[2*n+1] -= dy * inset;
+ if (nin == 0) {
+ /*
+ * We turned right, so inset this corner back along
+ * the edge towards the centre of the square.
+ */
+ coords[2*n+0] -= dy * inset;
+ coords[2*n+1] += dx * inset;
+ } else if (nin == 2) {
+ /*
+ * We turned left, so inset this corner further
+ * _out_ along the edge into the next square.
+ */
+ coords[2*n+0] += dy * inset;
+ coords[2*n+1] -= dx * inset;
+ }
+ n++;
+
+ } while (x != sx || y != sy || dx != sdx || dy != sdy);
+
+ /*
+ * That's our polygon; now draw it.
+ */
+ draw_polygon(dr, coords, n, -1, ink);
+ }
+
+ sfree(coords);
+}
+
+static void game_print(drawing *dr, game_state *state, int tilesize)
+{
+ int cr = state->cr;
+ int ink = print_mono_colour(dr, 0);
+ int x, y;
+
+ /* Ick: fake up `ds->tilesize' for macro expansion purposes */
+ game_drawstate ads, *ds = &ads;
+ game_set_size(dr, ds, NULL, tilesize);
+
+ /*
+ * Border.
+ */
+ print_line_width(dr, 3 * TILE_SIZE / 40);
+ draw_rect_outline(dr, BORDER, BORDER, cr*TILE_SIZE, cr*TILE_SIZE, ink);
+
+ /*
+ * Highlight X-diagonal squares.
+ */
+ if (state->xtype) {
+ int i;
+ int xhighlight = print_grey_colour(dr, 0.90F);
+
+ for (i = 0; i < cr; i++)
+ draw_rect(dr, BORDER + i*TILE_SIZE, BORDER + i*TILE_SIZE,
+ TILE_SIZE, TILE_SIZE, xhighlight);
+ for (i = 0; i < cr; i++)
+ if (i*2 != cr-1) /* avoid redoing centre square, just for fun */
+ draw_rect(dr, BORDER + i*TILE_SIZE,
+ BORDER + (cr-1-i)*TILE_SIZE,
+ TILE_SIZE, TILE_SIZE, xhighlight);
+ }
+
+ /*
+ * Main grid.
+ */
+ for (x = 1; x < cr; x++) {
+ print_line_width(dr, TILE_SIZE / 40);
+ draw_line(dr, BORDER+x*TILE_SIZE, BORDER,
+ BORDER+x*TILE_SIZE, BORDER+cr*TILE_SIZE, ink);
+ }
+ for (y = 1; y < cr; y++) {
+ print_line_width(dr, TILE_SIZE / 40);
+ draw_line(dr, BORDER, BORDER+y*TILE_SIZE,
+ BORDER+cr*TILE_SIZE, BORDER+y*TILE_SIZE, ink);
+ }
+
+ /*
+ * Thick lines between cells.
+ */
+ print_line_width(dr, 3 * TILE_SIZE / 40);
+ outline_block_structure(dr, ds, state, state->blocks, ink, 0);
+
+ /*
+ * Killer cages and their totals.
+ */
+ if (state->kblocks) {
+ print_line_width(dr, TILE_SIZE / 40);
+ print_line_dotted(dr, TRUE);
+ outline_block_structure(dr, ds, state, state->kblocks, ink,
+ 5 * TILE_SIZE / 40);
+ print_line_dotted(dr, FALSE);
+ for (y = 0; y < cr; y++)
+ for (x = 0; x < cr; x++)
+ if (state->kgrid[y*cr+x]) {
+ char str[20];
+ sprintf(str, "%d", state->kgrid[y*cr+x]);
+ draw_text(dr,
+ BORDER+x*TILE_SIZE + 7*TILE_SIZE/40,
+ BORDER+y*TILE_SIZE + 16*TILE_SIZE/40,
+ FONT_VARIABLE, TILE_SIZE/4,
+ ALIGN_VNORMAL | ALIGN_HLEFT,
+ ink, str);
+ }
+ }
+
+ /*
+ * Standard (non-Killer) clue numbers.
+ */
+ for (y = 0; y < cr; y++)
+ for (x = 0; x < cr; x++)
+ if (state->grid[y*cr+x]) {
+ char str[2];
+ str[1] = '\0';
+ str[0] = state->grid[y*cr+x] + '0';
+ if (str[0] > '9')
+ str[0] += 'a' - ('9'+1);
+ draw_text(dr, BORDER + x*TILE_SIZE + TILE_SIZE/2,
+ BORDER + y*TILE_SIZE + TILE_SIZE/2,
+ FONT_VARIABLE, TILE_SIZE/2,
+ ALIGN_VCENTRE | ALIGN_HCENTRE, ink, str);
+ }