+ total_moves = params->movetarget;
+ if (!total_moves)
+ /* Add a random move to avoid parity issues. */
+ total_moves = w*h*n*n*2 + random_upto(rs, 2);
+
+ do {
+ int *prevmoves;
+ int rw, rh; /* w/h of rotation centre space */
+
+ rw = w - n + 1;
+ rh = h - n + 1;
+ prevmoves = snewn(rw * rh, int);
+ for (i = 0; i < rw * rh; i++)
+ prevmoves[i] = 0;
+
+ for (i = 0; i < total_moves; i++) {
+ int x, y, r, oldtotal, newtotal, dx, dy;
+
+ do {
+ x = random_upto(rs, w - n + 1);
+ y = random_upto(rs, h - n + 1);
+ r = 2 * random_upto(rs, 2) - 1;
+
+ /*
+ * See if any previous rotations has happened at
+ * this point which nothing has overlapped since.
+ * If so, ensure we haven't either undone a
+ * previous move or repeated one so many times that
+ * it turns into fewer moves in the inverse
+ * direction (i.e. three identical rotations).
+ */
+ oldtotal = prevmoves[y*rw+x];
+ newtotal = oldtotal + r;
+
+ /*
+ * Special case here for w==h==n, in which case
+ * there is actually no way to _avoid_ all moves
+ * repeating or undoing previous ones.
+ */
+ } while ((w != n || h != n) &&
+ (abs(newtotal) < abs(oldtotal) || abs(newtotal) > 2));
+
+ do_rotate(grid, w, h, n, params->orientable, x, y, r);
+
+ /*
+ * Log the rotation we've just performed at this point,
+ * for inversion detection in the next move.
+ *
+ * Also zero a section of the prevmoves array, because
+ * any rotation area which _overlaps_ this one is now
+ * entirely safe to perform further moves in.
+ *
+ * Two rotation areas overlap if their top left
+ * coordinates differ by strictly less than n in both
+ * directions
+ */
+ prevmoves[y*rw+x] += r;
+ for (dy = -n+1; dy <= n-1; dy++) {
+ if (y + dy < 0 || y + dy >= rh)
+ continue;
+ for (dx = -n+1; dx <= n-1; dx++) {
+ if (x + dx < 0 || x + dx >= rw)
+ continue;
+ if (dx == 0 && dy == 0)
+ continue;
+ prevmoves[(y+dy)*rw+(x+dx)] = 0;
+ }
+ }
+ }