#define COORD(x) ( (x) * TILE_SIZE + BORDER )
#define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
-#define PI 3.141592653589793238462643383279502884197169399
-
#define ANIM_PER_RADIUS_UNIT 0.13F
#define FLASH_FRAME 0.13F
}
static char *new_game_desc(game_params *params, random_state *rs,
- game_aux_info **aux)
+ game_aux_info **aux, int interactive)
{
int *grid;
int w = params->w, h = params->h, n = params->n, wh = w*h;
*/
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 oldx = -1, oldy = -1, oldr = -1;
+ 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;
+ int x, y, r, oldtotal, newtotal, dx, dy;
do {
x = random_upto(rs, w - n + 1);
y = random_upto(rs, h - n + 1);
- r = 1 + 2 * random_upto(rs, 2);
- } while (x == oldx && y == oldy && (oldr == 0 || r == oldr));
-
- do_rotate(grid, w, h, n, params->orientable,
- x, y, r);
+ 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;
+ } while (abs(newtotal) < abs(oldtotal) || abs(newtotal) > 2);
+
+ do_rotate(grid, w, h, n, params->orientable, x, y, r);
/*
- * Prevent immediate reversal of a previous move, or
- * execution of three consecutive identical moves
- * adding up to a single inverse move. One exception is
- * when we only _have_ one x,y setting.
+ * 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
*/
- if (w != n || h != n) {
- if (oldx == x && oldy == y)
- oldr = 0; /* now avoid _any_ move in this x,y */
- else
- oldr = -r & 3; /* only prohibit the exact inverse */
- oldx = x;
- oldy = y;
+ 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;
+ }
}
}
+
+ sfree(prevmoves);
+
} while (grid_complete(grid, wh, params->orientable));
/*
return NULL;
}
-static game_state *new_game(game_params *params, char *desc)
+static game_state *new_game(midend_data *me, game_params *params, char *desc)
{
game_state *state = snew(game_state);
int w = params->w, h = params->h, n = params->n, wh = w*h;
{
}
-static game_state *make_move(game_state *from, game_ui *ui, int x, int y,
- int button)
+static game_state *make_move(game_state *from, game_ui *ui, game_drawstate *ds,
+ int x, int y, int button)
{
int w = from->w, h = from->h, n = from->n, wh = w*h;
game_state *ret;
int dir;
+ button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
+
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
/*
* Determine the coordinates of the click. We offset by n-1
}
static float game_anim_length(game_state *oldstate, game_state *newstate,
- int dir)
+ int dir, game_ui *ui)
{
if ((dir > 0 && newstate->just_used_solve) ||
(dir < 0 && oldstate->just_used_solve))
}
static float game_flash_length(game_state *oldstate, game_state *newstate,
- int dir)
+ int dir, game_ui *ui)
{
if (!oldstate->completed && newstate->completed &&
!oldstate->used_solve && !newstate->used_solve)
*/
if (oldstate) {
float angle;
- float anim_max = game_anim_length(oldstate, state, dir);
+ float anim_max = game_anim_length(oldstate, state, dir, ui);
if (dir > 0) {
lastx = state->lastx;
return TRUE;
}
+static int game_timing_state(game_state *state)
+{
+ return TRUE;
+}
+
#ifdef COMBINED
#define thegame twiddle
#endif
game_anim_length,
game_flash_length,
game_wants_statusbar,
+ FALSE, game_timing_state,
};