After it confused Verity, clarify in the Unequal docs that the
[sgt/puzzles] / net.c
diff --git a/net.c b/net.c
index a788d46..eb83a16 100644 (file)
--- a/net.c
+++ b/net.c
@@ -80,7 +80,7 @@ struct game_params {
 struct game_state {
     int width, height, wrapping, completed;
     int last_rotate_x, last_rotate_y, last_rotate_dir;
-    int used_solve, just_used_solve;
+    int used_solve;
     unsigned char *tiles;
     unsigned char *barriers;
 };
@@ -382,29 +382,6 @@ static char *validate_params(game_params *params, int full)
  * avoidance is required.
  */
 
-static int dsf_canonify(int *dsf, int val)
-{
-    int v2 = val;
-
-    while (dsf[val] != val)
-       val = dsf[val];
-
-    while (v2 != val) {
-       int tmp = dsf[v2];
-       dsf[v2] = val;
-       v2 = tmp;
-    }
-
-    return val;
-}
-
-static void dsf_merge(int *dsf, int v1, int v2)
-{
-    v1 = dsf_canonify(dsf, v1);
-    v2 = dsf_canonify(dsf, v2);
-    dsf[v2] = v1;
-}
-
 struct todo {
     unsigned char *marked;
     int *buffer;
@@ -544,9 +521,7 @@ static int net_solver(int w, int h, unsigned char *tiles,
      * classes) by finding the representative of each tile and
      * setting equivalence[one]=the_other.
      */
-    equivalence = snewn(w * h, int);
-    for (i = 0; i < w*h; i++)
-       equivalence[i] = i;            /* initially all distinct */
+    equivalence = snew_dsf(w * h);
 
     /*
      * On a non-wrapping grid, we instantly know that all the edges
@@ -920,14 +895,7 @@ static void perturb(int w, int h, unsigned char *tiles, int wrapping,
     perim2 = snewn(nperim, struct xyd);
     memcpy(perim2, perimeter, nperim * sizeof(struct xyd));
     /* Shuffle the perimeter, so as to search it without directional bias. */
-    for (i = nperim; --i ;) {
-       int j = random_upto(rs, i+1);
-       struct xyd t;
-
-       t = perim2[j];
-       perim2[j] = perim2[i];
-       perim2[i] = t;
-    }
+    shuffle(perim2, nperim, sizeof(*perim2), rs);
     for (i = 0; i < nperim; i++) {
        int x2, y2;
 
@@ -1540,7 +1508,7 @@ static char *validate_desc(game_params *params, char *desc)
  * Construct an initial game state, given a description and parameters.
  */
 
-static game_state *new_game(midend_data *me, game_params *params, char *desc)
+static game_state *new_game(midend *me, game_params *params, char *desc)
 {
     game_state *state;
     int w, h, x, y;
@@ -1556,7 +1524,7 @@ static game_state *new_game(midend_data *me, game_params *params, char *desc)
     h = state->height = params->height;
     state->wrapping = params->wrapping;
     state->last_rotate_dir = state->last_rotate_x = state->last_rotate_y = 0;
-    state->completed = state->used_solve = state->just_used_solve = FALSE;
+    state->completed = state->used_solve = FALSE;
     state->tiles = snewn(state->width * state->height, unsigned char);
     memset(state->tiles, 0, state->width * state->height);
     state->barriers = snewn(state->width * state->height, unsigned char);
@@ -1636,7 +1604,6 @@ static game_state *dup_game(game_state *state)
     ret->wrapping = state->wrapping;
     ret->completed = state->completed;
     ret->used_solve = state->used_solve;
-    ret->just_used_solve = state->just_used_solve;
     ret->last_rotate_dir = state->last_rotate_dir;
     ret->last_rotate_x = state->last_rotate_x;
     ret->last_rotate_y = state->last_rotate_y;
@@ -1683,6 +1650,8 @@ static char *solve_game(game_state *state, game_state *currstate,
                 tiles[i] = c - 'a' + 10;
             else if (c >= 'A' && c <= 'F')
                 tiles[i] = c - 'A' + 10;
+
+           tiles[i] |= LOCKED;
         }
     }
 
@@ -1832,7 +1801,7 @@ static game_ui *new_ui(game_state *state)
     ui->cur_y = ui->cy = state->height / 2;
     ui->cur_visible = FALSE;
     get_random_seed(&seed, &seedsize);
-    ui->rs = random_init(seed, seedsize);
+    ui->rs = random_new(seed, seedsize);
     sfree(seed);
 
     return ui;
@@ -2047,11 +2016,10 @@ static game_state *execute_move(game_state *from, char *move)
     int tx, ty, n, noanim, orig;
 
     ret = dup_game(from);
-    ret->just_used_solve = FALSE;
 
     if (move[0] == 'J' || move[0] == 'S') {
        if (move[0] == 'S')
-           ret->just_used_solve = ret->used_solve = TRUE;
+           ret->used_solve = TRUE;
 
        move++;
        if (*move == ';')
@@ -2132,7 +2100,7 @@ static game_state *execute_move(game_state *from, char *move)
  * Routines for drawing the game position on the screen.
  */
 
-static game_drawstate *game_new_drawstate(game_state *state)
+static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
 {
     game_drawstate *ds = snew(game_drawstate);
 
@@ -2147,7 +2115,7 @@ static game_drawstate *game_new_drawstate(game_state *state)
     return ds;
 }
 
-static void game_free_drawstate(game_drawstate *ds)
+static void game_free_drawstate(drawing *dr, game_drawstate *ds)
 {
     sfree(ds->visible);
     sfree(ds);
@@ -2160,13 +2128,13 @@ static void game_compute_size(game_params *params, int tilesize,
     *y = WINDOW_OFFSET * 2 + tilesize * params->height + TILE_BORDER;
 }
 
-static void game_set_size(game_drawstate *ds, game_params *params,
-                         int tilesize)
+static void game_set_size(drawing *dr, game_drawstate *ds,
+                         game_params *params, int tilesize)
 {
     ds->tilesize = tilesize;
 }
 
-static float *game_colours(frontend *fe, game_state *state, int *ncolours)
+static float *game_colours(frontend *fe, int *ncolours)
 {
     float *ret;
 
@@ -2224,17 +2192,17 @@ static float *game_colours(frontend *fe, game_state *state, int *ncolours)
     return ret;
 }
 
-static void draw_thick_line(frontend *fe, int x1, int y1, int x2, int y2,
+static void draw_thick_line(drawing *dr, int x1, int y1, int x2, int y2,
                             int colour)
 {
-    draw_line(fe, x1-1, y1, x2-1, y2, COL_WIRE);
-    draw_line(fe, x1+1, y1, x2+1, y2, COL_WIRE);
-    draw_line(fe, x1, y1-1, x2, y2-1, COL_WIRE);
-    draw_line(fe, x1, y1+1, x2, y2+1, COL_WIRE);
-    draw_line(fe, x1, y1, x2, y2, colour);
+    draw_line(dr, x1-1, y1, x2-1, y2, COL_WIRE);
+    draw_line(dr, x1+1, y1, x2+1, y2, COL_WIRE);
+    draw_line(dr, x1, y1-1, x2, y2-1, COL_WIRE);
+    draw_line(dr, x1, y1+1, x2, y2+1, COL_WIRE);
+    draw_line(dr, x1, y1, x2, y2, colour);
 }
 
-static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2,
+static void draw_rect_coords(drawing *dr, int x1, int y1, int x2, int y2,
                              int colour)
 {
     int mx = (x1 < x2 ? x1 : x2);
@@ -2242,13 +2210,13 @@ static void draw_rect_coords(frontend *fe, int x1, int y1, int x2, int y2,
     int dx = (x2 + x1 - 2*mx + 1);
     int dy = (y2 + y1 - 2*my + 1);
 
-    draw_rect(fe, mx, my, dx, dy, colour);
+    draw_rect(dr, mx, my, dx, dy, colour);
 }
 
 /*
  * draw_barrier_corner() and draw_barrier() are passed physical coords
  */
-static void draw_barrier_corner(frontend *fe, game_drawstate *ds,
+static void draw_barrier_corner(drawing *dr, game_drawstate *ds,
                                 int x, int y, int dx, int dy, int phase)
 {
     int bx = WINDOW_OFFSET + TILE_SIZE * x;
@@ -2259,20 +2227,20 @@ static void draw_barrier_corner(frontend *fe, game_drawstate *ds,
     y1 = (dy > 0 ? TILE_SIZE+TILE_BORDER-1 : 0);
 
     if (phase == 0) {
-        draw_rect_coords(fe, bx+x1+dx, by+y1,
+        draw_rect_coords(dr, bx+x1+dx, by+y1,
                          bx+x1-TILE_BORDER*dx, by+y1-(TILE_BORDER-1)*dy,
                          COL_WIRE);
-        draw_rect_coords(fe, bx+x1, by+y1+dy,
+        draw_rect_coords(dr, bx+x1, by+y1+dy,
                          bx+x1-(TILE_BORDER-1)*dx, by+y1-TILE_BORDER*dy,
                          COL_WIRE);
     } else {
-        draw_rect_coords(fe, bx+x1, by+y1,
+        draw_rect_coords(dr, bx+x1, by+y1,
                          bx+x1-(TILE_BORDER-1)*dx, by+y1-(TILE_BORDER-1)*dy,
                          COL_BARRIER);
     }
 }
 
-static void draw_barrier(frontend *fe, game_drawstate *ds,
+static void draw_barrier(drawing *dr, game_drawstate *ds,
                          int x, int y, int dir, int phase)
 {
     int bx = WINDOW_OFFSET + TILE_SIZE * x;
@@ -2285,16 +2253,16 @@ static void draw_barrier(frontend *fe, game_drawstate *ds,
     h = (Y(dir) ? TILE_BORDER : TILE_SIZE - TILE_BORDER);
 
     if (phase == 0) {
-        draw_rect(fe, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
+        draw_rect(dr, bx+x1-X(dir), by+y1-Y(dir), w, h, COL_WIRE);
     } else {
-        draw_rect(fe, bx+x1, by+y1, w, h, COL_BARRIER);
+        draw_rect(dr, bx+x1, by+y1, w, h, COL_BARRIER);
     }
 }
 
 /*
  * draw_tile() is passed physical coordinates
  */
-static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
+static void draw_tile(drawing *dr, game_state *state, game_drawstate *ds,
                       int x, int y, int tile, int src, float angle, int cursor)
 {
     int bx = WINDOW_OFFSET + TILE_SIZE * x;
@@ -2310,16 +2278,16 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
      * we must draw those connections on the borders themselves.
      */
 
-    clip(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
+    clip(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
 
     /*
      * So. First blank the tile out completely: draw a big
      * rectangle in border colour, and a smaller rectangle in
      * background colour to fill it in.
      */
-    draw_rect(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
+    draw_rect(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER,
               COL_BORDER);
-    draw_rect(fe, bx+TILE_BORDER, by+TILE_BORDER,
+    draw_rect(dr, bx+TILE_BORDER, by+TILE_BORDER,
               TILE_SIZE-TILE_BORDER, TILE_SIZE-TILE_BORDER,
               tile & LOCKED ? COL_LOCKED : COL_BACKGROUND);
 
@@ -2329,16 +2297,16 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
      * in.
      */
     if (cursor) {
-       draw_line(fe, bx+TILE_SIZE/8, by+TILE_SIZE/8,
+       draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8,
                  bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
                  tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
-       draw_line(fe, bx+TILE_SIZE/8, by+TILE_SIZE/8,
+       draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE/8,
                  bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
                  tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
-       draw_line(fe, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
+       draw_line(dr, bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE/8,
                  bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
                  tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
-       draw_line(fe, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
+       draw_line(dr, bx+TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
                  bx+TILE_SIZE-TILE_SIZE/8, by+TILE_SIZE-TILE_SIZE/8,
                  tile & LOCKED ? COL_BACKGROUND : COL_LOCKED);
     }
@@ -2361,7 +2329,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
             ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
             ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
             MATMUL(tx, ty, matrix, ex, ey);
-            draw_thick_line(fe, bx+(int)cx, by+(int)cy,
+            draw_thick_line(dr, bx+(int)cx, by+(int)cy,
                            bx+(int)(cx+tx), by+(int)(cy+ty),
                             COL_WIRE);
         }
@@ -2371,7 +2339,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
             ex = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * X(dir);
             ey = (TILE_SIZE - TILE_BORDER - 1.0F) / 2.0F * Y(dir);
             MATMUL(tx, ty, matrix, ex, ey);
-            draw_line(fe, bx+(int)cx, by+(int)cy,
+            draw_line(dr, bx+(int)cx, by+(int)cy,
                      bx+(int)(cx+tx), by+(int)(cy+ty), col);
         }
     }
@@ -2404,7 +2372,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
             points[i+1] = by+(int)(cy+ty);
         }
 
-        draw_polygon(fe, points, 4, col, COL_WIRE);
+        draw_polygon(dr, points, 4, col, COL_WIRE);
     }
 
     /*
@@ -2441,8 +2409,8 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
              * in: if we are fully connected to the other tile then
              * the two ACTIVE states will be the same.)
              */
-            draw_rect_coords(fe, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
-            draw_rect_coords(fe, px, py, px+lx, py+ly,
+            draw_rect_coords(dr, px-vx, py-vy, px+lx+vx, py+ly+vy, COL_WIRE);
+            draw_rect_coords(dr, px, py, px+lx, py+ly,
                              (tile & ACTIVE) ? COL_POWERED : COL_WIRE);
         } else {
             /*
@@ -2450,7 +2418,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
              * actually connected to us. Just draw a single black
              * dot.
              */
-            draw_rect_coords(fe, px, py, px, py, COL_WIRE);
+            draw_rect_coords(dr, px, py, px, py, COL_WIRE);
         }
     }
 
@@ -2494,7 +2462,7 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
                  * At least one barrier terminates here. Draw a
                  * corner.
                  */
-                draw_barrier_corner(fe, ds, x, y,
+                draw_barrier_corner(dr, ds, x, y,
                                     X(dir)+X(A(dir)), Y(dir)+Y(A(dir)),
                                     phase);
             }
@@ -2502,15 +2470,15 @@ static void draw_tile(frontend *fe, game_state *state, game_drawstate *ds,
 
         for (dir = 1; dir < 0x10; dir <<= 1)
             if (barrier(state, GX(x), GY(y)) & dir)
-                draw_barrier(fe, ds, x, y, dir, phase);
+                draw_barrier(dr, ds, x, y, dir, phase);
     }
 
-    unclip(fe);
+    unclip(dr);
 
-    draw_update(fe, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
+    draw_update(dr, bx, by, TILE_SIZE+TILE_BORDER, TILE_SIZE+TILE_BORDER);
 }
 
-static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
+static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
                  game_state *state, int dir, game_ui *ui, float t, float ft)
 {
     int x, y, tx, ty, frame, last_rotate_dir, moved_origin = FALSE;
@@ -2526,7 +2494,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
 
         ds->started = TRUE;
 
-        draw_rect(fe, 0, 0, 
+        draw_rect(dr, 0, 0, 
                   WINDOW_OFFSET * 2 + TILE_SIZE * state->width + TILE_BORDER,
                   WINDOW_OFFSET * 2 + TILE_SIZE * state->height + TILE_BORDER,
                   COL_BACKGROUND);
@@ -2535,7 +2503,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
         ds->org_y = ui->org_y;
         moved_origin = TRUE;
 
-        draw_update(fe, 0, 0, 
+        draw_update(dr, 0, 0, 
                     WINDOW_OFFSET*2 + TILE_SIZE*state->width + TILE_BORDER,
                     WINDOW_OFFSET*2 + TILE_SIZE*state->height + TILE_BORDER);
 
@@ -2544,38 +2512,38 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
             for (x = 0; x < ds->width; x++) {
                 if (x+1 < ds->width) {
                     if (barrier(state, GX(x), GY(0)) & R)
-                        draw_barrier_corner(fe, ds, x, -1, +1, +1, phase);
+                        draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
                     if (barrier(state, GX(x), GY(ds->height-1)) & R)
-                        draw_barrier_corner(fe, ds, x, ds->height, +1, -1, phase);
+                        draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
                 }
                 if (barrier(state, GX(x), GY(0)) & U) {
-                    draw_barrier_corner(fe, ds, x, -1, -1, +1, phase);
-                    draw_barrier_corner(fe, ds, x, -1, +1, +1, phase);
-                    draw_barrier(fe, ds, x, -1, D, phase);
+                    draw_barrier_corner(dr, ds, x, -1, -1, +1, phase);
+                    draw_barrier_corner(dr, ds, x, -1, +1, +1, phase);
+                    draw_barrier(dr, ds, x, -1, D, phase);
                 }
                 if (barrier(state, GX(x), GY(ds->height-1)) & D) {
-                    draw_barrier_corner(fe, ds, x, ds->height, -1, -1, phase);
-                    draw_barrier_corner(fe, ds, x, ds->height, +1, -1, phase);
-                    draw_barrier(fe, ds, x, ds->height, U, phase);
+                    draw_barrier_corner(dr, ds, x, ds->height, -1, -1, phase);
+                    draw_barrier_corner(dr, ds, x, ds->height, +1, -1, phase);
+                    draw_barrier(dr, ds, x, ds->height, U, phase);
                 }
             }
 
             for (y = 0; y < ds->height; y++) {
                 if (y+1 < ds->height) {
                     if (barrier(state, GX(0), GY(y)) & D)
-                        draw_barrier_corner(fe, ds, -1, y, +1, +1, phase);
+                        draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
                     if (barrier(state, GX(ds->width-1), GY(y)) & D)
-                        draw_barrier_corner(fe, ds, ds->width, y, -1, +1, phase);
+                        draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
                 }
                 if (barrier(state, GX(0), GY(y)) & L) {
-                    draw_barrier_corner(fe, ds, -1, y, +1, -1, phase);
-                    draw_barrier_corner(fe, ds, -1, y, +1, +1, phase);
-                    draw_barrier(fe, ds, -1, y, R, phase);
+                    draw_barrier_corner(dr, ds, -1, y, +1, -1, phase);
+                    draw_barrier_corner(dr, ds, -1, y, +1, +1, phase);
+                    draw_barrier(dr, ds, -1, y, R, phase);
                 }
                 if (barrier(state, GX(ds->width-1), GY(y)) & R) {
-                    draw_barrier_corner(fe, ds, ds->width, y, -1, -1, phase);
-                    draw_barrier_corner(fe, ds, ds->width, y, -1, +1, phase);
-                    draw_barrier(fe, ds, ds->width, y, L, phase);
+                    draw_barrier_corner(dr, ds, ds->width, y, -1, -1, phase);
+                    draw_barrier_corner(dr, ds, ds->width, y, -1, +1, phase);
+                    draw_barrier(dr, ds, ds->width, y, L, phase);
                 }
             }
         }
@@ -2641,7 +2609,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                 index(state, ds->visible, x, y) != c ||
                 index(state, ds->visible, x, y) == 0xFF ||
                 is_src || is_anim || is_cursor) {
-                draw_tile(fe, state, ds, x, y, c,
+                draw_tile(dr, state, ds, x, y, c,
                           is_src, (is_anim ? angle : 0.0F), is_cursor);
                 if (is_src || is_anim || is_cursor)
                     index(state, ds->visible, x, y) = 0xFF;
@@ -2669,7 +2637,7 @@ static void game_redraw(frontend *fe, game_drawstate *ds, game_state *oldstate,
                (state->used_solve ? "Auto-solved. " :
                 state->completed ? "COMPLETED! " : ""), a, n2);
 
-       status_bar(fe, statusbuf);
+       status_bar(dr, statusbuf);
     }
 
     sfree(active);
@@ -2681,13 +2649,6 @@ static float game_anim_length(game_state *oldstate,
     int last_rotate_dir;
 
     /*
-     * Don't animate an auto-solve move.
-     */
-    if ((dir > 0 && newstate->just_used_solve) ||
-       (dir < 0 && oldstate->just_used_solve))
-       return 0.0F;
-
-    /*
      * Don't animate if last_rotate_dir is zero.
      */
     last_rotate_dir = dir==-1 ? oldstate->last_rotate_dir :
@@ -2718,14 +2679,144 @@ static float game_flash_length(game_state *oldstate,
     return 0.0F;
 }
 
-static int game_wants_statusbar(void)
+static int game_timing_state(game_state *state, game_ui *ui)
 {
     return TRUE;
 }
 
-static int game_timing_state(game_state *state, game_ui *ui)
+static void game_print_size(game_params *params, float *x, float *y)
 {
-    return TRUE;
+    int pw, ph;
+
+    /*
+     * I'll use 8mm squares by default.
+     */
+    game_compute_size(params, 800, &pw, &ph);
+    *x = pw / 100.0;
+    *y = ph / 100.0;
+}
+
+static void draw_diagram(drawing *dr, game_drawstate *ds, int x, int y,
+                        int topleft, int v, int drawlines, int ink)
+{
+    int tx, ty, cx, cy, r, br, k, thick;
+
+    tx = WINDOW_OFFSET + TILE_SIZE * x;
+    ty = WINDOW_OFFSET + TILE_SIZE * y;
+
+    /*
+     * Find our centre point.
+     */
+    if (topleft) {
+       cx = tx + (v & L ? TILE_SIZE / 4 : TILE_SIZE / 6);
+       cy = ty + (v & U ? TILE_SIZE / 4 : TILE_SIZE / 6);
+       r = TILE_SIZE / 8;
+       br = TILE_SIZE / 32;
+    } else {
+       cx = tx + TILE_SIZE / 2;
+       cy = ty + TILE_SIZE / 2;
+       r = TILE_SIZE / 2;
+       br = TILE_SIZE / 8;
+    }
+    thick = r / 20;
+
+    /*
+     * Draw the square block if we have an endpoint.
+     */
+    if (v == 1 || v == 2 || v == 4 || v == 8)
+       draw_rect(dr, cx - br, cy - br, br*2, br*2, ink);
+
+    /*
+     * Draw each radial line.
+     */
+    if (drawlines) {
+       for (k = 1; k < 16; k *= 2)
+           if (v & k) {
+               int x1 = min(cx, cx + (r-thick) * X(k));
+               int x2 = max(cx, cx + (r-thick) * X(k));
+               int y1 = min(cy, cy + (r-thick) * Y(k));
+               int y2 = max(cy, cy + (r-thick) * Y(k));
+               draw_rect(dr, x1 - thick, y1 - thick,
+                         (x2 - x1) + 2*thick, (y2 - y1) + 2*thick, ink);
+           }
+    }
+}
+
+static void game_print(drawing *dr, game_state *state, int tilesize)
+{
+    int w = state->width, h = state->height;
+    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, TILE_SIZE / (state->wrapping ? 128 : 12));
+    draw_rect_outline(dr, WINDOW_OFFSET, WINDOW_OFFSET,
+                     TILE_SIZE * w, TILE_SIZE * h, ink);
+
+    /*
+     * Grid.
+     */
+    print_line_width(dr, TILE_SIZE / 128);
+    for (x = 1; x < w; x++)
+       draw_line(dr, WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET,
+                 WINDOW_OFFSET + TILE_SIZE * x, WINDOW_OFFSET + TILE_SIZE * h,
+                 ink);
+    for (y = 1; y < h; y++)
+       draw_line(dr, WINDOW_OFFSET, WINDOW_OFFSET + TILE_SIZE * y,
+                 WINDOW_OFFSET + TILE_SIZE * w, WINDOW_OFFSET + TILE_SIZE * y,
+                 ink);
+
+    /*
+     * Barriers.
+     */
+    for (y = 0; y <= h; y++)
+       for (x = 0; x <= w; x++) {
+           int b = barrier(state, x % w, y % h);
+           if (x < w && (b & U))
+               draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
+                         WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
+                         TILE_SIZE + TILE_SIZE/24 * 2, TILE_SIZE/24 * 2, ink);
+           if (y < h && (b & L))
+               draw_rect(dr, WINDOW_OFFSET + TILE_SIZE * x - TILE_SIZE/24,
+                         WINDOW_OFFSET + TILE_SIZE * y - TILE_SIZE/24,
+                         TILE_SIZE/24 * 2, TILE_SIZE + TILE_SIZE/24 * 2, ink);
+       }
+
+    /*
+     * Grid contents.
+     */
+    for (y = 0; y < h; y++)
+       for (x = 0; x < w; x++) {
+           int vx, v = tile(state, x, y);
+           int locked = v & LOCKED;
+
+           v &= 0xF;
+
+           /*
+            * Rotate into a standard orientation for the top left
+            * corner diagram.
+            */
+           vx = v;
+           while (vx != 0 && vx != 15 && vx != 1 && vx != 9 && vx != 13 &&
+                  vx != 5)
+               vx = A(vx);
+
+           /*
+            * Draw the top left corner diagram.
+            */
+           draw_diagram(dr, ds, x, y, TRUE, vx, TRUE, ink);
+
+           /*
+            * Draw the real solution diagram, if we're doing so.
+            */
+           draw_diagram(dr, ds, x, y, FALSE, v, locked, ink);
+       }
 }
 
 #ifdef COMBINED
@@ -2733,7 +2824,7 @@ static int game_timing_state(game_state *state, game_ui *ui)
 #endif
 
 const struct game thegame = {
-    "Net", "games.net",
+    "Net", "games.net", "net",
     default_params,
     game_fetch_preset,
     decode_params,
@@ -2763,7 +2854,8 @@ const struct game thegame = {
     game_redraw,
     game_anim_length,
     game_flash_length,
-    game_wants_statusbar,
+    TRUE, FALSE, game_print_size, game_print,
+    TRUE,                             /* wants_statusbar */
     FALSE, game_timing_state,
-    0,                                /* mouse_priorities */
+    0,                                /* flags */
 };